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