File Coverage

blib/lib/Starwoman/Server.pm
Criterion Covered Total %
statement 254 332 76.5
branch 85 160 53.1
condition 25 64 39.0
subroutine 38 46 82.6
pod 7 10 70.0
total 409 612 66.8


line stmt bran cond sub pod time code
1             package Starwoman::Server;
2             $Starwoman::Server::VERSION = '0.001';
3 50     50   67126 use strict;
  50         195  
  50         2929  
4 50     50   632 use base 'Net::Server::PreFork';
  50         201  
  50         35548  
5              
6 50     50   1473540 use Data::Dump qw(dump);
  50         250706  
  50         3346  
7 50     50   382 use Socket qw(IPPROTO_TCP TCP_NODELAY);
  50         431  
  50         4397  
8 50     50   393 use IO::Socket qw(:crlf);
  50         180  
  50         972  
9 50     50   36627 use HTTP::Parser::XS qw(parse_http_request);
  50         60967  
  50         4158  
10 50     50   834 use HTTP::Status qw(status_message);
  50         4452  
  50         20588  
11 50     50   25769 use HTTP::Date qw(time2str);
  50         190403  
  50         3334  
12 50     50   558 use POSIX qw(EINTR EPIPE ECONNRESET);
  50         184  
  50         500  
13 50     50   7089 use Symbol;
  50         125  
  50         6648  
14 50     50   922 use Try::Tiny;
  50         2349  
  50         5181  
15              
16 50     50   1465 use Plack::Util;
  50         3216  
  50         1616  
17 50     50   21083 use Plack::TempBuffer;
  50         390882  
  50         1883  
18              
19 50   50 50   331 use constant DEBUG => $ENV{STARWOMAN_DEBUG} || 0;
  50         94  
  50         3806  
20 50     50   295 use constant CHUNKSIZE => 64 * 1024;
  50         113  
  50         3297  
21              
22 50     50   374 my $null_io = do { open my $io, "<", \""; $io };
  50         82  
  50         1400  
23              
24 50     50   325 use Net::Server::SIG qw(register_sig);
  50         76  
  50         47602  
25              
26             # Override Net::Server's HUP handling - just restart all the workers and that's about it
27             sub sig_hup {
28 0     0 0 0 my $self = shift;
29 0         0 $self->hup_children;
30             }
31              
32             sub run {
33 49     49 1 849 my($self, $app, $options) = @_;
34              
35 49         327 $self->{app} = $app;
36 49         116 $self->{options} = $options;
37              
38 49         104 my %extra = ();
39              
40 49 50       190 if ($options->{net_server_args}) {
41 0         0 %extra = %{ $options->{net_server_args} };
  0         0  
42             }
43              
44 49 50       277 if ( $options->{pid} ) {
45 0         0 $extra{pid_file} = $options->{pid};
46             }
47 49 50       172 if ( $options->{daemonize} ) {
48 0         0 $extra{setsid} = $extra{background} = 1;
49             }
50 49 50       179 if ( $options->{error_log} ) {
51 0         0 $extra{log_file} = $options->{error_log};
52             }
53 49         111 if ( DEBUG ) {
54             $extra{log_level} = 4;
55             }
56 49 50       147 if ( $options->{ssl_cert} ) {
57 0         0 $extra{SSL_cert_file} = $options->{ssl_cert};
58             }
59 49 50       166 if ( $options->{ssl_key} ) {
60 0         0 $extra{SSL_key_file} = $options->{ssl_key};
61             }
62 49 50       166 if (! exists $options->{keepalive}) {
63 49         118 $options->{keepalive} = 1;
64             }
65 49 50       122 if (! exists $options->{keepalive_timeout}) {
66 49         86 $options->{keepalive_timeout} = 1;
67             }
68 49 50       141 if (! exists $options->{read_timeout}) {
69 49         92 $options->{read_timeout} = 5;
70             }
71 49 50       165 if (! exists $options->{proctitle}) {
72 49         80 $options->{proctitle} = 1;
73             }
74              
75 49         73 my @port;
76 49 50       116 for my $listen (@{$options->{listen} || [ "$options->{host}:$options->{port}" ]}) {
  49         405  
77 49         152 my %listen;
78 49 50       264 if ($listen =~ /:/) {
79 49         190 my($h, $p, $opt) = split /:/, $listen, 3;
80 49 50       233 $listen{host} = $h if $h;
81 49         122 $listen{port} = $p;
82 49 50       215 $listen{proto} = 'ssl' if 'ssl' eq lc $opt;
83             } else {
84 0         0 %listen = (
85             host => 'localhost',
86             port => $listen,
87             proto => 'unix',
88             );
89             }
90 49         159 push @port, \%listen;
91             }
92              
93 49   50     282 my $workers = $options->{workers} || 5;
94 49         142 local @ARGV = ();
95              
96             $self->SUPER::run(
97             port => \@port,
98             host => '*', # default host
99             proto => $options->{ssl} ? 'ssl' : 'tcp', # default proto
100             serialize => ( $^O =~ m!(linux|darwin|bsd|cygwin)$! ) ? 'none' : 'flock',
101             min_servers => $options->{min_servers} || $workers,
102             min_spare_servers => $options->{min_spare_servers} || $workers - 1,
103             max_spare_servers => $options->{max_spare_servers} || $workers - 1,
104             max_servers => $options->{max_servers} || $workers,
105             max_requests => $options->{max_requests} || 1000,
106             user => $options->{user} || $>,
107             group => $options->{group} || $),
108 49 50 33     3135 listen => $options->{backlog} || 1024,
    50 33        
      33        
      33        
      50        
      33        
      33        
      50        
109             check_for_waiting => 1,
110             no_client_stdout => 1,
111             %extra
112             );
113             }
114              
115             sub pre_loop_hook {
116 49     49 1 266452 my $self = shift;
117              
118 49         179 my $port = $self->{server}->{port}->[0];
119             my $proto = $port->{proto} eq 'ssl' ? 'https' :
120 49 50       264 $port->{proto} eq 'unix' ? 'unix' :
    50          
121             'http';
122              
123             $self->{options}{server_ready}->({
124             host => $port->{host},
125             port => $port->{port},
126             proto => $proto,
127             server_software => 'Starwoman',
128 49 50       184 }) if $self->{options}{server_ready};
129              
130             register_sig(
131 0     0   0 TTIN => sub { $self->{server}->{$_}++ for qw( min_servers max_servers ) },
132 0     0   0 TTOU => sub { $self->{server}->{$_}-- for qw( min_servers max_servers ) },
133 0     0   0 QUIT => sub { $self->server_close(1) },
134 49         469 );
135             }
136              
137             sub server_close {
138 6     6 1 1982732 my($self, $quit) = @_;
139              
140 6 50       130 if ($quit) {
141 0         0 $self->log(2, $self->log_time . " Received QUIT. Running a graceful shutdown\n");
142 0         0 $self->{server}->{$_} = 0 for qw( min_servers max_servers );
143 0         0 $self->hup_children;
144 0         0 while (1) {
145 0         0 Net::Server::SIG::check_sigs();
146 0         0 $self->coordinate_children;
147 0 0       0 last if !keys %{$self->{server}{children}};
  0         0  
148 0         0 sleep 1;
149             }
150 0         0 $self->log(2, $self->log_time . " Worker processes cleaned up\n");
151             }
152              
153 6         270 $self->SUPER::server_close();
154             }
155              
156             sub run_parent {
157 19     19 0 99876 my $self = shift;
158 19 50       1615 $0 = "starwoman master " . join(" ", @{$self->{options}{argv} || []})
159 19 50       640 if $self->{options}{proctitle};
160 50     50   395 no warnings 'redefine';
  50         125  
  50         84788  
161             local *Net::Server::PreFork::register_sig = sub {
162 19     19   4023 my %args = @_;
163 19         132 delete $args{QUIT};
164 19         492 Net::Server::SIG::register_sig(%args);
165 19         2152 };
166 19         147413 $self->SUPER::run_parent(@_);
167             }
168              
169             # The below methods run in the child process
170              
171             sub child_init_hook {
172 43     43 1 10773665 my $self = shift;
173 43         9422 srand();
174 43 50       1262 if ($self->{options}->{psgi_app_builder}) {
175 0         0 DEBUG && warn "[$$] Initializing the PSGI app\n";
176             # try/catch based on https://github.com/miyagawa/Starman/pull/117
177             try {
178 0     0   0 $self->{app} = $self->{options}->{psgi_app_builder}->();
179             } catch {
180 0 0   0   0 die $_ unless $_ =~ /Error while loading/;
181 0         0 DEBUG && warn "[$$] Failed to load the app, will try again on request: $_\n";
182 0         0 };
183             }
184 43 50       4436 $0 = "starwoman worker " . join(" ", @{$self->{options}{argv} || []})
185 43 50       1148 if $self->{options}{proctitle};
186              
187             }
188              
189             sub post_accept_hook {
190 51     51 1 886791 my $self = shift;
191              
192             $self->{client} = {
193 51         1170 headerbuf => '',
194             inputbuf => '',
195             keepalive => 1,
196             };
197             }
198              
199             sub dispatch_request {
200 45     45 0 106 my ($self, $env) = @_;
201              
202             # Run PSGI apps
203 45         1043 my $res = Plack::Util::run_app($self->{app}, $env);
204              
205 45 100       2233 if (ref $res eq 'CODE') {
206 2     2   10 $res->(sub { $self->_finalize_response($env, $_[0]) });
  2         52  
207             } else {
208 43         377 $self->_finalize_response($env, $res);
209             }
210             }
211              
212             sub process_request {
213 51     51 1 3532 my $self = shift;
214 51         385 my $conn = $self->{server}->{client};
215              
216 51 50       52009 if ($conn->NS_proto eq 'TCP') {
217 51 50       1359 setsockopt($conn, IPPROTO_TCP, TCP_NODELAY, 1)
218             or die $!;
219             }
220              
221 51         477 while ( $self->{client}->{keepalive} ) {
222 78 50       20177 last if !$conn->connected;
223              
224             # Read until we see all headers
225 78 100       1831 last if !$self->_read_headers;
226              
227             my $env = {
228             REMOTE_ADDR => $self->{server}->{peeraddr},
229             REMOTE_HOST => $self->{server}->{peerhost} || $self->{server}->{peeraddr},
230             REMOTE_PORT => $self->{server}->{peerport} || 0,
231             SERVER_NAME => $self->{server}->{sockaddr} || 0, # XXX: needs to be resolved?
232 45 50 33     44482 SERVER_PORT => $self->{server}->{sockport} || 0,
      50        
      50        
      50        
233             SCRIPT_NAME => '',
234             'psgi.version' => [ 1, 1 ],
235             'psgi.errors' => *STDERR,
236             'psgi.url_scheme' => ($conn->NS_proto eq 'SSL' ? 'https' : 'http'),
237             'psgi.nonblocking' => Plack::Util::FALSE,
238             'psgi.streaming' => Plack::Util::TRUE,
239             'psgi.run_once' => Plack::Util::FALSE,
240             'psgi.multithread' => Plack::Util::FALSE,
241             'psgi.multiprocess' => Plack::Util::TRUE,
242             'psgix.io' => $conn,
243             'psgix.input.buffered' => Plack::Util::TRUE,
244             'psgix.harakiri' => Plack::Util::TRUE,
245             };
246              
247             # Parse headers
248 45         23129 my $reqlen = parse_http_request(delete $self->{client}->{headerbuf}, $env);
249 45 50       204 if ( $reqlen == -1 ) {
250             # Bad request
251 0         0 DEBUG && warn "[$$] Bad request\n";
252 0         0 $self->_http_error(400, { SERVER_PROTOCOL => "HTTP/1.0" });
253 0         0 last;
254             }
255              
256             # Initialize PSGI environment
257             # Determine whether we will keep the connection open after the request
258 45         169 my $connection = delete $env->{HTTP_CONNECTION};
259 45         201 my $proto = $env->{SERVER_PROTOCOL};
260 45 50 33     750 if ( $proto && $proto eq 'HTTP/1.0' ) {
    50 33        
261 0 0 0     0 if ( $connection && $connection =~ /^keep-alive$/i ) {
262             # Keep-alive only with explicit header in HTTP/1.0
263 0         0 $self->{client}->{keepalive} = 1;
264             }
265             else {
266 0         0 $self->{client}->{keepalive} = 0;
267             }
268             }
269             elsif ( $proto && $proto eq 'HTTP/1.1' ) {
270 45 50 33     1899 if ( $connection && $connection =~ /^close$/i ) {
271 0         0 $self->{client}->{keepalive} = 0;
272             }
273             else {
274             # Keep-alive assumed in HTTP/1.1
275 45         155 $self->{client}->{keepalive} = 1;
276             }
277              
278             # Do we need to send 100 Continue?
279 45 50       204 if ( $env->{HTTP_EXPECT} ) {
280 0 0       0 if ( lc $env->{HTTP_EXPECT} eq '100-continue' ) {
281 0         0 _syswrite($conn, \('HTTP/1.1 100 Continue' . $CRLF . $CRLF));
282 0         0 DEBUG && warn "[$$] Sent 100 Continue response\n";
283             }
284             else {
285 0         0 DEBUG && warn "[$$] Invalid Expect header, returning 417\n";
286 0         0 $self->_http_error( 417, $env );
287 0         0 last;
288             }
289             }
290              
291 45 50       238 unless ($env->{HTTP_HOST}) {
292             # No host, bad request
293 0         0 DEBUG && warn "[$$] Bad request, HTTP/1.1 without Host header\n";
294 0         0 $self->_http_error( 400, $env );
295 0         0 last;
296             }
297             }
298              
299 45 50       236 unless ($self->{options}->{keepalive}) {
300 0         0 DEBUG && warn "[$$] keep-alive is disabled. Closing the connection after this request\n";
301 0         0 $self->{client}->{keepalive} = 0;
302             }
303              
304 45         597 $self->_prepare_env($env);
305              
306 45         363 $self->dispatch_request($env);
307              
308 45         96 DEBUG && warn "[$$] Request done\n";
309              
310 45 100       326 if ( $self->{client}->{keepalive} ) {
311             # If we still have data in the input buffer it may be a pipelined request
312 27 50       79 if ( $self->{client}->{inputbuf} ) {
313 0 0       0 if ( $self->{client}->{inputbuf} =~ /^(?:GET|HEAD)/ ) {
314 0         0 if ( DEBUG ) {
315             warn "Pipelined GET/HEAD request in input buffer: "
316             . dump( $self->{client}->{inputbuf} ) . "\n";
317             }
318              
319             # Continue processing the input buffer
320 0         0 next;
321             }
322             else {
323             # Input buffer just has junk, clear it
324 0         0 if ( DEBUG ) {
325             warn "Clearing junk from input buffer: "
326             . dump( $self->{client}->{inputbuf} ) . "\n";
327             }
328              
329 0         0 $self->{client}->{inputbuf} = '';
330             }
331             }
332              
333 27         45 DEBUG && warn "[$$] Waiting on previous connection for keep-alive request...\n";
334              
335 27         482 my $sel = IO::Select->new($conn);
336 27 50       2138 last unless $sel->can_read($self->{options}->{keepalive_timeout});
337             }
338             }
339              
340 51         190 DEBUG && warn "[$$] Closing connection\n";
341             }
342              
343             sub _read_headers {
344 78     78   325 my $self = shift;
345              
346 78         234 eval {
347 78     0   2320 local $SIG{ALRM} = sub { die "Timed out\n"; };
  0         0  
348              
349 78         972 alarm( $self->{options}->{read_timeout} );
350              
351 78         336 while (1) {
352             # Do we have a full header in the buffer?
353             # This is before sysread so we don't read if we have a pipelined request
354             # waiting in the buffer
355 123 100 100     4060 last if defined $self->{client}->{inputbuf} && $self->{client}->{inputbuf} =~ /$CR?$LF$CR?$LF/s;
356              
357             # If not, read some data
358 78         5335 my $read = sysread $self->{server}->{client}, my $buf, CHUNKSIZE;
359              
360 78 100 66     1058 if ( !defined $read || $read == 0 ) {
361 33         769 die "Read error: $!\n";
362             }
363              
364 45         188 if ( DEBUG ) {
365             warn "[$$] Read $read bytes: " . dump($buf) . "\n";
366             }
367              
368 45         227 $self->{client}->{inputbuf} .= $buf;
369             }
370             };
371              
372 78         544 alarm(0);
373              
374 78 100       438 if ( $@ ) {
375 33 50       272 if ( $@ =~ /Timed out/ ) {
376 0         0 DEBUG && warn "[$$] Client connection timed out\n";
377 0         0 return;
378             }
379              
380 33 50       287 if ( $@ =~ /Read error/ ) {
381 33         98 DEBUG && warn "[$$] Read error: $!\n";
382 33         158 return;
383             }
384             }
385              
386             # Pull out the complete header into a new buffer
387 45         190 $self->{client}->{headerbuf} = $self->{client}->{inputbuf};
388              
389             # Save any left-over data, possibly body data or pipelined requests
390 45         1267 $self->{client}->{inputbuf} =~ s/.*?$CR?$LF$CR?$LF//s;
391              
392 45         363 return 1;
393             }
394              
395             sub _http_error {
396 0     0   0 my ( $self, $code, $env ) = @_;
397              
398 0   0     0 my $status = $code || 500;
399 0         0 my $msg = status_message($status);
400              
401 0         0 my $res = [
402             $status,
403             [ 'Content-Type' => 'text/plain', 'Content-Length' => length($msg) ],
404             [ $msg ],
405             ];
406              
407 0         0 $self->{client}->{keepalive} = 0;
408 0         0 $self->_finalize_response($env, $res);
409             }
410              
411             sub _prepare_env {
412 46     46   871 my($self, $env) = @_;
413              
414             my $get_chunk = sub {
415 6 100   6   19 if ($self->{client}->{inputbuf}) {
416 1         3 my $chunk = delete $self->{client}->{inputbuf};
417 1         5 return ($chunk, length $chunk);
418             }
419 5         50 my $read = sysread $self->{server}->{client}, my($chunk), CHUNKSIZE;
420 5         62 return ($chunk, $read);
421 46         725 };
422              
423 50     50   457 my $chunked = do { no warnings; lc delete $env->{HTTP_TRANSFER_ENCODING} eq 'chunked' };
  50         101  
  50         62741  
  46         340  
  46         261  
424              
425 46 50       850 if (my $cl = $env->{CONTENT_LENGTH}) {
    100          
426 0         0 my $buf = Plack::TempBuffer->new($cl);
427 0         0 while ($cl > 0) {
428 0         0 my($chunk, $read) = $get_chunk->();
429              
430 0 0 0     0 if ( !defined $read || $read == 0 ) {
431 0         0 die "Read error: $!\n";
432             }
433              
434 0         0 $cl -= $read;
435 0         0 $buf->print($chunk);
436             }
437 0         0 $env->{'psgi.input'} = $buf->rewind;
438             } elsif ($chunked) {
439 2         70 my $buf = Plack::TempBuffer->new;
440 2         305 my $chunk_buffer = '';
441 2         5 my $length;
442              
443             DECHUNK:
444 2         39 while (1) {
445 6         19 my($chunk, $read) = $get_chunk->();
446 6         59 $chunk_buffer .= $chunk;
447              
448 6         60 while ( $chunk_buffer =~ s/^(([0-9a-fA-F]+).*\015\012)// ) {
449 20         69 my $trailer = $1;
450 20         54 my $chunk_len = hex $2;
451              
452 20 100       54 if ($chunk_len == 0) {
    100          
453 2         7 last DECHUNK;
454             } elsif (length $chunk_buffer < $chunk_len + 2) {
455 1         2 $chunk_buffer = $trailer . $chunk_buffer;
456 1         2 last;
457             }
458              
459 17         136 $buf->print(substr $chunk_buffer, 0, $chunk_len, '');
460 17         365 $chunk_buffer =~ s/^\015\012//;
461              
462 17         79 $length += $chunk_len;
463             }
464              
465 4 50 33     41 last unless $read && $read > 0;
466             }
467              
468 2         13 $env->{CONTENT_LENGTH} = $length;
469 2         20 $env->{'psgi.input'} = $buf->rewind;
470             } else {
471 44         578 $env->{'psgi.input'} = $null_io;
472             }
473             }
474              
475             sub _finalize_response {
476 45     45   133 my($self, $env, $res) = @_;
477              
478 45 100       163 if ($env->{'psgix.harakiri.commit'}) {
479 18         47 $self->{client}->{keepalive} = 0;
480 18         114 $self->{client}->{harakiri} = 1;
481             }
482              
483 45         124 my $protocol = $env->{SERVER_PROTOCOL};
484 45         93 my $status = $res->[0];
485 45         577 my $message = status_message($status);
486              
487 45         550 my(@headers, %headers);
488 45         213 push @headers, "$protocol $status $message";
489              
490             # Switch on Transfer-Encoding: chunked if we don't know Content-Length.
491 45         76 my $chunked;
492 45         103 my $headers = $res->[1];
493 45         226 for (my $i = 0; $i < @$headers; $i += 2) {
494 45         99 my $k = $headers->[$i];
495 45         100 my $v = $headers->[$i + 1];
496 45 50       129 next if $k eq 'Connection';
497 45         121 push @headers, "$k: $v";
498 45         323 $headers{lc $k} = $v;
499             }
500              
501 45 50       199 if ( $protocol eq 'HTTP/1.1' ) {
502 45 50       152 if ( !exists $headers{'content-length'} ) {
    0          
503 45 100 66     825 if ( $status !~ /^1\d\d|[23]04$/ && $env->{REQUEST_METHOD} ne 'HEAD' ) {
504 44         89 DEBUG && warn "[$$] Using chunked transfer-encoding to send unknown length body\n";
505 44         114 push @headers, 'Transfer-Encoding: chunked';
506 44         79 $chunked = 1;
507             }
508             }
509             elsif ( my $te = $headers{'transfer-encoding'} ) {
510 0 0       0 if ( $te eq 'chunked' ) {
511 0         0 DEBUG && warn "[$$] Chunked transfer-encoding set for response\n";
512 0         0 $chunked = 1;
513             }
514             }
515             } else {
516 0 0       0 if ( !exists $headers{'content-length'} ) {
517 0         0 DEBUG && warn "[$$] Disabling keep-alive after sending unknown length body on $protocol\n";
518 0         0 $self->{client}->{keepalive} = 0;
519             }
520             }
521              
522 45 50       135 if ( ! $headers{date} ) {
523 45         721 push @headers, "Date: " . time2str( time() );
524             }
525              
526             # Should we keep the connection open?
527 45 100       1869 if ( $self->{client}->{keepalive} ) {
528 27         60 push @headers, 'Connection: keep-alive';
529             } else {
530 18         136 push @headers, 'Connection: close';
531             }
532              
533 45         108 my $conn = $self->{server}->{client};
534              
535             # Buffer the headers so they are sent with the first write() call
536             # This reduces the number of TCP packets we are sending
537 45         485 _syswrite($conn, \(join( $CRLF, @headers, '' ) . $CRLF));
538              
539 45 100       214 if (defined $res->[2]) {
540             Plack::Util::foreach($res->[2], sub {
541 42     42   833 my $buffer = $_[0];
542 42 50       138 if ($chunked) {
543 42         93 my $len = length $buffer;
544 42 50       100 return unless $len;
545 42         293 $buffer = sprintf( "%x", $len ) . $CRLF . $buffer . $CRLF;
546             }
547 42         412 _syswrite($conn, \$buffer);
548 43         982 });
549 43 100       515 _syswrite($conn, \"0$CRLF$CRLF") if $chunked;
550             } else {
551             return Plack::Util::inline_object
552             write => sub {
553 3     3   214 my $buffer = $_[0];
554 3 50       9 if ($chunked) {
555 3         7 my $len = length $buffer;
556 3 100       6 return unless $len;
557 2         12 $buffer = sprintf( "%x", $len ) . $CRLF . $buffer . $CRLF;
558             }
559 2         6 _syswrite($conn, \$buffer);
560             },
561             close => sub {
562 2 50   2   139 _syswrite($conn, \"0$CRLF$CRLF") if $chunked;
563 2         77 };
564             }
565             }
566              
567             sub _syswrite {
568 133     133   294 my ($conn, $buffer_ref) = @_;
569              
570 133         254 my $amount = length $$buffer_ref;
571 133         200 my $offset = 0;
572              
573 133         412 while ($amount > 0) {
574 133         18705 my $len = syswrite($conn, $$buffer_ref, $amount, $offset);
575              
576 133 50       561 if (not defined $len) {
577 0 0       0 return if $! == EPIPE;
578 0 0       0 return if $! == ECONNRESET;
579 0 0       0 redo if $! == EINTR;
580 0         0 die "write error: $!";
581             }
582              
583 133         237 $amount -= $len;
584 133         245 $offset += $len;
585              
586 133         764 DEBUG && warn "[$$] Wrote $len byte", ($len == 1 ? '' : 's'), "\n";
587             }
588             }
589              
590             sub post_client_connection_hook {
591 51     51 1 6811 my $self = shift;
592 51 100       304 if ($self->{client}->{harakiri}) {
593 18         5715 exit;
594             }
595             }
596              
597             1;