1 # vim: ts=4 sts=4 sw=4 et:
5 # ABSTRACT: A small, simple, correct HTTP/1.1 client
6 our $VERSION = '0.022'; # VERSION
13 @attributes = qw(agent 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) = @_;
24 (my $agent = $class) =~ s{::}{-}g;
26 agent => $agent . "/" . ($class->VERSION || 0),
29 verify_SSL => $args{verify_SSL} || $args{verify_ssl} || 0, # no verification by default
31 for my $key ( @attributes ) {
32 $self->{$key} = $args{$key} if exists $args{$key}
35 # Never override proxy argument as this breaks backwards compat.
36 if (!exists $self->{proxy} && (my $http_proxy = $ENV{http_proxy})) {
37 if ($http_proxy =~ m{\Ahttp://[^/?#:@]+:\d+/?\z}) {
38 $self->{proxy} = $http_proxy;
41 Carp::croak(qq{Environment 'http_proxy' must be in format http://<host>:<port>/\n});
45 return bless $self, $class;
49 for my $sub_name ( qw/get head put post delete/ ) {
50 my $req_method = uc $sub_name;
52 eval <<"HERE"; ## no critic
54 my (\$self, \$url, \$args) = \@_;
55 \@_ == 2 || (\@_ == 3 && ref \$args eq 'HASH')
56 or Carp::croak(q/Usage: \$http->$sub_name(URL, [HASHREF])/ . "\n");
57 return \$self->request('$req_method', \$url, \$args || {});
64 my ($self, $url, $data, $args) = @_;
65 (@_ == 3 || @_ == 4 && ref $args eq 'HASH')
66 or Carp::croak(q/Usage: $http->post_form(URL, DATAREF, [HASHREF])/ . "\n");
69 while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
70 $headers->{lc $key} = $value;
72 delete $args->{headers};
74 return $self->request('POST', $url, {
76 content => $self->www_form_urlencode($data),
79 'content-type' => 'application/x-www-form-urlencoded'
87 my ($self, $url, $file, $args) = @_;
88 @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
89 or Carp::croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/ . "\n");
90 if ( -e $file and my $mtime = (stat($file))[9] ) {
91 $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
93 my $tempfile = $file . int(rand(2**31));
94 open my $fh, ">", $tempfile
95 or Carp::croak(qq/Error: Could not open temporary file $tempfile for downloading: $!\n/);
97 $args->{data_callback} = sub { print {$fh} $_[0] };
98 my $response = $self->request('GET', $url, $args);
100 or Carp::croak(qq/Error: Could not close temporary file $tempfile: $!\n/);
101 if ( $response->{success} ) {
102 rename $tempfile, $file
103 or Carp::croak(qq/Error replacing $file with $tempfile: $!\n/);
104 my $lm = $response->{headers}{'last-modified'};
105 if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
106 utime $mtime, $mtime, $file;
109 $response->{success} ||= $response->{status} eq '304';
115 my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/;
118 my ($self, $method, $url, $args) = @_;
119 @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
120 or Carp::croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/ . "\n");
121 $args ||= {}; # we keep some state in this during _request
123 # RFC 2616 Section 8.1.4 mandates a single retry on broken socket
126 $response = eval { $self->_request($method, $url, $args) };
127 last unless $@ && $idempotent{$method}
128 && $@ =~ m{^(?:Socket closed|Unexpected end)};
136 reason => 'Internal Exception',
139 'content-type' => 'text/plain',
140 'content-length' => length $e,
148 sub www_form_urlencode {
149 my ($self, $data) = @_;
150 (@_ == 2 && ref $data)
151 or Carp::croak(q/Usage: $http->www_form_urlencode(DATAREF)/ . "\n");
152 (ref $data eq 'HASH' || ref $data eq 'ARRAY')
153 or Carp::croak("form data must be a hash or array reference");
155 my @params = ref $data eq 'HASH' ? %$data : @$data;
157 or Carp::croak("form data reference must have an even number of terms\n");
161 my ($key, $value) = splice(@params, 0, 2);
162 if ( ref $value eq 'ARRAY' ) {
163 unshift @params, map { $key => $_ } @$value;
166 push @terms, join("=", map { $self->_uri_escape($_) } $key, $value);
170 return join("&", sort @terms);
173 #--------------------------------------------------------------------------#
175 #--------------------------------------------------------------------------#
183 my ($self, $method, $url, $args) = @_;
185 my ($scheme, $host, $port, $path_query) = $self->_split_url($url);
190 host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
195 my $handle = HTTP::Tiny::Handle->new(
196 timeout => $self->{timeout},
197 SSL_options => $self->{SSL_options},
198 verify_SSL => $self->{verify_SSL},
199 local_address => $self->{local_address},
202 if ($self->{proxy}) {
203 $request->{uri} = "$scheme://$request->{host_port}$path_query";
204 die(qq/HTTPS via proxy is not supported\n/)
205 if $request->{scheme} eq 'https';
206 $handle->connect(($self->_split_url($self->{proxy}))[0..2]);
209 $handle->connect($scheme, $host, $port);
212 $self->_prepare_headers_and_cb($request, $args);
213 $handle->write_request($request);
216 do { $response = $handle->read_response_header }
217 until (substr($response->{status},0,1) ne '1');
219 if ( my @redir_args = $self->_maybe_redirect($request, $response, $args) ) {
221 return $self->_request(@redir_args, $args);
224 if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) {
225 # response has no message body
228 my $data_cb = $self->_prepare_data_cb($response, $args);
229 $handle->read_body($data_cb, $response);
233 $response->{success} = substr($response->{status},0,1) eq '2';
234 $response->{url} = $url;
238 sub _prepare_headers_and_cb {
239 my ($self, $request, $args) = @_;
241 for ($self->{default_headers}, $args->{headers}) {
243 while (my ($k, $v) = each %$_) {
244 $request->{headers}{lc $k} = $v;
247 $request->{headers}{'host'} = $request->{host_port};
248 $request->{headers}{'connection'} = "close";
249 $request->{headers}{'user-agent'} ||= $self->{agent};
251 if (defined $args->{content}) {
252 $request->{headers}{'content-type'} ||= "application/octet-stream";
253 if (ref $args->{content} eq 'CODE') {
254 $request->{headers}{'transfer-encoding'} = 'chunked'
255 unless $request->{headers}{'content-length'}
256 || $request->{headers}{'transfer-encoding'};
257 $request->{cb} = $args->{content};
260 my $content = $args->{content};
261 if ( $] ge '5.008' ) {
262 utf8::downgrade($content, 1)
263 or die(qq/Wide character in request message body\n/);
265 $request->{headers}{'content-length'} = length $content
266 unless $request->{headers}{'content-length'}
267 || $request->{headers}{'transfer-encoding'};
268 $request->{cb} = sub { substr $content, 0, length $content, '' };
270 $request->{trailer_cb} = $args->{trailer_callback}
271 if ref $args->{trailer_callback} eq 'CODE';
276 sub _prepare_data_cb {
277 my ($self, $response, $args) = @_;
278 my $data_cb = $args->{data_callback};
279 $response->{content} = '';
281 if (!$data_cb || $response->{status} !~ /^2/) {
282 if (defined $self->{max_size}) {
284 $_[1]->{content} .= $_[0];
285 die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/)
286 if length $_[1]->{content} > $self->{max_size};
290 $data_cb = sub { $_[1]->{content} .= $_[0] };
296 sub _maybe_redirect {
297 my ($self, $request, $response, $args) = @_;
298 my $headers = $response->{headers};
299 my ($status, $method) = ($response->{status}, $request->{method});
300 if (($status eq '303' or ($status =~ /^30[127]/ && $method =~ /^GET|HEAD$/))
301 and $headers->{location}
302 and ++$args->{redirects} <= $self->{max_redirect}
304 my $location = ($headers->{location} =~ /^\//)
305 ? "$request->{scheme}://$request->{host_port}$headers->{location}"
306 : $headers->{location} ;
307 return (($status eq '303' ? 'GET' : $method), $location);
315 # URI regex adapted from the URI module
316 my ($scheme, $authority, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)>
317 or die(qq/Cannot parse URL: '$url'\n/);
319 $scheme = lc $scheme;
320 $path_query = "/$path_query" unless $path_query =~ m<\A/>;
322 my $host = (length($authority)) ? lc $authority : 'localhost';
323 $host =~ s/\A[^@]*@//; # userinfo
325 $host =~ s/:([0-9]*)\z// && length $1
327 : ($scheme eq 'http' ? 80 : $scheme eq 'https' ? 443 : undef);
330 return ($scheme, $host, $port, $path_query);
333 # Date conversions adapted from HTTP::Date
334 my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat";
335 my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
337 my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]);
338 return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
339 substr($DoW,$wday*4,3),
340 $mday, substr($MoY,$mon*4,3), $year+1900,
345 sub _parse_http_date {
346 my ($self, $str) = @_;
349 if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) {
350 @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
352 elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) {
353 @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
355 elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) {
356 @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6);
359 my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1;
364 # URI escaping adapted from URI::Escape
365 # c.f. http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
366 # perl 5.6 ready UTF-8 encoding adapted from JSON::PP
367 my %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255;
369 my $unsafe_char = qr/[^A-Za-z0-9\-\._~]/;
372 my ($self, $str) = @_;
373 if ( $] ge '5.008' ) {
377 $str = pack("U*", unpack("C*", $str)) # UTF-8 encode a byte string
378 if ( length $str == do { use bytes; length $str } );
379 $str = pack("C*", unpack("C*", $str)); # clear UTF-8 flag
381 $str =~ s/($unsafe_char)/$escapes{$1}/ge;
386 HTTP::Tiny::Handle; # hide from PAUSE/indexers
390 use Errno qw[EINTR EPIPE];
391 use IO::Socket qw[SOCK_STREAM];
393 sub BUFSIZE () { 32768 } ## no critic
395 my $Printable = sub {
400 s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
404 my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
407 my ($class, %args) = @_;
411 max_line_size => 16384,
412 max_header_lines => 64,
420 @_ == 4 || die(q/Usage: $handle->connect(scheme, host, port)/ . "\n");
421 my ($self, $scheme, $host, $port) = @_;
423 if ( $scheme eq 'https' ) {
424 die(qq/IO::Socket::SSL 1.56 must be installed for https support\n/)
425 unless eval {require IO::Socket::SSL; IO::Socket::SSL->VERSION(1.56)};
427 elsif ( $scheme ne 'http' ) {
428 die(qq/Unsupported URL scheme '$scheme'\n/);
430 $self->{fh} = 'IO::Socket::INET'->new(
433 $self->{local_address} ?
434 ( LocalAddr => $self->{local_address} ) : (),
437 Timeout => $self->{timeout}
438 ) or die(qq/Could not connect to '$host:$port': $@\n/);
441 or die(qq/Could not binmode() socket: '$!'\n/);
443 if ( $scheme eq 'https') {
444 my $ssl_args = $self->_ssl_args($host);
445 IO::Socket::SSL->start_SSL($self->{fh}, %$ssl_args);
446 unless ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
447 my $ssl_err = IO::Socket::SSL->errstr;
448 die(qq/SSL connection failed for $host: $ssl_err\n/);
452 $self->{host} = $host;
453 $self->{port} = $port;
459 @_ == 1 || die(q/Usage: $handle->close()/ . "\n");
461 CORE::close($self->{fh})
462 or die(qq/Could not close socket: '$!'\n/);
466 @_ == 2 || die(q/Usage: $handle->write(buf)/ . "\n");
467 my ($self, $buf) = @_;
469 if ( $] ge '5.008' ) {
470 utf8::downgrade($buf, 1)
471 or die(qq/Wide character in write()\n/);
474 my $len = length $buf;
477 local $SIG{PIPE} = 'IGNORE';
481 or die(qq/Timed out while waiting for socket to become ready for writing\n/);
482 my $r = syswrite($self->{fh}, $buf, $len, $off);
486 last unless $len > 0;
488 elsif ($! == EPIPE) {
489 die(qq/Socket closed by remote server: $!\n/);
491 elsif ($! != EINTR) {
492 die(qq/Could not write to socket: '$!'\n/);
499 @_ == 2 || @_ == 3 || die(q/Usage: $handle->read(len [, allow_partial])/ . "\n");
500 my ($self, $len, $allow_partial) = @_;
503 my $got = length $self->{rbuf};
506 my $take = ($got < $len) ? $got : $len;
507 $buf = substr($self->{rbuf}, 0, $take, '');
513 or die(q/Timed out while waiting for socket to become ready for reading/ . "\n");
514 my $r = sysread($self->{fh}, $buf, $len, length $buf);
519 elsif ($! != EINTR) {
520 die(qq/Could not read from socket: '$!'\n/);
523 if ($len && !$allow_partial) {
524 die(qq/Unexpected end of stream\n/);
530 @_ == 1 || die(q/Usage: $handle->readline()/ . "\n");
534 if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
537 if (length $self->{rbuf} >= $self->{max_line_size}) {
538 die(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}\n/);
541 or die(qq/Timed out while waiting for socket to become ready for reading\n/);
542 my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
546 elsif ($! != EINTR) {
547 die(qq/Could not read from socket: '$!'\n/);
550 die(qq/Unexpected end of stream while looking for line\n/);
553 sub read_header_lines {
554 @_ == 1 || @_ == 2 || die(q/Usage: $handle->read_header_lines([headers])/ . "\n");
555 my ($self, $headers) = @_;
561 my $line = $self->readline;
563 if (++$lines >= $self->{max_header_lines}) {
564 die(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}\n/);
566 elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
567 my ($field_name) = lc $1;
568 if (exists $headers->{$field_name}) {
569 for ($headers->{$field_name}) {
570 $_ = [$_] unless ref $_ eq "ARRAY";
576 $val = \($headers->{$field_name} = $2);
579 elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
581 or die(qq/Unexpected header continuation line\n/);
582 next unless length $1;
583 $$val .= ' ' if length $$val;
586 elsif ($line =~ /\A \x0D?\x0A \z/x) {
590 die(q/Malformed header line: / . $Printable->($line) . "\n");
597 @_ == 2 || die(q/Usage: $handle->write_request(request)/ . "\n");
598 my($self, $request) = @_;
599 $self->write_request_header(@{$request}{qw/method uri headers/});
600 $self->write_body($request) if $request->{cb};
605 'content-md5' => 'Content-MD5',
608 'www-authenticate' => 'WWW-Authenticate',
609 'x-xss-protection' => 'X-XSS-Protection',
612 sub write_header_lines {
613 (@_ == 2 && ref $_[1] eq 'HASH') || die(q/Usage: $handle->write_header_lines(headers)/ . "\n");
614 my($self, $headers) = @_;
617 while (my ($k, $v) = each %$headers) {
618 my $field_name = lc $k;
619 if (exists $HeaderCase{$field_name}) {
620 $field_name = $HeaderCase{$field_name};
623 $field_name =~ /\A $Token+ \z/xo
624 or die(q/Invalid HTTP header field name: / . $Printable->($field_name) . "\n");
625 $field_name =~ s/\b(\w)/\u$1/g;
626 $HeaderCase{lc $field_name} = $field_name;
628 for (ref $v eq 'ARRAY' ? @$v : $v) {
630 or die(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_). "\n");
631 $buf .= "$field_name: $_\x0D\x0A";
635 return $self->write($buf);
639 @_ == 3 || die(q/Usage: $handle->read_body(callback, response)/ . "\n");
640 my ($self, $cb, $response) = @_;
641 my $te = $response->{headers}{'transfer-encoding'} || '';
642 if ( grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ) {
643 $self->read_chunked_body($cb, $response);
646 $self->read_content_body($cb, $response);
652 @_ == 2 || die(q/Usage: $handle->write_body(request)/ . "\n");
653 my ($self, $request) = @_;
654 if ($request->{headers}{'content-length'}) {
655 return $self->write_content_body($request);
658 return $self->write_chunked_body($request);
662 sub read_content_body {
663 @_ == 3 || @_ == 4 || die(q/Usage: $handle->read_content_body(callback, response, [read_length])/ . "\n");
664 my ($self, $cb, $response, $content_length) = @_;
665 $content_length ||= $response->{headers}{'content-length'};
667 if ( $content_length ) {
668 my $len = $content_length;
670 my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
671 $cb->($self->read($read, 0), $response);
677 $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) );
683 sub write_content_body {
684 @_ == 2 || die(q/Usage: $handle->write_content_body(request)/ . "\n");
685 my ($self, $request) = @_;
687 my ($len, $content_length) = (0, $request->{headers}{'content-length'});
689 my $data = $request->{cb}->();
691 defined $data && length $data
694 if ( $] ge '5.008' ) {
695 utf8::downgrade($data, 1)
696 or die(qq/Wide character in write_content()\n/);
699 $len += $self->write($data);
702 $len == $content_length
703 or die(qq/Content-Length missmatch (got: $len expected: $content_length)\n/);
708 sub read_chunked_body {
709 @_ == 3 || die(q/Usage: $handle->read_chunked_body(callback, $response)/ . "\n");
710 my ($self, $cb, $response) = @_;
713 my $head = $self->readline;
715 $head =~ /\A ([A-Fa-f0-9]+)/x
716 or die(q/Malformed chunk head: / . $Printable->($head) . "\n");
721 $self->read_content_body($cb, $response, $len);
723 $self->read(2) eq "\x0D\x0A"
724 or die(qq/Malformed chunk: missing CRLF after chunk data\n/);
726 $self->read_header_lines($response->{headers});
730 sub write_chunked_body {
731 @_ == 2 || die(q/Usage: $handle->write_chunked_body(request)/ . "\n");
732 my ($self, $request) = @_;
736 my $data = $request->{cb}->();
738 defined $data && length $data
741 if ( $] ge '5.008' ) {
742 utf8::downgrade($data, 1)
743 or die(qq/Wide character in write_chunked_body()\n/);
746 $len += length $data;
748 my $chunk = sprintf '%X', length $data;
749 $chunk .= "\x0D\x0A";
751 $chunk .= "\x0D\x0A";
753 $self->write($chunk);
755 $self->write("0\x0D\x0A");
756 $self->write_header_lines($request->{trailer_cb}->())
757 if ref $request->{trailer_cb} eq 'CODE';
761 sub read_response_header {
762 @_ == 1 || die(q/Usage: $handle->read_response_header()/ . "\n");
765 my $line = $self->readline;
767 $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
768 or die(q/Malformed Status-Line: / . $Printable->($line). "\n");
770 my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
772 die (qq/Unsupported HTTP protocol: $protocol\n/)
773 unless $version =~ /0*1\.0*[01]/;
778 headers => $self->read_header_lines,
779 protocol => $protocol,
783 sub write_request_header {
784 @_ == 4 || die(q/Usage: $handle->write_request_header(method, request_uri, headers)/ . "\n");
785 my ($self, $method, $request_uri, $headers) = @_;
787 return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
788 + $self->write_header_lines($headers);
792 my ($self, $type, $timeout) = @_;
793 $timeout = $self->{timeout}
794 unless defined $timeout && $timeout >= 0;
796 my $fd = fileno $self->{fh};
797 defined $fd && $fd >= 0
798 or die(qq/select(2): 'Bad file descriptor'\n/);
801 my $pending = $timeout;
804 vec(my $fdset = '', $fd, 1) = 1;
807 $nfound = ($type eq 'read')
808 ? select($fdset, undef, undef, $pending)
809 : select(undef, $fdset, undef, $pending) ;
812 or die(qq/select(2): '$!'\n/);
813 redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
823 @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_read([timeout])/ . "\n");
825 return $self->_do_timeout('read', @_)
829 @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_write([timeout])/ . "\n");
831 return $self->_do_timeout('write', @_)
834 # Try to find a CA bundle to validate the SSL cert,
835 # prefer Mozilla::CA or fallback to a system file
837 return Mozilla::CA::SSL_ca_file()
838 if eval { require Mozilla::CA };
840 foreach my $ca_bundle (qw{
841 /etc/ssl/certs/ca-certificates.crt
842 /etc/pki/tls/certs/ca-bundle.crt
843 /etc/ssl/ca-bundle.pem
846 return $ca_bundle if -e $ca_bundle;
849 die qq/Couldn't find a CA bundle with which to verify the SSL certificate.\n/
850 . qq/Try installing Mozilla::CA from CPAN\n/;
854 my ($self, $host) = @_;
857 SSL_hostname => $host, # SNI
860 if ($self->{verify_SSL}) {
861 $ssl_args{SSL_verifycn_scheme} = 'http'; # enable CN validation
862 $ssl_args{SSL_verifycn_name} = $host; # set validation hostname
863 $ssl_args{SSL_verify_mode} = 0x01; # enable cert validation
864 $ssl_args{SSL_ca_file} = $self->_find_CA_file;
867 $ssl_args{SSL_verifycn_scheme} = 'none'; # disable CN validation
868 $ssl_args{SSL_verify_mode} = 0x00; # disable cert validation
871 # user options override settings from verify_SSL
872 for my $k ( keys %{$self->{SSL_options}} ) {
873 $ssl_args{$k} = $self->{SSL_options}{$k} if $k =~ m/^SSL_/;
888 HTTP::Tiny - A small, simple, correct HTTP/1.1 client
898 my $response = HTTP::Tiny->new->get('http://example.com/');
900 die "Failed!\n" unless $response->{success};
902 print "$response->{status} $response->{reason}\n";
904 while (my ($k, $v) = each %{$response->{headers}}) {
905 for (ref $v eq 'ARRAY' ? @$v : $v) {
910 print $response->{content} if length $response->{content};
914 This is a very simple HTTP/1.1 client, designed for doing simple GET
915 requests without the overhead of a large framework like L<LWP::UserAgent>.
917 It is more correct and more complete than L<HTTP::Lite>. It supports
918 proxies (currently only non-authenticating ones) and redirection. It
919 also correctly resumes after EINTR.
925 $http = HTTP::Tiny->new( %attributes );
927 This constructor returns a new HTTP::Tiny object. Valid attributes include:
935 A user-agent string (defaults to 'HTTP::Tiny/$VERSION')
941 A hashref of default headers to apply to requests
947 The local IP address to bind to
953 Maximum number of redirects allowed (defaults to 5)
959 Maximum response size (only when not using a data callback). If defined,
960 responses larger than this will return an exception.
966 URL of a proxy server to use (default is C<$ENV{http_proxy}> if set)
972 Request timeout in seconds (default is 60)
978 A boolean that indicates whether to validate the SSL certificate of an C<https>
979 connection (default is false)
985 A hashref of C<SSL_*> options to pass through to L<IO::Socket::SSL>
989 Exceptions from C<max_size>, C<timeout> or other errors will result in a
990 pseudo-HTTP status code of 599 and a reason of "Internal Exception". The
991 content field in the response will contain the text of the exception.
993 See L</SSL SUPPORT> for more on the C<verify_SSL> and C<SSL_options> attributes.
995 =head2 get|head|put|post|delete
997 $response = $http->get($url);
998 $response = $http->get($url, \%options);
999 $response = $http->head($url);
1001 These methods are shorthand for calling C<request()> for the given method. The
1002 URL must have unsafe characters escaped and international domain names encoded.
1003 See C<request()> for valid options and a description of the response.
1005 The C<success> field of the response will be true if the status code is 2XX.
1009 $response = $http->post_form($url, $form_data);
1010 $response = $http->post_form($url, $form_data, \%options);
1012 This method executes a C<POST> request and sends the key/value pairs from a
1013 form data hash or array reference to the given URL with a C<content-type> of
1014 C<application/x-www-form-urlencoded>. See documentation for the
1015 C<www_form_urlencode> method for details on the encoding.
1017 The URL must have unsafe characters escaped and international domain names
1018 encoded. See C<request()> for valid options and a description of the response.
1019 Any C<content-type> header or content in the options hashref will be ignored.
1021 The C<success> field of the response will be true if the status code is 2XX.
1025 $response = $http->mirror($url, $file, \%options)
1026 if ( $response->{success} ) {
1027 print "$file is up to date\n";
1030 Executes a C<GET> request for the URL and saves the response body to the file
1031 name provided. The URL must have unsafe characters escaped and international
1032 domain names encoded. If the file already exists, the request will includes an
1033 C<If-Modified-Since> header with the modification timestamp of the file. You
1034 may specify a different C<If-Modified-Since> header yourself in the C<<
1035 $options->{headers} >> hash.
1037 The C<success> field of the response will be true if the status code is 2XX
1038 or if the status code is 304 (unmodified).
1040 If the file was modified and the server response includes a properly
1041 formatted C<Last-Modified> header, the file modification time will
1042 be updated accordingly.
1046 $response = $http->request($method, $url);
1047 $response = $http->request($method, $url, \%options);
1049 Executes an HTTP request of the given method type ('GET', 'HEAD', 'POST',
1050 'PUT', etc.) on the given URL. The URL must have unsafe characters escaped and
1051 international domain names encoded. A hashref of options may be appended to
1062 A hashref containing headers to include with the request. If the value for
1063 a header is an array reference, the header will be output multiple times with
1064 each value in the array. These headers over-write any default headers.
1070 A scalar to include as the body of the request OR a code reference
1071 that will be called iteratively to produce the body of the request
1077 A code reference that will be called if it exists to provide a hashref
1078 of trailing headers (only used with chunked transfer-encoding)
1084 A code reference that will be called for each chunks of the response
1089 If the C<content> option is a code reference, it will be called iteratively
1090 to provide the content body of the request. It should return the empty
1091 string or undef when the iterator is exhausted.
1093 If the C<data_callback> option is provided, it will be called iteratively until
1094 the entire response body is received. The first argument will be a string
1095 containing a chunk of the response body, the second argument will be the
1096 in-progress response hash reference, as described below. (This allows
1097 customizing the action of the callback based on the C<status> or C<headers>
1098 received prior to the content body.)
1100 The C<request> method returns a hashref containing the response. The hashref
1101 will have the following keys:
1109 Boolean indicating whether the operation returned a 2XX status code
1115 URL that provided the response. This is the URL of the request unless
1116 there were redirections, in which case it is the last URL queried
1117 in a redirection chain
1123 The HTTP status code of the response
1129 The response phrase returned by the server
1135 The body of the response. If the response does not have any content
1136 or if a data callback is provided to consume the response body,
1137 this will be the empty string
1143 A hashref of header fields. All header field names will be normalized
1144 to be lower case. If a header is repeated, the value will be an arrayref;
1145 it will otherwise be a scalar string containing the value
1149 On an exception during the execution of the request, the C<status> field will
1150 contain 599, and the C<content> field will contain the text of the exception.
1152 =head2 www_form_urlencode
1154 $params = $http->www_form_urlencode( $data );
1155 $response = $http->get("http://example.com/query?$params");
1157 This method converts the key/value pairs from a data hash or array reference
1158 into a C<x-www-form-urlencoded> string. The keys and values from the data
1159 reference will be UTF-8 encoded and escaped per RFC 3986. If a value is an
1160 array reference, the key will be repeated with each of the values of the array
1161 reference. The key/value pairs in the resulting string will be sorted by key
1164 =for Pod::Coverage agent
1176 Direct C<https> connections are supported only if L<IO::Socket::SSL> 1.56 or
1177 greater is installed. An exception will be thrown if a new enough
1178 IO::Socket::SSL is not installed or if the SSL encryption fails. There is no
1179 support for C<https> connections via proxy (i.e. RFC 2817).
1181 SSL provides two distinct capabilities:
1187 Encrypted communication channel
1191 Verification of server identity
1195 B<By default, HTTP::Tiny does not verify server identity>.
1197 Server identity verification is controversial and potentially tricky because it
1198 depends on a (usually paid) third-party Certificate Authority (CA) trust model
1199 to validate a certificate as legitimate. This discriminates against servers
1200 with self-signed certificates or certificates signed by free, community-driven
1201 CA's such as L<CAcert.org|http://cacert.org>.
1203 By default, HTTP::Tiny does not make any assumptions about your trust model,
1204 threat level or risk tolerance. It just aims to give you an encrypted channel
1207 Setting the C<verify_SSL> attribute to a true value will make HTTP::Tiny verify
1208 that an SSL connection has a valid SSL certificate corresponding to the host
1209 name of the connection and that the SSL certificate has been verified by a CA.
1210 Assuming you trust the CA, this will protect against a L<man-in-the-middle
1211 attack|http://en.wikipedia.org/wiki/Man-in-the-middle_attack>. If you are
1212 concerned about security, you should enable this option.
1214 Certificate verification requires a file containing trusted CA certificates.
1215 If the L<Mozilla::CA> module is installed, HTTP::Tiny will use the CA file
1216 included with it as a source of trusted CA's. (This means you trust Mozilla,
1217 the author of Mozilla::CA, the CPAN mirror where you got Mozilla::CA, the
1218 toolchain used to install it, and your operating system security, right?)
1220 If that module is not available, then HTTP::Tiny will search several
1221 system-specific default locations for a CA certificate file:
1227 /etc/ssl/certs/ca-certificates.crt
1231 /etc/pki/tls/certs/ca-bundle.crt
1235 /etc/ssl/ca-bundle.pem
1239 An exception will be raised if C<verify_SSL> is true and no CA certificate file
1242 If you desire complete control over SSL connections, the C<SSL_options> attribute
1243 lets you provide a hash reference that will be passed through to
1244 C<IO::Socket::SSL::start_SSL()>, overriding any options set by HTTP::Tiny. For
1245 example, to provide your own trusted CA file:
1248 SSL_ca_file => $file_path,
1251 The C<SSL_options> attribute could also be used for such things as providing a
1252 client certificate for authentication to a server or controlling the choice of
1253 cipher used for the SSL connection. See L<IO::Socket::SSL> documentation for
1258 HTTP::Tiny is I<conditionally compliant> with the
1259 L<HTTP/1.1 specification|http://www.w3.org/Protocols/rfc2616/rfc2616.html>.
1260 It attempts to meet all "MUST" requirements of the specification, but does not
1261 implement all "SHOULD" requirements.
1263 Some particular limitations of note include:
1269 HTTP::Tiny focuses on correct transport. Users are responsible for ensuring
1270 that user-defined headers and content are compliant with the HTTP/1.1
1275 Users must ensure that URLs are properly escaped for unsafe characters and that
1276 international domain names are properly encoded to ASCII. See L<URI::Escape>,
1277 L<URI::_punycode> and L<Net::IDN::Encode>.
1281 Redirection is very strict against the specification. Redirection is only
1282 automatic for response codes 301, 302 and 307 if the request method is 'GET' or
1283 'HEAD'. Response code 303 is always converted into a 'GET' redirection, as
1284 mandated by the specification. There is no automatic support for status 305
1285 ("Use proxy") redirections.
1289 Persistent connections are not supported. The C<Connection> header will
1290 always be set to C<close>.
1294 Cookies are not directly supported. Users that set a C<Cookie> header
1295 should also set C<max_redirect> to zero to ensure cookies are not
1296 inappropriately re-transmitted.
1300 Only the C<http_proxy> environment variable is supported in the format
1301 C<http://HOST:PORT/>. If a C<proxy> argument is passed to C<new> (including
1302 undef), then the C<http_proxy> environment variable is ignored.
1306 There is no provision for delaying a request body using an C<Expect> header.
1307 Unexpected C<1XX> responses are silently ignored as per the specification.
1311 Only 'chunked' C<Transfer-Encoding> is supported.
1315 There is no support for a Request-URI of '*' for the 'OPTIONS' request.
1319 There is no support for IPv6 of any kind.
1341 =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
1345 =head2 Bugs / Feature Requests
1347 Please report any bugs or feature requests through the issue tracker
1348 at L<http://rt.cpan.org/Public/Dist/Display.html?Name=HTTP-Tiny>.
1349 You will be notified automatically of any progress on your issue.
1353 This is open source software. The code repository is available for
1354 public review and contribution under the terms of the license.
1356 L<https://github.com/dagolden/p5-http-tiny>
1358 git clone https://github.com/dagolden/p5-http-tiny.git
1366 Christian Hansen <chansen@cpan.org>
1370 David Golden <dagolden@cpan.org>
1374 Mike Doherty <doherty@cpan.org>
1378 =head1 COPYRIGHT AND LICENSE
1380 This software is copyright (c) 2012 by Christian Hansen.
1382 This is free software; you can redistribute it and/or modify it under
1383 the same terms as the Perl 5 programming language system itself.