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