File Coverage

blib/lib/Kossy/Request.pm
Criterion Covered Total %
statement 118 143 82.5
branch 26 40 65.0
condition 13 26 50.0
subroutine 27 32 84.3
pod 8 16 50.0
total 192 257 74.7


line stmt bran cond sub pod time code
1             package Kossy::Request;
2              
3 16     16   183598 use strict;
  16         31  
  16         613  
4 16     16   109 use warnings;
  16         36  
  16         929  
5 16     16   135 use parent qw/Plack::Request/;
  16         38  
  16         164  
6 16     16   1105960 use Hash::MultiValue;
  16         44  
  16         499  
7 16     16   88 use Encode;
  16         40  
  16         1584  
8 16     16   104 use HTTP::Headers::Fast;
  16         36  
  16         390  
9 16     16   10920 use Kossy::Validator;
  16         40133  
  16         623  
10 16     16   138 use HTTP::Entity::Parser;
  16         31  
  16         628  
11 16     16   77 use WWW::Form::UrlEncoded qw/parse_urlencoded_arrayref build_urlencoded_utf8/;
  16         32  
  16         1059  
12 16     16   86 use Cookie::Baker;
  16         28  
  16         30340  
13              
14             our $VERSION = '0.63';
15              
16             sub new {
17 85     85 1 2604558 my($class, $env, %opts) = @_;
18 85 50 33     901 Carp::croak(q{$env is required})
19             unless defined $env && ref($env) eq 'HASH';
20              
21 85         790 bless {
22             %opts,
23             env => $env,
24             }, $class;
25             }
26              
27             sub new_response {
28 44     44 1 131699 my $self = shift;
29 44         1264 require Kossy::Response;
30 44         556 Kossy::Response->new(@_);
31             }
32              
33             sub cookies {
34 7     7 1 4029 my $self = shift;
35              
36 7 100       40 return {} unless $self->env->{HTTP_COOKIE};
37              
38             # HTTP_COOKIE hasn't changed: reuse the parsed cookie
39 6 100 66     48 if ( $self->env->{'plack.cookie.parsed'}
40             && $self->env->{'plack.cookie.string'} eq $self->env->{HTTP_COOKIE}) {
41 4         50 return $self->env->{'plack.cookie.parsed'};
42             }
43              
44 2         47 $self->env->{'plack.cookie.string'} = $self->env->{HTTP_COOKIE};
45 2         14 $self->env->{'plack.cookie.parsed'} = crush_cookie($self->env->{'plack.cookie.string'});
46             }
47              
48              
49             sub request_body_parser {
50 34     34 0 76 my $self = shift;
51 34 50       155 unless (exists $self->{request_body_parser}) {
52 34         131 $self->{request_body_parser} = $self->_build_request_body_parser();
53             }
54 34         128 return $self->{request_body_parser};
55             }
56              
57             my $default_parser = HTTP::Entity::Parser->new();
58             $default_parser->register(
59             'application/x-www-form-urlencoded',
60             'HTTP::Entity::Parser::UrlEncoded'
61             );
62             $default_parser->register(
63             'multipart/form-data',
64             'HTTP::Entity::Parser::MultiPart'
65             );
66              
67             my $json_parser = HTTP::Entity::Parser->new();
68             $json_parser->register(
69             'application/x-www-form-urlencoded',
70             'HTTP::Entity::Parser::UrlEncoded'
71             );
72             $json_parser->register(
73             'multipart/form-data',
74             'HTTP::Entity::Parser::MultiPart'
75             );
76             $json_parser->register(
77             'application/json',
78             'HTTP::Entity::Parser::JSON'
79             );
80              
81             my $json_only_parser = HTTP::Entity::Parser->new();
82             $json_only_parser->register(
83             'application/json',
84             'HTTP::Entity::Parser::JSON'
85             );
86              
87             sub _build_request_body_parser {
88 34     34   80 my $self = shift;
89 34 100       106 if ( $self->env->{'kossy.request.parse_json_body'} ) {
90 26         208 return $json_parser;
91             }
92 8         84 $default_parser;
93             }
94              
95             sub _parse_request_body {
96 40     40   107 my $self = shift;
97 40 100       143 if ( !$self->env->{CONTENT_TYPE} ) {
98 6         38 $self->env->{'kossy.request.body_parameters'} = [];
99 6         69 $self->env->{'plack.request.upload'} = Hash::MultiValue->new();
100 6         284 return;
101             }
102              
103 34         303 my ($params,$uploads) = $self->request_body_parser->parse($self->env);
104 34         19460 $self->env->{'kossy.request.body_parameters'} = $params;
105              
106 34         362 my $upload_hmv = Hash::MultiValue->new();
107 34         1679 while ( my ($k,$v) = splice @$uploads, 0, 2 ) {
108 1         8 my %copy = %$v;
109 1         3 $copy{headers} = HTTP::Headers::Fast->new(@{$v->{headers}});
  1         11  
110 1         143 $upload_hmv->add($k, Plack::Request::Upload->new(%copy));
111             }
112 34         183 $self->env->{'plack.request.upload'} = $upload_hmv;
113             }
114              
115             sub uploads {
116 4     4 1 4641 my $self = shift;
117 4 50       16 unless ($self->env->{'plack.request.upload'}) {
118 0         0 $self->_parse_request_body;
119             }
120 4         28 $self->env->{'plack.request.upload'};
121             }
122              
123             sub body_parameters {
124 67     67 1 50623 my ($self) = @_;
125 67   66     279 $self->env->{'kossy.request.body'} ||= do {
126 40         419 Hash::MultiValue->new(map { _decode_recursively($_) } @{$self->_body_parameters()});
  90         709  
  40         151  
127             }
128             }
129              
130             sub json_parameters {
131 11     11 0 19389 my ($self) = @_;
132 11   66     67 $self->env->{'kossy.request.json_body'} ||= do {
133 10         118 +{ map { _decode_recursively($_) } @{$self->_json_parameters()} }
  28         220  
  10         47  
134             }
135             }
136              
137             sub _decode_recursively {
138 153     153   339 my $v = shift;
139 153 100       411 if (my $r = ref $v) {
140 25 100       100 if ($r eq 'ARRAY') {
    50          
141 15         68 return [ map { _decode_recursively($_) } @$v ];
  21         95  
142             }
143             elsif ($r eq 'HASH') {
144 10         51 return { map { Encode::decode_utf8($_) => _decode_recursively($v->{$_}) } keys %$v };
  14         107  
145             }
146             else {
147 0         0 die 'Cannot decode ' . $v;
148             }
149             }
150             else {
151 128         1099 return Encode::decode_utf8($v);
152             }
153             }
154              
155             sub query_parameters {
156 61     61 1 1360 my ($self) = @_;
157             $self->env->{'kossy.request.query'} ||=
158 61   66     377 Hash::MultiValue->new(map { Encode::decode_utf8($_) } @{$self->_query_parameters()});
  84         1376  
  35         344  
159             }
160              
161             sub parameters {
162 42     42 1 49516 my $self = shift;
163 42   66     193 $self->env->{'kossy.request.merged'} ||= do {
164 34         306 Hash::MultiValue->new(
165             $self->query_parameters->flatten,
166             $self->body_parameters->flatten,
167             );
168             };
169             }
170              
171             sub _body_parameters {
172 40     40   84 my $self = shift;
173 40 100       133 unless ($self->env->{'kossy.request.body_parameters'}) {
174 39         310 $self->_parse_request_body;
175             }
176 40         264 return $self->env->{'kossy.request.body_parameters'};
177             }
178              
179             sub _json_parameters {
180 10     10   29 my $self = shift;
181 10 50       40 unless ($self->env->{'kossy.request.json_parameters'}) {
182 10         69 my ($params) = $json_only_parser->parse($self->env);
183 10         3147 $self->env->{'kossy.request.json_parameters'} = $params;
184             }
185 10         77 return $self->env->{'kossy.request.json_parameters'};
186             }
187              
188             sub _query_parameters {
189 35     35   78 my $self = shift;
190 35 50       95 unless ( $self->env->{'kossy.request.query_parameters'} ) {
191             $self->env->{'kossy.request.query_parameters'} =
192 35         331 parse_urlencoded_arrayref($self->env->{'QUERY_STRING'});
193             }
194 35         455 return $self->env->{'kossy.request.query_parameters'};
195             }
196              
197             sub body_parameters_raw {
198 0     0 0 0 my $self = shift;
199 0 0       0 unless ($self->env->{'plack.request.body'}) {
200 0         0 $self->env->{'plack.request.body'} = Hash::MultiValue->new(@{$self->_body_parameters});
  0         0  
201             }
202 0         0 return $self->env->{'plack.request.body'};
203             }
204              
205             sub query_parameters_raw {
206 0     0 0 0 my $self = shift;
207 0 0       0 unless ($self->env->{'plack.request.query'}) {
208 0         0 $self->env->{'plack.request.query'} = Hash::MultiValue->new(@{$self->_query_parameters});
  0         0  
209             }
210 0         0 return $self->env->{'plack.request.query'};
211             }
212              
213             sub parameters_raw {
214 0     0 0 0 my $self = shift;
215 0   0     0 $self->env->{'plack.request.merged'} ||= do {
216             Hash::MultiValue->new(
217 0         0 @{$self->_query_parameters},
218 0         0 @{$self->_body_parameters}
  0         0  
219             );
220             };
221             }
222              
223             sub param_raw {
224 0     0 0 0 my $self = shift;
225              
226 0 0       0 return keys %{ $self->parameters_raw } if @_ == 0;
  0         0  
227              
228 0         0 my $key = shift;
229 0 0       0 return $self->parameters_raw->{$key} unless wantarray;
230 0         0 return $self->parameters_raw->get_all($key);
231             }
232              
233             sub base {
234 4     4 1 4 my $self = shift;
235 4   50     23 $self->{_base} ||= {};
236 4         12 my $base = $self->_uri_base;
237 4   33     75 $self->{_base}->{$base} ||= $self->SUPER::base;
238 4         584 $self->{_base}->{$base}->clone;
239             }
240              
241             sub uri_for {
242 4     4 0 43 my($self, $path, $args) = @_;
243 4         10 my $uri = $self->base;
244 4 100       26 my $base = $uri->path eq "/"
245             ? ""
246             : $uri->path;
247 4         56 my $query = '';
248 4 100       8 if ( $args ) {
249 2         21 $query = build_urlencoded_utf8($args);
250             }
251 4 100       17 $uri->path_query( $base . $path . (length $query ? "?$query" : ""));
252 4         170 $uri;
253             }
254              
255             sub validator {
256 0     0 0   my ($self, $rule) = @_;
257 0           Kossy::Validator->check($self,$rule);
258             }
259              
260             1;