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.08';
3             # ABSTRACT: Net::HTTP::Spore::Request - Portable HTTP request object from SPORE env hash
4              
5 28     28   361395 use Moose;
  28         3011591  
  28         184  
6 28     28   181044 use Carp ();
  28         67  
  28         511  
7 28     28   3562 use URI;
  28         29119  
  28         741  
8 28     28   2692 use HTTP::Headers;
  28         44704  
  28         709  
9 28     28   2196 use HTTP::Request;
  28         42149  
  28         797  
10 28     28   160 use URI::Escape;
  28         60  
  28         1718  
11 28     28   8797 use MIME::Base64;
  28         15402  
  28         1457  
12 28     28   2592 use Net::HTTP::Spore::Response;
  28         61  
  28         788  
13              
14 28     28   9635 use Encode qw{is_utf8};
  28         236739  
  28         50920  
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 27053 my $class = shift;
56              
57 51 50 33     393 if ( @_ == 1 && !exists $_[0]->{env} ) {
58 51         249 return { env => $_[0] };
59             }
60 0         0 return @_;
61             }
62              
63             sub _safe_uri_escape {
64 17     17   44 my ( $self, $str, $unsafe ) = @_;
65 17 50       33 return unless defined $str;
66 17 50       59 if ( is_utf8($str) ) {
67 0         0 utf8::encode($str);
68             }
69 17         55 return uri_escape( $str, $unsafe );
70             }
71              
72             sub method {
73 48     48 1 1463 my ( $self, $value ) = @_;
74 48 50       153 if ($value) {
75 0         0 $self->set_to_env( 'REQUEST_METHOD', $value );
76             }
77             else {
78 48         1757 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 5714 my ( $self, $value ) = @_;
93 4 50       11 if ($value) {
94 0         0 $self->set_to_env( 'SERVER_PORT', $value );
95             }
96             else {
97 4         125 return $self->get_from_env('SERVER_PORT');
98             }
99             }
100              
101             sub script_name {
102 2     2 1 1389 my ( $self, $value ) = @_;
103 2 50       7 if ($value) {
104 0         0 $self->set_to_env( 'SCRIPT_NAME', $value );
105             }
106             else {
107 2         71 return $self->get_from_env('SCRIPT_NAME');
108             }
109             }
110              
111             sub request_uri {
112 2     2 1 2004 my ($self, $value) = @_;
113 2 50       8 if ($value) {
114 0         0 $self->set_to_env( 'REQUEST_URI', $value );
115             }
116             else {
117 2         105 return $self->get_from_env('REQUEST_URI');
118             }
119             }
120              
121             sub scheme {
122 4     4 1 12 my ($self, $value) = @_;
123 4 50       12 if ($value) {
124 0         0 $self->set_to_env( 'spore.url_scheme', $value );
125             }
126             else {
127 4         152 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 1699 my ($self, $value) = @_;
143 47 50       129 if ($value) {
144 0         0 $self->set_to_env( 'spore.payload', $value );
145             }
146             else {
147 47         1786 return $self->get_from_env('spore.payload');
148             }
149             }
150              
151             sub base {
152 9     9 1 8837 my $self = shift;
153 9         47 URI->new( $self->_uri_base )->canonical;
154             }
155              
156 0     0 1 0 sub input { (shift)->body(@_) }
157 42     42 1 146 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 134 my ($self, $path_info, $query_string) = @_;
164              
165 43 100 66     214 if ( !defined $path_info || !defined $query_string ) {
166 9         21 my @path_info = $self->_path;
167 9 50       20 $path_info = $path_info[0] if !$path_info;
168 9 100       20 $query_string = $path_info[1] if !$query_string;
169             }
170              
171 43   100     134 $path_info //= '';
172 43         144 my $base = $self->_uri_base;
173              
174 43 100 100     228 if ( defined $query_string && length($query_string) > 0 ) {
175 9         19 my $is_interrogation = index( $path_info, '?' );
176 9 50       27 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       316 $base =~ s!/$!! if $path_info =~ m!^/!;
185 43         279 return URI->new( $base . $path_info )->canonical;
186             }
187              
188             sub _path {
189 9     9   12 my $self = shift;
190              
191 9         11 my $query_string;
192 9         201 my $path = $self->env->{PATH_INFO};
193 9 100       13 my @params = @{ $self->env->{'spore.params'} || [] };
  9         191  
194              
195 9         14 my $j = 0;
196 9         21 for ( my $i = 0; $i < scalar @params; $i++ ) {
197 3         6 my $key = $params[$i];
198 3         5 my $value = $params[++$i];
199              
200 3 100       7 $value = (defined $value) ? $value : '' ;
201 3 100       7 if (! length($value)) {
202 1         2 $query_string .= $key;
203 1         21 last;
204             }
205              
206             # add params as string vide to query_string even it's undefined
207 2 50 33     5 unless ( $path && $path =~ s/\:$key/$value/ ) {
208 2         6 $query_string .= $key . '=' . $self->_safe_uri_escape($value);
209 2 50 50     45 $query_string .= '&' if $query_string && scalar @params;
210             }
211             }
212              
213 9 100       21 $query_string =~ s/&$// if $query_string;
214 9         201 $self->env->{QUERY_STRING} = $query_string;
215              
216 9         23 return ( $path, $query_string );
217             }
218              
219             sub _uri_base {
220 52     52   110 my $self = shift;
221 52         1479 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     754 ) . ( $env->{SCRIPT_NAME} || '/' );
      66        
      100        
235              
236 52         195 return $uri;
237             }
238              
239             # stolen from HTTP::Request::Common
240             sub _boundary {
241 2     2   3 my ( $self, $size ) = @_;
242              
243 2 50       5 return "xYzZy" unless $size;
244              
245 2         65 my $b =
246             MIME::Base64::encode( join( "", map chr( rand(256) ), 1 .. $size * 3 ),
247             "" );
248 2         19 $b =~ s/[\W]/X/g;
249 2         6 return $b;
250             }
251              
252             sub _form_data {
253 2     2   4 my ( $self, $data ) = @_;
254              
255 2         2 my $form_data;
256 2         6 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         5 my $b = $self->_boundary(10);
265 2         5 my $t = [];
266 2         5 foreach (@$form_data) {
267 1         3 push @$t, '--', $b, "\r\n", $_, "\r\n";
268             }
269 2         8 push @$t, '--', $b, , '--', "\r\n";
270 2         8 my $content = join("", @$t);
271 2         9 return ($content, $b);
272             }
273              
274             sub new_response {
275 24     24 1 21934 my $self = shift;
276 24         243 my $res = Net::HTTP::Spore::Response->new(@_);
277 24         118 $res->request($self);
278 24         116 $res;
279             }
280              
281             sub finalize {
282 42     42 1 8630 my $self = shift;
283              
284 42         1278 my $path_info = $self->env->{PATH_INFO};
285              
286 42         1116 my $form_data = $self->env->{'spore.form_data'};
287 42         1051 my $headers = $self->env->{'spore.headers'};
288 42   100     1083 my $params = $self->env->{'spore.params'} || [];
289              
290 42         95 my $query = [];
291 42         89 my $form = {};
292              
293 42         96 my $path_escape_class = '^A-Za-z0-9\-\._~/@\:';
294              
295 42         159 for ( my $i = 0 ; $i < scalar @$params ; $i++ ) {
296 16         33 my $k = $params->[$i];
297 16         27 my $v = $params->[++$i];
298 16 100 50     70 $v = $self->_safe_uri_escape($v || '', $path_escape_class) if defined $v;
299 16         1059 my $modified = 0;
300              
301 16 100 100     191 if ($path_info && $path_info =~ s/\:$k/=$k/) {
302 3         6 $modified++;
303             }
304              
305 16         54 foreach my $f_k (keys %$form_data) {
306 2         4 my $f_v = $form_data->{$f_k};
307 2 100       15 if ($f_v =~ s/^\:$k/$v/) {
308 1         3 $form->{$f_k} = $f_v;
309 1         3 $modified++;
310             }
311             }
312              
313 16         41 foreach my $h_k (keys %$headers) {
314 4         9 my $h_v = $headers->{$h_k};
315 4 100       41 if ($h_v =~ s/^\:$k/$v/) {
316 2         10 $self->header($h_k => $h_v);
317 2         89 $modified++;
318             }
319             }
320              
321 16 100       43 if ($modified == 0) {
322 10 100       30 if (defined $v) {
323 9         42 push @$query, $k.'='.$v;
324             }else{
325 1         4 push @$query, $k;
326             }
327             }
328             }
329              
330             # clean remaining :name in url
331 42 100       162 if ($path_info) {
332 34         96 $path_info =~ s/:\w+//g;
333              
334 34         166 for (my $i = 0; $i < @$params; $i+=2) {
335 13         37 my ($k, $v) = @$params[$i,$i+1];
336              
337 13         100 $path_info =~ s/=$k/$v/;
338             }
339             }
340              
341              
342              
343 42         72 my $query_string;
344 42 100       123 if (scalar @$query) {
345 9         22 $query_string = join('&', @$query);
346             }
347              
348 42         1156 $self->env->{PATH_INFO} = $path_info;
349 42         1086 $self->env->{QUERY_STRING} = $query_string;
350              
351 42   100     277 my $uri = $self->uri($path_info, $query_string || '');
352              
353 42         20940 my $request = HTTP::Request->new(
354             $self->method => $uri, $self->headers
355             );
356              
357 42 100       6178 if ( keys %$form_data ) {
358 2         52 $self->env->{'spore.form_data'} = $form;
359 2         6 my ( $content, $b ) = $self->_form_data($form);
360 2         15 $request->content($content);
361 2         55 $request->header('Content-Length' => length($content));
362 2         146 $request->header(
363             'Content-Type' => 'multipart/form-data; boundary=' . $b );
364             }
365              
366 42 100       251 if ( my $payload = $self->content ) {
367 2         18 $request->content($payload);
368 2 50       67 $request->header(
369             'Content-Type' => 'application/x-www-form-urlencoded' )
370             unless $request->header('Content-Type');
371             }
372              
373 42         391 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.08
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