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