File Coverage

blib/lib/Perlbal/Socket.pm
Criterion Covered Total %
statement 148 181 81.7
branch 37 64 57.8
condition 2 12 16.6
subroutine 28 34 82.3
pod 3 19 15.7
total 218 310 70.3


line stmt bran cond sub pod time code
1             # Base class for all socket types
2             #
3             # Copyright 2004, Danga Interactive, Inc.
4             # Copyright 2005-2007, Six Apart, Ltd.
5              
6             package Perlbal::Socket;
7 22     22   147 use strict;
  22         51  
  22         806  
8 22     22   119 use warnings;
  22         49  
  22         629  
9 22     22   110 no warnings qw(deprecated);
  22         40  
  22         866  
10              
11 22     22   124 use Perlbal::HTTPHeaders;
  22         38  
  22         587  
12              
13 22     22   24927 use Sys::Syscall;
  22         81549  
  22         1350  
14 22     22   221 use POSIX ();
  22         51  
  22         721  
15              
16 22     22   25349 use Danga::Socket 1.44;
  22         1480124  
  22         982  
17 22     22   247 use base 'Danga::Socket';
  22         50  
  22         6047  
18              
19             use fields (
20 22         231 '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             'observed_ip_string', # if defined, contains the observed IP string of the peer
37             # we're serving. this is intended for hoding the value of
38             # the X-Forwarded-For and using it to govern ACLs.
39 22     22   151 );
  22         50  
40              
41 22     22   2709 use constant MAX_HTTP_HEADER_LENGTH => 102400; # 100k, arbitrary
  22         50  
  22         2639  
42              
43 22     22   127 use constant TRACK_OBJECTS => 0; # see @created_objects below
  22         48  
  22         1276  
44             if (TRACK_OBJECTS) {
45 22     22   131 use Scalar::Util qw(weaken isweak);
  22         47  
  22         12993  
46             }
47              
48             # kick-off one cleanup
49             _do_cleanup();
50              
51             our %state_changes = (); # { "objref" => [ state, state, state, ... ] }
52             our $last_callbacks = 0; # time last ran callbacks
53             our $callbacks = []; # [ [ time, subref ], [ time, subref ], ... ]
54              
55             # this one deserves its own section. we keep track of every Perlbal::Socket object
56             # created if the TRACK_OBJECTS constant is on. we use weakened references, though,
57             # so this list will hopefully contain mostly undefs. users can ask for this list if
58             # they want to work with it via the get_created_objects_ref function.
59             our @created_objects; # ( $ref, $ref, $ref ... )
60             our $last_co_cleanup = 0; # clean the list every few seconds
61              
62             sub get_statechange_ref {
63 0     0 0 0 return \%state_changes;
64             }
65              
66             sub get_created_objects_ref {
67 0     0 0 0 return \@created_objects;
68             }
69              
70             sub write_debuggy {
71 0     0 0 0 my $self = shift;
72              
73 0         0 my $cref = $_[0];
74 0 0       0 my $content = ref $cref eq "SCALAR" ? $$cref : $cref;
75 0 0       0 my $clen = defined $content ? length($content) : "undef";
76 0 0 0     0 $content = substr($content, 0, 17) . "..." if defined $content && $clen > 30;
77 0         0 my ($pkg, $filename, $line) = caller;
78 0         0 print "write($self, <$clen>\"$content\") from ($pkg, $filename, $line)\n" if Perlbal::DEBUG >= 4;
79 0         0 $self->SUPER::write(@_);
80             }
81              
82             if (Perlbal::DEBUG >= 4) {
83 22     22   627 no warnings 'redefine';
  22         180  
  22         5975914  
84             *write = \&write_debuggy;
85             }
86              
87             sub new {
88 151     151 1 407 my Perlbal::Socket $self = shift;
89 151 50       711 $self = fields::new( $self ) unless ref $self;
90              
91 151         803 Perlbal::objctor($self);
92              
93 151         1056 $self->SUPER::new( @_ );
94 151         15837 $self->{headers_string} = '';
95 151         450 $self->{state} = undef;
96 151         558 $self->{do_die} = 0;
97              
98 151         631 $self->{read_buf} = []; # arrayref of scalar refs of bufs read from client
99 151         455 $self->{read_ahead} = 0; # bytes sitting in read_buf
100 151         326 $self->{read_size} = 0; # total bytes read from client
101              
102 151         314 my $now = time;
103 151         433 $self->{alive_time} = $self->{create_time} = $now;
104              
105             # now put this item in the list of created objects
106 151         418 if (TRACK_OBJECTS) {
107             # clean the created objects list if necessary
108             if ($last_co_cleanup < $now - 5) {
109             # remove out undefs, because those are natural byproducts of weakening
110             # references
111             @created_objects = grep { $_ } @created_objects;
112              
113             # however, the grep turned our weak references back into strong ones, so
114             # we have to re-weaken them
115             weaken($_) foreach @created_objects;
116              
117             # we've cleaned up at this point
118             $last_co_cleanup = $now;
119             }
120              
121             # now add this one to our cleaned list and weaken it
122             push @created_objects, $self;
123             weaken($created_objects[-1]);
124             }
125              
126 151         555 return $self;
127             }
128              
129             # We need to maintain a cache of socket classes and what cleanup
130             # handler (if any) we perform on them. This is because classes based
131             # on Perlbal::Socket get one method, and Perlbal::SocketSSL gets a
132             # different handler. Caching this information is done rather than a
133             # static list because you can make new client classes in Perlbal.
134              
135             # If perl cached ->isa($class) call results we can make this shorter.
136             my %class_isa_cache;
137              
138             # A list of socket classes that we are interested in, listed in
139             # the order which they should be probed for.
140             my %socket_class_handlers = (
141             'Perlbal::Socket' => sub {
142             my Perlbal::Socket $v = shift;
143              
144             my $max_age = eval { $v->max_idle_time } || 0;
145             return unless $max_age;
146              
147             # We're inside the class where ->{alive_time} is defined, safe to use.
148             $v->close("perlbal_timeout")
149             if $v->{alive_time} < $Perlbal::tick_time - $max_age;
150             },
151             );
152              
153             sub set_socket_idle_handler {
154 22     22 0 60 my $class = shift;
155 22         54 my $handler_class = shift;
156 22         47 my $handler = shift;
157 22         101 $socket_class_handlers{$handler_class} = $handler;
158             }
159              
160             # FIXME: this doesn't scale in theory, but it might use less CPU in
161             # practice than using the Heap:: modules and manipulating the
162             # expirations all the time, thus doing things properly
163             # algorithmically. and this is definitely less work, so it's worth
164             # a try.
165              
166             sub _do_cleanup {
167 30     30   686 my $sf = Perlbal::Socket->get_sock_ref;
168              
169 30         422 SOCKET: while (my $k = each %$sf) {
170 51         120 my $sock = $sf->{$k};
171 51         808 my $sock_class = ref $sf->{$k};
172 51 100       228 if (exists $class_isa_cache{$sock_class}) {
173 40         150 my $handler = $class_isa_cache{$sock_class};
174 40 50       92 next unless defined $handler;
175 40         90 $handler->($sock);
176 40         310 next SOCKET;
177             }
178              
179             # No entry in the cache, find out what handler we should assign.
180 11         18 my $handler;
181 11         41 foreach my $check_class (keys %socket_class_handlers) {
182 14 100       151 next unless $sock->isa($check_class);
183 11         402 $handler = $socket_class_handlers{$check_class};
184 11         24 last;
185             }
186             # Outside the loop, so that we assign undef if none of the loop passes find anything.
187 11         74 $class_isa_cache{$sock_class} = $handler;
188             }
189              
190 30         247 Danga::Socket->AddTimer(5, \&_do_cleanup);
191             }
192              
193             # CLASS METHOD: given a delay (in seconds) and a subref, this will call
194             # that subref in AT LEAST delay seconds. if the subref returns 0, the
195             # callback is discarded, but if it returns a positive number, the callback
196             # is pushed onto the callback stack to be called again in at least that
197             # many seconds.
198             sub register_callback {
199             # adds a new callback to our list
200 23     23 0 57 my ($delay, $subref) = @_;
201 23         98 push @$callbacks, [ time + $delay, $subref ];
202 23         201 return 1;
203             }
204              
205             # CLASS METHOD: runs through the list of registered callbacks and executes
206             # any that need to be executed
207             # FIXME: this doesn't scale. need a heap.
208             sub run_callbacks {
209 830     830 0 2138 my $now = time;
210 830 100       4740 return if $last_callbacks == $now;
211 71         153 $last_callbacks = $now;
212              
213 71         209 my @destlist = ();
214 71         244 foreach my $ref (@$callbacks) {
215             # if their time is <= now...
216 85 100       316 if ($ref->[0] <= $now) {
217             # find out if they want to run again...
218 1         6 my $rv = $ref->[1]->();
219              
220             # and if they do, push onto list...
221 1 50 33     13 push @destlist, [ $rv + $now, $ref->[1] ]
222             if defined $rv && $rv > 0;
223             } else {
224             # not time for this one, just shove it
225 84         249 push @destlist, $ref;
226             }
227             }
228 71         276 $callbacks = \@destlist;
229             }
230              
231             # CLASS METHOD:
232             # default is for sockets to never time out. classes
233             # can override.
234 35     35 0 362 sub max_idle_time { 0; }
235              
236             # Socket: specific to HTTP socket types (only here and not in
237             # ClientHTTPBase because ClientManage wants it too)
238 247     247 0 1266 sub read_request_headers { read_headers($_[0], 0); }
239 151     151 0 618 sub read_response_headers { read_headers($_[0], 1); }
240             sub read_headers {
241 398     398 0 935 my Perlbal::Socket $self = shift;
242 398         716 my $is_res = shift;
243 398         797 print "Perlbal::Socket::read_headers($self) is_res=$is_res\n" if Perlbal::DEBUG >= 2;
244              
245 398         1246 my $sock = $self->{sock};
246              
247 398         1386 my $to_read = MAX_HTTP_HEADER_LENGTH - length($self->{headers_string});
248              
249 398         2265 my $bref = $self->read($to_read);
250 398 100       38255 unless (defined $bref) {
251             # client disconnected
252 7         17 print " client disconnected\n" if Perlbal::DEBUG >= 3;
253 7         47 return $self->close('remote_closure');
254             }
255              
256 391         3871 $self->{headers_string} .= $$bref;
257 391         2001 my $idx = index($self->{headers_string}, "\r\n\r\n");
258 391         749 my $delim_len = 4;
259              
260             # can't find the header delimiter? check for LFLF header delimiter.
261 391 100       9059 if ($idx == -1) {
262 30         304 $idx = index($self->{headers_string}, "\n\n");
263 30         75 $delim_len = 2;
264             }
265             # still can't find the header delimiter?
266 391 100       1591 if ($idx == -1) {
267              
268             # usually we get the headers all in one packet (one event), so
269             # if we get in here, that means it's more than likely the
270             # extra \r\n and if we clean it now (throw it away), then we
271             # can avoid a regexp later on.
272 30 50 33     282 if ($self->{ditch_leading_rn} && $self->{headers_string} eq "\r\n") {
273 30         750 print " throwing away leading \\r\\n\n" if Perlbal::DEBUG >= 3;
274 30         72 $self->{ditch_leading_rn} = 0;
275 30         69 $self->{headers_string} = "";
276 30         156 return 0;
277             }
278              
279 0         0 print " can't find end of headers\n" if Perlbal::DEBUG >= 3;
280 0 0       0 $self->close('long_headers')
281             if length($self->{headers_string}) >= MAX_HTTP_HEADER_LENGTH;
282 0         0 return 0;
283             }
284              
285 361         1372 my $hstr = substr($self->{headers_string}, 0, $idx);
286 361         662 print " pre-parsed headers: [$hstr]\n" if Perlbal::DEBUG >= 3;
287              
288 361         3325 my $extra = substr($self->{headers_string}, $idx+$delim_len);
289 361 100       1301 if (my $len = length($extra)) {
290 185         1376 print " pushing back $len bytes after header\n" if Perlbal::DEBUG >= 3;
291 185         1970 $self->push_back_read(\$extra);
292             }
293              
294             # some browsers send an extra \r\n after their POST bodies that isn't
295             # in their content-length. a base class can tell us when they're
296             # on their 2nd+ request after a POST and tell us to be ready for that
297             # condition, and we'll clean it up
298 361 100       4633 $hstr =~ s/^\r\n// if $self->{ditch_leading_rn};
299              
300 361 100       4017 unless (($is_res ? $self->{res_headers} : $self->{req_headers}) =
    100          
301             Perlbal::HTTPHeaders->new(\$hstr, $is_res)) {
302             # bogus headers? close connection.
303 1         2 print " bogus headers\n" if Perlbal::DEBUG >= 3;
304 1         7 return $self->close("parse_header_failure");
305             }
306              
307 360         1284 print " got valid headers\n" if Perlbal::DEBUG >= 3;
308              
309 360 100       1077 $Perlbal::reqs++ unless $is_res;
310 360         973 $self->{ditch_leading_rn} = 0;
311              
312 360 100       24738 return $is_res ? $self->{res_headers} : $self->{req_headers};
313             }
314              
315             ### METHOD: drain_read_buf_to( $destination )
316             ### Write read-buffered data (if any) from the receiving object to the
317             ### I object.
318             sub drain_read_buf_to {
319 106     106 0 283 my ($self, $dest) = @_;
320 106 100       620 return unless $self->{read_ahead};
321              
322 33         73 while (my $bref = shift @{$self->{read_buf}}) {
  71         507  
323 38         47 print "draining readbuf from $self to $dest: [$$bref]\n" if Perlbal::DEBUG >= 3;
324 38         133 $dest->write($bref);
325 38         209 $self->{read_ahead} -= length($$bref);
326             }
327             }
328              
329             ### METHOD: die_gracefully()
330             ### By default, if we're in persist_wait state, close. Else, ignore. Children
331             ### can override if they want to do some other processing.
332             sub die_gracefully {
333 0     0 0 0 my Perlbal::Socket $self = $_[0];
334 0 0 0     0 if (defined $self->state && $self->state eq 'persist_wait') {
335 0         0 $self->close('graceful_shutdown');
336             }
337 0         0 $self->{do_die} = 1;
338             }
339              
340             ### METHOD: write()
341             ### Overridden from Danga::Socket to update our alive time on successful writes
342             ### Stops sockets from being closed on long-running write operations
343             sub write {
344 1241     1241 1 2169 my $self = shift;
345              
346 1241         2027 my $ret;
347 1241 100       8062 if ($ret = $self->SUPER::write(@_)) {
348             # Mark this socket alive so we don't time out
349 1215         136631 $self->{alive_time} = $Perlbal::tick_time;
350             }
351              
352 1241         9491 return $ret;
353             }
354              
355             ### METHOD: close()
356             ### Set our state when we get closed.
357             sub close {
358 87     87 1 185 my Perlbal::Socket $self = $_[0];
359 87         261 $self->state('closed');
360 87         531 return $self->SUPER::close($_[1]);
361             }
362              
363             ### METHOD: state()
364             ### If you pass a parameter, sets the state, else returns it.
365             sub state {
366 1781     1781 0 3444 my Perlbal::Socket $self = shift;
367 1781 100       4895 return $self->{state} unless @_;
368              
369 1774         2257 push @{$state_changes{"$self"} ||= []}, $_[0] if Perlbal::TRACK_STATES;
370 1774         10035 return $self->{state} = $_[0];
371             }
372              
373             sub observed_ip_string {
374 0     0 0 0 my Perlbal::Socket $self = shift;
375              
376 0 0       0 if (@_) {
377 0         0 return $self->{observed_ip_string} = $_[0];
378             } else {
379 0         0 return $self->{observed_ip_string};
380             }
381             }
382              
383             sub as_string_html {
384 0     0 0 0 my Perlbal::Socket $self = shift;
385 0         0 return $self->SUPER::as_string;
386             }
387              
388             sub DESTROY {
389 217     217   6549 my Perlbal::Socket $self = shift;
390 217         424 delete $state_changes{"$self"} if Perlbal::TRACK_STATES;
391 217         2344 Perlbal::objdtor($self);
392             }
393              
394             # package function (not a method). returns bytes sent, or -1 on error.
395             our $sf_defined = Sys::Syscall::sendfile_defined;
396             our $max_sf_readwrite = 128 * 1024;
397             sub sendfile {
398 76     76 0 399 my ($sfd, $fd, $bytes) = @_;
399 76 50       554 return Sys::Syscall::sendfile($sfd, $fd, $bytes) if $sf_defined;
400              
401             # no support for sendfile. ghetto version: read and write.
402 0           my $buf;
403 0 0         $bytes = $max_sf_readwrite if $bytes > $max_sf_readwrite;
404              
405 0           my $rv = POSIX::read($fd, $buf, $bytes);
406 0 0         return -1 unless defined $rv;
407 0 0         return -1 unless $rv == $bytes;
408              
409 0           my $wv = POSIX::write($sfd, $buf, $rv);
410 0 0         return -1 unless defined $wv;
411              
412 0 0         if (my $over_read = $rv - $wv) {
413 0           POSIX::lseek($fd, -$over_read, &POSIX::SEEK_CUR);
414             }
415              
416 0           return $wv;
417             }
418              
419             1;
420              
421              
422             # Local Variables:
423             # mode: perl
424             # c-basic-indent: 4
425             # indent-tabs-mode: nil
426             # End: