root/lang/perl/Moobal/trunk/lib/Moobal/Listener/TCP.pm @ 11949

Revision 11949, 5.1 kB (checked in by daisuke, 5 years ago)

more hacks

  • Property svn:keywords set to Id
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 Moobal::Listener::TCP;
9
10use Moose;
11
12with 'Moobal::Role::Listener';
13with 'Moobal::Role::WithContext';
14
15around 'new' => sub {
16    my $next = shift;
17    my $self = $next->(@_);
18    $self->start_listen();
19    return $self;
20};
21
22has 'service' => (
23    is => 'rw',
24    isa => 'Moobal::Service'
25);
26
27has 'hostport' => (
28    is =>'rw',
29    isa => 'Str'
30);
31
32has 'sslopts' => (
33    is => 'rw'
34);
35
36use Danga::Socket::Callback;
37use Moobal;
38use IO::Socket::INET;
39use Socket qw(IPPROTO_TCP SOL_SOCKET SO_SNDBUF);
40use UNIVERSAL::require;
41
42sub start_listen
43{
44    my $self = shift;
45
46    my $sock = IO::Socket::INET->new(
47        LocalAddr => $self->hostport,
48        Proto     => IPPROTO_TCP,
49        Listen    => 1024,
50        ReuseAddr => 1,
51    );
52
53    return die ("Error creating listening socket: " . ($@ || $!))
54        unless $sock;
55
56    if (&Moobal::RUNNING_IN_HELL) {
57        # On Windows, we have to do this a bit differently.
58        # IO::Socket should really do this for us, but whatever.
59        my $do = 1;
60        ioctl($sock, 0x8004667E, \$do) or
61            die ("Unable to make listener on @{[$self->hostport]} non-blocking: $!");
62    }
63    else {
64        # IO::Socket::INET's Blocking => 0 just doesn't seem to work
65        # on lots of perls.  who knows why.
66        IO::Handle::blocking($sock, 0) or
67            die ("Unable to make listener on @{[$self->hostport]} non-blocking: $!");
68    }
69
70    # Create a Danga::Socket callback
71    Danga::Socket::Callback->new(
72        handle        => $sock,
73        on_read_ready => sub { $self->accept_client( @_ ) }
74    );
75
76    return $self;
77}
78
79# TCPListener: accepts a new client connection
80sub accept_client {
81    my ($self, $dsocket) = @_;
82
83    # accept as many connections as we can
84    while (my ($psock, $peeraddr) = $dsocket->{sock}->accept) {
85        IO::Handle::blocking($psock, 0);
86
87        if (my $sndbuf = $self->service->{client_sndbuf_size}) {
88            my $rv = setsockopt($psock, SOL_SOCKET, SO_SNDBUF, pack("L", $sndbuf));
89        }
90
91        if (&Moobal::DEBUG >= 1) {
92            my ($pport, $pipr) = Socket::sockaddr_in($peeraddr);
93            my $pip = Socket::inet_ntoa($pipr);
94            print "Got new conn: $psock ($pip:$pport) for " . $self->service->role . "\n";
95        }
96
97        # SSL promotion if necessary
98        if ($self->{sslopts}) {
99            if (&Moobal::SSL_AVAILABLE) {
100                # try to upgrade to SSL, this does no IO it just reblesses
101                # and prepares the SSL engine for handling us later
102                IO::Socket::SSL->start_SSL(
103                    $psock,
104                    SSL_server => 1,
105                    SSL_startHandshake => 0,
106                    %{ $self->{sslopts} },
107                );
108                print "  .. socket upgraded to SSL!\n" if &Moobal::DEBUG >= 1;
109
110                # safety checking to ensure we got upgraded
111                return $psock->close
112                    unless ref $psock eq 'IO::Socket::SSL';
113
114                # class into new package and run with it
115                my $sslsock = new Moobal::SocketSSL($psock, $self);
116                $sslsock->try_accept;
117
118                # all done from our point of view
119                next;
120            }
121        }
122
123        # puts this socket into the right class
124        $self->class_new_socket($psock);
125    }
126}
127
128my %ROLE2CLIENTCLASS = (
129    reverse_proxy => 'Moobal::Component::Proxy',
130    management    => 'Moobal::Component::Management',
131    web_server    => 'Moobal::Component::ClientHTTP',
132    selector      => 'Moobal::Component::HTTP'
133);
134
135sub class_new_socket {
136    my ( $self, $psock ) = @_;
137
138    my $service      = $self->service;
139    my $service_role = $service->role;
140    my $client_class = $ROLE2CLIENTCLASS{ $service_role };
141
142    my $ret;
143    if ($client_class) {
144        if (! Class::MOP::is_class_loaded($client_class) ) {
145            $client_class->require or die;
146        }
147        $ret = $client_class->new(
148            context => $service->context,
149            service => $service, socket => $psock );
150#    } elsif (my $creator = Moobal::Service::get_role_creator($service_role)) {
151#        # was defined by a plugin, so we want to return one of these
152#        $ret = $creator->( service => $service, socket => $psock);
153    }
154
155    return $ret;
156}
157
158# XXX TODO LATER
159#sub as_string {
160#    my Moobal::TCPListener $self = shift;
161#    my $ret = $self->SUPER::as_string;
162#    my Moobal::Service $svc = $self->service;
163#    $ret .= ": listening on $self->{hostport} for service '$svc->{name}'";
164#    return $ret;
165#}
166#
167#sub as_string_html {
168#    my Moobal::TCPListener $self = shift;
169#    my $ret = $self->SUPER::as_string_html;
170#    my Moobal::Service $svc = $self->service;
171#    $ret .= ": listening on $self->{hostport} for service <b>$svc->{name}</b>";
172#    return $ret;
173#}
174#
175#sub die_gracefully {
176#    # die off so we stop waiting for new connections
177#    my $self = shift;
178#    $self->close('graceful_death');
179#}
180
1811;
182
183
184# Local Variables:
185# mode: perl
186# c-basic-indent: 4
187# indent-tabs-mode: nil
188# End:
Note: See TracBrowser for help on using the browser.