File Coverage

blib/lib/HTTP/Message/PSGI.pm
Criterion Covered Total %
statement 85 112 75.8
branch 32 50 64.0
condition 13 19 68.4
subroutine 16 20 80.0
pod 2 4 50.0
total 148 205 72.2


line stmt bran cond sub pod time code
1             package HTTP::Message::PSGI;
2 71     71   715439 use strict;
  71         126  
  71         2166  
3 71     71   369 use warnings;
  71         139  
  71         3218  
4 71     71   2859 use parent qw(Exporter);
  71         1957  
  71         401  
5             our @EXPORT = qw( req_to_psgi res_from_psgi );
6              
7 71     71   4984 use Carp ();
  71         112  
  71         1433  
8 71     71   4565 use HTTP::Status qw(status_message);
  71         41481  
  71         8973  
9 71     71   3570 use URI::Escape ();
  71         22343  
  71         1434  
10 71     71   10057 use Plack::Util;
  71         126  
  71         1770  
11 71     71   290 use Scalar::Util ();
  71         125  
  71         81090  
12              
13             my $TRUE = (1 == 1);
14             my $FALSE = !$TRUE;
15              
16             sub req_to_psgi {
17 348     348 1 1100101 my $req = shift;
18              
19 348 50 33     2857 unless (Scalar::Util::blessed($req) && $req->isa('HTTP::Request')) {
20 0         0 Carp::croak("Request is not HTTP::Request: $req");
21             }
22              
23             # from HTTP::Request::AsCGI
24 348         1395 my $host = $req->header('Host');
25 348         18170 my $uri = $req->uri->clone;
26 348 100       4657 $uri->scheme('http') unless $uri->scheme;
27 348 100       8513 $uri->host('localhost') unless $uri->host;
28 348 50       9455 $uri->port(80) unless $uri->port;
29 348 100 66     7287 $uri->host_port($host) unless !$host || ( $host eq $uri->host_port );
30              
31 348         859 my $input;
32 348         1038 my $content = $req->content;
33 348 50       3640 if (ref $content eq 'CODE') {
34 0 0       0 if (defined $req->content_length) {
35 0         0 $input = HTTP::Message::PSGI::ChunkedInput->new($content);
36             } else {
37 0         0 $req->header("Transfer-Encoding" => "chunked");
38 0         0 $input = HTTP::Message::PSGI::ChunkedInput->new($content, 1);
39             }
40             } else {
41 348         3618 open $input, "<", \$content;
42 348 100       1295 $req->content_length(length $content)
43             unless defined $req->content_length;
44             }
45              
46 348 50 100     22530 my $env = {
      100        
      50        
      100        
47             PATH_INFO => URI::Escape::uri_unescape($uri->path || '/'),
48             QUERY_STRING => $uri->query || '',
49             SCRIPT_NAME => '',
50             SERVER_NAME => $uri->host,
51             SERVER_PORT => $uri->port,
52             SERVER_PROTOCOL => $req->protocol || 'HTTP/1.1',
53             REMOTE_ADDR => '127.0.0.1',
54             REMOTE_HOST => 'localhost',
55             REMOTE_PORT => int( rand(64000) + 1000 ), # not in RFC 3875
56             REQUEST_URI => $uri->path_query || '/', # not in RFC 3875
57             REQUEST_METHOD => $req->method,
58             'psgi.version' => [ 1, 1 ],
59             'psgi.url_scheme' => $uri->scheme eq 'https' ? 'https' : 'http',
60             'psgi.input' => $input,
61             'psgi.errors' => *STDERR,
62             'psgi.multithread' => $FALSE,
63             'psgi.multiprocess' => $FALSE,
64             'psgi.run_once' => $TRUE,
65             'psgi.streaming' => $TRUE,
66             'psgi.nonblocking' => $FALSE,
67             @_,
68             };
69              
70 348         42038 for my $field ( $req->headers->header_field_names ) {
71 397         8643 my $key = uc("HTTP_$field");
72 397         625 $key =~ tr/-/_/;
73 397 100       2264 $key =~ s/^HTTP_// if $field =~ /^Content-(Length|Type)$/;
74              
75 397 50       909 unless ( exists $env->{$key} ) {
76 397         773 $env->{$key} = $req->headers->header($field);
77             }
78             }
79              
80 348 50       11804 if ($env->{SCRIPT_NAME}) {
81 0         0 $env->{PATH_INFO} =~ s/^\Q$env->{SCRIPT_NAME}\E/\//;
82 0         0 $env->{PATH_INFO} =~ s/^\/+/\//;
83             }
84              
85 348 100 100     1280 if (!defined($env->{HTTP_HOST}) && $req->uri->can('host')) {
86 338         3234 $env->{HTTP_HOST} = $req->uri->host;
87 338 100       9179 $env->{HTTP_HOST} .= ':' . $req->uri->port
88             if $req->uri->port ne $req->uri->default_port;
89             }
90              
91 348         10360 return $env;
92             }
93              
94             sub res_from_psgi {
95 330     330 1 538 my ($psgi_res) = @_;
96              
97 330         1754 require HTTP::Response;
98              
99 330         464 my $res;
100 330 100       776 if (ref $psgi_res eq 'ARRAY') {
    100          
101 293         618 _res_from_psgi($psgi_res, \$res);
102             } elsif (ref $psgi_res eq 'CODE') {
103             $psgi_res->(sub {
104 33     33   96 _res_from_psgi($_[0], \$res);
105 35         146 });
106             } else {
107 2 100       303 Carp::croak("Bad response: ", defined $psgi_res ? $psgi_res : 'undef');
108             }
109              
110 326         1450 return $res;
111             }
112              
113             sub _res_from_psgi {
114 326     326   446 my ($status, $headers, $body) = @{+shift};
  326         726  
115 326         444 my $res_ref = shift;
116              
117             my $convert_resp = sub {
118 326     326   1227 my $res = HTTP::Response->new($status);
119 326         14409 $res->message(status_message($status));
120 326 100       3823 $res->headers->header(@$headers) if @$headers;
121              
122 326 100       17917 if (ref $body eq 'ARRAY') {
123 295         1520 $res->content(join '', grep defined, @$body);
124             } else {
125 31         151 local $/ = \4096;
126 31         49 my $content = '';
127 31         1124 while (defined(my $buf = $body->getline)) {
128 93         980 $content .= $buf;
129             }
130 31         181 $body->close;
131 31         3059 $res->content($content);
132             }
133              
134 326         6022 ${ $res_ref } = $res;
  326         527  
135              
136 326         2308 return;
137 326         1552 };
138              
139 326 100       732 if (!defined $body) {
140 15         27 $body = [];
141             my $o = Plack::Util::inline_object
142 21     21   53 write => sub { push @$body, @_ },
143 15         101 close => $convert_resp;
144              
145 15         43 return $o;
146             }
147              
148 311         473 $convert_resp->();
149             }
150              
151             sub HTTP::Request::to_psgi {
152 292     292 0 446210 req_to_psgi(@_);
153             }
154              
155             sub HTTP::Response::from_psgi {
156 293     293 0 2490 my $class = shift;
157 293         658 res_from_psgi(@_);
158             }
159              
160             package
161             HTTP::Message::PSGI::ChunkedInput;
162              
163             sub new {
164 0     0     my($class, $content, $chunked) = @_;
165              
166 0           my $content_cb;
167 0 0         if ($chunked) {
168 0           my $done;
169             $content_cb = sub {
170 0     0     my $chunk = $content->();
171 0 0         return if $done;
172 0 0         unless (defined $chunk) {
173 0           $done = 1;
174 0           return "0\015\012\015\012";
175             }
176 0 0         return '' unless length $chunk;
177 0           return sprintf('%x', length $chunk) . "\015\012$chunk\015\012";
178 0           };
179             } else {
180 0           $content_cb = $content;
181             }
182              
183 0           bless { content => $content_cb }, $class;
184             }
185              
186             sub read {
187 0     0     my $self = shift;
188              
189 0           my $chunk = $self->{content}->();
190 0 0         return 0 unless defined $chunk;
191              
192 0           $_[0] = '';
193 0   0       substr($_[0], $_[2] || 0, length $chunk) = $chunk;
194              
195 0           return length $chunk;
196             }
197              
198       0     sub close { }
199              
200             package HTTP::Message::PSGI;
201              
202             1;
203              
204             __END__