1 # vim: ts=4 sts=4 sw=4 et:
5 # ABSTRACT: A small, simple, correct HTTP/1.1 client
6 our $VERSION = '0.031'; # VERSION
13 @attributes = qw(agent cookie_jar default_headers local_address max_redirect max_size proxy timeout SSL_options verify_SSL);
15 for my $accessor ( @attributes ) {
17 @_ > 1 ? $_[0]->{$accessor} = $_[1] : $_[0]->{$accessor};
23 my($class, %args) = @_;
25 (my $default_agent = $class) =~ s{::}{-}g;
26 $default_agent .= "/" . ($class->VERSION || 0);
29 agent => $default_agent,
32 verify_SSL => $args{verify_SSL} || $args{verify_ssl} || 0, # no verification by default
35 $args{agent} .= $default_agent
36 if defined $args{agent} && $args{agent} =~ / $/;
38 $class->_validate_cookie_jar( $args{cookie_jar} ) if $args{cookie_jar};
40 for my $key ( @attributes ) {
41 $self->{$key} = $args{$key} if exists $args{$key}
44 # Never override proxy argument as this breaks backwards compat.
45 if (!exists $self->{proxy} && (my $http_proxy = $ENV{http_proxy})) {
46 if ($http_proxy =~ m{\Ahttp://[^/?#:@]+:\d+/?\z}) {
47 $self->{proxy} = $http_proxy;
50 Carp::croak(qq{Environment 'http_proxy' must be in format http://<host>:<port>/\n});
54 return bless $self, $class;
58 for my $sub_name ( qw/get head put post delete/ ) {
59 my $req_method = uc $sub_name;
61 eval <<"HERE"; ## no critic
63 my (\$self, \$url, \$args) = \@_;
64 \@_ == 2 || (\@_ == 3 && ref \$args eq 'HASH')
65 or Carp::croak(q/Usage: \$http->$sub_name(URL, [HASHREF])/ . "\n");
66 return \$self->request('$req_method', \$url, \$args || {});
73 my ($self, $url, $data, $args) = @_;
74 (@_ == 3 || @_ == 4 && ref $args eq 'HASH')
75 or Carp::croak(q/Usage: $http->post_form(URL, DATAREF, [HASHREF])/ . "\n");
78 while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
79 $headers->{lc $key} = $value;
81 delete $args->{headers};
83 return $self->request('POST', $url, {
85 content => $self->www_form_urlencode($data),
88 'content-type' => 'application/x-www-form-urlencoded'
96 my ($self, $url, $file, $args) = @_;
97 @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
98 or Carp::croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/ . "\n");
99 if ( -e $file and my $mtime = (stat($file))[9] ) {
100 $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
102 my $tempfile = $file . int(rand(2**31));
103 open my $fh, ">", $tempfile
104 or Carp::croak(qq/Error: Could not open temporary file $tempfile for downloading: $!\n/);
106 $args->{data_callback} = sub { print {$fh} $_[0] };
107 my $response = $self->request('GET', $url, $args);
109 or Carp::croak(qq/Error: Could not close temporary file $tempfile: $!\n/);
110 if ( $response->{success} ) {
111 rename $tempfile, $file
112 or Carp::croak(qq/Error replacing $file with $tempfile: $!\n/);
113 my $lm = $response->{headers}{'last-modified'};
114 if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
115 utime $mtime, $mtime, $file;
118 $response->{success} ||= $response->{status} eq '304';
124 my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/;
127 my ($self, $method, $url, $args) = @_;
128 @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
129 or Carp::croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/ . "\n");
130 $args ||= {}; # we keep some state in this during _request
132 # RFC 2616 Section 8.1.4 mandates a single retry on broken socket
135 $response = eval { $self->_request($method, $url, $args) };
136 last unless $@ && $idempotent{$method}
137 && $@ =~ m{^(?:Socket closed|Unexpected end)};
145 reason => 'Internal Exception',
148 'content-type' => 'text/plain',
149 'content-length' => length $e,
157 sub www_form_urlencode {
158 my ($self, $data) = @_;
159 (@_ == 2 && ref $data)
160 or Carp::croak(q/Usage: $http->www_form_urlencode(DATAREF)/ . "\n");
161 (ref $data eq 'HASH' || ref $data eq 'ARRAY')
162 or Carp::croak("form data must be a hash or array reference\n");
164 my @params = ref $data eq 'HASH' ? %$data : @$data;
166 or Carp::croak("form data reference must have an even number of terms\n");
170 my ($key, $value) = splice(@params, 0, 2);
171 if ( ref $value eq 'ARRAY' ) {
172 unshift @params, map { $key => $_ } @$value;
175 push @terms, join("=", map { $self->_uri_escape($_) } $key, $value);
179 return join("&", sort @terms);
182 #--------------------------------------------------------------------------#
184 #--------------------------------------------------------------------------#
192 my ($self, $method, $url, $args) = @_;
194 my ($scheme, $host, $port, $path_query) = $self->_split_url($url);
199 host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
204 my $handle = HTTP::Tiny::Handle->new(
205 timeout => $self->{timeout},
206 SSL_options => $self->{SSL_options},
207 verify_SSL => $self->{verify_SSL},
208 local_address => $self->{local_address},
211 if ($self->{proxy}) {
212 $request->{uri} = "$scheme://$request->{host_port}$path_query";
213 die(qq/HTTPS via proxy is not supported\n/)
214 if $request->{scheme} eq 'https';
215 $handle->connect(($self->_split_url($self->{proxy}))[0..2]);
218 $handle->connect($scheme, $host, $port);
221 $self->_prepare_headers_and_cb($request, $args, $url);
222 $handle->write_request($request);
225 do { $response = $handle->read_response_header }
226 until (substr($response->{status},0,1) ne '1');
228 $self->_update_cookie_jar( $url, $response ) if $self->{cookie_jar};
230 if ( my @redir_args = $self->_maybe_redirect($request, $response, $args) ) {
232 return $self->_request(@redir_args, $args);
235 if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) {
236 # response has no message body
239 my $data_cb = $self->_prepare_data_cb($response, $args);
240 $handle->read_body($data_cb, $response);
244 $response->{success} = substr($response->{status},0,1) eq '2';
245 $response->{url} = $url;
249 sub _prepare_headers_and_cb {
250 my ($self, $request, $args, $url) = @_;
252 for ($self->{default_headers}, $args->{headers}) {
254 while (my ($k, $v) = each %$_) {
255 $request->{headers}{lc $k} = $v;
258 $request->{headers}{'host'} = $request->{host_port};
259 $request->{headers}{'connection'} = "close";
260 $request->{headers}{'user-agent'} ||= $self->{agent};
262 if ( defined $args->{content} ) {
263 if (ref $args->{content} eq 'CODE') {
264 $request->{headers}{'content-type'} ||= "application/octet-stream";
265 $request->{headers}{'transfer-encoding'} = 'chunked'
266 unless $request->{headers}{'content-length'}
267 || $request->{headers}{'transfer-encoding'};
268 $request->{cb} = $args->{content};
270 elsif ( length $args->{content} ) {
271 my $content = $args->{content};
272 if ( $] ge '5.008' ) {
273 utf8::downgrade($content, 1)
274 or die(qq/Wide character in request message body\n/);
276 $request->{headers}{'content-type'} ||= "application/octet-stream";
277 $request->{headers}{'content-length'} = length $content
278 unless $request->{headers}{'content-length'}
279 || $request->{headers}{'transfer-encoding'};
280 $request->{cb} = sub { substr $content, 0, length $content, '' };
282 $request->{trailer_cb} = $args->{trailer_callback}
283 if ref $args->{trailer_callback} eq 'CODE';
286 ### If we have a cookie jar, then maybe add relevant cookies
287 if ( $self->{cookie_jar} ) {
288 my $cookies = $self->cookie_jar->cookie_header( $url );
289 $request->{headers}{cookie} = $cookies if length $cookies;
295 sub _prepare_data_cb {
296 my ($self, $response, $args) = @_;
297 my $data_cb = $args->{data_callback};
298 $response->{content} = '';
300 if (!$data_cb || $response->{status} !~ /^2/) {
301 if (defined $self->{max_size}) {
303 $_[1]->{content} .= $_[0];
304 die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/)
305 if length $_[1]->{content} > $self->{max_size};
309 $data_cb = sub { $_[1]->{content} .= $_[0] };
315 sub _update_cookie_jar {
316 my ($self, $url, $response) = @_;
318 my $cookies = $response->{headers}->{'set-cookie'};
319 return unless defined $cookies;
321 my @cookies = ref $cookies ? @$cookies : $cookies;
323 $self->cookie_jar->add( $url, $_ ) for @cookies;
328 sub _validate_cookie_jar {
329 my ($class, $jar) = @_;
332 for my $method ( qw/add cookie_header/ ) {
333 Carp::croak(qq/Cookie jar must provide the '$method' method\n/)
334 unless ref($jar) && ref($jar)->can($method);
340 sub _maybe_redirect {
341 my ($self, $request, $response, $args) = @_;
342 my $headers = $response->{headers};
343 my ($status, $method) = ($response->{status}, $request->{method});
344 if (($status eq '303' or ($status =~ /^30[127]/ && $method =~ /^GET|HEAD$/))
345 and $headers->{location}
346 and ++$args->{redirects} <= $self->{max_redirect}
348 my $location = ($headers->{location} =~ /^\//)
349 ? "$request->{scheme}://$request->{host_port}$headers->{location}"
350 : $headers->{location} ;
351 return (($status eq '303' ? 'GET' : $method), $location);
359 # URI regex adapted from the URI module
360 my ($scheme, $authority, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)>
361 or die(qq/Cannot parse URL: '$url'\n/);
363 $scheme = lc $scheme;
364 $path_query = "/$path_query" unless $path_query =~ m<\A/>;
366 my $host = (length($authority)) ? lc $authority : 'localhost';
367 $host =~ s/\A[^@]*@//; # userinfo
369 $host =~ s/:([0-9]*)\z// && length $1
371 : ($scheme eq 'http' ? 80 : $scheme eq 'https' ? 443 : undef);
374 return ($scheme, $host, $port, $path_query);
377 # Date conversions adapted from HTTP::Date
378 my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat";
379 my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
381 my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]);
382 return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
383 substr($DoW,$wday*4,3),
384 $mday, substr($MoY,$mon*4,3), $year+1900,
389 sub _parse_http_date {
390 my ($self, $str) = @_;
393 if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) {
394 @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
396 elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) {
397 @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
399 elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) {
400 @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6);
403 my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1;
408 # URI escaping adapted from URI::Escape
409 # c.f. http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
410 # perl 5.6 ready UTF-8 encoding adapted from JSON::PP
411 my %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255;
413 my $unsafe_char = qr/[^A-Za-z0-9\-\._~]/;
416 my ($self, $str) = @_;
417 if ( $] ge '5.008' ) {
421 $str = pack("U*", unpack("C*", $str)) # UTF-8 encode a byte string
422 if ( length $str == do { use bytes; length $str } );
423 $str = pack("C*", unpack("C*", $str)); # clear UTF-8 flag
425 $str =~ s/($unsafe_char)/$escapes{$1}/ge;
430 HTTP::Tiny::Handle; # hide from PAUSE/indexers
434 use Errno qw[EINTR EPIPE];
435 use IO::Socket qw[SOCK_STREAM];
437 sub BUFSIZE () { 32768 } ## no critic
439 my $Printable = sub {
444 s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
448 my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
451 my ($class, %args) = @_;
455 max_line_size => 16384,
456 max_header_lines => 64,
464 @_ == 4 || die(q/Usage: $handle->connect(scheme, host, port)/ . "\n");
465 my ($self, $scheme, $host, $port) = @_;
467 if ( $scheme eq 'https' ) {
468 # Need IO::Socket::SSL 1.42 for SSL_create_ctx_callback
469 die(qq/IO::Socket::SSL 1.42 must be installed for https support\n/)
470 unless eval {require IO::Socket::SSL; IO::Socket::SSL->VERSION(1.42)};
471 # Need Net::SSLeay 1.49 for MODE_AUTO_RETRY
472 die(qq/Net::SSLeay 1.49 must be installed for https support\n/)
473 unless eval {require Net::SSLeay; Net::SSLeay->VERSION(1.49)};
475 elsif ( $scheme ne 'http' ) {
476 die(qq/Unsupported URL scheme '$scheme'\n/);
478 $self->{fh} = 'IO::Socket::INET'->new(
481 $self->{local_address} ?
482 ( LocalAddr => $self->{local_address} ) : (),
485 Timeout => $self->{timeout}
486 ) or die(qq/Could not connect to '$host:$port': $@\n/);
489 or die(qq/Could not binmode() socket: '$!'\n/);
491 if ( $scheme eq 'https') {
492 my $ssl_args = $self->_ssl_args($host);
493 IO::Socket::SSL->start_SSL(
496 SSL_create_ctx_callback => sub {
498 Net::SSLeay::CTX_set_mode($ctx, Net::SSLeay::MODE_AUTO_RETRY());
502 unless ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
503 my $ssl_err = IO::Socket::SSL->errstr;
504 die(qq/SSL connection failed for $host: $ssl_err\n/);
508 $self->{host} = $host;
509 $self->{port} = $port;
515 @_ == 1 || die(q/Usage: $handle->close()/ . "\n");
517 CORE::close($self->{fh})
518 or die(qq/Could not close socket: '$!'\n/);
522 @_ == 2 || die(q/Usage: $handle->write(buf)/ . "\n");
523 my ($self, $buf) = @_;
525 if ( $] ge '5.008' ) {
526 utf8::downgrade($buf, 1)
527 or die(qq/Wide character in write()\n/);
530 my $len = length $buf;
533 local $SIG{PIPE} = 'IGNORE';
537 or die(qq/Timed out while waiting for socket to become ready for writing\n/);
538 my $r = syswrite($self->{fh}, $buf, $len, $off);
542 last unless $len > 0;
544 elsif ($! == EPIPE) {
545 die(qq/Socket closed by remote server: $!\n/);
547 elsif ($! != EINTR) {
548 if ($self->{fh}->can('errstr')){
549 my $err = $self->{fh}->errstr();
550 die (qq/Could not write to SSL socket: '$err'\n /);
553 die(qq/Could not write to socket: '$!'\n/);
562 @_ == 2 || @_ == 3 || die(q/Usage: $handle->read(len [, allow_partial])/ . "\n");
563 my ($self, $len, $allow_partial) = @_;
566 my $got = length $self->{rbuf};
569 my $take = ($got < $len) ? $got : $len;
570 $buf = substr($self->{rbuf}, 0, $take, '');
576 or die(q/Timed out while waiting for socket to become ready for reading/ . "\n");
577 my $r = sysread($self->{fh}, $buf, $len, length $buf);
582 elsif ($! != EINTR) {
583 if ($self->{fh}->can('errstr')){
584 my $err = $self->{fh}->errstr();
585 die (qq/Could not read from SSL socket: '$err'\n /);
588 die(qq/Could not read from socket: '$!'\n/);
592 if ($len && !$allow_partial) {
593 die(qq/Unexpected end of stream\n/);
599 @_ == 1 || die(q/Usage: $handle->readline()/ . "\n");
603 if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
606 if (length $self->{rbuf} >= $self->{max_line_size}) {
607 die(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}\n/);
610 or die(qq/Timed out while waiting for socket to become ready for reading\n/);
611 my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
615 elsif ($! != EINTR) {
616 if ($self->{fh}->can('errstr')){
617 my $err = $self->{fh}->errstr();
618 die (qq/Could not read from SSL socket: '$err'\n /);
621 die(qq/Could not read from socket: '$!'\n/);
625 die(qq/Unexpected end of stream while looking for line\n/);
628 sub read_header_lines {
629 @_ == 1 || @_ == 2 || die(q/Usage: $handle->read_header_lines([headers])/ . "\n");
630 my ($self, $headers) = @_;
636 my $line = $self->readline;
638 if (++$lines >= $self->{max_header_lines}) {
639 die(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}\n/);
641 elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
642 my ($field_name) = lc $1;
643 if (exists $headers->{$field_name}) {
644 for ($headers->{$field_name}) {
645 $_ = [$_] unless ref $_ eq "ARRAY";
651 $val = \($headers->{$field_name} = $2);
654 elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
656 or die(qq/Unexpected header continuation line\n/);
657 next unless length $1;
658 $$val .= ' ' if length $$val;
661 elsif ($line =~ /\A \x0D?\x0A \z/x) {
665 die(q/Malformed header line: / . $Printable->($line) . "\n");
672 @_ == 2 || die(q/Usage: $handle->write_request(request)/ . "\n");
673 my($self, $request) = @_;
674 $self->write_request_header(@{$request}{qw/method uri headers/});
675 $self->write_body($request) if $request->{cb};
680 'content-md5' => 'Content-MD5',
683 'www-authenticate' => 'WWW-Authenticate',
684 'x-xss-protection' => 'X-XSS-Protection',
687 sub write_header_lines {
688 (@_ == 2 && ref $_[1] eq 'HASH') || die(q/Usage: $handle->write_header_lines(headers)/ . "\n");
689 my($self, $headers) = @_;
692 while (my ($k, $v) = each %$headers) {
693 my $field_name = lc $k;
694 if (exists $HeaderCase{$field_name}) {
695 $field_name = $HeaderCase{$field_name};
698 $field_name =~ /\A $Token+ \z/xo
699 or die(q/Invalid HTTP header field name: / . $Printable->($field_name) . "\n");
700 $field_name =~ s/\b(\w)/\u$1/g;
701 $HeaderCase{lc $field_name} = $field_name;
703 for (ref $v eq 'ARRAY' ? @$v : $v) {
705 or die(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_). "\n");
706 $buf .= "$field_name: $_\x0D\x0A";
710 return $self->write($buf);
714 @_ == 3 || die(q/Usage: $handle->read_body(callback, response)/ . "\n");
715 my ($self, $cb, $response) = @_;
716 my $te = $response->{headers}{'transfer-encoding'} || '';
717 if ( grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ) {
718 $self->read_chunked_body($cb, $response);
721 $self->read_content_body($cb, $response);
727 @_ == 2 || die(q/Usage: $handle->write_body(request)/ . "\n");
728 my ($self, $request) = @_;
729 if ($request->{headers}{'content-length'}) {
730 return $self->write_content_body($request);
733 return $self->write_chunked_body($request);
737 sub read_content_body {
738 @_ == 3 || @_ == 4 || die(q/Usage: $handle->read_content_body(callback, response, [read_length])/ . "\n");
739 my ($self, $cb, $response, $content_length) = @_;
740 $content_length ||= $response->{headers}{'content-length'};
742 if ( defined $content_length ) {
743 my $len = $content_length;
745 my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
746 $cb->($self->read($read, 0), $response);
752 $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) );
758 sub write_content_body {
759 @_ == 2 || die(q/Usage: $handle->write_content_body(request)/ . "\n");
760 my ($self, $request) = @_;
762 my ($len, $content_length) = (0, $request->{headers}{'content-length'});
764 my $data = $request->{cb}->();
766 defined $data && length $data
769 if ( $] ge '5.008' ) {
770 utf8::downgrade($data, 1)
771 or die(qq/Wide character in write_content()\n/);
774 $len += $self->write($data);
777 $len == $content_length
778 or die(qq/Content-Length missmatch (got: $len expected: $content_length)\n/);
783 sub read_chunked_body {
784 @_ == 3 || die(q/Usage: $handle->read_chunked_body(callback, $response)/ . "\n");
785 my ($self, $cb, $response) = @_;
788 my $head = $self->readline;
790 $head =~ /\A ([A-Fa-f0-9]+)/x
791 or die(q/Malformed chunk head: / . $Printable->($head) . "\n");
796 $self->read_content_body($cb, $response, $len);
798 $self->read(2) eq "\x0D\x0A"
799 or die(qq/Malformed chunk: missing CRLF after chunk data\n/);
801 $self->read_header_lines($response->{headers});
805 sub write_chunked_body {
806 @_ == 2 || die(q/Usage: $handle->write_chunked_body(request)/ . "\n");
807 my ($self, $request) = @_;
811 my $data = $request->{cb}->();
813 defined $data && length $data
816 if ( $] ge '5.008' ) {
817 utf8::downgrade($data, 1)
818 or die(qq/Wide character in write_chunked_body()\n/);
821 $len += length $data;
823 my $chunk = sprintf '%X', length $data;
824 $chunk .= "\x0D\x0A";
826 $chunk .= "\x0D\x0A";
828 $self->write($chunk);
830 $self->write("0\x0D\x0A");
831 $self->write_header_lines($request->{trailer_cb}->())
832 if ref $request->{trailer_cb} eq 'CODE';
836 sub read_response_header {
837 @_ == 1 || die(q/Usage: $handle->read_response_header()/ . "\n");
840 my $line = $self->readline;
842 $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
843 or die(q/Malformed Status-Line: / . $Printable->($line). "\n");
845 my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
847 die (qq/Unsupported HTTP protocol: $protocol\n/)
848 unless $version =~ /0*1\.0*[01]/;
853 headers => $self->read_header_lines,
854 protocol => $protocol,
858 sub write_request_header {
859 @_ == 4 || die(q/Usage: $handle->write_request_header(method, request_uri, headers)/ . "\n");
860 my ($self, $method, $request_uri, $headers) = @_;
862 return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
863 + $self->write_header_lines($headers);
867 my ($self, $type, $timeout) = @_;
868 $timeout = $self->{timeout}
869 unless defined $timeout && $timeout >= 0;
871 my $fd = fileno $self->{fh};
872 defined $fd && $fd >= 0
873 or die(qq/select(2): 'Bad file descriptor'\n/);
876 my $pending = $timeout;
879 vec(my $fdset = '', $fd, 1) = 1;
882 $nfound = ($type eq 'read')
883 ? select($fdset, undef, undef, $pending)
884 : select(undef, $fdset, undef, $pending) ;
887 or die(qq/select(2): '$!'\n/);
888 redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
898 @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_read([timeout])/ . "\n");
900 return $self->_do_timeout('read', @_)
904 @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_write([timeout])/ . "\n");
906 return $self->_do_timeout('write', @_)
909 # Try to find a CA bundle to validate the SSL cert,
910 # prefer Mozilla::CA or fallback to a system file
914 return $self->{SSL_options}->{SSL_ca_file}
915 if $self->{SSL_options}->{SSL_ca_file} and -e $self->{SSL_options}->{SSL_ca_file};
917 return Mozilla::CA::SSL_ca_file()
918 if eval { require Mozilla::CA };
920 foreach my $ca_bundle (qw{
921 /etc/ssl/certs/ca-certificates.crt
922 /etc/pki/tls/certs/ca-bundle.crt
923 /etc/ssl/ca-bundle.pem
926 return $ca_bundle if -e $ca_bundle;
929 die qq/Couldn't find a CA bundle with which to verify the SSL certificate.\n/
930 . qq/Try installing Mozilla::CA from CPAN\n/;
934 my ($self, $host) = @_;
938 # This test reimplements IO::Socket::SSL::can_client_sni(), which wasn't
939 # added until IO::Socket::SSL 1.84
940 if ( Net::SSLeay::OPENSSL_VERSION_NUMBER() >= 0x01000000 ) {
941 $ssl_args{SSL_hostname} = $host, # Sane SNI support
944 if ($self->{verify_SSL}) {
945 $ssl_args{SSL_verifycn_scheme} = 'http'; # enable CN validation
946 $ssl_args{SSL_verifycn_name} = $host; # set validation hostname
947 $ssl_args{SSL_verify_mode} = 0x01; # enable cert validation
948 $ssl_args{SSL_ca_file} = $self->_find_CA_file;
951 $ssl_args{SSL_verifycn_scheme} = 'none'; # disable CN validation
952 $ssl_args{SSL_verify_mode} = 0x00; # disable cert validation
955 # user options override settings from verify_SSL
956 for my $k ( keys %{$self->{SSL_options}} ) {
957 $ssl_args{$k} = $self->{SSL_options}{$k} if $k =~ m/^SSL_/;
973 HTTP::Tiny - A small, simple, correct HTTP/1.1 client
983 my $response = HTTP::Tiny->new->get('http://example.com/');
985 die "Failed!\n" unless $response->{success};
987 print "$response->{status} $response->{reason}\n";
989 while (my ($k, $v) = each %{$response->{headers}}) {
990 for (ref $v eq 'ARRAY' ? @$v : $v) {
995 print $response->{content} if length $response->{content};
999 This is a very simple HTTP/1.1 client, designed for doing simple GET
1000 requests without the overhead of a large framework like L<LWP::UserAgent>.
1002 It is more correct and more complete than L<HTTP::Lite>. It supports
1003 proxies (currently only non-authenticating ones) and redirection. It
1004 also correctly resumes after EINTR.
1010 $http = HTTP::Tiny->new( %attributes );
1012 This constructor returns a new HTTP::Tiny object. Valid attributes include:
1020 A user-agent string (defaults to 'HTTP-Tiny/$VERSION'). If C<agent> ends in a space character, the default user-agent string is appended.
1026 An instance of L<HTTP::CookieJar> or equivalent class that supports the C<add> and C<cookie_header> methods
1032 A hashref of default headers to apply to requests
1038 The local IP address to bind to
1044 Maximum number of redirects allowed (defaults to 5)
1050 Maximum response size (only when not using a data callback). If defined,
1051 responses larger than this will return an exception.
1057 URL of a proxy server to use (default is C<$ENV{http_proxy}> if set)
1063 Request timeout in seconds (default is 60)
1069 A boolean that indicates whether to validate the SSL certificate of an C<https>
1070 connection (default is false)
1076 A hashref of C<SSL_*> options to pass through to L<IO::Socket::SSL>
1080 Exceptions from C<max_size>, C<timeout> or other errors will result in a
1081 pseudo-HTTP status code of 599 and a reason of "Internal Exception". The
1082 content field in the response will contain the text of the exception.
1084 See L</SSL SUPPORT> for more on the C<verify_SSL> and C<SSL_options> attributes.
1086 =head2 get|head|put|post|delete
1088 $response = $http->get($url);
1089 $response = $http->get($url, \%options);
1090 $response = $http->head($url);
1092 These methods are shorthand for calling C<request()> for the given method. The
1093 URL must have unsafe characters escaped and international domain names encoded.
1094 See C<request()> for valid options and a description of the response.
1096 The C<success> field of the response will be true if the status code is 2XX.
1100 $response = $http->post_form($url, $form_data);
1101 $response = $http->post_form($url, $form_data, \%options);
1103 This method executes a C<POST> request and sends the key/value pairs from a
1104 form data hash or array reference to the given URL with a C<content-type> of
1105 C<application/x-www-form-urlencoded>. See documentation for the
1106 C<www_form_urlencode> method for details on the encoding.
1108 The URL must have unsafe characters escaped and international domain names
1109 encoded. See C<request()> for valid options and a description of the response.
1110 Any C<content-type> header or content in the options hashref will be ignored.
1112 The C<success> field of the response will be true if the status code is 2XX.
1116 $response = $http->mirror($url, $file, \%options)
1117 if ( $response->{success} ) {
1118 print "$file is up to date\n";
1121 Executes a C<GET> request for the URL and saves the response body to the file
1122 name provided. The URL must have unsafe characters escaped and international
1123 domain names encoded. If the file already exists, the request will include an
1124 C<If-Modified-Since> header with the modification timestamp of the file. You
1125 may specify a different C<If-Modified-Since> header yourself in the C<<
1126 $options->{headers} >> hash.
1128 The C<success> field of the response will be true if the status code is 2XX
1129 or if the status code is 304 (unmodified).
1131 If the file was modified and the server response includes a properly
1132 formatted C<Last-Modified> header, the file modification time will
1133 be updated accordingly.
1137 $response = $http->request($method, $url);
1138 $response = $http->request($method, $url, \%options);
1140 Executes an HTTP request of the given method type ('GET', 'HEAD', 'POST',
1141 'PUT', etc.) on the given URL. The URL must have unsafe characters escaped and
1142 international domain names encoded. A hashref of options may be appended to
1153 A hashref containing headers to include with the request. If the value for
1154 a header is an array reference, the header will be output multiple times with
1155 each value in the array. These headers over-write any default headers.
1161 A scalar to include as the body of the request OR a code reference
1162 that will be called iteratively to produce the body of the request
1168 A code reference that will be called if it exists to provide a hashref
1169 of trailing headers (only used with chunked transfer-encoding)
1175 A code reference that will be called for each chunks of the response
1180 If the C<content> option is a code reference, it will be called iteratively
1181 to provide the content body of the request. It should return the empty
1182 string or undef when the iterator is exhausted.
1184 If the C<content> option is the empty string, no C<content-type> or
1185 C<content-length> headers will be generated.
1187 If the C<data_callback> option is provided, it will be called iteratively until
1188 the entire response body is received. The first argument will be a string
1189 containing a chunk of the response body, the second argument will be the
1190 in-progress response hash reference, as described below. (This allows
1191 customizing the action of the callback based on the C<status> or C<headers>
1192 received prior to the content body.)
1194 The C<request> method returns a hashref containing the response. The hashref
1195 will have the following keys:
1203 Boolean indicating whether the operation returned a 2XX status code
1209 URL that provided the response. This is the URL of the request unless
1210 there were redirections, in which case it is the last URL queried
1211 in a redirection chain
1217 The HTTP status code of the response
1223 The response phrase returned by the server
1229 The body of the response. If the response does not have any content
1230 or if a data callback is provided to consume the response body,
1231 this will be the empty string
1237 A hashref of header fields. All header field names will be normalized
1238 to be lower case. If a header is repeated, the value will be an arrayref;
1239 it will otherwise be a scalar string containing the value
1243 On an exception during the execution of the request, the C<status> field will
1244 contain 599, and the C<content> field will contain the text of the exception.
1246 =head2 www_form_urlencode
1248 $params = $http->www_form_urlencode( $data );
1249 $response = $http->get("http://example.com/query?$params");
1251 This method converts the key/value pairs from a data hash or array reference
1252 into a C<x-www-form-urlencoded> string. The keys and values from the data
1253 reference will be UTF-8 encoded and escaped per RFC 3986. If a value is an
1254 array reference, the key will be repeated with each of the values of the array
1255 reference. The key/value pairs in the resulting string will be sorted by key
1258 =for Pod::Coverage agent
1271 Direct C<https> connections are supported only if L<IO::Socket::SSL> 1.56 or
1272 greater and L<Net::SSLeay> 1.49 or greater are installed. An exception will be
1273 thrown if a new enough versions of these modules not installed or if the SSL
1274 encryption fails. There is no support for C<https> connections via proxy (i.e.
1277 SSL provides two distinct capabilities:
1283 Encrypted communication channel
1287 Verification of server identity
1291 B<By default, HTTP::Tiny does not verify server identity>.
1293 Server identity verification is controversial and potentially tricky because it
1294 depends on a (usually paid) third-party Certificate Authority (CA) trust model
1295 to validate a certificate as legitimate. This discriminates against servers
1296 with self-signed certificates or certificates signed by free, community-driven
1297 CA's such as L<CAcert.org|http://cacert.org>.
1299 By default, HTTP::Tiny does not make any assumptions about your trust model,
1300 threat level or risk tolerance. It just aims to give you an encrypted channel
1303 Setting the C<verify_SSL> attribute to a true value will make HTTP::Tiny verify
1304 that an SSL connection has a valid SSL certificate corresponding to the host
1305 name of the connection and that the SSL certificate has been verified by a CA.
1306 Assuming you trust the CA, this will protect against a L<man-in-the-middle
1307 attack|http://en.wikipedia.org/wiki/Man-in-the-middle_attack>. If you are
1308 concerned about security, you should enable this option.
1310 Certificate verification requires a file containing trusted CA certificates.
1311 If the L<Mozilla::CA> module is installed, HTTP::Tiny will use the CA file
1312 included with it as a source of trusted CA's. (This means you trust Mozilla,
1313 the author of Mozilla::CA, the CPAN mirror where you got Mozilla::CA, the
1314 toolchain used to install it, and your operating system security, right?)
1316 If that module is not available, then HTTP::Tiny will search several
1317 system-specific default locations for a CA certificate file:
1323 /etc/ssl/certs/ca-certificates.crt
1327 /etc/pki/tls/certs/ca-bundle.crt
1331 /etc/ssl/ca-bundle.pem
1335 An exception will be raised if C<verify_SSL> is true and no CA certificate file
1338 If you desire complete control over SSL connections, the C<SSL_options> attribute
1339 lets you provide a hash reference that will be passed through to
1340 C<IO::Socket::SSL::start_SSL()>, overriding any options set by HTTP::Tiny. For
1341 example, to provide your own trusted CA file:
1344 SSL_ca_file => $file_path,
1347 The C<SSL_options> attribute could also be used for such things as providing a
1348 client certificate for authentication to a server or controlling the choice of
1349 cipher used for the SSL connection. See L<IO::Socket::SSL> documentation for
1354 HTTP::Tiny is I<conditionally compliant> with the
1355 L<HTTP/1.1 specification|http://www.w3.org/Protocols/rfc2616/rfc2616.html>.
1356 It attempts to meet all "MUST" requirements of the specification, but does not
1357 implement all "SHOULD" requirements.
1359 Some particular limitations of note include:
1365 HTTP::Tiny focuses on correct transport. Users are responsible for ensuring
1366 that user-defined headers and content are compliant with the HTTP/1.1
1371 Users must ensure that URLs are properly escaped for unsafe characters and that
1372 international domain names are properly encoded to ASCII. See L<URI::Escape>,
1373 L<URI::_punycode> and L<Net::IDN::Encode>.
1377 Redirection is very strict against the specification. Redirection is only
1378 automatic for response codes 301, 302 and 307 if the request method is 'GET' or
1379 'HEAD'. Response code 303 is always converted into a 'GET' redirection, as
1380 mandated by the specification. There is no automatic support for status 305
1381 ("Use proxy") redirections.
1385 Persistent connections are not supported. The C<Connection> header will
1386 always be set to C<close>.
1390 Cookie support requires L<HTTP::CookieJar> or an equivalent class.
1394 Only the C<http_proxy> environment variable is supported in the format
1395 C<http://HOST:PORT/>. If a C<proxy> argument is passed to C<new> (including
1396 undef), then the C<http_proxy> environment variable is ignored.
1400 There is no provision for delaying a request body using an C<Expect> header.
1401 Unexpected C<1XX> responses are silently ignored as per the specification.
1405 Only 'chunked' C<Transfer-Encoding> is supported.
1409 There is no support for a Request-URI of '*' for the 'OPTIONS' request.
1413 There is no support for IPv6 of any kind.
1439 =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
1443 =head2 Bugs / Feature Requests
1445 Please report any bugs or feature requests through the issue tracker
1446 at L<https://github.com/chansen/p5-http-tiny/issues>.
1447 You will be notified automatically of any progress on your issue.
1451 This is open source software. The code repository is available for
1452 public review and contribution under the terms of the license.
1454 L<https://github.com/chansen/p5-http-tiny>
1456 git clone git://github.com/chansen/p5-http-tiny.git
1464 Christian Hansen <chansen@cpan.org>
1468 David Golden <dagolden@cpan.org>
1478 Alan Gardner <gardner@pythian.com>
1482 Alessandro Ghedini <al3xbio@gmail.com>
1486 Chris Nehren <apeiron@cpan.org>
1490 Chris Weyl <cweyl@alumni.drew.edu>
1494 Claes Jakobsson <claes@surfar.nu>
1498 Craig Berry <cberry@cpan.org>
1502 David Mitchell <davem@iabyn.com>
1506 Edward Zborowski <ed@rubensteintech.com>
1510 Jess Robinson <castaway@desert-island.me.uk>
1514 Lukas Eklund <leklund@gmail.com>
1518 Martin-Louis Bright <mlbright@gmail.com>
1522 Mike Doherty <doherty@cpan.org>
1526 Serguei Trouchelle <stro@cpan.org>
1530 Tony Cook <tony@develop-help.com>
1534 =head1 COPYRIGHT AND LICENSE
1536 This software is copyright (c) 2013 by Christian Hansen.
1538 This is free software; you can redistribute it and/or modify it under
1539 the same terms as the Perl 5 programming language system itself.