root/trunk/lib/Perlbal/Socket.pm @ 707

Revision 707, 10.9 kB (checked in by marksmith, 2 years ago)

-- make persist_client_timeout service tunable apply to the max_idle_time

value used to kill sockets that are idle

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1# Base class for all socket types
2#
3# Copyright 2004, Danga Interactive, Inc.
4# Copyright 2005-2007, Six Apart, Ltd.
5
6package Perlbal::Socket;
7use strict;
8use warnings;
9no  warnings qw(deprecated);
10
11use Perlbal::HTTPHeaders;
12
13use Sys::Syscall;
14use POSIX ();
15
16use Danga::Socket 1.44;
17use base 'Danga::Socket';
18
19use fields (
20            'headers_string',  # headers as they're being read
21
22            'req_headers',     # the final Perlbal::HTTPHeaders object inbound
23            'res_headers',     # response headers outbound (Perlbal::HTTPHeaders object)
24
25            'create_time',     # creation time
26            'alive_time',      # last time noted alive
27            'state',           # general purpose state; used by descendants.
28            'do_die',          # if on, die and do no further requests
29
30            'read_buf',        # arrayref of scalarref read from client
31            'read_ahead',      # bytes sitting in read_buf
32            'read_size',       # total bytes read from client, ever
33
34            'ditch_leading_rn', # if true, the next header parsing will ignore a leading \r\n
35            );
36
37use constant MAX_HTTP_HEADER_LENGTH => 102400;  # 100k, arbitrary
38
39use constant TRACK_OBJECTS => 0;            # see @created_objects below
40if (TRACK_OBJECTS) {
41    use Scalar::Util qw(weaken isweak);
42}
43
44# kick-off one cleanup
45_do_cleanup();
46
47our %state_changes = (); # { "objref" => [ state, state, state, ... ] }
48our $last_callbacks = 0; # time last ran callbacks
49our $callbacks = []; # [ [ time, subref ], [ time, subref ], ... ]
50
51# this one deserves its own section.  we keep track of every Perlbal::Socket object
52# created if the TRACK_OBJECTS constant is on.  we use weakened references, though,
53# so this list will hopefully contain mostly undefs.  users can ask for this list if
54# they want to work with it via the get_created_objects_ref function.
55our @created_objects; # ( $ref, $ref, $ref ... )
56our $last_co_cleanup = 0; # clean the list every few seconds
57
58sub get_statechange_ref {
59    return \%state_changes;
60}
61
62sub get_created_objects_ref {
63    return \@created_objects;
64}
65
66sub write_debuggy {
67    my $self = shift;
68
69    my $cref = $_[0];
70    my $content = ref $cref eq "SCALAR" ? $$cref : $cref;
71    my $clen = defined $content ? length($content) : "undef";
72    $content = substr($content, 0, 17) . "..." if defined $content && $clen > 30;
73    my ($pkg, $filename, $line) = caller;
74    print "write($self, <$clen>\"$content\") from ($pkg, $filename, $line)\n" if Perlbal::DEBUG >= 4;
75    $self->SUPER::write(@_);
76}
77
78if (Perlbal::DEBUG >= 4) {
79    *write = \&write_debuggy;
80}
81
82sub new {
83    my Perlbal::Socket $self = shift;
84    $self = fields::new( $self ) unless ref $self;
85
86    Perlbal::objctor($self);
87
88    $self->SUPER::new( @_ );
89    $self->{headers_string} = '';
90    $self->{state} = undef;
91    $self->{do_die} = 0;
92
93    $self->{read_buf} = [];        # arrayref of scalar refs of bufs read from client
94    $self->{read_ahead} = 0;       # bytes sitting in read_buf
95    $self->{read_size} = 0;        # total bytes read from client
96
97    my $now = time;
98    $self->{alive_time} = $self->{create_time} = $now;
99
100    # now put this item in the list of created objects
101    if (TRACK_OBJECTS) {
102        # clean the created objects list if necessary
103        if ($last_co_cleanup < $now - 5) {
104            # remove out undefs, because those are natural byproducts of weakening
105            # references
106            @created_objects = grep { $_ } @created_objects;
107
108            # however, the grep turned our weak references back into strong ones, so
109            # we have to reweaken them
110            weaken($_) foreach @created_objects;
111
112            # we've cleaned up at this point
113            $last_co_cleanup = $now;
114        }
115
116        # now add this one to our cleaned list and weaken it
117        push @created_objects, $self;
118        weaken($created_objects[-1]);
119    }
120
121    return $self;
122}
123
124# FIXME: this doesn't scale in theory, but it might use less CPU in
125# practice than using the Heap:: modules and manipulating the
126# expirations all the time, thus doing things properly
127# algorithmically.  and this is definitely less work, so it's worth
128# a try.
129sub _do_cleanup {
130    my $sf = Perlbal::Socket->get_sock_ref;
131
132    my $now = time;
133
134    my @to_close;
135    while (my $k = each %$sf) {
136        my Perlbal::Socket $v = $sf->{$k};
137
138        my $max_age = eval { $v->max_idle_time } || 0;
139        next unless $max_age;
140
141        if ($v->{alive_time} < $now - $max_age) {
142            push @to_close, $v;
143        }
144    }
145
146    foreach my $sock (@to_close) {
147        $sock->close("perlbal_timeout")
148    }
149
150    Danga::Socket->AddTimer(5, \&_do_cleanup);
151}
152
153# CLASS METHOD: given a delay (in seconds) and a subref, this will call
154# that subref in AT LEAST delay seconds. if the subref returns 0, the
155# callback is discarded, but if it returns a positive number, the callback
156# is pushed onto the callback stack to be called again in at least that
157# many seconds.
158sub register_callback {
159    # adds a new callback to our list
160    my ($delay, $subref) = @_;
161    push @$callbacks, [ time + $delay, $subref ];
162    return 1;
163}
164
165# CLASS METHOD: runs through the list of registered callbacks and executes
166# any that need to be executed
167# FIXME: this doesn't scale.  need a heap.
168sub run_callbacks {
169    my $now = time;
170    return if $last_callbacks == $now;
171    $last_callbacks = $now;
172
173    my @destlist = ();
174    foreach my $ref (@$callbacks) {
175        # if their time is <= now...
176        if ($ref->[0] <= $now) {
177            # find out if they want to run again...
178            my $rv = $ref->[1]->();
179
180            # and if they do, push onto list...
181            push @destlist, [ $rv + $now, $ref->[1] ]
182                if defined $rv && $rv > 0;
183        } else {
184            # not time for this one, just shove it
185            push @destlist, $ref;
186        }
187    }
188    $callbacks = \@destlist;
189}
190
191# CLASS METHOD:
192# default is for sockets to never time out.  classes
193# can override.
194sub max_idle_time { 0; }
195
196# Socket: specific to HTTP socket types (only here and not in
197# ClientHTTPBase because ClientManage wants it too)
198sub read_request_headers  { read_headers($_[0], 0); }
199sub read_response_headers { read_headers($_[0], 1); }
200sub read_headers {
201    my Perlbal::Socket $self = shift;
202    my $is_res = shift;
203    print "Perlbal::Socket::read_headers($self) is_res=$is_res\n" if Perlbal::DEBUG >= 2;
204
205    my $sock = $self->{sock};
206
207    my $to_read = MAX_HTTP_HEADER_LENGTH - length($self->{headers_string});
208
209    my $bref = $self->read($to_read);
210    unless (defined $bref) {
211        # client disconnected
212        print "  client disconnected\n" if Perlbal::DEBUG >= 3;
213        return $self->close('remote_closure');
214    }
215
216    $self->{headers_string} .= $$bref;
217    my $idx = index($self->{headers_string}, "\r\n\r\n");
218
219    # can't find the header delimiter?
220    if ($idx == -1) {
221
222        # usually we get the headers all in one packet (one event), so
223        # if we get in here, that means it's more than likely the
224        # extra \r\n and if we clean it now (throw it away), then we
225        # can avoid a regexp later on.
226        if ($self->{ditch_leading_rn} && $self->{headers_string} eq "\r\n") {
227            print "  throwing away leading \\r\\n\n" if Perlbal::DEBUG >= 3;
228            $self->{ditch_leading_rn} = 0;
229            $self->{headers_string}   = "";
230            return 0;
231        }
232
233        print "  can't find end of headers\n" if Perlbal::DEBUG >= 3;
234        $self->close('long_headers')
235            if length($self->{headers_string}) >= MAX_HTTP_HEADER_LENGTH;
236        return 0;
237    }
238
239    my $hstr = substr($self->{headers_string}, 0, $idx);
240    print "  pre-parsed headers: [$hstr]\n" if Perlbal::DEBUG >= 3;
241
242    my $extra = substr($self->{headers_string}, $idx+4);
243    if (my $len = length($extra)) {
244        print "  pushing back $len bytes after header\n" if Perlbal::DEBUG >= 3;
245        $self->push_back_read(\$extra);
246    }
247
248    # some browsers send an extra \r\n after their POST bodies that isn't
249    # in their content-length.  a base class can tell us when they're
250    # on their 2nd+ request after a POST and tell us to be ready for that
251    # condition, and we'll clean it up
252    $hstr =~ s/^\r\n// if $self->{ditch_leading_rn};
253
254    unless (($is_res ? $self->{res_headers} : $self->{req_headers}) =
255                Perlbal::HTTPHeaders->new(\$hstr, $is_res)) {
256        # bogus headers?  close connection.
257        print "  bogus headers\n" if Perlbal::DEBUG >= 3;
258        return $self->close("parse_header_failure");
259    }
260
261    print "  got valid headers\n" if Perlbal::DEBUG >= 3;
262
263    $Perlbal::reqs++ unless $is_res;
264    $self->{ditch_leading_rn} = 0;
265
266    return $is_res ? $self->{res_headers} : $self->{req_headers};
267}
268
269### METHOD: drain_read_buf_to( $destination )
270### Write read-buffered data (if any) from the receiving object to the
271### I<destination> object.
272sub drain_read_buf_to {
273    my ($self, $dest) = @_;
274    return unless $self->{read_ahead};
275
276    while (my $bref = shift @{$self->{read_buf}}) {
277        print "draining readbuf from $self to $dest: [$$bref]\n" if Perlbal::DEBUG >= 3;
278        $dest->write($bref);
279        $self->{read_ahead} -= length($$bref);
280    }
281}
282
283### METHOD: die_gracefully()
284### By default, if we're in persist_wait state, close.  Else, ignore.  Children
285### can override if they want to do some other processing.
286sub die_gracefully {
287    my Perlbal::Socket $self = $_[0];
288    if ($self->state eq 'persist_wait') {
289        $self->close('graceful_shutdown');
290    }
291    $self->{do_die} = 1;
292}
293
294### METHOD: close()
295### Set our state when we get closed.
296sub close {
297    my Perlbal::Socket $self = $_[0];
298    $self->state('closed');
299    return $self->SUPER::close($_[1]);
300}
301
302### METHOD: state()
303### If you pass a parameter, sets the state, else returns it.
304sub state {
305    my Perlbal::Socket $self = shift;
306    return $self->{state} unless @_;
307
308    push @{$state_changes{"$self"} ||= []}, $_[0] if Perlbal::TRACK_STATES;
309    return $self->{state} = $_[0];
310}
311
312sub as_string_html {
313    my Perlbal::Socket $self = shift;
314    return $self->SUPER::as_string;
315}
316
317sub DESTROY {
318    my Perlbal::Socket $self = shift;
319    delete $state_changes{"$self"} if Perlbal::TRACK_STATES;
320    Perlbal::objdtor($self);
321}
322
323# package function (not a method).  returns bytes sent, or -1 on error.
324our $sf_defined = Sys::Syscall::sendfile_defined;
325our $max_sf_readwrite = 128 * 1024;
326sub sendfile {
327    my ($sfd, $fd, $bytes) = @_;
328    return Sys::Syscall::sendfile($sfd, $fd, $bytes) if $sf_defined;
329
330    # no support for sendfile.  ghetto version:  read and write.
331    my $buf;
332    $bytes = $max_sf_readwrite if $bytes > $max_sf_readwrite;
333
334    my $rv = POSIX::read($fd, $buf, $bytes);
335    return -1 unless defined $rv;
336    return -1 unless $rv == $bytes;
337
338    my $wv = POSIX::write($sfd, $buf, $rv);
339    return -1 unless defined $wv;
340
341    if (my $over_read = $rv - $wv) {
342        POSIX::lseek($fd, -$over_read, &POSIX::SEEK_CUR);
343    }
344
345    return $wv;
346}
347
3481;
349
350
351# Local Variables:
352# mode: perl
353# c-basic-indent: 4
354# indent-tabs-mode: nil
355# End:
Note: See TracBrowser for help on using the browser.