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