root/trunk/lib/Perlbal/TCPListener.pm @ 709

Revision 709, 5.0 kB (checked in by ask, 2 years ago)

make SSL support optional (would be better to only try loading the
SSL stuff if it's configured, but this helps a little)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1######################################################################
2# TCP listener on a given port
3#
4# Copyright 2004, Danga Interactive, Inc.
5# Copyright 2005-2007, Six Apart, Ltd.
6
7
8package Perlbal::TCPListener;
9use strict;
10use warnings;
11no  warnings qw(deprecated);
12
13use base "Perlbal::Socket";
14use fields qw(service hostport sslopts);
15use Socket qw(IPPROTO_TCP SOL_SOCKET SO_SNDBUF);
16BEGIN {
17    eval { require Perlbal::SocketSSL };
18    if ($@) { warn "SSL support not available: $@\n" }
19}
20
21# TCPListener
22sub new {
23    my ($class, $hostport, $service, $opts) = @_;
24    $opts ||= {};
25
26    my $sock = IO::Socket::INET->new(
27                                     LocalAddr => $hostport,
28                                     Proto => IPPROTO_TCP,
29                                     Listen => 1024,
30                                     ReuseAddr => 1,
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    $self->{sslopts} = $opts->{ssl};
52    bless $self, ref $class || $class;
53    $self->watch_read(1);
54    return $self;
55}
56
57# TCPListener: accepts a new client connection
58sub event_read {
59    my Perlbal::TCPListener $self = shift;
60
61    # accept as many connections as we can
62    while (my ($psock, $peeraddr) = $self->{sock}->accept) {
63        IO::Handle::blocking($psock, 0);
64
65        if (my $sndbuf = $self->{service}->{client_sndbuf_size}) {
66            my $rv = setsockopt($psock, SOL_SOCKET, SO_SNDBUF, pack("L", $sndbuf));
67        }
68
69        if (Perlbal::DEBUG >= 1) {
70            my ($pport, $pipr) = Socket::sockaddr_in($peeraddr);
71            my $pip = Socket::inet_ntoa($pipr);
72            print "Got new conn: $psock ($pip:$pport) for " . $self->{service}->role . "\n";
73        }
74
75        # SSL promotion if necessary
76        if ($self->{sslopts}) {
77            # try to upgrade to SSL, this does no IO it just reblesses
78            # and prepares the SSL engine for handling us later
79            IO::Socket::SSL->start_SSL(
80                                       $psock,
81                                       SSL_server => 1,
82                                       SSL_startHandshake => 0,
83                                       %{ $self->{sslopts} },
84                                       );
85            print "  .. socket upgraded to SSL!\n" if Perlbal::DEBUG >= 1;
86
87            # safety checking to ensure we got upgraded
88            return $psock->close
89                unless ref $psock eq 'IO::Socket::SSL';
90
91            # class into new package and run with it
92            my $sslsock = new Perlbal::SocketSSL($psock, $self);
93            $sslsock->try_accept;
94
95            # all done from our point of view
96            next;
97        }
98
99        # puts this socket into the right class
100        $self->class_new_socket($psock);
101    }
102}
103
104sub class_new_socket {
105    my Perlbal::TCPListener $self = shift;
106    my $psock = shift;
107
108    my $service_role = $self->{service}->role;
109    if ($service_role eq "reverse_proxy") {
110        Perlbal::ClientProxy->new($self->{service}, $psock);
111    } elsif ($service_role eq "management") {
112        Perlbal::ClientManage->new($self->{service}, $psock);
113    } elsif ($service_role eq "web_server") {
114        Perlbal::ClientHTTP->new($self->{service}, $psock);
115    } elsif ($service_role eq "selector") {
116        # will be cast to a more specific class later...
117        Perlbal::ClientHTTPBase->new($self->{service}, $psock, $self->{service});
118    } elsif (my $creator = Perlbal::Service::get_role_creator($service_role)) {
119        # was defined by a plugin, so we want to return one of these
120        $creator->($self->{service}, $psock);
121    }
122}
123
124sub as_string {
125    my Perlbal::TCPListener $self = shift;
126    my $ret = $self->SUPER::as_string;
127    my Perlbal::Service $svc = $self->{service};
128    $ret .= ": listening on $self->{hostport} for service '$svc->{name}'";
129    return $ret;
130}
131
132sub as_string_html {
133    my Perlbal::TCPListener $self = shift;
134    my $ret = $self->SUPER::as_string_html;
135    my Perlbal::Service $svc = $self->{service};
136    $ret .= ": listening on $self->{hostport} for service <b>$svc->{name}</b>";
137    return $ret;
138}
139
140sub die_gracefully {
141    # die off so we stop waiting for new connections
142    my $self = shift;
143    $self->close('graceful_death');
144}
145
1461;
147
148
149# Local Variables:
150# mode: perl
151# c-basic-indent: 4
152# indent-tabs-mode: nil
153# End:
Note: See TracBrowser for help on using the browser.