File Coverage

blib/lib/CGI/Parse/PSGI.pm
Criterion Covered Total %
statement 60 62 96.7
branch 19 22 86.3
condition 9 14 64.2
subroutine 7 7 100.0
pod 0 1 0.0
total 95 106 89.6


line stmt bran cond sub pod time code
1             package CGI::Parse::PSGI;
2 6     6   14458 use strict;
  6         12  
  6         161  
3 6     6   28 use base qw(Exporter);
  6         11  
  6         556  
4             our @EXPORT_OK = qw( parse_cgi_output );
5              
6 6     6   2320 use IO::File; # perl bug: should be loaded to call ->getline etc. on filehandle/PerlIO
  6         41777  
  6         611  
7 6     6   2744 use HTTP::Response;
  6         138002  
  6         2910  
8              
9             our %DEFAULT_OPTS = (
10             ignore_status_line => 0,
11             );
12              
13             sub parse_cgi_output {
14 16     16 0 8702 my $output = shift;
15 16         49 my $options = \%DEFAULT_OPTS;
16 16 100       77 if (ref $_[0] eq 'HASH') {
17 4         19 $options = { %DEFAULT_OPTS, %{ +shift } }; # Use default opts where none supplied
  4         14  
18             }
19              
20 16         28 my $length;
21 16 100       53 if (ref $output eq 'SCALAR') {
22 11         18 $length = length $$output;
23 11     1   131 open my $io, "<", $output;
  1         8  
  1         2  
  1         8  
24 11         829 $output = $io;
25             } else {
26 5 50       93 open my $tmp, '<&=:perlio:raw', fileno($output) or die $!;
27 5         13 $output = $tmp;
28 5         28 $length = -s $output;
29             }
30              
31 16         26 my $headers;
32 16         396 while ( my $line = $output->getline ) {
33 49         1069 $headers .= $line;
34 49 100       560 last if $headers =~ /\x0d?\x0a\x0d?\x0a$/;
35             }
36 16 50       48 unless ( defined $headers ) {
37 0         0 $headers = "HTTP/1.1 500 Internal Server Error\x0d\x0a";
38             }
39              
40 16 100       55 unless ( $headers =~ /^HTTP/ ) {
41 10         28 $headers = "HTTP/1.1 200 OK\x0d\x0a" . $headers;
42             }
43              
44 16         112 my $response = HTTP::Response->parse($headers);
45              
46             # RFC 3875 6.2.3
47 16 100 100     3073 if ($response->header('Location') && !$response->header('Status')) {
48 1         399 $response->header('Status', 302);
49             }
50              
51             my $status = $options->{ignore_status_line}?
52 16 100 50     913 200 : ($response->code || 200);
53              
54 16         176 my $status_header = $response->header('Status');
55 16 100       799 if ($status_header) {
56             # Use the header status preferentially, if present and well formed
57              
58             # Extract the code from the header (should be 3 digits, non zero)
59 6         22 my ($code) = ($status_header =~ /^ \s* (\d+) /x);
60              
61 6   33     18 $status = $code || $status;
62             }
63              
64 16         82 $response->remove_header('Status'); # PSGI doesn't allow having Status header in the response
65              
66 16         440 my $remaining = $length - tell( $output );
67 16 50 33     45 if ( $response->code == 500 && !$remaining ) {
68             return [
69 0         0 500,
70             [ 'Content-Type' => 'text/html' ],
71             [ $response->error_as_HTML ]
72             ];
73             }
74              
75             # TODO we can pass $output to the response body without buffering all?
76              
77             {
78 16         208 my $length = 0;
  16         31  
79 16         68 while ( $output->read( my $buffer, 4096 ) ) {
80 15         170 $length += length($buffer);
81 15         60 $response->add_content($buffer);
82             }
83              
84 16 100 100     399 if ( $length && !$response->content_length ) {
85 13         468 $response->content_length($length);
86             }
87             }
88              
89             return [
90             $status,
91             +[
92             map {
93 16         584 my $k = $_;
  35         528  
94 35         89 map { ( $k => _cleanup_newline($_) ) } $response->headers->header($_);
  35         1268  
95             } $response->headers->header_field_names
96             ],
97             [$response->content],
98             ];
99             }
100              
101             sub _cleanup_newline {
102 35     35   66 local $_ = shift;
103 35         77 s/\r?\n//g;
104 35         218 return $_;
105             }
106              
107             1;
108              
109             __END__