root/trunk/lib/Perlbal/SocketSSL.pm

Revision 774, 4.0 kB (checked in by ask, 17 months ago)

Lots of typo corrections in documentation and comments from Nick Andrew

Line 
1# Base class for SSL sockets.
2#
3# This is a simple class that extends Danga::Socket and contains an IO::Socket::SSL
4# for the purpose of allowing non-blocking SSL in Perlbal.
5#
6# WARNING: this code will break IO::Socket::SSL if you use it in any plugins or
7# have custom Perlbal modifications that use it.  you will run into issues.  This
8# is because we override the close method to prevent premature closure of the socket,
9# so you will end up with the socket not closing properly.
10#
11# Copyright 2007, Mark Smith <mark@plogs.net>.
12#
13# This file is licensed under the same terms as Perl itself.
14
15package Perlbal::SocketSSL;
16
17use strict;
18use warnings;
19no  warnings qw(deprecated);
20
21use Danga::Socket 1.44;
22use IO::Socket::SSL 0.98;
23use Errno qw( EAGAIN );
24
25use base 'Danga::Socket';
26use fields qw( listener create_time );
27
28# magic IO::Socket::SSL crap to make it play nice with us
29{
30    no strict 'refs';
31    no warnings 'redefine';
32
33    # replace IO::Socket::SSL::close with our own code...
34    my $orig = *IO::Socket::SSL::close{CODE};
35    *IO::Socket::SSL::close = sub {
36        my $self = shift()
37            or return IO::Socket::SSL::_invalid_object();
38
39        # if we have args, close ourselves (second call!), else don't
40        if (exists ${*$self}->{__close_args}) {
41            $orig->($self, @{${*$self}->{__close_args}});
42        } else {
43            ${*$self}->{__close_args} = [ @_ ];
44            ${*$self}->{_danga_socket}->close('intercepted_ssl_close');
45        }
46    };
47}
48
49# called: CLASS->new( $sock, $tcplistener )
50sub new {
51    my Perlbal::SocketSSL $self = shift;
52    $self = fields::new( $self ) unless ref $self;
53
54    Perlbal::objctor($self);
55
56    my ($sock, $listener) = @_;
57
58    ${*$sock}->{_danga_socket} = $self;
59    $self->{listener} = $listener;
60    $self->{create_time} = time;
61
62    $self->SUPER::new($sock);
63
64    # TODO: would be good to have an overall timeout so that we can
65    # kill sockets that are open and just sitting there.  "ssl_handshake_timeout"
66    # or something like that...
67
68    return $self;
69}
70
71# this is nonblocking, it attempts to setup SSL and if it can't then
72# it returns whether it needs to read or write.  we then setup to wait
73# for the event it indicates and then wait.  when that event fires, we
74# call down again, and repeat the process until we have setup the
75# SSL connection.
76sub try_accept {
77    my Perlbal::SocketSSL $self = shift;
78
79    my $sock = $self->{sock}->accept_SSL;
80
81    if (defined $sock) {
82        # looks like we got it!  let's steal it from ourselves
83        # so Danga::Socket gives up on it and we can send
84        # it out to someone else.  (we discard the return value
85        # as we already have it in $sock)
86        #
87        # of course, life isn't as simple as that.  we have to do
88        # some trickery with the ordering here to ensure that we
89        # don't setup the new class until after the Perlbal::SocketSSL
90        # goes away according to Danga::Socket.
91        #
92        # if we don't do it this way, we get nasty errors because
93        # we (this object) still exists in the DescriptorMap of
94        # Danga::Socket when the new Perlbal::ClientXX tries to
95        # insert itself there.
96
97        # removes us from the active polling, closes up shop, but
98        # save our fd first!
99        my $fd = $self->{fd};
100        $self->steal_socket;
101
102        # finish blowing us away
103        my $ref = Danga::Socket->DescriptorMap();
104        delete $ref->{$fd};
105
106        # now stick the new one in
107        $self->{listener}->class_new_socket($sock);
108        return;
109    }
110
111    # nope, let's see if we can continue the process
112    if ($! == EAGAIN) {
113        if ($SSL_ERROR == SSL_WANT_READ) {
114            $self->watch_read(1);
115        } elsif ($SSL_ERROR == SSL_WANT_WRITE) {
116            $self->watch_write(1);
117        } else {
118            $self->close('invalid_ssl_state');
119        }
120    } else {
121        $self->close('invalid_ssl_error');
122    }
123}
124
125sub event_read {
126    $_[0]->watch_read(0);
127    $_[0]->try_accept;
128}
129
130sub event_write {
131    $_[0]->watch_write(0);
132    $_[0]->try_accept;
133}
134
1351;
Note: See TracBrowser for help on using the browser.