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
$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;
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);
}
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;
+ }
}
}
}
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;