File Coverage

blib/lib/POE/Component/FastCGI.pm
Criterion Covered Total %
statement 25 76 32.8
branch 4 32 12.5
condition 3 18 16.6
subroutine 9 19 47.3
pod 1 1 100.0
total 42 146 28.7


line stmt bran cond sub pod time code
1             package POE::Component::FastCGI;
2             $POE::Component::FastCGI::VERSION = '0.20';
3 1     1   73733 use strict;
  1         2  
  1         27  
4              
5 1     1   5 use Carp qw(croak);
  1         1  
  1         37  
6 1     1   4 use Socket qw(AF_UNIX);
  1         1  
  1         164  
7              
8 1         6 use POE qw(
9             Component::Server::TCP
10             Wheel::ReadWrite
11             Driver::SysRW
12 1     1   741 Filter::FastCGI);
  1         16173  
13              
14 1     1   412 use POE::Component::FastCGI::Request;
  1         4  
  1         33  
15 1     1   5 use POE::Component::FastCGI::Response;
  1         2  
  1         919  
16              
17             sub new {
18 1     1 1 2162 my($class, %args) = @_;
19              
20             croak "No port or address to listen on configured"
21             unless defined $args{Port} or (defined $args{Unix} and defined
22 1 50 33     10 $args{Address});
      33        
23             croak "No handlers defined" unless defined $args{Auth} or defined
24 1 50 33     5 $args{Handlers};
25              
26             my $session_id = POE::Session->create(
27             inline_states => {
28             _start => \&_start,
29             accept => \&_accept,
30             input => \&_input,
31             error => \&_error,
32             client_shutdown => \&_client_shutdown,
33              
34             # For graceful external shutdown
35             shutdown => \&_shutdown,
36              
37             # triggered from PoCo::FastCGI::Response in order to make sure
38             # we're writing to our wheel from the correct session.
39             w_send => \&_w_send,
40             w_write => \&_w_write,
41             w_close => \&_w_close,
42              
43             # Dummys to keep of warnings
44       0     _stop => sub {},
45       1     _child => sub {}
46             },
47 1         16 heap => \%args,
48             )->ID;
49              
50 1         197 return $session_id;
51             }
52              
53             sub _start {
54 1     1   826 my($session, $heap) = @_[SESSION, HEAP];
55              
56             $heap->{server} = POE::Component::Server::TCP->new(
57             Port => $heap->{Port},
58             (defined $heap->{Unix} ? (Domain => AF_UNIX) : ()),
59             (defined $heap->{Address} ? (Address => $heap->{Address}) : ()),
60             Acceptor => sub {
61 0     0     $poe_kernel->post($session => accept => @_[ARG0, ARG1, ARG2]);
62             }
63 1 50       30 );
    50          
64             }
65              
66             sub _accept {
67 0     0     my($heap, $socket, $remote_addr, $remote_port) = @_[HEAP, ARG0, ARG1, ARG2];
68              
69             # XXX: check fastcgi is allowed to connect.
70              
71 0           my $wheel = POE::Wheel::ReadWrite->new(
72             Handle => $socket,
73             Driver => POE::Driver::SysRW->new(),
74             Filter => POE::Filter::FastCGI->new(),
75             InputEvent => 'input',
76             ErrorEvent => 'error'
77             );
78 0           $heap->{wheels}->{$wheel->ID} = $wheel;
79             }
80              
81             sub _input {
82 0     0     my($heap, $session, $kernel, $fcgi, $wheel_id) = @_[HEAP, SESSION, KERNEL, ARG0, ARG1];
83              
84 0           my $client = $heap->{wheels}->{$wheel_id};
85              
86             my $request = POE::Component::FastCGI::Request->new(
87             $client, $session->ID,
88             $fcgi->[0], # request id
89             $fcgi->[2], # cgi parameters
90             $fcgi->[1]->{postdata}
91 0           );
92              
93 0 0         if($fcgi->[1]->{role} eq 'AUTHORIZER') {
94 0 0         if(defined $heap->{Auth}) {
95 0           $heap->{Auth}->($request);
96             }else{
97 0           $request->error(500, "FastCGI authorizer role requested but not configured");
98             }
99 0           return;
100             }
101              
102 0           my $path = $request->uri->path;
103              
104 0           my $run;
105              
106 0           for my $handler(@{$heap->{Handlers}}) {
  0            
107 0 0         if(ref $handler->[0] eq 'Regexp') {
108 0 0         $run = $handler, last if $path =~ /$handler->[0]/;
109             }else{
110 0 0 0       $run = $handler, last if
      0        
111             (($handler->[0] !~ m!/! and $path =~ m!^/$handler->[0]($|/)!) or
112             ($handler->[0] eq $path));
113             }
114             }
115              
116 0 0         if(not defined $run) {
117 0           $request->error(404, "No handler found for $path");
118             }else{
119              
120 0 0 0       if(ref($run->[1]) eq 'CODE' or $run->[1]->isa('POE::Session::AnonEvent') ) {
121 0           $run->[1]->($request, $run->[0]);
122             } else {
123 0           $kernel->post($heap->{Session}, $run->[1],$request, $run->[0]);
124             }
125              
126 0 0         if($request->{_res}) {
127             # Streaming support
128 0 0         if($request->{_res}->streaming) {
129 0           push @{$heap->{toclose}->{$wheel_id}}, $request->{_res};
  0            
130             } else {
131             # Send and break circular ref
132 0 0         $request->{_res}->send if exists $request->{_res}->{client};
133 0           $request->{_res} = 0;
134             }
135             }
136             }
137             }
138              
139             sub _error {
140 0     0     my($heap, $wheel_id) = @_[HEAP, ARG3];
141 0 0         if(exists $heap->{toclose}->{$wheel_id}) {
142 0           for(@{$heap->{toclose}->{$wheel_id}}) {
  0            
143 0           $_->closed;
144             }
145 0           delete $heap->{toclose}->{$wheel_id};
146             }
147 0           delete $heap->{wheels}->{$wheel_id};
148              
149 0           undef;
150             }
151              
152             sub _client_shutdown {
153 0     0     my($heap, $wheel_id) = @_[HEAP, ARG0];
154              
155 0           delete $heap->{wheels}->{$wheel_id};
156              
157 0           undef;
158             }
159              
160             sub _shutdown {
161 0     0     my($heap, $kernel) = @_[HEAP, KERNEL];
162              
163 0 0         return unless defined $heap->{server};
164              
165             # Tell TCP server to shutdown
166 0           $kernel->post($heap->{server}, 'shutdown');
167 0           delete $heap->{server};
168             }
169              
170             # these are here to help PoCo::FastCGI::Response
171             # to deal with it's wheel from the right session
172             sub _w_send {
173 0     0     my($resp) = $_[ARG0];
174 0           $resp->_send();
175             }
176              
177             sub _w_write {
178 0     0     my($resp, $out) = @_[ARG0, ARG1];
179 0           $resp->_write($out);
180             }
181              
182             sub _w_close {
183 0     0     my($resp, $out) = @_[ARG0, ARG1];
184 0           $resp->_close($out);
185             }
186              
187             1;
188              
189             =head1 NAME
190              
191             POE::Component::FastCGI - POE FastCGI server
192              
193             =head1 SYNOPSIS
194              
195             You can use this module with a direct subroutine callback:
196              
197             use POE;
198             use POE::Component::FastCGI;
199              
200             POE::Component::FastCGI->new(
201             Port => 1026,
202             Handlers => [
203             [ '/' => \&default ],
204             ]
205             );
206              
207             sub default {
208             my($request) = @_;
209              
210             my $response = $request->make_response;
211             $response->header("Content-type" => "text/html");
212             $response->content("A page");
213             $response->send;
214             }
215              
216             POE::Kernel->run;
217              
218             and a POE event callback:
219              
220             use POE;
221             use POE::Component::FastCGI;
222              
223             POE::Component::FastCGI->new(
224             Port => 1026,
225             Handlers => [
226             [ '/' => 'poe_event_name' ],
227             ]
228             Session => 'MAIN',
229             );
230              
231             sub default {
232             my($request) = @_;
233              
234             my $response = $request->make_response;
235             $response->header("Content-type" => "text/html");
236             $response->content("A page");
237             $response->send;
238             }
239              
240             =head1 DESCRIPTION
241              
242             Provides a FastCGI (L) server for L.
243              
244             =over 4
245              
246             =item POE::Component::FastCGI->new([name => value], ...)
247              
248             Creates a new POE session for the FastCGI server and listens on the specified
249             port.
250              
251             Parameters
252             Auth (optional)
253             A code reference to run when called as a FastCGI authorizer.
254             Handlers (required)
255             A array reference with a mapping of paths to code references or POE event names.
256             Port (required unless Unix is set)
257             Port number to listen on.
258             Address (requied if Unix is set)
259             Address to listen on.
260             Unix (optional)
261             Listen on UNIX socket given in Address.
262             Session (required if you want to get POE callbacks)
263             Into which session we should post the POE event back.
264              
265             The call returns a POE session ID. This should be stored, and when application is to be terminated, a 'shutdown' event can be posted to this session. This will terminate the server socket and free resources.
266              
267             The handlers parameter should be a list of lists defining either regexps of
268             paths to match or absolute paths to code references.
269              
270             The code references will be passed one parameter, a
271             L object. To send a response
272             the C method should be called which returns a
273             L object. These objects
274             are subclasses of L and L
275             respectively.
276              
277             Example:
278             Handlers => [
279             [ '/page' => \&page ],
280             [ qr!^/(\w+)\.html$! => sub {
281             my $request = shift;
282             my $response = $request->make_response;
283             output_template($request, $response, $1);
284             }
285             ],
286             ]
287              
288             =back
289              
290             =head1 USING FASTCGI
291              
292             Many webservers have support for FastCGI. PoCo::FastCGI has been
293             tested on Mac OSX and Linux using lighttpd.
294              
295             Currently you must run the PoCo::FastCGI script separately to the
296             webserver and then instruct the webserver to connect to it.
297              
298             Lighttpd configuration example (assuming listening on port 1026):
299              
300             $HTTP["host"] == "some.host" {
301             fastcgi.server = ( "/" =>
302             ( "localhost" => (
303             "host" => "127.0.0.1",
304             "port" => 1026,
305             "check-local" => "disable",
306             "docroot" => "/"
307             )
308             )
309             )
310             }
311              
312             With mod_fastcgi on Apache the equivalent directive is
313             C.
314              
315             =head1 MAINTAINER
316              
317             Chris 'BinGOs' Williams on behalf of the POE community
318              
319             =head1 AUTHOR
320              
321             Copyright 2005, David Leadbeater L. All rights reserved.
322              
323             This library is free software; you can redistribute it and/or modify
324             it under the same terms as Perl itself.
325              
326             =head1 BUGS
327              
328             Please let me know.
329              
330             =head1 SEE ALSO
331              
332             L, L,
333             L, L.
334              
335             =cut