File Coverage

blib/lib/FCGI/EV.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package FCGI::EV;
2              
3 2     2   457567 use warnings;
  2         6  
  2         78  
4 2     2   11 use strict;
  2         3  
  2         69  
5 2     2   10 use Carp;
  2         9  
  2         219  
6              
7 2     2   2228 use version; our $VERSION = qv('1.0.9'); # update POD & Changes & README
  2         5408  
  2         13  
8              
9             # update DEPENDENCIES in POD & Makefile.PL & README
10 2     2   200 use Scalar::Util qw( weaken );
  2         4  
  2         223  
11 2     2   289887 use IO::Stream;
  0            
  0            
12              
13              
14             use constant FCGI_HEADER_LEN => 8;
15             use constant FCGI_VERSION_1 => 1;
16             use constant FCGI_BEGIN_REQUEST => 1;
17             use constant FCGI_END_REQUEST => 3;
18             use constant FCGI_PARAMS => 4;
19             use constant FCGI_STDIN => 5;
20             use constant FCGI_STDOUT => 6;
21             use constant FCGI_RESPONDER => 1;
22             use constant FCGI_REQUEST_COMPLETE => 0;
23             use constant END_REQUEST_COMPLETE =>
24             pack 'N C CCC', 0, FCGI_REQUEST_COMPLETE, 0, 0, 0;
25             use constant MAX_CONTENT_LEN => 0xFFFF;
26              
27              
28             sub new {
29             my ($class, $sock, $handler_class) = @_;
30             my $self = bless {
31             io => undef,
32             req_id => undef,
33             params => q{},
34             stdin_eof => undef,
35             handler => undef,
36             handler_class=>$handler_class,
37             }, $class;
38             $self->{io} = IO::Stream->new({
39             fh => $sock,
40             wait_for => IN|EOF,
41             cb => $self,
42             Wait_header => 1,
43             Need_in => FCGI_HEADER_LEN,
44             });
45             weaken($self->{io});
46             # It MAY have sense to add timeout between read() calls and timeout for
47             # overall time until EOF on STDIN will be received. First timeout
48             # can be about 3 minutes for slow clients, second can be about 4 hours
49             # for uploading huge files.
50             return;
51             }
52              
53             sub DESTROY {
54             my ($self) = @_;
55             $self->{handler} = undef; # call handler's DESTROY while $self is alive
56             return;
57             }
58              
59             sub stdout {
60             my ($self, $stdout, $is_eof) = @_;
61             my $io = $self->{io};
62             if (length $stdout) {
63             $io->{out_buf} .= _pack_pkt(FCGI_STDOUT, $self->{req_id}, $stdout);
64             }
65             if ($is_eof) {
66             $io->{out_buf} .= _pack_pkt(FCGI_STDOUT, $self->{req_id}, q{});
67             $io->{out_buf} .= _pack_pkt(FCGI_END_REQUEST, $self->{req_id}, END_REQUEST_COMPLETE);
68             $io->{wait_for} |= SENT;
69             }
70             $io->write();
71             return;
72             }
73              
74             sub IO {
75             my ($self, $io, $e, $err) = @_;
76             if ($err) {
77             warn "FCGI::EV: IO: $err\n";
78             return $io->close();
79             }
80             if ($e & EOF) {
81             return $io->close();
82             }
83             if ($e & SENT) {
84             return $io->close();
85             }
86             while (length $io->{in_buf} >= $io->{Need_in}) {
87             if ($io->{Wait_header}) {
88             $io->{Wait_header} = 0;
89             my ($content_len, $padding_len) = unpack 'x4 n C', $io->{in_buf};
90             $io->{Need_in} += $content_len + $padding_len;
91             }
92             else {
93             my $pkt = substr $io->{in_buf}, 0, $io->{Need_in}, q{};
94             $io->{Wait_header} = 1;
95             $io->{Need_in} = FCGI_HEADER_LEN;
96             my $error = $self->_process($pkt);
97             if ($error) {
98             warn "FCGI::EV: $error\n";
99             return $io->close();
100             }
101             }
102             }
103             return;
104             }
105              
106             sub _process {
107             my ($self, $pkt) = @_;
108             my ($ver, $type, $req_id, $content_len) = unpack 'C C n n', $pkt;
109             my $content = substr $pkt, FCGI_HEADER_LEN, $content_len;
110             if ($ver != FCGI_VERSION_1) {
111             return "unsupported version: $ver";
112             }
113             if (defined $self->{req_id} && $self->{req_id} != $req_id) {
114             return "unknown request id: $req_id";
115             }
116             if ($type == FCGI_BEGIN_REQUEST) {
117             my ($role) = unpack 'n', $content;
118             if ($role != FCGI_RESPONDER) {
119             return "role not supported: $role";
120             }
121             if (defined $self->{req_id}) {
122             return 'duplicated BEGIN_REQUEST';
123             }
124             $self->{req_id} = $req_id;
125             }
126             elsif ($type == FCGI_PARAMS) {
127             if ($self->{handler}) {
128             return 'got PARAMS for existing handler';
129             }
130             if (length $content) {
131             $self->{params} .= $content;
132             }
133             else {
134             my ($env, $err) = _unpack_nv($self->{params});
135             return $err if $err;
136             $self->{handler} = $self->{handler_class}->new($self, $env);
137             }
138             }
139             elsif ($type == FCGI_STDIN) {
140             if (!$self->{handler}) {
141             return 'got STDIN for non-existing handler';
142             }
143             if ($self->{stdin_eof}) {
144             return 'got STDIN after STDIN EOF';
145             }
146             if (length $content) {
147             $self->{handler}->stdin($content, 0);
148             }
149             else {
150             $self->{handler}->stdin(q{}, 1);
151             $self->{stdin_eof} = 1;
152             }
153             }
154             else {
155             return 'unknown type';
156             }
157             return;
158             }
159              
160             sub _unpack_nv {
161             my ($s) = @_;
162             my %nv;
163             while (length $s) {
164             my ($nlen, $vlen);
165             for my $len ($nlen, $vlen) {
166             ## no critic (ProhibitMagicNumbers)
167             return (undef, 'unpack_nv: not enough data') if length $s < 1;
168             ($len) = unpack 'C', $s;
169             if ($len & 0x80) {
170             return (undef, 'unpack_nv: not enough data') if length $s < 4;
171             ($len) = unpack 'N', $s;
172             $len &= 0x7FFFFFFF;
173             substr $s, 0, 4, q{};
174             }
175             else {
176             substr $s, 0, 1, q{};
177             }
178             ## use critic
179             }
180             return (undef, 'unpack_nv: not enough data') if length $s < $nlen + $vlen;
181             my $n = substr $s, 0, $nlen, q{};
182             my $v = substr $s, 0, $vlen, q{};
183             $nv{$n} = $v;
184             }
185             return (\%nv, undef);
186             }
187              
188             sub _pack_pkt {
189             my ($type, $req_id, $content) = @_;
190             $content = pack 'a*', $content; # convert from Unicode to UTF-8, if any
191             my $pkt = q{};
192             while (1) {
193             my $c = substr $content, 0, MAX_CONTENT_LEN, q{};
194             my $padding = q{};
195             $pkt .= pack 'CCnnCCa*a*',
196             FCGI_VERSION_1,
197             $type,
198             $req_id,
199             length $c,
200             length $padding,
201             0, # reserved
202             $c,
203             $padding,
204             ;
205             last if !length $content;
206             }
207             return $pkt;
208             }
209              
210              
211             1; # Magic true value required at end of module
212             __END__
213              
214             =head1 NAME
215              
216             FCGI::EV - Implement FastCGI protocol for use in EV-based applications
217              
218              
219             =head1 VERSION
220              
221             This document describes FCGI::EV version 1.0.9
222              
223              
224             =head1 SYNOPSIS
225              
226             use FCGI::EV;
227             use Some::FCGI::EV::Handler;
228              
229             # while in EV::loop, accept incoming connection from web server into
230             # $sock, then start handling FastCGI protocol on that connection,
231             # using Some::FCGI::EV::Handler for processing CGI requests:
232             FCGI::EV->new($sock, 'Some::FCGI::EV::Handler');
233              
234              
235             #
236             # EXAMPLE: complete FastCGI server (without error handling code)
237             # use FCGI::EV::Std handler (download separately from CPAN)
238             #
239              
240             use Socket;
241             use Fcntl;
242             use EV;
243             use FCGI::EV;
244             use FCGI::EV::Std;
245              
246             my $path = '/tmp/fastcgi.sock';
247              
248             socket my $srvsock, AF_UNIX, SOCK_STREAM, 0;
249             unlink $path;
250             my $umask = umask 0; # ensure 0777 perms for unix socket
251             bind $srvsock, sockaddr_un($path);
252             umask $umask;
253             listen $srvsock, SOMAXCONN;
254             fcntl $srvsock, F_SETFL, O_NONBLOCK;
255              
256             my $w = EV::io $srvsock, EV::READ, sub {
257             accept my($sock), $srvsock;
258             fcntl $sock, F_SETFL, O_NONBLOCK;
259             FCGI::EV->new($sock, 'FCGI::EV::Std');
260             };
261              
262             EV::loop;
263              
264              
265             =head1 DESCRIPTION
266              
267             This module implement FastCGI protocol for use in EV-based applications.
268             (That mean you have to run EV::loop in your application or this module
269             will not work.)
270              
271             It receive and parse data from web server, pack and send data to web
272             server, but it doesn't process CGI requests received from web server -
273             instead it delegate this work to another module called 'handler'. For
274             one example of such handler, see L<FCGI::EV::Std>.
275              
276             FCGI::EV work using non-blocking sockets and initially was designed to use
277             in event-based CGI applications (which able to handle multiple parallel
278             CGI requests in single process without threads/fork). This require from
279             CGI to avoid any operations which may block, like using SQL database -
280             instead CGI should delegate all such tasks to remote services and talk to
281             these services in non-blocking mode.
282              
283             It also possible to use it to run usual CGI.pm-based applications. If you
284             will do this using FCGI::EV::Std handler, then only one CGI request will
285             be executed at a time (which is probably not what you expect from
286             FastCGI!), because FCGI::EV::Std doesn't implement any process-manager.
287             But it's possible to develop another handlers for FCGI::EV, which will
288             support process-management and so will handle multiple CGI request in
289             parallel.
290              
291             This module doesn't require from user to use CGI.pm - any module for
292             parsing CGI params can be used in general (details depends on used
293             FCGI::EV handler module).
294              
295              
296             =head1 INTERFACE
297              
298             =over
299              
300             =item new( $sock, $class )
301              
302             Start talking FastCGI protocol on $sock (which should be socket open to
303             just-connected web server), and use $class to handle received CGI requests.
304              
305             Module $class should implement "FCGI::EV handler" interface. You can use
306             either L<FCGI::EV::Std> from CPAN or develop your own.
307              
308             Return nothing. (Created FCGI::EV object will work in background and will
309             be automatically destroyed after finishing I/O with web server.)
310              
311             =back
312              
313              
314             =head1 HANDLER CLASS INTERFACE
315              
316             Handler class (which name provided in $class parameter to FCGI::EV->new())
317             must implement this interface:
318              
319             =over
320              
321             =item new( $server, \%env )
322              
323             When FCGI::EV object receive initial part of CGI request (environment
324             variables) it will call $handler_class->new() to create handler object
325             which should process that CGI request.
326              
327             Parameter $server is FCGI::EV object itself. It's required to send CGI
328             reply. WARNING! Handler may keep only weaken() reference to $server!
329              
330             After calling new() FCGI::EV object ($server) will continue receiving
331             STDIN content from web server and will call $handler->stdin() each time it
332             get next part of STDIN.
333              
334             =item stdin( $data, $is_eof )
335              
336             The $data is next chunk of STDIN received from web server. Flag $is_eof will
337             be true if $data was last part of STDIN.
338              
339             Usually handler shouldn't begin processing CGI request until all content
340             of STDIN will be received.
341              
342             =item DESTROY
343              
344             This method is optional. It will be called when connection to web server is
345             closed and FCGI::EV object going to die (but it's still exists when DESTROY
346             is called - except if DESTROY was called while global destruction stage).
347              
348             Handler object may use DESTROY to interrupt current CGI request if web server
349             close connection before CGI send it reply.
350              
351             =back
352              
353             =head2 SENDING CGI REPLY
354              
355             After handler got %env (in new()) and complete STDIN (in one or more calls
356             of stdin()) it may start handling this CGI request and prepare reply to send
357             to web server. To send this data it should use method $server->stdout(),
358             where $server is object given to new() while creating handler object
359             (it should keep weak reference to $server inside to be able to reply).
360              
361             =over
362              
363             =item stdout( $data, $is_eof )
364              
365             CGI may send reply in one or more parts. Last part should have $is_eof set
366             to true. DESTROY method of handler object will be called shortly after
367             handler object will do $server->stdout( $data, 1 ).
368              
369             =back
370              
371             =head2 HANDLER EXAMPLE
372              
373             This handler will process CGI requests one-by-one (i.e. in blocking mode).
374             On request function main::main() will be executed. That function may use
375             standard CGI.pm module to get request parameters and send it reply using
376             usual print to STDOUT.
377              
378             There no error-handling code in this example, see L<FCGI::EV::Std> for
379             more details.
380              
381             package FCGI::EV::ExampleHandler;
382              
383             use Scalar::Util qw( weaken );
384             use CGI::Stateless; # needed to re-init CGI.pm state between requests
385              
386             sub new {
387             my ($class, $server, $env) = @_;
388             my $self = bless {
389             server => $server,
390             env => $env,
391             stdin => q{},
392             }, $class;
393             weaken($self->{server});
394             return $self;
395             }
396              
397             sub stdin {
398             my ($self, $stdin, $is_eof) = @_;
399             $self->{stdin} .= $stdin;
400             if ($is_eof) {
401             local *STDIN;
402             open STDIN, '<', \$self->{stdin};
403             local %ENV = %{ $self->{env} };
404             local $CGI::Q = CGI::Stateless->new();
405             local *STDOUT;
406             my $reply = q{};
407             open STDOUT, '>', \$reply;
408             main::main();
409             $self->{server}->stdout($reply, 1);
410             }
411             return;
412             }
413              
414              
415             =head1 DIAGNOSTICS
416              
417             There no errors returned in any way by this module, but there few warning
418             messages may be printed:
419              
420             =over
421              
422             =item C<< FCGI::EV: IO: %s >>
423              
424             While doing I/O with web server error %s happened and connection was closed.
425              
426             =item C<< FCGI::EV: %s >>
427              
428             While parsing data from web server error %s happened and connection was closed.
429             (That error probably mean bug either in web server or this module.)
430              
431             =back
432              
433              
434             =head1 CONFIGURATION AND ENVIRONMENT
435              
436             FCGI::EV requires no configuration files or environment variables.
437              
438              
439             =head1 DEPENDENCIES
440              
441             IO::Stream
442             EV
443              
444              
445             =head1 INCOMPATIBILITIES
446              
447             None reported.
448              
449              
450             =head1 BUGS AND LIMITATIONS
451              
452             No bugs have been reported.
453              
454             Please report any bugs or feature requests to
455             C<bug-fcgi-ev@rt.cpan.org>, or through the web interface at
456             L<http://rt.cpan.org>.
457              
458              
459             =head1 AUTHOR
460              
461             Alex Efros C<< <powerman-asdf@ya.ru> >>
462              
463              
464             =head1 LICENSE AND COPYRIGHT
465              
466             Copyright (c) 2009, Alex Efros C<< <powerman-asdf@ya.ru> >>. All rights reserved.
467              
468             This module is free software; you can redistribute it and/or
469             modify it under the same terms as Perl itself. See L<perlartistic>.
470              
471              
472             =head1 DISCLAIMER OF WARRANTY
473              
474             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
475             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
476             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
477             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
478             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
479             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
480             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
481             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
482             NECESSARY SERVICING, REPAIR, OR CORRECTION.
483              
484             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
485             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
486             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
487             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
488             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
489             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
490             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
491             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
492             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
493             SUCH DAMAGES.