File Coverage

blib/lib/Starlight/Server.pm
Criterion Covered Total %
statement 351 485 72.3
branch 136 312 43.5
condition 53 115 46.0
subroutine 53 58 91.3
pod 0 10 0.0
total 593 980 60.5


line stmt bran cond sub pod time code
1             package Starlight::Server;
2              
3             =head1 NAME
4              
5             Starlight::Server - Core class for a Starlight server
6              
7             =head1 SYNOPSIS
8              
9             =for markdown ```perl
10              
11             use Starlight::Server;
12              
13             my $server = Starlight::Server->new(port => $port);
14             $server->run(sub { [200, ['Content-Type', 'text/plain'], ['PSGI app']] });
15              
16             =for markdown ```
17              
18             =head1 DESCRIPTION
19              
20             This is a core class for a Starlight server. It should be used by a
21             L handler when started with `plackup` command.
22              
23             =cut
24              
25 96     96   2017613 use strict;
  96         667  
  96         4736  
26 96     96   854 use warnings;
  96         506  
  96         6435  
27              
28             our $VERSION = '0.0502';
29              
30 96     96   895 use Config;
  96         403  
  96         11123  
31              
32 96     96   69587 use English '-no_match_vars';
  96         178343  
  96         612  
33 96     96   73548 use Errno ();
  96         315  
  96         4867  
34 96     96   678 use File::Spec;
  96         279  
  96         7898  
35 96     96   52463 use Plack;
  96         18036  
  96         7009  
36 96     96   43750 use Plack::HTTPParser qw( parse_http_request );
  96         266159  
  96         11621  
37 96     96   1308 use IO::Socket::INET;
  96         20360  
  96         7093  
38 96     96   122052 use HTTP::Date;
  96         117990  
  96         14073  
39 96     96   7535 use HTTP::Status;
  96         75393  
  96         59369  
40 96     96   796 use List::Util qw(max sum);
  96         190  
  96         20152  
41 96     96   5867 use Plack::Util;
  96         33845  
  96         2783  
42 96     96   44050 use Plack::TempBuffer;
  96         689344  
  96         5117  
43 96     96   752 use Socket qw(IPPROTO_TCP TCP_NODELAY);
  96         207  
  96         10226  
44              
45 96     96   766 use Try::Tiny;
  96         405  
  96         10079  
46              
47             BEGIN {
48 96         7470 try { require Time::HiRes; Time::HiRes->import(qw(time)) }
  96         15032  
49 96     96   1087 }
50              
51 96     96   39072 use constant DEBUG => $ENV{PERL_STARLIGHT_DEBUG};
  96         232  
  96         11758  
52 96     96   658 use constant CHUNKSIZE => 64 * 1024;
  96         209  
  96         6819  
53 96     96   643 use constant MAX_REQUEST_SIZE => 131072;
  96         240  
  96         8617  
54              
55 96   50 96   735 use constant HAS_INET6 => eval { AF_INET6 && socket my $ipv6_socket, AF_INET6, SOCK_DGRAM, 0 } && 1;
  96         345  
  96         285  
56 96   50 96   728 use constant HAS_IO_SOCKET_IP => eval { require IO::Socket::IP; 1 } && 1;
  96         546  
  96         265  
57              
58 96 50   96   939 use constant EINTR => exists &Errno::EINTR ? &Errno::EINTR : -1; ## no critic
  96         702  
  96         11033  
59 96 50   96   732 use constant EAGAIN => exists &Errno::EAGAIN ? &Errno::EAGAIN : -1; ## no critic
  96         213  
  96         8587  
60 96 50   96   651 use constant EWOULDBLOCK => exists &Errno::EWOULDBLOCK ? &Errno::EWOULDBLOCK : -1; ## no critic
  96         411  
  96         235374  
61              
62             ## no critic(InputOutput::RequireBriefOpen InputOutput::RequireCheckedOpen)
63 96     96   737 my $null_io = do { open my $io, "<", \""; $io };
  96         211  
  96         3443  
64              
65             sub new {
66 89     89 0 16731 my ($class, %args) = @_;
67              
68             my $self = bless {
69             host => $args{host},
70             port => $args{port},
71             socket => $args{socket},
72             listen => $args{listen},
73             listen_sock => $args{listen_sock},
74             timeout => $args{timeout} || 300,
75             keepalive_timeout => $args{keepalive_timeout} || 2,
76             max_keepalive_reqs => $args{max_keepalive_reqs} || 1,
77             server_software => $args{server_software} || "Starlight/$VERSION ($^O)",
78       88     server_ready => $args{server_ready} || sub { },
79             ssl => $args{ssl},
80             ipv6 => $args{ipv6} || HAS_IO_SOCKET_IP,
81             ssl_key_file => $args{ssl_key_file},
82             ssl_cert_file => $args{ssl_cert_file},
83             ssl_ca_file => $args{ssl_ca_file},
84             ssl_client_ca_file => $args{ssl_client_ca_file},
85             ssl_verify_mode => $args{ssl_verify_mode},
86             user => $args{user},
87             group => $args{group},
88             umask => $args{umask},
89             daemonize => $args{daemonize},
90             pid => $args{pid},
91             error_log => $args{error_log},
92             quiet => $args{quiet} || $args{q} || $ENV{PLACK_QUIET},
93             min_reqs_per_child => (
94             defined $args{min_reqs_per_child} ? $args{min_reqs_per_child} : undef,
95             ),
96             max_reqs_per_child => (
97             $args{max_reqs_per_child} || $args{max_requests} || 1000,
98             ),
99             spawn_interval => $args{spawn_interval} || 0,
100             err_respawn_interval => (
101             defined $args{err_respawn_interval} ? $args{err_respawn_interval} : undef,
102             ),
103 89 100 50     7621 main_process_delay => $args{main_process_delay} || 0.1,
    50 50        
      50        
      33        
      50        
      50        
      0        
      50        
      50        
      50        
104             is_multithread => Plack::Util::FALSE,
105             is_multiprocess => Plack::Util::FALSE,
106             _using_defer_accept => undef,
107             _unlink => [],
108             _sigint => 'INT',
109             }, $class;
110              
111             # Windows 7 and previous have bad SIGINT handling
112 89 50       685 if ($^O eq 'MSWin32') {
113 0         0 require Win32;
114 0         0 my @v = Win32::GetOSVersion();
115 0 0       0 if ($v[1] * 1000 + $v[2] < 6_002) {
116 0         0 $self->{_sigint} = 'TERM';
117             }
118             }
119              
120 89 50 33     1025 if ($args{max_workers} && $args{max_workers} > 1) {
121 0         0 die(
122             "Forking in $class is deprecated. Falling back to the single process mode. ",
123             "If you need more workers, use Starlight instead and run like `plackup -s Starlight`\n",
124             );
125             }
126              
127 89         724 $self;
128             }
129              
130             sub run {
131 3     3 0 22 my ($self, $app) = @_;
132 3         103 $self->setup_listener();
133 3         94 $self->accept_loop($app);
134             }
135              
136             sub prepare_socket_class {
137 88     88 0 365 my ($self, $args) = @_;
138              
139 88 50 66     99797 if ($self->{socket} and $self->{port}) {
140 0         0 die "UNIX socket and ether IPv4 or IPv6 are not supported at the same time.\n";
141             }
142              
143 88 50 66     493 if ($self->{socket} and $self->{ssl}) {
144 0         0 die "UNIX socket and SSL are not supported at the same time.\n";
145             }
146              
147 88 100       426 if ($self->{socket}) {
148 6     6   324 try { require IO::Socket::UNIX; 1 }
  6         36  
149 6 50       42 or die "UNIX socket suport requires IO::Socket::UNIX\n";
150 6         102 $args->{Local} =~ s/^@/\0/; # abstract socket address
151 6         18 return "IO::Socket::UNIX";
152             }
153              
154 82 50       340 if ($self->{ipv6}) {
155 82         1226 die "IPv6 support requires IO::Socket::IP\n" unless HAS_IO_SOCKET_IP;
156             }
157              
158 82 50       422 if ($self->{ssl}) {
159 0     0   0 try { require IO::Socket::SSL; 1 }
  0         0  
160 0 0       0 or die "SSL suport requires IO::Socket::SSL\n";
161 0         0 $args->{SSL_key_file} = $self->{ssl_key_file};
162 0         0 $args->{SSL_cert_file} = $self->{ssl_cert_file};
163 0         0 $args->{SSL_ca_file} = $self->{ssl_ca_file};
164 0         0 $args->{SSL_client_ca_file} = $self->{ssl_client_ca_file};
165 0         0 $args->{SSL_startHandshake} = 0;
166             }
167              
168 82 50       283 return "IO::Socket::SSL" if $self->{ssl};
169 82 50       434 return "IO::Socket::IP" if $self->{ipv6};
170 0         0 return "IO::Socket::INET";
171             }
172              
173             sub setup_listener {
174 88     88 0 239 my ($self) = @_;
175              
176             my %args = $self->{socket}
177             ? (
178             Listen => Socket::SOMAXCONN,
179             Local => $self->{socket},
180             )
181             : (
182             Listen => Socket::SOMAXCONN,
183             LocalPort => $self->{port} || 5000,
184             LocalAddr => $self->{host},
185 88 100 50     2204 Proto => 'tcp',
186             ReuseAddr => 1,
187             );
188              
189 88 50       1846 my $proto = $self->{ssl} ? 'https' : 'http';
190 88 100       561 my $listening = $self->{socket} ? "socket $self->{socket}" : "port $self->{port}";
191              
192 88         989 my $class = $self->prepare_socket_class(\%args);
193             $self->{listen_sock} ||= $class->new(%args)
194 88 50 33     2420 or do {
195 0         0 die "failed to listen to $listening: $!\n";
196             };
197              
198             print STDERR "Starting $self->{server_software} $proto server listening at $listening\n"
199 88 50       79240 unless $self->{quiet};
200              
201 88         1218 my $family = Socket::sockaddr_family(getsockname($self->{listen_sock}));
202 88         395 $self->{_listen_sock_is_unix} = $family == AF_UNIX;
203 88         283 $self->{_listen_sock_is_tcp} = $family != AF_UNIX;
204              
205             # set defer accept
206 88 100 66     793 if ($^O eq 'linux' && $self->{_listen_sock_is_tcp}) {
207             setsockopt($self->{listen_sock}, IPPROTO_TCP, 9, 1)
208 82 50       1104 and $self->{_using_defer_accept} = 1;
209             }
210              
211 88 100 66     505 if ($self->{_listen_sock_is_unix} && not $args{Local} =~ /^\0/) {
212 6         414 $self->_add_to_unlink(File::Spec->rel2abs($args{Local}));
213             }
214              
215 88         2605 $self->{server_ready}->({ %$self, proto => $proto });
216             }
217              
218             sub accept_loop {
219              
220             # TODO handle $max_reqs_per_child
221 81     81 0 1624 my ($self, $app, $max_reqs_per_child) = @_;
222 81         952 my $proc_req_count = 0;
223              
224 81         1891 $self->{can_exit} = 1;
225 81         786 my $is_keepalive = 0;
226 81         1196 my $sigint = $self->{_sigint};
227             local $SIG{$sigint} = local $SIG{TERM} = sub {
228 58     58   71121916 my ($sig) = @_;
229 58         551 warn "*** SIG$sig received in process $$" if DEBUG;
230 58 50       15874 exit 0 if $self->{can_exit};
231 0         0 $self->{term_received}++;
232             exit 0
233 0 0 0     0 if ($is_keepalive && $self->{can_exit}) || $self->{term_received} > 1;
      0        
234              
235             # warn "server termination delayed while handling current HTTP request";
236 81         9039 };
237              
238 81         2540 local $SIG{PIPE} = 'IGNORE';
239              
240 81   66     6392 while (!defined $max_reqs_per_child || $proc_req_count < $max_reqs_per_child) {
241 159 50       12845 my ($conn, $peer) = $self->{listen_sock}->accept or do {
242 0         0 warn "failed to accept: $!\n";
243 0         0 next;
244             };
245              
246 101         37751515 my ($peerport, $peerhost, $peeraddr) = (0, undef, undef);
247 101 100       1724 if ($self->{_listen_sock_is_tcp}) {
248 100 50       3834 if (HAS_INET6 && Socket::sockaddr_family(getsockname($conn)) == AF_INET6) {
249 0         0 ($peerport, $peerhost) = Socket::unpack_sockaddr_in6($peer);
250 0         0 $peeraddr = Socket::inet_ntop(AF_INET6, $peerhost);
251             } else {
252 100         30962 ($peerport, $peerhost) = Socket::unpack_sockaddr_in($peer);
253 100         1776 $peeraddr = Socket::inet_ntoa($peerhost);
254             }
255 100 50   100   5599 if (try { TCP_NODELAY }) {
  100         28318  
256             $conn->setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
257 100 50       7075 or do {
258 0         0 warn "setsockopt(TCP_NODELAY) failed for $peeraddr:$peerport: $!\n";
259 0         0 next;
260             };
261             }
262             }
263              
264 101 50       10956 if ($conn->isa('IO::Socket::SSL')) {
265 0 0       0 $conn->accept_SSL or do {
266 0         0 my @err = ();
267 0 0       0 push @err, $! if $!;
268 0 0       0 push @err, $IO::Socket::SSL::SSL_ERROR if $IO::Socket::SSL::SSL_ERROR;
269 0         0 warn "failed to ssl handshake with $peeraddr:$peerport: @{[join ': ', @err]}\n";
  0         0  
270 0         0 next;
271             };
272             }
273              
274 101         1043 $self->{_is_deferred_accept} = $self->{_using_defer_accept};
275             $conn->blocking(0)
276 101 50       3028 or do {
277 0         0 warn "failed to set socket to nonblocking mode for $peeraddr:$peerport: $!\n";
278 0         0 next;
279             };
280              
281 101         6668 my $req_count = 0;
282 101         812 my $pipelined_buf = '';
283 101         622 while (1) {
284 101         415 ++$req_count;
285 101         402 ++$proc_req_count;
286             my $env = {
287             SERVER_PORT => $self->{port} || 0,
288             SERVER_NAME => $self->{host} || '*',
289             SCRIPT_NAME => '',
290             REMOTE_ADDR => $peeraddr,
291             REMOTE_PORT => $peerport,
292             'psgi.version' => [1, 1],
293             'psgi.errors' => *STDERR,
294             'psgi.url_scheme' => $self->{ssl} ? 'https' : 'http',
295             'psgi.run_once' => Plack::Util::FALSE,
296             'psgi.multithread' => $self->{is_multithread},
297             'psgi.multiprocess' => $self->{is_multiprocess},
298 101 50 100     8219 'psgi.streaming' => Plack::Util::TRUE,
      100        
299             'psgi.nonblocking' => Plack::Util::FALSE,
300             'psgix.input.buffered' => Plack::Util::TRUE,
301             'psgix.io' => $conn,
302             'psgix.harakiri' => Plack::Util::TRUE,
303             };
304              
305 101         721 my $may_keepalive = $req_count < $self->{max_keepalive_reqs};
306 101 0 33     889 if ($may_keepalive && $max_reqs_per_child && $proc_req_count >= $max_reqs_per_child) {
      33        
307 0         0 $may_keepalive = undef;
308             }
309 101 50       712 $may_keepalive = 1 if length $pipelined_buf;
310 101         328 my $keepalive;
311 101         2237 ($keepalive, $pipelined_buf) = $self->handle_connection(
312             $env, $conn, $app,
313             $may_keepalive, $req_count != 1, $pipelined_buf
314             );
315              
316 101 100       706 if ($env->{'psgix.harakiri.commit'}) {
317 23         380 $conn->close;
318 23         3153 return;
319             }
320 78 50       1129 last unless $keepalive;
321              
322             # TODO add special cases for clients with broken keep-alive support, as well as disabling keep-alive for HTTP/1.0 proxies
323             }
324 78         911 $conn->close;
325             }
326             }
327              
328             my $bad_response = [400, ['Content-Type' => 'text/plain', 'Connection' => 'close'], ['Bad Request']];
329              
330             sub handle_connection {
331 101     101 0 691 my ($self, $env, $conn, $app, $use_keepalive, $is_keepalive, $prebuf) = @_;
332              
333 101         401 my $buf = '';
334 101         667 my $pipelined_buf = '';
335 101         889 my $res = $bad_response;
336              
337 101 50       1190 local $self->{can_exit} = (defined $prebuf) ? 0 : 1;
338 101         444 while (1) {
339 101         412 my $rlen;
340 101 50       955 if ($rlen = length $prebuf) {
341 0         0 $buf = $prebuf;
342 0         0 undef $prebuf;
343             } else {
344             $rlen = $self->read_timeout(
345             $conn, \$buf, MAX_REQUEST_SIZE - length($buf), length($buf),
346             $is_keepalive ? $self->{keepalive_timeout} : $self->{timeout},
347 101 50       2246 ) or return;
    100          
348             }
349 92         589 $self->{can_exit} = 0;
350 92         2632 my $reqlen = parse_http_request($buf, $env);
351 92 50       49437 if ($reqlen >= 0) {
352              
353             # handle request
354 92         785 my $protocol = $env->{SERVER_PROTOCOL};
355 92 50       558 if ($use_keepalive) {
356 0 0       0 if ($protocol eq 'HTTP/1.1') {
357 0 0       0 if (my $c = $env->{HTTP_CONNECTION}) {
358 0 0       0 $use_keepalive = undef
359             if $c =~ /^\s*close\s*/i;
360             }
361             } else {
362 0 0       0 if (my $c = $env->{HTTP_CONNECTION}) {
363 0 0       0 $use_keepalive = undef
364             unless $c =~ /^\s*keep-alive\s*/i;
365             } else {
366 0         0 $use_keepalive = undef;
367             }
368             }
369             }
370 92         798 $buf = substr $buf, $reqlen;
371 96     96   1018 my $chunked = do { no warnings 'all'; lc delete $env->{HTTP_TRANSFER_ENCODING} eq 'chunked' };
  96         270  
  96         394811  
  92         268  
  92         573  
372 92 100       780 if (my $cl = $env->{CONTENT_LENGTH}) {
    100          
373 5         414 my $buffer = Plack::TempBuffer->new($cl);
374 5         1383 while ($cl > 0) {
375 6         29 my $chunk;
376 6 100       49 if (length $buf) {
377 4         18 $chunk = $buf;
378 4         21 $buf = '';
379             } else {
380             $self->read_timeout($conn, \$chunk, $cl, 0, $self->{timeout})
381 2 100       42 or return;
382             }
383 5         63 $buffer->print($chunk);
384 5         618 $cl -= length $chunk;
385             }
386 4         85 $env->{'psgi.input'} = $buffer->rewind;
387             } elsif ($chunked) {
388 1         58 my $buffer = Plack::TempBuffer->new;
389 1         367 my $chunk_buffer = '';
390 1         4 my $length;
391 1         4 DECHUNK: while (1) {
392 9         12 my $chunk;
393 9 100       22 if (length $buf) {
394 1         2 $chunk = $buf;
395 1         11 $buf = '';
396             } else {
397             $self->read_timeout($conn, \$chunk, CHUNKSIZE, 0, $self->{timeout})
398 8 50       97 or return;
399             }
400              
401 9         197 $chunk_buffer .= $chunk;
402 9         221 while ($chunk_buffer =~ s/^(([0-9a-fA-F]+).*\015\012)//) {
403 100         221 my $trailer = $1;
404 100         153 my $chunk_len = hex $2;
405 100 100       249 if ($chunk_len == 0) {
    100          
406 1         10 last DECHUNK;
407             } elsif (length $chunk_buffer < $chunk_len + 2) {
408 1         12 $chunk_buffer = $trailer . $chunk_buffer;
409 1         6 last;
410             }
411 98         330 $buffer->print(substr $chunk_buffer, 0, $chunk_len, '');
412 98         1918 $chunk_buffer =~ s/^\015\012//;
413 98         430 $length += $chunk_len;
414             }
415             }
416 1         11 $env->{CONTENT_LENGTH} = $length;
417 1         19 $env->{'psgi.input'} = $buffer->rewind;
418             } else {
419 86 50       460 if ($buf =~ m!^(?:GET|HEAD)!) { #pipeline
420 0         0 $pipelined_buf = $buf;
421 0         0 $use_keepalive = 1; #force keepalive
422             } # else clear buffer
423 86         948 $env->{'psgi.input'} = $null_io;
424             }
425              
426 91 50       1438 if ($env->{HTTP_EXPECT}) {
427 0 0       0 if ($env->{HTTP_EXPECT} eq '100-continue') {
428 0 0       0 $self->write_all($conn, "HTTP/1.1 100 Continue\015\012\015\012")
429             or return;
430             } else {
431 0         0 $res = [417, ['Content-Type' => 'text/plain', 'Connection' => 'close'], ['Expectation Failed']];
432 0         0 last;
433             }
434             }
435              
436 91         2191 $res = Plack::Util::run_app $app, $env;
437 91         27223 last;
438             }
439 0 0       0 if ($reqlen == -2) {
    0          
440              
441             # request is incomplete, do nothing
442             } elsif ($reqlen == -1) {
443              
444             # error, close conn
445 0         0 last;
446             }
447             }
448              
449 91 100       711 if (ref $res eq 'ARRAY') {
    50          
450 88         1289 $self->_handle_response($env->{SERVER_PROTOCOL}, $res, $conn, \$use_keepalive);
451             } elsif (ref $res eq 'CODE') {
452             $res->(
453             sub {
454 3     3   412 $self->_handle_response($env->{SERVER_PROTOCOL}, $_[0], $conn, \$use_keepalive);
455             }
456 3         113 );
457             } else {
458 0         0 die "Bad response $res\n";
459             }
460 91 50       588 if ($self->{term_received}) {
461 0         0 exit 0;
462             }
463              
464 91         1029 return ($use_keepalive, $pipelined_buf);
465             }
466              
467             sub _handle_response {
468 91     91   483 my ($self, $protocol, $res, $conn, $use_keepalive_r) = @_;
469 91         329 my $status_code = $res->[0];
470 91         391 my $headers = $res->[1];
471 91         405 my $body = $res->[2];
472              
473 91         310 my @lines;
474             my %send_headers;
475 91         667 for (my $i = 0; $i < @$headers; $i += 2) {
476 104         320 my $k = $headers->[$i];
477 104         449 my $v = $headers->[$i + 1];
478 104 50       506 $v = '' if not defined $v;
479 104         379 my $lck = lc $k;
480 104 50       500 if ($lck eq 'connection') {
481 0 0 0     0 $$use_keepalive_r = undef
482             if $$use_keepalive_r && lc $v ne 'keep-alive';
483             } else {
484 104         873 push @lines, "$k: $v\015\012";
485 104         1511 $send_headers{$lck} = $v;
486             }
487             }
488 91 100       689 if (!exists $send_headers{server}) {
489 90         803 unshift @lines, "Server: $self->{server_software}\015\012";
490             }
491 91 50       846 if (!exists $send_headers{date}) {
492 91         345 unshift @lines, "Date: @{[HTTP::Date::time2str()]}\015\012";
  91         1802  
493             }
494              
495             # try to set content-length when keepalive can be used, or disable it
496 91         5983 my $use_chunked;
497 91 100 66     1636 if (defined $protocol and $protocol eq 'HTTP/1.1') {
498 89 100 66     2841 if ( defined $send_headers{'content-length'}
    100          
499             || defined $send_headers{'transfer-encoding'})
500             {
501             # ok
502             } elsif (!Plack::Util::status_with_no_entity_body($status_code)) {
503 84         1913 push @lines, "Transfer-Encoding: chunked\015\012";
504 84         307 $use_chunked = 1;
505             }
506 89 50       773 push @lines, "Connection: close\015\012" unless $$use_keepalive_r;
507             } else {
508              
509             # HTTP/1.0
510 2 50       19 if ($$use_keepalive_r) {
511 0 0 0     0 if ( defined $send_headers{'content-length'}
    0 0        
512             || defined $send_headers{'transfer-encoding'})
513             {
514             # ok
515             } elsif (!Plack::Util::status_with_no_entity_body($status_code)
516             && defined(my $cl = Plack::Util::content_length($body)))
517             {
518 0         0 push @lines, "Content-Length: $cl\015\012";
519             } else {
520 0         0 $$use_keepalive_r = undef;
521             }
522             }
523 2 50       9 push @lines, "Connection: keep-alive\015\012" if $$use_keepalive_r;
524 2 50       37 push @lines, "Connection: close\015\012" if !$$use_keepalive_r; #fmm..
525             }
526              
527 91   50     512 unshift @lines, "HTTP/1.1 $status_code @{[ HTTP::Status::status_message($status_code) || 'Unknown' ]}\015\012";
  91         2120  
528 91         2448 push @lines, "\015\012";
529              
530 91 100 100     2885 if ( defined $body
      100        
      66        
      100        
531             && ref $body eq 'ARRAY'
532             && @$body == 1
533             && defined $body->[0]
534             && length $body->[0] < 8192)
535             {
536             # combine response header and small request body
537 81         329 my $buf = $body->[0];
538 81 100       367 if ($use_chunked) {
539 78         247 my $len = length $buf;
540 78         596 $buf = sprintf("%x", $len) . "\015\012" . $buf . "\015\012" . '0' . "\015\012\015\012";
541             }
542             $self->write_all(
543             $conn, join('', @lines, $buf), $self->{timeout},
544 81         1520 );
545 81         718 return;
546             }
547             $self->write_all($conn, join('', @lines), $self->{timeout})
548 10 50       238 or return;
549              
550 10 100       108 if (defined $body) {
551 8         51 my $failed;
552             my $completed;
553 8 100       57 my $body_count = (ref $body eq 'ARRAY') ? $#{$body} + 1 : -1;
  3         19  
554             Plack::Util::foreach(
555             $body,
556             sub {
557 10 50   10   1856 unless ($failed) {
558 10         34 my $buf = $_[0];
559 10         24 --$body_count;
560 10 100       39 if ($use_chunked) {
561 7         14 my $len = length $buf;
562 7 50       20 return unless $len;
563 7         383 $buf = sprintf("%x", $len) . "\015\012" . $buf . "\015\012";
564 7 100       42 if ($body_count == 0) {
565 2         24 $buf .= '0' . "\015\012\015\012";
566 2         5 $completed = 1;
567             }
568             }
569             $self->write_all($conn, $buf, $self->{timeout})
570 10 50       70 or $failed = 1;
571             }
572             },
573 8         271 );
574 8 100 100     5787 $self->write_all($conn, '0' . "\015\012\015\012", $self->{timeout}) if $use_chunked && !$completed;
575             } else {
576             return Plack::Util::inline_object write => sub {
577 5     5   532 my $buf = $_[0];
578 5 50       27 if ($use_chunked) {
579 5         12 my $len = length $buf;
580 5 100       22 return unless $len;
581 4         33 $buf = sprintf("%x", $len) . "\015\012" . $buf . "\015\012";
582             }
583 4         33 $self->write_all($conn, $buf, $self->{timeout});
584             },
585             close => sub {
586 2 50   2   98 $self->write_all($conn, '0' . "\015\012\015\012", $self->{timeout}) if $use_chunked;
587 2         130 };
588             }
589             }
590              
591             # returns value returned by $cb, or undef on timeout or network error
592             sub do_io {
593 220     220 0 1079 my ($self, $is_write, $sock, $buf, $len, $off, $timeout) = @_;
594 220         661 my $ret;
595 220 100 100     2234 unless ($is_write || delete $self->{_is_deferred_accept}) {
596 11         60 goto DO_SELECT;
597             }
598             DO_READWRITE:
599              
600             # try to do the IO
601 220 100       1226 if ($is_write) {
602 109 50       13439 $ret = syswrite $sock, $buf, $len, $off
603             and return $ret;
604             } else {
605 111 100       5547 $ret = sysread $sock, $$buf, $len, $off
606             and return $ret;
607             }
608 10 50 0     78 if (defined($ret) || ($! != EINTR && $! != EAGAIN && $! != EWOULDBLOCK)) {
      0        
      33        
609 10         199 return;
610             }
611              
612             # wait for data
613             DO_SELECT:
614 11         42 while (1) {
615 11         40 my ($rfd, $wfd);
616 11         87 my $efd = '';
617 11         182 vec($efd, fileno($sock), 1) = 1;
618 11 50       59 if ($is_write) {
619 0         0 ($rfd, $wfd) = ('', $efd);
620             } else {
621 11         58 ($rfd, $wfd) = ($efd, '');
622             }
623 11         127 my $start_at = time;
624 11         795 my $nfound = select($rfd, $wfd, $efd, $timeout);
625 11         98 $timeout -= (time - $start_at);
626 11 50       47 last if $nfound;
627 0 0       0 return if $timeout <= 0;
628             }
629 11         32 goto DO_READWRITE;
630             }
631              
632             # returns (positive) number of bytes read, or undef if the socket is to be closed
633             sub read_timeout {
634 111     111 0 547 my ($self, $sock, $buf, $len, $off, $timeout) = @_;
635 111         2137 $self->do_io(undef, $sock, $buf, $len, $off, $timeout);
636             }
637              
638             # returns (positive) number of bytes written, or undef if the socket is to be closed
639             sub write_timeout {
640 109     109 0 626 my ($self, $sock, $buf, $len, $off, $timeout) = @_;
641 109         619 $self->do_io(1, $sock, $buf, $len, $off, $timeout);
642             }
643              
644             # writes all data in buf and returns number of bytes written or undef if failed
645             sub write_all {
646 109     109 0 675 my ($self, $sock, $buf, $timeout) = @_;
647 109         443 my $off = 0;
648 109         972 while (my $len = length($buf) - $off) {
649 109 50       1434 my $ret = $self->write_timeout($sock, $buf, $len, $off, $timeout)
650             or return;
651 109         1013 $off += $ret;
652             }
653 109         849 return length $buf;
654             }
655              
656             sub _add_to_unlink {
657 6     6   24 my ($self, $filename) = @_;
658 6         6 push @{ $self->{_unlink} }, File::Spec->rel2abs($filename);
  6         48  
659             }
660              
661             sub _daemonize {
662 85     85   250 my $self = shift;
663              
664 85 50       379 if ($^O eq 'MSWin32') {
665 0         0 foreach my $arg (qw(daemonize pid)) {
666 0 0       0 die "$arg parameter is not supported on this platform ($^O)\n" if $self->{$arg};
667             }
668             }
669              
670 85         244 my ($pidfh, $pidfile);
671 85 50       295 if ($self->{pid}) {
672 0         0 $pidfile = File::Spec->rel2abs($self->{pid});
673 0 0       0 if (defined *Fcntl::O_EXCL{CODE}) {
674 0 0       0 sysopen $pidfh, $pidfile, Fcntl::O_WRONLY | Fcntl::O_CREAT | Fcntl::O_EXCL
675             or die "Cannot open pid file: $self->{pid}: $!\n";
676             } else {
677 0 0       0 open $pidfh, '>', $pidfile or die "Cannot open pid file: $self->{pid}: $!\n";
678             }
679             }
680              
681 85 50       420879 if (defined $self->{error_log}) {
682 0 0       0 open STDERR, '>>', $self->{error_log} or die "Cannot open error log file: $self->{error_log}: $!\n";
683             }
684              
685 85 50       790 if ($self->{daemonize}) {
686              
687 0 0       0 chdir File::Spec->rootdir or die "Cannot chdir to root directory: $!\n";
688              
689 0 0       0 open my $devnull, '+>', File::Spec->devnull or die "Cannot open null device: $!\n";
690              
691 0 0       0 open STDIN, '>&', $devnull or die "Cannot dup null device: $!\n";
692 0 0       0 open STDOUT, '>&', $devnull or die "Cannot dup null device: $!\n";
693              
694 0 0       0 defined(my $pid = fork) or die "Cannot fork: $!\n";
695 0 0       0 if ($pid) {
696 0 0 0     0 if ($self->{pid} and $pid) {
697 0 0       0 print $pidfh "$pid\n" or die "Cannot write pidfile $self->{pid}: $!\n";
698 0         0 close $pidfh;
699 0 0       0 open STDERR, '>&', $devnull or die "Cannot dup null device: $!\n";
700             }
701 0         0 exit;
702             }
703              
704 0 0       0 close $pidfh if $pidfh;
705              
706 0 0       0 if ($Config::Config{d_setsid}) {
707 0 0       0 POSIX::setsid() or die "Cannot setsid: $!\n";
708             }
709              
710 0 0       0 if (not defined $self->{error_log}) {
711 0 0       0 open STDERR, '>&', $devnull or die "Cannot dup null device: $!\n";
712             }
713             }
714              
715 85 50       249 if ($pidfile) {
716 0         0 $self->_add_to_unlink($pidfile);
717             }
718              
719 85         520 return;
720             }
721              
722             sub _setup_privileges {
723 85     85   249 my ($self) = @_;
724              
725 85 50       306 if (defined $self->{group}) {
726 0 0       0 if (not $Config::Config{d_setegid}) {
727 0         0 die "group parameter is not supported on this platform ($^O)\n";
728             }
729 0 0       0 if ($self->_get_gid($self->{group}) ne $EGID) {
730 0         0 warn "*** setting group to \"$self->{group}\"" if DEBUG;
731 0         0 $self->_set_gid($self->{group});
732             }
733             }
734              
735 85 50       249 if (defined $self->{user}) {
736 0 0       0 if (not $Config::Config{d_seteuid}) {
737 0         0 die "user parameter is not supported on this platform ($^O)\n";
738             }
739 0 0       0 if ($self->_get_uid($self->{user}) ne $EUID) {
740 0         0 warn "*** setting user to \"$self->{user}\"" if DEBUG;
741 0         0 $self->_set_uid($self->{user});
742             }
743             }
744              
745 85 50       342 if (defined $self->{umask}) {
746 0 0       0 if (not $Config::Config{d_umask}) {
747 0         0 die "umask parameter is not supported on this platform ($^O)\n";
748             }
749 0         0 warn "*** setting umask to \"$self->{umask}\"" if DEBUG;
750 0         0 umask(oct($self->{umask}));
751             }
752              
753 85         187 return;
754             }
755              
756             # Taken from Net::Server::Daemonize
757             sub _get_uid {
758 0     0   0 my ($self, $user) = @_;
759 0         0 my $uid = do {
760 0 0       0 if ($user =~ /^(\d+)$/) {
761 0         0 $1;
762             } else {
763 0         0 getpwnam($user);
764             }
765             };
766              
767 0 0       0 die "No such user \"$user\"\n" unless defined $uid;
768 0         0 return $uid;
769             }
770              
771             # Taken from Net::Server::Daemonize
772             sub _get_gid {
773 0     0   0 my ($self, @groups) = @_;
774 0         0 my @gid;
775              
776 0         0 foreach my $group (split(/[, ]+/, join(" ", @groups))) {
777 0 0       0 if ($group =~ /^\d+$/) {
778 0         0 push @gid, $group;
779             } else {
780 0         0 my $id = getgrnam($group);
781 0 0       0 die "No such group \"$group\"\n" unless defined $id;
782 0         0 push @gid, $id;
783             }
784             }
785              
786 0 0       0 die "No group found in arguments.\n" unless @gid;
787 0         0 return join(" ", $gid[0], @gid);
788             }
789              
790             # Taken from Net::Server::Daemonize
791             sub _set_uid {
792 0     0   0 my ($self, $user) = @_;
793 0         0 my $uid = $self->_get_uid($user);
794              
795 0 0       0 eval { POSIX::setuid($uid) } or 1;
  0         0  
796 0 0 0     0 if ($UID != $uid || $EUID != $uid) { # check $> also (rt #21262)
797 0         0 $UID = $EUID = $uid; # try again - needed by some 5.8.0 linux systems (rt #13450)
798 0 0       0 if ($UID != $uid) {
799 0         0 die "Couldn't become uid \"$uid\": $!\n";
800             }
801             }
802              
803 0         0 return 1;
804             }
805              
806             # Taken from Net::Server::Daemonize
807             sub _set_gid {
808 0     0   0 my ($self, @groups) = @_;
809 0         0 my $gids = $self->_get_gid(@groups);
810 0         0 my $gid = (split /\s+/, $gids)[0];
811 0 0       0 eval { $EGID = $gids } or 1; # store all the gids - this is really sort of optional
  0         0  
812              
813 0 0       0 eval { POSIX::setgid($gid) } or 1;
  0         0  
814 0 0       0 if (!grep { $gid == $_ } split /\s+/, $GID) { # look for any valid id in the list
  0         0  
815 0         0 die "Couldn't become gid \"$gid\": $!\n";
816             }
817              
818 0         0 return 1;
819             }
820              
821             sub _sleep {
822 1654     1654   9079 my ($self, $t) = @_;
823 1654 100       67255832 select undef, undef, undef, $t if $t;
824             }
825              
826             sub _create_process {
827 849     849   1877 my ($self, $app) = @_;
828 849         913518 my $pid = fork;
829 849 50       20405 return warn "cannot fork: $!" unless defined $pid;
830              
831 849 100       19045 if ($pid == 0) {
832 78         4518 warn "*** process $$ starting" if DEBUG;
833 78 50       2946 eval {
834 78         5762 $SIG{CHLD} = 'DEFAULT';
835 78         13800 $self->accept_loop($app, $self->_calc_reqs_per_child());
836             } or 1;
837 23 50       113 warn $@ if $@;
838 23         35 warn "*** process $$ ending" if DEBUG;
839 23         2728 exit 0;
840             } else {
841 771         65524 $self->{processes}->{$pid} = 1;
842             }
843             }
844              
845             sub _calc_reqs_per_child {
846 10078     10078   1047686 my $self = shift;
847 10078         14151 my $max = $self->{max_reqs_per_child};
848 10078 100       17053 if (my $min = $self->{min_reqs_per_child}) {
849 10000         22442 srand((rand() * 2**30) ^ $$ ^ time);
850 10000         22784 return $max - int(($max - $min + 1) * rand);
851             } else {
852 78         5231 return $max;
853             }
854             }
855              
856             sub DESTROY {
857 89     89   4303 my ($self) = @_;
858 89         649 while (my $f = shift @{ $self->{_unlink} }) {
  95         79555  
859 6         527 unlink $f;
860             }
861             }
862              
863             1;
864              
865             __END__