File Coverage

blib/lib/POE/Component/FastCGI.pm
Criterion Covered Total %
statement 27 79 34.1
branch 4 32 12.5
condition 3 18 16.6
subroutine 10 20 50.0
pod 1 1 100.0
total 45 150 30.0


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