root/lang/perl/HTTP-Engine/branches/shika/lib/HTTP/Engine/Request.pm @ 25457

Revision 25457, 12.3 kB (checked in by yappo, 4 years ago)

fixed many Header type problem

Line 
1package HTTP::Engine::Request;
2use Shika;
3use HTTP::Headers::Fast;
4use HTTP::Engine::Types::Core qw( Uri Header );
5use URI::QueryParam;
6require Carp; # Carp->import is too heavy =(
7
8# Shika role merging is borked with attributes
9#with qw(HTTP::Engine::Request);
10
11# this object constructs all our lazy fields for us
12has request_builder => (
13    does     => "HTTP::Engine::Role::RequestBuilder",
14    is       => "rw",
15    required => 1,
16);
17
18sub BUILD {
19    my ( $self, $param ) = @_;
20
21    foreach my $field qw(base path) {
22        if ( my $val = $param->{$field} ) {
23            $self->$field($val);
24        }
25    }
26}
27
28has _connection => (
29    is => "ro",
30    isa => 'HashRef',
31    required => 1,
32);
33
34has "_read_state" => (
35    is => "rw",
36    lazy_build => 1,
37);
38
39sub _build__read_state {
40    my $self = shift;
41    $self->request_builder->_build_read_state($self);
42}
43
44has connection_info => (
45    is => "rw",
46    isa => "HashRef",
47    lazy_build => 1,
48);
49
50sub _build_connection_info {
51    my $self = shift;
52    $self->request_builder->_build_connection_info($self);
53}
54
55has cookies => (
56    is      => 'rw',
57    isa     => 'HashRef',
58    lazy_build => 1,
59);
60
61sub _build_cookies {
62    my $self = shift;
63    $self->request_builder->_build_cookies($self);
64}
65
66foreach my $attr qw(address method protocol user port _https_info request_uri) {
67    has $attr => (
68        is => 'rw',
69        # isa => "Str",
70        lazy => 1,
71        default => sub { shift->connection_info->{$attr} },
72    );
73}
74has query_parameters => (
75    is      => 'rw',
76    isa     => 'HashRef',
77    lazy_build => 1,
78);
79
80sub _build_query_parameters {
81    my $self = shift;
82    $self->uri->query_form_hash;
83}
84
85# https or not?
86has secure => (
87    is      => 'rw',
88    isa     => 'Bool',
89    lazy_build => 1,
90);
91
92sub _build_secure {
93    my $self = shift;
94
95    if ( my $https = $self->_https_info ) {
96        return 1 if uc($https) eq 'ON';
97    }
98
99    if ( my $port = $self->port ) {
100        return 1 if $port == 443;
101    }
102
103    return 0;
104}
105
106# proxy request?
107has proxy_request => (
108    is         => 'rw',
109    isa        => 'Str', # TODO: union(Uri, Undef) type
110#    coerce     => 1,
111    lazy_build => 1,
112);
113
114sub _build_proxy_request {
115    my $self = shift;
116    return '' unless $self->request_uri;                   # TODO: return undef
117    return '' unless $self->request_uri =~ m!^https?://!i; # TODO: return undef
118    return $self->request_uri;                             # TODO: return URI->new($self->request_uri);
119}
120
121has uri => (
122    is     => 'rw',
123    isa => 'Uri',
124    coerce => 1,
125    lazy_build => 1,
126    handles => [qw(base path)],
127);
128
129sub _build_uri {
130    my $self = shift;
131    $self->request_builder->_build_uri($self);
132}
133
134has raw_body => (
135    is      => 'rw',
136    isa     => 'Str',
137    lazy_build => 1,
138);
139
140sub _build_raw_body {
141    my $self = shift;
142    $self->request_builder->_build_raw_body($self);
143}
144
145has headers => (
146    is      => 'rw',
147    isa => 'Header',
148    coerce  => 1,
149    lazy_build => 1,
150    handles => [ qw(content_encoding content_length content_type header referer user_agent) ],
151);
152
153sub _build_headers {
154    my $self = shift;
155    $self->request_builder->_build_headers($self);
156}
157
158# Contains the URI base. This will always have a trailing slash.
159# If your application was queried with the URI C<http://localhost:3000/some/path> then C<base> is C<http://localhost:3000/>.
160
161has hostname => (
162    is      => 'rw',
163    isa     => 'Str',
164    lazy_build => 1,
165);
166
167sub _build_hostname {
168    my $self = shift;
169    $self->request_builder->_build_hostname($self);
170}
171
172has http_body => (
173    is         => 'rw',
174    isa        => 'HTTP::Body',
175    lazy_build => 1,
176    handles => {
177        body_parameters => 'param',
178        body            => 'body',
179    },
180);
181
182sub _build_http_body {
183    my $self = shift;
184    $self->request_builder->_build_http_body($self);
185}
186
187# contains body_params and query_params
188has parameters => (
189    is      => 'rw',
190    isa     => 'HashRef',
191    lazy_build => 1,
192);
193
194sub _build_parameters {
195    my $self = shift;
196
197    my $query = $self->query_parameters;
198    my $body = $self->body_parameters;
199
200    my %merged;
201
202    foreach my $hash ( $query, $body ) {
203        foreach my $name ( keys %$hash ) {
204            my $param = $hash->{$name};
205            push( @{ $merged{$name} ||= [] }, ( ref $param ? @$param : $param ) );
206        }
207    }
208
209    foreach my $param ( values %merged ) {
210        $param = $param->[0] if @$param == 1;
211    }
212
213    return \%merged;
214}
215
216has uploads => (
217    is      => 'rw',
218    isa     => 'HashRef',
219    lazy_build => 1,
220);
221
222sub _build_uploads {
223    my $self = shift;
224    $self->request_builder->_prepare_uploads($self);
225}
226
227# aliases
228*body_params  = \&body_parameters;
229*input        = \&body;
230*params       = \&parameters;
231*query_params = \&query_parameters;
232*path_info    = \&path;
233
234sub cookie {
235    my $self = shift;
236
237    return keys %{ $self->cookies } if @_ == 0;
238
239    if (@_ == 1) {
240        my $name = shift;
241        return undef unless exists $self->cookies->{$name}; ## no critic.
242        return $self->cookies->{$name};
243    }
244    return;
245}
246
247sub param {
248    my $self = shift;
249
250    return keys %{ $self->parameters } if @_ == 0;
251
252    if (@_ == 1) {
253        my $param = shift;
254        return wantarray ? () : undef unless exists $self->parameters->{$param};
255
256        if ( ref $self->parameters->{$param} eq 'ARRAY' ) {
257            return (wantarray)
258              ? @{ $self->parameters->{$param} }
259                  : $self->parameters->{$param}->[0];
260        } else {
261            return (wantarray)
262              ? ( $self->parameters->{$param} )
263                  : $self->parameters->{$param};
264        }
265    } else {
266        my $field = shift;
267        $self->parameters->{$field} = [@_];
268    }
269}
270
271sub upload {
272    my $self = shift;
273
274    return keys %{ $self->uploads } if @_ == 0;
275
276    if (@_ == 1) {
277        my $upload = shift;
278        return wantarray ? () : undef unless exists $self->uploads->{$upload};
279
280        if (ref $self->uploads->{$upload} eq 'ARRAY') {
281            return (wantarray)
282              ? @{ $self->uploads->{$upload} }
283          : $self->uploads->{$upload}->[0];
284        } else {
285            return (wantarray)
286              ? ( $self->uploads->{$upload} )
287          : $self->uploads->{$upload};
288        }
289    } else {
290        while ( my($field, $upload) = splice(@_, 0, 2) ) {
291            if ( exists $self->uploads->{$field} ) {
292                for ( $self->uploads->{$field} ) {
293                    $_ = [$_] unless ref($_) eq "ARRAY";
294                    push(@{ $_ }, $upload);
295                }
296            } else {
297                $self->uploads->{$field} = $upload;
298            }
299        }
300    }
301}
302
303sub uri_with {
304    my($self, $args) = @_;
305   
306    Carp::carp( 'No arguments passed to uri_with()' ) unless $args;
307
308    for my $value (values %{ $args }) {
309        next unless defined $value;
310        for ( ref $value eq 'ARRAY' ? @{ $value } : $value ) {
311            $_ = "$_";
312            utf8::encode( $_ );
313        }
314    };
315   
316    my $uri = $self->uri->clone;
317   
318    $uri->query_form( {
319        %{ $uri->query_form_hash },
320        %{ $args },
321    } );
322    return $uri;
323}
324
325sub as_http_request {
326    my $self = shift;
327    HTTP::Engine::Util::require_once('HTTP/Request.pm');
328    HTTP::Request->new( $self->method, $self->uri, $self->headers, $self->raw_body );
329}
330
331sub absolute_url {
332    my ($self, $location) = @_;
333
334    unless ($location =~ m!^https?://!) {
335        return URI->new( $location )->abs( $self->base );
336    } else {
337        return $location;
338    }
339}
340
341sub content {
342    my ( $self, @args ) = @_;
343
344    if ( @args ) {
345        Carp::croak "The HTTP::Request method 'content' is unsupported when used as a writer, use HTTP::Engine::RequestBuilder";
346    } else {
347        return $self->raw_body;
348    }
349}
350
351sub as_string {
352    my $self = shift;
353    $self->as_http_request->as_string; # FIXME not efficient
354}
355
356sub parse {
357    Carp::croak "The HTTP::Request method 'parse' is unsupported, use HTTP::Engine::RequestBuilder";
358}
359
3601;
361__END__
362
363=for stopwords Stringifies URI http https param CGI.pm-compatible referer uri IP hostname API enviroments
364
365=head1 NAME
366
367HTTP::Engine::Request - Portable HTTP request object
368
369=head1 SYNOPSIS
370
371    # normally a request object is passed into your handler
372    sub handle_request {
373        my $req = shift;
374
375   };
376
377=head1 DESCRIPTION
378
379L<HTTP::Engine::Request> provides a consistent API for request objects across web
380server enviroments.
381
382=head1 METHODS
383
384=head2 new
385
386    HTTP::Engine::Request->new(
387        request_builder => $BUILDER,
388        _connection => {
389            env           => \%ENV,
390            input_handle  => \*STDIN,
391            output_handle => \*STDOUT,
392        },
393        %args
394    );
395
396Normally, new() is not called directly, but a pre-built HTTP::Engine::Request
397object is passed for you into your request handler. You may build your own,
398following the example above. The C<$BUILDER> may be one of
399L<HTTP::Engine::RequestBuilder::CGI> or L<HTTP::Engine::RequestBuilder::NoEnv>.
400
401=head1 ATTRIBUTES
402
403=over 4
404
405=item address
406
407Returns the IP address of the client.
408
409=item cookies
410
411Returns a reference to a hash containing the cookies
412
413=item method
414
415Contains the request method (C<GET>, C<POST>, C<HEAD>, etc).
416
417=item protocol
418
419Returns the protocol (HTTP/1.0 or HTTP/1.1) used for the current request.
420
421=item request_uri
422
423Returns the request uri (like $ENV{REQUEST_URI})
424
425=item query_parameters
426
427Returns a reference to a hash containing query string (GET) parameters. Values can                                                   
428be either a scalar or an arrayref containing scalars.
429
430=item secure
431
432Returns true or false, indicating whether the connection is secure (https).
433
434=item proxy_request
435
436Returns undef or uri, if it is proxy request, uri of a connection place is returned.
437
438=item uri
439
440Returns a URI object for the current request. Stringifies to the URI text.
441
442=item user
443
444Returns REMOTE_USER.
445
446=item raw_body
447
448Returns string containing body(POST).
449
450=item headers
451
452Returns an L<HTTP::Headers> object containing the headers for the current request.
453
454=item base
455
456Contains the URI base. This will always have a trailing slash.
457
458=item hostname
459
460Returns the hostname of the client.
461
462=item http_body
463
464Returns an L<HTTP::Body> object.
465
466=item parameters
467
468Returns a reference to a hash containing GET and POST parameters. Values can
469be either a scalar or an arrayref containing scalars.
470
471=item uploads
472
473Returns a reference to a hash containing uploads. Values can be either a
474L<HTTP::Engine::Request::Upload> object, or an arrayref of
475L<HTTP::Engine::Request::Upload> objects.
476
477=item content_encoding
478
479Shortcut to $req->headers->content_encoding.
480
481=item content_length
482
483Shortcut to $req->headers->content_length.
484
485=item content_type
486
487Shortcut to $req->headers->content_type.
488
489=item header
490
491Shortcut to $req->headers->header.
492
493=item referer
494
495Shortcut to $req->headers->referer.
496
497=item user_agent
498
499Shortcut to $req->headers->user_agent.
500
501=item cookie
502
503A convenient method to access $req->cookies.
504
505    $cookie  = $req->cookie('name');
506    @cookies = $req->cookie;
507
508=item param
509
510Returns GET and POST parameters with a CGI.pm-compatible param method. This
511is an alternative method for accessing parameters in $req->parameters.
512
513    $value  = $req->param( 'foo' );
514    @values = $req->param( 'foo' );
515    @params = $req->param;
516
517Like L<CGI>, and B<unlike> earlier versions of Catalyst, passing multiple
518arguments to this method, like this:
519
520    $req->param( 'foo', 'bar', 'gorch', 'quxx' );
521
522will set the parameter C<foo> to the multiple values C<bar>, C<gorch> and
523C<quxx>. Previously this would have added C<bar> as another value to C<foo>
524(creating it if it didn't exist before), and C<quxx> as another value for
525C<gorch>.
526
527=item path
528
529Returns the path, i.e. the part of the URI after $req->base, for the current request.
530
531=item upload
532
533A convenient method to access $req->uploads.
534
535    $upload  = $req->upload('field');
536    @uploads = $req->upload('field');
537    @fields  = $req->upload;
538
539    for my $upload ( $req->upload('field') ) {
540        print $upload->filename;
541    }
542
543
544=item uri_with
545
546Returns a rewritten URI object for the current request. Key/value pairs
547passed in will override existing parameters. Unmodified pairs will be
548preserved.
549
550=item as_http_request
551
552convert HTTP::Engine::Request to HTTP::Request.
553
554=item $req->absolute_url($location)
555
556convert $location to absolute uri.
557
558=back
559
560=head1 AUTHORS
561
562Kazuhiro Osawa and HTTP::Engine Authors.
563
564=head1 THANKS TO
565
566L<Catalyst::Request>
567
568=head1 SEE ALSO
569
570L<HTTP::Request>, L<Catalyst::Request>
571
Note: See TracBrowser for help on using the browser.