fix broken test case, update copyright date
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Request.pm
CommitLineData
fc7ec1d9 1package Catalyst::Request;
2
b4ca0ee8 3use IO::Socket qw[AF_INET inet_aton];
bd917b94 4use Carp;
fc42a730 5use utf8;
de19de2e 6use URI::http;
7use URI::https;
e669e88a 8use URI::QueryParam;
6680c772 9use HTTP::Headers;
191665f3 10use Stream::Buffered;
11use Hash::MultiValue;
12use Scalar::Util;
b94f8e72 13use Catalyst::Exception;
059c085b 14use Moose;
15
6802c884 16use namespace::clean -except => 'meta';
17
b99ff5d8 18with 'MooseX::Emulate::Class::Accessor::Fast';
19
f52cae56 20has env => (is => 'ro', writer => '_set_env', predicate => '_has_env');
47b9d68e 21# XXX Deprecated crap here - warn?
22has action => (is => 'rw');
23# XXX: Deprecated in docs ages ago (2006), deprecated with warning in 5.8000 due
24# to confusion between Engines and Plugin::Authentication. Remove in 5.8100?
25has user => (is => 'rw');
26sub snippets { shift->captures(@_) }
faa02805 27
47b9d68e 28has _read_position => (
26fc3c5f 29 # FIXME: work around Moose bug RT#75367
30 # init_arg => undef,
47b9d68e 31 is => 'ro',
32 writer => '_set_read_position',
33 default => 0,
34);
35has _read_length => (
26fc3c5f 36 # FIXME: work around Moose bug RT#75367
37 # init_arg => undef,
47b9d68e 38 is => 'ro',
faa02805 39 default => sub {
40 my $self = shift;
41 $self->header('Content-Length') || 0;
42 },
43 lazy => 1,
44);
45
5fb12dbb 46has address => (is => 'rw');
47has arguments => (is => 'rw', default => sub { [] });
d5f4b434 48has cookies => (is => 'ro', builder => 'prepare_cookies', lazy => 1);
49
d5f4b434 50sub prepare_cookies {
51 my ( $self ) = @_;
52
53 if ( my $header = $self->header('Cookie') ) {
54 return { CGI::Simple::Cookie->parse($header) };
55 }
56 {};
57}
58
5fb12dbb 59has query_keywords => (is => 'rw');
60has match => (is => 'rw');
61has method => (is => 'rw');
62has protocol => (is => 'rw');
f152ae23 63has query_parameters => (is => 'rw', lazy=>1, default => sub { shift->_use_hash_multivalue ? Hash::MultiValue->new : +{} });
5fb12dbb 64has secure => (is => 'rw', default => 0);
65has captures => (is => 'rw', default => sub { [] });
6cb9e383 66has uri => (is => 'rw', predicate => 'has_uri');
8026359e 67has remote_user => (is => 'rw');
5fb12dbb 68has headers => (
e5ecd5bc 69 is => 'rw',
059c085b 70 isa => 'HTTP::Headers',
71 handles => [qw(content_encoding content_length content_type header referer user_agent)],
d5f4b434 72 builder => 'prepare_headers',
6680c772 73 lazy => 1,
059c085b 74);
75
d5f4b434 76sub prepare_headers {
77 my ($self) = @_;
78
79 my $env = $self->env;
80 my $headers = HTTP::Headers->new();
81
82 for my $header (keys %{ $env }) {
83 next unless $header =~ /^(HTTP|CONTENT|COOKIE)/i;
84 (my $field = $header) =~ s/^HTTPS?_//;
85 $field =~ tr/_/-/;
86 $headers->header($field => $env->{$header});
87 }
88 return $headers;
89}
90
7c1c4dc6 91has _log => (
92 is => 'ro',
93 weak_ref => 1,
94 required => 1,
059c085b 95);
96
eb1f4b49 97has io_fh => (
b87d834e 98 is=>'ro',
c2fef52f 99 predicate=>'_has_io_fh',
b87d834e 100 lazy=>1,
101 builder=>'_build_io_fh');
eb1f4b49 102
ade3da0a 103sub _build_io_fh {
eb1f4b49 104 my $self = shift;
105 return $self->env->{'psgix.io'}
c368f69e 106 || (
107 $self->env->{'net.async.http.server.req'} &&
108 $self->env->{'net.async.http.server.req'}->stream) ## Until I can make ioasync cabal see the value of supportin psgix.io (jnap)
eb1f4b49 109 || die "Your Server does not support psgix.io";
ade3da0a 110};
eb1f4b49 111
b87d834e 112has data_handlers => ( is=>'ro', isa=>'HashRef', default=>sub { +{} } );
ade3da0a 113
b87d834e 114has body_data => (
115 is=>'ro',
116 lazy=>1,
117 builder=>'_build_body_data');
118
119sub _build_body_data {
120 my ($self) = @_;
b94f8e72 121
122 # Not sure if these returns should not be exceptions...
123 my $content_type = $self->content_type || return;
124 return unless ($self->method eq 'POST' || $self->method eq 'PUT');
125
b87d834e 126 my ($match) = grep { $content_type =~/$_/i }
127 keys(%{$self->data_handlers});
128
129 if($match) {
130 my $fh = $self->body;
131 local $_ = $fh;
132 return $self->data_handlers->{$match}->($fh, $self);
133 } else {
b94f8e72 134 Catalyst::Exception->throw("$content_type is does not have an available data handler");
b87d834e 135 }
136}
eb1f4b49 137
88ba7793 138has _use_hash_multivalue => (
139 is=>'ro',
140 required=>1,
141 default=> sub {0});
142
f083854e 143# Amount of data to read from input on each pass
144our $CHUNKSIZE = 64 * 1024;
145
146sub read {
147 my ($self, $maxlength) = @_;
148 my $remaining = $self->_read_length - $self->_read_position;
149 $maxlength ||= $CHUNKSIZE;
150
151 # Are we done reading?
152 if ( $remaining <= 0 ) {
153 return;
154 }
155
156 my $readlen = ( $remaining > $maxlength ) ? $maxlength : $remaining;
157 my $rc = $self->read_chunk( my $buffer, $readlen );
158 if ( defined $rc ) {
159 if (0 == $rc) { # Nothing more to read even though Content-Length
160 # said there should be.
161 return;
162 }
47b9d68e 163 $self->_set_read_position( $self->_read_position + $rc );
f083854e 164 return $buffer;
165 }
166 else {
167 Catalyst::Exception->throw(
168 message => "Unknown error reading input: $!" );
169 }
170}
171
87f50436 172sub read_chunk {
173 my $self = shift;
174 return $self->env->{'psgi.input'}->read(@_);
175}
176
059c085b 177has body_parameters => (
5fb12dbb 178 is => 'rw',
179 required => 1,
180 lazy => 1,
d003ff83 181 builder => 'prepare_body_parameters',
fc7ec1d9 182);
183
059c085b 184has uploads => (
5fb12dbb 185 is => 'rw',
186 required => 1,
5fb12dbb 187 default => sub { {} },
059c085b 188);
189
059c085b 190has parameters => (
1cbdfa9b 191 is => 'rw',
192 lazy => 1,
11e7af55 193 builder => '_build_parameters',
194 clearer => '_clear_parameters',
059c085b 195);
196
341620d5 197# TODO:
198# - Can we lose the before modifiers which just call prepare_body ?
199# they are wasteful, slow us down and feel cluttery.
200
201# Can we make _body an attribute, have the rest of
202# these lazy build from there and kill all the direct hash access
203# in Catalyst.pm and Engine.pm?
204
1cbdfa9b 205sub prepare_parameters {
206 my ( $self ) = @_;
11e7af55 207 $self->_clear_parameters;
208 return $self->parameters;
209}
210
11e7af55 211sub _build_parameters {
212 my ( $self ) = @_;
1cbdfa9b 213 my $parameters = {};
214 my $body_parameters = $self->body_parameters;
215 my $query_parameters = $self->query_parameters;
bd822b43 216
88ba7793 217 if($self->_use_hash_multivalue) {
f152ae23 218 return Hash::MultiValue->new($query_parameters->flatten, $body_parameters->flatten);
88ba7793 219 }
220
1cbdfa9b 221 # We copy, no references
222 foreach my $name (keys %$query_parameters) {
223 my $param = $query_parameters->{$name};
224 $parameters->{$name} = ref $param eq 'ARRAY' ? [ @$param ] : $param;
225 }
226
227 # Merge query and body parameters
228 foreach my $name (keys %$body_parameters) {
229 my $param = $body_parameters->{$name};
230 my @values = ref $param eq 'ARRAY' ? @$param : ($param);
231 if ( my $existing = $parameters->{$name} ) {
232 unshift(@values, (ref $existing eq 'ARRAY' ? @$existing : $existing));
233 }
234 $parameters->{$name} = @values > 1 ? \@values : $values[0];
235 }
236 $parameters;
237}
238
398f13db 239has _uploadtmp => (
240 is => 'ro',
241 predicate => '_has_uploadtmp',
242);
243
244sub prepare_body {
245 my ( $self ) = @_;
246
191665f3 247 # If previously applied middleware created the HTTP::Body object, then we
248 # just use that one.
952ff530 249
b111b5c1 250 if(my $plack_body = $self->_has_env ? $self->env->{'plack.request.http.body'} : undef) {
952ff530 251 $self->_body($plack_body);
191665f3 252 $self->_body->cleanup(1);
253 return;
952ff530 254 }
255
191665f3 256 # If there is nothing to read, set body to naught and return. This
257 # will cause all body code to be skipped
398f13db 258
191665f3 259 return $self->_body(0) unless my $length = $self->_read_length;
952ff530 260
191665f3 261 # Unless the body has already been set, create it. Not sure about this
262 # code, how else might it be set, but this was existing logic.
263
264 unless ($self->_body) {
265 my $type = $self->header('Content-Type');
266 $self->_body(HTTP::Body->new( $type, $length ));
267 $self->_body->cleanup(1);
952ff530 268
191665f3 269 # JNAP: I'm not sure this is doing what we expect, but it also doesn't
270 # seem to be hurting (seems ->_has_uploadtmp is true more than I would
271 # expect.
272
273 $self->_body->tmpdir( $self->_uploadtmp )
274 if $self->_has_uploadtmp;
952ff530 275 }
276
191665f3 277 # Ok if we get this far, we have to read psgi.input into the new body
278 # object. Lets play nice with any plack app or other downstream, so
279 # we create a buffer unless one exists.
280
281 my $stream_buffer;
282 if ($self->env->{'psgix.input.buffered'}) {
283 # Be paranoid about previous psgi middleware or apps that read the
284 # input but didn't return the buffer to the start.
285 $self->env->{'psgi.input'}->seek(0, 0);
286 } else {
287 $stream_buffer = Stream::Buffered->new($length);
288 }
952ff530 289
191665f3 290 # Check for definedness as you could read '0'
291 while ( defined ( my $chunk = $self->read() ) ) {
292 $self->prepare_body_chunk($chunk);
293 $stream_buffer->print($chunk) if $stream_buffer;
294 }
952ff530 295
191665f3 296 # Ok, we read the body. Lets play nice for any PSGI app down the pipe
297
298 if ($stream_buffer) {
299 $self->env->{'psgix.input.buffered'} = 1;
300 $self->env->{'psgi.input'} = $stream_buffer->rewind;
301 } else {
302 $self->env->{'psgi.input'}->seek(0, 0); # Reset the buffer for downstream middleware or apps
952ff530 303 }
191665f3 304
191665f3 305 # paranoia against wrong Content-Length header
306 my $remaining = $length - $self->_read_position;
307 if ( $remaining > 0 ) {
308 Catalyst::Exception->throw("Wrong Content-Length value: $length" );
952ff530 309 }
310}
311
398f13db 312sub prepare_body_chunk {
313 my ( $self, $chunk ) = @_;
314
315 $self->_body->add($chunk);
316}
317
398f13db 318sub prepare_body_parameters {
319 my ( $self ) = @_;
320
d003ff83 321 $self->prepare_body if ! $self->_has_body;
f152ae23 322
323 unless($self->_body) {
324 return $self->_use_hash_multivalue ? Hash::MultiValue->new : {};
325 }
398f13db 326
88ba7793 327 return $self->_use_hash_multivalue ?
f152ae23 328 Hash::MultiValue->from_mixed($self->_body->param) :
88ba7793 329 $self->_body->param;
398f13db 330}
341620d5 331
2f498a7e 332sub prepare_connection {
333 my ($self) = @_;
334
335 my $env = $self->env;
336
337 $self->address( $env->{REMOTE_ADDR} );
338 $self->hostname( $env->{REMOTE_HOST} )
339 if exists $env->{REMOTE_HOST};
340 $self->protocol( $env->{SERVER_PROTOCOL} );
341 $self->remote_user( $env->{REMOTE_USER} );
342 $self->method( $env->{REQUEST_METHOD} );
343 $self->secure( $env->{'psgi.url_scheme'} eq 'https' ? 1 : 0 );
344}
345
346# XXX - FIXME - method is here now, move this crap...
e99ec2dc 347around parameters => sub {
348 my ($orig, $self, $params) = @_;
349 if ($params) {
350 if ( !ref $params ) {
7c1c4dc6 351 $self->_log->warn(
e99ec2dc 352 "Attempt to retrieve '$params' with req->params(), " .
353 "you probably meant to call req->param('$params')"
354 );
355 $params = undef;
356 }
357 return $self->$orig($params);
358 }
359 $self->$orig();
059c085b 360};
361
362has base => (
5fb12dbb 363 is => 'rw',
364 required => 1,
365 lazy => 1,
366 default => sub {
059c085b 367 my $self = shift;
6cb9e383 368 return $self->path if $self->has_uri;
059c085b 369 },
370);
371
069355da 372has _body => (
0f56bbcf 373 is => 'rw', clearer => '_clear_body', predicate => '_has_body',
059c085b 374);
610bc6ec 375# Eugh, ugly. Should just be able to rename accessor methods to 'body'
b0ad47c1 376# and provide a custom reader..
610bc6ec 377sub body {
378 my $self = shift;
952ff530 379 $self->prepare_body unless $self->_has_body;
14c057aa 380 croak 'body is a reader' if scalar @_;
610bc6ec 381 return blessed $self->_body ? $self->_body->body : $self->_body;
382}
059c085b 383
384has hostname => (
385 is => 'rw',
386 required => 1,
387 lazy => 1,
388 default => sub {
389 my ($self) = @_;
9fb936e5 390 gethostbyaddr( inet_aton( $self->address ), AF_INET ) || $self->address
059c085b 391 },
392);
393
02570318 394has _path => ( is => 'rw', predicate => '_has_path', clearer => '_clear_path' );
395
059c085b 396sub args { shift->arguments(@_) }
397sub body_params { shift->body_parameters(@_) }
398sub input { shift->body(@_) }
399sub params { shift->parameters(@_) }
400sub query_params { shift->query_parameters(@_) }
401sub path_info { shift->path(@_) }
f7e4e231 402
8738b8fe 403=for stopwords param params
965f3e35 404
fc7ec1d9 405=head1 NAME
406
3e19f4f6 407Catalyst::Request - provides information about the current client request
fc7ec1d9 408
409=head1 SYNOPSIS
410
b22c6668 411 $req = $c->request;
767480fd 412 $req->address eq "127.0.0.1";
b22c6668 413 $req->arguments;
3e19f4f6 414 $req->args;
b22c6668 415 $req->base;
06e1b616 416 $req->body;
974733c0 417 $req->body_data;
fbcc39ad 418 $req->body_parameters;
b5176d9e 419 $req->content_encoding;
420 $req->content_length;
421 $req->content_type;
b77e7869 422 $req->cookie;
b22c6668 423 $req->cookies;
b5176d9e 424 $req->header;
b22c6668 425 $req->headers;
426 $req->hostname;
61bacdcc 427 $req->input;
3b4d1251 428 $req->query_keywords;
b22c6668 429 $req->match;
430 $req->method;
e7c0c583 431 $req->param;
e7c0c583 432 $req->parameters;
3e19f4f6 433 $req->params;
b22c6668 434 $req->path;
bfde09a2 435 $req->protocol;
fbcc39ad 436 $req->query_parameters;
437 $req->read;
b5176d9e 438 $req->referer;
bfde09a2 439 $req->secure;
47b9d68e 440 $req->captures;
e7c0c583 441 $req->upload;
b22c6668 442 $req->uploads;
77d12cae 443 $req->uri;
7ce7ca2e 444 $req->user;
66294129 445 $req->user_agent;
9d8d0ab9 446 $req->env;
b22c6668 447
3e22baa5 448See also L<Catalyst>, L<Catalyst::Request::Upload>.
fc7ec1d9 449
450=head1 DESCRIPTION
451
3e19f4f6 452This is the Catalyst Request class, which provides an interface to data for the
453current client request. The request object is prepared by L<Catalyst::Engine>,
454thus hiding the details of the particular engine implementation.
b22c6668 455
456=head1 METHODS
fc7ec1d9 457
b5ecfcf0 458=head2 $req->address
0556eb49 459
3e19f4f6 460Returns the IP address of the client.
61b1e958 461
b5ecfcf0 462=head2 $req->arguments
61b1e958 463
b22c6668 464Returns a reference to an array containing the arguments.
fc7ec1d9 465
466 print $c->request->arguments->[0];
467
c436c1e8 468For example, if your action was
469
7d7519a4 470 package MyApp::Controller::Foo;
85d9fce6 471
472 sub moose : Local {
473 ...
474 }
c436c1e8 475
3e19f4f6 476and the URI for the request was C<http://.../foo/moose/bah>, the string C<bah>
c436c1e8 477would be the first and only argument.
478
6d920953 479Arguments get automatically URI-unescaped for you.
8f58057d 480
b5ecfcf0 481=head2 $req->args
3e19f4f6 482
01011731 483Shortcut for L</arguments>.
3e19f4f6 484
b5ecfcf0 485=head2 $req->base
fc7ec1d9 486
328f225e 487Contains the URI base. This will always have a trailing slash. Note that the
f4dda4a8 488URI scheme (e.g., http vs. https) must be determined through heuristics;
328f225e 489depending on your server configuration, it may be incorrect. See $req->secure
490for more info.
c436c1e8 491
3e19f4f6 492If your application was queried with the URI
493C<http://localhost:3000/some/path> then C<base> is C<http://localhost:3000/>.
fc7ec1d9 494
b5ecfcf0 495=head2 $req->body
06e1b616 496
843871cf 497Returns the message body of the request, as returned by L<HTTP::Body>: a string,
498unless Content-Type is C<application/x-www-form-urlencoded>, C<text/xml>, or
499C<multipart/form-data>, in which case a L<File::Temp> object is returned.
e060fe05 500
974733c0 501=head2 $req->body_data
502
503Returns a Perl representation of POST/PUT body data that is not classic HTML
504form data, such as JSON, XML, etc. By default, Catalyst will parse incoming
505data of the type 'application/json' and return access to that data via this
506method. You may define addition data_handlers via a global configuration
507setting. See L<Catalyst\DATA HANDLERS> for more information.
508
b94f8e72 509If the POST is malformed in some way (such as undefined or not content that
510matches the content-type) we raise a L<Catalyst::Exception> with the error
511text as the message.
512
513If the POSTed content type does not match an availabled data handler, this
514will also raise an exception.
515
b5ecfcf0 516=head2 $req->body_parameters
fbcc39ad 517
3e19f4f6 518Returns a reference to a hash containing body (POST) parameters. Values can
fbcc39ad 519be either a scalar or an arrayref containing scalars.
520
521 print $c->request->body_parameters->{field};
522 print $c->request->body_parameters->{field}->[0];
c436c1e8 523
d631b5f9 524These are the parameters from the POST part of the request, if any.
e5ecd5bc 525
b5ecfcf0 526=head2 $req->body_params
fbcc39ad 527
3e19f4f6 528Shortcut for body_parameters.
fbcc39ad 529
b5ecfcf0 530=head2 $req->content_encoding
b5176d9e 531
3e19f4f6 532Shortcut for $req->headers->content_encoding.
b5176d9e 533
b5ecfcf0 534=head2 $req->content_length
b5176d9e 535
3e19f4f6 536Shortcut for $req->headers->content_length.
b5176d9e 537
b5ecfcf0 538=head2 $req->content_type
b5176d9e 539
3e19f4f6 540Shortcut for $req->headers->content_type.
b5176d9e 541
b5ecfcf0 542=head2 $req->cookie
3ad654e0 543
3e19f4f6 544A convenient method to access $req->cookies.
3ad654e0 545
546 $cookie = $c->request->cookie('name');
547 @cookies = $c->request->cookie;
548
549=cut
550
551sub cookie {
552 my $self = shift;
553
554 if ( @_ == 0 ) {
b77e7869 555 return keys %{ $self->cookies };
3ad654e0 556 }
557
558 if ( @_ == 1 ) {
559
560 my $name = shift;
561
b77e7869 562 unless ( exists $self->cookies->{$name} ) {
3ad654e0 563 return undef;
564 }
fbcc39ad 565
b77e7869 566 return $self->cookies->{$name};
3ad654e0 567 }
568}
569
b5ecfcf0 570=head2 $req->cookies
fc7ec1d9 571
b22c6668 572Returns a reference to a hash containing the cookies.
fc7ec1d9 573
574 print $c->request->cookies->{mycookie}->value;
575
7e743798 576The cookies in the hash are indexed by name, and the values are L<CGI::Simple::Cookie>
c436c1e8 577objects.
578
b5ecfcf0 579=head2 $req->header
b5176d9e 580
3e19f4f6 581Shortcut for $req->headers->header.
b5176d9e 582
b5ecfcf0 583=head2 $req->headers
fc7ec1d9 584
3e19f4f6 585Returns an L<HTTP::Headers> object containing the headers for the current request.
fc7ec1d9 586
587 print $c->request->headers->header('X-Catalyst');
588
b5ecfcf0 589=head2 $req->hostname
0556eb49 590
178dca5f 591Returns the hostname of the client. Use C<< $req->uri->host >> to get the hostname of the server.
e5ecd5bc 592
b5ecfcf0 593=head2 $req->input
61bacdcc 594
3e19f4f6 595Alias for $req->body.
61bacdcc 596
3b4d1251 597=head2 $req->query_keywords
598
599Contains the keywords portion of a query string, when no '=' signs are
600present.
601
602 http://localhost/path?some+keywords
b0ad47c1 603
3b4d1251 604 $c->request->query_keywords will contain 'some keywords'
605
b5ecfcf0 606=head2 $req->match
fc7ec1d9 607
3e19f4f6 608This contains the matching part of a Regex action. Otherwise
2c83fd5a 609it returns the same as 'action', except for default actions,
610which return an empty string.
fc7ec1d9 611
b5ecfcf0 612=head2 $req->method
b5176d9e 613
614Contains the request method (C<GET>, C<POST>, C<HEAD>, etc).
615
b5ecfcf0 616=head2 $req->param
e7c0c583 617
b0ad47c1 618Returns GET and POST parameters with a CGI.pm-compatible param method. This
3e19f4f6 619is an alternative method for accessing parameters in $c->req->parameters.
e7c0c583 620
a82c2894 621 $value = $c->request->param( 'foo' );
622 @values = $c->request->param( 'foo' );
e7c0c583 623 @params = $c->request->param;
624
3e705254 625Like L<CGI>, and B<unlike> earlier versions of Catalyst, passing multiple
a82c2894 626arguments to this method, like this:
627
85d9fce6 628 $c->request->param( 'foo', 'bar', 'gorch', 'quxx' );
a82c2894 629
630will set the parameter C<foo> to the multiple values C<bar>, C<gorch> and
631C<quxx>. Previously this would have added C<bar> as another value to C<foo>
3e19f4f6 632(creating it if it didn't exist before), and C<quxx> as another value for
633C<gorch>.
a82c2894 634
83312afd 635B<NOTE> this is considered a legacy interface and care should be taken when
636using it. C<< scalar $c->req->param( 'foo' ) >> will return only the first
637C<foo> param even if multiple are present; C<< $c->req->param( 'foo' ) >> will
638return a list of as many are present, which can have unexpected consequences
639when writing code of the form:
640
641 $foo->bar(
642 a => 'b',
643 baz => $c->req->param( 'baz' ),
644 );
645
646If multiple C<baz> parameters are provided this code might corrupt data or
647cause a hash initialization error. For a more straightforward interface see
648C<< $c->req->parameters >>.
649
f384c848 650B<NOTE> Interfaces like this, which are based on L<CGI> and the C<param> method
651are now known to cause demonstrated exploits. It is highly recommended that you
652avoid using this method, and migrate existing code away from it. Here's the
653whitepaper of the exploit:
0810283f 654
655L<http://blog.gerv.net/2014/10/new-class-of-vulnerability-in-perl-web-applications/>
656
657Basically this is an exploit that takes advantage of how L<\param> will do one thing
658in scalar context and another thing in list context. This is combined with how Perl
659chooses to deal with duplicate keys in a hash definition by overwriting the value of
660existing keys with a new value if the same key shows up again. Generally you will be
661vulnerale to this exploit if you are using this method in a direct assignment in a
662hash, such as with a L<DBIx::Class> create statement. For example, if you have
663parameters like:
664
665 user?user=123&foo=a&foo=user&foo=456
666
667You could end up with extra parameters injected into your method calls:
668
669 $c->model('User')->create({
670 user => $c->req->param('user'),
671 foo => $c->req->param('foo'),
672 });
673
674Which would look like:
675
676 $c->model('User')->create({
677 user => 123,
678 foo => qw(a user 456),
679 });
680
681(or to be absolutely clear if you are not seeing it):
682
683 $c->model('User')->create({
684 user => 456,
685 foo => 'a',
686 });
687
688Possible remediations include scrubbing your parameters with a form validator like
689L<HTML::FormHandler> or being careful to force scalar context using the scalar
690keyword:
691
692 $c->model('User')->create({
693 user => scalar($c->req->param('user')),
694 foo => scalar($c->req->param('foo')),
695 });
696
f384c848 697Upcoming versions of L<Catalyst> will disable this interface by default and require
698you to positively enable it should you require it for backwards compatibility reasons.
699
e7c0c583 700=cut
701
702sub param {
703 my $self = shift;
704
705 if ( @_ == 0 ) {
706 return keys %{ $self->parameters };
707 }
708
4f96d61c 709 # If anything in @_ is undef, carp about that, and remove it from
710 # the list;
711
712 my @params = grep { defined($_) ? 1 : do {carp "You called ->params with an undefined value"; 0} } @_;
713
714 if ( @params == 1 ) {
e7c0c583 715
4f96d61c 716 defined(my $param = shift @params) ||
717 carp "You called ->params with an undefined value 2";
6bd2b72c 718
bfde09a2 719 unless ( exists $self->parameters->{$param} ) {
720 return wantarray ? () : undef;
721 }
722
723 if ( ref $self->parameters->{$param} eq 'ARRAY' ) {
724 return (wantarray)
725 ? @{ $self->parameters->{$param} }
726 : $self->parameters->{$param}->[0];
727 }
728 else {
729 return (wantarray)
730 ? ( $self->parameters->{$param} )
731 : $self->parameters->{$param};
732 }
d7945f32 733 }
4f96d61c 734 elsif ( @params > 1 ) {
735 my $field = shift @params;
90d3ac10 736 $self->parameters->{$field} = [@params];
d7945f32 737 }
e7c0c583 738}
b5176d9e 739
b5ecfcf0 740=head2 $req->parameters
61b1e958 741
3e19f4f6 742Returns a reference to a hash containing GET and POST parameters. Values can
d08ced28 743be either a scalar or an arrayref containing scalars.
fc7ec1d9 744
e7c0c583 745 print $c->request->parameters->{field};
746 print $c->request->parameters->{field}->[0];
fc7ec1d9 747
c436c1e8 748This is the combination of C<query_parameters> and C<body_parameters>.
749
b5ecfcf0 750=head2 $req->params
3e19f4f6 751
752Shortcut for $req->parameters.
753
b5ecfcf0 754=head2 $req->path
fc7ec1d9 755
3e19f4f6 756Returns the path, i.e. the part of the URI after $req->base, for the current request.
fc7ec1d9 757
be6801fa 758 http://localhost/path/foo
759
760 $c->request->path will contain 'path/foo'
761
b5ecfcf0 762=head2 $req->path_info
fbcc39ad 763
10011c19 764Alias for path, added for compatibility with L<CGI>.
fbcc39ad 765
766=cut
767
768sub path {
02fb5d78 769 my ( $self, @params ) = @_;
4f5ebacd 770
02fb5d78 771 if (@params) {
772 $self->uri->path(@params);
02570318 773 $self->_clear_path;
fbcc39ad 774 }
02570318 775 elsif ( $self->_has_path ) {
776 return $self->_path;
e561386f 777 }
02fb5d78 778 else {
779 my $path = $self->uri->path;
780 my $location = $self->base->path;
781 $path =~ s/^(\Q$location\E)?//;
782 $path =~ s/^\///;
02570318 783 $self->_path($path);
fbcc39ad 784
02fb5d78 785 return $path;
786 }
fbcc39ad 787}
788
b5ecfcf0 789=head2 $req->protocol
bfde09a2 790
3e19f4f6 791Returns the protocol (HTTP/1.0 or HTTP/1.1) used for the current request.
bfde09a2 792
b5ecfcf0 793=head2 $req->query_parameters
fbcc39ad 794
def54ce2 795=head2 $req->query_params
796
3e19f4f6 797Returns a reference to a hash containing query string (GET) parameters. Values can
fbcc39ad 798be either a scalar or an arrayref containing scalars.
799
800 print $c->request->query_parameters->{field};
801 print $c->request->query_parameters->{field}->[0];
b0ad47c1 802
b5ecfcf0 803=head2 $req->read( [$maxlength] )
fbcc39ad 804
3e19f4f6 805Reads a chunk of data from the request body. This method is intended to be
806used in a while loop, reading $maxlength bytes on every call. $maxlength
fbcc39ad 807defaults to the size of the request if not specified.
808
87f50436 809=head2 $req->read_chunk(\$buff, $max)
810
d7f18923 811Reads a chunk.
87f50436 812
9779c885 813You have to set MyApp->config(parse_on_demand => 1) to use this directly.
fbcc39ad 814
b5ecfcf0 815=head2 $req->referer
fc7ec1d9 816
3e19f4f6 817Shortcut for $req->headers->referer. Returns the referring page.
fc7ec1d9 818
b5ecfcf0 819=head2 $req->secure
bfde09a2 820
328f225e 821Returns true or false, indicating whether the connection is secure
d7f18923 822(https). The reliability of $req->secure may depend on your server
823configuration; Catalyst relies on PSGI to determine whether or not a
824request is secure (Catalyst looks at psgi.url_scheme), and different
825PSGI servers may make this determination in different ways (as by
826directly passing along information from the server, interpreting any of
827several HTTP headers, or using heuristics of their own).
bfde09a2 828
2982e768 829=head2 $req->captures
830
5c6a56e0 831Returns a reference to an array containing captured args from chained
832actions or regex captures.
fc7ec1d9 833
2982e768 834 my @captures = @{ $c->request->captures };
835
b5ecfcf0 836=head2 $req->upload
e7c0c583 837
3e19f4f6 838A convenient method to access $req->uploads.
e7c0c583 839
840 $upload = $c->request->upload('field');
841 @uploads = $c->request->upload('field');
842 @fields = $c->request->upload;
bfde09a2 843
e7c0c583 844 for my $upload ( $c->request->upload('field') ) {
146554c5 845 print $upload->filename;
e7c0c583 846 }
847
848=cut
849
850sub upload {
851 my $self = shift;
852
853 if ( @_ == 0 ) {
854 return keys %{ $self->uploads };
855 }
856
bfde09a2 857 if ( @_ == 1 ) {
e7c0c583 858
bfde09a2 859 my $upload = shift;
860
861 unless ( exists $self->uploads->{$upload} ) {
862 return wantarray ? () : undef;
863 }
6bd2b72c 864
bfde09a2 865 if ( ref $self->uploads->{$upload} eq 'ARRAY' ) {
866 return (wantarray)
867 ? @{ $self->uploads->{$upload} }
868 : $self->uploads->{$upload}->[0];
869 }
870 else {
871 return (wantarray)
fbcc39ad 872 ? ( $self->uploads->{$upload} )
873 : $self->uploads->{$upload};
bfde09a2 874 }
d7945f32 875 }
bfde09a2 876
a4f5c51e 877 if ( @_ > 1 ) {
bfde09a2 878
879 while ( my ( $field, $upload ) = splice( @_, 0, 2 ) ) {
880
881 if ( exists $self->uploads->{$field} ) {
882 for ( $self->uploads->{$field} ) {
883 $_ = [$_] unless ref($_) eq "ARRAY";
884 push( @$_, $upload );
885 }
886 }
887 else {
888 $self->uploads->{$field} = $upload;
889 }
890 }
e7c0c583 891 }
892}
893
b5ecfcf0 894=head2 $req->uploads
fc7ec1d9 895
bfde09a2 896Returns a reference to a hash containing uploads. Values can be either a
b0ad47c1 897L<Catalyst::Request::Upload> object, or an arrayref of
84e7aa89 898L<Catalyst::Request::Upload> objects.
e7c0c583 899
900 my $upload = $c->request->uploads->{field};
901 my $upload = $c->request->uploads->{field}->[0];
902
b5ecfcf0 903=head2 $req->uri
fbcc39ad 904
d26ee0d0 905Returns a L<URI> object for the current request. Stringifies to the URI text.
fbcc39ad 906
a375a206 907=head2 $req->mangle_params( { key => 'value' }, $appendmode);
bd917b94 908
a375a206 909Returns a hashref of parameters stemming from the current request's params,
910plus the ones supplied. Keys for which no current param exists will be
911added, keys with undefined values will be removed and keys with existing
912params will be replaced. Note that you can supply a true value as the final
913argument to change behavior with regards to existing parameters, appending
914values rather than replacing them.
915
916A quick example:
917
918 # URI query params foo=1
919 my $hashref = $req->mangle_params({ foo => 2 });
920 # Result is query params of foo=2
921
922versus append mode:
923
924 # URI query params foo=1
925 my $hashref = $req->mangle_params({ foo => 2 }, 1);
926 # Result is query params of foo=1&foo=2
927
928This is the code behind C<uri_with>.
bd917b94 929
930=cut
931
a375a206 932sub mangle_params {
933 my ($self, $args, $append) = @_;
b0ad47c1 934
a375a206 935 carp('No arguments passed to mangle_params()') unless $args;
fbb513f7 936
2f381252 937 foreach my $value ( values %$args ) {
d0f0fcf6 938 next unless defined $value;
fbb513f7 939 for ( ref $value eq 'ARRAY' ? @$value : $value ) {
940 $_ = "$_";
7066a4d5 941 utf8::encode( $_ ) if utf8::is_utf8($_);
fc42a730 942 }
fc42a730 943 };
b0ad47c1 944
a375a206 945 my %params = %{ $self->uri->query_form_hash };
946 foreach my $key (keys %{ $args }) {
947 my $val = $args->{$key};
948 if(defined($val)) {
949
950 if($append && exists($params{$key})) {
951
952 # This little bit of heaven handles appending a new value onto
953 # an existing one regardless if the existing value is an array
954 # or not, and regardless if the new value is an array or not
955 $params{$key} = [
956 ref($params{$key}) eq 'ARRAY' ? @{ $params{$key} } : $params{$key},
957 ref($val) eq 'ARRAY' ? @{ $val } : $val
958 ];
959
960 } else {
961 $params{$key} = $val;
962 }
963 } else {
964
965 # If the param wasn't defined then we delete it.
966 delete($params{$key});
967 }
968 }
969
970
971 return \%params;
972}
973
974=head2 $req->uri_with( { key => 'value' } );
975
976Returns a rewritten URI object for the current request. Key/value pairs
977passed in will override existing parameters. You can remove an existing
978parameter by passing in an undef value. Unmodified pairs will be
979preserved.
980
981You may also pass an optional second parameter that puts C<uri_with> into
982append mode:
983
984 $req->uri_with( { key => 'value' }, { mode => 'append' } );
9779c885 985
a375a206 986See C<mangle_params> for an explanation of this behavior.
987
988=cut
989
990sub uri_with {
991 my( $self, $args, $behavior) = @_;
992
993 carp( 'No arguments passed to uri_with()' ) unless $args;
994
995 my $append = 0;
996 if((ref($behavior) eq 'HASH') && defined($behavior->{mode}) && ($behavior->{mode} eq 'append')) {
997 $append = 1;
998 }
999
1000 my $params = $self->mangle_params($args, $append);
1001
1002 my $uri = $self->uri->clone;
1003 $uri->query_form($params);
2f381252 1004
bd917b94 1005 return $uri;
1006}
1007
8026359e 1008=head2 $req->remote_user
1009
1010Returns the value of the C<REMOTE_USER> environment variable.
7ce7ca2e 1011
b5ecfcf0 1012=head2 $req->user_agent
b5176d9e 1013
3e19f4f6 1014Shortcut to $req->headers->user_agent. Returns the user agent (browser)
1015version string.
b5176d9e 1016
eb1f4b49 1017=head2 $req->io_fh
1018
1019Returns a psgix.io bidirectional socket, if your server supports one. Used for
1020when you want to jailbreak out of PSGI and handle bidirectional client server
1021communication manually, such as when you are using cometd or websockets.
1022
47b9d68e 1023=head1 SETUP METHODS
1024
1025You should never need to call these yourself in application code,
1026however they are useful if extending Catalyst by applying a request role.
1027
1028=head2 $self->prepare_headers()
1029
1030Sets up the C<< $res->headers >> accessor.
1031
1032=head2 $self->prepare_body()
1033
1034Sets up the body using L<HTTP::Body>
1035
1036=head2 $self->prepare_body_chunk()
1037
1038Add a chunk to the request body.
1039
1040=head2 $self->prepare_body_parameters()
1041
1042Sets up parameters from body.
1043
8738b8fe 1044=head2 $self->prepare_cookies()
47b9d68e 1045
1046Parse cookies from header. Sets up a L<CGI::Simple::Cookie> object.
1047
8738b8fe 1048=head2 $self->prepare_connection()
1049
1050Sets up various fields in the request like the local and remote addresses,
f59eeb09 1051request method, hostname requested etc.
8738b8fe 1052
1053=head2 $self->prepare_parameters()
1054
1055Ensures that the body has been parsed, then builds the parameters, which are
1056combined from those in the request and those in the body.
1057
11e7af55 1058If parameters have already been set will clear the parameters and build them again.
1059
9d8d0ab9 1060=head2 $self->env
1061
1062Access to the raw PSGI env.
8738b8fe 1063
059c085b 1064=head2 meta
1065
1066Provided by Moose
1067
3e19f4f6 1068=head1 AUTHORS
fc7ec1d9 1069
2f381252 1070Catalyst Contributors, see Catalyst.pm
fc7ec1d9 1071
1072=head1 COPYRIGHT
1073
536bee89 1074This library is free software. You can redistribute it and/or modify
61b1e958 1075it under the same terms as Perl itself.
fc7ec1d9 1076
1077=cut
1078
e5ecd5bc 1079__PACKAGE__->meta->make_immutable;
1080
fc7ec1d9 10811;