use strict;
use base 'Catalyst::Engine::HTTP::Base';
-use IO::Socket qw(AF_INET INADDR_ANY SOCK_STREAM SOMAXCONN);
+use IO::Select;
+use IO::Socket;
+
+BEGIN {
+
+ if ( $^O eq 'MSWin32' ) {
+
+ *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] );
+ }
+}
=head1 NAME
=over 4
-=item $c->run
+=item $c->handler
=cut
-$SIG{'PIPE'} = 'IGNORE';
+sub handler {
+ my ( $class, $request, $response, $client ) = @_;
+
+ $request->uri->scheme('http'); # Force URI::http
+ $request->uri->host( $request->header('Host') || $client->sockhost );
+ $request->uri->port( $client->sockport );
+
+ my $http = Catalyst::Engine::HTTP::Base::struct->new(
+ address => $client->peerhost,
+ request => $request,
+ response => $response
+ );
+
+ $class->SUPER::handler($http);
+}
+
+=item $c->run
+
+=cut
sub run {
my $class = shift;
my $port = shift || 3000;
- my $daemon = Catalyst::Engine::HTTP::Catalyst->new(
+ $SIG{'PIPE'} = 'IGNORE';
+
+ my $daemon = Catalyst::Engine::HTTP::Daemon::Catalyst->new(
Listen => SOMAXCONN,
LocalPort => $port,
ReuseAddr => 1,
- Type => SOCK_STREAM,
+ Timeout => 5
);
- unless ($daemon) {
- die("Failed to create daemon: $!\n");
+ unless ( defined $daemon ) {
+ die(qq/Failed to create daemon. Reason: '$!'/);
}
my $base = URI->new( $daemon->url )->canonical;
printf( "You can connect to your server at %s\n", $base );
- while ( my $connection = $daemon->accept ) {
+ my $select = IO::Select->new($daemon);
+
+ while (1) {
+
+ for my $client ( $select->can_read(1) ) {
+
+ if ( $client == $daemon ) {
+ $client = $daemon->accept;
+ $client->timestamp = time;
+ $client->blocking(0);
+ $select->add($client);
+ }
+
+ else {
+ next if $client->request;
+ next if $client->response;
+
+ my $nread = $client->sysread( my $buf, 4096 );
+
+ unless ( defined($nread) && length($buf) ) {
+
+ $select->remove($client);
+ $client->close;
+
+ next;
+ }
+
+ $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;
- $connection->timeout(5);
+ if ( ( time - $client->timestamp ) > 60 ) {
- while ( my $request = $connection->get_request ) {
+ $select->remove($client);
+ $client->close;
- $request->uri->scheme('http'); # Force URI::http
- $request->uri->host( $request->header('Host') || $base->host );
- $request->uri->port( $base->port );
-
- my $hostname = gethostbyaddr( $connection->peeraddr, AF_INET );
+ next;
+ }
- my $http = Catalyst::Engine::HTTP::Base::struct->new(
- address => $connection->peerhost,
- hostname => $hostname || $connection->peerhost,
- request => $request,
- response => HTTP::Response->new
- );
+ next if $client->response;
+ next unless $client->request;
- $class->handler($http);
- $connection->send_response( $http->response );
+ $client->response = HTTP::Response->new;
+ $client->response->protocol( $client->request->protocol );
+ $class->handler( $client->request, $client->response, $client );
}
- $connection->close;
- undef($connection);
+ for my $client ( $select->can_write(1) ) {
+
+ next unless $client->response;
+
+ unless ( $client->response_buffer ) {
+ $client->response_buffer = $client->response->as_string;
+ $client->response_offset = 0;
+ }
+
+ my $nwrite = $client->syswrite( $client->response_buffer,
+ $client->response_length,
+ $client->response_offset );
+
+ unless ( defined($nwrite) ) {
+
+ $select->remove($client);
+ $client->close;
+
+ next;
+ }
+
+ $client->response_offset += $nwrite;
+
+ if ( $client->response_offset == $client->response_length ) {
+
+ my $connection = $client->request->header('Connection');
+
+ unless ( $connection && $connection =~ /Keep-Alive/i ) {
+ $select->remove($client);
+ $client->close;
+ }
+
+ $client->response = undef;
+ $client->request = undef;
+ $client->response_buffer = undef;
+ }
+ }
}
}
=head1 SEE ALSO
-L<Catalyst>, L<Catalyst::Engine>, L<Catalyst::Engine::HTTP::Base>,
+L<Catalyst>, L<Catalyst::Engine>, L<Catalyst::Engine::HTTP::Base>,
L<HTTP::Daemon>.
=head1 AUTHOR
=cut
-package Catalyst::Engine::HTTP::Catalyst;
+package Catalyst::Engine::HTTP::Daemon::Catalyst;
use strict;
use base 'HTTP::Daemon';
+sub accept {
+ return shift->SUPER::accept('Catalyst::Engine::HTTP::Daemon::Client');
+}
+
sub product_tokens {
- "Catalyst/$Catalyst::VERSION";
+ return "Catalyst/$Catalyst::VERSION";
+}
+
+package Catalyst::Engine::HTTP::Daemon::Client;
+
+use strict;
+use base 'HTTP::Daemon::ClientConn';
+
+sub request : lvalue {
+ my $self = shift;
+ ${*$self}{'request'};
+}
+
+sub request_buffer : lvalue {
+ my $self = shift;
+ ${*$self}{'httpd_rbuf'};
+}
+
+sub response : lvalue {
+ my $self = shift;
+ ${*$self}{'response'};
+}
+
+sub response_buffer : lvalue {
+ my $self = shift;
+ ${*$self}{'httpd_wbuf'};
+}
+
+sub response_length {
+ my $self = shift;
+ return length( $self->response_buffer );
+}
+
+sub response_offset : lvalue {
+ my $self = shift;
+ ${*$self}{'httpd_woffset'};
+}
+
+sub timestamp : lvalue {
+ my $self = shift;
+ ${*$self}{'timestamp'};
}
1;