File Coverage

blib/lib/FCGI/Async/PSGI.pm
Criterion Covered Total %
statement 53 53 100.0
branch 14 18 77.7
condition 1 2 50.0
subroutine 9 9 100.0
pod 2 3 66.6
total 79 85 92.9


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2010 -- leonerd@leonerd.org.uk
5              
6             package FCGI::Async::PSGI;
7              
8 2     2   212101 use strict;
  2         5  
  2         61  
9 2     2   10 use warnings;
  2         4  
  2         45  
10              
11 2     2   11 use Carp;
  2         2  
  2         114  
12              
13 2     2   9 use base qw( FCGI::Async );
  2         4  
  2         1027  
14              
15             our $VERSION = '0.22';
16              
17             my $CRLF = "\x0d\x0a";
18              
19             =head1 NAME
20              
21             C - use C applications with C
22              
23             =head1 SYNOPSIS
24              
25             use FCGI::Async::PSGI;
26             use IO::Async::Loop;
27              
28             my $loop = IO::Async::Loop->new;
29              
30             my $fcgi = FCGI::Async::PSGI->new(
31             port => 12345,
32             app => sub {
33             my $env = shift;
34              
35             return [
36             200,
37             [ "Content-Type" => "text/plain" ],
38             [ "Hello, world!" ],
39             ];
40             },
41             );
42              
43             $loop->add( $fcgi );
44              
45             $loop->loop_forever;
46              
47             =head1 DESCRIPTION
48              
49             This subclass of L allows a FastCGI responder to use a L
50             application to respond to requests. It acts as a gateway between the FastCGI
51             connection from the webserver, and the C application. Aside from the use
52             of C instead of the C callback, this class behaves similarly
53             to C.
54              
55             =cut
56              
57             sub new
58             {
59             # FCGI::Async's constructor tries to pass on all the args to $loop->listen
60             # so we need to pull out app, if present
61 2     2 1 10729 my $class = shift;
62 2         12 my %args = @_;
63              
64 2         6 my $app = delete $args{app};
65              
66             my $self = $class->SUPER::new(
67             %args,
68             on_request => sub {
69 6     6   23386 my $self = shift;
70 6         10 my ( $req ) = @_;
71              
72 6         18 $self->process_request( $req );
73             },
74 2         29 );
75              
76 2 50       11 $self->configure( app => $app ) if defined $app;
77              
78 2         67 return $self;
79             }
80              
81             =head1 PARAMETERS
82              
83             The following named parameters may be passed to C or C:
84              
85             =over 8
86              
87             =item app => CODE
88              
89             Reference to the actual C application to use for responding to requests
90              
91             =back
92              
93             =cut
94              
95             sub configure
96             {
97 9     9 1 7098 my $self = shift;
98 9         24 my %args = @_;
99              
100 9 100       26 if( exists $args{app} ) {
101 5         14 $self->{app} = delete $args{app};
102             }
103              
104 9         49 $self->SUPER::configure( %args );
105             }
106              
107             =head1 PSGI ENVIRONMENT
108              
109             The following extra keys are supplied to the environment of the C app:
110              
111             =over 8
112              
113             =item C
114              
115             The C object serving the request
116              
117             =item C
118              
119             The L object representing this particular request
120              
121             =item C
122              
123             The L object that the C object is a member
124             of. This is also provided as C for backward-compatibility
125             with version 0.21, but at some point will be removed.
126              
127             =back
128              
129             =cut
130              
131             sub process_request
132             {
133 6     6 0 10 my $self = shift;
134 6         10 my ( $req ) = @_;
135              
136             # Much of this code stolen fro^W^Winspired by Plack::Handler::Net::FastCGI
137              
138 6         20 my %env = (
139 6 50 50     49 %{ $req->params },
140             'psgi.version' => [1,0],
141             'psgi.url_scheme' => ($req->param("HTTPS")||"off") =~ m/^(?:on|1)/i ? "https" : "http",
142             'psgi.input' => $req->stdin,
143             'psgi.errors' => $req->stderr,
144             'psgi.multithread' => 0,
145             'psgi.multiprocess' => 0,
146             'psgi.run_once' => 0,
147             'psgi.nonblocking' => 1,
148             'psgi.streaming' => 1,
149              
150             # Extensions
151             'fcgi.async' => $self,
152             'fcgi.async.req' => $req,
153             'fcgi.async.loop' => $self->get_loop,
154             'io.async.loop' => $self->get_loop,
155             );
156              
157 6         721 my $resp = $self->{app}->( \%env );
158              
159             my $responder = sub {
160 6     6   1084 my ( $status, $headers, $body ) = @{ +shift };
  6         15  
161              
162 6         30 $req->print_stdout( "Status: $status$CRLF" );
163 6         836 while( my ( $header, $value ) = splice @$headers, 0, 2 ) {
164 6         22 $req->print_stdout( "$header: $value$CRLF" );
165             }
166 6         85 $req->print_stdout( $CRLF );
167              
168 6 100       84 if( !defined $body ) {
169 1 50       4 croak "Responder given no body in void context" unless defined wantarray;
170              
171 1         4 return $req->stdout_with_close;
172             }
173              
174 5 100       16 if( ref $body eq "ARRAY" ) {
175 4         16 $req->print_stdout( $_ ) for @$body;
176 4         61 $req->finish( 0 );
177             }
178             else {
179             $req->stream_stdout_then_finish(
180             sub {
181 2         1452 local $/ = \8192;
182 2         58 my $buffer = $body->getline;
183 2 100       64 defined $buffer and return $buffer;
184              
185 1         9 $body->close;
186 1         7 return undef;
187             },
188 1         6 0
189             );
190             }
191 6         3282 };
192              
193 6 100       24 if( ref $resp eq "ARRAY" ) {
    50          
194 4         12 $responder->( $resp );
195             }
196             elsif( ref $resp eq "CODE" ) {
197 2         13 $resp->( $responder );
198             }
199             }
200              
201             # Keep perl happy; keep Britain tidy
202             1;
203              
204             __END__