File Coverage

blib/lib/Net/Async/FastCGI/Request.pm
Criterion Covered Total %
statement 219 223 98.2
branch 49 64 76.5
condition 10 16 62.5
subroutine 56 56 100.0
pod 22 27 81.4
total 356 386 92.2


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, 2005-2013 -- leonerd@leonerd.org.uk
5              
6             package Net::Async::FastCGI::Request;
7              
8 17     17   96 use strict;
  17         28  
  17         791  
9 17     17   97 use warnings;
  17         33  
  17         558  
10              
11 17     17   92 use Carp;
  17         31  
  17         1119  
12              
13 17     17   114 use Net::FastCGI::Constant qw( :type :flag :protocol_status );
  17         45  
  17         3849  
14 17         946 use Net::FastCGI::Protocol qw(
15             parse_params
16             build_end_request_body
17 17     17   105 );
  17         30  
18              
19             # The largest amount of data we can fit in a FastCGI record - MUST NOT
20             # be greater than 2^16-1
21 17     17   90 use constant MAXRECORDDATA => 65535;
  17         70  
  17         1065  
22              
23 17     17   92 use Encode qw( find_encoding );
  17         31  
  17         800  
24 17     17   141 use POSIX qw( EAGAIN );
  17         49  
  17         252  
25              
26             our $VERSION = '0.25';
27              
28             my $CRLF = "\x0d\x0a";
29              
30             =head1 NAME
31              
32             C - a single active FastCGI request
33              
34             =head1 SYNOPSIS
35              
36             use Net::Async::FastCGI;
37             use IO::Async::Loop;
38              
39             my $fcgi = Net::Async::FastCGI->new(
40             on_request => sub {
41             my ( $fcgi, $req ) = @_;
42              
43             my $path = $req->param( "PATH_INFO" );
44             $req->print_stdout( "Status: 200 OK\r\n" .
45             "Content-type: text/plain\r\n" .
46             "\r\n" .
47             "You requested $path" );
48             $req->finish();
49             }
50             );
51              
52             my $loop = IO::Async::Loop->new();
53              
54             $loop->add( $fcgi );
55              
56             $loop->run;
57              
58             =head1 DESCRIPTION
59              
60             Instances of this object class represent individual requests received from the
61             webserver that are currently in-progress, and have not yet been completed.
62             When given to the controlling program, each request will already have its
63             parameters and STDIN data. The program can then write response data to the
64             STDOUT stream, messages to the STDERR stream, and eventually finish it.
65              
66             This module would not be used directly by a program using
67             C, but rather, objects in this class are passed into the
68             C event of the containing C object.
69              
70             =cut
71              
72             sub new
73             {
74 21     21 0 46 my $class = shift;
75 21         94 my %args = @_;
76              
77 21         46 my $rec = $args{rec};
78              
79 21         297 my $self = bless {
80             conn => $args{conn},
81             fcgi => $args{fcgi},
82              
83             reqid => $rec->{reqid},
84             keepconn => $rec->{flags} & FCGI_KEEP_CONN,
85              
86             stdin => "",
87             stdindone => 0,
88             params => {},
89             paramsdone => 0,
90              
91             stdout => "",
92             stderr => "",
93              
94             used_stderr => 0,
95             }, $class;
96              
97 21         127 $self->set_encoding( $args{fcgi}->_default_encoding );
98              
99 21         79 return $self;
100             }
101              
102             sub write_record
103             {
104 66     66 0 248 my $self = shift;
105 66         86 my ( $rec ) = @_;
106              
107 66 50       133 return if $self->is_aborted;
108              
109 66         118 my $content = $rec->{content};
110 66         99 my $contentlen = length( $content );
111 66 50       157 if( $contentlen > MAXRECORDDATA ) {
112 0         0 warn __PACKAGE__."->write_record() called with content longer than ".MAXRECORDDATA." bytes - truncating";
113 0         0 $content = substr( $content, 0, MAXRECORDDATA );
114             }
115              
116 66 50       239 $rec->{reqid} = $self->{reqid} unless defined $rec->{reqid};
117              
118 66         100 my $conn = $self->{conn};
119              
120 66         340 $conn->write_record( $rec, $content );
121              
122             }
123              
124             sub incomingrecord
125             {
126 63     63 0 106 my $self = shift;
127 63         81 my ( $rec ) = @_;
128              
129 63         97 my $type = $rec->{type};
130              
131 63 100       187 if( $type == FCGI_PARAMS ) {
    50          
132 35         115 $self->incomingrecord_params( $rec );
133             }
134             elsif( $type == FCGI_STDIN ) {
135 28         90 $self->incomingrecord_stdin( $rec );
136             }
137             else {
138 0         0 warn "$self just received unknown record type";
139             }
140             }
141              
142             sub _ready_check
143             {
144 49     49   169 my $self = shift;
145              
146 49 100 66     349 if( $self->{stdindone} and $self->{paramsdone} ) {
147 21         109 $self->{fcgi}->_request_ready( $self );
148             }
149             }
150              
151             sub incomingrecord_params
152             {
153 35     35 0 67 my $self = shift;
154 35         48 my ( $rec ) = @_;
155              
156 35         68 my $content = $rec->{content};
157 35         61 my $len = $rec->{len};
158              
159 35 100       99 if( $len ) {
160 17     17   9713 no warnings 'uninitialized';
  17         36  
  17         39523  
161 14         42 $self->{paramscontent} .= $content;
162 14         60 return;
163             }
164             else {
165 21         114 $self->{params} = parse_params( delete $self->{paramscontent} );
166 21         1111 $self->{paramsdone} = 1;
167             }
168              
169 21         73 $self->_ready_check;
170             }
171              
172             sub incomingrecord_stdin
173             {
174 28     28 0 45 my $self = shift;
175 28         55 my ( $rec ) = @_;
176              
177 28         61 my $content = $rec->{content};
178 28         51 my $len = $rec->{len};
179              
180 28 100       85 if( $len ) {
181 7         24 $self->{stdin} .= $content;
182             }
183             else {
184 21         44 $self->{stdindone} = 1;
185             }
186              
187 28         78 $self->_ready_check;
188             }
189              
190             =head1 METHODS
191              
192             =cut
193              
194             =head2 $hashref = $req->params
195              
196             This method returns a reference to a hash containing a copy of the request
197             parameters that had been sent by the webserver as part of the request.
198              
199             =cut
200              
201             sub params
202             {
203 17     17 1 856 my $self = shift;
204              
205 17         89 my %p = %{$self->{params}};
  17         131  
206              
207 17         154 return \%p;
208             }
209              
210             =head2 $p = $req->param( $key )
211              
212             This method returns the value of a single request parameter, or C if no
213             such key exists.
214              
215             =cut
216              
217             sub param
218             {
219 20     20 1 64 my $self = shift;
220 20         36 my ( $key ) = @_;
221              
222 20         190 return $self->{params}{$key};
223             }
224              
225             =head2 $method = $req->method
226              
227             Returns the value of the C parameter, or C if there is no
228             value set for it.
229              
230             =cut
231              
232             sub method
233             {
234 2     2 1 7 my $self = shift;
235 2   50     12 return $self->param( "REQUEST_METHOD" ) || "GET";
236             }
237              
238             =head2 $script_name = $req->script_name
239              
240             Returns the value of the C parameter.
241              
242             =cut
243              
244             sub script_name
245             {
246 3     3 1 7 my $self = shift;
247 3         9 return $self->param( "SCRIPT_NAME" );
248             }
249              
250             =head2 $path_info = $req->path_info
251              
252             Returns the value of the C parameter.
253              
254             =cut
255              
256             sub path_info
257             {
258 3     3 1 5 my $self = shift;
259 3         10 return $self->param( "PATH_INFO" );
260             }
261              
262             =head2 $path = $req->path
263              
264             Returns the full request path by reconstructing it from C and
265             C.
266              
267             =cut
268              
269             sub path
270             {
271 2     2 1 4 my $self = shift;
272              
273 2   33     13 my $path = join "", grep defined && length,
274             $self->script_name,
275             $self->path_info;
276 2 50       10 $path = "/" if !length $path;
277              
278 2         6 return $path;
279             }
280              
281             =head2 $query_string = $req->query_string
282              
283             Returns the value of the C parameter.
284              
285             =cut
286              
287             sub query_string
288             {
289 2     2 1 7 my $self = shift;
290 2   100     9 return $self->param( "QUERY_STRING" ) || "";
291             }
292              
293             =head2 $protocol = $req->protocol
294              
295             Returns the value of the C parameter.
296              
297             =cut
298              
299             sub protocol
300             {
301 2     2 1 6 my $self = shift;
302 2         9 return $self->param( "SERVER_PROTOCOL" );
303             }
304              
305             =head2 $req->set_encoding( $encoding )
306              
307             Sets the character encoding used by the request's STDIN, STDOUT and STDERR
308             streams. This method may be called at any time to change the encoding in
309             effect, which will be used the next time C, C,
310             C or C are called. This encoding will remain in
311             effect until changed again. The encoding of a new request is determined by the
312             C parameter of the containing C object.
313             If the value C is passed, the encoding will be removed, and the above
314             methods will work directly on bytes instead of encoded strings.
315              
316             =cut
317              
318             sub set_encoding
319             {
320 22     22 1 1245 my $self = shift;
321 22         42 my ( $encoding ) = @_;
322              
323 22 50       75 if( defined $encoding ) {
324 22         129 my $codec = find_encoding( $encoding );
325 22 50       2858 defined $codec or croak "Unrecognised encoding '$encoding'";
326 22         110 $self->{codec} = $codec;
327             }
328             else {
329 0         0 undef $self->{codec};
330             }
331             }
332              
333             =head2 $line = $req->read_stdin_line
334              
335             This method works similarly to the C<< >> operator. If at least one
336             line of data is available then it is returned, including the linefeed, and
337             removed from the buffer. If not, then any remaining partial line is returned
338             and removed from the buffer. If no data is available any more, then C
339             is returned instead.
340              
341             =cut
342              
343             sub read_stdin_line
344             {
345 13     13 1 3137 my $self = shift;
346              
347 13         299 my $codec = $self->{codec};
348              
349 13 100       278 if( $self->{stdin} =~ s/^(.*[\r\n])// ) {
    100          
350 4 50       77 return $codec ? $codec->decode( $1 ) : $1;
351             }
352             elsif( $self->{stdin} =~ s/^(.+)// ) {
353 1 50       41 return $codec ? $codec->decode( $1 ) : $1;
354             }
355             else {
356 8         70 return undef;
357             }
358             }
359              
360             =head2 $data = $req->read_stdin( $size )
361              
362             This method works similarly to the C function. It returns the
363             next block of up to $size bytes from the STDIN buffer. If no data is available
364             any more, then C is returned instead. If $size is not defined, then it
365             will return all the available data.
366              
367             =cut
368              
369             sub read_stdin
370             {
371 8     8 1 1814 my $self = shift;
372 8         58 my ( $size ) = @_;
373              
374 8 100       34 return undef unless length $self->{stdin};
375              
376 6 100       22 $size = length $self->{stdin} unless defined $size;
377              
378 6         11 my $codec = $self->{codec};
379              
380             # If $size is too big, substr() will cope
381 6         17 my $bytes = substr( $self->{stdin}, 0, $size, "" );
382 6 50       72 return $codec ? $codec->decode( $bytes ) : $bytes;
383             }
384              
385             sub _print_stream
386             {
387 23     23   45 my $self = shift;
388 23         49 my ( $data, $stream ) = @_;
389              
390 23         81 while( length $data ) {
391             # Send chunks of up to MAXRECORDDATA bytes at once
392 23         82 my $chunk = substr( $data, 0, MAXRECORDDATA, "" );
393 23         113 $self->write_record( { type => $stream, content => $chunk } );
394             }
395             }
396              
397             sub _flush_streams
398             {
399 36     36   63 my $self = shift;
400              
401 36 100       155 if( length $self->{stdout} ) {
    100          
402 20         80 $self->_print_stream( $self->{stdout}, FCGI_STDOUT );
403 20         1596 $self->{stdout} = "";
404             }
405             elsif( my $cb = $self->{stdout_cb} ) {
406 4         9 $cb->();
407             }
408              
409 36 100       269 if( length $self->{stderr} ) {
410 3         15 $self->_print_stream( $self->{stderr}, FCGI_STDERR );
411 3         315 $self->{stderr} = "";
412             }
413             }
414              
415             sub _needs_flush
416             {
417 16     16   23 my $self = shift;
418 16         104 return defined $self->{stdout_cb};
419             }
420              
421             =head2 $req->print_stdout( $data )
422              
423             This method appends the given data to the STDOUT stream of the FastCGI
424             request, sending it to the webserver to be sent to the client.
425              
426             =cut
427              
428             sub print_stdout
429             {
430 41     41 1 492 my $self = shift;
431 41         71 my ( $data ) = @_;
432              
433 41         115 my $codec = $self->{codec};
434              
435 41 50       289 $self->{stdout} .= $codec ? $codec->encode( $data ) : $data;
436              
437 41         211 $self->{conn}->_req_needs_flush( $self );
438             }
439              
440             =head2 $req->print_stderr( $data )
441              
442             This method appends the given data to the STDERR stream of the FastCGI
443             request, sending it to the webserver.
444              
445             =cut
446              
447             sub print_stderr
448             {
449 3     3 1 11 my $self = shift;
450 3         10 my ( $data ) = @_;
451              
452 3         8 my $codec = $self->{codec};
453              
454 3         8 $self->{used_stderr} = 1;
455 3 50       26 $self->{stderr} .= $codec ? $codec->encode( $data ) : $data;
456              
457 3         18 $self->{conn}->_req_needs_flush( $self );
458             }
459              
460             =head2 $req->stream_stdout_then_finish( $readfn, $exitcode )
461              
462             This method installs a callback for streaming data to the STDOUT stream.
463             Whenever the output stream is otherwise-idle, the function will be called to
464             generate some more data to output. When this function returns C it
465             indicates the end of the stream, and the request will be finished with the
466             given exit code.
467              
468             If this method is used, then care should be taken to ensure that the number of
469             bytes written to the server matches the number that was claimed in the
470             C, if such was provided. This logic should be performed by the
471             containing application; C will not track it.
472              
473             =cut
474              
475             sub stream_stdout_then_finish
476             {
477 2     2 1 6 my $self = shift;
478 2         4 my ( $readfn, $exitcode ) = @_;
479              
480             $self->{stdout_cb} = sub {
481 4     4   11 my $data = $readfn->();
482              
483 4 100       18 if( defined $data ) {
484 2         9 $self->print_stdout( $data );
485             }
486             else {
487 2         5 delete $self->{stdout_cb};
488 2         9 $self->finish( $exitcode );
489             }
490 2         11 };
491              
492 2         14 $self->{conn}->_req_needs_flush( $self );
493             }
494              
495             =head2 $stdin = $req->stdin
496              
497             Returns an IO handle representing the request's STDIN buffer. This may be read
498             from using the C or C functions or the C<< <$stdin> >>
499             operator.
500              
501             Note that this will be a tied IO handle, it will not be useable directly as an
502             OS-level filehandle.
503              
504             =cut
505              
506             sub stdin
507             {
508 7     7 1 87 my $self = shift;
509              
510             return Net::Async::FastCGI::Request::TiedHandle->new(
511             READ => sub {
512 3     3   12 $_[1] = $self->read_stdin( $_[2] );
513 3 100       36 return defined $_[1] ? length $_[1] : 0;
514             },
515             READLINE => sub {
516 1     1   5 return $self->read_stdin_line;
517             },
518 7         113 );
519             }
520              
521             =head2 $stdout = $req->stdout
522              
523             =head2 $stderr = $req->stderr
524              
525             Returns an IO handle representing the request's STDOUT or STDERR streams
526             respectively. These may written to using C, C, C, etc..
527              
528             Note that these will be tied IO handles, they will not be useable directly as
529             an OS-level filehandle.
530              
531             =cut
532              
533             sub _stdouterr
534             {
535 8     8   12 my $self = shift;
536 8         14 my ( $method ) = @_;
537              
538             return Net::Async::FastCGI::Request::TiedHandle->new(
539 3     3   15 WRITE => sub { $self->$method( $_[1] ) },
540 8         62 );
541             }
542              
543             sub stdout
544             {
545 1     1 1 4 return shift->_stdouterr( "print_stdout" );
546             }
547              
548             sub stderr
549             {
550 7     7 1 1681 return shift->_stdouterr( "print_stderr" );
551             }
552              
553             =head2 $req->finish( $exitcode )
554              
555             When the request has been dealt with, this method should be called to indicate
556             to the webserver that it is finished. After calling this method, no more data
557             may be appended to the STDOUT stream. At some point after calling this method,
558             the request object will be removed from the containing C
559             object, once all the buffered outbound data has been sent.
560              
561             If present, C<$exitcode> should indicate the numeric status code to send to
562             the webserver. If absent, a value of C<0> is presumed.
563              
564             =cut
565              
566             sub finish
567             {
568 21     21 1 1211 my $self = shift;
569 21         51 my ( $exitcode ) = @_;
570              
571 21 100       75 return if $self->is_aborted;
572              
573 20         92 $self->_flush_streams;
574              
575             # Signal the end of STDOUT
576 20         96 $self->write_record( { type => FCGI_STDOUT, content => "" } );
577              
578             # Signal the end of STDERR if we used it
579 20 100       1907 $self->write_record( { type => FCGI_STDERR, content => "" } ) if $self->{used_stderr};
580              
581 20   100     375 $self->write_record( { type => FCGI_END_REQUEST,
582             content => build_end_request_body( $exitcode || 0, FCGI_REQUEST_COMPLETE )
583             } );
584              
585 20         1275 my $conn = $self->{conn};
586              
587 20 100       84 if( $self->{keepconn} ) {
588 12         53 $conn->_removereq( $self->{reqid} );
589             }
590             else {
591 8         106 $conn->close;
592             }
593             }
594              
595             =head2 $stdout = $req->stdout_with_close
596              
597             Similar to the C method, except that when the C method is
598             called on the returned filehandle, the request will be finished by calling
599             C.
600              
601             =cut
602              
603             sub stdout_with_close
604             {
605 1     1 1 3 my $self = shift;
606              
607             return Net::Async::FastCGI::Request::TiedHandle->new(
608 2     2   11 WRITE => sub { $self->print_stdout( $_[1] ) },
609 1     1   5 CLOSE => sub { $self->finish( 0 ) },
610 1         12 );
611             }
612              
613             sub _abort
614             {
615 9     9   19 my $self = shift;
616 9         40 $self->{aborted} = 1;
617              
618 9         21 my $conn = $self->{conn};
619 9         53 $conn->_removereq( $self->{reqid} );
620              
621 9         50 delete $self->{stdout_cb};
622             }
623              
624             =head2 $req->is_aborted
625              
626             Returns true if the webserver has already closed the control connection. No
627             further work on this request is necessary, as it will be discarded.
628              
629             It is not required to call this method; if the request is aborted then any
630             output will be discarded. It may however be useful to call just before
631             expensive operations, in case effort can be avoided if it would otherwise be
632             wasted.
633              
634             =cut
635              
636             sub is_aborted
637             {
638 90     90 1 543 my $self = shift;
639 90         299 return $self->{aborted};
640             }
641              
642             =head1 HTTP::Request/Response Interface
643              
644             The following pair of methods form an interface that allows the request to be
645             used as a source of L objects, responding to them by sending
646             L objects. This may be useful to fit it in to existing code
647             that already uses these.
648              
649             =cut
650              
651             =head2 $http_req = $req->as_http_request
652              
653             Returns a new C object that gives a reasonable approximation to
654             the request. Because the webserver has translated the original HTTP request
655             into FastCGI parameters, this may not be a perfect recreation of the request
656             as received by the webserver.
657              
658             =cut
659              
660             sub as_http_request
661             {
662 1     1 1 66 my $self = shift;
663              
664 1         731 require HTTP::Request;
665              
666 1         20770 my $params = $self->params;
667              
668 1   50     10 my $authority =
      50        
669             ( $params->{HTTP_HOST} || $params->{SERVER_NAME} || "" ) . ":" .
670             ( $params->{SERVER_PORT} || "80" );
671              
672 1         5 my $path = $self->path;
673 1         4 my $query_string = $self->query_string;
674              
675 1 50       4 $path .= "?$query_string" if length $query_string;
676              
677 1         5 my $uri = URI->new( "http://$authority$path" )->canonical;
678              
679 1         8146 my @headers;
680              
681             # Content-Type and Content-Length come specially
682 1 50       9 push @headers, "Content-Type" => $params->{CONTENT_TYPE}
683             if exists $params->{CONTENT_TYPE};
684              
685 1 50       10 push @headers, "Content-Length" => $params->{CONTENT_LENGTH}
686             if exists $params->{CONTENT_LENGTH};
687              
688             # Pull all the HTTP_FOO parameters as headers. These will be in all-caps
689             # and use _ for word separators, but HTTP::Headers can cope
690 1         6 foreach ( keys %$params ) {
691 11 100       30 m/^HTTP_(.*)$/ and push @headers, $1 => $params->{$_};
692             }
693              
694 1         4 my $content = $self->{stdin};
695              
696 1         6 my $req = HTTP::Request->new( $self->method, $uri, \@headers, $content );
697              
698 1         204 $req->protocol( $self->protocol );
699              
700 1         17 return $req;
701             }
702              
703             =head2 $req->send_http_response( $resp )
704              
705             Sends the given C object as the response to this request. The
706             status, headers and content are all written out to the request's STDOUT stream
707             and then the request is finished with 0 as the exit code.
708              
709             =cut
710              
711             sub send_http_response
712             {
713 1     1 1 11155 my $self = shift;
714 1         2 my ( $resp ) = @_;
715              
716             # (Fast)CGI suggests this is the way to report the status
717 1         4 $resp->header( Status => $resp->code );
718              
719 1         53 my $topline = $resp->protocol . " " . $resp->status_line;
720              
721 1         20 $self->print_stdout( $topline . $CRLF );
722 1         268 $self->print_stdout( $resp->headers_as_string( $CRLF ) );
723              
724 1         5 $self->print_stdout( $CRLF );
725              
726 1         4 $self->print_stdout( $resp->content );
727 1         5 $self->finish( 0 );
728             }
729              
730             package # hide from CPAN
731             Net::Async::FastCGI::Request::TiedHandle;
732 17     17   132 use base qw( Tie::Handle );
  17         37  
  17         16447  
733              
734 17     17   41033 use Symbol qw( gensym );
  17         39  
  17         3897  
735              
736             sub new
737             {
738 16     16   25 my $class = shift;
739              
740 16         55 my $handle = gensym;
741 16         347 tie *$handle, $class, @_;
742              
743 16         85 return $handle;
744             }
745              
746             sub TIEHANDLE
747             {
748 16     16   23 my $class = shift;
749 16         81 return bless { @_ }, $class;
750             }
751              
752 1     1   284 sub CLOSE { shift->{CLOSE}->( @_ ) }
753 3     3   514 sub READ { shift->{READ}->( @_ ) }
754 1     1   703 sub READLINE { shift->{READLINE}->( @_ ) }
755 5     5   38788 sub WRITE { shift->{WRITE}->( @_ ) }
756              
757             =head1 EXAMPLES
758              
759             =head2 Streaming A File
760              
761             To serve contents of files on disk, it may be more efficient to use
762             C:
763              
764             use Net::Async::FastCGI;
765             use IO::Async::Loop;
766              
767             my $fcgi = Net::Async::FastCGI->new(
768             on_request => sub {
769             my ( $fcgi, $req ) = @_;
770              
771             open( my $file, "<", "/path/to/file" );
772             $req->print_stdout( "Status: 200 OK\r\n" .
773             "Content-type: application/octet-stream\r\n" .
774             "\r\n" );
775              
776             $req->stream_stdout_then_finish(
777             sub { read( $file, my $buffer, 8192 ) or return undef; return $buffer },
778             0
779             );
780             }
781              
782             my $loop = IO::Async::Loop->new();
783              
784             $loop->add( $fcgi );
785              
786             $loop->run;
787              
788             It may be more efficient again to instead use the C feature of
789             certain webservers, which allows the webserver itself to serve the file
790             efficiently. See your webserver's documentation for more detail.
791              
792             =head1 AUTHOR
793              
794             Paul Evans
795              
796             =cut
797              
798             0x55AA;