Revert "Roll NDK to r11c and extract it into its own repository."
[android_tools.git] / ndk / prebuilt / linux-x86_64 / lib / perl5 / 5.16.2 / HTTP / Tiny.pm
blob46dce742e38029e7d457588e91007b22a2e47a42
1 # vim: ts=4 sts=4 sw=4 et:
2 package HTTP::Tiny;
3 use strict;
4 use warnings;
5 # ABSTRACT: A small, simple, correct HTTP/1.1 client
6 our $VERSION = '0.017'; # VERSION
8 use Carp ();
11 my @attributes;
12 BEGIN {
13 @attributes = qw(agent default_headers max_redirect max_size proxy timeout);
14 no strict 'refs';
15 for my $accessor ( @attributes ) {
16 *{$accessor} = sub {
17 @_ > 1 ? $_[0]->{$accessor} = $_[1] : $_[0]->{$accessor};
22 sub new {
23 my($class, %args) = @_;
24 (my $agent = $class) =~ s{::}{-}g;
25 my $self = {
26 agent => $agent . "/" . ($class->VERSION || 0),
27 max_redirect => 5,
28 timeout => 60,
30 for my $key ( @attributes ) {
31 $self->{$key} = $args{$key} if exists $args{$key}
34 # Never override proxy argument as this breaks backwards compat.
35 if (!exists $self->{proxy} && (my $http_proxy = $ENV{http_proxy})) {
36 if ($http_proxy =~ m{\Ahttp://[^/?#:@]+:\d+/?\z}) {
37 $self->{proxy} = $http_proxy;
39 else {
40 Carp::croak(qq{Environment 'http_proxy' must be in format http://<host>:<port>/\n});
44 return bless $self, $class;
48 for my $sub_name ( qw/get head put post delete/ ) {
49 my $req_method = uc $sub_name;
50 no strict 'refs';
51 eval <<"HERE"; ## no critic
52 sub $sub_name {
53 my (\$self, \$url, \$args) = \@_;
54 \@_ == 2 || (\@_ == 3 && ref \$args eq 'HASH')
55 or Carp::croak(q/Usage: \$http->$sub_name(URL, [HASHREF])/ . "\n");
56 return \$self->request('$req_method', \$url, \$args || {});
58 HERE
62 sub post_form {
63 my ($self, $url, $data, $args) = @_;
64 (@_ == 3 || @_ == 4 && ref $args eq 'HASH')
65 or Carp::croak(q/Usage: $http->post_form(URL, DATAREF, [HASHREF])/ . "\n");
67 my $headers = {};
68 while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
69 $headers->{lc $key} = $value;
71 delete $args->{headers};
73 return $self->request('POST', $url, {
74 %$args,
75 content => $self->www_form_urlencode($data),
76 headers => {
77 %$headers,
78 'content-type' => 'application/x-www-form-urlencoded'
85 sub mirror {
86 my ($self, $url, $file, $args) = @_;
87 @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
88 or Carp::croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/ . "\n");
89 if ( -e $file and my $mtime = (stat($file))[9] ) {
90 $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
92 my $tempfile = $file . int(rand(2**31));
93 open my $fh, ">", $tempfile
94 or Carp::croak(qq/Error: Could not open temporary file $tempfile for downloading: $!\n/);
95 binmode $fh;
96 $args->{data_callback} = sub { print {$fh} $_[0] };
97 my $response = $self->request('GET', $url, $args);
98 close $fh
99 or Carp::croak(qq/Error: Could not close temporary file $tempfile: $!\n/);
100 if ( $response->{success} ) {
101 rename $tempfile, $file
102 or Carp::croak(qq/Error replacing $file with $tempfile: $!\n/);
103 my $lm = $response->{headers}{'last-modified'};
104 if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
105 utime $mtime, $mtime, $file;
108 $response->{success} ||= $response->{status} eq '304';
109 unlink $tempfile;
110 return $response;
114 my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/;
116 sub request {
117 my ($self, $method, $url, $args) = @_;
118 @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
119 or Carp::croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/ . "\n");
120 $args ||= {}; # we keep some state in this during _request
122 # RFC 2616 Section 8.1.4 mandates a single retry on broken socket
123 my $response;
124 for ( 0 .. 1 ) {
125 $response = eval { $self->_request($method, $url, $args) };
126 last unless $@ && $idempotent{$method}
127 && $@ =~ m{^(?:Socket closed|Unexpected end)};
130 if (my $e = "$@") {
131 $response = {
132 success => q{},
133 status => 599,
134 reason => 'Internal Exception',
135 content => $e,
136 headers => {
137 'content-type' => 'text/plain',
138 'content-length' => length $e,
142 return $response;
146 sub www_form_urlencode {
147 my ($self, $data) = @_;
148 (@_ == 2 && ref $data)
149 or Carp::croak(q/Usage: $http->www_form_urlencode(DATAREF)/ . "\n");
150 (ref $data eq 'HASH' || ref $data eq 'ARRAY')
151 or Carp::croak("form data must be a hash or array reference");
153 my @params = ref $data eq 'HASH' ? %$data : @$data;
154 @params % 2 == 0
155 or Carp::croak("form data reference must have an even number of terms\n");
157 my @terms;
158 while( @params ) {
159 my ($key, $value) = splice(@params, 0, 2);
160 if ( ref $value eq 'ARRAY' ) {
161 unshift @params, map { $key => $_ } @$value;
163 else {
164 push @terms, join("=", map { $self->_uri_escape($_) } $key, $value);
168 return join("&", sort @terms);
171 #--------------------------------------------------------------------------#
172 # private methods
173 #--------------------------------------------------------------------------#
175 my %DefaultPort = (
176 http => 80,
177 https => 443,
180 sub _request {
181 my ($self, $method, $url, $args) = @_;
183 my ($scheme, $host, $port, $path_query) = $self->_split_url($url);
185 my $request = {
186 method => $method,
187 scheme => $scheme,
188 host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
189 uri => $path_query,
190 headers => {},
193 my $handle = HTTP::Tiny::Handle->new(timeout => $self->{timeout});
195 if ($self->{proxy}) {
196 $request->{uri} = "$scheme://$request->{host_port}$path_query";
197 die(qq/HTTPS via proxy is not supported\n/)
198 if $request->{scheme} eq 'https';
199 $handle->connect(($self->_split_url($self->{proxy}))[0..2]);
201 else {
202 $handle->connect($scheme, $host, $port);
205 $self->_prepare_headers_and_cb($request, $args);
206 $handle->write_request($request);
208 my $response;
209 do { $response = $handle->read_response_header }
210 until (substr($response->{status},0,1) ne '1');
212 if ( my @redir_args = $self->_maybe_redirect($request, $response, $args) ) {
213 $handle->close;
214 return $self->_request(@redir_args, $args);
217 if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) {
218 # response has no message body
220 else {
221 my $data_cb = $self->_prepare_data_cb($response, $args);
222 $handle->read_body($data_cb, $response);
225 $handle->close;
226 $response->{success} = substr($response->{status},0,1) eq '2';
227 return $response;
230 sub _prepare_headers_and_cb {
231 my ($self, $request, $args) = @_;
233 for ($self->{default_headers}, $args->{headers}) {
234 next unless defined;
235 while (my ($k, $v) = each %$_) {
236 $request->{headers}{lc $k} = $v;
239 $request->{headers}{'host'} = $request->{host_port};
240 $request->{headers}{'connection'} = "close";
241 $request->{headers}{'user-agent'} ||= $self->{agent};
243 if (defined $args->{content}) {
244 $request->{headers}{'content-type'} ||= "application/octet-stream";
245 if (ref $args->{content} eq 'CODE') {
246 $request->{headers}{'transfer-encoding'} = 'chunked'
247 unless $request->{headers}{'content-length'}
248 || $request->{headers}{'transfer-encoding'};
249 $request->{cb} = $args->{content};
251 else {
252 my $content = $args->{content};
253 if ( $] ge '5.008' ) {
254 utf8::downgrade($content, 1)
255 or die(qq/Wide character in request message body\n/);
257 $request->{headers}{'content-length'} = length $content
258 unless $request->{headers}{'content-length'}
259 || $request->{headers}{'transfer-encoding'};
260 $request->{cb} = sub { substr $content, 0, length $content, '' };
262 $request->{trailer_cb} = $args->{trailer_callback}
263 if ref $args->{trailer_callback} eq 'CODE';
265 return;
268 sub _prepare_data_cb {
269 my ($self, $response, $args) = @_;
270 my $data_cb = $args->{data_callback};
271 $response->{content} = '';
273 if (!$data_cb || $response->{status} !~ /^2/) {
274 if (defined $self->{max_size}) {
275 $data_cb = sub {
276 $_[1]->{content} .= $_[0];
277 die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/)
278 if length $_[1]->{content} > $self->{max_size};
281 else {
282 $data_cb = sub { $_[1]->{content} .= $_[0] };
285 return $data_cb;
288 sub _maybe_redirect {
289 my ($self, $request, $response, $args) = @_;
290 my $headers = $response->{headers};
291 my ($status, $method) = ($response->{status}, $request->{method});
292 if (($status eq '303' or ($status =~ /^30[127]/ && $method =~ /^GET|HEAD$/))
293 and $headers->{location}
294 and ++$args->{redirects} <= $self->{max_redirect}
296 my $location = ($headers->{location} =~ /^\//)
297 ? "$request->{scheme}://$request->{host_port}$headers->{location}"
298 : $headers->{location} ;
299 return (($status eq '303' ? 'GET' : $method), $location);
301 return;
304 sub _split_url {
305 my $url = pop;
307 # URI regex adapted from the URI module
308 my ($scheme, $authority, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)>
309 or die(qq/Cannot parse URL: '$url'\n/);
311 $scheme = lc $scheme;
312 $path_query = "/$path_query" unless $path_query =~ m<\A/>;
314 my $host = (length($authority)) ? lc $authority : 'localhost';
315 $host =~ s/\A[^@]*@//; # userinfo
316 my $port = do {
317 $host =~ s/:([0-9]*)\z// && length $1
318 ? $1
319 : ($scheme eq 'http' ? 80 : $scheme eq 'https' ? 443 : undef);
322 return ($scheme, $host, $port, $path_query);
325 # Date conversions adapted from HTTP::Date
326 my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat";
327 my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
328 sub _http_date {
329 my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]);
330 return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
331 substr($DoW,$wday*4,3),
332 $mday, substr($MoY,$mon*4,3), $year+1900,
333 $hour, $min, $sec
337 sub _parse_http_date {
338 my ($self, $str) = @_;
339 require Time::Local;
340 my @tl_parts;
341 if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) {
342 @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
344 elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) {
345 @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
347 elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) {
348 @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6);
350 return eval {
351 my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1;
352 $t < 0 ? undef : $t;
356 # URI escaping adapted from URI::Escape
357 # c.f. http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
358 # perl 5.6 ready UTF-8 encoding adapted from JSON::PP
359 my %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255;
360 $escapes{' '}="+";
361 my $unsafe_char = qr/[^A-Za-z0-9\-\._~]/;
363 sub _uri_escape {
364 my ($self, $str) = @_;
365 if ( $] ge '5.008' ) {
366 utf8::encode($str);
368 else {
369 $str = pack("U*", unpack("C*", $str)) # UTF-8 encode a byte string
370 if ( length $str == do { use bytes; length $str } );
371 $str = pack("C*", unpack("C*", $str)); # clear UTF-8 flag
373 $str =~ s/($unsafe_char)/$escapes{$1}/ge;
374 return $str;
377 package
378 HTTP::Tiny::Handle; # hide from PAUSE/indexers
379 use strict;
380 use warnings;
382 use Errno qw[EINTR EPIPE];
383 use IO::Socket qw[SOCK_STREAM];
385 sub BUFSIZE () { 32768 } ## no critic
387 my $Printable = sub {
388 local $_ = shift;
389 s/\r/\\r/g;
390 s/\n/\\n/g;
391 s/\t/\\t/g;
392 s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
396 my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
398 sub new {
399 my ($class, %args) = @_;
400 return bless {
401 rbuf => '',
402 timeout => 60,
403 max_line_size => 16384,
404 max_header_lines => 64,
405 %args
406 }, $class;
409 my $ssl_verify_args = {
410 check_cn => "when_only",
411 wildcards_in_alt => "anywhere",
412 wildcards_in_cn => "anywhere"
415 sub connect {
416 @_ == 4 || die(q/Usage: $handle->connect(scheme, host, port)/ . "\n");
417 my ($self, $scheme, $host, $port) = @_;
419 if ( $scheme eq 'https' ) {
420 eval "require IO::Socket::SSL"
421 unless exists $INC{'IO/Socket/SSL.pm'};
422 die(qq/IO::Socket::SSL must be installed for https support\n/)
423 unless $INC{'IO/Socket/SSL.pm'};
425 elsif ( $scheme ne 'http' ) {
426 die(qq/Unsupported URL scheme '$scheme'\n/);
429 $self->{fh} = 'IO::Socket::INET'->new(
430 PeerHost => $host,
431 PeerPort => $port,
432 Proto => 'tcp',
433 Type => SOCK_STREAM,
434 Timeout => $self->{timeout}
435 ) or die(qq/Could not connect to '$host:$port': $@\n/);
437 binmode($self->{fh})
438 or die(qq/Could not binmode() socket: '$!'\n/);
440 if ( $scheme eq 'https') {
441 IO::Socket::SSL->start_SSL($self->{fh});
442 ref($self->{fh}) eq 'IO::Socket::SSL'
443 or die(qq/SSL connection failed for $host\n/);
444 $self->{fh}->verify_hostname( $host, $ssl_verify_args )
445 or die(qq/SSL certificate not valid for $host\n/);
448 $self->{host} = $host;
449 $self->{port} = $port;
451 return $self;
454 sub close {
455 @_ == 1 || die(q/Usage: $handle->close()/ . "\n");
456 my ($self) = @_;
457 CORE::close($self->{fh})
458 or die(qq/Could not close socket: '$!'\n/);
461 sub write {
462 @_ == 2 || die(q/Usage: $handle->write(buf)/ . "\n");
463 my ($self, $buf) = @_;
465 if ( $] ge '5.008' ) {
466 utf8::downgrade($buf, 1)
467 or die(qq/Wide character in write()\n/);
470 my $len = length $buf;
471 my $off = 0;
473 local $SIG{PIPE} = 'IGNORE';
475 while () {
476 $self->can_write
477 or die(qq/Timed out while waiting for socket to become ready for writing\n/);
478 my $r = syswrite($self->{fh}, $buf, $len, $off);
479 if (defined $r) {
480 $len -= $r;
481 $off += $r;
482 last unless $len > 0;
484 elsif ($! == EPIPE) {
485 die(qq/Socket closed by remote server: $!\n/);
487 elsif ($! != EINTR) {
488 die(qq/Could not write to socket: '$!'\n/);
491 return $off;
494 sub read {
495 @_ == 2 || @_ == 3 || die(q/Usage: $handle->read(len [, allow_partial])/ . "\n");
496 my ($self, $len, $allow_partial) = @_;
498 my $buf = '';
499 my $got = length $self->{rbuf};
501 if ($got) {
502 my $take = ($got < $len) ? $got : $len;
503 $buf = substr($self->{rbuf}, 0, $take, '');
504 $len -= $take;
507 while ($len > 0) {
508 $self->can_read
509 or die(q/Timed out while waiting for socket to become ready for reading/ . "\n");
510 my $r = sysread($self->{fh}, $buf, $len, length $buf);
511 if (defined $r) {
512 last unless $r;
513 $len -= $r;
515 elsif ($! != EINTR) {
516 die(qq/Could not read from socket: '$!'\n/);
519 if ($len && !$allow_partial) {
520 die(qq/Unexpected end of stream\n/);
522 return $buf;
525 sub readline {
526 @_ == 1 || die(q/Usage: $handle->readline()/ . "\n");
527 my ($self) = @_;
529 while () {
530 if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
531 return $1;
533 if (length $self->{rbuf} >= $self->{max_line_size}) {
534 die(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}\n/);
536 $self->can_read
537 or die(qq/Timed out while waiting for socket to become ready for reading\n/);
538 my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
539 if (defined $r) {
540 last unless $r;
542 elsif ($! != EINTR) {
543 die(qq/Could not read from socket: '$!'\n/);
546 die(qq/Unexpected end of stream while looking for line\n/);
549 sub read_header_lines {
550 @_ == 1 || @_ == 2 || die(q/Usage: $handle->read_header_lines([headers])/ . "\n");
551 my ($self, $headers) = @_;
552 $headers ||= {};
553 my $lines = 0;
554 my $val;
556 while () {
557 my $line = $self->readline;
559 if (++$lines >= $self->{max_header_lines}) {
560 die(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}\n/);
562 elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
563 my ($field_name) = lc $1;
564 if (exists $headers->{$field_name}) {
565 for ($headers->{$field_name}) {
566 $_ = [$_] unless ref $_ eq "ARRAY";
567 push @$_, $2;
568 $val = \$_->[-1];
571 else {
572 $val = \($headers->{$field_name} = $2);
575 elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
576 $val
577 or die(qq/Unexpected header continuation line\n/);
578 next unless length $1;
579 $$val .= ' ' if length $$val;
580 $$val .= $1;
582 elsif ($line =~ /\A \x0D?\x0A \z/x) {
583 last;
585 else {
586 die(q/Malformed header line: / . $Printable->($line) . "\n");
589 return $headers;
592 sub write_request {
593 @_ == 2 || die(q/Usage: $handle->write_request(request)/ . "\n");
594 my($self, $request) = @_;
595 $self->write_request_header(@{$request}{qw/method uri headers/});
596 $self->write_body($request) if $request->{cb};
597 return;
600 my %HeaderCase = (
601 'content-md5' => 'Content-MD5',
602 'etag' => 'ETag',
603 'te' => 'TE',
604 'www-authenticate' => 'WWW-Authenticate',
605 'x-xss-protection' => 'X-XSS-Protection',
608 sub write_header_lines {
609 (@_ == 2 && ref $_[1] eq 'HASH') || die(q/Usage: $handle->write_header_lines(headers)/ . "\n");
610 my($self, $headers) = @_;
612 my $buf = '';
613 while (my ($k, $v) = each %$headers) {
614 my $field_name = lc $k;
615 if (exists $HeaderCase{$field_name}) {
616 $field_name = $HeaderCase{$field_name};
618 else {
619 $field_name =~ /\A $Token+ \z/xo
620 or die(q/Invalid HTTP header field name: / . $Printable->($field_name) . "\n");
621 $field_name =~ s/\b(\w)/\u$1/g;
622 $HeaderCase{lc $field_name} = $field_name;
624 for (ref $v eq 'ARRAY' ? @$v : $v) {
625 /[^\x0D\x0A]/
626 or die(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_). "\n");
627 $buf .= "$field_name: $_\x0D\x0A";
630 $buf .= "\x0D\x0A";
631 return $self->write($buf);
634 sub read_body {
635 @_ == 3 || die(q/Usage: $handle->read_body(callback, response)/ . "\n");
636 my ($self, $cb, $response) = @_;
637 my $te = $response->{headers}{'transfer-encoding'} || '';
638 if ( grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ) {
639 $self->read_chunked_body($cb, $response);
641 else {
642 $self->read_content_body($cb, $response);
644 return;
647 sub write_body {
648 @_ == 2 || die(q/Usage: $handle->write_body(request)/ . "\n");
649 my ($self, $request) = @_;
650 if ($request->{headers}{'content-length'}) {
651 return $self->write_content_body($request);
653 else {
654 return $self->write_chunked_body($request);
658 sub read_content_body {
659 @_ == 3 || @_ == 4 || die(q/Usage: $handle->read_content_body(callback, response, [read_length])/ . "\n");
660 my ($self, $cb, $response, $content_length) = @_;
661 $content_length ||= $response->{headers}{'content-length'};
663 if ( $content_length ) {
664 my $len = $content_length;
665 while ($len > 0) {
666 my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
667 $cb->($self->read($read, 0), $response);
668 $len -= $read;
671 else {
672 my $chunk;
673 $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) );
676 return;
679 sub write_content_body {
680 @_ == 2 || die(q/Usage: $handle->write_content_body(request)/ . "\n");
681 my ($self, $request) = @_;
683 my ($len, $content_length) = (0, $request->{headers}{'content-length'});
684 while () {
685 my $data = $request->{cb}->();
687 defined $data && length $data
688 or last;
690 if ( $] ge '5.008' ) {
691 utf8::downgrade($data, 1)
692 or die(qq/Wide character in write_content()\n/);
695 $len += $self->write($data);
698 $len == $content_length
699 or die(qq/Content-Length missmatch (got: $len expected: $content_length)\n/);
701 return $len;
704 sub read_chunked_body {
705 @_ == 3 || die(q/Usage: $handle->read_chunked_body(callback, $response)/ . "\n");
706 my ($self, $cb, $response) = @_;
708 while () {
709 my $head = $self->readline;
711 $head =~ /\A ([A-Fa-f0-9]+)/x
712 or die(q/Malformed chunk head: / . $Printable->($head) . "\n");
714 my $len = hex($1)
715 or last;
717 $self->read_content_body($cb, $response, $len);
719 $self->read(2) eq "\x0D\x0A"
720 or die(qq/Malformed chunk: missing CRLF after chunk data\n/);
722 $self->read_header_lines($response->{headers});
723 return;
726 sub write_chunked_body {
727 @_ == 2 || die(q/Usage: $handle->write_chunked_body(request)/ . "\n");
728 my ($self, $request) = @_;
730 my $len = 0;
731 while () {
732 my $data = $request->{cb}->();
734 defined $data && length $data
735 or last;
737 if ( $] ge '5.008' ) {
738 utf8::downgrade($data, 1)
739 or die(qq/Wide character in write_chunked_body()\n/);
742 $len += length $data;
744 my $chunk = sprintf '%X', length $data;
745 $chunk .= "\x0D\x0A";
746 $chunk .= $data;
747 $chunk .= "\x0D\x0A";
749 $self->write($chunk);
751 $self->write("0\x0D\x0A");
752 $self->write_header_lines($request->{trailer_cb}->())
753 if ref $request->{trailer_cb} eq 'CODE';
754 return $len;
757 sub read_response_header {
758 @_ == 1 || die(q/Usage: $handle->read_response_header()/ . "\n");
759 my ($self) = @_;
761 my $line = $self->readline;
763 $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
764 or die(q/Malformed Status-Line: / . $Printable->($line). "\n");
766 my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
768 die (qq/Unsupported HTTP protocol: $protocol\n/)
769 unless $version =~ /0*1\.0*[01]/;
771 return {
772 status => $status,
773 reason => $reason,
774 headers => $self->read_header_lines,
775 protocol => $protocol,
779 sub write_request_header {
780 @_ == 4 || die(q/Usage: $handle->write_request_header(method, request_uri, headers)/ . "\n");
781 my ($self, $method, $request_uri, $headers) = @_;
783 return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
784 + $self->write_header_lines($headers);
787 sub _do_timeout {
788 my ($self, $type, $timeout) = @_;
789 $timeout = $self->{timeout}
790 unless defined $timeout && $timeout >= 0;
792 my $fd = fileno $self->{fh};
793 defined $fd && $fd >= 0
794 or die(qq/select(2): 'Bad file descriptor'\n/);
796 my $initial = time;
797 my $pending = $timeout;
798 my $nfound;
800 vec(my $fdset = '', $fd, 1) = 1;
802 while () {
803 $nfound = ($type eq 'read')
804 ? select($fdset, undef, undef, $pending)
805 : select(undef, $fdset, undef, $pending) ;
806 if ($nfound == -1) {
807 $! == EINTR
808 or die(qq/select(2): '$!'\n/);
809 redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
810 $nfound = 0;
812 last;
814 $! = 0;
815 return $nfound;
818 sub can_read {
819 @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_read([timeout])/ . "\n");
820 my $self = shift;
821 return $self->_do_timeout('read', @_)
824 sub can_write {
825 @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_write([timeout])/ . "\n");
826 my $self = shift;
827 return $self->_do_timeout('write', @_)
834 __END__
835 =pod
837 =head1 NAME
839 HTTP::Tiny - A small, simple, correct HTTP/1.1 client
841 =head1 VERSION
843 version 0.017
845 =head1 SYNOPSIS
847 use HTTP::Tiny;
849 my $response = HTTP::Tiny->new->get('http://example.com/');
851 die "Failed!\n" unless $response->{success};
853 print "$response->{status} $response->{reason}\n";
855 while (my ($k, $v) = each %{$response->{headers}}) {
856 for (ref $v eq 'ARRAY' ? @$v : $v) {
857 print "$k: $_\n";
861 print $response->{content} if length $response->{content};
863 =head1 DESCRIPTION
865 This is a very simple HTTP/1.1 client, designed for doing simple GET
866 requests without the overhead of a large framework like L<LWP::UserAgent>.
868 It is more correct and more complete than L<HTTP::Lite>. It supports
869 proxies (currently only non-authenticating ones) and redirection. It
870 also correctly resumes after EINTR.
872 =head1 METHODS
874 =head2 new
876 $http = HTTP::Tiny->new( %attributes );
878 This constructor returns a new HTTP::Tiny object. Valid attributes include:
880 =over 4
882 =item *
884 C<agent>
886 A user-agent string (defaults to 'HTTP::Tiny/$VERSION')
888 =item *
890 C<default_headers>
892 A hashref of default headers to apply to requests
894 =item *
896 C<max_redirect>
898 Maximum number of redirects allowed (defaults to 5)
900 =item *
902 C<max_size>
904 Maximum response size (only when not using a data callback). If defined,
905 responses larger than this will return an exception.
907 =item *
909 C<proxy>
911 URL of a proxy server to use (default is C<$ENV{http_proxy}> if set)
913 =item *
915 C<timeout>
917 Request timeout in seconds (default is 60)
919 =back
921 Exceptions from C<max_size>, C<timeout> or other errors will result in a
922 pseudo-HTTP status code of 599 and a reason of "Internal Exception". The
923 content field in the response will contain the text of the exception.
925 =head2 get|head|put|post|delete
927 $response = $http->get($url);
928 $response = $http->get($url, \%options);
929 $response = $http->head($url);
931 These methods are shorthand for calling C<request()> for the given method. The
932 URL must have unsafe characters escaped and international domain names encoded.
933 See C<request()> for valid options and a description of the response.
935 The C<success> field of the response will be true if the status code is 2XX.
937 =head2 post_form
939 $response = $http->post_form($url, $form_data);
940 $response = $http->post_form($url, $form_data, \%options);
942 This method executes a C<POST> request and sends the key/value pairs from a
943 form data hash or array reference to the given URL with a C<content-type> of
944 C<application/x-www-form-urlencoded>. See documentation for the
945 C<www_form_urlencode> method for details on the encoding.
947 The URL must have unsafe characters escaped and international domain names
948 encoded. See C<request()> for valid options and a description of the response.
949 Any C<content-type> header or content in the options hashref will be ignored.
951 The C<success> field of the response will be true if the status code is 2XX.
953 =head2 mirror
955 $response = $http->mirror($url, $file, \%options)
956 if ( $response->{success} ) {
957 print "$file is up to date\n";
960 Executes a C<GET> request for the URL and saves the response body to the file
961 name provided. The URL must have unsafe characters escaped and international
962 domain names encoded. If the file already exists, the request will includes an
963 C<If-Modified-Since> header with the modification timestamp of the file. You
964 may specify a different C<If-Modified-Since> header yourself in the C<<
965 $options->{headers} >> hash.
967 The C<success> field of the response will be true if the status code is 2XX
968 or if the status code is 304 (unmodified).
970 If the file was modified and the server response includes a properly
971 formatted C<Last-Modified> header, the file modification time will
972 be updated accordingly.
974 =head2 request
976 $response = $http->request($method, $url);
977 $response = $http->request($method, $url, \%options);
979 Executes an HTTP request of the given method type ('GET', 'HEAD', 'POST',
980 'PUT', etc.) on the given URL. The URL must have unsafe characters escaped and
981 international domain names encoded. A hashref of options may be appended to
982 modify the request.
984 Valid options are:
986 =over 4
988 =item *
990 headers
992 A hashref containing headers to include with the request. If the value for
993 a header is an array reference, the header will be output multiple times with
994 each value in the array. These headers over-write any default headers.
996 =item *
998 content
1000 A scalar to include as the body of the request OR a code reference
1001 that will be called iteratively to produce the body of the response
1003 =item *
1005 trailer_callback
1007 A code reference that will be called if it exists to provide a hashref
1008 of trailing headers (only used with chunked transfer-encoding)
1010 =item *
1012 data_callback
1014 A code reference that will be called for each chunks of the response
1015 body received.
1017 =back
1019 If the C<content> option is a code reference, it will be called iteratively
1020 to provide the content body of the request. It should return the empty
1021 string or undef when the iterator is exhausted.
1023 If the C<data_callback> option is provided, it will be called iteratively until
1024 the entire response body is received. The first argument will be a string
1025 containing a chunk of the response body, the second argument will be the
1026 in-progress response hash reference, as described below. (This allows
1027 customizing the action of the callback based on the C<status> or C<headers>
1028 received prior to the content body.)
1030 The C<request> method returns a hashref containing the response. The hashref
1031 will have the following keys:
1033 =over 4
1035 =item *
1037 success
1039 Boolean indicating whether the operation returned a 2XX status code
1041 =item *
1043 status
1045 The HTTP status code of the response
1047 =item *
1049 reason
1051 The response phrase returned by the server
1053 =item *
1055 content
1057 The body of the response. If the response does not have any content
1058 or if a data callback is provided to consume the response body,
1059 this will be the empty string
1061 =item *
1063 headers
1065 A hashref of header fields. All header field names will be normalized
1066 to be lower case. If a header is repeated, the value will be an arrayref;
1067 it will otherwise be a scalar string containing the value
1069 =back
1071 On an exception during the execution of the request, the C<status> field will
1072 contain 599, and the C<content> field will contain the text of the exception.
1074 =head2 www_form_urlencode
1076 $params = $http->www_form_urlencode( $data );
1077 $response = $http->get("http://example.com/query?$params");
1079 This method converts the key/value pairs from a data hash or array reference
1080 into a C<x-www-form-urlencoded> string. The keys and values from the data
1081 reference will be UTF-8 encoded and escaped per RFC 3986. If a value is an
1082 array reference, the key will be repeated with each of the values of the array
1083 reference. The key/value pairs in the resulting string will be sorted by key
1084 and value.
1086 =for Pod::Coverage agent
1087 default_headers
1088 max_redirect
1089 max_size
1090 proxy
1091 timeout
1093 =head1 LIMITATIONS
1095 HTTP::Tiny is I<conditionally compliant> with the
1096 L<HTTP/1.1 specification|http://www.w3.org/Protocols/rfc2616/rfc2616.html>.
1097 It attempts to meet all "MUST" requirements of the specification, but does not
1098 implement all "SHOULD" requirements.
1100 Some particular limitations of note include:
1102 =over
1104 =item *
1106 HTTP::Tiny focuses on correct transport. Users are responsible for ensuring
1107 that user-defined headers and content are compliant with the HTTP/1.1
1108 specification.
1110 =item *
1112 Users must ensure that URLs are properly escaped for unsafe characters and that
1113 international domain names are properly encoded to ASCII. See L<URI::Escape>,
1114 L<URI::_punycode> and L<Net::IDN::Encode>.
1116 =item *
1118 Redirection is very strict against the specification. Redirection is only
1119 automatic for response codes 301, 302 and 307 if the request method is 'GET' or
1120 'HEAD'. Response code 303 is always converted into a 'GET' redirection, as
1121 mandated by the specification. There is no automatic support for status 305
1122 ("Use proxy") redirections.
1124 =item *
1126 Persistent connections are not supported. The C<Connection> header will
1127 always be set to C<close>.
1129 =item *
1131 Direct C<https> connections are supported only if L<IO::Socket::SSL> is
1132 installed. There is no support for C<https> connections via proxy.
1133 Any SSL certificate that matches the host is accepted -- SSL certificates
1134 are not verified against certificate authorities.
1136 =item *
1138 Cookies are not directly supported. Users that set a C<Cookie> header
1139 should also set C<max_redirect> to zero to ensure cookies are not
1140 inappropriately re-transmitted.
1142 =item *
1144 Only the C<http_proxy> environment variable is supported in the format
1145 C<http://HOST:PORT/>. If a C<proxy> argument is passed to C<new> (including
1146 undef), then the C<http_proxy> environment variable is ignored.
1148 =item *
1150 There is no provision for delaying a request body using an C<Expect> header.
1151 Unexpected C<1XX> responses are silently ignored as per the specification.
1153 =item *
1155 Only 'chunked' C<Transfer-Encoding> is supported.
1157 =item *
1159 There is no support for a Request-URI of '*' for the 'OPTIONS' request.
1161 =back
1163 =head1 SEE ALSO
1165 =over 4
1167 =item *
1169 L<LWP::UserAgent>
1171 =back
1173 =for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders
1175 =head1 SUPPORT
1177 =head2 Bugs / Feature Requests
1179 Please report any bugs or feature requests through the issue tracker
1180 at L<http://rt.cpan.org/Public/Dist/Display.html?Name=HTTP-Tiny>.
1181 You will be notified automatically of any progress on your issue.
1183 =head2 Source Code
1185 This is open source software. The code repository is available for
1186 public review and contribution under the terms of the license.
1188 L<https://github.com/dagolden/p5-http-tiny>
1190 git clone https://github.com/dagolden/p5-http-tiny.git
1192 =head1 AUTHORS
1194 =over 4
1196 =item *
1198 Christian Hansen <chansen@cpan.org>
1200 =item *
1202 David Golden <dagolden@cpan.org>
1204 =back
1206 =head1 COPYRIGHT AND LICENSE
1208 This software is copyright (c) 2012 by Christian Hansen.
1210 This is free software; you can redistribute it and/or modify it under
1211 the same terms as the Perl 5 programming language system itself.
1213 =cut