File Coverage

blib/lib/Net/HTTP/Knork/Request.pm
Criterion Covered Total %
statement 80 152 52.6
branch 14 44 31.8
condition 12 32 37.5
subroutine 18 31 58.0
pod 13 19 68.4
total 137 278 49.2


line stmt bran cond sub pod time code
1             package Net::HTTP::Knork::Request;
2              
3             # ABSTRACT: HTTP request object from SPORE env hash
4              
5 6     6   23 use Moo;
  6         7  
  6         39  
6 6     6   1589 use Carp;
  6         14  
  6         306  
7 6     6   24 use URI;
  6         6  
  6         112  
8 6     6   24 use HTTP::Headers;
  6         6  
  6         122  
9 6     6   19 use HTTP::Request;
  6         6  
  6         92  
10 6     6   20 use URI::Escape;
  6         10  
  6         296  
11 6     6   2765 use MIME::Base64;
  6         3434  
  6         324  
12 6     6   2067 use Net::HTTP::Knork::Response;
  6         19  
  6         9197  
13              
14             has env => (
15             is => 'rw',
16             required => 1,
17             default => sub { { } },
18             );
19              
20             sub get_from_env {
21 16     16 0 131 return $_[0]->env->{$_[1]};
22             }
23              
24             sub set_to_env {
25 0     0 0 0 $_[0]->env->{$_[1]} = $_[2];
26             }
27              
28             has path => (
29             is => 'rw',
30             lazy => 1,
31             default => sub { $_[0]->env->{PATH_INFO} }
32             );
33              
34             has headers => (
35             is => 'rw',
36             lazy => 1,
37             handles => {
38             header => 'header',
39             },
40             default => sub {
41             my $self = shift;
42             my $env = $self->env;
43             my $h = HTTP::Headers->new(
44             map {
45             ( my $field = $_ ) =~ s/^HTTPS?_//;
46             ( $field => $env->{$_} );
47             } grep { /^(?:HTTP|CONTENT)/i } keys %$env
48             );
49             return $h;
50             },
51             );
52              
53             sub BUILDARGS {
54 8     8 0 4550 my $class = shift;
55              
56 8 50 33     57 if (@_ == 1 && !exists $_[0]->{env}) {
57 8         213 return {env => $_[0]};
58             }
59 0         0 return @_;
60             }
61              
62             sub set_or_get_from_env {
63 16     16 0 25 my ( $self, $var, $value ) = @_;
64 16 50       32 if ($value) {
65 0         0 $self->set_to_env( $var, $value );
66 0         0 return;
67             }
68             else {
69 16         35 return $self->get_from_env($var);
70             }
71             }
72              
73             sub method {
74 8     8 1 12 my ( $self, $value ) = @_;
75 8         23 return $self->set_or_get_from_env( 'REQUEST_METHOD', $value );
76             }
77              
78             sub host {
79 0     0 0 0 my ( $self, $value ) = @_;
80 0         0 return $self->set_or_get_from_env( 'SERVER_NAME', $value );
81             }
82              
83             sub port {
84 0     0 1 0 my ( $self, $value ) = @_;
85 0         0 return $self->set_or_get_from_env( 'SERVER_PORT', $value );
86             }
87              
88             sub script_name {
89 0     0 1 0 my ( $self, $value ) = @_;
90 0         0 return $self->set_or_get_from_env( 'SCRIPT_NAME', $value );
91             }
92              
93             sub request_uri {
94 0     0 1 0 my ( $self, $value ) = @_;
95 0         0 return $self->set_or_get_from_env( 'REQUEST_URI', $value );
96             }
97              
98             sub scheme {
99 0     0 1 0 my ( $self, $value ) = @_;
100 0         0 return $self->set_or_get_from_env( 'spore.url_scheme', $value );
101             }
102              
103             sub logger {
104 0     0 0 0 my ( $self, $value ) = @_;
105 0         0 return $self->set_or_get_from_env( 'sporex.logger', $value );
106             }
107              
108             sub body {
109 8     8 1 9 my ( $self, $value ) = @_;
110 8         16 return $self->set_or_get_from_env( 'spore.payload', $value );
111             }
112              
113             sub base {
114 0     0 1 0 my $self = shift;
115 0         0 URI->new( $self->_uri_base )->canonical;
116             }
117              
118 0     0 1 0 sub input { (shift)->body(@_) }
119 8     8 1 25 sub content { (shift)->body(@_) }
120 0     0 1 0 sub secure { $_[0]->scheme eq 'https' }
121              
122             # TODO
123             # need to refactor this method, with path_info and query_string construction
124             sub uri {
125 8     8 1 15 my ($self, $path_info, $query_string) = @_;
126              
127 8 50 33     40 if ( !defined $path_info || !defined $query_string ) {
128 0         0 ($path_info,$query_string) = $self->_path;
129             }
130              
131 8         26 my $base = $self->_uri_base;
132              
133 8         10 my $path_escape_class = '^A-Za-z0-9\-\._~/';
134              
135 8   50     39 my $path = URI::Escape::uri_escape($path_info // '', $path_escape_class);
136              
137 8 50 33     875 if (defined $query_string && length($query_string) > 0) {
138 0         0 $path .= '?' . $query_string;
139             }
140              
141 8 50       47 $base =~ s/\/$// if $path =~ m/^\//;
142 8         41 return URI->new( $base . $path )->canonical;
143             }
144              
145             sub _path {
146 0     0   0 my $self = shift;
147              
148 0         0 my $query_string;
149 0         0 my $path = $self->env->{PATH_INFO};
150 0 0       0 my @params = @{ $self->env->{'spore.params'} || [] };
  0         0  
151              
152 0         0 my $j = 0;
153 0         0 for (my $i = 0; $i < scalar @params; $i++) {
154 0         0 my $key = $params[$i];
155 0         0 my $value = $params[++$i];
156 0 0       0 if (!$value) {
157 0         0 $query_string .= $key;
158 0         0 last;
159             }
160 0 0 0     0 unless ( $path && $path =~ s/\:$key/$value/ ) {
161 0         0 $query_string .= $key . '=' . $value;
162 0 0 0     0 $query_string .= '&' if $query_string && scalar @params;
163             }
164             }
165              
166 0 0       0 $query_string =~ s/&$// if $query_string;
167 0         0 return ( $path, $query_string );
168             }
169              
170             sub _uri_base {
171 8     8   12 my $self = shift;
172 8         24 my $env = $self->env;
173              
174 8 50 50     149 my $uri =
      33        
      50        
175             ( $env->{'spore.url_scheme'} || "http" ) . "://"
176             . (
177             defined $env->{'spore.userinfo'}
178             ? $env->{'spore.userinfo'} . '@'
179             : ''
180             )
181             . (
182             $env->{HTTP_HOST}
183             || (( $env->{SERVER_NAME} || "" ) . ":"
184             . ( $env->{SERVER_PORT} || 80 ) )
185             ) . ( $env->{SCRIPT_NAME} || '/' );
186              
187 8         18 return $uri;
188             }
189              
190             # stolen from HTTP::Request::Common
191             sub _boundary {
192 0     0   0 my ( $self, $size ) = @_;
193              
194 0 0       0 return "xYzZy" unless $size;
195              
196 0         0 my $b =
197             MIME::Base64::encode( join( "", map chr( rand(256) ), 1 .. $size * 3 ),
198             "" );
199 0         0 $b =~ s/[\W]/X/g;
200 0         0 return $b;
201             }
202              
203             sub _form_data {
204 0     0   0 my ( $self, $data ) = @_;
205              
206 0         0 my $form_data;
207 0         0 foreach my $k ( keys %$data ) {
208 0         0 push @$form_data,
209             'Content-Disposition: form-data; name="'
210             . $k
211             . '"'."\r\n\r\n"
212             . $data->{$k};
213             }
214              
215 0         0 my $b = $self->_boundary(10);
216 0         0 my $t = [];
217 0         0 foreach (@$form_data) {
218 0         0 push @$t, '--', $b, "\r\n", $_, "\r\n";
219             }
220 0         0 push @$t, '--', $b, , '--', "\r\n";
221 0         0 my $content = join("", @$t);
222 0         0 return ($content, $b);
223             }
224              
225             sub new_response {
226 8     8 1 215 my $self = shift;
227 8         152 my $res = Net::HTTP::Knork::Response->new(@_);
228 8         827 $res->request($self);
229 8         19 $res;
230             }
231              
232             sub finalize {
233 8     8 1 131 my $self = shift;
234              
235 8         25 my $path_info = $self->env->{PATH_INFO};
236              
237 8         26 my $form_data = $self->env->{'spore.form_data'};
238 8         19 my $headers = $self->env->{'spore.headers'};
239 8   100     48 my $params = $self->env->{'spore.params'} || [];
240              
241 8         11 my $query = [];
242 8         63 my $form = {};
243              
244 8         28 for ( my $i = 0 ; $i < scalar @$params ; $i++ ) {
245 3         5 my $k = $params->[$i];
246 3   50     14 my $v = $params->[++$i] // '';
247 3         4 my $modified = 0;
248              
249 3 50 33     44 if ($path_info && $path_info =~ s/\:$k/$v/) {
250 3         5 $modified++;
251             }
252              
253 3         12 foreach my $f_k (keys %$form_data) {
254 0         0 my $f_v = $form_data->{$f_k};
255 0 0       0 if ($f_v =~ s/^\:$k/$v/) {
256 0         0 $form->{$f_k} = $f_v;
257 0         0 $modified++;
258             }
259             }
260              
261 3         8 foreach my $h_k (keys %$headers) {
262 0         0 my $h_v = $headers->{$h_k};
263 0 0       0 if ($h_v =~ s/^\:$k/$v/) {
264 0         0 $self->header($h_k => $h_v);
265 0         0 $modified++;
266             }
267             }
268              
269 3 50       15 if ($modified == 0) {
270 0 0       0 if (defined $v) {
271 0         0 push @$query, $k.'='.$v;
272             }else{
273 0         0 push @$query, $k;
274             }
275             }
276             }
277              
278             # clean remaining :name in url
279 8 50       26 $path_info =~ s/:\w+//g if $path_info;
280              
281 8         9 my $query_string;
282 8 50       22 if (scalar @$query) {
283 0         0 $query_string = join('&', @$query);
284             }
285              
286 8         25 $self->env->{PATH_INFO} = $path_info;
287 8         17 $self->env->{QUERY_STRING} = $query_string;
288              
289 8   50     46 my $uri = $self->uri($path_info, $query_string || '');
290              
291 8         1773 my $request = HTTP::Request->new(
292             $self->method => $uri, $self->headers
293             );
294              
295 8 50       920 if ( keys %$form_data ) {
296 0         0 $self->env->{'spore.form_data'} = $form;
297 0         0 my ( $content, $b ) = $self->_form_data($form);
298 0         0 $request->content($content);
299 0         0 $request->header('Content-Length' => length($content));
300 0         0 $request->header(
301             'Content-Type' => 'multipart/form-data; boundary=' . $b );
302             }
303              
304 8 100       25 if ( my $payload = $self->content ) {
305 4         19 $request->content($payload);
306 4 50       102 $request->header(
307             'Content-Type' => 'application/x-www-form-urlencoded' )
308             unless $request->header('Content-Type');
309             }
310              
311 8         298 return $request;
312             }
313              
314             1;
315              
316              
317              
318             =pod
319              
320             =head1 NAME
321              
322             Net::HTTP::Knork::Request - HTTP request object from SPORE env hash
323              
324             =head1 VERSION
325              
326             version 0.11
327              
328             =head1 SYNOPSIS
329              
330             use Net::HTTP::Knork::Request;
331              
332             my $request = Net::HTTP::Knork::Request->new($env);
333              
334             =head1 DESCRIPTION
335              
336             Net::HTTP::Knork::Request create a HTTP request
337             Based mostly on L, except that it uses Moo.
338              
339             =head1 METHODS
340              
341             =over 4
342              
343             =item new
344              
345             my $req = Net::HTTP::Knork::Request->new();
346              
347             Creates a new Net::HTTP::Knork::Request object.
348              
349             =item env
350              
351             my $env = $request->env;
352              
353             Get the environment for the given request
354              
355             =item method
356              
357             my $method = $request->method;
358              
359             Get the HTTP method for the given request
360              
361             =item port
362              
363             my $port = $request->port;
364              
365             Get the HTTP port from the URL
366              
367             =item script_name
368              
369             my $script_name = $request->script_name;
370              
371             Get the script name part from the URL
372              
373             =item path
374              
375             =item path_info
376              
377             my $path = $request->path_info;
378              
379             Get the path info part from the URL
380              
381             =item request_uri
382              
383             my $request_uri = $request->request_uri;
384              
385             Get the request uri from the URL
386              
387             =item scheme
388              
389             my $scheme = $request->scheme;
390              
391             Get the scheme from the URL
392              
393             =item secure
394              
395             my $secure = $request->secure;
396              
397             Return true if the URL is HTTPS
398              
399             =item content
400              
401             =item body
402              
403             =item input
404              
405             my $input = $request->input;
406              
407             Get the content that will be posted
408              
409             =item query_string
410              
411             =item headers
412              
413             =item header
414              
415             =item uri
416              
417             =item query_parameters
418              
419             =item base
420              
421             =item new_response
422              
423             =item finalize
424              
425             =back
426              
427             =head1 AUTHOR
428              
429             Emmanuel Peroumalnaïk
430              
431             =head1 COPYRIGHT AND LICENSE
432              
433             This software is copyright (c) 2014 by E. Peroumalnaik.
434              
435             This is free software; you can redistribute it and/or modify it under
436             the same terms as the Perl 5 programming language system itself.
437              
438             =cut
439              
440              
441             __END__