File Coverage

blib/lib/Plack/Request.pm
Criterion Covered Total %
statement 149 164 90.8
branch 30 36 83.3
condition 22 33 66.6
subroutine 45 59 76.2
pod 38 40 95.0
total 284 332 85.5


line stmt bran cond sub pod time code
1             package Plack::Request;
2 67     67   1109175 use strict;
  67         100  
  67         4349  
3 67     67   341 use warnings;
  67         139  
  67         2940  
4 67     67   1069 use 5.008_001;
  67         219  
5             our $VERSION = '1.0051';
6              
7 67     67   32509 use HTTP::Headers::Fast;
  67         383260  
  67         2482  
8 67     67   417 use Carp ();
  67         148  
  67         1180  
9 67     67   32151 use Hash::MultiValue;
  67         167193  
  67         5750  
10              
11 67     67   32074 use Plack::Request::Upload;
  67         3698  
  67         4358  
12 67     67   32246 use Stream::Buffered;
  67         578440  
  67         1866  
13 67     67   13373 use URI;
  67         213272  
  67         1555  
14 67     67   334 use URI::Escape ();
  67         130  
  67         845  
15 67     67   28276 use Cookie::Baker ();
  67         106561  
  67         2438  
16              
17 67     67   28290 use HTTP::Entity::Parser;
  67         3279738  
  67         3754  
18 67     67   647 use WWW::Form::UrlEncoded qw/parse_urlencoded_arrayref/;
  67         143  
  67         182213  
19              
20             sub new {
21 68     68 1 2239424 my($class, $env) = @_;
22 68 50 33     788 Carp::croak(q{$env is required})
23             unless defined $env && ref($env) eq 'HASH';
24              
25 68         424 bless { env => $env }, $class;
26             }
27              
28 436     436 1 2393 sub env { $_[0]->{env} }
29              
30 2     2 1 515 sub address { $_[0]->env->{REMOTE_ADDR} }
31 1     1 1 18 sub remote_host { $_[0]->env->{REMOTE_HOST} }
32 1     1 1 3 sub protocol { $_[0]->env->{SERVER_PROTOCOL} }
33 1     1 1 4 sub method { $_[0]->env->{REQUEST_METHOD} }
34 1     1 0 9063 sub port { $_[0]->env->{SERVER_PORT} }
35 0     0 1 0 sub user { $_[0]->env->{REMOTE_USER} }
36 2     2 1 23 sub request_uri { $_[0]->env->{REQUEST_URI} }
37 3     3 1 98 sub path_info { $_[0]->env->{PATH_INFO} }
38 4 50   4 1 25 sub path { $_[0]->env->{PATH_INFO} || '/' }
39 2     2 1 13 sub query_string{ $_[0]->env->{QUERY_STRING} }
40 0     0 1 0 sub script_name { $_[0]->env->{SCRIPT_NAME} }
41 1     1 1 5 sub scheme { $_[0]->env->{'psgi.url_scheme'} }
42 0     0 1 0 sub secure { $_[0]->scheme eq 'https' }
43 0     0 1 0 sub body { $_[0]->env->{'psgi.input'} }
44 13     13 1 69 sub input { $_[0]->env->{'psgi.input'} }
45              
46 0     0 1 0 sub content_length { $_[0]->env->{CONTENT_LENGTH} }
47 0     0 1 0 sub content_type { $_[0]->env->{CONTENT_TYPE} }
48              
49 0     0 1 0 sub session { $_[0]->env->{'psgix.session'} }
50 0     0 1 0 sub session_options { $_[0]->env->{'psgix.session.options'} }
51 0     0 1 0 sub logger { $_[0]->env->{'psgix.logger'} }
52              
53             sub cookies {
54 13     13 1 51 my $self = shift;
55              
56 13 100       32 return {} unless $self->env->{HTTP_COOKIE};
57              
58             # HTTP_COOKIE hasn't changed: reuse the parsed cookie
59 12 100 66     24 if ( $self->env->{'plack.cookie.parsed'}
60             && $self->env->{'plack.cookie.string'} eq $self->env->{HTTP_COOKIE}) {
61 11         16 return $self->env->{'plack.cookie.parsed'};
62             }
63              
64 1         3 $self->env->{'plack.cookie.string'} = $self->env->{HTTP_COOKIE};
65 1         2 $self->env->{'plack.cookie.parsed'} = Cookie::Baker::crush_cookie($self->env->{'plack.cookie.string'});
66             }
67              
68             sub content {
69 13     13 1 1285 my $self = shift;
70              
71 13 100       85 unless ($self->env->{'psgix.input.buffered'}) {
72 7         33 $self->_parse_request_body;
73             }
74              
75 13 50       80 my $fh = $self->input or return '';
76 13 100       53824 my $cl = $self->env->{CONTENT_LENGTH} or return '';
77              
78 9         136 $fh->seek(0, 0); # just in case middleware/apps read it without seeking back
79 9         194 $fh->read(my($content), $cl, 0);
80 9         125 $fh->seek(0, 0);
81              
82 9         120 return $content;
83             }
84              
85 0     0 1 0 sub raw_body { $_[0]->content }
86              
87             # XXX you can mutate headers with ->headers but it's not written through to the env
88              
89             sub headers {
90 2     2 1 10 my $self = shift;
91 2 50       11 if (!defined $self->{headers}) {
92 2         8 my $env = $self->env;
93             $self->{headers} = HTTP::Headers::Fast->new(
94             map {
95 8         25 (my $field = $_) =~ s/^HTTPS?_//;
96 8         35 ( lc($field) => $env->{$_} );
97             }
98 2         17 grep { /^(?:HTTP|CONTENT)_/ } keys %$env
  52         114  
99             );
100             }
101 2         368 $self->{headers};
102             }
103              
104 0     0 1 0 sub content_encoding { shift->headers->content_encoding(@_) }
105 0     0 1 0 sub header { shift->headers->header(@_) }
106 0     0 1 0 sub referer { shift->headers->referer(@_) }
107 0     0 1 0 sub user_agent { shift->headers->user_agent(@_) }
108              
109             sub _body_parameters {
110 12     12   37 my $self = shift;
111 12 100       33 unless ($self->env->{'plack.request.body_parameters'}) {
112 10         40 $self->_parse_request_body;
113             }
114 11         40 return $self->env->{'plack.request.body_parameters'};
115             }
116              
117             sub _query_parameters {
118 27     27   51 my $self = shift;
119 27   66     79 $self->env->{'plack.request.query_parameters'} ||= parse_urlencoded_arrayref($self->env->{'QUERY_STRING'});
120             }
121              
122             sub query_parameters {
123 18     18 1 34656 my $self = shift;
124 18   33     66 $self->env->{'plack.request.query'} ||= Hash::MultiValue->new(@{$self->_query_parameters});
  18         44  
125             }
126              
127             sub body_parameters {
128 3     3 1 20 my $self = shift;
129 3   66     15 $self->env->{'plack.request.body'} ||= Hash::MultiValue->new(@{$self->_body_parameters});
  3         16  
130             }
131              
132             # contains body + query
133             sub parameters {
134 17     17 1 2303 my $self = shift;
135              
136 17   66     55 $self->env->{'plack.request.merged'} ||= do {
137             Hash::MultiValue->new(
138 9         37 @{$self->_query_parameters},
139 9         18 @{$self->_body_parameters}
  9         439  
140             );
141             };
142             }
143              
144             sub uploads {
145 16     16 1 3222 my $self = shift;
146              
147 16 100       65 if ($self->env->{'plack.request.upload'}) {
148 13         21 return $self->env->{'plack.request.upload'};
149             }
150              
151 3         22 $self->_parse_request_body;
152 3         7 return $self->env->{'plack.request.upload'};
153             }
154              
155             sub param {
156 8     8 1 4486 my $self = shift;
157              
158 8 100       28 return keys %{ $self->parameters } if @_ == 0;
  3         9  
159              
160 5         21 my $key = shift;
161 5 100       17 return $self->parameters->{$key} unless wantarray;
162 2         7 return $self->parameters->get_all($key);
163             }
164              
165             sub upload {
166 11     11 1 4109 my $self = shift;
167              
168 11 100       30 return keys %{ $self->uploads } if @_ == 0;
  1         4  
169              
170 10         15 my $key = shift;
171 10 100       24 return $self->uploads->{$key} unless wantarray;
172 6         38 return $self->uploads->get_all($key);
173             }
174              
175             sub uri {
176 21     21 1 163 my $self = shift;
177              
178 21         139 my $base = $self->_uri_base;
179              
180             # We have to escape back PATH_INFO in case they include stuff like
181             # ? or # so that the URI parser won't be tricked. However we should
182             # preserve '/' since encoding them into %2f doesn't make sense.
183             # This means when a request like /foo%2fbar comes in, we recognize
184             # it as /foo/bar which is not ideal, but that's how the PSGI PATH_INFO
185             # spec goes and we can't do anything about it. See PSGI::FAQ for details.
186              
187             # See RFC 3986 before modifying.
188 21         42 my $path_escape_class = q{^/;:@&=A-Za-z0-9\$_.+!*'(),-};
189              
190 21   100     51 my $path = URI::Escape::uri_escape($self->env->{PATH_INFO} || '', $path_escape_class);
191             $path .= '?' . $self->env->{QUERY_STRING}
192 21 100 100     980 if defined $self->env->{QUERY_STRING} && $self->env->{QUERY_STRING} ne '';
193              
194 21 100       95 $base =~ s!/$!! if $path =~ m!^/!;
195              
196 21         121 return URI->new($base . $path)->canonical;
197             }
198              
199             sub base {
200 9     9 1 24 my $self = shift;
201 9         16 URI->new($self->_uri_base)->canonical;
202             }
203              
204             sub _uri_base {
205 30     30   52 my $self = shift;
206              
207 30         81 my $env = $self->env;
208              
209             my $uri = ($env->{'psgi.url_scheme'} || "http") .
210             "://" .
211             ($env->{HTTP_HOST} || (($env->{SERVER_NAME} || "") . ":" . ($env->{SERVER_PORT} || 80))) .
212 30   100     241 ($env->{SCRIPT_NAME} || '/');
      66        
      100        
213              
214 30         90 return $uri;
215             }
216              
217             sub new_response {
218 17     17 1 1535 my $self = shift;
219 17         7052 require Plack::Response;
220 17         138 Plack::Response->new(@_);
221             }
222              
223             sub request_body_parser {
224 20     20 0 35 my $self = shift;
225 20   33     162 $self->{request_body_parser} ||= $self->_build_body_parser;
226             }
227              
228             sub _build_body_parser {
229 20     20   58 my $self = shift;
230              
231 20         66 my $len = $self->_buffer_length_for($self->env);
232              
233 20         219 my $parser = HTTP::Entity::Parser->new(buffer_length => $len);
234 20         336 $parser->register('application/x-www-form-urlencoded', 'HTTP::Entity::Parser::UrlEncoded');
235 20         396 $parser->register('multipart/form-data', 'HTTP::Entity::Parser::MultiPart');
236              
237 20         262 $parser;
238             }
239              
240             sub _buffer_length_for {
241 20     20   94 my($self, $env) = @_;
242              
243 20 50       95 return $ENV{PLACK_BUFFER_LENGTH} if defined $ENV{PLACK_BUFFER_LENGTH};
244              
245 20 50       93 if ($env->{'psgix.input.buffered'}) {
246 0         0 return 1024 * 1024; # 1MB for buffered
247             } else {
248 20         87 return 1024 * 64; # 64K for unbuffered
249             }
250             }
251              
252             sub _parse_request_body {
253 20     20   43 my $self = shift;
254              
255 20         78 my ($params,$uploads) = $self->request_body_parser->parse($self->env);
256 19         14466 $self->env->{'plack.request.body_parameters'} = $params;
257              
258 19         157 my $upload_hash = Hash::MultiValue->new();
259 19         984 while ( my ($k,$v) = splice @$uploads, 0, 2 ) {
260 10         181 my %copy = %$v;
261 10         16 $copy{headers} = HTTP::Headers::Fast->new(@{$v->{headers}});
  10         39  
262 10         636 $upload_hash->add($k, Plack::Request::Upload->new(%copy));
263             }
264 19         192 $self->env->{'plack.request.upload'} = $upload_hash;
265 19         85 1;
266             }
267              
268             1;
269             __END__