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   2027406 use strict;
  96         487  
  96         4054  
26 96     96   745 use warnings;
  96         680  
  96         5888  
27              
28             our $VERSION = '0.0503';
29              
30 96     96   668 use Config;
  96         461  
  96         9034  
31              
32 96     96   59795 use English '-no_match_vars';
  96         161291  
  96         809  
33 96     96   71753 use Errno ();
  96         347  
  96         4421  
34 96     96   668 use File::Spec;
  96         199  
  96         6709  
35 96     96   52084 use Plack;
  96         16469  
  96         5932  
36 96     96   42041 use Plack::HTTPParser qw( parse_http_request );
  96         262413  
  96         12019  
37 96     96   1466 use IO::Socket::INET;
  96         21408  
  96         6506  
38 96     96   122337 use HTTP::Date;
  96         113201  
  96         12486  
39 96     96   7411 use HTTP::Status;
  96         72275  
  96         55136  
40 96     96   829 use List::Util qw(max sum);
  96         220  
  96         18670  
41 96     96   5816 use Plack::Util;
  96         33417  
  96         2714  
42 96     96   45695 use Plack::TempBuffer;
  96         702178  
  96         5387  
43 96     96   764 use Socket qw(IPPROTO_TCP TCP_NODELAY);
  96         459  
  96         9602  
44              
45 96     96   811 use Try::Tiny;
  96         203  
  96         10917  
46              
47             BEGIN {
48 96         7375 try { require Time::HiRes; Time::HiRes->import(qw(time)) }
  96         15181  
49 96     96   944 }
50              
51 96     96   42495 use constant DEBUG => $ENV{PERL_STARLIGHT_DEBUG};
  96         234  
  96         10763  
52 96     96   670 use constant CHUNKSIZE => 64 * 1024;
  96         209  
  96         6735  
53 96     96   624 use constant MAX_REQUEST_SIZE => 131072;
  96         240  
  96         8152  
54              
55 96   50 96   680 use constant HAS_INET6 => eval { AF_INET6 && socket my $ipv6_socket, AF_INET6, SOCK_DGRAM, 0 } && 1;
  96         326  
  96         423  
56 96   50 96   854 use constant HAS_IO_SOCKET_IP => eval { require IO::Socket::IP; 1 } && 1;
  96         334  
  96         311  
57              
58 96 50   96   722 use constant EINTR => exists &Errno::EINTR ? &Errno::EINTR : -1; ## no critic
  96         226  
  96         11896  
59 96 50   96   664 use constant EAGAIN => exists &Errno::EAGAIN ? &Errno::EAGAIN : -1; ## no critic
  96         557  
  96         8217  
60 96 50   96   888 use constant EWOULDBLOCK => exists &Errno::EWOULDBLOCK ? &Errno::EWOULDBLOCK : -1; ## no critic
  96         464  
  96         242041  
61              
62             ## no critic(InputOutput::RequireBriefOpen InputOutput::RequireCheckedOpen)
63 96     96   786 my $null_io = do { open my $io, "<", \""; $io };
  96         220  
  96         3912  
64              
65             sub new {
66 89     89 0 17743 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     8902 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       611 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     1381 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         879 $self;
128             }
129              
130             sub run {
131 3     3 0 58 my ($self, $app) = @_;
132 3         158 $self->setup_listener();
133 3         95 $self->accept_loop($app);
134             }
135              
136             sub prepare_socket_class {
137 88     88 0 435 my ($self, $args) = @_;
138              
139 88 50 66     346 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     101071 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       553 if ($self->{socket}) {
148 6     6   378 try { require IO::Socket::UNIX; 1 }
  6         42  
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       420 if ($self->{ipv6}) {
155 82         1314 die "IPv6 support requires IO::Socket::IP\n" unless HAS_IO_SOCKET_IP;
156             }
157              
158 82 50       426 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       320 return "IO::Socket::SSL" if $self->{ssl};
169 82 50       436 return "IO::Socket::IP" if $self->{ipv6};
170 0         0 return "IO::Socket::INET";
171             }
172              
173             sub setup_listener {
174 88     88 0 307 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     2714 Proto => 'tcp',
186             ReuseAddr => 1,
187             );
188              
189 88 50       1874 my $proto = $self->{ssl} ? 'https' : 'http';
190 88 100       543 my $listening = $self->{socket} ? "socket $self->{socket}" : "port $self->{port}";
191              
192 88         1102 my $class = $self->prepare_socket_class(\%args);
193             $self->{listen_sock} ||= $class->new(%args)
194 88 50 33     2320 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       80261 unless $self->{quiet};
200              
201 88         1120 my $family = Socket::sockaddr_family(getsockname($self->{listen_sock}));
202 88         371 $self->{_listen_sock_is_unix} = $family == AF_UNIX;
203 88         298 $self->{_listen_sock_is_tcp} = $family != AF_UNIX;
204              
205             # set defer accept
206 88 100 66     796 if ($^O eq 'linux' && $self->{_listen_sock_is_tcp}) {
207             setsockopt($self->{listen_sock}, IPPROTO_TCP, 9, 1)
208 82 50       1016 and $self->{_using_defer_accept} = 1;
209             }
210              
211 88 100 66     398 if ($self->{_listen_sock_is_unix} && not $args{Local} =~ /^\0/) {
212 6         408 $self->_add_to_unlink(File::Spec->rel2abs($args{Local}));
213             }
214              
215 88         2542 $self->{server_ready}->({ %$self, proto => $proto });
216             }
217              
218             sub accept_loop {
219              
220             # TODO handle $max_reqs_per_child
221 81     81 0 1509 my ($self, $app, $max_reqs_per_child) = @_;
222 81         783 my $proc_req_count = 0;
223              
224 81         2017 $self->{can_exit} = 1;
225 81         681 my $is_keepalive = 0;
226 81         1247 my $sigint = $self->{_sigint};
227             local $SIG{$sigint} = local $SIG{TERM} = sub {
228 58     58   71098077 my ($sig) = @_;
229 58         354 warn "*** SIG$sig received in process $$" if DEBUG;
230 58 50       16464 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         8120 };
237              
238 81         2602 local $SIG{PIPE} = 'IGNORE';
239              
240 81   66     5607 while (!defined $max_reqs_per_child || $proc_req_count < $max_reqs_per_child) {
241 159 50       11678 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         36720419 my ($peerport, $peerhost, $peeraddr) = (0, undef, undef);
247 101 100       1393 if ($self->{_listen_sock_is_tcp}) {
248 100 50       3278 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         28471 ($peerport, $peerhost) = Socket::unpack_sockaddr_in($peer);
253 100         1575 $peeraddr = Socket::inet_ntoa($peerhost);
254             }
255 100 50   100   4284 if (try { TCP_NODELAY }) {
  100         23577  
256             $conn->setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
257 100 50       6107 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       9594 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         897 $self->{_is_deferred_accept} = $self->{_using_defer_accept};
275             $conn->blocking(0)
276 101 50       2564 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         5810 my $req_count = 0;
282 101         724 my $pipelined_buf = '';
283 101         652 while (1) {
284 101         679 ++$req_count;
285 101         574 ++$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     7757 '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         538 my $may_keepalive = $req_count < $self->{max_keepalive_reqs};
306 101 0 33     775 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       824 $may_keepalive = 1 if length $pipelined_buf;
310 101         486 my $keepalive;
311 101         1819 ($keepalive, $pipelined_buf) = $self->handle_connection(
312             $env, $conn, $app,
313             $may_keepalive, $req_count != 1, $pipelined_buf
314             );
315              
316 101 100       591 if ($env->{'psgix.harakiri.commit'}) {
317 23         191 $conn->close;
318 23         3076 return;
319             }
320 78 50       1011 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         761 $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 845 my ($self, $env, $conn, $app, $use_keepalive, $is_keepalive, $prebuf) = @_;
332              
333 101         421 my $buf = '';
334 101         616 my $pipelined_buf = '';
335 101         878 my $res = $bad_response;
336              
337 101 50       1127 local $self->{can_exit} = (defined $prebuf) ? 0 : 1;
338 101         309 while (1) {
339 101         291 my $rlen;
340 101 50       708 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       2069 ) or return;
    100          
348             }
349 92         473 $self->{can_exit} = 0;
350 92         2020 my $reqlen = parse_http_request($buf, $env);
351 92 50       41957 if ($reqlen >= 0) {
352              
353             # handle request
354 92         429 my $protocol = $env->{SERVER_PROTOCOL};
355 92 50       823 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         627 $buf = substr $buf, $reqlen;
371 96     96   875 my $chunked = do { no warnings 'all'; lc delete $env->{HTTP_TRANSFER_ENCODING} eq 'chunked' };
  96         215  
  96         395900  
  92         327  
  92         692  
372 92 100       610 if (my $cl = $env->{CONTENT_LENGTH}) {
    100          
373 5         251 my $buffer = Plack::TempBuffer->new($cl);
374 5         945 while ($cl > 0) {
375 6         19 my $chunk;
376 6 100       31 if (length $buf) {
377 4         15 $chunk = $buf;
378 4         11 $buf = '';
379             } else {
380             $self->read_timeout($conn, \$chunk, $cl, 0, $self->{timeout})
381 2 100       25 or return;
382             }
383 5         41 $buffer->print($chunk);
384 5         498 $cl -= length $chunk;
385             }
386 4         28 $env->{'psgi.input'} = $buffer->rewind;
387             } elsif ($chunked) {
388 1         55 my $buffer = Plack::TempBuffer->new;
389 1         255 my $chunk_buffer = '';
390 1         3 my $length;
391 1         3 DECHUNK: while (1) {
392 9         10 my $chunk;
393 9 100       22 if (length $buf) {
394 1         5 $chunk = $buf;
395 1         10 $buf = '';
396             } else {
397             $self->read_timeout($conn, \$chunk, CHUNKSIZE, 0, $self->{timeout})
398 8 50       29 or return;
399             }
400              
401 9         148 $chunk_buffer .= $chunk;
402 9         198 while ($chunk_buffer =~ s/^(([0-9a-fA-F]+).*\015\012)//) {
403 100         200 my $trailer = $1;
404 100         171 my $chunk_len = hex $2;
405 100 100       215 if ($chunk_len == 0) {
    100          
406 1         6 last DECHUNK;
407             } elsif (length $chunk_buffer < $chunk_len + 2) {
408 1         7 $chunk_buffer = $trailer . $chunk_buffer;
409 1         3 last;
410             }
411 98         431 $buffer->print(substr $chunk_buffer, 0, $chunk_len, '');
412 98         1871 $chunk_buffer =~ s/^\015\012//;
413 98         414 $length += $chunk_len;
414             }
415             }
416 1         3 $env->{CONTENT_LENGTH} = $length;
417 1         10 $env->{'psgi.input'} = $buffer->rewind;
418             } else {
419 86 50       444 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         648 $env->{'psgi.input'} = $null_io;
424             }
425              
426 91 50       970 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         1974 $res = Plack::Util::run_app $app, $env;
437 91         20497 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       628 if (ref $res eq 'ARRAY') {
    50          
450 88         1038 $self->_handle_response($env->{SERVER_PROTOCOL}, $res, $conn, \$use_keepalive);
451             } elsif (ref $res eq 'CODE') {
452             $res->(
453             sub {
454 3     3   278 $self->_handle_response($env->{SERVER_PROTOCOL}, $_[0], $conn, \$use_keepalive);
455             }
456 3         80 );
457             } else {
458 0         0 die "Bad response $res\n";
459             }
460 91 50       526 if ($self->{term_received}) {
461 0         0 exit 0;
462             }
463              
464 91         767 return ($use_keepalive, $pipelined_buf);
465             }
466              
467             sub _handle_response {
468 91     91   413 my ($self, $protocol, $res, $conn, $use_keepalive_r) = @_;
469 91         308 my $status_code = $res->[0];
470 91         317 my $headers = $res->[1];
471 91         257 my $body = $res->[2];
472              
473 91         314 my @lines;
474             my %send_headers;
475 91         575 for (my $i = 0; $i < @$headers; $i += 2) {
476 104         297 my $k = $headers->[$i];
477 104         346 my $v = $headers->[$i + 1];
478 104 50       508 $v = '' if not defined $v;
479 104         285 my $lck = lc $k;
480 104 50       443 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         678 push @lines, "$k: $v\015\012";
485 104         1222 $send_headers{$lck} = $v;
486             }
487             }
488 91 100       637 if (!exists $send_headers{server}) {
489 90         612 unshift @lines, "Server: $self->{server_software}\015\012";
490             }
491 91 50       404 if (!exists $send_headers{date}) {
492 91         409 unshift @lines, "Date: @{[HTTP::Date::time2str()]}\015\012";
  91         1764  
493             }
494              
495             # try to set content-length when keepalive can be used, or disable it
496 91         5339 my $use_chunked;
497 91 100 66     1484 if (defined $protocol and $protocol eq 'HTTP/1.1') {
498 89 100 66     2579 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         1568 push @lines, "Transfer-Encoding: chunked\015\012";
504 84         264 $use_chunked = 1;
505             }
506 89 50       573 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       11 push @lines, "Connection: keep-alive\015\012" if $$use_keepalive_r;
524 2 50       66 push @lines, "Connection: close\015\012" if !$$use_keepalive_r; #fmm..
525             }
526              
527 91   50     390 unshift @lines, "HTTP/1.1 $status_code @{[ HTTP::Status::status_message($status_code) || 'Unknown' ]}\015\012";
  91         1964  
528 91         2443 push @lines, "\015\012";
529              
530 91 100 100     2485 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         333 my $buf = $body->[0];
538 81 100       365 if ($use_chunked) {
539 78         231 my $len = length $buf;
540 78         538 $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         1493 );
545 81         540 return;
546             }
547             $self->write_all($conn, join('', @lines), $self->{timeout})
548 10 50       150 or return;
549              
550 10 100       57 if (defined $body) {
551 8         47 my $failed;
552             my $completed;
553 8 100       48 my $body_count = (ref $body eq 'ARRAY') ? $#{$body} + 1 : -1;
  3         11  
554             Plack::Util::foreach(
555             $body,
556             sub {
557 10 50   10   1426 unless ($failed) {
558 10         25 my $buf = $_[0];
559 10         19 --$body_count;
560 10 100       39 if ($use_chunked) {
561 7         16 my $len = length $buf;
562 7 50       43 return unless $len;
563 7         328 $buf = sprintf("%x", $len) . "\015\012" . $buf . "\015\012";
564 7 100       37 if ($body_count == 0) {
565 2         13 $buf .= '0' . "\015\012\015\012";
566 2         6 $completed = 1;
567             }
568             }
569             $self->write_all($conn, $buf, $self->{timeout})
570 10 50       39 or $failed = 1;
571             }
572             },
573 8         261 );
574 8 100 100     3889 $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       30 if ($use_chunked) {
579 5         15 my $len = length $buf;
580 5 100       23 return unless $len;
581 4         58 $buf = sprintf("%x", $len) . "\015\012" . $buf . "\015\012";
582             }
583 4         46 $self->write_all($conn, $buf, $self->{timeout});
584             },
585             close => sub {
586 2 50   2   81 $self->write_all($conn, '0' . "\015\012\015\012", $self->{timeout}) if $use_chunked;
587 2         135 };
588             }
589             }
590              
591             # returns value returned by $cb, or undef on timeout or network error
592             sub do_io {
593 220     220 0 1287 my ($self, $is_write, $sock, $buf, $len, $off, $timeout) = @_;
594 220         759 my $ret;
595 220 100 100     1941 unless ($is_write || delete $self->{_is_deferred_accept}) {
596 11         55 goto DO_SELECT;
597             }
598             DO_READWRITE:
599              
600             # try to do the IO
601 220 100       703 if ($is_write) {
602 109 50       10570 $ret = syswrite $sock, $buf, $len, $off
603             and return $ret;
604             } else {
605 111 100       4943 $ret = sysread $sock, $$buf, $len, $off
606             and return $ret;
607             }
608 10 50 0     94 if (defined($ret) || ($! != EINTR && $! != EAGAIN && $! != EWOULDBLOCK)) {
      0        
      33        
609 10         236 return;
610             }
611              
612             # wait for data
613             DO_SELECT:
614 11         56 while (1) {
615 11         28 my ($rfd, $wfd);
616 11         59 my $efd = '';
617 11         125 vec($efd, fileno($sock), 1) = 1;
618 11 50       51 if ($is_write) {
619 0         0 ($rfd, $wfd) = ('', $efd);
620             } else {
621 11         32 ($rfd, $wfd) = ($efd, '');
622             }
623 11         102 my $start_at = time;
624 11         931 my $nfound = select($rfd, $wfd, $efd, $timeout);
625 11         61 $timeout -= (time - $start_at);
626 11 50       72 last if $nfound;
627 0 0       0 return if $timeout <= 0;
628             }
629 11         31 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 472 my ($self, $sock, $buf, $len, $off, $timeout) = @_;
635 111         1733 $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 447 my ($self, $sock, $buf, $len, $off, $timeout) = @_;
641 109         418 $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 638 my ($self, $sock, $buf, $timeout) = @_;
647 109         308 my $off = 0;
648 109         662 while (my $len = length($buf) - $off) {
649 109 50       1140 my $ret = $self->write_timeout($sock, $buf, $len, $off, $timeout)
650             or return;
651 109         785 $off += $ret;
652             }
653 109         528 return length $buf;
654             }
655              
656             sub _add_to_unlink {
657 6     6   24 my ($self, $filename) = @_;
658 6         12 push @{ $self->{_unlink} }, File::Spec->rel2abs($filename);
  6         54  
659             }
660              
661             sub _daemonize {
662 85     85   227 my $self = shift;
663              
664 85 50       413 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         243 my ($pidfh, $pidfile);
671 85 50       337 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       421358 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       977 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       402 if ($pidfile) {
716 0         0 $self->_add_to_unlink($pidfile);
717             }
718              
719 85         553 return;
720             }
721              
722             sub _setup_privileges {
723 85     85   255 my ($self) = @_;
724              
725 85 50       600 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       277 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       232 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         239 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 1626     1626   8566 my ($self, $t) = @_;
823 1626 100       66219469 select undef, undef, undef, $t if $t;
824             }
825              
826             sub _create_process {
827 849     849   2184 my ($self, $app) = @_;
828 849         1007636 my $pid = fork;
829 849 50       26686 return warn "cannot fork: $!" unless defined $pid;
830              
831 849 100       18208 if ($pid == 0) {
832 78         4323 warn "*** process $$ starting" if DEBUG;
833 78 50       2655 eval {
834 78         5292 $SIG{CHLD} = 'DEFAULT';
835 78         12384 $self->accept_loop($app, $self->_calc_reqs_per_child());
836             } or 1;
837 23 50       138 warn $@ if $@;
838 23         78 warn "*** process $$ ending" if DEBUG;
839 23         2496 exit 0;
840             } else {
841 771         64280 $self->{processes}->{$pid} = 1;
842             }
843             }
844              
845             sub _calc_reqs_per_child {
846 10078     10078   1047204 my $self = shift;
847 10078         14746 my $max = $self->{max_reqs_per_child};
848 10078 100       16477 if (my $min = $self->{min_reqs_per_child}) {
849 10000         22053 srand((rand() * 2**30) ^ $$ ^ time);
850 10000         20877 return $max - int(($max - $min + 1) * rand);
851             } else {
852 78         4482 return $max;
853             }
854             }
855              
856             sub DESTROY {
857 89     89   4319 my ($self) = @_;
858 89         9716 while (my $f = shift @{ $self->{_unlink} }) {
  95         86536  
859 6         696 unlink $f;
860             }
861             }
862              
863             1;
864              
865             __END__