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