line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Kelp::Request; |
2
|
|
|
|
|
|
|
|
3
|
21
|
|
|
21
|
|
645
|
use Kelp::Base 'Plack::Request'; |
|
21
|
|
|
|
|
44
|
|
|
21
|
|
|
|
|
195
|
|
4
|
|
|
|
|
|
|
|
5
|
21
|
|
|
21
|
|
2040
|
use Encode; |
|
21
|
|
|
|
|
102
|
|
|
21
|
|
|
|
|
1650
|
|
6
|
21
|
|
|
21
|
|
135
|
use Carp; |
|
21
|
|
|
|
|
89
|
|
|
21
|
|
|
|
|
1121
|
|
7
|
21
|
|
|
21
|
|
130
|
use Try::Tiny; |
|
21
|
|
|
|
|
47
|
|
|
21
|
|
|
|
|
19130
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
attr -app => sub { croak "app is required" }; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# The stash is used to pass values from one route to another |
12
|
|
|
|
|
|
|
attr stash => sub { {} }; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# The named hash contains the values of the named placeholders |
15
|
|
|
|
|
|
|
attr named => sub { {} }; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# The name of the matched route for this request |
18
|
|
|
|
|
|
|
attr route_name => sub { undef }; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# If you're running the web app as a proxy, use Plack::Middleware::ReverseProxy |
21
|
1
|
|
|
1
|
1
|
4
|
sub address { $_[0]->env->{REMOTE_ADDR} } |
22
|
0
|
|
|
0
|
1
|
0
|
sub remote_host { $_[0]->env->{REMOTE_HOST} } |
23
|
0
|
|
|
0
|
1
|
0
|
sub user { $_[0]->env->{REMOTE_USER} } |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub new { |
26
|
202
|
|
|
202
|
1
|
742
|
my ( $class, %args ) = @_; |
27
|
202
|
|
|
|
|
966
|
my $self = $class->SUPER::new( delete $args{env} ); |
28
|
202
|
|
|
|
|
2371
|
$self->{$_} = $args{$_} for keys %args; |
29
|
202
|
|
|
|
|
928
|
return $self; |
30
|
|
|
|
|
|
|
} |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub is_ajax { |
33
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
34
|
1
|
50
|
|
|
|
9
|
return unless my $with = $self->headers->header('X-Requested-With'); |
35
|
1
|
|
|
|
|
235
|
return $with =~ /XMLHttpRequest/i; |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub is_json { |
39
|
76
|
|
|
76
|
1
|
114
|
my $self = shift; |
40
|
76
|
100
|
|
|
|
243
|
return unless $self->content_type; |
41
|
43
|
|
|
|
|
304
|
return lc($self->content_type) =~ qr[^application/json]i; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub param { |
46
|
72
|
|
|
72
|
1
|
172
|
my $self = shift; |
47
|
72
|
|
100
|
|
|
179
|
my $safe_param = $self->app->config('safe_param') // 0; |
48
|
72
|
|
|
|
|
160
|
my $warn_message = |
49
|
|
|
|
|
|
|
'Using "param" with argument in list context is deprecated ' . |
50
|
|
|
|
|
|
|
'in Kelp version 1.04. See documentation of for details' |
51
|
|
|
|
|
|
|
; |
52
|
|
|
|
|
|
|
|
53
|
72
|
100
|
66
|
|
|
162
|
if ( $self->is_json && $self->app->can('json') ) { |
54
|
|
|
|
|
|
|
my $hash = try { |
55
|
21
|
|
|
21
|
|
833
|
$self->app->json->decode( $self->content ); |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
catch { |
58
|
1
|
|
|
1
|
|
241
|
{}; |
59
|
21
|
|
|
|
|
138
|
}; |
60
|
21
|
100
|
|
|
|
5103
|
$hash = { ref($hash), $hash } unless ref($hash) eq 'HASH'; |
61
|
|
|
|
|
|
|
|
62
|
21
|
100
|
|
|
|
138
|
return $hash->{ $_[0] } if @_; |
63
|
7
|
100
|
|
|
|
26
|
return $hash if !wantarray; |
64
|
4
|
|
|
|
|
33
|
return keys %$hash; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# unsafe method - Plack::Request::param |
68
|
51
|
100
|
100
|
|
|
656
|
if (@_ && wantarray && !$safe_param) { |
|
|
|
100
|
|
|
|
|
69
|
14
|
|
|
|
|
2932
|
carp $warn_message; |
70
|
14
|
|
|
|
|
610
|
return $self->SUPER::param(@_); |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# safe method without calling PLack::Request::param |
74
|
37
|
100
|
|
|
|
140
|
return $self->parameters->get($_[0]) if @_; |
75
|
9
|
|
|
|
|
14
|
return keys %{ $self->parameters }; |
|
9
|
|
|
|
|
41
|
|
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
sub cgi_param { |
79
|
0
|
|
|
0
|
1
|
0
|
shift->SUPER::param(@_); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub session { |
83
|
10
|
|
|
10
|
1
|
32
|
my $self = shift; |
84
|
10
|
|
50
|
|
|
22
|
my $session = $self->env->{'psgix.session'} |
85
|
|
|
|
|
|
|
// die "No Session middleware wrapped"; |
86
|
|
|
|
|
|
|
|
87
|
10
|
100
|
|
|
|
55
|
return $session if !@_; |
88
|
|
|
|
|
|
|
|
89
|
8
|
100
|
|
|
|
17
|
if ( @_ == 1 ) { |
90
|
5
|
|
|
|
|
9
|
my $value = shift; |
91
|
5
|
100
|
|
|
|
27
|
return $session->{$value} unless ref $value; |
92
|
1
|
|
|
|
|
4
|
return $self->env->{'psgix.session'} = $value; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
|
95
|
3
|
|
|
|
|
10
|
my %hash = @_; |
96
|
3
|
|
|
|
|
12
|
$session->{$_} = $hash{$_} for keys %hash; |
97
|
3
|
|
|
|
|
16
|
return $session; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
1; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
__END__ |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=pod |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head1 NAME |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Kelp::Request - Request class for a Kelp application |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=head1 SYNOPSIS |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
my $request = Kelp::Request( app => $app, env => $env ); |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head1 DESCRIPTION |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
This module provides a convenience layer on top of L<Plack::Request>. It extends |
117
|
|
|
|
|
|
|
it to add several convenience methods. |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head1 ATTRIBUTES |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=head2 app |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
A reference to the Kelp application. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=head2 stash |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Returns a hashref, which represents the stash of the current the request |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
An all use, utility hash to use to pass information between routes. The stash |
130
|
|
|
|
|
|
|
is a concept originally conceived by the developers of L<Catalyst>. It's a hash |
131
|
|
|
|
|
|
|
that you can use to pass data from one route to another. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# put value into stash |
134
|
|
|
|
|
|
|
$self->req->stash->{username} = app->authenticate(); |
135
|
|
|
|
|
|
|
# more convenient way |
136
|
|
|
|
|
|
|
$self->stash->{username} = app->authenticate(); |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# get value from stash |
139
|
|
|
|
|
|
|
return "Hello " . $self->req->stash->{username}; |
140
|
|
|
|
|
|
|
# more convenient way |
141
|
|
|
|
|
|
|
return "Hello " . $self->stash('username'); |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=head2 named |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
This hash is initialized with the named placeholders of the path that the |
146
|
|
|
|
|
|
|
current route is processing. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=head2 route_name |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Contains a string name of the route matched for this request. Contains route pattern |
151
|
|
|
|
|
|
|
if the route was not named. |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=head2 param |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
I<B<Change of behavior> in version 1.04, see below for details> |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
Returns the HTTP parameters of the request. This method delegates all the work |
158
|
|
|
|
|
|
|
to L<Plack::Request/param>, except when the content type of the request is |
159
|
|
|
|
|
|
|
C<application/json> and a JSON module is loaded. In that case, it will decode |
160
|
|
|
|
|
|
|
the JSON body and return as follows: |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=over |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=item |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
If no arguments are passed, then it will return the names of the HTTP parameters |
167
|
|
|
|
|
|
|
when called in array contest, and a reference to the entire JSON hash when |
168
|
|
|
|
|
|
|
called in scalar context. |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# JSON body = { bar => 1, foo => 2 } |
171
|
|
|
|
|
|
|
my @names = $self->param; # @names = ('bar', 'foo') |
172
|
|
|
|
|
|
|
my $json = $self->param; # $json = { bar => 1, foo => 2 } |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=item |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
If a single argument is passed, then the corresponding value in the JSON |
178
|
|
|
|
|
|
|
document is returned. |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
my $bar = $self->param('bar'); # $bar = 1 |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=item |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
If the root contents of the JSON document is not an C<HASH> (after decoding), then it will be wrapped into a hash with its reftype as a key, for example: |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
{ ARRAY => [...] } # when JSON contains an array as root element |
187
|
|
|
|
|
|
|
{ '' => [...] } # when JSON contains something that's not a reference |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
my $array = $kelp->param('ARRAY'); |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=back |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
Since version I<1.04>, a new application configuration field C<safe_param> is |
194
|
|
|
|
|
|
|
introduced that B<changes the behavior> of this method: |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=over |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
=item |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
Without C<safe_param>, method will produce a warning if used in list context |
201
|
|
|
|
|
|
|
while passing the first argument, but will continue to work the same. This is |
202
|
|
|
|
|
|
|
done to combat a very nasty and easy to make bug: |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
$kelp->some_function( |
205
|
|
|
|
|
|
|
param1 => $value, |
206
|
|
|
|
|
|
|
param2 => $kelp->param('key'), # BUG, list context |
207
|
|
|
|
|
|
|
); |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
Since HTTP requests can accept multiple values for the same key, someone could |
210
|
|
|
|
|
|
|
inject additional parameters to the function with the simple query, due to |
211
|
|
|
|
|
|
|
array flattening: |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
?key=something&key=additional_hash_key&key=additional_hash_value |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=item |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
With C<safe_param>, a call to C<param> with an argument (a key to fetch from |
218
|
|
|
|
|
|
|
the parameters) will no longer return a list but always a scalar value |
219
|
|
|
|
|
|
|
regardless of context, even if there are more than one entries of that name |
220
|
|
|
|
|
|
|
(will then return the last one). This makes usages like the one above perfectly |
221
|
|
|
|
|
|
|
safe. |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
my @array = $kelp->param('name'); # changed, will never return more than one scalar |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=item |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
Since this method has so many ways to use it, you're still B<encouraged> to use |
228
|
|
|
|
|
|
|
other, more specific methods from L<Plack::Request>. |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=back |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
You are B<strongly advised> to introduce C<safe_param> into your configuration as |
233
|
|
|
|
|
|
|
quickly as possible. Currently, a value of C<0> is the default, meaning that |
234
|
|
|
|
|
|
|
param will work the same as it did, but produce warnings. In no less than half |
235
|
|
|
|
|
|
|
a year from version 1.04 the old behavior of C<param> will be removed |
236
|
|
|
|
|
|
|
altogether, and C<safe_param> configuration will no longer cause any change in |
237
|
|
|
|
|
|
|
behavior, allowing for its safe removal. Use L</cgi_param> if you'd like to |
238
|
|
|
|
|
|
|
retain the old behavior regardless of security risks. |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=head2 cgi_param |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
Calls C<param> in L<Plack::Request>, which is CGI.pm compatible. It is B<not |
243
|
|
|
|
|
|
|
recommended> to use this method, unless for some reason you have to maintain |
244
|
|
|
|
|
|
|
CGI.pm compatibility. Misusing this method can lead to bugs and security |
245
|
|
|
|
|
|
|
vulnerabilities. |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=head2 address, remote_host, user |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
These are shortcuts to the REMOTE_ADDR, REMOTE_HOST and REMOTE_USER environment |
250
|
|
|
|
|
|
|
variables. |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
if ( $self->req->address eq '127.0.0.1' ) { |
253
|
|
|
|
|
|
|
... |
254
|
|
|
|
|
|
|
} |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Note: See L<Kelp::Cookbook/Deploying> for configuration required for these |
257
|
|
|
|
|
|
|
fields when using a proxy. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=head2 session |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
Returns the Plack session hash or dies if no C<Session> middleware was included. |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
sub get_session_value { |
264
|
|
|
|
|
|
|
my $self = shift; |
265
|
|
|
|
|
|
|
$self->session->{user} = 45; |
266
|
|
|
|
|
|
|
} |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
If called with a single argument, returns that value from the session hash: |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub set_session_value { |
271
|
|
|
|
|
|
|
my $self = shift; |
272
|
|
|
|
|
|
|
my $user = $self->req->session('user'); |
273
|
|
|
|
|
|
|
# Same as $self->req->session->{'user'}; |
274
|
|
|
|
|
|
|
} |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
Set values in the session using key-value pairs: |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
sub set_session_hash { |
279
|
|
|
|
|
|
|
my $self = shift; |
280
|
|
|
|
|
|
|
$self->req->session( |
281
|
|
|
|
|
|
|
name => 'Jill Andrews', |
282
|
|
|
|
|
|
|
age => 24, |
283
|
|
|
|
|
|
|
email => 'jill@perlkelp.com' |
284
|
|
|
|
|
|
|
); |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
Set values using a Hashref: |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
sub set_session_hashref { |
290
|
|
|
|
|
|
|
my $self = shift; |
291
|
|
|
|
|
|
|
$self->req->session( { bar => 'foo' } ); |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
Clear the session: |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub clear_session { |
297
|
|
|
|
|
|
|
my $self = shift; |
298
|
|
|
|
|
|
|
$self->req->session( {} ); |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=head3 Common tasks with sessions |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=over |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=item Initialize file sessions |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
In your config file: |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
middleware => ['Session'], |
310
|
|
|
|
|
|
|
middleware_init => { |
311
|
|
|
|
|
|
|
Session => { |
312
|
|
|
|
|
|
|
store => 'File' |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
=item Delete session value |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
delete $self->req->session->{'useless'}; |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=item Remove all session values |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
$self->req->session( {} ); |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=back |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
=head2 is_ajax |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
Returns true if the request was called with C<XMLHttpRequest>. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
=head2 is_json |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
Returns true if the request's content type was C<application/json>. |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
=cut |
335
|
|
|
|
|
|
|
|