Start re-arranging and fixing docs. remove docs for deprecated stuff
[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
767480fd 28has action => (is => 'rw'); # XXX Deprecated - warn?
5fb12dbb 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
7c1c4dc6 84has _log => (
85 is => 'ro',
86 weak_ref => 1,
87 required => 1,
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 ) {
7c1c4dc6 268 $self->_log->warn(
e99ec2dc 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;
767480fd 334 $req->address eq "127.0.0.1";
b22c6668 335 $req->arguments;
3e19f4f6 336 $req->args;
b22c6668 337 $req->base;
06e1b616 338 $req->body;
fbcc39ad 339 $req->body_parameters;
b5176d9e 340 $req->content_encoding;
341 $req->content_length;
342 $req->content_type;
b77e7869 343 $req->cookie;
b22c6668 344 $req->cookies;
b5176d9e 345 $req->header;
b22c6668 346 $req->headers;
347 $req->hostname;
61bacdcc 348 $req->input;
3b4d1251 349 $req->query_keywords;
b22c6668 350 $req->match;
351 $req->method;
e7c0c583 352 $req->param;
e7c0c583 353 $req->parameters;
3e19f4f6 354 $req->params;
b22c6668 355 $req->path;
bfde09a2 356 $req->protocol;
fbcc39ad 357 $req->query_parameters;
358 $req->read;
b5176d9e 359 $req->referer;
bfde09a2 360 $req->secure;
2982e768 361 $req->captures; # previously knows as snippets
e7c0c583 362 $req->upload;
b22c6668 363 $req->uploads;
77d12cae 364 $req->uri;
7ce7ca2e 365 $req->user;
66294129 366 $req->user_agent;
b22c6668 367
3e22baa5 368See also L<Catalyst>, L<Catalyst::Request::Upload>.
fc7ec1d9 369
370=head1 DESCRIPTION
371
3e19f4f6 372This is the Catalyst Request class, which provides an interface to data for the
373current client request. The request object is prepared by L<Catalyst::Engine>,
374thus hiding the details of the particular engine implementation.
b22c6668 375
376=head1 METHODS
fc7ec1d9 377
b5ecfcf0 378=head2 $req->address
0556eb49 379
3e19f4f6 380Returns the IP address of the client.
61b1e958 381
b5ecfcf0 382=head2 $req->arguments
61b1e958 383
b22c6668 384Returns a reference to an array containing the arguments.
fc7ec1d9 385
386 print $c->request->arguments->[0];
387
c436c1e8 388For example, if your action was
389
7d7519a4 390 package MyApp::Controller::Foo;
85d9fce6 391
392 sub moose : Local {
393 ...
394 }
c436c1e8 395
3e19f4f6 396and the URI for the request was C<http://.../foo/moose/bah>, the string C<bah>
c436c1e8 397would be the first and only argument.
398
6d920953 399Arguments get automatically URI-unescaped for you.
8f58057d 400
b5ecfcf0 401=head2 $req->args
3e19f4f6 402
01011731 403Shortcut for L</arguments>.
3e19f4f6 404
b5ecfcf0 405=head2 $req->base
fc7ec1d9 406
328f225e 407Contains the URI base. This will always have a trailing slash. Note that the
f4dda4a8 408URI scheme (e.g., http vs. https) must be determined through heuristics;
328f225e 409depending on your server configuration, it may be incorrect. See $req->secure
410for more info.
c436c1e8 411
3e19f4f6 412If your application was queried with the URI
413C<http://localhost:3000/some/path> then C<base> is C<http://localhost:3000/>.
fc7ec1d9 414
b5ecfcf0 415=head2 $req->body
06e1b616 416
843871cf 417Returns the message body of the request, as returned by L<HTTP::Body>: a string,
418unless Content-Type is C<application/x-www-form-urlencoded>, C<text/xml>, or
419C<multipart/form-data>, in which case a L<File::Temp> object is returned.
e060fe05 420
b5ecfcf0 421=head2 $req->body_parameters
fbcc39ad 422
3e19f4f6 423Returns a reference to a hash containing body (POST) parameters. Values can
fbcc39ad 424be either a scalar or an arrayref containing scalars.
425
426 print $c->request->body_parameters->{field};
427 print $c->request->body_parameters->{field}->[0];
c436c1e8 428
d631b5f9 429These are the parameters from the POST part of the request, if any.
e5ecd5bc 430
b5ecfcf0 431=head2 $req->body_params
fbcc39ad 432
3e19f4f6 433Shortcut for body_parameters.
fbcc39ad 434
b5ecfcf0 435=head2 $req->content_encoding
b5176d9e 436
3e19f4f6 437Shortcut for $req->headers->content_encoding.
b5176d9e 438
b5ecfcf0 439=head2 $req->content_length
b5176d9e 440
3e19f4f6 441Shortcut for $req->headers->content_length.
b5176d9e 442
b5ecfcf0 443=head2 $req->content_type
b5176d9e 444
3e19f4f6 445Shortcut for $req->headers->content_type.
b5176d9e 446
b5ecfcf0 447=head2 $req->cookie
3ad654e0 448
3e19f4f6 449A convenient method to access $req->cookies.
3ad654e0 450
451 $cookie = $c->request->cookie('name');
452 @cookies = $c->request->cookie;
453
454=cut
455
456sub cookie {
457 my $self = shift;
458
459 if ( @_ == 0 ) {
b77e7869 460 return keys %{ $self->cookies };
3ad654e0 461 }
462
463 if ( @_ == 1 ) {
464
465 my $name = shift;
466
b77e7869 467 unless ( exists $self->cookies->{$name} ) {
3ad654e0 468 return undef;
469 }
fbcc39ad 470
b77e7869 471 return $self->cookies->{$name};
3ad654e0 472 }
473}
474
b5ecfcf0 475=head2 $req->cookies
fc7ec1d9 476
b22c6668 477Returns a reference to a hash containing the cookies.
fc7ec1d9 478
479 print $c->request->cookies->{mycookie}->value;
480
7e743798 481The cookies in the hash are indexed by name, and the values are L<CGI::Simple::Cookie>
c436c1e8 482objects.
483
b5ecfcf0 484=head2 $req->header
b5176d9e 485
3e19f4f6 486Shortcut for $req->headers->header.
b5176d9e 487
b5ecfcf0 488=head2 $req->headers
fc7ec1d9 489
3e19f4f6 490Returns an L<HTTP::Headers> object containing the headers for the current request.
fc7ec1d9 491
492 print $c->request->headers->header('X-Catalyst');
493
b5ecfcf0 494=head2 $req->hostname
0556eb49 495
178dca5f 496Returns the hostname of the client. Use C<< $req->uri->host >> to get the hostname of the server.
e5ecd5bc 497
b5ecfcf0 498=head2 $req->input
61bacdcc 499
3e19f4f6 500Alias for $req->body.
61bacdcc 501
3b4d1251 502=head2 $req->query_keywords
503
504Contains the keywords portion of a query string, when no '=' signs are
505present.
506
507 http://localhost/path?some+keywords
b0ad47c1 508
3b4d1251 509 $c->request->query_keywords will contain 'some keywords'
510
b5ecfcf0 511=head2 $req->match
fc7ec1d9 512
3e19f4f6 513This contains the matching part of a Regex action. Otherwise
2c83fd5a 514it returns the same as 'action', except for default actions,
515which return an empty string.
fc7ec1d9 516
b5ecfcf0 517=head2 $req->method
b5176d9e 518
519Contains the request method (C<GET>, C<POST>, C<HEAD>, etc).
520
b5ecfcf0 521=head2 $req->param
e7c0c583 522
b0ad47c1 523Returns GET and POST parameters with a CGI.pm-compatible param method. This
3e19f4f6 524is an alternative method for accessing parameters in $c->req->parameters.
e7c0c583 525
a82c2894 526 $value = $c->request->param( 'foo' );
527 @values = $c->request->param( 'foo' );
e7c0c583 528 @params = $c->request->param;
529
3e705254 530Like L<CGI>, and B<unlike> earlier versions of Catalyst, passing multiple
a82c2894 531arguments to this method, like this:
532
85d9fce6 533 $c->request->param( 'foo', 'bar', 'gorch', 'quxx' );
a82c2894 534
535will set the parameter C<foo> to the multiple values C<bar>, C<gorch> and
536C<quxx>. Previously this would have added C<bar> as another value to C<foo>
3e19f4f6 537(creating it if it didn't exist before), and C<quxx> as another value for
538C<gorch>.
a82c2894 539
83312afd 540B<NOTE> this is considered a legacy interface and care should be taken when
541using it. C<< scalar $c->req->param( 'foo' ) >> will return only the first
542C<foo> param even if multiple are present; C<< $c->req->param( 'foo' ) >> will
543return a list of as many are present, which can have unexpected consequences
544when writing code of the form:
545
546 $foo->bar(
547 a => 'b',
548 baz => $c->req->param( 'baz' ),
549 );
550
551If multiple C<baz> parameters are provided this code might corrupt data or
552cause a hash initialization error. For a more straightforward interface see
553C<< $c->req->parameters >>.
554
e7c0c583 555=cut
556
557sub param {
558 my $self = shift;
559
560 if ( @_ == 0 ) {
561 return keys %{ $self->parameters };
562 }
563
bfde09a2 564 if ( @_ == 1 ) {
e7c0c583 565
bfde09a2 566 my $param = shift;
6bd2b72c 567
bfde09a2 568 unless ( exists $self->parameters->{$param} ) {
569 return wantarray ? () : undef;
570 }
571
572 if ( ref $self->parameters->{$param} eq 'ARRAY' ) {
573 return (wantarray)
574 ? @{ $self->parameters->{$param} }
575 : $self->parameters->{$param}->[0];
576 }
577 else {
578 return (wantarray)
579 ? ( $self->parameters->{$param} )
580 : $self->parameters->{$param};
581 }
d7945f32 582 }
a82c2894 583 elsif ( @_ > 1 ) {
584 my $field = shift;
585 $self->parameters->{$field} = [@_];
d7945f32 586 }
e7c0c583 587}
b5176d9e 588
b5ecfcf0 589=head2 $req->parameters
61b1e958 590
3e19f4f6 591Returns a reference to a hash containing GET and POST parameters. Values can
d08ced28 592be either a scalar or an arrayref containing scalars.
fc7ec1d9 593
e7c0c583 594 print $c->request->parameters->{field};
595 print $c->request->parameters->{field}->[0];
fc7ec1d9 596
c436c1e8 597This is the combination of C<query_parameters> and C<body_parameters>.
598
b5ecfcf0 599=head2 $req->params
3e19f4f6 600
601Shortcut for $req->parameters.
602
b5ecfcf0 603=head2 $req->path
fc7ec1d9 604
3e19f4f6 605Returns the path, i.e. the part of the URI after $req->base, for the current request.
fc7ec1d9 606
be6801fa 607 http://localhost/path/foo
608
609 $c->request->path will contain 'path/foo'
610
b5ecfcf0 611=head2 $req->path_info
fbcc39ad 612
10011c19 613Alias for path, added for compatibility with L<CGI>.
fbcc39ad 614
615=cut
616
617sub path {
02fb5d78 618 my ( $self, @params ) = @_;
4f5ebacd 619
02fb5d78 620 if (@params) {
621 $self->uri->path(@params);
02570318 622 $self->_clear_path;
fbcc39ad 623 }
02570318 624 elsif ( $self->_has_path ) {
625 return $self->_path;
e561386f 626 }
02fb5d78 627 else {
628 my $path = $self->uri->path;
629 my $location = $self->base->path;
630 $path =~ s/^(\Q$location\E)?//;
631 $path =~ s/^\///;
02570318 632 $self->_path($path);
fbcc39ad 633
02fb5d78 634 return $path;
635 }
fbcc39ad 636}
637
b5ecfcf0 638=head2 $req->protocol
bfde09a2 639
3e19f4f6 640Returns the protocol (HTTP/1.0 or HTTP/1.1) used for the current request.
bfde09a2 641
b5ecfcf0 642=head2 $req->query_parameters
fbcc39ad 643
def54ce2 644=head2 $req->query_params
645
3e19f4f6 646Returns a reference to a hash containing query string (GET) parameters. Values can
fbcc39ad 647be either a scalar or an arrayref containing scalars.
648
649 print $c->request->query_parameters->{field};
650 print $c->request->query_parameters->{field}->[0];
b0ad47c1 651
b5ecfcf0 652=head2 $req->read( [$maxlength] )
fbcc39ad 653
3e19f4f6 654Reads a chunk of data from the request body. This method is intended to be
655used in a while loop, reading $maxlength bytes on every call. $maxlength
fbcc39ad 656defaults to the size of the request if not specified.
657
87f50436 658=head2 $req->read_chunk(\$buff, $max)
659
660Reads a chunk..
661
9779c885 662You have to set MyApp->config(parse_on_demand => 1) to use this directly.
fbcc39ad 663
b5ecfcf0 664=head2 $req->referer
fc7ec1d9 665
3e19f4f6 666Shortcut for $req->headers->referer. Returns the referring page.
fc7ec1d9 667
b5ecfcf0 668=head2 $req->secure
bfde09a2 669
328f225e 670Returns true or false, indicating whether the connection is secure
f4dda4a8 671(https). Note that the URI scheme (e.g., http vs. https) must be determined
ae7da8f5 672through heuristics, and therefore the reliability of $req->secure will depend
328f225e 673on your server configuration. If you are serving secure pages on the standard
674SSL port (443) and/or setting the HTTPS environment variable, $req->secure
675should be valid.
bfde09a2 676
2982e768 677=head2 $req->captures
678
5c6a56e0 679Returns a reference to an array containing captured args from chained
680actions or regex captures.
fc7ec1d9 681
2982e768 682 my @captures = @{ $c->request->captures };
683
684=head2 $req->snippets
fc7ec1d9 685
10011c19 686C<captures> used to be called snippets. This is still available for backwards
2982e768 687compatibility, but is considered deprecated.
fc7ec1d9 688
b5ecfcf0 689=head2 $req->upload
e7c0c583 690
3e19f4f6 691A convenient method to access $req->uploads.
e7c0c583 692
693 $upload = $c->request->upload('field');
694 @uploads = $c->request->upload('field');
695 @fields = $c->request->upload;
bfde09a2 696
e7c0c583 697 for my $upload ( $c->request->upload('field') ) {
146554c5 698 print $upload->filename;
e7c0c583 699 }
700
701=cut
702
703sub upload {
704 my $self = shift;
705
706 if ( @_ == 0 ) {
707 return keys %{ $self->uploads };
708 }
709
bfde09a2 710 if ( @_ == 1 ) {
e7c0c583 711
bfde09a2 712 my $upload = shift;
713
714 unless ( exists $self->uploads->{$upload} ) {
715 return wantarray ? () : undef;
716 }
6bd2b72c 717
bfde09a2 718 if ( ref $self->uploads->{$upload} eq 'ARRAY' ) {
719 return (wantarray)
720 ? @{ $self->uploads->{$upload} }
721 : $self->uploads->{$upload}->[0];
722 }
723 else {
724 return (wantarray)
fbcc39ad 725 ? ( $self->uploads->{$upload} )
726 : $self->uploads->{$upload};
bfde09a2 727 }
d7945f32 728 }
bfde09a2 729
a4f5c51e 730 if ( @_ > 1 ) {
bfde09a2 731
732 while ( my ( $field, $upload ) = splice( @_, 0, 2 ) ) {
733
734 if ( exists $self->uploads->{$field} ) {
735 for ( $self->uploads->{$field} ) {
736 $_ = [$_] unless ref($_) eq "ARRAY";
737 push( @$_, $upload );
738 }
739 }
740 else {
741 $self->uploads->{$field} = $upload;
742 }
743 }
e7c0c583 744 }
745}
746
b5ecfcf0 747=head2 $req->uploads
fc7ec1d9 748
bfde09a2 749Returns a reference to a hash containing uploads. Values can be either a
b0ad47c1 750L<Catalyst::Request::Upload> object, or an arrayref of
84e7aa89 751L<Catalyst::Request::Upload> objects.
e7c0c583 752
753 my $upload = $c->request->uploads->{field};
754 my $upload = $c->request->uploads->{field}->[0];
755
b5ecfcf0 756=head2 $req->uri
fbcc39ad 757
d26ee0d0 758Returns a L<URI> object for the current request. Stringifies to the URI text.
fbcc39ad 759
a375a206 760=head2 $req->mangle_params( { key => 'value' }, $appendmode);
bd917b94 761
a375a206 762Returns a hashref of parameters stemming from the current request's params,
763plus the ones supplied. Keys for which no current param exists will be
764added, keys with undefined values will be removed and keys with existing
765params will be replaced. Note that you can supply a true value as the final
766argument to change behavior with regards to existing parameters, appending
767values rather than replacing them.
768
769A quick example:
770
771 # URI query params foo=1
772 my $hashref = $req->mangle_params({ foo => 2 });
773 # Result is query params of foo=2
774
775versus append mode:
776
777 # URI query params foo=1
778 my $hashref = $req->mangle_params({ foo => 2 }, 1);
779 # Result is query params of foo=1&foo=2
780
781This is the code behind C<uri_with>.
bd917b94 782
783=cut
784
a375a206 785sub mangle_params {
786 my ($self, $args, $append) = @_;
b0ad47c1 787
a375a206 788 carp('No arguments passed to mangle_params()') unless $args;
fbb513f7 789
2f381252 790 foreach my $value ( values %$args ) {
d0f0fcf6 791 next unless defined $value;
fbb513f7 792 for ( ref $value eq 'ARRAY' ? @$value : $value ) {
793 $_ = "$_";
7066a4d5 794 utf8::encode( $_ ) if utf8::is_utf8($_);
fc42a730 795 }
fc42a730 796 };
b0ad47c1 797
a375a206 798 my %params = %{ $self->uri->query_form_hash };
799 foreach my $key (keys %{ $args }) {
800 my $val = $args->{$key};
801 if(defined($val)) {
802
803 if($append && exists($params{$key})) {
804
805 # This little bit of heaven handles appending a new value onto
806 # an existing one regardless if the existing value is an array
807 # or not, and regardless if the new value is an array or not
808 $params{$key} = [
809 ref($params{$key}) eq 'ARRAY' ? @{ $params{$key} } : $params{$key},
810 ref($val) eq 'ARRAY' ? @{ $val } : $val
811 ];
812
813 } else {
814 $params{$key} = $val;
815 }
816 } else {
817
818 # If the param wasn't defined then we delete it.
819 delete($params{$key});
820 }
821 }
822
823
824 return \%params;
825}
826
827=head2 $req->uri_with( { key => 'value' } );
828
829Returns a rewritten URI object for the current request. Key/value pairs
830passed in will override existing parameters. You can remove an existing
831parameter by passing in an undef value. Unmodified pairs will be
832preserved.
833
834You may also pass an optional second parameter that puts C<uri_with> into
835append mode:
836
837 $req->uri_with( { key => 'value' }, { mode => 'append' } );
9779c885 838
a375a206 839See C<mangle_params> for an explanation of this behavior.
840
841=cut
842
843sub uri_with {
844 my( $self, $args, $behavior) = @_;
845
846 carp( 'No arguments passed to uri_with()' ) unless $args;
847
848 my $append = 0;
849 if((ref($behavior) eq 'HASH') && defined($behavior->{mode}) && ($behavior->{mode} eq 'append')) {
850 $append = 1;
851 }
852
853 my $params = $self->mangle_params($args, $append);
854
855 my $uri = $self->uri->clone;
856 $uri->query_form($params);
2f381252 857
bd917b94 858 return $uri;
859}
860
8026359e 861=head2 $req->remote_user
862
863Returns the value of the C<REMOTE_USER> environment variable.
7ce7ca2e 864
b5ecfcf0 865=head2 $req->user_agent
b5176d9e 866
3e19f4f6 867Shortcut to $req->headers->user_agent. Returns the user agent (browser)
868version string.
b5176d9e 869
059c085b 870=head2 meta
871
872Provided by Moose
873
3e19f4f6 874=head1 AUTHORS
fc7ec1d9 875
2f381252 876Catalyst Contributors, see Catalyst.pm
fc7ec1d9 877
878=head1 COPYRIGHT
879
536bee89 880This library is free software. You can redistribute it and/or modify
61b1e958 881it under the same terms as Perl itself.
fc7ec1d9 882
883=cut
884
e5ecd5bc 885__PACKAGE__->meta->make_immutable;
886
fc7ec1d9 8871;