Added recursive -r flag to prove example
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Engine / HTTP / Daemon.pm
index 6da6e65..00daf22 100644 (file)
@@ -3,7 +3,32 @@ package Catalyst::Engine::HTTP::Daemon;
 use strict;
 use base 'Catalyst::Engine::HTTP::Base';
 
+use Catalyst::Exception;
 use IO::Select;
+use IO::Socket;
+
+BEGIN {
+
+    if ( $^O eq 'MSWin32' ) {
+
+        *EINTR       = sub { 10004 };
+        *EINPROGRESS = sub { 10036 };
+        *EWOULDBLOCK = sub { 10035 };
+        *F_GETFL     = sub {     0 };
+        *F_SETFL     = sub {     0 };
+
+        *IO::Socket::blocking = sub {
+            my ( $self, $blocking ) = @_;
+            my $nonblocking = $blocking ? 0 : 1;
+            ioctl( $self, 0x8004667e, \$nonblocking );
+        };
+    }
+
+    else {
+        Errno->require;
+        Errno->import( qw[EWOULDBLOCK EINPROGRESS EINTR] );
+    }
+}
 
 =head1 NAME
 
@@ -64,14 +89,17 @@ sub run {
     $SIG{'PIPE'} = 'IGNORE';
 
     my $daemon = Catalyst::Engine::HTTP::Daemon::Catalyst->new(
-        Listen    => 1,
+        Listen    => SOMAXCONN,
         LocalPort => $port,
         ReuseAddr => 1,
         Timeout   => 5
     );
 
     unless ( defined $daemon ) {
-        die(qq/Failed to create daemon. Reason: '$!'/);
+
+        Catalyst::Exception->throw(
+            message => qq/Failed to create daemon. Reason: '$!'/
+        );
     }
 
     my $base = URI->new( $daemon->url )->canonical;
@@ -82,10 +110,11 @@ sub run {
 
     while (1) {
 
-        for my $client ( $select->can_read ) {
+        for my $client ( $select->can_read(0.01) ) {
 
             if ( $client == $daemon ) {
                 $client = $daemon->accept;
+                $client->timestamp = time;
                 $client->blocking(0);
                 $select->add($client);
             }
@@ -94,46 +123,114 @@ sub run {
                 next if $client->request;
                 next if $client->response;
 
-                my $read = $client->sysread( my $buf, 4096 );
-                
-                unless ( defined($read) && length($buf) ) {
-             
+                my $nread = $client->sysread( my $buf, 4096 );
+
+                unless ( $nread ) {
+
+                    next if $! == EWOULDBLOCK;
+                    next if $! == EINPROGRESS;
+                    next if $! == EINTR;
+
                     $select->remove($client);
                     $client->close;
 
                     next;
                 }
 
-                $client->read_buffer($buf);
-                $client->request( $client->get_request );
+                $client->request_buffer .= $buf;
+
+                if ( my $request = $client->get_request ) {
+                    $client->request   = $request;
+                    $client->timestamp = time
+                }
             }
         }
 
         for my $client ( $select->handles ) {
 
             next if $client == $daemon;
+
+            if ( ( time - $client->timestamp ) > 60 ) {
+
+                $select->remove($client);
+                $client->close;
+
+                next;
+            }
+
             next if $client->response;
             next unless $client->request;
 
-            $client->response( HTTP::Response->new );
-            $class->handler( $client->request, $client->response, $client );    
+            $client->response = HTTP::Response->new;
+            $client->response->protocol( $client->request->protocol );
+
+            $class->handler( $client->request, $client->response, $client );
         }
 
-        for my $client ( $select->can_write(0) ) {
+        for my $client ( $select->can_write(0.01) ) {
 
             next unless $client->response;
 
-            $client->send_response( $client->response );
+            unless ( $client->response_buffer ) {
+
+                $client->response->header( Server => $daemon->product_tokens );
+
+                my $connection = $client->request->header('Connection') || '';
+
+                if ( $connection =~ /Keep-Alive/i ) {
+                    $client->response->header( 'Connection' => 'Keep-Alive' );
+                    $client->response->header( 'Keep-Alive' => 'timeout=60, max=100' );
+                }
+
+                if ( $connection =~ /close/i ) {
+                    $client->response->header( 'Connection' => 'close' );
+                }
+
+                $client->response_buffer = $client->response->as_string("\x0D\x0A");
+                $client->response_offset = 0;
+            }
+
+            my $nwrite = $client->syswrite( $client->response_buffer,
+                                            $client->response_length,
+                                            $client->response_offset );
 
-            my $connection = $client->request->header('Connection');
+            unless ( $nwrite ) {
+
+                next if $! == EWOULDBLOCK;
+                next if $! == EINPROGRESS;
+                next if $! == EINTR;
 
-            unless ( $connection && $connection =~ /Keep-Alive/i ) {
                 $select->remove($client);
                 $client->close;
+
+                next;
             }
 
-            $client->request(undef);
-            $client->response(undef);
+            $client->response_offset += $nwrite;
+
+            if ( $client->response_offset == $client->response_length ) {
+
+                my $connection = $client->request->header('Connection') || '';
+                my $protocol   = $client->request->protocol;
+                my $persistent = 0;
+
+                if ( $protocol eq 'HTTP/1.1' && $connection !~ /close/i ) {
+                    $persistent++;
+                }
+
+                if ( $protocol ne 'HTTP/1.1' && $connection =~ /Keep-Alive/i ) {
+                    $persistent++;
+                }
+
+                unless ( $persistent ) {
+                    $select->remove($client);
+                    $client->close;
+                }
+
+                $client->response        = undef;
+                $client->request         = undef;
+                $client->response_buffer = undef;
+            }
         }
     }
 }
@@ -175,34 +272,39 @@ package Catalyst::Engine::HTTP::Daemon::Client;
 use strict;
 use base 'HTTP::Daemon::ClientConn';
 
-sub read_buffer {
+sub request : lvalue {
     my $self = shift;
-
-    if (@_) {
-        ${*$self}{'httpd_rbuf'} .= shift;
-    }
-
-    return ${*$self}{'httpd_rbuf'};
+    ${*$self}{'request'};
 }
 
-sub request {
+sub request_buffer : lvalue {
     my $self = shift;
+    ${*$self}{'httpd_rbuf'};
+}
 
-    if (@_) {
-        ${*$self}{'request'} = shift;
-    }
+sub response : lvalue {
+    my $self = shift;
+    ${*$self}{'response'};
+}
 
-    return ${*$self}{'request'};
+sub response_buffer : lvalue {
+    my $self = shift;
+    ${*$self}{'httpd_wbuf'};
 }
 
-sub response {
+sub response_length {
     my $self = shift;
+    return length( $self->response_buffer );
+}
 
-    if (@_) {
-        ${*$self}{'response'} = shift;
-    }
+sub response_offset : lvalue {
+    my $self = shift;
+    ${*$self}{'httpd_woffset'};
+}
 
-    return ${*$self}{'response'};
+sub timestamp : lvalue {
+    my $self = shift;
+    ${*$self}{'timestamp'};
 }
 
 1;