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   1172744 use strict;
  67         137  
  67         2373  
3 67     67   388 use warnings;
  67         139  
  67         3236  
4 67     67   1404 use 5.008_001;
  67         255  
5             our $VERSION = '1.0054';
6              
7 67     67   37086 use HTTP::Headers::Fast;
  67         414156  
  67         2941  
8 67     67   724 use Carp ();
  67         88  
  67         1203  
9 67     67   44680 use Hash::MultiValue;
  67         191393  
  67         9993  
10              
11 67     67   44972 use Plack::Request::Upload;
  67         2153  
  67         3588  
12 67     67   40903 use Stream::Buffered;
  67         748935  
  67         2355  
13 67     67   17129 use URI;
  67         196573  
  67         2624  
14 67     67   524 use URI::Escape ();
  67         210  
  67         1344  
15 67     67   35944 use Cookie::Baker ();
  67         128228  
  67         2019  
16              
17 67     67   41680 use HTTP::Entity::Parser;
  67         3901301  
  67         3235  
18 67     67   589 use WWW::Form::UrlEncoded qw/parse_urlencoded_arrayref/;
  67         104  
  67         135094  
19              
20             sub new {
21 68     68 1 2066131 my($class, $env) = @_;
22 68 50 33     640 Carp::croak(q{$env is required})
23             unless defined $env && ref($env) eq 'HASH';
24              
25 68         468 bless { env => $env }, $class;
26             }
27              
28 436     436 1 2577 sub env { $_[0]->{env} }
29              
30 2     2 1 618 sub address { $_[0]->env->{REMOTE_ADDR} }
31 1     1 1 10 sub remote_host { $_[0]->env->{REMOTE_HOST} }
32 1     1 1 4 sub protocol { $_[0]->env->{SERVER_PROTOCOL} }
33 1     1 1 6 sub method { $_[0]->env->{REQUEST_METHOD} }
34 1     1 0 9138 sub port { $_[0]->env->{SERVER_PORT} }
35 0     0 1 0 sub user { $_[0]->env->{REMOTE_USER} }
36 2     2 1 93 sub request_uri { $_[0]->env->{REQUEST_URI} }
37 3     3 1 65 sub path_info { $_[0]->env->{PATH_INFO} }
38 4 50   4 1 23 sub path { $_[0]->env->{PATH_INFO} || '/' }
39 2     2 1 16 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 59 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 35 my $self = shift;
55              
56 13 100       33 return {} unless $self->env->{HTTP_COOKIE};
57              
58             # HTTP_COOKIE hasn't changed: reuse the parsed cookie
59 12 100 66     22 if ( $self->env->{'plack.cookie.parsed'}
60             && $self->env->{'plack.cookie.string'} eq $self->env->{HTTP_COOKIE}) {
61 11         21 return $self->env->{'plack.cookie.parsed'};
62             }
63              
64 1         4 $self->env->{'plack.cookie.string'} = $self->env->{HTTP_COOKIE};
65 1         3 $self->env->{'plack.cookie.parsed'} = Cookie::Baker::crush_cookie($self->env->{'plack.cookie.string'});
66             }
67              
68             sub content {
69 13     13 1 783 my $self = shift;
70              
71 13 100       113 unless ($self->env->{'psgix.input.buffered'}) {
72 7         29 $self->_parse_request_body;
73             }
74              
75 13 50       114 my $fh = $self->input or return '';
76 13 100       61724 my $cl = $self->env->{CONTENT_LENGTH} or return '';
77              
78 9         114 $fh->seek(0, 0); # just in case middleware/apps read it without seeking back
79 9         211 $fh->read(my($content), $cl, 0);
80 9         113 $fh->seek(0, 0);
81              
82 9         147 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 13 my $self = shift;
91 2 50       12 if (!defined $self->{headers}) {
92 2         9 my $env = $self->env;
93             $self->{headers} = HTTP::Headers::Fast->new(
94             map {
95 8         22 (my $field = $_) =~ s/^HTTPS?_//;
96 8         34 ( lc($field) => $env->{$_} );
97             }
98 2         15 grep { /^(?:HTTP|CONTENT)_/ } keys %$env
  52         100  
99             );
100             }
101 2         437 $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   24 my $self = shift;
111 12 100       29 unless ($self->env->{'plack.request.body_parameters'}) {
112 10         30 $self->_parse_request_body;
113             }
114 11         56 return $self->env->{'plack.request.body_parameters'};
115             }
116              
117             sub _query_parameters {
118 27     27   43 my $self = shift;
119 27   66     58 $self->env->{'plack.request.query_parameters'} ||= parse_urlencoded_arrayref($self->env->{'QUERY_STRING'});
120             }
121              
122             sub query_parameters {
123 18     18 1 45801 my $self = shift;
124 18   33     73 $self->env->{'plack.request.query'} ||= Hash::MultiValue->new(@{$self->_query_parameters});
  18         46  
125             }
126              
127             sub body_parameters {
128 3     3 1 31 my $self = shift;
129 3   66     29 $self->env->{'plack.request.body'} ||= Hash::MultiValue->new(@{$self->_body_parameters});
  3         14  
130             }
131              
132             # contains body + query
133             sub parameters {
134 17     17 1 2511 my $self = shift;
135              
136 17   66     48 $self->env->{'plack.request.merged'} ||= do {
137             Hash::MultiValue->new(
138 9         23 @{$self->_query_parameters},
139 9         16 @{$self->_body_parameters}
  9         362  
140             );
141             };
142             }
143              
144             sub uploads {
145 16     16 1 933 my $self = shift;
146              
147 16 100       53 if ($self->env->{'plack.request.upload'}) {
148 13         29 return $self->env->{'plack.request.upload'};
149             }
150              
151 3         20 $self->_parse_request_body;
152 3         8 return $self->env->{'plack.request.upload'};
153             }
154              
155             sub param {
156 8     8 1 2745 my $self = shift;
157              
158 8 100       19 return keys %{ $self->parameters } if @_ == 0;
  3         7  
159              
160 5         7 my $key = shift;
161 5 100       13 return $self->parameters->{$key} unless wantarray;
162 2         4 return $self->parameters->get_all($key);
163             }
164              
165             sub upload {
166 11     11 1 7271 my $self = shift;
167              
168 11 100       39 return keys %{ $self->uploads } if @_ == 0;
  1         5  
169              
170 10         21 my $key = shift;
171 10 100       40 return $self->uploads->{$key} unless wantarray;
172 6         20 return $self->uploads->get_all($key);
173             }
174              
175             sub uri {
176 21     21 1 183 my $self = shift;
177              
178 21         65 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         44 my $path_escape_class = q{^/;:@&=A-Za-z0-9\$_.+!*'(),-};
189              
190 21   100     73 my $path = URI::Escape::uri_escape($self->env->{PATH_INFO} || '', $path_escape_class);
191             $path .= '?' . $self->env->{QUERY_STRING}
192 21 100 100     987 if defined $self->env->{QUERY_STRING} && $self->env->{QUERY_STRING} ne '';
193              
194 21 100       98 $base =~ s!/$!! if $path =~ m!^/!;
195              
196 21         134 return URI->new($base . $path)->canonical;
197             }
198              
199             sub base {
200 9     9 1 43 my $self = shift;
201 9         29 URI->new($self->_uri_base)->canonical;
202             }
203              
204             sub _uri_base {
205 30     30   56 my $self = shift;
206              
207 30         96 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     287 ($env->{SCRIPT_NAME} || '/');
      66        
      100        
213              
214 30         105 return $uri;
215             }
216              
217             sub new_response {
218 17     17 1 2155 my $self = shift;
219 17         7239 require Plack::Response;
220 17         150 Plack::Response->new(@_);
221             }
222              
223             sub request_body_parser {
224 20     20 0 34 my $self = shift;
225 20   33     131 $self->{request_body_parser} ||= $self->_build_body_parser;
226             }
227              
228             sub _build_body_parser {
229 20     20   128 my $self = shift;
230              
231 20         88 my $len = $self->_buffer_length_for($self->env);
232              
233 20         199 my $parser = HTTP::Entity::Parser->new(buffer_length => $len);
234 20         289 $parser->register('application/x-www-form-urlencoded', 'HTTP::Entity::Parser::UrlEncoded');
235 20         319 $parser->register('multipart/form-data', 'HTTP::Entity::Parser::MultiPart');
236              
237 20         233 $parser;
238             }
239              
240             sub _buffer_length_for {
241 20     20   44 my($self, $env) = @_;
242              
243 20 50       82 return $ENV{PLACK_BUFFER_LENGTH} if defined $ENV{PLACK_BUFFER_LENGTH};
244              
245 20 50       92 if ($env->{'psgix.input.buffered'}) {
246 0         0 return 1024 * 1024; # 1MB for buffered
247             } else {
248 20         62 return 1024 * 64; # 64K for unbuffered
249             }
250             }
251              
252             sub _parse_request_body {
253 20     20   36 my $self = shift;
254              
255 20         87 my ($params,$uploads) = $self->request_body_parser->parse($self->env);
256 19         16627 $self->env->{'plack.request.body_parameters'} = $params;
257              
258 19         176 my $upload_hash = Hash::MultiValue->new();
259 19         962 while ( my ($k,$v) = splice @$uploads, 0, 2 ) {
260 10         315 my %copy = %$v;
261 10         25 $copy{headers} = HTTP::Headers::Fast->new(@{$v->{headers}});
  10         56  
262 10         1006 $upload_hash->add($k, Plack::Request::Upload->new(%copy));
263             }
264 19         175 $self->env->{'plack.request.upload'} = $upload_hash;
265 19         99 1;
266             }
267              
268             1;
269             __END__