| 1 | ###################################################################### |
|---|
| 2 | # TCP listener on a given port |
|---|
| 3 | # |
|---|
| 4 | # Copyright 2004, Danga Interactive, Inc. |
|---|
| 5 | # Copyright 2005-2007, Six Apart, Ltd. |
|---|
| 6 | |
|---|
| 7 | |
|---|
| 8 | package Perlbal::TCPListener; |
|---|
| 9 | use strict; |
|---|
| 10 | use warnings; |
|---|
| 11 | no warnings qw(deprecated); |
|---|
| 12 | |
|---|
| 13 | use base "Perlbal::Socket"; |
|---|
| 14 | use fields qw(service hostport); |
|---|
| 15 | use Socket qw(IPPROTO_TCP SOL_SOCKET SO_SNDBUF); |
|---|
| 16 | |
|---|
| 17 | # TCPListener |
|---|
| 18 | sub new { |
|---|
| 19 | my ($class, $hostport, $service, $opts) = @_; |
|---|
| 20 | $opts ||= {}; |
|---|
| 21 | |
|---|
| 22 | my $sockclass = $opts->{ssl} ? "IO::Socket::SSL" : "IO::Socket::INET"; |
|---|
| 23 | my $sock = eval { |
|---|
| 24 | $sockclass->new( |
|---|
| 25 | LocalAddr => $hostport, |
|---|
| 26 | Proto => IPPROTO_TCP, |
|---|
| 27 | Listen => 1024, |
|---|
| 28 | ReuseAddr => 1, |
|---|
| 29 | ($opts->{ssl} ? %{$opts->{ssl}} : ()), |
|---|
| 30 | ); |
|---|
| 31 | }; |
|---|
| 32 | |
|---|
| 33 | return Perlbal::error("Error creating listening socket: " . ($@ || $!)) |
|---|
| 34 | unless $sock; |
|---|
| 35 | |
|---|
| 36 | if ($^O eq 'MSWin32') { |
|---|
| 37 | # On Windows, we have to do this a bit differently. |
|---|
| 38 | # IO::Socket should really do this for us, but whatever. |
|---|
| 39 | my $do = 1; |
|---|
| 40 | ioctl($sock, 0x8004667E, \$do) or return Perlbal::error("Unable to make listener on $hostport non-blocking: $!"); |
|---|
| 41 | } |
|---|
| 42 | else { |
|---|
| 43 | # IO::Socket::INET's Blocking => 0 just doesn't seem to work |
|---|
| 44 | # on lots of perls. who knows why. |
|---|
| 45 | IO::Handle::blocking($sock, 0) or return Perlbal::error("Unable to make listener on $hostport non-blocking: $!"); |
|---|
| 46 | } |
|---|
| 47 | |
|---|
| 48 | my $self = $class->SUPER::new($sock); |
|---|
| 49 | $self->{service} = $service; |
|---|
| 50 | $self->{hostport} = $hostport; |
|---|
| 51 | bless $self, ref $class || $class; |
|---|
| 52 | $self->watch_read(1); |
|---|
| 53 | return $self; |
|---|
| 54 | } |
|---|
| 55 | |
|---|
| 56 | # TCPListener: accepts a new client connection |
|---|
| 57 | sub event_read { |
|---|
| 58 | my Perlbal::TCPListener $self = shift; |
|---|
| 59 | |
|---|
| 60 | # accept as many connections as we can |
|---|
| 61 | while (my ($psock, $peeraddr) = $self->{sock}->accept) { |
|---|
| 62 | my $service_role = $self->{service}->role; |
|---|
| 63 | |
|---|
| 64 | if (Perlbal::DEBUG >= 1) { |
|---|
| 65 | my ($pport, $pipr) = Socket::sockaddr_in($peeraddr); |
|---|
| 66 | my $pip = Socket::inet_ntoa($pipr); |
|---|
| 67 | print "Got new conn: $psock ($pip:$pport) for $service_role\n"; |
|---|
| 68 | } |
|---|
| 69 | |
|---|
| 70 | IO::Handle::blocking($psock, 0); |
|---|
| 71 | |
|---|
| 72 | if (my $sndbuf = $self->{service}->{client_sndbuf_size}) { |
|---|
| 73 | my $rv = setsockopt($psock, SOL_SOCKET, SO_SNDBUF, pack("L", $sndbuf)); |
|---|
| 74 | } |
|---|
| 75 | |
|---|
| 76 | if ($service_role eq "reverse_proxy") { |
|---|
| 77 | Perlbal::ClientProxy->new($self->{service}, $psock); |
|---|
| 78 | } elsif ($service_role eq "management") { |
|---|
| 79 | Perlbal::ClientManage->new($self->{service}, $psock); |
|---|
| 80 | } elsif ($service_role eq "web_server") { |
|---|
| 81 | Perlbal::ClientHTTP->new($self->{service}, $psock); |
|---|
| 82 | } elsif ($service_role eq "selector") { |
|---|
| 83 | # will be cast to a more specific class later... |
|---|
| 84 | Perlbal::ClientHTTPBase->new($self->{service}, $psock, $self->{service}); |
|---|
| 85 | } elsif (my $creator = Perlbal::Service::get_role_creator($service_role)) { |
|---|
| 86 | # was defined by a plugin, so we want to return one of these |
|---|
| 87 | $creator->($self->{service}, $psock); |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | } |
|---|
| 91 | } |
|---|
| 92 | |
|---|
| 93 | sub as_string { |
|---|
| 94 | my Perlbal::TCPListener $self = shift; |
|---|
| 95 | my $ret = $self->SUPER::as_string; |
|---|
| 96 | my Perlbal::Service $svc = $self->{service}; |
|---|
| 97 | $ret .= ": listening on $self->{hostport} for service '$svc->{name}'"; |
|---|
| 98 | return $ret; |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | sub as_string_html { |
|---|
| 102 | my Perlbal::TCPListener $self = shift; |
|---|
| 103 | my $ret = $self->SUPER::as_string_html; |
|---|
| 104 | my Perlbal::Service $svc = $self->{service}; |
|---|
| 105 | $ret .= ": listening on $self->{hostport} for service <b>$svc->{name}</b>"; |
|---|
| 106 | return $ret; |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | sub die_gracefully { |
|---|
| 110 | # die off so we stop waiting for new connections |
|---|
| 111 | my $self = shift; |
|---|
| 112 | $self->close('graceful_death'); |
|---|
| 113 | } |
|---|
| 114 | |
|---|
| 115 | |
|---|
| 116 | 1; |
|---|
| 117 | |
|---|
| 118 | |
|---|
| 119 | # Local Variables: |
|---|
| 120 | # mode: perl |
|---|
| 121 | # c-basic-indent: 4 |
|---|
| 122 | # indent-tabs-mode: nil |
|---|
| 123 | # End: |
|---|