File Coverage

blib/lib/Net/HTTP/Spore/Request.pm
Criterion Covered Total %
statement 162 181 89.5
branch 52 72 72.2
condition 24 32 75.0
subroutine 26 30 86.6
pod 14 16 87.5
total 278 331 83.9


line stmt bran cond sub pod time code
1             package Net::HTTP::Spore::Request;
2             $Net::HTTP::Spore::Request::VERSION = '0.09';
3             # ABSTRACT: Net::HTTP::Spore::Request - Portable HTTP request object from SPORE env hash
4              
5 28     28   297312 use Moose;
  28         2642513  
  28         184  
6 28     28   173380 use Carp ();
  28         67  
  28         506  
7 28     28   3496 use URI;
  28         26049  
  28         774  
8 28     28   2438 use HTTP::Headers;
  28         40366  
  28         705  
9 28     28   1841 use HTTP::Request;
  28         37665  
  28         760  
10 28     28   151 use URI::Escape;
  28         58  
  28         1722  
11 28     28   8601 use MIME::Base64;
  28         14916  
  28         1346  
12 28     28   2869 use Net::HTTP::Spore::Response;
  28         63  
  28         690  
13              
14 28     28   9230 use Encode qw{is_utf8};
  28         224898  
  28         50385  
15              
16             has env => (
17             is => 'rw',
18             isa => 'HashRef',
19             required => 1,
20             traits => ['Hash'],
21             handles => {
22             set_to_env => 'set',
23             get_from_env => 'get',
24             }
25             );
26              
27             has path => (
28             is => 'rw',
29             isa => 'Str',
30             lazy => 1,
31             default => sub { $_[0]->env->{PATH_INFO} }
32             );
33              
34             has headers => (
35             is => 'rw',
36             isa => 'HTTP::Headers',
37             lazy => 1,
38             handles => {
39             header => 'header',
40             },
41             default => sub {
42             my $self = shift;
43             my $env = $self->env;
44             my $h = HTTP::Headers->new(
45             map {
46             ( my $field = $_ ) =~ s/^HTTPS?_//;
47             ( $field => $env->{$_} );
48             } grep { /^(?:HTTP|CONTENT)/i } keys %$env
49             );
50             return $h;
51             },
52             );
53              
54             sub BUILDARGS {
55 51     51 1 22588 my $class = shift;
56              
57 51 50 33     338 if ( @_ == 1 && !exists $_[0]->{env} ) {
58 51         203 return { env => $_[0] };
59             }
60 0         0 return @_;
61             }
62              
63             sub _safe_uri_escape {
64 17     17   39 my ( $self, $str, $unsafe ) = @_;
65 17 50       30 return unless defined $str;
66 17 50       56 if ( is_utf8($str) ) {
67 0         0 utf8::encode($str);
68             }
69 17         48 return uri_escape( $str, $unsafe );
70             }
71              
72             sub method {
73 48     48 1 1923 my ( $self, $value ) = @_;
74 48 50       160 if ($value) {
75 0         0 $self->set_to_env( 'REQUEST_METHOD', $value );
76             }
77             else {
78 48         1720 return $self->get_from_env('REQUEST_METHOD');
79             }
80             }
81              
82             sub host {
83 0     0 0 0 my ($self, $value) = @_;
84 0 0       0 if ($value) {
85 0         0 $self->set_to_env('SERVER_NAME', $value);
86             }else{
87 0         0 return $self->get_from_env('SERVER_NAME');
88             }
89             }
90              
91             sub port {
92 4     4 1 6696 my ( $self, $value ) = @_;
93 4 50       12 if ($value) {
94 0         0 $self->set_to_env( 'SERVER_PORT', $value );
95             }
96             else {
97 4         122 return $self->get_from_env('SERVER_PORT');
98             }
99             }
100              
101             sub script_name {
102 2     2 1 1320 my ( $self, $value ) = @_;
103 2 50       6 if ($value) {
104 0         0 $self->set_to_env( 'SCRIPT_NAME', $value );
105             }
106             else {
107 2         74 return $self->get_from_env('SCRIPT_NAME');
108             }
109             }
110              
111             sub request_uri {
112 2     2 1 1286 my ($self, $value) = @_;
113 2 50       6 if ($value) {
114 0         0 $self->set_to_env( 'REQUEST_URI', $value );
115             }
116             else {
117 2         70 return $self->get_from_env('REQUEST_URI');
118             }
119             }
120              
121             sub scheme {
122 4     4 1 8 my ($self, $value) = @_;
123 4 50       11 if ($value) {
124 0         0 $self->set_to_env( 'spore.url_scheme', $value );
125             }
126             else {
127 4         123 return $self->get_from_env('spore.url_scheme');
128             }
129             }
130              
131             sub logger {
132 0     0 0 0 my ($self, $value) = @_;
133 0 0       0 if ($value) {
134 0         0 $self->set_to_env( 'sporex.logger', $value );
135             }
136             else {
137 0         0 return $self->get_from_env('sporex.logger');
138             }
139             }
140              
141             sub body {
142 47     47 1 2221 my ($self, $value) = @_;
143 47 50       117 if ($value) {
144 0         0 $self->set_to_env( 'spore.payload', $value );
145             }
146             else {
147 47         1701 return $self->get_from_env('spore.payload');
148             }
149             }
150              
151             sub base {
152 9     9 1 5643 my $self = shift;
153 9         22 URI->new( $self->_uri_base )->canonical;
154             }
155              
156 0     0 1 0 sub input { (shift)->body(@_) }
157 42     42 1 140 sub content { (shift)->body(@_) }
158 0     0 1 0 sub secure { $_[0]->scheme eq 'https' }
159              
160             # TODO
161             # need to refactor this method, with path_info and query_string construction
162             sub uri {
163 43     43 1 120 my ($self, $path_info, $query_string) = @_;
164              
165 43 100 66     206 if ( !defined $path_info || !defined $query_string ) {
166 9         19 my @path_info = $self->_path;
167 9 50       21 $path_info = $path_info[0] if !$path_info;
168 9 100       19 $query_string = $path_info[1] if !$query_string;
169             }
170              
171 43   100     127 $path_info = $path_info // '';
172 43         131 my $base = $self->_uri_base;
173              
174 43 100 100     220 if ( defined $query_string && length($query_string) > 0 ) {
175 9         22 my $is_interrogation = index( $path_info, '?' );
176 9 50       24 if ( $is_interrogation >= 0 ) {
177 0         0 $path_info .= '&' . $query_string;
178             }
179             else {
180 9         22 $path_info .= '?' . $query_string;
181             }
182             }
183              
184 43 100       246 $base =~ s!/$!! if $path_info =~ m!^/!;
185 43         247 return URI->new( $base . $path_info )->canonical;
186             }
187              
188             sub _path {
189 9     9   11 my $self = shift;
190              
191 9         12 my $query_string;
192 9         196 my $path = $self->env->{PATH_INFO};
193 9 100       15 my @params = @{ $self->env->{'spore.params'} || [] };
  9         183  
194              
195 9         14 my $j = 0;
196 9         23 for ( my $i = 0; $i < scalar @params; $i++ ) {
197 3         5 my $key = $params[$i];
198 3         5 my $value = $params[++$i];
199              
200 3 100       6 $value = (defined $value) ? $value : '' ;
201 3 100       8 if (! length($value)) {
202 1         2 $query_string .= $key;
203 1         15 last;
204             }
205              
206             # add params as string vide to query_string even it's undefined
207 2 50 33     6 unless ( $path && $path =~ s/\:$key/$value/ ) {
208 2         5 $query_string .= $key . '=' . $self->_safe_uri_escape($value);
209 2 50 50     43 $query_string .= '&' if $query_string && scalar @params;
210             }
211             }
212              
213 9 100       20 $query_string =~ s/&$// if $query_string;
214 9         199 $self->env->{QUERY_STRING} = $query_string;
215              
216 9         21 return ( $path, $query_string );
217             }
218              
219             sub _uri_base {
220 52     52   100 my $self = shift;
221 52         1284 my $env = $self->env;
222              
223             my $uri =
224             ( $env->{'spore.url_scheme'} || "http" ) . "://"
225             . (
226             defined $env->{'spore.userinfo'}
227             ? $env->{'spore.userinfo'} . '@'
228             : ''
229             )
230             . (
231             $env->{HTTP_HOST}
232             || (( $env->{SERVER_NAME} || "" ) . ":"
233             . ( $env->{SERVER_PORT} || 80 ) )
234 52 50 100     634 ) . ( $env->{SCRIPT_NAME} || '/' );
      66        
      100        
235              
236 52         150 return $uri;
237             }
238              
239             # stolen from HTTP::Request::Common
240             sub _boundary {
241 2     2   4 my ( $self, $size ) = @_;
242              
243 2 50       4 return "xYzZy" unless $size;
244              
245 2         66 my $b =
246             MIME::Base64::encode( join( "", map chr( rand(256) ), 1 .. $size * 3 ),
247             "" );
248 2         13 $b =~ s/[\W]/X/g;
249 2         6 return $b;
250             }
251              
252             sub _form_data {
253 2     2   5 my ( $self, $data ) = @_;
254              
255 2         3 my $form_data;
256 2         5 foreach my $k ( keys %$data ) {
257             push @$form_data,
258             'Content-Disposition: form-data; name="'
259             . $k
260             . '"'."\r\n\r\n"
261 1         5 . $data->{$k};
262             }
263              
264 2         6 my $b = $self->_boundary(10);
265 2         3 my $t = [];
266 2         5 foreach (@$form_data) {
267 1         6 push @$t, '--', $b, "\r\n", $_, "\r\n";
268             }
269 2         5 push @$t, '--', $b, , '--', "\r\n";
270 2         6 my $content = join("", @$t);
271 2         6 return ($content, $b);
272             }
273              
274             sub new_response {
275 24     24 1 16763 my $self = shift;
276 24         250 my $res = Net::HTTP::Spore::Response->new(@_);
277 24         100 $res->request($self);
278 24         108 $res;
279             }
280              
281             sub finalize {
282 42     42 1 8558 my $self = shift;
283              
284 42         1132 my $path_info = $self->env->{PATH_INFO};
285              
286 42         982 my $form_data = $self->env->{'spore.form_data'};
287 42         992 my $headers = $self->env->{'spore.headers'};
288 42   100     971 my $params = $self->env->{'spore.params'} || [];
289              
290 42         97 my $query = [];
291 42         83 my $form = {};
292              
293 42         80 my $path_escape_class = '^A-Za-z0-9\-\._~/@\:';
294              
295 42         139 for ( my $i = 0 ; $i < scalar @$params ; $i++ ) {
296 16         28 my $k = $params->[$i];
297 16         27 my $v = $params->[++$i];
298 16 100 50     71 $v = $self->_safe_uri_escape($v || '', $path_escape_class) if defined $v;
299 16         1021 my $modified = 0;
300              
301 16 100 100     164 if ($path_info && $path_info =~ s/\:$k/=$k/) {
302 3         8 $modified++;
303             }
304              
305 16         44 foreach my $f_k (keys %$form_data) {
306 2         4 my $f_v = $form_data->{$f_k};
307 2 100       17 if ($f_v =~ s/^\:$k/$v/) {
308 1         3 $form->{$f_k} = $f_v;
309 1         3 $modified++;
310             }
311             }
312              
313 16         36 foreach my $h_k (keys %$headers) {
314 4         8 my $h_v = $headers->{$h_k};
315 4 100       34 if ($h_v =~ s/^\:$k/$v/) {
316 2         10 $self->header($h_k => $h_v);
317 2         92 $modified++;
318             }
319             }
320              
321 16 100       43 if ($modified == 0) {
322 10 100       26 if (defined $v) {
323 9         37 push @$query, $k.'='.$v;
324             }else{
325 1         3 push @$query, $k;
326             }
327             }
328             }
329              
330             # clean remaining :name in url
331 42 100       108 if ($path_info) {
332 34         89 $path_info =~ s/:\w+//g;
333              
334 34         116 for (my $i = 0; $i < @$params; $i+=2) {
335 13         33 my ($k, $v) = @$params[$i,$i+1];
336              
337 13         91 $path_info =~ s/=$k/$v/;
338             }
339             }
340              
341              
342              
343 42         77 my $query_string;
344 42 100       113 if (scalar @$query) {
345 9         21 $query_string = join('&', @$query);
346             }
347              
348 42         1051 $self->env->{PATH_INFO} = $path_info;
349 42         974 $self->env->{QUERY_STRING} = $query_string;
350              
351 42   100     241 my $uri = $self->uri($path_info, $query_string || '');
352              
353 42         20331 my $request = HTTP::Request->new(
354             $self->method => $uri, $self->headers
355             );
356              
357 42 100       5830 if ( keys %$form_data ) {
358 2         55 $self->env->{'spore.form_data'} = $form;
359 2         7 my ( $content, $b ) = $self->_form_data($form);
360 2         12 $request->content($content);
361 2         50 $request->header('Content-Length' => length($content));
362 2         122 $request->header(
363             'Content-Type' => 'multipart/form-data; boundary=' . $b );
364             }
365              
366 42 100       215 if ( my $payload = $self->content ) {
367 2         24 $request->content($payload);
368 2 50       97 $request->header(
369             'Content-Type' => 'application/x-www-form-urlencoded' )
370             unless $request->header('Content-Type');
371             }
372              
373 42         477 return $request;
374             }
375              
376             1;
377              
378             __END__
379              
380             =pod
381              
382             =encoding UTF-8
383              
384             =head1 NAME
385              
386             Net::HTTP::Spore::Request - Net::HTTP::Spore::Request - Portable HTTP request object from SPORE env hash
387              
388             =head1 VERSION
389              
390             version 0.09
391              
392             =head1 SYNOPSIS
393              
394             use Net::HTTP::Spore::Request;
395              
396             my $request = Net::HTTP::Spore::Request->new($env);
397              
398             =head1 DESCRIPTION
399              
400             Net::HTTP::Spore::Request create a HTTP request
401              
402             =head1 METHODS
403              
404             =over 4
405              
406             =item new
407              
408             my $req = Net::HTTP::Spore::Request->new();
409              
410             Creates a new Net::HTTP::Spore::Request object.
411              
412             =item env
413              
414             my $env = $request->env;
415              
416             Get the environment for the given request
417              
418             =item method
419              
420             my $method = $request->method;
421              
422             Get the HTTP method for the given request
423              
424             =item port
425              
426             my $port = $request->port;
427              
428             Get the HTTP port from the URL
429              
430             =item script_name
431              
432             my $script_name = $request->script_name;
433              
434             Get the script name part from the URL
435              
436             =item path
437              
438             =item path_info
439              
440             my $path = $request->path_info;
441              
442             Get the path info part from the URL
443              
444             =item request_uri
445              
446             my $request_uri = $request->request_uri;
447              
448             Get the request uri from the URL
449              
450             =item scheme
451              
452             my $scheme = $request->scheme;
453              
454             Get the scheme from the URL
455              
456             =item secure
457              
458             my $secure = $request->secure;
459              
460             Return true if the URL is HTTPS
461              
462             =item content
463              
464             =item body
465              
466             =item input
467              
468             my $input = $request->input;
469              
470             Get the content that will be posted
471              
472             =item query_string
473              
474             =item headers
475              
476             =item header
477              
478             =item uri
479              
480             =item query_parameters
481              
482             =item base
483              
484             =item new_response
485              
486             =item finalize
487              
488             =back
489              
490             =head1 AUTHORS
491              
492             =over 4
493              
494             =item *
495              
496             Franck Cuny <franck.cuny@gmail.com>
497              
498             =item *
499              
500             Ash Berlin <ash@cpan.org>
501              
502             =item *
503              
504             Ahmad Fatoum <athreef@cpan.org>
505              
506             =back
507              
508             =head1 COPYRIGHT AND LICENSE
509              
510             This software is copyright (c) 2012 by Linkfluence.
511              
512             This is free software; you can redistribute it and/or modify it under
513             the same terms as the Perl 5 programming language system itself.
514              
515             =cut