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