Added recursive -r flag to prove example
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Engine / HTTP / Daemon.pm
CommitLineData
2cdfbf5e 1package Catalyst::Engine::HTTP::Daemon;
2
3use strict;
4use base 'Catalyst::Engine::HTTP::Base';
5
a2f2cde9 6use Catalyst::Exception;
8a0ec4fd 7use IO::Select;
89f2bd8d 8use IO::Socket;
2cdfbf5e 9
73ad9769 10BEGIN {
11
12 if ( $^O eq 'MSWin32' ) {
13
16d65a0e 14 *EINTR = sub { 10004 };
15 *EINPROGRESS = sub { 10036 };
16 *EWOULDBLOCK = sub { 10035 };
17 *F_GETFL = sub { 0 };
18 *F_SETFL = sub { 0 };
19
20 *IO::Socket::blocking = sub {
21 my ( $self, $blocking ) = @_;
22 my $nonblocking = $blocking ? 0 : 1;
23 ioctl( $self, 0x8004667e, \$nonblocking );
24 };
73ad9769 25 }
26
27 else {
28 Errno->require;
16d65a0e 29 Errno->import( qw[EWOULDBLOCK EINPROGRESS EINTR] );
73ad9769 30 }
31}
32
2cdfbf5e 33=head1 NAME
34
35Catalyst::Engine::HTTP::Daemon - Catalyst HTTP Daemon Engine
36
37=head1 SYNOPSIS
38
39A script using the Catalyst::Engine::HTTP::Daemon module might look like:
40
41 #!/usr/bin/perl -w
42
43 BEGIN { $ENV{CATALYST_ENGINE} = 'HTTP::Daemon' }
44
45 use strict;
46 use lib '/path/to/MyApp/lib';
47 use MyApp;
48
49 MyApp->run;
50
51=head1 DESCRIPTION
52
53This is the Catalyst engine specialized for development and testing.
54
55=head1 OVERLOADED METHODS
56
57This class overloads some methods from C<Catalyst::Engine::HTTP::Base>.
58
59=over 4
60
bce14c0d 61=item $c->handler
2cdfbf5e 62
63=cut
64
bce14c0d 65sub handler {
8a0ec4fd 66 my ( $class, $request, $response, $client ) = @_;
bce14c0d 67
8a0ec4fd 68 $request->uri->scheme('http'); # Force URI::http
69 $request->uri->host( $request->header('Host') || $client->sockhost );
70 $request->uri->port( $client->sockport );
bce14c0d 71
8a0ec4fd 72 my $http = Catalyst::Engine::HTTP::Base::struct->new(
73 address => $client->peerhost,
74 request => $request,
75 response => $response
76 );
bce14c0d 77
8a0ec4fd 78 $class->SUPER::handler($http);
bce14c0d 79}
80
81=item $c->run
82
83=cut
2cdfbf5e 84
85sub run {
86 my $class = shift;
87 my $port = shift || 3000;
8a0ec4fd 88
bce14c0d 89 $SIG{'PIPE'} = 'IGNORE';
2cdfbf5e 90
296e7663 91 my $daemon = Catalyst::Engine::HTTP::Daemon::Catalyst->new(
89f2bd8d 92 Listen => SOMAXCONN,
2cdfbf5e 93 LocalPort => $port,
94 ReuseAddr => 1,
8a0ec4fd 95 Timeout => 5
2cdfbf5e 96 );
8a0ec4fd 97
b4ca0ee8 98 unless ( defined $daemon ) {
a2f2cde9 99
100 Catalyst::Exception->throw(
ebfde331 101 message => qq/Failed to create daemon. Reason: '$!'/
a2f2cde9 102 );
b4ca0ee8 103 }
2cdfbf5e 104
2cdfbf5e 105 my $base = URI->new( $daemon->url )->canonical;
106
107 printf( "You can connect to your server at %s\n", $base );
108
8a0ec4fd 109 my $select = IO::Select->new($daemon);
110
111 while (1) {
112
61b19d8b 113 for my $client ( $select->can_read(0.01) ) {
8a0ec4fd 114
115 if ( $client == $daemon ) {
116 $client = $daemon->accept;
c7b7c423 117 $client->timestamp = time;
8a0ec4fd 118 $client->blocking(0);
119 $select->add($client);
120 }
121
122 else {
123 next if $client->request;
124 next if $client->response;
125
ad1fb680 126 my $nread = $client->sysread( my $buf, 4096 );
abdcb6e4 127
aea15dfa 128 unless ( $nread ) {
d290eee8 129
aea15dfa 130 next if $! == EWOULDBLOCK;
131 next if $! == EINPROGRESS;
d290eee8 132 next if $! == EINTR;
abdcb6e4 133
8a0ec4fd 134 $select->remove($client);
135 $client->close;
136
137 next;
138 }
139
abdcb6e4 140 $client->request_buffer .= $buf;
c7b7c423 141
142 if ( my $request = $client->get_request ) {
143 $client->request = $request;
144 $client->timestamp = time
145 }
8a0ec4fd 146 }
147 }
148
149 for my $client ( $select->handles ) {
150
151 next if $client == $daemon;
c7b7c423 152
153 if ( ( time - $client->timestamp ) > 60 ) {
154
155 $select->remove($client);
156 $client->close;
157
158 next;
159 }
160
8a0ec4fd 161 next if $client->response;
162 next unless $client->request;
163
abdcb6e4 164 $client->response = HTTP::Response->new;
165 $client->response->protocol( $client->request->protocol );
c7b7c423 166
abdcb6e4 167 $class->handler( $client->request, $client->response, $client );
8a0ec4fd 168 }
169
61b19d8b 170 for my $client ( $select->can_write(0.01) ) {
8a0ec4fd 171
172 next unless $client->response;
173
abdcb6e4 174 unless ( $client->response_buffer ) {
61b19d8b 175
d290eee8 176 $client->response->header( Server => $daemon->product_tokens );
177
0347394a 178 my $connection = $client->request->header('Connection') || '';
61b19d8b 179
0347394a 180 if ( $connection =~ /Keep-Alive/i ) {
61b19d8b 181 $client->response->header( 'Connection' => 'Keep-Alive' );
182 $client->response->header( 'Keep-Alive' => 'timeout=60, max=100' );
183 }
184
0347394a 185 if ( $connection =~ /close/i ) {
d290eee8 186 $client->response->header( 'Connection' => 'close' );
187 }
188
189 $client->response_buffer = $client->response->as_string("\x0D\x0A");
abdcb6e4 190 $client->response_offset = 0;
191 }
192
ad1fb680 193 my $nwrite = $client->syswrite( $client->response_buffer,
194 $client->response_length,
195 $client->response_offset );
8a0ec4fd 196
aea15dfa 197 unless ( $nwrite ) {
d290eee8 198
aea15dfa 199 next if $! == EWOULDBLOCK;
200 next if $! == EINPROGRESS;
d290eee8 201 next if $! == EINTR;
8a0ec4fd 202
8a0ec4fd 203 $select->remove($client);
204 $client->close;
abdcb6e4 205
206 next;
8a0ec4fd 207 }
208
ad1fb680 209 $client->response_offset += $nwrite;
c7b7c423 210
abdcb6e4 211 if ( $client->response_offset == $client->response_length ) {
212
0347394a 213 my $connection = $client->request->header('Connection') || '';
d290eee8 214 my $protocol = $client->request->protocol;
0347394a 215 my $persistent = 0;
d290eee8 216
0347394a 217 if ( $protocol eq 'HTTP/1.1' && $connection !~ /close/i ) {
d290eee8 218 $persistent++;
d290eee8 219 }
220
0347394a 221 if ( $protocol ne 'HTTP/1.1' && $connection =~ /Keep-Alive/i ) {
222 $persistent++;
d290eee8 223 }
abdcb6e4 224
d290eee8 225 unless ( $persistent ) {
abdcb6e4 226 $select->remove($client);
227 $client->close;
228 }
229
230 $client->response = undef;
231 $client->request = undef;
232 $client->response_buffer = undef;
233 }
8a0ec4fd 234 }
2cdfbf5e 235 }
236}
237
238=back
239
240=head1 SEE ALSO
241
8a0ec4fd 242L<Catalyst>, L<Catalyst::Engine>, L<Catalyst::Engine::HTTP::Base>,
2cdfbf5e 243L<HTTP::Daemon>.
244
245=head1 AUTHOR
246
247Sebastian Riedel, C<sri@cpan.org>
248Christian Hansen, C<ch@ngmedia.com>
249
250=head1 COPYRIGHT
251
252This program is free software, you can redistribute it and/or modify it under
253the same terms as Perl itself.
254
255=cut
256
296e7663 257package Catalyst::Engine::HTTP::Daemon::Catalyst;
2cdfbf5e 258
259use strict;
260use base 'HTTP::Daemon';
261
8a0ec4fd 262sub accept {
263 return shift->SUPER::accept('Catalyst::Engine::HTTP::Daemon::Client');
264}
265
2cdfbf5e 266sub product_tokens {
8a0ec4fd 267 return "Catalyst/$Catalyst::VERSION";
268}
269
270package Catalyst::Engine::HTTP::Daemon::Client;
271
272use strict;
273use base 'HTTP::Daemon::ClientConn';
274
abdcb6e4 275sub request : lvalue {
8a0ec4fd 276 my $self = shift;
abdcb6e4 277 ${*$self}{'request'};
8a0ec4fd 278}
279
abdcb6e4 280sub request_buffer : lvalue {
8a0ec4fd 281 my $self = shift;
abdcb6e4 282 ${*$self}{'httpd_rbuf'};
283}
8a0ec4fd 284
abdcb6e4 285sub response : lvalue {
286 my $self = shift;
287 ${*$self}{'response'};
8a0ec4fd 288}
289
abdcb6e4 290sub response_buffer : lvalue {
8a0ec4fd 291 my $self = shift;
abdcb6e4 292 ${*$self}{'httpd_wbuf'};
293}
8a0ec4fd 294
abdcb6e4 295sub response_length {
296 my $self = shift;
297 return length( $self->response_buffer );
298}
8a0ec4fd 299
abdcb6e4 300sub response_offset : lvalue {
301 my $self = shift;
302 ${*$self}{'httpd_woffset'};
2cdfbf5e 303}
304
c7b7c423 305sub timestamp : lvalue {
306 my $self = shift;
307 ${*$self}{'timestamp'};
308}
abdcb6e4 309
2cdfbf5e 3101;