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