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 65     65   517709 use strict;
  65         209  
  65         1749  
3 65     65   304 use warnings;
  65         151  
  65         1749  
4 65     65   1654 use 5.008_001;
  65         226  
5             our $VERSION = '1.0050';
6              
7 65     65   28278 use HTTP::Headers::Fast;
  65         300552  
  65         4131  
8 65     65   616 use Carp ();
  65         122  
  65         1007  
9 65     65   39095 use Hash::MultiValue;
  65         137525  
  65         2449  
10              
11 65     65   30872 use Plack::Request::Upload;
  65         176  
  65         1815  
12 65     65   27260 use Stream::Buffered;
  65         481742  
  65         1684  
13 65     65   10379 use URI;
  65         128727  
  65         1900  
14 65     65   303 use URI::Escape ();
  65         164  
  65         792  
15 65     65   24804 use Cookie::Baker ();
  65         85816  
  65         1542  
16              
17 65     65   29598 use HTTP::Entity::Parser;
  65         2307602  
  65         4490  
18 65     65   741 use WWW::Form::UrlEncoded qw/parse_urlencoded_arrayref/;
  65         85  
  65         116653  
19              
20             sub new {
21 68     68 1 28612 my($class, $env) = @_;
22 68 50 33     435 Carp::croak(q{$env is required})
23             unless defined $env && ref($env) eq 'HASH';
24              
25 68         300 bless { env => $env }, $class;
26             }
27              
28 435     435 1 1939 sub env { $_[0]->{env} }
29              
30 2     2 1 446 sub address { $_[0]->env->{REMOTE_ADDR} }
31 1     1 1 7 sub remote_host { $_[0]->env->{REMOTE_HOST} }
32 1     1 1 3 sub protocol { $_[0]->env->{SERVER_PROTOCOL} }
33 1     1 1 3 sub method { $_[0]->env->{REQUEST_METHOD} }
34 1     1 0 6834 sub port { $_[0]->env->{SERVER_PORT} }
35 0     0 1 0 sub user { $_[0]->env->{REMOTE_USER} }
36 2     2 1 9 sub request_uri { $_[0]->env->{REQUEST_URI} }
37 3     3 1 65 sub path_info { $_[0]->env->{PATH_INFO} }
38 4 50   4 1 20 sub path { $_[0]->env->{PATH_INFO} || '/' }
39 2     2 1 9 sub query_string{ $_[0]->env->{QUERY_STRING} }
40 0     0 1 0 sub script_name { $_[0]->env->{SCRIPT_NAME} }
41 1     1 1 3 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 46 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 27 my $self = shift;
55              
56 13 100       23 return {} unless $self->env->{HTTP_COOKIE};
57              
58             # HTTP_COOKIE hasn't changed: reuse the parsed cookie
59 12 100 66     21 if ( $self->env->{'plack.cookie.parsed'}
60             && $self->env->{'plack.cookie.string'} eq $self->env->{HTTP_COOKIE}) {
61 11         15 return $self->env->{'plack.cookie.parsed'};
62             }
63              
64 1         2 $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 1270 my $self = shift;
70              
71 13 100       76 unless ($self->env->{'psgix.input.buffered'}) {
72 7         23 $self->_parse_request_body;
73             }
74              
75 13 50       77 my $fh = $self->input or return '';
76 13 100       29 my $cl = $self->env->{CONTENT_LENGTH} or return '';
77              
78 9         65 $fh->seek(0, 0); # just in case middleware/apps read it without seeking back
79 9         150 $fh->read(my($content), $cl, 0);
80 9         75 $fh->seek(0, 0);
81              
82 9         104 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 1     1 1 5 my $self = shift;
91 1 50       5 if (!defined $self->{headers}) {
92 1         13 my $env = $self->env;
93             $self->{headers} = HTTP::Headers::Fast->new(
94             map {
95 5         15 (my $field = $_) =~ s/^HTTPS?_//;
96 5         22 ( lc($field) => $env->{$_} );
97             }
98 1         6 grep { /^(?:HTTP|CONTENT)/i } keys %$env
  25         43  
99             );
100             }
101 1         166 $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   18 my $self = shift;
111 12 100       23 unless ($self->env->{'plack.request.body_parameters'}) {
112 10         38 $self->_parse_request_body;
113             }
114 11         23 return $self->env->{'plack.request.body_parameters'};
115             }
116              
117             sub _query_parameters {
118 27     27   36 my $self = shift;
119 27   66     59 $self->env->{'plack.request.query_parameters'} ||= parse_urlencoded_arrayref($self->env->{'QUERY_STRING'});
120             }
121              
122             sub query_parameters {
123 18     18 1 17242 my $self = shift;
124 18   33     37 $self->env->{'plack.request.query'} ||= Hash::MultiValue->new(@{$self->_query_parameters});
  18         26  
125             }
126              
127             sub body_parameters {
128 3     3 1 16 my $self = shift;
129 3   66     8 $self->env->{'plack.request.body'} ||= Hash::MultiValue->new(@{$self->_body_parameters});
  3         8  
130             }
131              
132             # contains body + query
133             sub parameters {
134 17     17 1 1349 my $self = shift;
135              
136 17   66     33 $self->env->{'plack.request.merged'} ||= do {
137             Hash::MultiValue->new(
138 9         21 @{$self->_query_parameters},
139 9         15 @{$self->_body_parameters}
  9         364  
140             );
141             };
142             }
143              
144             sub uploads {
145 16     16 1 586 my $self = shift;
146              
147 16 100       42 if ($self->env->{'plack.request.upload'}) {
148 13         27 return $self->env->{'plack.request.upload'};
149             }
150              
151 3         14 $self->_parse_request_body;
152 3         8 return $self->env->{'plack.request.upload'};
153             }
154              
155             sub param {
156 8     8 1 2554 my $self = shift;
157              
158 8 100       17 return keys %{ $self->parameters } if @_ == 0;
  3         8  
159              
160 5         8 my $key = shift;
161 5 100       12 return $self->parameters->{$key} unless wantarray;
162 2         6 return $self->parameters->get_all($key);
163             }
164              
165             sub upload {
166 11     11 1 3502 my $self = shift;
167              
168 11 100       35 return keys %{ $self->uploads } if @_ == 0;
  1         5  
169              
170 10         18 my $key = shift;
171 10 100       26 return $self->uploads->{$key} unless wantarray;
172 6         21 return $self->uploads->get_all($key);
173             }
174              
175             sub uri {
176 21     21 1 120 my $self = shift;
177              
178 21         61 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         27 my $path_escape_class = q{^/;:@&=A-Za-z0-9\$_.+!*'(),-};
189              
190 21   100     34 my $path = URI::Escape::uri_escape($self->env->{PATH_INFO} || '', $path_escape_class);
191             $path .= '?' . $self->env->{QUERY_STRING}
192 21 100 100     1327 if defined $self->env->{QUERY_STRING} && $self->env->{QUERY_STRING} ne '';
193              
194 21 100       64 $base =~ s!/$!! if $path =~ m!^/!;
195              
196 21         75 return URI->new($base . $path)->canonical;
197             }
198              
199             sub base {
200 9     9 1 36 my $self = shift;
201 9         24 URI->new($self->_uri_base)->canonical;
202             }
203              
204             sub _uri_base {
205 30     30   42 my $self = shift;
206              
207 30         57 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     198 ($env->{SCRIPT_NAME} || '/');
      66        
      100        
213              
214 30         75 return $uri;
215             }
216              
217             sub new_response {
218 17     17 1 1226 my $self = shift;
219 17         4259 require Plack::Response;
220 17         105 Plack::Response->new(@_);
221             }
222              
223             sub request_body_parser {
224 20     20 0 47 my $self = shift;
225 20   33     109 $self->{request_body_parser} ||= $self->_build_body_parser;
226             }
227              
228             sub _build_body_parser {
229 20     20   41 my $self = shift;
230              
231 20         56 my $len = $self->_buffer_length_for($self->env);
232              
233 20         149 my $parser = HTTP::Entity::Parser->new(buffer_length => $len);
234 20         252 $parser->register('application/x-www-form-urlencoded', 'HTTP::Entity::Parser::UrlEncoded');
235 20         335 $parser->register('multipart/form-data', 'HTTP::Entity::Parser::MultiPart');
236              
237 20         186 $parser;
238             }
239              
240             sub _buffer_length_for {
241 20     20   56 my($self, $env) = @_;
242              
243 20 50       69 return $ENV{PLACK_BUFFER_LENGTH} if defined $ENV{PLACK_BUFFER_LENGTH};
244              
245 20 50       77 if ($env->{'psgix.input.buffered'}) {
246 0         0 return 1024 * 1024; # 1MB for buffered
247             } else {
248 20         53 return 1024 * 64; # 64K for unbuffered
249             }
250             }
251              
252             sub _parse_request_body {
253 20     20   33 my $self = shift;
254              
255 20         56 my ($params,$uploads) = $self->request_body_parser->parse($self->env);
256 19         10652 $self->env->{'plack.request.body_parameters'} = $params;
257              
258 19         126 my $upload_hash = Hash::MultiValue->new();
259 19         849 while ( my ($k,$v) = splice @$uploads, 0, 2 ) {
260 10         227 my %copy = %$v;
261 10         15 $copy{headers} = HTTP::Headers::Fast->new(@{$v->{headers}});
  10         55  
262 10         724 $upload_hash->add($k, Plack::Request::Upload->new(%copy));
263             }
264 19         132 $self->env->{'plack.request.upload'} = $upload_hash;
265 19         39 1;
266             }
267              
268             1;
269             __END__