Move prepare_parametrs to be the builder.
[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;
b4ca0ee8 10
059c085b 11use Moose;
12
6802c884 13use namespace::clean -except => 'meta';
14
b99ff5d8 15with 'MooseX::Emulate::Class::Accessor::Fast';
16
faa02805 17has env => (is => 'ro', writer => '_set_env');
18
19has _read_position => ( is => 'rw', default => 0 );
20has _read_length => ( is => 'ro',
21 default => sub {
22 my $self = shift;
23 $self->header('Content-Length') || 0;
24 },
25 lazy => 1,
26);
27
5fb12dbb 28has action => (is => 'rw');
29has address => (is => 'rw');
30has arguments => (is => 'rw', default => sub { [] });
31has cookies => (is => 'rw', default => sub { {} });
32has query_keywords => (is => 'rw');
33has match => (is => 'rw');
34has method => (is => 'rw');
35has protocol => (is => 'rw');
059c085b 36has query_parameters => (is => 'rw', default => sub { {} });
5fb12dbb 37has secure => (is => 'rw', default => 0);
38has captures => (is => 'rw', default => sub { [] });
6cb9e383 39has uri => (is => 'rw', predicate => 'has_uri');
8026359e 40has remote_user => (is => 'rw');
5fb12dbb 41has headers => (
e5ecd5bc 42 is => 'rw',
059c085b 43 isa => 'HTTP::Headers',
44 handles => [qw(content_encoding content_length content_type header referer user_agent)],
6680c772 45 default => sub { HTTP::Headers->new() },
46 required => 1,
47 lazy => 1,
059c085b 48);
49
50has _context => (
51 is => 'rw',
52 weak_ref => 1,
02570318 53 clearer => '_clear_context',
059c085b 54);
55
f083854e 56# Amount of data to read from input on each pass
57our $CHUNKSIZE = 64 * 1024;
58
59sub read {
60 my ($self, $maxlength) = @_;
61 my $remaining = $self->_read_length - $self->_read_position;
62 $maxlength ||= $CHUNKSIZE;
63
64 # Are we done reading?
65 if ( $remaining <= 0 ) {
66 return;
67 }
68
69 my $readlen = ( $remaining > $maxlength ) ? $maxlength : $remaining;
70 my $rc = $self->read_chunk( my $buffer, $readlen );
71 if ( defined $rc ) {
72 if (0 == $rc) { # Nothing more to read even though Content-Length
73 # said there should be.
74 return;
75 }
76 $self->_read_position( $self->_read_position + $rc );
77 return $buffer;
78 }
79 else {
80 Catalyst::Exception->throw(
81 message => "Unknown error reading input: $!" );
82 }
83}
84
87f50436 85sub read_chunk {
86 my $self = shift;
87 return $self->env->{'psgi.input'}->read(@_);
88}
89
059c085b 90has body_parameters => (
5fb12dbb 91 is => 'rw',
92 required => 1,
93 lazy => 1,
94 default => sub { {} },
fc7ec1d9 95);
96
059c085b 97has uploads => (
5fb12dbb 98 is => 'rw',
99 required => 1,
5fb12dbb 100 default => sub { {} },
059c085b 101);
102
059c085b 103has parameters => (
1cbdfa9b 104 is => 'rw',
105 lazy => 1,
106 builder => 'prepare_parameters',
059c085b 107);
108
341620d5 109# TODO:
110# - Can we lose the before modifiers which just call prepare_body ?
111# they are wasteful, slow us down and feel cluttery.
112
113# Can we make _body an attribute, have the rest of
114# these lazy build from there and kill all the direct hash access
115# in Catalyst.pm and Engine.pm?
116
1cbdfa9b 117sub prepare_parameters {
118 my ( $self ) = @_;
119
398f13db 120 $self->prepare_body;
1cbdfa9b 121 my $parameters = {};
122 my $body_parameters = $self->body_parameters;
123 my $query_parameters = $self->query_parameters;
124 # We copy, no references
125 foreach my $name (keys %$query_parameters) {
126 my $param = $query_parameters->{$name};
127 $parameters->{$name} = ref $param eq 'ARRAY' ? [ @$param ] : $param;
128 }
129
130 # Merge query and body parameters
131 foreach my $name (keys %$body_parameters) {
132 my $param = $body_parameters->{$name};
133 my @values = ref $param eq 'ARRAY' ? @$param : ($param);
134 if ( my $existing = $parameters->{$name} ) {
135 unshift(@values, (ref $existing eq 'ARRAY' ? @$existing : $existing));
136 }
137 $parameters->{$name} = @values > 1 ? \@values : $values[0];
138 }
139 $parameters;
140}
141
398f13db 142before body_parameters => sub {
143 my ($self) = @_;
144 $self->prepare_body;
145 $self->prepare_body_parameters;
146};
147
148=head2 $self->prepare_body()
149
150sets up the L<Catalyst::Request> object body using L<HTTP::Body>
151
152=cut
153
154has _uploadtmp => (
155 is => 'ro',
156 predicate => '_has_uploadtmp',
157);
158
159sub prepare_body {
160 my ( $self ) = @_;
161
162 if ( my $length = $self->_read_length ) {
163 unless ( $self->_body ) {
164 my $type = $self->header('Content-Type');
165 $self->_body(HTTP::Body->new( $type, $length ));
166 $self->_body->cleanup(1); # Make extra sure!
167 $self->_body->tmpdir( $self->_uploadtmp )
168 if $self->_has_uploadtmp;
169 }
170
171 # Check for definedness as you could read '0'
172 while ( defined ( my $buffer = $self->read() ) ) {
173 $self->prepare_body_chunk($buffer);
174 }
175
176 # paranoia against wrong Content-Length header
177 my $remaining = $length - $self->_read_position;
178 if ( $remaining > 0 ) {
179 Catalyst::Exception->throw(
180 "Wrong Content-Length value: $length" );
181 }
182 }
183 else {
184 # Defined but will cause all body code to be skipped
185 $self->_body(0);
186 }
187}
188
189=head2 $self->prepare_body_chunk()
190
191Add a chunk to the request body.
192
193=cut
194
195sub prepare_body_chunk {
196 my ( $self, $chunk ) = @_;
197
198 $self->_body->add($chunk);
199}
200
201=head2 $self->prepare_body_parameters()
202
203Sets up parameters from body.
204
205=cut
206
207sub prepare_body_parameters {
208 my ( $self ) = @_;
209
210 return unless $self->_body;
211
212 $self->{body_parameters} = $self->_body->param; # FIXME!! Recursion here.
213}
341620d5 214
e99ec2dc 215around parameters => sub {
216 my ($orig, $self, $params) = @_;
217 if ($params) {
218 if ( !ref $params ) {
219 $self->_context->log->warn(
220 "Attempt to retrieve '$params' with req->params(), " .
221 "you probably meant to call req->param('$params')"
222 );
223 $params = undef;
224 }
225 return $self->$orig($params);
226 }
227 $self->$orig();
059c085b 228};
229
230has base => (
5fb12dbb 231 is => 'rw',
232 required => 1,
233 lazy => 1,
234 default => sub {
059c085b 235 my $self = shift;
6cb9e383 236 return $self->path if $self->has_uri;
059c085b 237 },
238);
239
069355da 240has _body => (
0f56bbcf 241 is => 'rw', clearer => '_clear_body', predicate => '_has_body',
059c085b 242);
610bc6ec 243# Eugh, ugly. Should just be able to rename accessor methods to 'body'
b0ad47c1 244# and provide a custom reader..
610bc6ec 245sub body {
246 my $self = shift;
398f13db 247 $self->prepare_body();
14c057aa 248 croak 'body is a reader' if scalar @_;
610bc6ec 249 return blessed $self->_body ? $self->_body->body : $self->_body;
250}
059c085b 251
252has hostname => (
253 is => 'rw',
254 required => 1,
255 lazy => 1,
256 default => sub {
257 my ($self) = @_;
9fb936e5 258 gethostbyaddr( inet_aton( $self->address ), AF_INET ) || $self->address
059c085b 259 },
260);
261
02570318 262has _path => ( is => 'rw', predicate => '_has_path', clearer => '_clear_path' );
263
8026359e 264# XXX: Deprecated in docs ages ago (2006), deprecated with warning in 5.8000 due
265# to confusion between Engines and Plugin::Authentication. Remove in 5.8100?
266has user => (is => 'rw');
267
059c085b 268sub args { shift->arguments(@_) }
269sub body_params { shift->body_parameters(@_) }
270sub input { shift->body(@_) }
271sub params { shift->parameters(@_) }
272sub query_params { shift->query_parameters(@_) }
273sub path_info { shift->path(@_) }
274sub snippets { shift->captures(@_) }
f7e4e231 275
965f3e35 276=for stopwords param params
277
fc7ec1d9 278=head1 NAME
279
3e19f4f6 280Catalyst::Request - provides information about the current client request
fc7ec1d9 281
282=head1 SYNOPSIS
283
b22c6668 284 $req = $c->request;
285 $req->action;
286 $req->address;
b22c6668 287 $req->arguments;
3e19f4f6 288 $req->args;
b22c6668 289 $req->base;
06e1b616 290 $req->body;
fbcc39ad 291 $req->body_parameters;
b5176d9e 292 $req->content_encoding;
293 $req->content_length;
294 $req->content_type;
b77e7869 295 $req->cookie;
b22c6668 296 $req->cookies;
b5176d9e 297 $req->header;
b22c6668 298 $req->headers;
299 $req->hostname;
61bacdcc 300 $req->input;
3b4d1251 301 $req->query_keywords;
b22c6668 302 $req->match;
303 $req->method;
e7c0c583 304 $req->param;
e7c0c583 305 $req->parameters;
3e19f4f6 306 $req->params;
b22c6668 307 $req->path;
bfde09a2 308 $req->protocol;
fbcc39ad 309 $req->query_parameters;
310 $req->read;
b5176d9e 311 $req->referer;
bfde09a2 312 $req->secure;
2982e768 313 $req->captures; # previously knows as snippets
e7c0c583 314 $req->upload;
b22c6668 315 $req->uploads;
77d12cae 316 $req->uri;
7ce7ca2e 317 $req->user;
66294129 318 $req->user_agent;
b22c6668 319
3e22baa5 320See also L<Catalyst>, L<Catalyst::Request::Upload>.
fc7ec1d9 321
322=head1 DESCRIPTION
323
3e19f4f6 324This is the Catalyst Request class, which provides an interface to data for the
325current client request. The request object is prepared by L<Catalyst::Engine>,
326thus hiding the details of the particular engine implementation.
b22c6668 327
328=head1 METHODS
fc7ec1d9 329
b5ecfcf0 330=head2 $req->action
fc7ec1d9 331
aae8d418 332[DEPRECATED] Returns the name of the requested action.
333
334
335Use C<< $c->action >> instead (which returns a
336L<Catalyst::Action|Catalyst::Action> object).
fc7ec1d9 337
b5ecfcf0 338=head2 $req->address
0556eb49 339
3e19f4f6 340Returns the IP address of the client.
61b1e958 341
b5ecfcf0 342=head2 $req->arguments
61b1e958 343
b22c6668 344Returns a reference to an array containing the arguments.
fc7ec1d9 345
346 print $c->request->arguments->[0];
347
c436c1e8 348For example, if your action was
349
7d7519a4 350 package MyApp::Controller::Foo;
85d9fce6 351
352 sub moose : Local {
353 ...
354 }
c436c1e8 355
3e19f4f6 356and the URI for the request was C<http://.../foo/moose/bah>, the string C<bah>
c436c1e8 357would be the first and only argument.
358
6d920953 359Arguments get automatically URI-unescaped for you.
8f58057d 360
b5ecfcf0 361=head2 $req->args
3e19f4f6 362
01011731 363Shortcut for L</arguments>.
3e19f4f6 364
b5ecfcf0 365=head2 $req->base
fc7ec1d9 366
328f225e 367Contains the URI base. This will always have a trailing slash. Note that the
f4dda4a8 368URI scheme (e.g., http vs. https) must be determined through heuristics;
328f225e 369depending on your server configuration, it may be incorrect. See $req->secure
370for more info.
c436c1e8 371
3e19f4f6 372If your application was queried with the URI
373C<http://localhost:3000/some/path> then C<base> is C<http://localhost:3000/>.
fc7ec1d9 374
b5ecfcf0 375=head2 $req->body
06e1b616 376
843871cf 377Returns the message body of the request, as returned by L<HTTP::Body>: a string,
378unless Content-Type is C<application/x-www-form-urlencoded>, C<text/xml>, or
379C<multipart/form-data>, in which case a L<File::Temp> object is returned.
e060fe05 380
b5ecfcf0 381=head2 $req->body_parameters
fbcc39ad 382
3e19f4f6 383Returns a reference to a hash containing body (POST) parameters. Values can
fbcc39ad 384be either a scalar or an arrayref containing scalars.
385
386 print $c->request->body_parameters->{field};
387 print $c->request->body_parameters->{field}->[0];
c436c1e8 388
d631b5f9 389These are the parameters from the POST part of the request, if any.
e5ecd5bc 390
b5ecfcf0 391=head2 $req->body_params
fbcc39ad 392
3e19f4f6 393Shortcut for body_parameters.
fbcc39ad 394
b5ecfcf0 395=head2 $req->content_encoding
b5176d9e 396
3e19f4f6 397Shortcut for $req->headers->content_encoding.
b5176d9e 398
b5ecfcf0 399=head2 $req->content_length
b5176d9e 400
3e19f4f6 401Shortcut for $req->headers->content_length.
b5176d9e 402
b5ecfcf0 403=head2 $req->content_type
b5176d9e 404
3e19f4f6 405Shortcut for $req->headers->content_type.
b5176d9e 406
b5ecfcf0 407=head2 $req->cookie
3ad654e0 408
3e19f4f6 409A convenient method to access $req->cookies.
3ad654e0 410
411 $cookie = $c->request->cookie('name');
412 @cookies = $c->request->cookie;
413
414=cut
415
416sub cookie {
417 my $self = shift;
418
419 if ( @_ == 0 ) {
b77e7869 420 return keys %{ $self->cookies };
3ad654e0 421 }
422
423 if ( @_ == 1 ) {
424
425 my $name = shift;
426
b77e7869 427 unless ( exists $self->cookies->{$name} ) {
3ad654e0 428 return undef;
429 }
fbcc39ad 430
b77e7869 431 return $self->cookies->{$name};
3ad654e0 432 }
433}
434
b5ecfcf0 435=head2 $req->cookies
fc7ec1d9 436
b22c6668 437Returns a reference to a hash containing the cookies.
fc7ec1d9 438
439 print $c->request->cookies->{mycookie}->value;
440
7e743798 441The cookies in the hash are indexed by name, and the values are L<CGI::Simple::Cookie>
c436c1e8 442objects.
443
b5ecfcf0 444=head2 $req->header
b5176d9e 445
3e19f4f6 446Shortcut for $req->headers->header.
b5176d9e 447
b5ecfcf0 448=head2 $req->headers
fc7ec1d9 449
3e19f4f6 450Returns an L<HTTP::Headers> object containing the headers for the current request.
fc7ec1d9 451
452 print $c->request->headers->header('X-Catalyst');
453
b5ecfcf0 454=head2 $req->hostname
0556eb49 455
178dca5f 456Returns the hostname of the client. Use C<< $req->uri->host >> to get the hostname of the server.
e5ecd5bc 457
b5ecfcf0 458=head2 $req->input
61bacdcc 459
3e19f4f6 460Alias for $req->body.
61bacdcc 461
3b4d1251 462=head2 $req->query_keywords
463
464Contains the keywords portion of a query string, when no '=' signs are
465present.
466
467 http://localhost/path?some+keywords
b0ad47c1 468
3b4d1251 469 $c->request->query_keywords will contain 'some keywords'
470
b5ecfcf0 471=head2 $req->match
fc7ec1d9 472
3e19f4f6 473This contains the matching part of a Regex action. Otherwise
2c83fd5a 474it returns the same as 'action', except for default actions,
475which return an empty string.
fc7ec1d9 476
b5ecfcf0 477=head2 $req->method
b5176d9e 478
479Contains the request method (C<GET>, C<POST>, C<HEAD>, etc).
480
b5ecfcf0 481=head2 $req->param
e7c0c583 482
b0ad47c1 483Returns GET and POST parameters with a CGI.pm-compatible param method. This
3e19f4f6 484is an alternative method for accessing parameters in $c->req->parameters.
e7c0c583 485
a82c2894 486 $value = $c->request->param( 'foo' );
487 @values = $c->request->param( 'foo' );
e7c0c583 488 @params = $c->request->param;
489
3e705254 490Like L<CGI>, and B<unlike> earlier versions of Catalyst, passing multiple
a82c2894 491arguments to this method, like this:
492
85d9fce6 493 $c->request->param( 'foo', 'bar', 'gorch', 'quxx' );
a82c2894 494
495will set the parameter C<foo> to the multiple values C<bar>, C<gorch> and
496C<quxx>. Previously this would have added C<bar> as another value to C<foo>
3e19f4f6 497(creating it if it didn't exist before), and C<quxx> as another value for
498C<gorch>.
a82c2894 499
83312afd 500B<NOTE> this is considered a legacy interface and care should be taken when
501using it. C<< scalar $c->req->param( 'foo' ) >> will return only the first
502C<foo> param even if multiple are present; C<< $c->req->param( 'foo' ) >> will
503return a list of as many are present, which can have unexpected consequences
504when writing code of the form:
505
506 $foo->bar(
507 a => 'b',
508 baz => $c->req->param( 'baz' ),
509 );
510
511If multiple C<baz> parameters are provided this code might corrupt data or
512cause a hash initialization error. For a more straightforward interface see
513C<< $c->req->parameters >>.
514
e7c0c583 515=cut
516
517sub param {
518 my $self = shift;
519
520 if ( @_ == 0 ) {
521 return keys %{ $self->parameters };
522 }
523
bfde09a2 524 if ( @_ == 1 ) {
e7c0c583 525
bfde09a2 526 my $param = shift;
6bd2b72c 527
bfde09a2 528 unless ( exists $self->parameters->{$param} ) {
529 return wantarray ? () : undef;
530 }
531
532 if ( ref $self->parameters->{$param} eq 'ARRAY' ) {
533 return (wantarray)
534 ? @{ $self->parameters->{$param} }
535 : $self->parameters->{$param}->[0];
536 }
537 else {
538 return (wantarray)
539 ? ( $self->parameters->{$param} )
540 : $self->parameters->{$param};
541 }
d7945f32 542 }
a82c2894 543 elsif ( @_ > 1 ) {
544 my $field = shift;
545 $self->parameters->{$field} = [@_];
d7945f32 546 }
e7c0c583 547}
b5176d9e 548
b5ecfcf0 549=head2 $req->parameters
61b1e958 550
3e19f4f6 551Returns a reference to a hash containing GET and POST parameters. Values can
d08ced28 552be either a scalar or an arrayref containing scalars.
fc7ec1d9 553
e7c0c583 554 print $c->request->parameters->{field};
555 print $c->request->parameters->{field}->[0];
fc7ec1d9 556
c436c1e8 557This is the combination of C<query_parameters> and C<body_parameters>.
558
b5ecfcf0 559=head2 $req->params
3e19f4f6 560
561Shortcut for $req->parameters.
562
b5ecfcf0 563=head2 $req->path
fc7ec1d9 564
3e19f4f6 565Returns the path, i.e. the part of the URI after $req->base, for the current request.
fc7ec1d9 566
be6801fa 567 http://localhost/path/foo
568
569 $c->request->path will contain 'path/foo'
570
b5ecfcf0 571=head2 $req->path_info
fbcc39ad 572
10011c19 573Alias for path, added for compatibility with L<CGI>.
fbcc39ad 574
575=cut
576
577sub path {
02fb5d78 578 my ( $self, @params ) = @_;
4f5ebacd 579
02fb5d78 580 if (@params) {
581 $self->uri->path(@params);
02570318 582 $self->_clear_path;
fbcc39ad 583 }
02570318 584 elsif ( $self->_has_path ) {
585 return $self->_path;
e561386f 586 }
02fb5d78 587 else {
588 my $path = $self->uri->path;
589 my $location = $self->base->path;
590 $path =~ s/^(\Q$location\E)?//;
591 $path =~ s/^\///;
02570318 592 $self->_path($path);
fbcc39ad 593
02fb5d78 594 return $path;
595 }
fbcc39ad 596}
597
b5ecfcf0 598=head2 $req->protocol
bfde09a2 599
3e19f4f6 600Returns the protocol (HTTP/1.0 or HTTP/1.1) used for the current request.
bfde09a2 601
b5ecfcf0 602=head2 $req->query_parameters
fbcc39ad 603
def54ce2 604=head2 $req->query_params
605
3e19f4f6 606Returns a reference to a hash containing query string (GET) parameters. Values can
fbcc39ad 607be either a scalar or an arrayref containing scalars.
608
609 print $c->request->query_parameters->{field};
610 print $c->request->query_parameters->{field}->[0];
b0ad47c1 611
b5ecfcf0 612=head2 $req->read( [$maxlength] )
fbcc39ad 613
3e19f4f6 614Reads a chunk of data from the request body. This method is intended to be
615used in a while loop, reading $maxlength bytes on every call. $maxlength
fbcc39ad 616defaults to the size of the request if not specified.
617
87f50436 618=head2 $req->read_chunk(\$buff, $max)
619
620Reads a chunk..
621
9779c885 622You have to set MyApp->config(parse_on_demand => 1) to use this directly.
fbcc39ad 623
b5ecfcf0 624=head2 $req->referer
fc7ec1d9 625
3e19f4f6 626Shortcut for $req->headers->referer. Returns the referring page.
fc7ec1d9 627
b5ecfcf0 628=head2 $req->secure
bfde09a2 629
328f225e 630Returns true or false, indicating whether the connection is secure
f4dda4a8 631(https). Note that the URI scheme (e.g., http vs. https) must be determined
ae7da8f5 632through heuristics, and therefore the reliability of $req->secure will depend
328f225e 633on your server configuration. If you are serving secure pages on the standard
634SSL port (443) and/or setting the HTTPS environment variable, $req->secure
635should be valid.
bfde09a2 636
2982e768 637=head2 $req->captures
638
5c6a56e0 639Returns a reference to an array containing captured args from chained
640actions or regex captures.
fc7ec1d9 641
2982e768 642 my @captures = @{ $c->request->captures };
643
644=head2 $req->snippets
fc7ec1d9 645
10011c19 646C<captures> used to be called snippets. This is still available for backwards
2982e768 647compatibility, but is considered deprecated.
fc7ec1d9 648
b5ecfcf0 649=head2 $req->upload
e7c0c583 650
3e19f4f6 651A convenient method to access $req->uploads.
e7c0c583 652
653 $upload = $c->request->upload('field');
654 @uploads = $c->request->upload('field');
655 @fields = $c->request->upload;
bfde09a2 656
e7c0c583 657 for my $upload ( $c->request->upload('field') ) {
146554c5 658 print $upload->filename;
e7c0c583 659 }
660
661=cut
662
663sub upload {
664 my $self = shift;
665
666 if ( @_ == 0 ) {
667 return keys %{ $self->uploads };
668 }
669
bfde09a2 670 if ( @_ == 1 ) {
e7c0c583 671
bfde09a2 672 my $upload = shift;
673
674 unless ( exists $self->uploads->{$upload} ) {
675 return wantarray ? () : undef;
676 }
6bd2b72c 677
bfde09a2 678 if ( ref $self->uploads->{$upload} eq 'ARRAY' ) {
679 return (wantarray)
680 ? @{ $self->uploads->{$upload} }
681 : $self->uploads->{$upload}->[0];
682 }
683 else {
684 return (wantarray)
fbcc39ad 685 ? ( $self->uploads->{$upload} )
686 : $self->uploads->{$upload};
bfde09a2 687 }
d7945f32 688 }
bfde09a2 689
a4f5c51e 690 if ( @_ > 1 ) {
bfde09a2 691
692 while ( my ( $field, $upload ) = splice( @_, 0, 2 ) ) {
693
694 if ( exists $self->uploads->{$field} ) {
695 for ( $self->uploads->{$field} ) {
696 $_ = [$_] unless ref($_) eq "ARRAY";
697 push( @$_, $upload );
698 }
699 }
700 else {
701 $self->uploads->{$field} = $upload;
702 }
703 }
e7c0c583 704 }
705}
706
b5ecfcf0 707=head2 $req->uploads
fc7ec1d9 708
bfde09a2 709Returns a reference to a hash containing uploads. Values can be either a
b0ad47c1 710L<Catalyst::Request::Upload> object, or an arrayref of
84e7aa89 711L<Catalyst::Request::Upload> objects.
e7c0c583 712
713 my $upload = $c->request->uploads->{field};
714 my $upload = $c->request->uploads->{field}->[0];
715
b5ecfcf0 716=head2 $req->uri
fbcc39ad 717
d26ee0d0 718Returns a L<URI> object for the current request. Stringifies to the URI text.
fbcc39ad 719
a375a206 720=head2 $req->mangle_params( { key => 'value' }, $appendmode);
bd917b94 721
a375a206 722Returns a hashref of parameters stemming from the current request's params,
723plus the ones supplied. Keys for which no current param exists will be
724added, keys with undefined values will be removed and keys with existing
725params will be replaced. Note that you can supply a true value as the final
726argument to change behavior with regards to existing parameters, appending
727values rather than replacing them.
728
729A quick example:
730
731 # URI query params foo=1
732 my $hashref = $req->mangle_params({ foo => 2 });
733 # Result is query params of foo=2
734
735versus append mode:
736
737 # URI query params foo=1
738 my $hashref = $req->mangle_params({ foo => 2 }, 1);
739 # Result is query params of foo=1&foo=2
740
741This is the code behind C<uri_with>.
bd917b94 742
743=cut
744
a375a206 745sub mangle_params {
746 my ($self, $args, $append) = @_;
b0ad47c1 747
a375a206 748 carp('No arguments passed to mangle_params()') unless $args;
fbb513f7 749
2f381252 750 foreach my $value ( values %$args ) {
d0f0fcf6 751 next unless defined $value;
fbb513f7 752 for ( ref $value eq 'ARRAY' ? @$value : $value ) {
753 $_ = "$_";
7066a4d5 754 utf8::encode( $_ ) if utf8::is_utf8($_);
fc42a730 755 }
fc42a730 756 };
b0ad47c1 757
a375a206 758 my %params = %{ $self->uri->query_form_hash };
759 foreach my $key (keys %{ $args }) {
760 my $val = $args->{$key};
761 if(defined($val)) {
762
763 if($append && exists($params{$key})) {
764
765 # This little bit of heaven handles appending a new value onto
766 # an existing one regardless if the existing value is an array
767 # or not, and regardless if the new value is an array or not
768 $params{$key} = [
769 ref($params{$key}) eq 'ARRAY' ? @{ $params{$key} } : $params{$key},
770 ref($val) eq 'ARRAY' ? @{ $val } : $val
771 ];
772
773 } else {
774 $params{$key} = $val;
775 }
776 } else {
777
778 # If the param wasn't defined then we delete it.
779 delete($params{$key});
780 }
781 }
782
783
784 return \%params;
785}
786
787=head2 $req->uri_with( { key => 'value' } );
788
789Returns a rewritten URI object for the current request. Key/value pairs
790passed in will override existing parameters. You can remove an existing
791parameter by passing in an undef value. Unmodified pairs will be
792preserved.
793
794You may also pass an optional second parameter that puts C<uri_with> into
795append mode:
796
797 $req->uri_with( { key => 'value' }, { mode => 'append' } );
9779c885 798
a375a206 799See C<mangle_params> for an explanation of this behavior.
800
801=cut
802
803sub uri_with {
804 my( $self, $args, $behavior) = @_;
805
806 carp( 'No arguments passed to uri_with()' ) unless $args;
807
808 my $append = 0;
809 if((ref($behavior) eq 'HASH') && defined($behavior->{mode}) && ($behavior->{mode} eq 'append')) {
810 $append = 1;
811 }
812
813 my $params = $self->mangle_params($args, $append);
814
815 my $uri = $self->uri->clone;
816 $uri->query_form($params);
2f381252 817
bd917b94 818 return $uri;
819}
820
8026359e 821=head2 $req->remote_user
822
823Returns the value of the C<REMOTE_USER> environment variable.
7ce7ca2e 824
b5ecfcf0 825=head2 $req->user_agent
b5176d9e 826
3e19f4f6 827Shortcut to $req->headers->user_agent. Returns the user agent (browser)
828version string.
b5176d9e 829
059c085b 830=head2 meta
831
832Provided by Moose
833
3e19f4f6 834=head1 AUTHORS
fc7ec1d9 835
2f381252 836Catalyst Contributors, see Catalyst.pm
fc7ec1d9 837
838=head1 COPYRIGHT
839
536bee89 840This library is free software. You can redistribute it and/or modify
61b1e958 841it under the same terms as Perl itself.
fc7ec1d9 842
843=cut
844
e5ecd5bc 845__PACKAGE__->meta->make_immutable;
846
fc7ec1d9 8471;