File Coverage

blib/lib/POE/Component/Client/HTTP/RequestFactory.pm
Criterion Covered Total %
statement 119 133 89.4
branch 51 72 70.8
condition 26 40 65.0
subroutine 21 21 100.0
pod 11 11 100.0
total 228 277 82.3


line stmt bran cond sub pod time code
1             package POE::Component::Client::HTTP::RequestFactory;
2             # vim: ts=2 sw=2 expandtab
3             $POE::Component::Client::HTTP::RequestFactory::VERSION = '0.949';
4 21     21   22858 use strict;
  21         42  
  21         839  
5 21     21   118 use warnings;
  21         40  
  21         883  
6              
7 21     21   126 use Carp;
  21         37  
  21         1489  
8 21     21   16449 use POE::Component::Client::HTTP::Request;
  21         67  
  21         229  
9 21     21   1001 use POE::Component::Client::HTTP;
  21         46  
  21         1025  
10              
11             use constant {
12 21         2845 FCT_AGENT => 0,
13             FCT_STREAMING => 1,
14             FCT_MAXSIZE => 2,
15             FCT_PROTOCOL => 3,
16             FCT_COOKIEJAR => 4,
17             FCT_FROM => 5,
18             FCT_NOPROXY => 6,
19             FCT_HTTP_PROXY => 7,
20             FCT_FOLLOWREDIRECTS => 8,
21             FCT_TIMEOUT => 9,
22 21     21   107 };
  21         39  
23              
24 21     21   111 use constant DEBUG => 0;
  21         38  
  21         888  
25              
26 21     21   103 use constant DEFAULT_BLOCK_SIZE => 4096;
  21         39  
  21         21130  
27              
28              
29             =head1 NAME
30              
31             POE::Component::Client::HTTP::RequestFactory - an HTTP request factory object
32              
33             =head1 VERSION
34              
35             version 0.949
36              
37             =head1 SYNOPSIS
38              
39             # Used internally by POE::Component::Client::HTTP
40              
41             =head1 CONSTRUCTOR
42              
43             =head2 new
44              
45             Create a new request factory object. It expects its parameters in a
46             hashref.
47              
48             The following parameters are accepted. They are explained in detail
49             in L.
50              
51             =over 4
52              
53             =item
54              
55             Agent
56              
57             =item
58              
59             MaxSize
60              
61             =item
62              
63             Streaming
64              
65             =item
66              
67             Protocol
68              
69             =item
70              
71             From
72              
73             =item
74              
75             CookieJar
76              
77             =item
78              
79             NoProxy
80              
81             =item
82              
83             Proxy
84              
85             =item
86              
87             FollowRedirects
88              
89             =item
90              
91             Timeout
92              
93             =back
94              
95             =cut
96              
97              
98             sub new {
99 32     32 1 6539 my ($class, $params) = @_;
100              
101 32 100 100     553 croak __PACKAGE__ . "expects its arguments in a hashref"
102             unless (!defined ($params) or ref($params) eq 'HASH');
103              
104             # Accept an agent, or a reference to a list of agents.
105 30         80 my $agent = delete $params->{Agent};
106 30 100       151 $agent = [] unless defined $agent;
107 30 100       110 $agent = [ $agent ] unless ref($agent);
108 30 100       162 unless (ref($agent) eq "ARRAY") {
109 1         141 croak "Agent must be a scalar or a reference to a list of agent strings";
110             }
111              
112 29         63 my $v = $POE::Component::Client::HTTP::VERSION;
113 29 50       97 $v = "0.000" unless defined $v;
114              
115 29 100       591 push(
116             @$agent,
117             sprintf(
118             'POE-Component-Client-HTTP/%s (perl; N; POE; en; rv:%f)',
119             $v, $v
120             )
121             ) unless @$agent;
122              
123 29         68 my $max_size = delete $params->{MaxSize};
124              
125 29         72 my $streaming = delete $params->{Streaming};
126              
127 29         59 my $protocol = delete $params->{Protocol};
128 29 100 100     156 $protocol = 'HTTP/1.1' unless defined $protocol and length $protocol;
129              
130 29         65 my $cookie_jar = delete $params->{CookieJar};
131 29         56 my $from = delete $params->{From};
132 29         63 my $no_proxy = delete $params->{NoProxy};
133 29         63 my $proxy = delete $params->{Proxy};
134 29   100     166 my $follow_redirects = delete $params->{FollowRedirects} || 0;
135 29         77 my $timeout = delete $params->{Timeout};
136              
137             # Process HTTP_PROXY and NO_PROXY environment variables.
138              
139 29 100 33     303 $proxy = $ENV{HTTP_PROXY} || $ENV{http_proxy} unless defined $proxy;
140 29 50 33     238 $no_proxy = $ENV{NO_PROXY} || $ENV{no_proxy} unless defined $no_proxy;
141              
142             # Translate environment variable formats into internal versions.
143              
144 29 100       123 $class->parse_proxy($proxy) if defined $proxy;
145              
146 27 50       113 if (defined $no_proxy) {
147 0 0       0 unless (ref($no_proxy) eq 'ARRAY') {
148 0         0 $no_proxy = [ split(/\s*\,\s*/, $no_proxy) ];
149             }
150             }
151              
152 27 100 66     162 $timeout = 180 unless (defined $timeout and $timeout > 0);
153              
154 27         117 my $self = [
155             $agent, # FCT_AGENT
156             $streaming, # FCT_STREAMING
157             $max_size, # FCT_MAXSIZE
158             $protocol, # FCT_PROTOCOL
159             $cookie_jar, # FCT_COOKIEJAR
160             $from, # FCT_FROM
161             $no_proxy, # FCT_NOPROXY
162             $proxy, # FCT_HTTP_PROXY
163             $follow_redirects, # FCT_FOLLOWREDIRECTS
164             $timeout, # FCT_TIMEOUT
165             ];
166              
167 27         162 return bless $self, $class;
168             }
169              
170              
171             =head1 METHODS
172              
173             =head2 timeout [$timeout]
174              
175             Method that lets you query and/or change the timeout value for requests
176             created by this factory.
177              
178             =cut
179              
180              
181             sub timeout {
182 312     312 1 607 my ($self, $timeout) = @_;
183              
184 312 50       913 if (defined $timeout) {
185 0         0 $self->[FCT_TIMEOUT] = $timeout;
186             }
187 312         1921 return $self->[FCT_TIMEOUT];
188             }
189              
190              
191             =head2 is_streaming
192              
193             Accessor for the Streaming parameter
194              
195             =cut
196              
197              
198             sub is_streaming {
199 65     65 1 102 my ($self) = @_;
200              
201 65         77 DEBUG and warn(
202             "FCT: this is "
203             . ($self->[FCT_STREAMING] ? "" : "not ")
204             . "streaming"
205             );
206 65         215 return $self->[FCT_STREAMING];
207             }
208              
209              
210             =head2 agent
211              
212             Accessor to the Agent parameter
213              
214             =cut
215              
216              
217             sub agent {
218 55     55 1 111 my ($self) = @_;
219              
220 55         130 return $self->[FCT_AGENT]->[rand @{$self->[FCT_AGENT]}];
  55         1575  
221             }
222              
223              
224             =head2 from
225              
226             getter/setter for the From parameter
227              
228             =cut
229              
230              
231             sub from {
232 59     59 1 118 my ($self) = @_;
233              
234 59 50 33     301 if (defined $self->[FCT_FROM] and length $self->[FCT_FROM]) {
235 0         0 return $self->[FCT_FROM];
236             }
237 59         215 return undef;
238             }
239              
240              
241             =head2 create_request
242              
243             Creates a new L
244              
245             =cut
246              
247              
248             sub create_request {
249 59     59 1 170 my ($self, $http_request, $response_event, $tag,
250             $progress_event, $proxy_override, $sender) = @_;
251              
252             # Add a protocol if one isn't included.
253 59 100 66     557 $http_request->protocol( $self->[FCT_PROTOCOL] ) unless (
254             defined $http_request->protocol()
255             and length $http_request->protocol()
256             );
257              
258             # Add the User-Agent: header if one isn't included.
259 59 100       1886 unless (defined $http_request->user_agent()) {
260 55         3345 $http_request->user_agent($self->agent);
261             }
262              
263             # Add a From: header if one isn't included.
264 59 50       2216 if (defined $self->from) {
265 0         0 my $req_from = $http_request->from();
266 0 0 0     0 unless (defined $req_from and length $req_from) {
267 0         0 $http_request->from( $self->from );
268             }
269             }
270              
271             # Add a Content-Length header if this request has content but
272             # doesn't have a Content-Length header already. Also, don't do it
273             # if the content is a reference, as this means we're streaming via
274             # callback.
275 59 50 100     438 if (
      66        
276             length($http_request->content()) and
277             !ref($http_request->content()) and
278             !$http_request->content_length()
279             ) {
280 21     21   15187 use bytes;
  21         148  
  21         605  
281 0         0 $http_request->content_length(length($http_request->content()));
282             }
283              
284 59         1223 my ($last_request, $postback);
285 59 100       214 if (ref($response_event) eq 'POE::Component::Client::HTTP::Request') {
286 4         10 $last_request = $response_event;
287 4         19 $postback = $last_request->postback;
288             }
289             else {
290 55         334 $postback = $sender->postback( $response_event, $http_request, $tag );
291             }
292             # Create a progress postback if requested.
293 59         4517 my $progress_postback;
294 59 100       252 if (defined $progress_event) {
295 1 50       4 if (ref $progress_event) {
296             # The given progress event appears to already
297             # be a postback, so use it. This is needed to
298             # propagate the postback through redirects.
299 0         0 $progress_postback = $progress_event;
300             }
301             else {
302 1         6 $progress_postback = $sender->postback(
303             $progress_event,
304             $http_request,
305             $tag
306             );
307             }
308             }
309              
310             # If we have a cookie jar, have it add the appropriate headers.
311             # LWP rocks!
312              
313 59 50       277 if (defined $self->[FCT_COOKIEJAR]) {
314 0         0 $self->[FCT_COOKIEJAR]->add_cookie_header($http_request);
315             }
316              
317             # MEXNIX 2002-06-01: If we have a proxy set, and the request URI is
318             # not in our no_proxy, then use the proxy. Otherwise use the
319             # request URI.
320             #
321             # RCAPUTO 2006-03-23: We only support http proxying right now.
322             # Avoid proxying if this isn't an http request.
323              
324             # TODO CONNECT - Create a PCCH::Request object in https-CONNECT mode
325             # if we're using https and there's an appropriate proxy.
326              
327 59         115 my $proxy = $proxy_override;
328 59 50       223 if ($http_request->uri->scheme() eq "http") {
329 59   100     1458 $proxy ||= $self->[FCT_HTTP_PROXY];
330             }
331              
332 59 100       199 if (defined $proxy) {
333             # This request qualifies for proxying. Replace the host and port
334             # with the proxy's host and port. This comes after the Host:
335             # header is set, so it doesn't break the request object.
336 8         27 my $host = $http_request->uri->host;
337              
338 8 50 33     285 undef $proxy if (
339             !defined($host) or
340             _in_no_proxy ($host, $self->[FCT_NOPROXY])
341             );
342             }
343              
344 59         2702 my $request = POE::Component::Client::HTTP::Request->new (
345             Request => $http_request,
346             Proxy => $proxy,
347             Postback => $postback,
348             #Tag => $tag, # TODO - Is this needed for anything?
349             Progress => $progress_postback,
350             Factory => $self,
351             );
352              
353 59 100       245 if (defined $last_request) {
354 4         22 $request->does_redirect($last_request);
355             }
356 59         229 return $request;
357             }
358              
359              
360             # Determine whether a host is in a no-proxy list.
361              
362             sub _in_no_proxy {
363 8     8   20 my ($host, $no_proxy) = @_;
364              
365 8         22 foreach my $no_proxy_domain (@$no_proxy) {
366 0 0       0 return 1 if $host =~ /\Q$no_proxy_domain\E$/i;
367             }
368 8         59 return 0;
369             }
370              
371              
372             =head2 max_response_size
373              
374             Method to retrieve the maximum size of a response, as set by the
375             C parameter to L's C method.
376              
377             =cut
378              
379              
380             sub max_response_size {
381 116     116 1 516 my ($self) = @_;
382              
383 116         517 return $self->[FCT_MAXSIZE];
384             }
385              
386              
387             =head2 block_size
388              
389             Accessor for the Streaming parameter
390              
391             =cut
392              
393              
394             sub block_size {
395 53     53 1 132 my ($self) = @_;
396              
397 53   100     5817 my $block_size = $self->[FCT_STREAMING] || DEFAULT_BLOCK_SIZE;
398 53 50       194 $block_size = DEFAULT_BLOCK_SIZE if $block_size < 1;
399              
400 53         187 return $block_size;
401             }
402              
403              
404             =head2 frob_cookies $response
405              
406             Store the cookies from the L parameter passed into
407             our cookie jar
408              
409             =cut
410              
411              
412             sub frob_cookies {
413 112     112 1 224 my ($self, $response) = @_;
414              
415 112 50       409 if (defined $self->[FCT_COOKIEJAR]) {
416 0         0 $self->[FCT_COOKIEJAR] ->extract_cookies($response);
417             }
418             }
419              
420              
421             =head2 max_redirect_count [$count]
422              
423             Function to get/set the maximum number of redirects to follow
424             automatically. This allows you to retrieve or modify the value
425             you passed with the FollowRedirects parameter to L's
426             C method.
427              
428             =cut
429              
430              
431             sub max_redirect_count {
432 48     48 1 199 my ($self, $count) = @_;
433              
434 48 50       703 if (defined $count) {
435 0         0 $self->[FCT_FOLLOWREDIRECTS] = $count;
436             }
437 48         230 return $self->[FCT_FOLLOWREDIRECTS];
438             }
439              
440              
441             =head2 parse_proxy $proxy
442              
443             This static method is used for parsing proxies. The $proxy can be
444             array reference like [host, port] or comma separated string like
445             "http://1.2.3.4:80/,http://2.3.4.5:80/".
446              
447             parse_proxy() returns an array reference of two-element tuples (also
448             array ferences), each containing a host and a port:
449              
450             [ [ host1, port1 ],
451             [ host2, port2 ],
452             ...
453             ]
454              
455             =cut
456              
457              
458             sub parse_proxy {
459 11     11 1 24 my $proxy = $_[1];
460              
461 11 100       32 if (ref($proxy) eq 'ARRAY') {
462 2 100       123 croak "Proxy must contain [HOST,PORT]" unless @$proxy == 2;
463 1         3 $proxy = [ $proxy ];
464             } else {
465 9         44 my @proxies = split /\s*\,\s*/, $proxy;
466 9         20 foreach (@proxies) {
467 9         43 s/^http:\/+//;
468 9         36 s/\/+$//;
469 9 100       161 croak "Proxy must contain host:port" unless /^(.+):(\d+)$/;
470 8         49 $_ = [ $1, $2 ];
471             }
472 8 50       30 if (@proxies) {
473 8         20 $proxy = \@proxies;
474             } else {
475 0         0 undef $proxy; # Empty proxy list means not to use proxy
476             }
477             }
478              
479 9         28 $_[1] = $proxy;
480             }
481              
482             1;