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