File Coverage

blib/lib/Furl/HTTP.pm
Criterion Covered Total %
statement 311 498 62.4
branch 139 292 47.6
condition 45 100 45.0
subroutine 37 56 66.0
pod 9 18 50.0
total 541 964 56.1


line stmt bran cond sub pod time code
1             package Furl::HTTP;
2 45     45   455136 use strict;
  45         94  
  45         1284  
3 45     45   226 use warnings;
  45         82  
  45         1274  
4 45     45   219 use base qw/Exporter/;
  45         83  
  45         3928  
5 45     45   1123 use 5.008001;
  45         165  
6              
7             our $VERSION = '3.12';
8              
9 45     45   280 use Carp ();
  45         96  
  45         913  
10 45     45   10755 use Furl::ConnectionCache;
  45         119  
  45         1343  
11              
12 45     45   252 use Scalar::Util ();
  45         76  
  45         1003  
13 45     45   10889 use Errno qw(EAGAIN ECONNRESET EINPROGRESS EINTR EWOULDBLOCK ECONNABORTED EISCONN);
  45         49602  
  45         4725  
14 45     45   289 use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK SEEK_SET SEEK_END);
  45         89  
  45         2503  
15 45         7226 use Socket qw(
16             PF_INET SOCK_STREAM
17             IPPROTO_TCP
18             TCP_NODELAY
19             pack_sockaddr_in
20 45     45   14747 );
  45         150527  
21 45     45   13381 use Time::HiRes qw(time);
  45         39312  
  45         177  
22              
23 45     45   7273 use constant WIN32 => $^O eq 'MSWin32';
  45         81  
  45         3630  
24 45     45   11760 use HTTP::Parser::XS qw/HEADERS_NONE HEADERS_AS_ARRAYREF HEADERS_AS_HASHREF/;
  45         27643  
  45         232216  
25              
26             our @EXPORT_OK = qw/HEADERS_NONE HEADERS_AS_ARRAYREF HEADERS_AS_HASHREF/;
27              
28              
29             # ref. RFC 2616, 3.5 Content Codings:
30             # For compatibility with previous implementations of HTTP,
31             # applications SHOULD consider "x-gzip" and "x-compress" to be
32             # equivalent to "gzip" and "compress" respectively.
33             # ("compress" is not supported, though)
34             my %COMPRESSED = map { $_ => undef } qw(gzip x-gzip deflate);
35              
36             my $HTTP_TOKEN = '[^\x00-\x31\x7F]+';
37             my $HTTP_QUOTED_STRING = q{"([^"]+|\\.)*"};
38              
39             sub new {
40 32     32 1 129300 my $class = shift;
41 32 50       316 my %args = @_ == 1 ? %{$_[0]} : @_;
  0         0  
42              
43             my @headers = (
44 32   66     510 'User-Agent' => (delete($args{agent}) || __PACKAGE__ . '/' . $Furl::HTTP::VERSION),
45             );
46 32         129 my $connection_header = 'keep-alive';
47 32 50       160 if(defined $args{headers}) {
48 0         0 my $in_headers = delete $args{headers};
49 0         0 for (my $i = 0; $i < @$in_headers; $i += 2) {
50 0         0 my $name = $in_headers->[$i];
51 0 0       0 if (lc($name) eq 'connection') {
52 0         0 $connection_header = $in_headers->[$i + 1];
53             } else {
54 0         0 push @headers, $name, $in_headers->[$i + 1];
55             }
56             }
57             }
58             bless {
59             timeout => 10,
60             max_redirects => 7,
61             bufsize => 10*1024, # no mmap
62             headers => \@headers,
63             connection_header => $connection_header,
64             proxy => '',
65             no_proxy => '',
66             connection_pool => Furl::ConnectionCache->new(),
67             header_format => HEADERS_AS_ARRAYREF,
68       0     stop_if => sub {},
69 36     36   7362 inet_aton => sub { Socket::inet_aton($_[0]) },
70             ssl_opts => {},
71 32   100     605 capture_request => $args{capture_request} || 0,
72             inactivity_timeout => 600,
73             %args
74             }, $class;
75             }
76              
77             sub get {
78 0     0 1 0 my ( $self, $url, $headers ) = @_;
79 0         0 $self->request(
80             method => 'GET',
81             url => $url,
82             headers => $headers
83             );
84             }
85              
86             sub head {
87 0     0 1 0 my ( $self, $url, $headers ) = @_;
88 0         0 $self->request(
89             method => 'HEAD',
90             url => $url,
91             headers => $headers
92             );
93             }
94              
95             sub post {
96 0     0 1 0 my ( $self, $url, $headers, $content ) = @_;
97 0         0 $self->request(
98             method => 'POST',
99             url => $url,
100             headers => $headers,
101             content => $content
102             );
103             }
104              
105             sub put {
106 0     0 1 0 my ( $self, $url, $headers, $content ) = @_;
107 0         0 $self->request(
108             method => 'PUT',
109             url => $url,
110             headers => $headers,
111             content => $content
112             );
113             }
114              
115             sub delete {
116 0     0 1 0 my ( $self, $url, $headers, $content ) = @_;
117 0         0 $self->request(
118             method => 'DELETE',
119             url => $url,
120             headers => $headers,
121             content => $content
122             );
123             }
124              
125             sub agent {
126 5 100   5 1 47 if ( @_ == 2 ) {
127 3         38 _header_set(shift->{headers}, 'User-Agent', shift);
128             } else {
129 2         8 return _header_get(shift->{headers}, 'User-Agent');
130             }
131             }
132              
133             sub _header_set {
134 3     3   19 my ($headers, $key, $value) = (shift, lc shift, shift);
135 3         27 for (my $i=0; $i<@$headers; $i+=2) {
136 3 50       22 if (lc($headers->[$i]) eq $key) {
137 3         10 $headers->[$i+1] = $value;
138 3         20 return;
139             }
140             }
141 0         0 push @$headers, $key, $value;
142             }
143              
144             sub _header_get {
145 23     23   34309 my ($headers, $key) = (shift, lc shift);
146 23         104 for (my $i=0; $i<@$headers; $i+=2) {
147 29 100       176 return $headers->[$i+1] if lc($headers->[$i]) eq $key;
148             }
149 3         6 return undef;
150             }
151              
152             sub _requires {
153 12     12   25 my($file, $feature, $library) = @_;
154 12 100       25 return if exists $INC{$file};
155 2 50       4 unless(eval { require $file }) {
  2         676  
156 0 0       0 if ($@ =~ /^Can't locate/) {
157 0   0     0 $library ||= do {
158 0         0 local $_ = $file;
159 0         0 s/ \.pm \z//xms;
160 0         0 s{/}{::}g;
161 0         0 $_;
162             };
163 0         0 Carp::croak(
164             "$feature requires $library, but it is not available."
165             . " Please install $library using your prefer CPAN client"
166             );
167             } else {
168 0         0 die $@;
169             }
170             }
171             }
172              
173             # returns $scheme, $host, $port, $path_query
174             sub _parse_url {
175 35     35   6541 my($self, $url) = @_;
176 35 100       683 $url =~ m{\A
177             ([a-z]+) # scheme
178             ://
179             (?:
180             ([^/:@?]+) # user
181             :
182             ([^/:@?]+) # password
183             @
184             )?
185             ([^/:?]+) # host
186             (?: : (\d+) )? # port
187             (?: ( /? \? .* | / .*) )? # path_query
188             \z}xms or Carp::croak("Passed malformed URL: $url");
189 34         332 return( $1, $2, $3, $4, $5, $6 );
190             }
191              
192             sub make_x_www_form_urlencoded {
193 0     0 0 0 my($self, $content) = @_;
194 0         0 my @params;
195 0         0 my @p = ref($content) eq 'HASH' ? %{$content}
196 0 0       0 : ref($content) eq 'ARRAY' ? @{$content}
  0 0       0  
197             : Carp::croak("Cannot coerce $content to x-www-form-urlencoded");
198 0         0 while ( my ( $k, $v ) = splice @p, 0, 2 ) {
199 0         0 foreach my $s($k, $v) {
200 0         0 utf8::downgrade($s); # will die in wide characters
201             # escape unsafe chars (defined by RFC 3986)
202 0         0 $s =~ s/ ([^A-Za-z0-9\-\._~]) / sprintf '%%%02X', ord $1 /xmsge;
  0         0  
203             }
204 0         0 push @params, "$k=$v";
205             }
206 0         0 return join( "&", @params );
207             }
208              
209             sub env_proxy {
210 8     8 1 14 my $self = shift;
211             # Under CGI, bypass HTTP_PROXY as request sets it from Proxy header
212             # Note: This doesn't work on windows correctly.
213 8 100       25 local $ENV{HTTP_PROXY} if $ENV{REQUEST_METHOD};
214 8   66     37 $self->{proxy} = $ENV{http_proxy} || $ENV{HTTP_PROXY} || $self->{proxy};
215 8   50     24 $self->{no_proxy} = $ENV{NO_PROXY} || '';
216 8         23 $self;
217             }
218              
219             sub request {
220 67     67 1 20055402 my $self = shift;
221 67         441 my %args = @_;
222              
223 67         497 my $timeout_at = time + $self->{timeout};
224              
225 67         168 my ($scheme, $username, $password, $host, $port, $path_query);
226 67 100       234 if (defined(my $url = $args{url})) {
227 23         146 ($scheme, $username, $password, $host, $port, $path_query) = $self->_parse_url($url);
228             }
229             else {
230 44         168 ($scheme, $host, $port, $path_query) = @args{qw/scheme host port path_query/};
231 44 50       150 if (not defined $host) {
232 0         0 Carp::croak("Missing host name in arguments");
233             }
234             }
235              
236 67 100 66     310 if (not defined $scheme) {
    100          
237 44         134 $scheme = 'http';
238             } elsif($scheme ne 'http' && $scheme ne 'https') {
239 6         461 Carp::croak("Unsupported scheme: $scheme");
240             }
241              
242 61 50       210 my $default_port = $scheme eq 'http'
243             ? 80
244             : 443;
245 61 100       265 if(not defined $port) {
246 1         2 $port = $default_port;
247             }
248 61 100       162 if(not defined $path_query) {
249 13         20 $path_query = '/';
250             }
251              
252 61 100       219 unless (substr($path_query, 0, 1) eq '/') {
253 3         15 $path_query = "/$path_query"; # Compensate for slash (?foo=bar => /?foo=bar)
254             }
255              
256             # Note. '_' is a invalid character for URI, but some servers using fucking underscore for domain name. Then, I accept the '_' character for domain name.
257 61 50       346 if ($host =~ /[^A-Za-z0-9._-]/) {
258 0         0 _requires('Net/IDN/Encode.pm',
259             'Internationalized Domain Name (IDN)');
260 0         0 $host = Net::IDN::Encode::domain_to_ascii($host);
261             }
262              
263 61         185 my $proxy = $self->{proxy};
264 61         126 my $no_proxy = $self->{no_proxy};
265 61 50 33     204 if ($proxy && $no_proxy) {
266 0 0       0 if ($self->match_no_proxy($no_proxy, $host)) {
267 0         0 undef $proxy;
268             }
269             }
270              
271 61         822 local $SIG{PIPE} = 'IGNORE';
272 61         391 my $sock = $self->{connection_pool}->steal($host, $port);
273 61         129 my $in_keepalive;
274 61 100       158 if (defined $sock) {
275 26 100       85 if ($self->_do_select(0, $sock, 0)) {
276 2         127 close $sock;
277 2         14 undef $sock;
278             } else {
279 24         42 $in_keepalive = 1;
280             }
281             }
282 61 100       162 if(!$in_keepalive) {
283 37         55 my $err_reason;
284 37 50       108 if ($proxy) {
285 0         0 my (undef, $proxy_user, $proxy_pass, $proxy_host, $proxy_port, undef)
286             = $self->_parse_url($proxy);
287 0         0 my $proxy_authorization;
288 0 0       0 if (defined $proxy_user) {
289 0         0 _requires('URI/Escape.pm',
290             'Basic auth');
291 0         0 my($unescape_proxy_user) = URI::Escape::uri_unescape($proxy_user);
292 0         0 my($unescape_proxy_pass) = URI::Escape::uri_unescape($proxy_pass);
293 0         0 _requires('MIME/Base64.pm',
294             'Basic auth');
295 0         0 $proxy_authorization = 'Basic ' . MIME::Base64::encode_base64("$unescape_proxy_user:$unescape_proxy_pass","");
296             }
297 0 0       0 if ($scheme eq 'http') {
298 0         0 ($sock, $err_reason)
299             = $self->connect($proxy_host, $proxy_port, $timeout_at);
300 0 0       0 if (defined $proxy_authorization) {
301 0         0 $self->{proxy_authorization} = $proxy_authorization;
302             }
303             } else {
304 0         0 ($sock, $err_reason) = $self->connect_ssl_over_proxy(
305             $proxy_host, $proxy_port, $host, $port, $timeout_at, $proxy_authorization);
306             }
307             } else {
308 37 50       115 if ($scheme eq 'http') {
309 37         397 ($sock, $err_reason)
310             = $self->connect($host, $port, $timeout_at);
311             } else {
312 0         0 ($sock, $err_reason)
313             = $self->connect_ssl($host, $port, $timeout_at);
314             }
315             }
316 37 100       114 return $self->_r500($err_reason)
317             unless $sock;
318             }
319              
320             # keep request dump
321 60         241 my ($req_headers, $req_content) = ("", "");
322              
323             # write request
324 60   100     345 my $method = $args{method} || 'GET';
325 60         128 my $connection_header = $self->{connection_header};
326 60         119 my $cookie_jar = $self->{cookie_jar};
327             {
328 60         94 my @headers = @{$self->{headers}};
  60         104  
  60         202  
329 60 100       200 $connection_header = 'close'
330             if $method eq 'HEAD';
331 60 100       211 if (my $in_headers = $args{headers}) {
332 14         75 for (my $i = 0; $i < @$in_headers; $i += 2) {
333 18         70 my $name = $in_headers->[$i];
334 18 50       67 if (lc($name) eq 'connection') {
335 0         0 $connection_header = $in_headers->[$i + 1];
336             } else {
337 18         82 push @headers, $name, $in_headers->[$i + 1];
338             }
339             }
340             }
341 60         207 unshift @headers, 'Connection', $connection_header;
342 60 50       186 if (exists $self->{proxy_authorization}) {
343 0         0 push @headers, 'Proxy-Authorization', $self->{proxy_authorization};
344             }
345 60 100       161 if (defined $username) {
346 6         11 _requires('URI/Escape.pm', 'Basic auth');
347 6         1472 my($unescape_username) = URI::Escape::uri_unescape($username);
348 6         71 my($unescape_password) = URI::Escape::uri_unescape($password);
349 6         50 _requires('MIME/Base64.pm', 'Basic auth');
350 6         619 push @headers, 'Authorization', 'Basic ' . MIME::Base64::encode_base64("${unescape_username}:${unescape_password}","");
351             }
352              
353             # set Cookie header
354 60 50       153 if (defined $cookie_jar) {
355 0         0 my $url;
356 0 0       0 if ($args{url}) {
357 0         0 $url = $args{url};
358             } else {
359             $url = join(
360             '',
361             $args{scheme},
362             '://',
363             $args{host},
364             (exists($args{port}) ? ":$args{port}" : ()),
365 0 0       0 exists($args{path_query}) ? $args{path_query} : '/',
    0          
366             );
367             }
368 0         0 push @headers, 'Cookie' => $cookie_jar->cookie_header($url);
369             }
370              
371 60         131 my $content = $args{content};
372 60         98 my $content_is_fh = 0;
373 60 100       143 if(defined $content) {
374 3         9 $content_is_fh = Scalar::Util::openhandle($content);
375 3 50 33     17 if(!$content_is_fh && ref $content) {
376 0         0 $content = $self->make_x_www_form_urlencoded($content);
377 0 0       0 if(!defined _header_get(\@headers, 'Content-Type')) {
378 0         0 push @headers, 'Content-Type'
379             => 'application/x-www-form-urlencoded';
380             }
381             }
382 3 50       12 if(!defined _header_get(\@headers, 'Content-Length')) {
383 3         3 my $content_length;
384 3 50       7 if($content_is_fh) {
385             my $assert = sub {
386 0 0   0   0 $_[0] or Carp::croak(
387             "Failed to $_[1] for Content-Length: $!",
388             );
389 0         0 };
390 0         0 $assert->(defined(my $cur_pos = tell($content)), 'tell');
391 0         0 $assert->(seek($content, 0, SEEK_END), 'seek');
392 0         0 $assert->(defined(my $end_pos = tell($content)), 'tell');
393 0         0 $assert->(seek($content, $cur_pos, SEEK_SET), 'seek');
394              
395 0         0 $content_length = $end_pos - $cur_pos;
396             }
397             else {
398 3         4 $content_length = length($content);
399             }
400 3         9 push @headers, 'Content-Length' => $content_length;
401             }
402             }
403              
404             # finally, set Host header
405 60 100       286 my $request_target = ($port == $default_port) ? $host : "$host:$port";
406 60         213 push @headers, 'Host' => $request_target;
407              
408 60 50 33     212 my $request_uri = $proxy && $scheme eq 'http' ? "$scheme://$request_target$path_query" : $path_query;
409              
410 60         200 my $p = "$method $request_uri HTTP/1.1\015\012";
411 60         198 for (my $i = 0; $i < @headers; $i += 2) {
412 207         385 my $val = $headers[ $i + 1 ];
413             # the de facto standard way to handle [\015\012](by kazuho-san)
414 207         346 $val =~ tr/\015\012/ /;
415 207         665 $p .= "$headers[$i]: $val\015\012";
416             }
417 60         137 $p .= "\015\012";
418 60 50       231 $self->write_all($sock, $p, $timeout_at)
419             or return $self->_r500(
420             "Failed to send HTTP request: " . _strerror_or_timeout());
421              
422 60 100       191 if ($self->{capture_request}) {
423 1         2 $req_headers = $p;
424             }
425              
426 60 100       245 if (defined $content) {
427 3 50       5 if ($content_is_fh) {
428 0         0 my $ret;
429             my $buf;
430 0         0 SENDFILE: while (1) {
431 0         0 $ret = read($content, $buf, $self->{bufsize});
432 0 0       0 if (not defined $ret) {
    0          
433 0         0 Carp::croak("Failed to read request content: $!");
434             } elsif ($ret == 0) { # EOF
435 0         0 last SENDFILE;
436             }
437 0 0       0 $self->write_all($sock, $buf, $timeout_at)
438             or return $self->_r500(
439             "Failed to send content: " . _strerror_or_timeout()
440             );
441              
442 0 0       0 if ($self->{capture_request}) {
443 0         0 $req_content .= $buf;
444             }
445             }
446             } else { # simple string
447 3 50       12 if (length($content) > 0) {
448 0 0       0 $self->write_all($sock, $content, $timeout_at)
449             or return $self->_r500(
450             "Failed to send content: " . _strerror_or_timeout()
451             );
452              
453 0 0       0 if ($self->{capture_request}) {
454 0         0 $req_content = $content;
455             }
456             }
457             }
458             }
459             }
460              
461             # read response
462 60         218 my $buf = '';
463 60         382 my $rest_header;
464             my $res_minor_version;
465 60         0 my $res_status;
466 60         0 my $res_msg;
467 60         0 my $res_headers;
468 60   50     434 my $special_headers = $args{special_headers} || +{};
469 60         247 $special_headers->{'connection'} = '';
470 60         174 $special_headers->{'content-length'} = undef;
471 60         125 $special_headers->{'location'} = '';
472 60         162 $special_headers->{'content-encoding'} = '';
473 60         178 $special_headers->{'transfer-encoding'} = '';
474 60         93 LOOP: while (1) {
475             my $n = $self->read_timeout($sock,
476 212         786 \$buf, $self->{bufsize}, length($buf), $timeout_at);
477 212 100       527 if(!$n) { # error or eof
478 3 50 33     70 if ($in_keepalive && length($buf) == 0
      50        
      33        
479             && (defined($n) || $!==ECONNRESET || (WIN32 && $! == ECONNABORTED))) {
480             # the server closes the connection (maybe because of keep-alive timeout)
481 0         0 return $self->request(%args);
482             }
483 3 50       18 return $self->_r500(
484             !defined($n)
485             ? "Cannot read response header: " . _strerror_or_timeout()
486             : "Unexpected EOF while reading response header"
487             );
488             }
489             else {
490 209         289 my $ret;
491             ( $ret, $res_minor_version, $res_status, $res_msg, $res_headers )
492             = HTTP::Parser::XS::parse_http_response( $buf,
493 209         1386 $self->{header_format}, $special_headers );
494 209 50       615 if ( $ret == -1 ) {
    100          
495 0         0 return $self->_r500("Invalid HTTP response");
496             }
497             elsif ( $ret == -2 ) {
498             # partial response
499 147         266 next LOOP;
500             }
501             else {
502             # succeeded
503 62         176 $rest_header = substr( $buf, $ret );
504 62 100       318 if ((int $res_status / 100) eq 1) { # Continue
505             # The origin server must not wait for the request body
506             # before sending the 100 (Continue) response.
507             # see http://greenbytes.de/tech/webdav/rfc2616.html#status.100
508 5         12 $buf = $rest_header;
509 5         8 next LOOP;
510             }
511 57         140 last LOOP;
512             }
513             }
514             }
515              
516 57         91 my $max_redirects = 0;
517 57         93 my $do_redirect = undef;
518 57 50       155 if ($special_headers->{location}) {
519 0 0       0 $max_redirects = defined($args{max_redirects}) ? $args{max_redirects} : $self->{max_redirects};
520 0   0     0 $do_redirect = $max_redirects && $res_status =~ /^30[1237]$/;
521             }
522              
523 57         124 my $res_content = '';
524 57 50       145 unless ($do_redirect) {
525 57 50       250 if (my $fh = $args{write_file}) {
    50          
526 0         0 $res_content = Furl::FileStream->new( $fh );
527             } elsif (my $coderef = $args{write_code}) {
528             $res_content = Furl::CallbackStream->new(
529 0     0   0 sub { $coderef->($res_status, $res_msg, $res_headers, @_) }
530 0         0 );
531             }
532             }
533              
534 57 50       204 if (exists $COMPRESSED{ $special_headers->{'content-encoding'} }) {
535 0         0 _requires('Furl/ZlibStream.pm', 'Content-Encoding', 'Compress::Raw::Zlib');
536              
537 0         0 $res_content = Furl::ZlibStream->new($res_content);
538             }
539              
540 57         134 my $chunked = ($special_headers->{'transfer-encoding'} eq 'chunked');
541 57         113 my $content_length = $special_headers->{'content-length'};
542 57 100 100     640 if (defined($content_length) && $content_length !~ /\A[0-9]+\z/) {
543 1         5 return $self->_r500("Bad Content-Length: ${content_length}");
544             }
545              
546 56 100 33     639 unless ($method eq 'HEAD'
      66        
      66        
      100        
547             || ($res_status < 200 && $res_status >= 100)
548             || $res_status == 204
549             || $res_status == 304) {
550 49         99 my @err;
551 49 100       148 if ( $chunked ) {
552 7         26 @err = $self->_read_body_chunked($sock,
553             \$res_content, $rest_header, $timeout_at);
554             } else {
555 42         94 $res_content .= $rest_header;
556 42 100 66     221 if (ref $res_content || !defined($content_length)) {
557 1         3 @err = $self->_read_body_normal($sock,
558             \$res_content, length($rest_header),
559             $content_length, $timeout_at);
560             } else {
561 41         177 @err = $self->_read_body_normal_to_string_buffer($sock,
562             \$res_content, length($rest_header),
563             $content_length, $timeout_at);
564             }
565             }
566 49 100       155 if(@err) {
567 1         51 return @err;
568             }
569             }
570              
571             # manage connection cache (i.e. keep-alive)
572 55 100       212 if (lc($connection_header) eq 'keep-alive') {
573 54         135 my $connection = lc $special_headers->{'connection'};
574 54 100 66     357 if (($res_minor_version == 0
    50 100        
575             ? $connection eq 'keep-alive' # HTTP/1.0 needs explicit keep-alive
576             : $connection ne 'close') # HTTP/1.1 can keep alive by default
577             && ( defined $content_length or $chunked)) {
578 44         265 $self->{connection_pool}->push($host, $port, $sock);
579             }
580             }
581             # explicitly close here, just after returning the socket to the pool,
582             # since it might be reused in the upcoming recursive call
583 55         556 undef $sock;
584              
585             # process 'Set-Cookie' header.
586 55 50       155 if (defined $cookie_jar) {
587 0 0 0     0 my $req_url = join(
588             '',
589             $scheme,
590             '://',
591             (defined($username) && defined($password) ? "${username}:${password}@" : ()),
592             "$host:${port}${path_query}",
593             );
594 0         0 my $cookies = $res_headers->{'set-cookie'};
595 0 0       0 $cookies = [$cookies] if !ref$cookies;
596 0         0 for my $cookie (@$cookies) {
597 0         0 $cookie_jar->add($req_url, $cookie);
598             }
599             }
600              
601 55 50       131 if ($do_redirect) {
602 0         0 my $location = $special_headers->{location};
603 0 0       0 unless ($location =~ m{^[a-z0-9]+://}) {
604             # RFC 2616 14.30 says Location header is absolute URI.
605             # But, a lot of servers return relative URI.
606 0         0 _requires("URI.pm", "redirect with relative url");
607 0         0 $location = URI->new_abs($location, "$scheme://$host:$port$path_query")->as_string;
608             }
609             # Note: RFC 1945 and RFC 2068 specify that the client is not allowed
610             # to change the method on the redirected request. However, most
611             # existing user agent implementations treat 302 as if it were a 303
612             # response, performing a GET on the Location field-value regardless
613             # of the original request method. The status codes 303 and 307 have
614             # been added for servers that wish to make unambiguously clear which
615             # kind of reaction is expected of the client.
616 0 0 0     0 return $self->request(
617             @_,
618             method => ($res_status eq '301' or $res_status eq '307') ? $method : 'GET',
619             url => $location,
620             max_redirects => $max_redirects - 1,
621             );
622             }
623              
624             # return response.
625              
626 55 50       120 if (ref $res_content) {
627 0         0 $res_content = $res_content->get_response_string;
628             }
629              
630             return (
631 55         1182 $res_minor_version, $res_status, $res_msg, $res_headers, $res_content,
632             $req_headers, $req_content, undef, undef, [$scheme, $username, $password, $host, $port, $path_query],
633             );
634             }
635              
636             # connects to $host:$port and returns $socket
637             sub connect :method {
638 37     37 0 122 my($self, $host, $port, $timeout_at) = @_;
639 37         56 my $sock;
640              
641 37         112 my $timeout = $timeout_at - time;
642 37 50       160 return (undef, "Failed to resolve host name: timeout")
643             if $timeout <= 0;
644 37         137 my ($sock_addr, $err_reason) = $self->_get_address($host, $port, $timeout);
645 37 100 33     2045 return (undef, "Cannot resolve host name: $host (port: $port), " . ($err_reason || $!))
646             unless $sock_addr;
647              
648 36 50       1015 RETRY:
649             socket($sock, Socket::sockaddr_family($sock_addr), SOCK_STREAM, 0)
650             or Carp::croak("Cannot create socket: $!");
651 36         217 _set_sockopts($sock);
652 36 50 50     3193 if (connect($sock, $sock_addr)) {
    50          
653             # connected
654             } elsif ($! == EINPROGRESS || (WIN32 && $! == EWOULDBLOCK)) {
655 36 50       302 $self->do_select(1, $sock, $timeout_at)
656             or return (undef, "Cannot connect to ${host}:${port}: timeout");
657             # connected
658             } else {
659 0 0 0     0 if ($! == EINTR && ! $self->{stop_if}->()) {
660 0         0 close $sock;
661 0         0 goto RETRY;
662             }
663 0         0 return (undef, "Cannot connect to ${host}:${port}: $!");
664             }
665 36         126 $sock;
666             }
667              
668             sub _get_address {
669 37     37   104 my ($self, $host, $port, $timeout) = @_;
670 37 100       123 if ($self->{get_address}) {
671 1         4 return $self->{get_address}->($host, $port, $timeout);
672             }
673             # default rule (TODO add support for IPv6)
674 36 100       101 my $iaddr = $self->{inet_aton}->($host, $timeout)
675             or return (undef, $!);
676 35         235 pack_sockaddr_in($port, $iaddr);
677             }
678              
679             sub _ssl_opts {
680 0     0   0 my $self = shift;
681 0         0 my $ssl_opts = $self->{ssl_opts};
682 0 0       0 unless (exists $ssl_opts->{SSL_verify_mode}) {
683             # set SSL_VERIFY_PEER as default.
684 0         0 $ssl_opts->{SSL_verify_mode} = IO::Socket::SSL::SSL_VERIFY_PEER();
685 0 0       0 unless (exists $ssl_opts->{SSL_verifycn_scheme}) {
686 0         0 $ssl_opts->{SSL_verifycn_scheme} = 'www'
687             }
688             }
689 0 0       0 if ($ssl_opts->{SSL_verify_mode}) {
690 0 0 0     0 unless (exists $ssl_opts->{SSL_ca_file} || exists $ssl_opts->{SSL_ca_path}) {
691 0         0 require Mozilla::CA;
692 0         0 $ssl_opts->{SSL_ca_file} = Mozilla::CA::SSL_ca_file();
693             }
694             }
695 0         0 $ssl_opts;
696             }
697              
698             # connect SSL socket.
699             # You can override this method in your child class, if you want to use Crypt::SSLeay or some other library.
700             # @return file handle like object
701             sub connect_ssl {
702 0     0 0 0 my ($self, $host, $port, $timeout_at) = @_;
703 0         0 _requires('IO/Socket/SSL.pm', 'SSL');
704              
705 0         0 my ($sock, $err_reason) = $self->connect($host, $port, $timeout_at);
706 0 0       0 return (undef, $err_reason)
707             unless $sock;
708              
709 0         0 my $timeout = $timeout_at - time;
710 0 0       0 return (undef, "Cannot create SSL connection: timeout")
711             if $timeout <= 0;
712              
713 0         0 my $ssl_opts = $self->_ssl_opts;
714 0 0       0 IO::Socket::SSL->start_SSL(
715             $sock,
716             PeerHost => $host,
717             PeerPort => $port,
718             Timeout => $timeout,
719             %$ssl_opts,
720             ) or return (undef, "Cannot create SSL connection: " . IO::Socket::SSL::errstr());
721 0         0 _set_sockopts($sock);
722 0         0 $sock;
723             }
724              
725             sub connect_ssl_over_proxy {
726 0     0 0 0 my ($self, $proxy_host, $proxy_port, $host, $port, $timeout_at, $proxy_authorization) = @_;
727 0         0 _requires('IO/Socket/SSL.pm', 'SSL');
728              
729 0         0 my $sock = $self->connect($proxy_host, $proxy_port, $timeout_at);
730              
731 0         0 my $p = "CONNECT $host:$port HTTP/1.0\015\012Server: $host\015\012";
732 0 0       0 if (defined $proxy_authorization) {
733 0         0 $p .= "Proxy-Authorization: $proxy_authorization\015\012";
734             }
735 0         0 $p .= "\015\012";
736 0 0       0 $self->write_all($sock, $p, $timeout_at)
737             or return $self->_r500(
738             "Failed to send HTTP request to proxy: " . _strerror_or_timeout());
739 0         0 my $buf = '';
740             my $read = $self->read_timeout($sock,
741 0         0 \$buf, $self->{bufsize}, length($buf), $timeout_at);
742 0 0       0 if (not defined $read) {
    0          
    0          
743 0         0 return (undef, "Cannot read proxy response: " . _strerror_or_timeout());
744             } elsif ( $read == 0 ) { # eof
745 0         0 return (undef, "Unexpected EOF while reading proxy response");
746             } elsif ( $buf !~ /^HTTP\/1\.[0-9] 200 .+\015\012/ ) {
747 0         0 return (undef, "Invalid HTTP Response via proxy");
748             }
749              
750 0         0 my $timeout = $timeout_at - time;
751 0 0       0 return (undef, "Cannot start SSL connection: timeout")
752             if $timeout_at <= 0;
753              
754 0         0 my $ssl_opts = $self->_ssl_opts;
755 0 0       0 unless (exists $ssl_opts->{SSL_verifycn_name}) {
756 0         0 $ssl_opts->{SSL_verifycn_name} = $host;
757             }
758             IO::Socket::SSL->start_SSL(
759 0 0       0 $sock,
760             PeerHost => $host,
761             PeerPort => $port,
762             Timeout => $timeout,
763             %$ssl_opts
764             ) or return (undef, "Cannot start SSL connection: " . IO::Socket::SSL::errstr());
765 0         0 _set_sockopts($sock); # just in case (20101118 kazuho)
766 0         0 $sock;
767             }
768              
769             sub _read_body_chunked {
770 7     7   16 my ($self, $sock, $res_content, $rest_header, $timeout_at) = @_;
771              
772 7         13 my $buf = $rest_header;
773 7         7 READ_LOOP: while (1) {
774 2606 100       11863 if (
775             my ( $header, $next_len ) = (
776             $buf =~
777             m{\A ( # header
778             ( [0-9a-fA-F]+ ) # next_len (hex number)
779             (?:;
780             $HTTP_TOKEN
781             =
782             (?: $HTTP_TOKEN | $HTTP_QUOTED_STRING )
783             )* # optional chunk-extensions
784             [ ]* # www.yahoo.com adds spaces here.
785             # Is this valid?
786             \015\012 # CR+LF
787             ) }xmso
788             )
789             )
790             {
791 2082         3727 $buf = substr($buf, length($header)); # remove header from buf
792 2082         2551 $next_len = hex($next_len);
793 2082 100       2915 if ($next_len == 0) {
794 6         12 last READ_LOOP;
795             }
796              
797             # +2 means trailing CRLF
798 2076         3293 READ_CHUNK: while ( $next_len+2 > length($buf) ) {
799             my $n = $self->read_timeout( $sock,
800 1026         2074 \$buf, $self->{bufsize}, length($buf), $timeout_at );
801 1026 50       2602 if (!$n) {
802 0 0       0 return $self->_r500(
803             !defined($n)
804             ? "Cannot read chunk: " . _strerror_or_timeout()
805             : "Unexpected EOF while reading packets"
806             );
807             }
808             }
809 2076         3395 $$res_content .= substr($buf, 0, $next_len);
810 2076         3020 $buf = substr($buf, $next_len+2);
811 2076 100       3010 if (length($buf) > 0) {
812 1820         2680 next; # re-parse header
813             }
814             }
815              
816             my $n = $self->read_timeout( $sock,
817 780         1730 \$buf, $self->{bufsize}, length($buf), $timeout_at );
818 780 100       1525 if (!$n) {
819 1 50       4 return $self->_r500(
820             !defined($n)
821             ? "Cannot read chunk: " . _strerror_or_timeout()
822             : "Unexpected EOF while reading packets"
823             );
824             }
825             }
826             # read last CRLF
827 6         25 return $self->_read_body_normal(
828             $sock, \$buf, length($buf), 2, $timeout_at);
829             }
830              
831             sub _read_body_normal {
832 7     7   15 my ($self, $sock, $res_content, $nread, $res_content_length, $timeout_at)
833             = @_;
834 7   66     39 while (!defined($res_content_length) || $res_content_length != $nread) {
835             my $n = $self->read_timeout( $sock,
836 1         3 \my $buf, $self->{bufsize}, 0, $timeout_at );
837 1 50       3 if (!$n) {
838 1 50       2 last if ! defined($res_content_length);
839 0 0       0 return $self->_r500(
840             !defined($n)
841             ? "Cannot read content body: " . _strerror_or_timeout()
842             : "Unexpected EOF while reading content body"
843             );
844             }
845 0         0 $$res_content .= $buf;
846 0         0 $nread += $n;
847             }
848 7         21 return;
849             }
850              
851             # This function loads all content at once if it's possible. Since $res_content is just a plain scalar.
852             # Buffering is not needed.
853             sub _read_body_normal_to_string_buffer {
854 41     41   135 my ($self, $sock, $res_content, $nread, $res_content_length, $timeout_at)
855             = @_;
856 41         155 while ($res_content_length != $nread) {
857 29         84 my $n = $self->read_timeout( $sock,
858             $res_content, $res_content_length, $nread, $timeout_at );
859 29 50       104 if (!$n) {
860 0 0       0 return $self->_r500(
861             !defined($n)
862             ? "Cannot read content body: " . _strerror_or_timeout()
863             : "Unexpected EOF while reading content body"
864             );
865             }
866 29         87 $nread += $n;
867             }
868 41         124 return;
869             }
870              
871             # returns true if the socket is ready to read, false if timeout has occurred ($! will be cleared upon timeout)
872             sub do_select {
873 94     94 0 256 my($self, $is_write, $sock, $timeout_at) = @_;
874 94         286 my $now = time;
875 94         244 my $inactivity_timeout_at = $now + $self->{inactivity_timeout};
876 94 50       245 $timeout_at = $inactivity_timeout_at
877             if $timeout_at > $inactivity_timeout_at;
878             # wait for data
879 94         139 while (1) {
880 97         169 my $timeout = $timeout_at - $now;
881 97 50       242 if ($timeout <= 0) {
882 0         0 $! = 0;
883 0         0 return 0;
884             }
885 97         264 my $nfound = $self->_do_select($is_write, $sock, $timeout);
886 97 100       6374 return 1 if $nfound > 0;
887 6 100 33     112 return 0 if $nfound == -1 && $! == EINTR && $self->{stop_if}->();
      66        
888 3         35 $now = time;
889             }
890 0         0 die 'not reached';
891             }
892              
893             sub _do_select {
894 123     123   256 my($self, $is_write, $sock, $timeout) = @_;
895 123         180 my($rfd, $wfd);
896 123         243 my $efd = '';
897 123         515 vec($efd, fileno($sock), 1) = 1;
898 123 100       359 if ($is_write) {
899 36         75 $wfd = $efd;
900             } else {
901 87         152 $rfd = $efd;
902             }
903 123         18044253 my $nfound = select($rfd, $wfd, $efd, $timeout);
904 123         724 return $nfound;
905             }
906              
907             # returns (positive) number of bytes read, or undef if the socket is to be closed
908             sub read_timeout {
909 2048     2048 0 3234 my ($self, $sock, $buf, $len, $off, $timeout_at) = @_;
910 2048         2252 my $ret;
911              
912             # NOTE: select-read-select may get stuck in SSL,
913             # so we use read-select-read instead.
914 2048         2171 while(1) {
915             # try to do the IO
916 2103 100       10868 defined($ret = sysread($sock, $$buf, $len, $off))
917             and return $ret;
918 58 50 33     547 if ($! == EAGAIN || $! == EWOULDBLOCK || (WIN32 && $! == EISCONN)) {
    0 50        
919             # passthru
920             } elsif ($! == EINTR) {
921 0 0       0 return undef if $self->{stop_if}->();
922             # otherwise passthru
923             } else {
924 0         0 return undef;
925             }
926             # on EINTER/EAGAIN/EWOULDBLOCK
927 58 100       190 $self->do_select(0, $sock, $timeout_at) or return undef;
928             }
929             }
930              
931             # returns (positive) number of bytes written, or undef if the socket is to be closed
932             sub write_timeout {
933 60     60 0 149 my ($self, $sock, $buf, $len, $off, $timeout_at) = @_;
934 60         87 my $ret;
935 60         132 while(1) {
936             # try to do the IO
937 60 50       2871 defined($ret = syswrite($sock, $buf, $len, $off))
938             and return $ret;
939 0 0 0     0 if ($! == EAGAIN || $! == EWOULDBLOCK || (WIN32 && $! == EISCONN)) {
    0 0        
940             # passthru
941             } elsif ($! == EINTR) {
942 0 0       0 return undef if $self->{stop_if}->();
943             # otherwise passthru
944             } else {
945 0         0 return undef;
946             }
947 0 0       0 $self->do_select(1, $sock, $timeout_at) or return undef;
948             }
949             }
950              
951             # writes all data in buf and returns number of bytes written or undef if failed
952             sub write_all {
953 60     60 0 157 my ($self, $sock, $buf, $timeout_at) = @_;
954 60         108 my $off = 0;
955 60         167 while (my $len = length($buf) - $off) {
956 60 50       196 my $ret = $self->write_timeout($sock, $buf, $len, $off, $timeout_at)
957             or return undef;
958 60         281 $off += $ret;
959             }
960 60         250 return $off;
961             }
962              
963              
964             sub _r500 {
965 6     6   21 my($self, $message) = @_;
966 6         1528 $message = Carp::shortmess($message); # add lineno and filename
967 6         910 return(0, 500, "Internal Response: $message",
968             [
969             'Content-Length' => length($message),
970             'X-Internal-Response' => 1,
971             # XXX ^^ EXPERIMENTAL header. Do not depend to this.
972             ], $message
973             );
974             }
975              
976             sub _strerror_or_timeout {
977 3 50   3   32 $! != 0 ? "$!" : 'timeout';
978             }
979              
980             sub _set_sockopts {
981 36     36   73 my $sock = shift;
982              
983 36 50       214 setsockopt( $sock, IPPROTO_TCP, TCP_NODELAY, 1 )
984             or Carp::croak("Failed to setsockopt(TCP_NODELAY): $!");
985 36         60 if (WIN32) {
986             if (ref($sock) ne 'IO::Socket::SSL') {
987             my $tmp = 1;
988             ioctl( $sock, 0x8004667E, \$tmp )
989             or Carp::croak("Cannot set flags for the socket: $!");
990             }
991             } else {
992 36 50       161 my $flags = fcntl( $sock, F_GETFL, 0 )
993             or Carp::croak("Cannot get flags for the socket: $!");
994 36 50       174 $flags = fcntl( $sock, F_SETFL, $flags | O_NONBLOCK )
995             or Carp::croak("Cannot set flags for the socket: $!");
996             }
997              
998             {
999             # no buffering
1000 36         68 my $orig = select();
  36         121  
1001 36         89 select($sock); $|=1;
  36         104  
1002 36         112 select($orig);
1003             }
1004              
1005 36         112 binmode $sock;
1006             }
1007              
1008             # You can override this method if you want to use more powerful matcher.
1009             sub match_no_proxy {
1010 0     0 0   my ( $self, $no_proxy, $host ) = @_;
1011              
1012             # ref. curl.1.
1013             # list of host names that shouldn't go through any proxy.
1014             # If set to a asterisk '*' only, it matches all hosts.
1015 0 0         if ( $no_proxy eq '*' ) {
1016 0           return 1;
1017             }
1018             else {
1019 0           for my $pat ( split /\s*,\s*/, lc $no_proxy ) {
1020 0 0         if ( $host =~ /\Q$pat\E$/ ) { # suffix match(same behavior with LWP)
1021 0           return 1;
1022             }
1023             }
1024             }
1025 0           return 0;
1026             }
1027              
1028             # utility class
1029             {
1030             package # hide from pause
1031             Furl::FileStream;
1032 45     45   37957 use overload '.=' => 'append', fallback => 1;
  45         34269  
  45         265  
1033             sub new {
1034 0     0     my ($class, $fh) = @_;
1035 0           bless {fh => $fh}, $class;
1036             }
1037             sub append {
1038 0     0     my($self, $partial) = @_;
1039 0           print {$self->{fh}} $partial;
  0            
1040 0           return $self;
1041             }
1042 0     0     sub get_response_string { undef }
1043             }
1044              
1045             {
1046             package # hide from pause
1047             Furl::CallbackStream;
1048 45     45   7519 use overload '.=' => 'append', fallback => 1;
  45         90  
  45         159  
1049             sub new {
1050 0     0     my ($class, $cb) = @_;
1051 0           bless {cb => $cb}, $class;
1052             }
1053             sub append {
1054 0     0     my($self, $partial) = @_;
1055 0           $self->{cb}->($partial);
1056 0           return $self;
1057             }
1058 0     0     sub get_response_string { undef }
1059             }
1060              
1061             1;
1062             __END__