Fix the return value of Catalyst::Request's body method + tests.
[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
b99ff5d8 13with 'MooseX::Emulate::Class::Accessor::Fast';
14
5fb12dbb 15has action => (is => 'rw');
16has address => (is => 'rw');
17has arguments => (is => 'rw', default => sub { [] });
18has cookies => (is => 'rw', default => sub { {} });
19has query_keywords => (is => 'rw');
20has match => (is => 'rw');
21has method => (is => 'rw');
22has protocol => (is => 'rw');
059c085b 23has query_parameters => (is => 'rw', default => sub { {} });
5fb12dbb 24has secure => (is => 'rw', default => 0);
25has captures => (is => 'rw', default => sub { [] });
6cb9e383 26has uri => (is => 'rw', predicate => 'has_uri');
5fb12dbb 27has user => (is => 'rw');
28has headers => (
e5ecd5bc 29 is => 'rw',
059c085b 30 isa => 'HTTP::Headers',
31 handles => [qw(content_encoding content_length content_type header referer user_agent)],
6680c772 32 default => sub { HTTP::Headers->new() },
33 required => 1,
34 lazy => 1,
059c085b 35);
36
02570318 37# Moose TODO:
38# - Can we lose the before modifiers which just call prepare_body ?
39# they are wasteful, slow us down and feel cluttery.
0fc2d522 40# Can we call prepare_body at BUILD time?
02570318 41# Can we make _body an attribute, have the rest of
42# these lazy build from there and kill all the direct hash access
43# in Catalyst.pm and Engine.pm?
0fc2d522 44
059c085b 45has _context => (
46 is => 'rw',
47 weak_ref => 1,
0fc2d522 48 handles => ['read'],
02570318 49 clearer => '_clear_context',
059c085b 50);
51
52has body_parameters => (
5fb12dbb 53 is => 'rw',
54 required => 1,
55 lazy => 1,
56 default => sub { {} },
fc7ec1d9 57);
58
059c085b 59before body_parameters => sub {
60 my ($self) = @_;
61 $self->_context->prepare_body();
62};
63
64has uploads => (
5fb12dbb 65 is => 'rw',
66 required => 1,
67 lazy => 1,
68 default => sub { {} },
059c085b 69);
70
059c085b 71has parameters => (
72 is => 'rw',
73 required => 1,
74 lazy => 1,
75 default => sub { {} },
76);
77
78before parameters => sub {
79 my ($self, $params) = @_;
059c085b 80 if ( $params && !ref $params ) {
e5ecd5bc 81 $self->_context->log->warn(
059c085b 82 "Attempt to retrieve '$params' with req->params(), " .
83 "you probably meant to call req->param('$params')" );
84 $params = undef;
85 }
86
87};
88
89has base => (
5fb12dbb 90 is => 'rw',
91 required => 1,
92 lazy => 1,
93 default => sub {
059c085b 94 my $self = shift;
6cb9e383 95 return $self->path if $self->has_uri;
059c085b 96 },
97);
98
069355da 99has _body => (
100 is => 'rw',
059c085b 101);
610bc6ec 102# Eugh, ugly. Should just be able to rename accessor methods to 'body'
103# and provide a custom reader..
104sub body {
105 my $self = shift;
059c085b 106 $self->_context->prepare_body();
610bc6ec 107 $self->_body(@_) if scalar @_;
108 return blessed $self->_body ? $self->_body->body : $self->_body;
109}
059c085b 110
111has hostname => (
112 is => 'rw',
113 required => 1,
114 lazy => 1,
115 default => sub {
116 my ($self) = @_;
8fc0d39e 117 gethostbyaddr( inet_aton( $self->address ), AF_INET ) || 'localhost'
059c085b 118 },
119);
120
02570318 121has _path => ( is => 'rw', predicate => '_has_path', clearer => '_clear_path' );
122
059c085b 123no Moose;
124
125sub args { shift->arguments(@_) }
126sub body_params { shift->body_parameters(@_) }
127sub input { shift->body(@_) }
128sub params { shift->parameters(@_) }
129sub query_params { shift->query_parameters(@_) }
130sub path_info { shift->path(@_) }
131sub snippets { shift->captures(@_) }
f7e4e231 132
fc7ec1d9 133=head1 NAME
134
3e19f4f6 135Catalyst::Request - provides information about the current client request
fc7ec1d9 136
137=head1 SYNOPSIS
138
b22c6668 139 $req = $c->request;
140 $req->action;
141 $req->address;
b22c6668 142 $req->arguments;
3e19f4f6 143 $req->args;
b22c6668 144 $req->base;
06e1b616 145 $req->body;
fbcc39ad 146 $req->body_parameters;
b5176d9e 147 $req->content_encoding;
148 $req->content_length;
149 $req->content_type;
b77e7869 150 $req->cookie;
b22c6668 151 $req->cookies;
b5176d9e 152 $req->header;
b22c6668 153 $req->headers;
154 $req->hostname;
61bacdcc 155 $req->input;
3b4d1251 156 $req->query_keywords;
b22c6668 157 $req->match;
158 $req->method;
e7c0c583 159 $req->param;
e7c0c583 160 $req->parameters;
3e19f4f6 161 $req->params;
b22c6668 162 $req->path;
bfde09a2 163 $req->protocol;
fbcc39ad 164 $req->query_parameters;
165 $req->read;
b5176d9e 166 $req->referer;
bfde09a2 167 $req->secure;
2982e768 168 $req->captures; # previously knows as snippets
e7c0c583 169 $req->upload;
b22c6668 170 $req->uploads;
77d12cae 171 $req->uri;
7ce7ca2e 172 $req->user;
66294129 173 $req->user_agent;
b22c6668 174
3e22baa5 175See also L<Catalyst>, L<Catalyst::Request::Upload>.
fc7ec1d9 176
177=head1 DESCRIPTION
178
3e19f4f6 179This is the Catalyst Request class, which provides an interface to data for the
180current client request. The request object is prepared by L<Catalyst::Engine>,
181thus hiding the details of the particular engine implementation.
b22c6668 182
183=head1 METHODS
fc7ec1d9 184
b5ecfcf0 185=head2 $req->action
fc7ec1d9 186
aae8d418 187[DEPRECATED] Returns the name of the requested action.
188
189
190Use C<< $c->action >> instead (which returns a
191L<Catalyst::Action|Catalyst::Action> object).
fc7ec1d9 192
b5ecfcf0 193=head2 $req->address
0556eb49 194
3e19f4f6 195Returns the IP address of the client.
61b1e958 196
b5ecfcf0 197=head2 $req->arguments
61b1e958 198
b22c6668 199Returns a reference to an array containing the arguments.
fc7ec1d9 200
201 print $c->request->arguments->[0];
202
c436c1e8 203For example, if your action was
204
85d9fce6 205 package MyApp::C::Foo;
206
207 sub moose : Local {
208 ...
209 }
c436c1e8 210
3e19f4f6 211and the URI for the request was C<http://.../foo/moose/bah>, the string C<bah>
c436c1e8 212would be the first and only argument.
213
b5ecfcf0 214=head2 $req->args
3e19f4f6 215
216Shortcut for arguments.
217
b5ecfcf0 218=head2 $req->base
fc7ec1d9 219
c436c1e8 220Contains the URI base. This will always have a trailing slash.
221
3e19f4f6 222If your application was queried with the URI
223C<http://localhost:3000/some/path> then C<base> is C<http://localhost:3000/>.
fc7ec1d9 224
b5ecfcf0 225=head2 $req->body
06e1b616 226
3e19f4f6 227Returns the message body of the request, unless Content-Type is
e060fe05 228C<application/x-www-form-urlencoded> or C<multipart/form-data>.
229
b5ecfcf0 230=head2 $req->body_parameters
fbcc39ad 231
3e19f4f6 232Returns a reference to a hash containing body (POST) parameters. Values can
fbcc39ad 233be either a scalar or an arrayref containing scalars.
234
235 print $c->request->body_parameters->{field};
236 print $c->request->body_parameters->{field}->[0];
c436c1e8 237
d631b5f9 238These are the parameters from the POST part of the request, if any.
e5ecd5bc 239
b5ecfcf0 240=head2 $req->body_params
fbcc39ad 241
3e19f4f6 242Shortcut for body_parameters.
fbcc39ad 243
b5ecfcf0 244=head2 $req->content_encoding
b5176d9e 245
3e19f4f6 246Shortcut for $req->headers->content_encoding.
b5176d9e 247
b5ecfcf0 248=head2 $req->content_length
b5176d9e 249
3e19f4f6 250Shortcut for $req->headers->content_length.
b5176d9e 251
b5ecfcf0 252=head2 $req->content_type
b5176d9e 253
3e19f4f6 254Shortcut for $req->headers->content_type.
b5176d9e 255
b5ecfcf0 256=head2 $req->cookie
3ad654e0 257
3e19f4f6 258A convenient method to access $req->cookies.
3ad654e0 259
260 $cookie = $c->request->cookie('name');
261 @cookies = $c->request->cookie;
262
263=cut
264
265sub cookie {
266 my $self = shift;
267
268 if ( @_ == 0 ) {
b77e7869 269 return keys %{ $self->cookies };
3ad654e0 270 }
271
272 if ( @_ == 1 ) {
273
274 my $name = shift;
275
b77e7869 276 unless ( exists $self->cookies->{$name} ) {
3ad654e0 277 return undef;
278 }
fbcc39ad 279
b77e7869 280 return $self->cookies->{$name};
3ad654e0 281 }
282}
283
b5ecfcf0 284=head2 $req->cookies
fc7ec1d9 285
b22c6668 286Returns a reference to a hash containing the cookies.
fc7ec1d9 287
288 print $c->request->cookies->{mycookie}->value;
289
3e19f4f6 290The cookies in the hash are indexed by name, and the values are L<CGI::Cookie>
c436c1e8 291objects.
292
b5ecfcf0 293=head2 $req->header
b5176d9e 294
3e19f4f6 295Shortcut for $req->headers->header.
b5176d9e 296
b5ecfcf0 297=head2 $req->headers
fc7ec1d9 298
3e19f4f6 299Returns an L<HTTP::Headers> object containing the headers for the current request.
fc7ec1d9 300
301 print $c->request->headers->header('X-Catalyst');
302
b5ecfcf0 303=head2 $req->hostname
0556eb49 304
3e19f4f6 305Returns the hostname of the client.
e5ecd5bc 306
b5ecfcf0 307=head2 $req->input
61bacdcc 308
3e19f4f6 309Alias for $req->body.
61bacdcc 310
3b4d1251 311=head2 $req->query_keywords
312
313Contains the keywords portion of a query string, when no '=' signs are
314present.
315
316 http://localhost/path?some+keywords
ac5c933b 317
3b4d1251 318 $c->request->query_keywords will contain 'some keywords'
319
b5ecfcf0 320=head2 $req->match
fc7ec1d9 321
3e19f4f6 322This contains the matching part of a Regex action. Otherwise
2c83fd5a 323it returns the same as 'action', except for default actions,
324which return an empty string.
fc7ec1d9 325
b5ecfcf0 326=head2 $req->method
b5176d9e 327
328Contains the request method (C<GET>, C<POST>, C<HEAD>, etc).
329
b5ecfcf0 330=head2 $req->param
e7c0c583 331
ac5c933b 332Returns GET and POST parameters with a CGI.pm-compatible param method. This
3e19f4f6 333is an alternative method for accessing parameters in $c->req->parameters.
e7c0c583 334
a82c2894 335 $value = $c->request->param( 'foo' );
336 @values = $c->request->param( 'foo' );
e7c0c583 337 @params = $c->request->param;
338
3e705254 339Like L<CGI>, and B<unlike> earlier versions of Catalyst, passing multiple
a82c2894 340arguments to this method, like this:
341
85d9fce6 342 $c->request->param( 'foo', 'bar', 'gorch', 'quxx' );
a82c2894 343
344will set the parameter C<foo> to the multiple values C<bar>, C<gorch> and
345C<quxx>. Previously this would have added C<bar> as another value to C<foo>
3e19f4f6 346(creating it if it didn't exist before), and C<quxx> as another value for
347C<gorch>.
a82c2894 348
e7c0c583 349=cut
350
351sub param {
352 my $self = shift;
353
354 if ( @_ == 0 ) {
355 return keys %{ $self->parameters };
356 }
357
bfde09a2 358 if ( @_ == 1 ) {
e7c0c583 359
bfde09a2 360 my $param = shift;
6bd2b72c 361
bfde09a2 362 unless ( exists $self->parameters->{$param} ) {
363 return wantarray ? () : undef;
364 }
365
366 if ( ref $self->parameters->{$param} eq 'ARRAY' ) {
367 return (wantarray)
368 ? @{ $self->parameters->{$param} }
369 : $self->parameters->{$param}->[0];
370 }
371 else {
372 return (wantarray)
373 ? ( $self->parameters->{$param} )
374 : $self->parameters->{$param};
375 }
d7945f32 376 }
a82c2894 377 elsif ( @_ > 1 ) {
378 my $field = shift;
379 $self->parameters->{$field} = [@_];
d7945f32 380 }
e7c0c583 381}
b5176d9e 382
b5ecfcf0 383=head2 $req->parameters
61b1e958 384
3e19f4f6 385Returns a reference to a hash containing GET and POST parameters. Values can
d08ced28 386be either a scalar or an arrayref containing scalars.
fc7ec1d9 387
e7c0c583 388 print $c->request->parameters->{field};
389 print $c->request->parameters->{field}->[0];
fc7ec1d9 390
c436c1e8 391This is the combination of C<query_parameters> and C<body_parameters>.
392
b5ecfcf0 393=head2 $req->params
3e19f4f6 394
395Shortcut for $req->parameters.
396
b5ecfcf0 397=head2 $req->path
fc7ec1d9 398
3e19f4f6 399Returns the path, i.e. the part of the URI after $req->base, for the current request.
fc7ec1d9 400
b5ecfcf0 401=head2 $req->path_info
fbcc39ad 402
3e19f4f6 403Alias for path, added for compability with L<CGI>.
fbcc39ad 404
405=cut
406
407sub path {
02fb5d78 408 my ( $self, @params ) = @_;
4f5ebacd 409
02fb5d78 410 if (@params) {
411 $self->uri->path(@params);
02570318 412 $self->_clear_path;
fbcc39ad 413 }
02570318 414 elsif ( $self->_has_path ) {
415 return $self->_path;
e561386f 416 }
02fb5d78 417 else {
418 my $path = $self->uri->path;
419 my $location = $self->base->path;
420 $path =~ s/^(\Q$location\E)?//;
421 $path =~ s/^\///;
02570318 422 $self->_path($path);
fbcc39ad 423
02fb5d78 424 return $path;
425 }
fbcc39ad 426}
427
b5ecfcf0 428=head2 $req->protocol
bfde09a2 429
3e19f4f6 430Returns the protocol (HTTP/1.0 or HTTP/1.1) used for the current request.
bfde09a2 431
b5ecfcf0 432=head2 $req->query_parameters
fbcc39ad 433
def54ce2 434=head2 $req->query_params
435
3e19f4f6 436Returns a reference to a hash containing query string (GET) parameters. Values can
fbcc39ad 437be either a scalar or an arrayref containing scalars.
438
439 print $c->request->query_parameters->{field};
440 print $c->request->query_parameters->{field}->[0];
ac5c933b 441
b5ecfcf0 442=head2 $req->read( [$maxlength] )
fbcc39ad 443
3e19f4f6 444Reads a chunk of data from the request body. This method is intended to be
445used in a while loop, reading $maxlength bytes on every call. $maxlength
fbcc39ad 446defaults to the size of the request if not specified.
447
448You have to set MyApp->config->{parse_on_demand} to use this directly.
449
b5ecfcf0 450=head2 $req->referer
fc7ec1d9 451
3e19f4f6 452Shortcut for $req->headers->referer. Returns the referring page.
fc7ec1d9 453
b5ecfcf0 454=head2 $req->secure
bfde09a2 455
3e19f4f6 456Returns true or false, indicating whether the connection is secure (https).
bfde09a2 457
2982e768 458=head2 $req->captures
459
5c6a56e0 460Returns a reference to an array containing captured args from chained
461actions or regex captures.
fc7ec1d9 462
2982e768 463 my @captures = @{ $c->request->captures };
464
465=head2 $req->snippets
fc7ec1d9 466
2982e768 467C<captures> used to be called snippets. This is still available for backwoards
468compatibility, but is considered deprecated.
fc7ec1d9 469
b5ecfcf0 470=head2 $req->upload
e7c0c583 471
3e19f4f6 472A convenient method to access $req->uploads.
e7c0c583 473
474 $upload = $c->request->upload('field');
475 @uploads = $c->request->upload('field');
476 @fields = $c->request->upload;
bfde09a2 477
e7c0c583 478 for my $upload ( $c->request->upload('field') ) {
146554c5 479 print $upload->filename;
e7c0c583 480 }
481
482=cut
483
484sub upload {
485 my $self = shift;
486
487 if ( @_ == 0 ) {
488 return keys %{ $self->uploads };
489 }
490
bfde09a2 491 if ( @_ == 1 ) {
e7c0c583 492
bfde09a2 493 my $upload = shift;
494
495 unless ( exists $self->uploads->{$upload} ) {
496 return wantarray ? () : undef;
497 }
6bd2b72c 498
bfde09a2 499 if ( ref $self->uploads->{$upload} eq 'ARRAY' ) {
500 return (wantarray)
501 ? @{ $self->uploads->{$upload} }
502 : $self->uploads->{$upload}->[0];
503 }
504 else {
505 return (wantarray)
fbcc39ad 506 ? ( $self->uploads->{$upload} )
507 : $self->uploads->{$upload};
bfde09a2 508 }
d7945f32 509 }
bfde09a2 510
a4f5c51e 511 if ( @_ > 1 ) {
bfde09a2 512
513 while ( my ( $field, $upload ) = splice( @_, 0, 2 ) ) {
514
515 if ( exists $self->uploads->{$field} ) {
516 for ( $self->uploads->{$field} ) {
517 $_ = [$_] unless ref($_) eq "ARRAY";
518 push( @$_, $upload );
519 }
520 }
521 else {
522 $self->uploads->{$field} = $upload;
523 }
524 }
e7c0c583 525 }
526}
527
b5ecfcf0 528=head2 $req->uploads
fc7ec1d9 529
bfde09a2 530Returns a reference to a hash containing uploads. Values can be either a
ac5c933b 531L<Catalyst::Request::Upload> object, or an arrayref of
84e7aa89 532L<Catalyst::Request::Upload> objects.
e7c0c583 533
534 my $upload = $c->request->uploads->{field};
535 my $upload = $c->request->uploads->{field}->[0];
536
b5ecfcf0 537=head2 $req->uri
fbcc39ad 538
3e19f4f6 539Returns a URI object for the current request. Stringifies to the URI text.
fbcc39ad 540
bd917b94 541=head2 $req->uri_with( { key => 'value' } );
542
3338e8ce 543Returns a rewritten URI object for the current request. Key/value pairs
2f381252 544passed in will override existing parameters. You can remove an existing
545parameter by passing in an undef value. Unmodified pairs will be
3338e8ce 546preserved.
bd917b94 547
548=cut
549
550sub uri_with {
551 my( $self, $args ) = @_;
ac5c933b 552
bd917b94 553 carp( 'No arguments passed to uri_with()' ) unless $args;
fbb513f7 554
2f381252 555 foreach my $value ( values %$args ) {
d0f0fcf6 556 next unless defined $value;
fbb513f7 557 for ( ref $value eq 'ARRAY' ? @$value : $value ) {
558 $_ = "$_";
7066a4d5 559 utf8::encode( $_ ) if utf8::is_utf8($_);
fc42a730 560 }
fc42a730 561 };
ac5c933b 562
2f381252 563 my $uri = $self->uri->clone;
564 my %query = ( %{ $uri->query_form_hash }, %$args );
565
bd917b94 566 $uri->query_form( {
2f381252 567 # remove undef values
568 map { defined $query{ $_ } ? ( $_ => $query{ $_ } ) : () } keys %query
bd917b94 569 } );
570 return $uri;
571}
572
b5ecfcf0 573=head2 $req->user
7ce7ca2e 574
3e19f4f6 575Returns the currently logged in user. Deprecated. The method recommended for
576newer plugins is $c->user.
7ce7ca2e 577
b5ecfcf0 578=head2 $req->user_agent
b5176d9e 579
3e19f4f6 580Shortcut to $req->headers->user_agent. Returns the user agent (browser)
581version string.
b5176d9e 582
059c085b 583=head2 meta
584
585Provided by Moose
586
3e19f4f6 587=head1 AUTHORS
fc7ec1d9 588
2f381252 589Catalyst Contributors, see Catalyst.pm
fc7ec1d9 590
591=head1 COPYRIGHT
592
e7c0c583 593This program is free software, you can redistribute it and/or modify
61b1e958 594it under the same terms as Perl itself.
fc7ec1d9 595
596=cut
597
e5ecd5bc 598__PACKAGE__->meta->make_immutable;
599
fc7ec1d9 6001;