Fixed a bug with the HTTP engine where very large response bodies would not be sent...
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Engine / HTTP.pm
1 package Catalyst::Engine::HTTP;
2
3 use strict;
4 use base 'Catalyst::Engine::CGI';
5 use Data::Dump qw(dump);
6 use Errno 'EWOULDBLOCK';
7 use HTTP::Date ();
8 use HTTP::Headers;
9 use HTTP::Status;
10 use NEXT;
11 use Socket;
12 use IO::Socket::INET ();
13 use IO::Select       ();
14
15 # For PAR
16 require Catalyst::Engine::HTTP::Restarter;
17 require Catalyst::Engine::HTTP::Restarter::Watcher;
18
19 use constant CHUNKSIZE => 64 * 1024;
20 use constant DEBUG     => $ENV{CATALYST_HTTP_DEBUG} || 0;
21
22 =head1 NAME
23
24 Catalyst::Engine::HTTP - Catalyst HTTP Engine
25
26 =head1 SYNOPSIS
27
28 A script using the Catalyst::Engine::HTTP module might look like:
29
30     #!/usr/bin/perl -w
31
32     BEGIN {  $ENV{CATALYST_ENGINE} = 'HTTP' }
33
34     use strict;
35     use lib '/path/to/MyApp/lib';
36     use MyApp;
37
38     MyApp->run;
39
40 =head1 DESCRIPTION
41
42 This is the Catalyst engine specialized for development and testing.
43
44 =head1 METHODS
45
46 =head2 $self->finalize_headers($c)
47
48 =cut
49
50 sub finalize_headers {
51     my ( $self, $c ) = @_;
52     my $protocol = $c->request->protocol;
53     my $status   = $c->response->status;
54     my $message  = status_message($status);
55     
56     my @headers;
57     push @headers, "$protocol $status $message";
58     
59     $c->response->headers->header( Date => HTTP::Date::time2str(time) );
60     $c->response->headers->header( Status => $status );
61     
62     # Should we keep the connection open?
63     my $connection = $c->request->header('Connection');
64     if (   $self->{options}->{keepalive} 
65         && $connection 
66         && $connection =~ /^keep-alive$/i
67     ) {
68         $c->response->headers->header( Connection => 'keep-alive' );
69         $self->{_keepalive} = 1;
70     }
71     else {
72         $c->response->headers->header( Connection => 'close' );
73     }
74     
75     push @headers, $c->response->headers->as_string("\x0D\x0A");
76     
77     # Buffer the headers so they are sent with the first write() call
78     # This reduces the number of TCP packets we are sending
79     $self->{_header_buf} = join("\x0D\x0A", @headers, '');
80 }
81
82 =head2 $self->finalize_read($c)
83
84 =cut
85
86 sub finalize_read {
87     my ( $self, $c ) = @_;
88
89     # Never ever remove this, it would result in random length output
90     # streams if STDIN eq STDOUT (like in the HTTP engine)
91     *STDIN->blocking(1);
92
93     return $self->NEXT::finalize_read($c);
94 }
95
96 =head2 $self->prepare_read($c)
97
98 =cut
99
100 sub prepare_read {
101     my ( $self, $c ) = @_;
102
103     # Set the input handle to non-blocking
104     *STDIN->blocking(0);
105
106     return $self->NEXT::prepare_read($c);
107 }
108
109 =head2 $self->read_chunk($c, $buffer, $length)
110
111 =cut
112
113 sub read_chunk {
114     my $self = shift;
115     my $c    = shift;
116     
117     # If we have any remaining data in the input buffer, send it back first
118     if ( $_[0] = delete $self->{inputbuf} ) {
119         my $read = length( $_[0] );
120         DEBUG && warn "read_chunk: Read $read bytes from previous input buffer\n";
121         return $read;
122     }
123
124     # support for non-blocking IO
125     my $rin = '';
126     vec( $rin, *STDIN->fileno, 1 ) = 1;
127
128   READ:
129     {
130         select( $rin, undef, undef, undef );
131         my $rc = *STDIN->sysread(@_);
132         if ( defined $rc ) {
133             DEBUG && warn "read_chunk: Read $rc bytes from socket\n";
134             return $rc;
135         }
136         else {
137             next READ if $! == EWOULDBLOCK;
138             return;
139         }
140     }
141 }
142
143 =head2 $self->write($c, $buffer)
144
145 Writes the buffer to the client.
146
147 =cut
148
149 sub write {
150     my ( $self, $c, $buffer ) = @_;
151     
152     # Avoid 'print() on closed filehandle Remote' warnings when using IE
153     return unless *STDOUT->opened();
154
155     # Prepend the headers if they have not yet been sent
156     if ( my $headers = delete $self->{_header_buf} ) {
157         $buffer = $headers . $buffer;
158     }
159     
160     my $ret = $self->NEXT::write( $c, $buffer );
161     
162     DEBUG && warn "write: Wrote response ($ret bytes)\n";
163     
164     if ( !defined $ret ) {
165         $self->{_write_error} = $!;
166     }
167     
168     return $ret;
169 }
170
171 =head2 run
172
173 =cut
174
175 # A very very simple HTTP server that initializes a CGI environment
176 sub run {
177     my ( $self, $class, $port, $host, $options ) = @_;
178
179     $options ||= {};
180     
181     $self->{options} = $options;
182
183     if ($options->{background}) {
184         my $child = fork;
185         die "Can't fork: $!" unless defined($child);
186         exit if $child;
187     }
188
189     my $restart = 0;
190     local $SIG{CHLD} = 'IGNORE';
191
192     my $allowed = $options->{allowed} || { '127.0.0.1' => '255.255.255.255' };
193     my $addr = $host ? inet_aton($host) : INADDR_ANY;
194     if ( $addr eq INADDR_ANY ) {
195         require Sys::Hostname;
196         $host = lc Sys::Hostname::hostname();
197     }
198     else {
199         $host = gethostbyaddr( $addr, AF_INET ) || inet_ntoa($addr);
200     }
201
202     # Handle requests
203
204     # Setup socket
205     my $daemon = IO::Socket::INET->new(
206         Listen    => SOMAXCONN,
207         LocalAddr => inet_ntoa($addr),
208         LocalPort => $port,
209         Proto     => 'tcp',
210         ReuseAddr => 1,
211         Type      => SOCK_STREAM,
212       )
213       or die "Couldn't create daemon: $!";
214
215     my $url = "http://$host";
216     $url .= ":$port" unless $port == 80;
217
218     print "You can connect to your server at $url\n";
219
220     if ($options->{background}) {
221         open STDIN,  "+</dev/null" or die $!;
222         open STDOUT, ">&STDIN"     or die $!;
223         open STDERR, ">&STDIN"     or die $!;
224         if ( $^O !~ /MSWin32/ ) {
225              require POSIX;
226              POSIX::setsid()
227                  or die "Can't start a new session: $!";
228         }
229     }
230
231     if (my $pidfile = $options->{pidfile}) {
232         if (! open PIDFILE, "> $pidfile") {
233             warn("Cannot open: $pidfile: $!");
234         }
235         print PIDFILE "$$\n";
236         close PIDFILE;
237     }
238
239     my $pid = undef;
240     
241     # Ignore broken pipes as an HTTP server should
242     local $SIG{PIPE} = 'IGNORE';
243     
244     # Restart on HUP
245     local $SIG{HUP} = sub { 
246         $restart = 1;
247         warn "Restarting server on SIGHUP...\n";
248     };
249     
250     LISTEN:
251     while ( !$restart ) {
252         while ( accept( Remote, $daemon ) ) {        
253             DEBUG && warn "New connection\n";
254
255             select Remote;
256
257             Remote->blocking(1);
258         
259             # Read until we see all headers
260             $self->{inputbuf} = '';
261             
262             if ( !$self->_read_headers ) {
263                 # Error reading, give up
264                 next LISTEN;
265             }
266
267             my ( $method, $uri, $protocol ) = $self->_parse_request_line;
268         
269             DEBUG && warn "Parsed request: $method $uri $protocol\n";
270         
271             next unless $method;
272
273             unless ( uc($method) eq 'RESTART' ) {
274
275                 # Fork
276                 if ( $options->{fork} ) { 
277                     if ( $pid = fork ) {
278                         DEBUG && warn "Forked child $pid\n";
279                         next;
280                     }
281                 }
282
283                 $self->_handler( $class, $port, $method, $uri, $protocol );
284             
285                 if ( my $error = delete $self->{_write_error} ) {
286                     DEBUG && warn "Write error: $error\n";
287                     close Remote;
288                     
289                     if ( !defined $pid ) {
290                         next LISTEN;
291                     }
292                 }
293
294                 if ( defined $pid ) {
295                     # Child process, close connection and exit
296                     DEBUG && warn "Child process exiting\n";
297                     $daemon->close;
298                     exit;
299                 }
300             }
301             else {
302                 my $sockdata = $self->_socket_data( \*Remote );
303                 my $ipaddr   = _inet_addr( $sockdata->{peeraddr} );
304                 my $ready    = 0;
305                 foreach my $ip ( keys %$allowed ) {
306                     my $mask = $allowed->{$ip};
307                     $ready = ( $ipaddr & _inet_addr($mask) ) == _inet_addr($ip);
308                     last if $ready;
309                 }
310                 if ($ready) {
311                     $restart = 1;
312                     last;
313                 }
314             }
315         }
316         continue {
317             close Remote;
318         }
319     }
320     
321     $daemon->close;
322     
323     DEBUG && warn "Shutting down\n";
324
325     if ($restart) {
326         $SIG{CHLD} = 'DEFAULT';
327         wait;
328
329         ### if the standalone server was invoked with perl -I .. we will loose
330         ### those include dirs upon re-exec. So add them to PERL5LIB, so they
331         ### are available again for the exec'ed process --kane
332         use Config;
333         $ENV{PERL5LIB} .= join $Config{path_sep}, @INC; 
334         
335         exec $^X . ' "' . $0 . '" ' . join( ' ', @{ $options->{argv} } );
336     }
337
338     exit;
339 }
340
341 sub _handler {
342     my ( $self, $class, $port, $method, $uri, $protocol ) = @_;
343
344     local *STDIN  = \*Remote;
345     local *STDOUT = \*Remote;
346
347     # We better be careful and just use 1.0
348     $protocol = '1.0';
349
350     my $sockdata    = $self->_socket_data( \*Remote );
351     my %copy_of_env = %ENV;
352
353     my $sel = IO::Select->new;
354     $sel->add( \*STDIN );
355     
356     REQUEST:
357     while (1) {
358         my ( $path, $query_string ) = split /\?/, $uri, 2;
359         
360         # Initialize CGI environment
361         local %ENV = (
362             PATH_INFO       => $path         || '',
363             QUERY_STRING    => $query_string || '',
364             REMOTE_ADDR     => $sockdata->{peeraddr},
365             REMOTE_HOST     => $sockdata->{peername},
366             REQUEST_METHOD  => $method || '',
367             SERVER_NAME     => $sockdata->{localname},
368             SERVER_PORT     => $port,
369             SERVER_PROTOCOL => "HTTP/$protocol",
370             %copy_of_env,
371         );
372
373         # Parse headers
374         if ( $protocol >= 1 ) {
375             $self->_parse_headers;
376         }
377
378         # Pass flow control to Catalyst
379         $class->handle_request;
380     
381         DEBUG && warn "Request done\n";
382     
383         # Allow keepalive requests, this is a hack but we'll support it until
384         # the next major release.
385         if ( delete $self->{_keepalive} ) {
386             
387             DEBUG && warn "Reusing previous connection for keep-alive request\n";
388             
389             if ( $sel->can_read(1) ) {            
390                 if ( !$self->_read_headers ) {
391                     # Error reading, give up
392                     last REQUEST;
393                 }
394
395                 ( $method, $uri, $protocol ) = $self->_parse_request_line;
396                 
397                 DEBUG && warn "Parsed request: $method $uri $protocol\n";
398                 
399                 # Force HTTP/1.0
400                 $protocol = '1.0';
401                 
402                 next REQUEST;
403             }
404             
405             DEBUG && warn "No keep-alive request within 1 second\n";
406         }
407         
408         last REQUEST;
409     }
410     
411     DEBUG && warn "Closing connection\n";
412
413     close Remote;
414 }
415
416 sub _read_headers {
417     my $self = shift;
418     
419     while (1) {
420         my $read = sysread Remote, my $buf, CHUNKSIZE;
421     
422         if ( !$read ) {
423             DEBUG && warn "EOF or error: $!\n";
424             return;
425         }
426     
427         DEBUG && warn "Read $read bytes\n";
428         $self->{inputbuf} .= $buf;
429         last if $self->{inputbuf} =~ /(\x0D\x0A?\x0D\x0A?|\x0A\x0D?\x0A\x0D?)/s;
430     }
431     
432     return 1;
433 }
434
435 sub _parse_request_line {
436     my $self = shift;
437
438     # Parse request line    
439     if ( $self->{inputbuf} !~ s/^(\w+)[ \t]+(\S+)(?:[ \t]+(HTTP\/\d+\.\d+))?[^\012]*\012// ) {
440         return ();
441     }
442     
443     my $method = $1;
444     my $uri    = $2;
445     my $proto  = $3 || 'HTTP/0.9';
446     
447     return ( $method, $uri, $proto );
448 }
449
450 sub _parse_headers {
451     my $self = shift;
452     
453     # Copy the buffer for header parsing, and remove the header block
454     # from the content buffer.
455     my $buf = $self->{inputbuf};
456     $self->{inputbuf} =~ s/.*?(\x0D\x0A?\x0D\x0A?|\x0A\x0D?\x0A\x0D?)//s;
457     
458     # Parse headers
459     my $headers = HTTP::Headers->new;
460     my ($key, $val);
461     HEADER:
462     while ( $buf =~ s/^([^\012]*)\012// ) {
463         $_ = $1;
464         s/\015$//;
465         if ( /^([\w\-~]+)\s*:\s*(.*)/ ) {
466             $headers->push_header( $key, $val ) if $key;
467             ($key, $val) = ($1, $2);
468         }
469         elsif ( /^\s+(.*)/ ) {
470             $val .= " $1";
471         }
472         else {
473             last HEADER;
474         }
475     }
476     $headers->push_header( $key, $val ) if $key;
477     
478     DEBUG && warn "Parsed headers: " . dump($headers) . "\n";
479
480     # Convert headers into ENV vars
481     $headers->scan( sub {
482         my ( $key, $val ) = @_;
483         
484         $key = uc $key;
485         $key = 'COOKIE' if $key eq 'COOKIES';
486         $key =~ tr/-/_/;
487         $key = 'HTTP_' . $key
488             unless $key =~ m/\A(?:CONTENT_(?:LENGTH|TYPE)|COOKIE)\z/;
489             
490         if ( exists $ENV{$key} ) {
491             $ENV{$key} .= ", $val";
492         }
493         else {
494             $ENV{$key} = $val;
495         }
496     } );
497 }
498
499 sub _socket_data {
500     my ( $self, $handle ) = @_;
501
502     my $remote_sockaddr       = getpeername($handle);
503     my ( undef, $iaddr )      = $remote_sockaddr 
504         ? sockaddr_in($remote_sockaddr) 
505         : (undef, undef);
506         
507     my $local_sockaddr        = getsockname($handle);
508     my ( undef, $localiaddr ) = sockaddr_in($local_sockaddr);
509
510     # This mess is necessary to keep IE from crashing the server
511     my $data = {
512         peername  => $iaddr 
513             ? ( gethostbyaddr( $iaddr, AF_INET ) || 'localhost' )
514             : 'localhost',
515         peeraddr  => $iaddr 
516             ? ( inet_ntoa($iaddr) || '127.0.0.1' )
517             : '127.0.0.1',
518         localname => gethostbyaddr( $localiaddr, AF_INET ) || 'localhost',
519         localaddr => inet_ntoa($localiaddr) || '127.0.0.1',
520     };
521
522     return $data;
523 }
524
525 sub _inet_addr { unpack "N*", inet_aton( $_[0] ) }
526
527 =head1 SEE ALSO
528
529 L<Catalyst>, L<Catalyst::Engine>.
530
531 =head1 AUTHORS
532
533 Sebastian Riedel, <sri@cpan.org>
534
535 Dan Kubb, <dan.kubb-cpan@onautopilot.com>
536
537 Sascha Kiefer, <esskar@cpan.org>
538
539 Andy Grundman, <andy@hybridized.org>
540
541 =head1 THANKS
542
543 Many parts are ripped out of C<HTTP::Server::Simple> by Jesse Vincent.
544
545 =head1 COPYRIGHT
546
547 This program is free software, you can redistribute it and/or modify it under
548 the same terms as Perl itself.
549
550 =cut
551
552 1;