line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package CGI::Parse::PSGI::Streaming; |
2
|
3
|
|
|
3
|
|
198080
|
use strict; |
|
3
|
|
|
|
|
25
|
|
|
3
|
|
|
|
|
89
|
|
3
|
3
|
|
|
3
|
|
17
|
use warnings; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
138
|
|
4
|
|
|
|
|
|
|
our $VERSION = '1.0.1'; # VERSION |
5
|
3
|
|
|
3
|
|
1003
|
use HTTP::Response; |
|
3
|
|
|
|
|
47127
|
|
|
3
|
|
|
|
|
89
|
|
6
|
3
|
|
|
3
|
|
1357
|
use CGI::Parse::PSGI::Streaming::Handle; |
|
3
|
|
|
|
|
11
|
|
|
3
|
|
|
|
|
92
|
|
7
|
3
|
|
|
3
|
|
20
|
use SelectSaver; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
1608
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# ABSTRACT: creates a filehandle that parses CGI output and writes to a PSGI responder |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
sub parse_cgi_output_streaming_fh { |
13
|
5
|
|
|
5
|
1
|
88529
|
my ($responder) = @_; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# ugly-ish way to get a ref to a new filehandle |
16
|
5
|
|
|
|
|
12
|
my $output = \do {local *HANDLE}; |
|
5
|
|
|
|
|
32
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# state for the callback closure |
19
|
5
|
|
|
|
|
19
|
my $headers; # string, accumulated headers |
20
|
|
|
|
|
|
|
my $response; # HTTP::Response object with parsed headers |
21
|
5
|
|
|
|
|
0
|
my $writer; # the writer object returned by the responder |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
## no critic(ProhibitTies) |
24
|
5
|
|
|
|
|
128
|
tie *{$output},'CGI::Parse::PSGI::Streaming::Handle', sub { |
25
|
|
|
|
|
|
|
# this callback is invoked with whatever bytes were printed to |
26
|
|
|
|
|
|
|
# the filehandle; it will be called with no argument (or an |
27
|
|
|
|
|
|
|
# undef) when the filehandle is closed |
28
|
9
|
|
|
9
|
|
276
|
my ($data) = @_; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# reset the default filehandle to the real STDOUT, just in |
31
|
|
|
|
|
|
|
# case: it's nice to make sure all the callbacks are invoked |
32
|
|
|
|
|
|
|
# with the state they expect |
33
|
9
|
|
|
|
|
83
|
my $saver = SelectSaver->new("::STDOUT"); |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# if we're still parsing the headers |
36
|
9
|
100
|
|
|
|
284
|
if (!$response) { |
37
|
6
|
50
|
|
|
|
20
|
if (defined $data) { |
38
|
6
|
|
|
|
|
307
|
$headers .= $data; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
else { # closed file before the end of headers |
41
|
0
|
|
|
|
|
0
|
$headers = "HTTP/1.1 500 Internal Server Error\x0d\x0a"; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# still more headers to come, return to the CGI |
45
|
6
|
100
|
|
|
|
55
|
return unless $headers =~ /\x0d?\x0a\x0d?\x0a/; |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# since we may have received the last bytes of the headers |
48
|
|
|
|
|
|
|
# together with the first bytes of the body, we want to |
49
|
|
|
|
|
|
|
# make sure that $headers contains only the headers, and |
50
|
|
|
|
|
|
|
# $data contains only the body (or '') |
51
|
5
|
|
|
|
|
391
|
($headers,$data) = |
52
|
|
|
|
|
|
|
($headers =~ m{\A(.+?)\x0d?\x0a\x0d?\x0a(.*)\z}sm); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# HTTP::Response wants things formatted like... an HTTP |
55
|
|
|
|
|
|
|
# response. CGI output is slightly different. Let's cheat. |
56
|
5
|
50
|
|
|
|
24
|
unless ( $headers =~ /^HTTP/ ) { |
57
|
5
|
|
|
|
|
17
|
$headers = "HTTP/1.1 200 OK\x0d\x0a" . $headers; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
5
|
|
|
|
|
52
|
$response = HTTP::Response->parse($headers); |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# RFC 3875 6.2.3 |
63
|
5
|
100
|
100
|
|
|
1334
|
if ($response->header('Location') && !$response->header('Status')) { |
64
|
1
|
|
|
|
|
96
|
$response->header('Status', 302); |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# this is not a "elsif"! we may have the start of the body |
69
|
|
|
|
|
|
|
# with the same 'print' as the end of the headers, and we want |
70
|
|
|
|
|
|
|
# to stream out that body already |
71
|
8
|
50
|
|
|
|
451
|
if ($response) { # we have parsed the headers |
72
|
8
|
50
|
33
|
|
|
31
|
if ( $response->code == 500 && !defined($data) ) { |
73
|
|
|
|
|
|
|
# filehandle closed after a raw 500, synthesise a body |
74
|
0
|
|
|
|
|
0
|
$responder->([ |
75
|
|
|
|
|
|
|
500, |
76
|
|
|
|
|
|
|
[ 'Content-Type' => 'text/html' ], |
77
|
|
|
|
|
|
|
[ $response->error_as_HTML ] |
78
|
|
|
|
|
|
|
]); |
79
|
0
|
|
|
|
|
0
|
return; |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
# we haven't sent the headers to the PSGI backend yet |
82
|
8
|
100
|
|
|
|
148
|
if (!$writer) { |
83
|
5
|
|
100
|
|
|
15
|
my $status = $response->header('Status') || 200; |
84
|
5
|
|
|
|
|
234
|
$status =~ s/\s+.*$//; # remove ' OK' in '200 OK' |
85
|
|
|
|
|
|
|
# PSGI doesn't allow having Status header in the response |
86
|
5
|
|
|
|
|
33
|
$response->remove_header('Status'); |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# we send the status and headers, we get a writer |
89
|
|
|
|
|
|
|
# object back |
90
|
|
|
|
|
|
|
$writer = $responder->([ |
91
|
|
|
|
|
|
|
$status, |
92
|
|
|
|
|
|
|
+[ |
93
|
|
|
|
|
|
|
map { |
94
|
5
|
|
|
|
|
194
|
my $k = $_; |
|
8
|
|
|
|
|
154
|
|
95
|
8
|
|
|
|
|
21
|
map { ( $k => _cleanup_newline($_) ) } |
|
8
|
|
|
|
|
288
|
|
96
|
|
|
|
|
|
|
$response->headers->header($_); |
97
|
|
|
|
|
|
|
} $response->headers->header_field_names |
98
|
|
|
|
|
|
|
], |
99
|
|
|
|
|
|
|
]); |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# ok, now we have a writer object (either just built, or |
103
|
|
|
|
|
|
|
# built during a previous call). Let's send it whatever |
104
|
|
|
|
|
|
|
# body we have |
105
|
8
|
100
|
|
|
|
10607
|
if (defined $data) { |
106
|
7
|
100
|
|
|
|
90
|
$writer->write($data) if length($data); |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
else { |
109
|
1
|
|
|
|
|
14
|
$writer->close; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
} |
112
|
5
|
|
|
|
|
10
|
}; |
113
|
|
|
|
|
|
|
|
114
|
5
|
|
|
|
|
20
|
return $output; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub _cleanup_newline { |
118
|
8
|
|
|
8
|
|
16
|
local $_ = shift; |
119
|
8
|
|
|
|
|
17
|
s/\r?\n//g; |
120
|
8
|
|
|
|
|
42
|
return $_; |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
1; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
__END__ |