1 package Catalyst::Engine::HTTP::Daemon;
4 use base 'Catalyst::Engine::HTTP::Base';
6 use Catalyst::Exception;
12 if ( $^O eq 'MSWin32' ) {
14 *EINTR = sub { 10004 };
15 *EINPROGRESS = sub { 10036 };
16 *EWOULDBLOCK = sub { 10035 };
20 *IO::Socket::blocking = sub {
21 my ( $self, $blocking ) = @_;
22 my $nonblocking = $blocking ? 0 : 1;
23 ioctl( $self, 0x8004667e, \$nonblocking );
29 Errno->import( qw[EWOULDBLOCK EINPROGRESS EINTR] );
35 Catalyst::Engine::HTTP::Daemon - Catalyst HTTP Daemon Engine
39 A script using the Catalyst::Engine::HTTP::Daemon module might look like:
43 BEGIN { $ENV{CATALYST_ENGINE} = 'HTTP::Daemon' }
46 use lib '/path/to/MyApp/lib';
53 This is the Catalyst engine specialized for development and testing.
55 =head1 OVERLOADED METHODS
57 This class overloads some methods from C<Catalyst::Engine::HTTP::Base>.
66 my ( $class, $request, $response, $client ) = @_;
68 $request->uri->scheme('http'); # Force URI::http
69 $request->uri->host( $request->header('Host') || $client->sockhost );
70 $request->uri->port( $client->sockport );
72 my $http = Catalyst::Engine::HTTP::Base::struct->new(
73 address => $client->peerhost,
78 $class->SUPER::handler($http);
87 my $port = shift || 3000;
89 $SIG{'PIPE'} = 'IGNORE';
91 my $daemon = Catalyst::Engine::HTTP::Daemon::Catalyst->new(
98 unless ( defined $daemon ) {
100 Catalyst::Exception->throw(
101 message => qq/Failed to create daemon. Reason: '$!'/
105 my $base = URI->new( $daemon->url )->canonical;
107 printf( "You can connect to your server at %s\n", $base );
109 my $select = IO::Select->new($daemon);
113 for my $client ( $select->can_read(0.01) ) {
115 if ( $client == $daemon ) {
116 $client = $daemon->accept;
117 $client->timestamp = time;
118 $client->blocking(0);
119 $select->add($client);
123 next if $client->request;
124 next if $client->response;
126 my $nread = $client->sysread( my $buf, 4096 );
130 next if $! == EWOULDBLOCK;
131 next if $! == EINPROGRESS;
134 $select->remove($client);
140 $client->request_buffer .= $buf;
142 if ( my $request = $client->get_request ) {
143 $client->request = $request;
144 $client->timestamp = time
149 for my $client ( $select->handles ) {
151 next if $client == $daemon;
153 if ( ( time - $client->timestamp ) > 60 ) {
155 $select->remove($client);
161 next if $client->response;
162 next unless $client->request;
164 $client->response = HTTP::Response->new;
165 $client->response->protocol( $client->request->protocol );
167 $class->handler( $client->request, $client->response, $client );
170 for my $client ( $select->can_write(0.01) ) {
172 next unless $client->response;
174 unless ( $client->response_buffer ) {
176 $client->response->header( Server => $daemon->product_tokens );
178 my $connection = $client->request->header('Connection') || '';
180 if ( $connection =~ /Keep-Alive/i ) {
181 $client->response->header( 'Connection' => 'Keep-Alive' );
182 $client->response->header( 'Keep-Alive' => 'timeout=60, max=100' );
185 if ( $connection =~ /close/i ) {
186 $client->response->header( 'Connection' => 'close' );
189 $client->response_buffer = $client->response->as_string("\x0D\x0A");
190 $client->response_offset = 0;
193 my $nwrite = $client->syswrite( $client->response_buffer,
194 $client->response_length,
195 $client->response_offset );
199 next if $! == EWOULDBLOCK;
200 next if $! == EINPROGRESS;
203 $select->remove($client);
209 $client->response_offset += $nwrite;
211 if ( $client->response_offset == $client->response_length ) {
213 my $connection = $client->request->header('Connection') || '';
214 my $protocol = $client->request->protocol;
217 if ( $protocol eq 'HTTP/1.1' && $connection !~ /close/i ) {
221 if ( $protocol ne 'HTTP/1.1' && $connection =~ /Keep-Alive/i ) {
225 unless ( $persistent ) {
226 $select->remove($client);
230 $client->response = undef;
231 $client->request = undef;
232 $client->response_buffer = undef;
242 L<Catalyst>, L<Catalyst::Engine>, L<Catalyst::Engine::HTTP::Base>,
247 Sebastian Riedel, C<sri@cpan.org>
248 Christian Hansen, C<ch@ngmedia.com>
252 This program is free software, you can redistribute it and/or modify it under
253 the same terms as Perl itself.
257 package Catalyst::Engine::HTTP::Daemon::Catalyst;
260 use base 'HTTP::Daemon';
263 return shift->SUPER::accept('Catalyst::Engine::HTTP::Daemon::Client');
267 return "Catalyst/$Catalyst::VERSION";
270 package Catalyst::Engine::HTTP::Daemon::Client;
273 use base 'HTTP::Daemon::ClientConn';
275 sub request : lvalue {
277 ${*$self}{'request'};
280 sub request_buffer : lvalue {
282 ${*$self}{'httpd_rbuf'};
285 sub response : lvalue {
287 ${*$self}{'response'};
290 sub response_buffer : lvalue {
292 ${*$self}{'httpd_wbuf'};
295 sub response_length {
297 return length( $self->response_buffer );
300 sub response_offset : lvalue {
302 ${*$self}{'httpd_woffset'};
305 sub timestamp : lvalue {
307 ${*$self}{'timestamp'};