File Coverage

blib/lib/Starman/Server.pm
Criterion Covered Total %
statement 274 339 80.8
branch 94 160 58.7
condition 30 70 42.8
subroutine 40 46 86.9
pod 7 10 70.0
total 445 625 71.2


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