File Coverage

blib/lib/Dancer2/Core/Request.pm
Criterion Covered Total %
statement 327 347 94.2
branch 118 142 83.1
condition 47 63 74.6
subroutine 85 92 92.3
pod 48 55 87.2
total 625 699 89.4


line stmt bran cond sub pod time code
1             package Dancer2::Core::Request;
2             # ABSTRACT: Interface for accessing incoming requests
3             $Dancer2::Core::Request::VERSION = '2.1.0';
4 165     165   813182 use strict;
  165         389  
  165         6875  
5 165     165   909 use warnings;
  165         361  
  165         10514  
6 165     165   4605 use parent 'Plack::Request';
  165         2078  
  165         1683  
7              
8 165     165   14824328 use Carp;
  165         411  
  165         13539  
9 165     165   1147 use Encode qw(decode FB_CROAK LEAVE_SRC);
  165         377  
  165         9282  
10 165     165   1034 use URI;
  165         380  
  165         4305  
11 165     165   861 use URI::Escape;
  165         347  
  165         9500  
12 165     165   8392 use Safe::Isa;
  165         9929  
  165         25977  
13 165     165   1338 use Hash::MultiValue;
  165         441  
  165         6898  
14 165     165   7892 use Ref::Util qw< is_ref is_arrayref is_hashref is_coderef >;
  165         19167  
  165         14240  
15              
16 165     165   4040 use Dancer2::Core::Types;
  165         585  
  165         3407  
17 165     165   2701675 use Dancer2::Core::Request::Upload;
  165         849  
  165         8610  
18 165     165   9180 use Dancer2::Core::Cookie;
  165         560  
  165         29259  
19              
20             # add an attribute for each HTTP_* variables
21             # (HOST is managed manually)
22             my @http_env_keys = (qw/
23             accept_charset
24             accept_encoding
25             accept_language
26             connection
27             keep_alive
28             x_requested_with
29             /);
30              
31             # apparently you can't eval core functions
32 1     1 1 1945 sub accept { $_[0]->env->{'HTTP_ACCEPT'} }
33              
34 1     1 1 435 eval << "_EVAL" or die $@ for @http_env_keys; ## no critic
  1     1 1 430  
  1     1 1 418  
  1     1 1 404  
  1     1 1 434  
  1     1 1 1357  
35             sub $_ { \$_[0]->env->{ 'HTTP_' . ( uc "$_" ) } }
36             1;
37             _EVAL
38              
39             eval {
40             require Unicode::UTF8;
41 165     165   1418 no warnings qw;
  165         447  
  165         26501  
42 27     27   461 *__decode = sub { Unicode::UTF8::decode_utf8($_[0]) };
43 29     29   209 *__valid = sub { Unicode::UTF8::valid_utf8($_[0]) };
44             1;
45             } or do {
46 165     165   1287 no warnings qw;
  165         363  
  165         980921  
47             *__decode = sub { decode( 'UTF-8', $_[0] ) };
48             *__valid = sub {
49             eval { decode( 'UTF-8', $_[0], FB_CROAK | LEAVE_SRC ); 1 };
50             };
51             };
52              
53             # check presence of XS module to speedup request
54             our $XS_URL_DECODE = eval { require URL::Encode::XS; 1; };
55             our $XS_PARSE_QUERY_STRING = eval { require CGI::Deurl::XS; 1; };
56             our $XS_HTTP_COOKIES = eval { require HTTP::XSCookies; 1; };
57              
58             our $_id = 0;
59              
60             # self->new( env => {}, serializer => $s, is_behind_proxy => 0|1 )
61             sub new {
62 783     783 1 2414061 my ( $class, @args ) = @_;
63              
64             # even sized list
65 783 50       3598 @args % 2 == 0
66             or croak 'Must provide even sized list';
67              
68 783         4011 my %opts = @args;
69 783         2220 my $env = $opts{'env'};
70              
71 783         5920 my $self = $class->SUPER::new($env);
72              
73 783 100       11518 if ( my $s = $opts{'serializer'} ) {
74 85 100       502 $s->$_does('Dancer2::Core::Role::Serializer')
75             or croak 'Serializer provided not a Serializer object';
76              
77 84         6470 $self->{'serializer'} = $s;
78             }
79              
80             # additionally supported attributes
81 782         3041 $self->{'id'} = ++$_id;
82 782         2398 $self->{'vars'} = {};
83 782         2537 $self->{'is_behind_proxy'} = !!$opts{'is_behind_proxy'};
84 782         2118 $self->{'uri_for_route'} = $opts{'uri_for_route'};
85              
86             $opts{'body_params'}
87 782 100       2681 and $self->{'_body_params'} = $opts{'body_params'};
88 782         3357 $self->{'_strict_utf8'} = !!$opts{'strict_utf8'};
89              
90             # Deserialize/parse body for HMV
91 782         3587 $self->data;
92 779         7695 $self->_build_uploads();
93              
94 778         4319 return $self;
95             }
96              
97             # a buffer for per-request variables
98 100     100 1 352 sub vars { $_[0]->{'vars'} }
99              
100             sub var {
101 12     12 1 710 my $self = shift;
102             @_ == 2
103             ? $self->vars->{ $_[0] } = $_[1]
104 12 100       62 : $self->vars->{ $_[0] };
105             }
106              
107             # I don't like this. I know send_file uses this and I wonder
108             # if we can remove it.
109             # -- Sawyer
110 0     0 0 0 sub set_path_info { $_[0]->env->{'PATH_INFO'} = $_[1] }
111              
112             # XXX: incompatible with Plack::Request
113 32     32 1 237 sub body { $_[0]->raw_body }
114              
115 17     17 1 3365 sub id { $_id }
116              
117             # Private 'read-only' attributes for request params. See the params()
118             # method for the public interface.
119             #
120             # _body_params, _query_params and _route_params have setter methods that
121             # decode byte string to characters before setting; If you know you have
122             # decoded (character) params, such as output from a deserializer, you can
123             # set these directly in the request object hash to avoid the decode op.
124 297   66 297   1841 sub _params { $_[0]->{'_params'} ||= $_[0]->_build_params }
125              
126 615     615   2998 sub _has_params { defined $_[0]->{'_params'} }
127              
128 1405   66 1405   7930 sub _body_params { $_[0]->{'_body_params'} ||= $_[0]->body_parameters->as_hashref_mixed }
129              
130 79     79   263 sub _query_params { $_[0]->{'_query_params'} }
131              
132             sub _set_query_params {
133 31     31   208 my ( $self, $params ) = @_;
134 31         107 $self->{_query_params} = $self->_decode( $params, 'query parameters' );
135             }
136              
137 616   100 616   3813 sub _route_params { $_[0]->{'_route_params'} ||= {} }
138              
139             sub _set_route_params {
140 588     588   1639 my ( $self, $params ) = @_;
141 588         2047 $self->{_route_params} = $self->_decode( $params, 'route parameters' );
142 588         2507 $self->_build_params();
143             }
144              
145             # XXX: incompatible with Plack::Request
146 5     5 1 36 sub uploads { $_[0]->{'uploads'} }
147              
148 333 100   333 0 2047 sub is_behind_proxy { $_[0]->{'is_behind_proxy'} || 0 }
149              
150             sub host {
151 160     160 1 3169 my ($self) = @_;
152              
153 160 100 100     519 if ( $self->is_behind_proxy and exists $self->env->{'HTTP_X_FORWARDED_HOST'} ) {
154 3         30 my @hosts = split /\s*,\s*/, $self->env->{'HTTP_X_FORWARDED_HOST'}, 2;
155 3         37 return $hosts[0];
156             } else {
157 157         595 return $self->env->{'HTTP_HOST'};
158             }
159             }
160              
161             # aliases, kept for backward compat
162 8     8 1 2156 sub agent { shift->user_agent }
163 4     4 1 6236 sub remote_address { shift->address }
164 3     3 1 5453 sub forwarded_for_address { shift->env->{'HTTP_X_FORWARDED_FOR'} }
165 0     0 1 0 sub forwarded_host { shift->env->{'HTTP_X_FORWARDED_HOST'} }
166              
167             # there are two options
168             sub forwarded_protocol {
169             $_[0]->env->{'HTTP_X_FORWARDED_PROTO'} ||
170             $_[0]->env->{'HTTP_X_FORWARDED_PROTOCOL'} ||
171 14 100 100 14 1 65 $_[0]->env->{'HTTP_FORWARDED_PROTO'}
172             }
173              
174             sub scheme {
175 172     172 1 14921 my ($self) = @_;
176 172 100       503 my $scheme = $self->is_behind_proxy
177             ? $self->forwarded_protocol
178             : '';
179              
180 172   66     1046 return $scheme || $self->env->{'psgi.url_scheme'};
181             }
182              
183 839     839 1 8975 sub serializer { $_[0]->{'serializer'} }
184              
185 832   100 832 1 6060 sub data { $_[0]->{'data'} ||= $_[0]->deserialize() }
186              
187             sub deserialize {
188 782     782 0 1843 my $self = shift;
189              
190             # don't attempt to deserialize if the form is 'multipart/form-data'
191 782 100 100     3942 if (
192             $self->content_type
193             && $self->content_type =~ /^multipart\/form-data/i
194             ) {
195 7         207 return;
196             }
197              
198              
199 775 100       10539 my $serializer = $self->serializer
200             or return;
201              
202             # The latest draft of the RFC does not forbid DELETE to have content,
203             # rather the behaviour is undefined. Take the most lenient route and
204             # deserialize any content on delete as well.
205             return
206 83 100       256 unless grep { $self->method eq $_ } qw/ PUT POST PATCH DELETE /;
  332         1869  
207              
208             # try to deserialize
209 31         254 my $body = $self->body;
210              
211 31 100 66     12572 $body && length $body > 0
212             or return;
213              
214             # Catch serializer fails - which is tricky as Role::Serializer
215             # wraps the deserializaion in an eval and returns undef.
216             # We want to generate a 500 error on serialization fail (Ref #794)
217             # to achieve that, override the log callback so we can catch a signal
218             # that it failed. This is messy (messes with serializer internals), but
219             # "works".
220 30         87 my $serializer_fail;
221 30         222 my $serializer_log_cb = $serializer->log_cb;
222             local $serializer->{log_cb} = sub {
223 3     3   7 $serializer_fail = $_[1];
224 3         20 $serializer_log_cb->(@_);
225 30         227 };
226             # work-around to resolve a chicken-and-egg issue when instantiating a
227             # request object; the serializer needs that request object to deserialize
228             # the body params.
229 30         87 Scalar::Util::weaken( my $request = $self );
230 30 100       84 $self->serializer->has_request || $self->serializer->set_request($request);
231 30         1755 my $data = $serializer->deserialize($body);
232 30 100       199 die $serializer_fail if $serializer_fail;
233              
234             # Set _body_params directly rather than using the setter. Deserializiation
235             # returns characters and skipping the decode op in the setter ensures
236             # that numerical data "stays" numerical; decoding an SV that is an IV
237             # converts that to a PVIV. Some serializers are picky (JSON)..
238 27         90 $self->{_body_params} = $data;
239              
240             # Set body parameters (decoded HMV)
241 27 100       268 $self->{'body_parameters'} =
242             Hash::MultiValue->from_mixed( is_hashref($data) ? %$data : () );
243              
244 27         1710 return $data;
245             }
246              
247 3     3 1 12095 sub uri { $_[0]->request_uri }
248              
249 4     4 1 991 sub is_head { $_[0]->method eq 'HEAD' }
250 7     7 1 4310 sub is_post { $_[0]->method eq 'POST' }
251 7     7 1 5657 sub is_get { $_[0]->method eq 'GET' }
252 4     4 1 1055 sub is_put { $_[0]->method eq 'PUT' }
253 4     4 1 869 sub is_delete { $_[0]->method eq 'DELETE' }
254 4     4 0 886 sub is_patch { $_[0]->method eq 'PATCH' }
255 0     0 1 0 sub is_options { $_[0]->method eq 'OPTIONS' }
256              
257             # public interface compat with CGI.pm objects
258 3     3 1 3196 sub request_method { $_[0]->method }
259 0     0 1 0 sub input_handle { $_[0]->env->{'psgi.input'} }
260              
261             sub to_string {
262 7     7 1 9287 my ($self) = @_;
263 7         30 return "[#" . $self->id . "] " . $self->method . " " . $self->path;
264             }
265              
266             sub base {
267 37     37 1 14422 my $self = shift;
268 37         106 my $uri = $self->_common_uri;
269              
270 37         172 return $uri->canonical;
271             }
272              
273             sub _common_uri {
274 154     154   351 my $self = shift;
275              
276 154         747 my $path = $self->env->{SCRIPT_NAME};
277 154         964 my $port = $self->env->{SERVER_PORT};
278 154         800 my $server = $self->env->{SERVER_NAME};
279 154         1093 my $host = $self->host;
280 154         1038 my $scheme = $self->scheme;
281              
282 154         1621 my $uri = URI->new;
283 154         37751 $uri->scheme($scheme);
284 154   66     26439 $uri->authority( $host || "$server:$port" );
285 154   100     12411 $uri->path( $path || '/' );
286              
287 154         6914 return $uri;
288             }
289              
290             sub uri_base {
291 117     117 1 300 my $self = shift;
292 117         484 my $uri = $self->_common_uri;
293 117         587 my $canon = $uri->canonical;
294              
295 117 100       13815 if ( $uri->path eq '/' ) {
296 110         1952 $canon =~ s{/$}{};
297             }
298              
299 117         2251 return $canon;
300             }
301              
302             sub path {
303 1606     1606 1 33469 my $self = shift;
304 1606   100     5212 my $path = $self->env->{PATH_INFO} || '/';
305 1606         10379 return $self->_decode_bytes( $path, 'PATH_INFO' );
306             }
307              
308             sub dispatch_path {
309 0     0 1 0 Carp::croak q{DEPRECATED: request->dispatch_path. Please use request->path instead};
310             }
311              
312             sub uri_for {
313 28     28 1 17429 my ( $self, $part, $params, $dont_escape ) = @_;
314              
315 28   50     100 $part ||= '';
316 28         93 my $uri = $self->base;
317              
318             # Make sure there's exactly one slash between the base and the new part
319 28         2785 my $base = $uri->path;
320 28         291 $base =~ s|/$||;
321 28         97 $part =~ s|^/||;
322 28         104 $uri->path("$base/$part");
323              
324 28 100       872 $uri->query_form($params) if $params;
325              
326             return $dont_escape
327 3         12 ? uri_unescape( ${ $uri->canonical } )
328 28 100       1241 : ${ $uri->canonical };
  25         65  
329             }
330              
331             sub uri_for_route {
332 0     0 1 0 my ( $self, @args ) = @_;
333              
334 0 0       0 is_coderef( $self->{'uri_for_route'} )
335             or die 'uri_for_route called on a request instance without it';
336              
337 0         0 return $self->{'uri_for_route'}->(@_);
338             }
339              
340             sub params {
341 265     265 1 14669 my ( $self, $source ) = @_;
342              
343 265 100 66     1149 return %{ $self->_params } if wantarray && @_ == 1;
  31         135  
344 234 100       1141 return $self->_params if @_ == 1;
345              
346 16 100       82 if ( $source eq 'query' ) {
    100          
347 5 0       18 return %{ $self->_query_params || {} } if wantarray;
  0 50       0  
348 5         18 return $self->_query_params;
349             }
350             elsif ( $source eq 'body' ) {
351 10 0       32 return %{ $self->_body_params || {} } if wantarray;
  0 50       0  
352 10         39 return $self->_body_params;
353             }
354 1 50       6 if ( $source eq 'route' ) {
355 1 50       4 return %{ $self->_route_params } if wantarray;
  0         0  
356 1         5 return $self->_route_params;
357             }
358             else {
359 0         0 croak "Unknown source params \"$source\".";
360             }
361             }
362              
363             sub query_parameters {
364 59     59 1 101 my $self = shift;
365 59   66     332 $self->{'query_parameters'} ||= do {
366 47 100       211 if ($XS_PARSE_QUERY_STRING) {
367             my $query = $self->_decode(CGI::Deurl::XS::parse_query_string(
368 43         146 $self->env->{'QUERY_STRING'}
369             ), 'query parameters');
370              
371             Hash::MultiValue->new(
372             map {;
373 10         17 my $key = $_;
374             is_arrayref( $query->{$key} )
375 2         20 ? ( map +( $key => $_ ), @{ $query->{$key} } )
376 10 100       51 : ( $key => $query->{$key} )
377 43         97 } keys %{$query}
  43         235  
378             );
379             } else {
380             # defer to Plack::Request
381 4         31 $self->_decode($self->SUPER::query_parameters, 'query parameters');
382             }
383             };
384             }
385              
386             # this will be filled once the route is matched
387 17   33 17 0 95 sub route_parameters { $_[0]->{'route_parameters'} ||= Hash::MultiValue->new }
388              
389             sub _set_route_parameters {
390 588     588   1501 my ( $self, $params ) = @_;
391             # remove reserved splat parameter name
392             # you should access splat parameters using splat() keyword
393 588         1323 delete @{$params}{qw};
  588         1587  
394             $self->{'route_parameters'} = Hash::MultiValue->from_mixed(
395 588         1145 %{ $self->_decode( $params, 'route parameters' ) }
  588         2286  
396             );
397             }
398              
399             sub body_parameters {
400 760     760 1 1585 my $self = shift;
401             # defer to (the overridden) Plack::Request->body_parameters
402 760   66     5540 $self->{'body_parameters'} ||= $self->_decode(
403             $self->SUPER::body_parameters(),
404             'body parameters',
405             );
406             }
407              
408             sub parameters {
409 3     3 1 4 my ( $self, $type ) = @_;
410              
411             # handle a specific case
412 3 50       8 if ($type) {
413 0         0 my $attr = "${type}_parameters";
414 0         0 return $self->$attr;
415             }
416              
417             # merge together the *decoded* parameters
418 3   33     8 $self->{'merged_parameters'} ||= do {
419 3         6 my $query = $self->query_parameters;
420 3         7 my $body = $self->body_parameters;
421 3         19 my $route = $self->route_parameters; # not in Plack::Request
422 3         11 Hash::MultiValue->new( map $_->flatten, $query, $body, $route );
423             };
424             }
425              
426 2 100   2 0 10 sub captures { shift->params->{captures} || {} }
427              
428 31 100   31 0 74 sub splat { @{ shift->params->{splat} || [] } }
  31         166  
429              
430             # XXX: incompatible with Plack::Request
431 10     10 1 1262 sub param { shift->params->{ $_[0] } }
432              
433             sub _decode {
434 24323     24323   306169 my ( $self, $h, $context ) = @_;
435 24323 100       41100 return if not defined $h;
436              
437 24280 100 100     71404 if ( !is_ref($h) && !utf8::is_utf8($h) ) {
    100          
    100          
    100          
438 21570         34952 return $self->_decode_bytes( $h, $context );
439             }
440             elsif ( ref($h) eq 'Hash::MultiValue' ) {
441 702         3532 return Hash::MultiValue->from_mixed(
442             $self->_decode( $h->as_hashref_mixed, $context )
443             );
444             }
445             elsif ( is_hashref($h) ) {
446 1918         3478 return { map scalar $self->_decode( $_, $context ), %{$h} };
  1918         12577  
447             }
448             elsif ( is_arrayref($h) ) {
449 80         173 return [ map $self->_decode( $_, $context ), @{$h} ];
  80         303  
450             }
451              
452 10         42 return $h;
453             }
454              
455             sub _decode_bytes {
456 23176     23176   38081 my ( $self, $bytes, $context ) = @_;
457              
458             # If PSGI already gave us characters, avoid re-decoding.
459 23176 50       43882 return $bytes if utf8::is_utf8($bytes);
460 23176 100       112178 return $bytes if $bytes !~ /[\x80-\xFF]/;
461              
462 29 100       109 return __decode($bytes) if __valid($bytes);
463 2         9 return $self->_invalid_utf8( $bytes, $context );
464             }
465              
466             sub _invalid_utf8 {
467 2     2   7 my ( $self, $bytes, $context ) = @_;
468 2         5 my $strict = $self->{_strict_utf8};
469 2   50     6 my $where = $context || 'input';
470 2         83 my $msg = "Invalid UTF-8 in $where";
471              
472 2 100       176 $strict
473             and Carp::croak($msg);
474              
475 1 50       4 if ( my $logger = $self->env->{'psgix.logger'} ) {
476 0         0 $logger->({
477             level => 'warning',
478             message => "$msg; leaving bytes unchanged",
479             });
480             } else {
481 1         318 Carp::carp("$msg; leaving bytes unchanged");
482             }
483              
484 1         9 return $bytes;
485             }
486              
487             sub is_ajax {
488 0     0 1 0 my $self = shift;
489              
490 0 0       0 return 0 unless defined $self->headers;
491 0 0       0 return 0 unless defined $self->header('X-Requested-With');
492 0 0       0 return 0 if $self->header('X-Requested-With') ne 'XMLHttpRequest';
493 0         0 return 1;
494             }
495              
496             # XXX incompatible with Plack::Request
497             # context-aware accessor for uploads
498             sub upload {
499 21     21 1 6108 my ( $self, $name ) = @_;
500 21         86 my $res = $self->{uploads}{$name};
501              
502 21 100       86 return $res unless wantarray;
503 9 100       30 return () unless defined $res;
504 6 100       34 return ( is_arrayref($res) ) ? @$res : $res;
505             }
506              
507             sub _build_params {
508 615     615   1560 my ($self) = @_;
509              
510             # params may have been populated by before filters
511             # _before_ we get there, so we have to save it first
512 615 100       2260 my $previous = $self->_has_params ? $self->_params : {};
513              
514             # now parse environment params...
515 615         2472 my $get_params = $self->_parse_get_params();
516              
517             # and merge everything
518             $self->{_params} = {
519 615 100       2478 map +( is_hashref($_) ? %{$_} : () ),
  1917         7126  
520             $previous,
521             $get_params,
522             $self->_body_params,
523             $self->_route_params,
524             };
525              
526             }
527              
528             sub _url_decode {
529 64     64   125 my ( $self, $encoded ) = @_;
530 64 100       191 return URL::Encode::XS::url_decode($encoded) if $XS_URL_DECODE;
531 32         44 my $clean = $encoded;
532 32         60 $clean =~ tr/\+/ /;
533 32         75 $clean =~ s/%([a-fA-F0-9]{2})/pack "H2", $1/eg;
  7         33  
534 32         66 return $clean;
535             }
536              
537             sub _parse_get_params {
538 615     615   1435 my ($self) = @_;
539 615 100       2514 return $self->_query_params if defined $self->{_query_params};
540              
541 572         1316 my $query_params = {};
542              
543 572         2341 my $source = $self->env->{QUERY_STRING};
544 572 100 100     6249 return if !defined $source || $source eq '';
545              
546 31 100       120 if ($XS_PARSE_QUERY_STRING) {
547 21   50     283 $self->_set_query_params(
548             CGI::Deurl::XS::parse_query_string($source) || {}
549             );
550 21         138 return $self->_query_params;
551             }
552              
553 10         92 foreach my $token ( split /[&;]/, $source ) {
554 32         113 my ( $key, $val ) = split( /=/, $token );
555 32 50       103 next unless defined $key;
556 32 50       103 $val = ( defined $val ) ? $val : '';
557 32         86 $key = $self->_url_decode($key);
558 32         73 $val = $self->_url_decode($val);
559              
560             # looking for multi-value params
561 32 100       103 if ( exists $query_params->{$key} ) {
562 6         13 my $prev_val = $query_params->{$key};
563 6 100       24 if ( is_arrayref($prev_val) ) {
564 2         7 push @{ $query_params->{$key} }, $val;
  2         7  
565             }
566             else {
567 4         19 $query_params->{$key} = [ $prev_val, $val ];
568             }
569             }
570              
571             # simple value param (first time we see it)
572             else {
573 26         121 $query_params->{$key} = $val;
574             }
575             }
576 10         54 $self->_set_query_params( $query_params );
577 10         44 return $self->_query_params;
578             }
579              
580             sub _build_uploads {
581 779     779   2018 my ($self) = @_;
582              
583             # parse body and build body params
584 779         2968 my $body_params = $self->_body_params;
585              
586 778         43365 my $uploads = $self->SUPER::uploads;
587 778         9705 my %uploads;
588              
589 778         2634 for my $name ( keys %$uploads ) {
590             my @uploads = map Dancer2::Core::Request::Upload->new(
591             # For back-compatibility, we use a HashRef of headers
592 21         5190 headers => {@{$_->{headers}->psgi_flatten_without_sort}},
593             tempname => $_->{tempname},
594             size => $_->{size},
595 15         64 filename => $self->_decode( $_->{filename}, 'upload filename' ),
596             ), $uploads->get_all($name);
597              
598 15 100       8895 $uploads{$name} = @uploads > 1 ? \@uploads : $uploads[0];
599              
600             # support access to the filename as a normal param
601 15         62 my @filenames = map $_->{'filename'}, @uploads;
602 15 100       77 $self->{_body_params}{$name} =
603             @filenames > 1 ? \@filenames : $filenames[0];
604             }
605              
606 778         3586 $self->{uploads} = \%uploads;
607             }
608              
609             # XXX: incompatible with Plack::Request
610 614   66 614 1 4598 sub cookies { $_[0]->{'cookies'} ||= $_[0]->_build_cookies }
611              
612             sub _build_cookies {
613 521     521   1098 my $self = shift;
614 521         1241 my $cookies = {};
615              
616 521         3334 my $http_cookie = $self->header('Cookie');
617 521 100       100611 return $cookies unless defined $http_cookie; # nothing to do
618              
619 64 100       236 if ( $XS_HTTP_COOKIES ) {
620 63         533 $cookies = HTTP::XSCookies::crush_cookie($http_cookie);
621             }
622             else {
623             # handle via Plack::Request
624 1         16 $cookies = $self->SUPER::cookies();
625             }
626              
627             # convert to objects
628 64         234 while (my ($name, $value) = each %{$cookies}) {
  131         2397  
629 67 100       2310 $cookies->{$name} = Dancer2::Core::Cookie->new(
630             name => $name,
631             # HTTP::XSCookies v0.17+ will do the split and return an arrayref
632             value => is_arrayref($value) ? $value : [split '&', $value ]
633             );
634             }
635 64         642 return $cookies;
636             }
637              
638             # poor man's clone
639             sub _shallow_clone {
640 53     53   150 my ($self, $params, $options) = @_;
641              
642             # shallow clone $env; we don't want to alter the existing one
643             # in $self, then merge any overridden values
644 53 50       102 my $env = { %{ $self->env }, %{ $options || {} } };
  53         278  
  53         1349  
645              
646 53         499 my $new_request = __PACKAGE__->new(
647             env => $env,
648             body_params => {},
649             );
650              
651             # Clone and merge query params
652 53         255 my $new_params = $self->params;
653 53 100       121 $new_request->{_query_params} = { %{ $self->{_query_params} || {} } };
  53         323  
654 53         164 $new_request->{_strict_utf8} = $self->{_strict_utf8};
655 53         194 $new_request->{query_parameters} = $self->query_parameters->clone;
656 53 100       4555 for my $key ( keys %{ $params || {} } ) {
  53         379  
657 9         18 my $value = $params->{$key};
658 9         20 $new_params->{$key} = $value;
659 9         18 $new_request->{_query_params}->{$key} = $value;
660 9         31 $new_request->{query_parameters}->add( $key => $value );
661             }
662              
663             # Copy params (these are already decoded)
664 53         398 $new_request->{_params} = $new_params;
665 53         140 $new_request->{_body_params} = $self->{_body_params};
666 53         154 $new_request->{_route_params} = $self->{_route_params};
667 53         300 $new_request->{headers} = $self->headers;
668              
669             # Copy remaining settings
670 53         5873 $new_request->{is_behind_proxy} = $self->{is_behind_proxy};
671 53         145 $new_request->{vars} = $self->{vars};
672              
673             # Clone any existing decoded & cached body params. (GH#1116 GH#1269)
674 53         171 $new_request->{'body_parameters'} = $self->body_parameters->clone;
675              
676             # Delete merged HMV parameters, allowing them to be reconstructed on first use.
677 53         2379 delete $new_request->{'merged_parameters'};
678              
679 53         181 return $new_request;
680             }
681              
682              
683             sub _set_route {
684 588     588   1580 my ( $self, $route ) = @_;
685 588         1766 $self->{'route'} = $route;
686             }
687              
688 6     6 1 61 sub route { $_[0]->{'route'} }
689              
690             sub body_data {
691 2     2 1 6 my $self = shift;
692 2 100       9 return $self->data if $self->serializer;
693 1         8 $self->_body_params;
694 1 50       2 return $self->{_body_params} if keys %{ $self->{_body_params} };
  1         7  
695 1         8 return $self->body;
696             }
697              
698             1;
699              
700             __END__