Blame lib/HTTP/Tiny.pm

Packit 3f4df8
# vim: ts=4 sts=4 sw=4 et:
Packit 3f4df8
package HTTP::Tiny;
Packit 3f4df8
use strict;
Packit 3f4df8
use warnings;
Packit 3f4df8
# ABSTRACT: A small, simple, correct HTTP/1.1 client
Packit 3f4df8
Packit 3f4df8
our $VERSION = '0.074';
Packit 3f4df8
Packit 3f4df8
sub _croak { require Carp; Carp::croak(@_) }
Packit 3f4df8
Packit 3f4df8
#pod =method new
Packit 3f4df8
#pod
Packit 3f4df8
#pod     $http = HTTP::Tiny->new( %attributes );
Packit 3f4df8
#pod
Packit 3f4df8
#pod This constructor returns a new HTTP::Tiny object.  Valid attributes include:
Packit 3f4df8
#pod
Packit 3f4df8
#pod =for :list
Packit 3f4df8
#pod * C<agent> — A user-agent string (defaults to 'HTTP-Tiny/$VERSION'). If
Packit 3f4df8
#pod   C<agent> — ends in a space character, the default user-agent string is
Packit 3f4df8
#pod   appended.
Packit 3f4df8
#pod * C<cookie_jar> — An instance of L<HTTP::CookieJar> — or equivalent class
Packit 3f4df8
#pod   that supports the C<add> and C<cookie_header> methods
Packit 3f4df8
#pod * C<default_headers> — A hashref of default headers to apply to requests
Packit 3f4df8
#pod * C<local_address> — The local IP address to bind to
Packit 3f4df8
#pod * C<keep_alive> — Whether to reuse the last connection (if for the same
Packit 3f4df8
#pod   scheme, host and port) (defaults to 1)
Packit 3f4df8
#pod * C<max_redirect> — Maximum number of redirects allowed (defaults to 5)
Packit 3f4df8
#pod * C<max_size> — Maximum response size in bytes (only when not using a data
Packit 3f4df8
#pod   callback).  If defined, responses larger than this will return an
Packit 3f4df8
#pod   exception.
Packit 3f4df8
#pod * C<http_proxy> — URL of a proxy server to use for HTTP connections
Packit 3f4df8
#pod   (default is C<$ENV{http_proxy}> — if set)
Packit 3f4df8
#pod * C<https_proxy> — URL of a proxy server to use for HTTPS connections
Packit 3f4df8
#pod   (default is C<$ENV{https_proxy}> — if set)
Packit 3f4df8
#pod * C<proxy> — URL of a generic proxy server for both HTTP and HTTPS
Packit 3f4df8
#pod   connections (default is C<$ENV{all_proxy}> — if set)
Packit 3f4df8
#pod * C<no_proxy> — List of domain suffixes that should not be proxied.  Must
Packit 3f4df8
#pod   be a comma-separated string or an array reference. (default is
Packit 3f4df8
#pod   C<$ENV{no_proxy}> —)
Packit 3f4df8
#pod * C<timeout> — Request timeout in seconds (default is 60) If a socket open,
Packit 3f4df8
#pod   read or write takes longer than the timeout, an exception is thrown.
Packit 3f4df8
#pod * C<verify_SSL> — A boolean that indicates whether to validate the SSL
Packit 3f4df8
#pod   certificate of an C<https> — connection (default is false)
Packit 3f4df8
#pod * C<SSL_options> — A hashref of C<SSL_*> — options to pass through to
Packit 3f4df8
#pod   L<IO::Socket::SSL>
Packit 3f4df8
#pod
Packit 3f4df8
#pod Passing an explicit C<undef> for C<proxy>, C<http_proxy> or C<https_proxy> will
Packit 3f4df8
#pod prevent getting the corresponding proxies from the environment.
Packit 3f4df8
#pod
Packit 3f4df8
#pod Exceptions from C<max_size>, C<timeout> or other errors will result in a
Packit 3f4df8
#pod pseudo-HTTP status code of 599 and a reason of "Internal Exception". The
Packit 3f4df8
#pod content field in the response will contain the text of the exception.
Packit 3f4df8
#pod
Packit 3f4df8
#pod The C<keep_alive> parameter enables a persistent connection, but only to a
Packit 3f4df8
#pod single destination scheme, host and port.  Also, if any connection-relevant
Packit 3f4df8
#pod attributes are modified, or if the process ID or thread ID change, the
Packit 3f4df8
#pod persistent connection will be dropped.  If you want persistent connections
Packit 3f4df8
#pod across multiple destinations, use multiple HTTP::Tiny objects.
Packit 3f4df8
#pod
Packit 3f4df8
#pod See L</SSL SUPPORT> for more on the C<verify_SSL> and C<SSL_options> attributes.
Packit 3f4df8
#pod
Packit 3f4df8
#pod =cut
Packit 3f4df8
Packit 3f4df8
my @attributes;
Packit 3f4df8
BEGIN {
Packit 3f4df8
    @attributes = qw(
Packit 3f4df8
        cookie_jar default_headers http_proxy https_proxy keep_alive
Packit 3f4df8
        local_address max_redirect max_size proxy no_proxy
Packit 3f4df8
        SSL_options verify_SSL
Packit 3f4df8
    );
Packit 3f4df8
    my %persist_ok = map {; $_ => 1 } qw(
Packit 3f4df8
        cookie_jar default_headers max_redirect max_size
Packit 3f4df8
    );
Packit 3f4df8
    no strict 'refs';
Packit 3f4df8
    no warnings 'uninitialized';
Packit 3f4df8
    for my $accessor ( @attributes ) {
Packit 3f4df8
        *{$accessor} = sub {
Packit 3f4df8
            @_ > 1
Packit 3f4df8
                ? do {
Packit 3f4df8
                    delete $_[0]->{handle} if !$persist_ok{$accessor} && $_[1] ne $_[0]->{$accessor};
Packit 3f4df8
                    $_[0]->{$accessor} = $_[1]
Packit 3f4df8
                }
Packit 3f4df8
                : $_[0]->{$accessor};
Packit 3f4df8
        };
Packit 3f4df8
    }
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub agent {
Packit 3f4df8
    my($self, $agent) = @_;
Packit 3f4df8
    if( @_ > 1 ){
Packit 3f4df8
        $self->{agent} =
Packit 3f4df8
            (defined $agent && $agent =~ / $/) ? $agent . $self->_agent : $agent;
Packit 3f4df8
    }
Packit 3f4df8
    return $self->{agent};
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub timeout {
Packit 3f4df8
    my ($self, $timeout) = @_;
Packit 3f4df8
    if ( @_ > 1 ) {
Packit 3f4df8
        $self->{timeout} = $timeout;
Packit 3f4df8
        if ($self->{handle}) {
Packit 3f4df8
            $self->{handle}->timeout($timeout);
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
    return $self->{timeout};
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub new {
Packit 3f4df8
    my($class, %args) = @_;
Packit 3f4df8
Packit 3f4df8
    my $self = {
Packit 3f4df8
        max_redirect => 5,
Packit 3f4df8
        timeout      => defined $args{timeout} ? $args{timeout} : 60,
Packit 3f4df8
        keep_alive   => 1,
Packit 3f4df8
        verify_SSL   => $args{verify_SSL} || $args{verify_ssl} || 0, # no verification by default
Packit 3f4df8
        no_proxy     => $ENV{no_proxy},
Packit 3f4df8
    };
Packit 3f4df8
Packit 3f4df8
    bless $self, $class;
Packit 3f4df8
Packit 3f4df8
    $class->_validate_cookie_jar( $args{cookie_jar} ) if $args{cookie_jar};
Packit 3f4df8
Packit 3f4df8
    for my $key ( @attributes ) {
Packit 3f4df8
        $self->{$key} = $args{$key} if exists $args{$key}
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    $self->agent( exists $args{agent} ? $args{agent} : $class->_agent );
Packit 3f4df8
Packit 3f4df8
    $self->_set_proxies;
Packit 3f4df8
Packit 3f4df8
    return $self;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _set_proxies {
Packit 3f4df8
    my ($self) = @_;
Packit 3f4df8
Packit 3f4df8
    # get proxies from %ENV only if not provided; explicit undef will disable
Packit 3f4df8
    # getting proxies from the environment
Packit 3f4df8
Packit 3f4df8
    # generic proxy
Packit 3f4df8
    if (! exists $self->{proxy} ) {
Packit 3f4df8
        $self->{proxy} = $ENV{all_proxy} || $ENV{ALL_PROXY};
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    if ( defined $self->{proxy} ) {
Packit 3f4df8
        $self->_split_proxy( 'generic proxy' => $self->{proxy} ); # validate
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        delete $self->{proxy};
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    # http proxy
Packit 3f4df8
    if (! exists $self->{http_proxy} ) {
Packit 3f4df8
        # under CGI, bypass HTTP_PROXY as request sets it from Proxy header
Packit 3f4df8
        local $ENV{HTTP_PROXY} if $ENV{REQUEST_METHOD};
Packit 3f4df8
        $self->{http_proxy} = $ENV{http_proxy} || $ENV{HTTP_PROXY} || $self->{proxy};
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    if ( defined $self->{http_proxy} ) {
Packit 3f4df8
        $self->_split_proxy( http_proxy => $self->{http_proxy} ); # validate
Packit 3f4df8
        $self->{_has_proxy}{http} = 1;
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        delete $self->{http_proxy};
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    # https proxy
Packit 3f4df8
    if (! exists $self->{https_proxy} ) {
Packit 3f4df8
        $self->{https_proxy} = $ENV{https_proxy} || $ENV{HTTPS_PROXY} || $self->{proxy};
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    if ( $self->{https_proxy} ) {
Packit 3f4df8
        $self->_split_proxy( https_proxy => $self->{https_proxy} ); # validate
Packit 3f4df8
        $self->{_has_proxy}{https} = 1;
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        delete $self->{https_proxy};
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    # Split no_proxy to array reference if not provided as such
Packit 3f4df8
    unless ( ref $self->{no_proxy} eq 'ARRAY' ) {
Packit 3f4df8
        $self->{no_proxy} =
Packit 3f4df8
            (defined $self->{no_proxy}) ? [ split /\s*,\s*/, $self->{no_proxy} ] : [];
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    return;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
#pod =method get|head|put|post|delete
Packit 3f4df8
#pod
Packit 3f4df8
#pod     $response = $http->get($url);
Packit 3f4df8
#pod     $response = $http->get($url, \%options);
Packit 3f4df8
#pod     $response = $http->head($url);
Packit 3f4df8
#pod
Packit 3f4df8
#pod These methods are shorthand for calling C<request()> for the given method.  The
Packit 3f4df8
#pod URL must have unsafe characters escaped and international domain names encoded.
Packit 3f4df8
#pod See C<request()> for valid options and a description of the response.
Packit 3f4df8
#pod
Packit 3f4df8
#pod The C<success> field of the response will be true if the status code is 2XX.
Packit 3f4df8
#pod
Packit 3f4df8
#pod =cut
Packit 3f4df8
Packit 3f4df8
for my $sub_name ( qw/get head put post delete/ ) {
Packit 3f4df8
    my $req_method = uc $sub_name;
Packit 3f4df8
    no strict 'refs';
Packit 3f4df8
    eval <<"HERE"; ## no critic
Packit 3f4df8
    sub $sub_name {
Packit 3f4df8
        my (\$self, \$url, \$args) = \@_;
Packit 3f4df8
        \@_ == 2 || (\@_ == 3 && ref \$args eq 'HASH')
Packit 3f4df8
        or _croak(q/Usage: \$http->$sub_name(URL, [HASHREF])/ . "\n");
Packit 3f4df8
        return \$self->request('$req_method', \$url, \$args || {});
Packit 3f4df8
    }
Packit 3f4df8
HERE
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
#pod =method post_form
Packit 3f4df8
#pod
Packit 3f4df8
#pod     $response = $http->post_form($url, $form_data);
Packit 3f4df8
#pod     $response = $http->post_form($url, $form_data, \%options);
Packit 3f4df8
#pod
Packit 3f4df8
#pod This method executes a C<POST> request and sends the key/value pairs from a
Packit 3f4df8
#pod form data hash or array reference to the given URL with a C<content-type> of
Packit 3f4df8
#pod C<application/x-www-form-urlencoded>.  If data is provided as an array
Packit 3f4df8
#pod reference, the order is preserved; if provided as a hash reference, the terms
Packit 3f4df8
#pod are sorted on key and value for consistency.  See documentation for the
Packit 3f4df8
#pod C<www_form_urlencode> method for details on the encoding.
Packit 3f4df8
#pod
Packit 3f4df8
#pod The URL must have unsafe characters escaped and international domain names
Packit 3f4df8
#pod encoded.  See C<request()> for valid options and a description of the response.
Packit 3f4df8
#pod Any C<content-type> header or content in the options hashref will be ignored.
Packit 3f4df8
#pod
Packit 3f4df8
#pod The C<success> field of the response will be true if the status code is 2XX.
Packit 3f4df8
#pod
Packit 3f4df8
#pod =cut
Packit 3f4df8
Packit 3f4df8
sub post_form {
Packit 3f4df8
    my ($self, $url, $data, $args) = @_;
Packit 3f4df8
    (@_ == 3 || @_ == 4 && ref $args eq 'HASH')
Packit 3f4df8
        or _croak(q/Usage: $http->post_form(URL, DATAREF, [HASHREF])/ . "\n");
Packit 3f4df8
Packit 3f4df8
    my $headers = {};
Packit 3f4df8
    while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
Packit 3f4df8
        $headers->{lc $key} = $value;
Packit 3f4df8
    }
Packit 3f4df8
    delete $args->{headers};
Packit 3f4df8
Packit 3f4df8
    return $self->request('POST', $url, {
Packit 3f4df8
            %$args,
Packit 3f4df8
            content => $self->www_form_urlencode($data),
Packit 3f4df8
            headers => {
Packit 3f4df8
                %$headers,
Packit 3f4df8
                'content-type' => 'application/x-www-form-urlencoded'
Packit 3f4df8
            },
Packit 3f4df8
        }
Packit 3f4df8
    );
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
#pod =method mirror
Packit 3f4df8
#pod
Packit 3f4df8
#pod     $response = $http->mirror($url, $file, \%options)
Packit 3f4df8
#pod     if ( $response->{success} ) {
Packit 3f4df8
#pod         print "$file is up to date\n";
Packit 3f4df8
#pod     }
Packit 3f4df8
#pod
Packit 3f4df8
#pod Executes a C<GET> request for the URL and saves the response body to the file
Packit 3f4df8
#pod name provided.  The URL must have unsafe characters escaped and international
Packit 3f4df8
#pod domain names encoded.  If the file already exists, the request will include an
Packit 3f4df8
#pod C<If-Modified-Since> header with the modification timestamp of the file.  You
Packit 3f4df8
#pod may specify a different C<If-Modified-Since> header yourself in the C<<
Packit 3f4df8
#pod $options->{headers} >> hash.
Packit 3f4df8
#pod
Packit 3f4df8
#pod The C<success> field of the response will be true if the status code is 2XX
Packit 3f4df8
#pod or if the status code is 304 (unmodified).
Packit 3f4df8
#pod
Packit 3f4df8
#pod If the file was modified and the server response includes a properly
Packit 3f4df8
#pod formatted C<Last-Modified> header, the file modification time will
Packit 3f4df8
#pod be updated accordingly.
Packit 3f4df8
#pod
Packit 3f4df8
#pod =cut
Packit 3f4df8
Packit 3f4df8
sub mirror {
Packit 3f4df8
    my ($self, $url, $file, $args) = @_;
Packit 3f4df8
    @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
Packit 3f4df8
      or _croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/ . "\n");
Packit 3f4df8
Packit 3f4df8
    if ( exists $args->{headers} ) {
Packit 3f4df8
        my $headers = {};
Packit 3f4df8
        while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
Packit 3f4df8
            $headers->{lc $key} = $value;
Packit 3f4df8
        }
Packit 3f4df8
        $args->{headers} = $headers;
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    if ( -e $file and my $mtime = (stat($file))[9] ) {
Packit 3f4df8
        $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
Packit 3f4df8
    }
Packit 3f4df8
    my $tempfile = $file . int(rand(2**31));
Packit 3f4df8
Packit 3f4df8
    require Fcntl;
Packit 3f4df8
    sysopen my $fh, $tempfile, Fcntl::O_CREAT()|Fcntl::O_EXCL()|Fcntl::O_WRONLY()
Packit 3f4df8
       or _croak(qq/Error: Could not create temporary file $tempfile for downloading: $!\n/);
Packit 3f4df8
    binmode $fh;
Packit Service aa8bca
    $args->{data_callback} = sub {
Packit Service aa8bca
        print {$fh} $_[0]
Packit Service aa8bca
        or _croak(qq/Error: Could not write into temporary file $tempfile: $!\n/);
Packit Service aa8bca
    };
Packit 3f4df8
    my $response = $self->request('GET', $url, $args);
Packit 3f4df8
    close $fh
Packit 3f4df8
        or _croak(qq/Error: Caught error closing temporary file $tempfile: $!\n/);
Packit 3f4df8
Packit 3f4df8
    if ( $response->{success} ) {
Packit 3f4df8
        rename $tempfile, $file
Packit 3f4df8
            or _croak(qq/Error replacing $file with $tempfile: $!\n/);
Packit 3f4df8
        my $lm = $response->{headers}{'last-modified'};
Packit 3f4df8
        if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
Packit 3f4df8
            utime $mtime, $mtime, $file;
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
    $response->{success} ||= $response->{status} eq '304';
Packit 3f4df8
    unlink $tempfile;
Packit 3f4df8
    return $response;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
#pod =method request
Packit 3f4df8
#pod
Packit 3f4df8
#pod     $response = $http->request($method, $url);
Packit 3f4df8
#pod     $response = $http->request($method, $url, \%options);
Packit 3f4df8
#pod
Packit 3f4df8
#pod Executes an HTTP request of the given method type ('GET', 'HEAD', 'POST',
Packit 3f4df8
#pod 'PUT', etc.) on the given URL.  The URL must have unsafe characters escaped and
Packit 3f4df8
#pod international domain names encoded.
Packit 3f4df8
#pod
Packit 3f4df8
#pod B<NOTE>: Method names are B<case-sensitive> per the HTTP/1.1 specification.
Packit 3f4df8
#pod Don't use C<get> when you really want C<GET>.  See L<LIMITATIONS> for
Packit 3f4df8
#pod how this applies to redirection.
Packit 3f4df8
#pod
Packit 3f4df8
#pod If the URL includes a "user:password" stanza, they will be used for Basic-style
Packit 3f4df8
#pod authorization headers.  (Authorization headers will not be included in a
Packit 3f4df8
#pod redirected request.) For example:
Packit 3f4df8
#pod
Packit 3f4df8
#pod     $http->request('GET', 'http://Aladdin:open sesame@example.com/');
Packit 3f4df8
#pod
Packit 3f4df8
#pod If the "user:password" stanza contains reserved characters, they must
Packit 3f4df8
#pod be percent-escaped:
Packit 3f4df8
#pod
Packit 3f4df8
#pod     $http->request('GET', 'http://john%40example.com:password@example.com/');
Packit 3f4df8
#pod
Packit 3f4df8
#pod A hashref of options may be appended to modify the request.
Packit 3f4df8
#pod
Packit 3f4df8
#pod Valid options are:
Packit 3f4df8
#pod
Packit 3f4df8
#pod =for :list
Packit 3f4df8
#pod * C<headers> —
Packit 3f4df8
#pod     A hashref containing headers to include with the request.  If the value for
Packit 3f4df8
#pod     a header is an array reference, the header will be output multiple times with
Packit 3f4df8
#pod     each value in the array.  These headers over-write any default headers.
Packit 3f4df8
#pod * C<content> —
Packit 3f4df8
#pod     A scalar to include as the body of the request OR a code reference
Packit 3f4df8
#pod     that will be called iteratively to produce the body of the request
Packit 3f4df8
#pod * C<trailer_callback> —
Packit 3f4df8
#pod     A code reference that will be called if it exists to provide a hashref
Packit 3f4df8
#pod     of trailing headers (only used with chunked transfer-encoding)
Packit 3f4df8
#pod * C<data_callback> —
Packit 3f4df8
#pod     A code reference that will be called for each chunks of the response
Packit 3f4df8
#pod     body received.
Packit 3f4df8
#pod * C<peer> —
Packit 3f4df8
#pod     Override host resolution and force all connections to go only to a
Packit 3f4df8
#pod     specific peer address, regardless of the URL of the request.  This will
Packit 3f4df8
#pod     include any redirections!  This options should be used with extreme
Packit 3f4df8
#pod     caution (e.g. debugging or very special circumstances).
Packit 3f4df8
#pod
Packit 3f4df8
#pod The C<Host> header is generated from the URL in accordance with RFC 2616.  It
Packit 3f4df8
#pod is a fatal error to specify C<Host> in the C<headers> option.  Other headers
Packit 3f4df8
#pod may be ignored or overwritten if necessary for transport compliance.
Packit 3f4df8
#pod
Packit 3f4df8
#pod If the C<content> option is a code reference, it will be called iteratively
Packit 3f4df8
#pod to provide the content body of the request.  It should return the empty
Packit 3f4df8
#pod string or undef when the iterator is exhausted.
Packit 3f4df8
#pod
Packit 3f4df8
#pod If the C<content> option is the empty string, no C<content-type> or
Packit 3f4df8
#pod C<content-length> headers will be generated.
Packit 3f4df8
#pod
Packit 3f4df8
#pod If the C<data_callback> option is provided, it will be called iteratively until
Packit 3f4df8
#pod the entire response body is received.  The first argument will be a string
Packit 3f4df8
#pod containing a chunk of the response body, the second argument will be the
Packit 3f4df8
#pod in-progress response hash reference, as described below.  (This allows
Packit 3f4df8
#pod customizing the action of the callback based on the C<status> or C<headers>
Packit 3f4df8
#pod received prior to the content body.)
Packit 3f4df8
#pod
Packit 3f4df8
#pod The C<request> method returns a hashref containing the response.  The hashref
Packit 3f4df8
#pod will have the following keys:
Packit 3f4df8
#pod
Packit 3f4df8
#pod =for :list
Packit 3f4df8
#pod * C<success> —
Packit 3f4df8
#pod     Boolean indicating whether the operation returned a 2XX status code
Packit 3f4df8
#pod * C<url> —
Packit 3f4df8
#pod     URL that provided the response. This is the URL of the request unless
Packit 3f4df8
#pod     there were redirections, in which case it is the last URL queried
Packit 3f4df8
#pod     in a redirection chain
Packit 3f4df8
#pod * C<status> —
Packit 3f4df8
#pod     The HTTP status code of the response
Packit 3f4df8
#pod * C<reason> —
Packit 3f4df8
#pod     The response phrase returned by the server
Packit 3f4df8
#pod * C<content> —
Packit 3f4df8
#pod     The body of the response.  If the response does not have any content
Packit 3f4df8
#pod     or if a data callback is provided to consume the response body,
Packit 3f4df8
#pod     this will be the empty string
Packit 3f4df8
#pod * C<headers> —
Packit 3f4df8
#pod     A hashref of header fields.  All header field names will be normalized
Packit 3f4df8
#pod     to be lower case. If a header is repeated, the value will be an arrayref;
Packit 3f4df8
#pod     it will otherwise be a scalar string containing the value
Packit 3f4df8
#pod * C<protocol> -
Packit 3f4df8
#pod     If this field exists, it is the protocol of the response
Packit 3f4df8
#pod     such as HTTP/1.0 or HTTP/1.1
Packit 3f4df8
#pod * C<redirects>
Packit 3f4df8
#pod     If this field exists, it is an arrayref of response hash references from
Packit 3f4df8
#pod     redirects in the same order that redirections occurred.  If it does
Packit 3f4df8
#pod     not exist, then no redirections occurred.
Packit 3f4df8
#pod
Packit 3f4df8
#pod On an exception during the execution of the request, the C<status> field will
Packit 3f4df8
#pod contain 599, and the C<content> field will contain the text of the exception.
Packit 3f4df8
#pod
Packit 3f4df8
#pod =cut
Packit 3f4df8
Packit 3f4df8
my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/;
Packit 3f4df8
Packit 3f4df8
sub request {
Packit 3f4df8
    my ($self, $method, $url, $args) = @_;
Packit 3f4df8
    @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
Packit 3f4df8
      or _croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/ . "\n");
Packit 3f4df8
    $args ||= {}; # we keep some state in this during _request
Packit 3f4df8
Packit 3f4df8
    # RFC 2616 Section 8.1.4 mandates a single retry on broken socket
Packit 3f4df8
    my $response;
Packit 3f4df8
    for ( 0 .. 1 ) {
Packit 3f4df8
        $response = eval { $self->_request($method, $url, $args) };
Packit 3f4df8
        last unless $@ && $idempotent{$method}
Packit 3f4df8
            && $@ =~ m{^(?:Socket closed|Unexpected end)};
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    if (my $e = $@) {
Packit 3f4df8
        # maybe we got a response hash thrown from somewhere deep
Packit 3f4df8
        if ( ref $e eq 'HASH' && exists $e->{status} ) {
Packit 3f4df8
            $e->{redirects} = delete $args->{_redirects} if @{ $args->{_redirects} || []};
Packit 3f4df8
            return $e;
Packit 3f4df8
        }
Packit 3f4df8
Packit 3f4df8
        # otherwise, stringify it
Packit 3f4df8
        $e = "$e";
Packit 3f4df8
        $response = {
Packit 3f4df8
            url     => $url,
Packit 3f4df8
            success => q{},
Packit 3f4df8
            status  => 599,
Packit 3f4df8
            reason  => 'Internal Exception',
Packit 3f4df8
            content => $e,
Packit 3f4df8
            headers => {
Packit 3f4df8
                'content-type'   => 'text/plain',
Packit 3f4df8
                'content-length' => length $e,
Packit 3f4df8
            },
Packit 3f4df8
            ( @{$args->{_redirects} || []} ? (redirects => delete $args->{_redirects}) : () ),
Packit 3f4df8
        };
Packit 3f4df8
    }
Packit 3f4df8
    return $response;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
#pod =method www_form_urlencode
Packit 3f4df8
#pod
Packit 3f4df8
#pod     $params = $http->www_form_urlencode( $data );
Packit 3f4df8
#pod     $response = $http->get("http://example.com/query?$params");
Packit 3f4df8
#pod
Packit 3f4df8
#pod This method converts the key/value pairs from a data hash or array reference
Packit 3f4df8
#pod into a C<x-www-form-urlencoded> string.  The keys and values from the data
Packit 3f4df8
#pod reference will be UTF-8 encoded and escaped per RFC 3986.  If a value is an
Packit 3f4df8
#pod array reference, the key will be repeated with each of the values of the array
Packit 3f4df8
#pod reference.  If data is provided as a hash reference, the key/value pairs in the
Packit 3f4df8
#pod resulting string will be sorted by key and value for consistent ordering.
Packit 3f4df8
#pod
Packit 3f4df8
#pod =cut
Packit 3f4df8
Packit 3f4df8
sub www_form_urlencode {
Packit 3f4df8
    my ($self, $data) = @_;
Packit 3f4df8
    (@_ == 2 && ref $data)
Packit 3f4df8
        or _croak(q/Usage: $http->www_form_urlencode(DATAREF)/ . "\n");
Packit 3f4df8
    (ref $data eq 'HASH' || ref $data eq 'ARRAY')
Packit 3f4df8
        or _croak("form data must be a hash or array reference\n");
Packit 3f4df8
Packit 3f4df8
    my @params = ref $data eq 'HASH' ? %$data : @$data;
Packit 3f4df8
    @params % 2 == 0
Packit 3f4df8
        or _croak("form data reference must have an even number of terms\n");
Packit 3f4df8
Packit 3f4df8
    my @terms;
Packit 3f4df8
    while( @params ) {
Packit 3f4df8
        my ($key, $value) = splice(@params, 0, 2);
Packit 3f4df8
        if ( ref $value eq 'ARRAY' ) {
Packit 3f4df8
            unshift @params, map { $key => $_ } @$value;
Packit 3f4df8
        }
Packit 3f4df8
        else {
Packit 3f4df8
            push @terms, join("=", map { $self->_uri_escape($_) } $key, $value);
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    return join("&", (ref $data eq 'ARRAY') ? (@terms) : (sort @terms) );
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
#pod =method can_ssl
Packit 3f4df8
#pod
Packit 3f4df8
#pod     $ok         = HTTP::Tiny->can_ssl;
Packit 3f4df8
#pod     ($ok, $why) = HTTP::Tiny->can_ssl;
Packit 3f4df8
#pod     ($ok, $why) = $http->can_ssl;
Packit 3f4df8
#pod
Packit 3f4df8
#pod Indicates if SSL support is available.  When called as a class object, it
Packit 3f4df8
#pod checks for the correct version of L<Net::SSLeay> and L<IO::Socket::SSL>.
Packit 3f4df8
#pod When called as an object methods, if C<SSL_verify> is true or if C<SSL_verify_mode>
Packit 3f4df8
#pod is set in C<SSL_options>, it checks that a CA file is available.
Packit 3f4df8
#pod
Packit 3f4df8
#pod In scalar context, returns a boolean indicating if SSL is available.
Packit 3f4df8
#pod In list context, returns the boolean and a (possibly multi-line) string of
Packit 3f4df8
#pod errors indicating why SSL isn't available.
Packit 3f4df8
#pod
Packit 3f4df8
#pod =cut
Packit 3f4df8
Packit 3f4df8
sub can_ssl {
Packit 3f4df8
    my ($self) = @_;
Packit 3f4df8
Packit 3f4df8
    my($ok, $reason) = (1, '');
Packit 3f4df8
Packit 3f4df8
    # Need IO::Socket::SSL 1.42 for SSL_create_ctx_callback
Packit 3f4df8
    local @INC = @INC;
Packit 3f4df8
    pop @INC if $INC[-1] eq '.';
Packit 3f4df8
    unless (eval {require IO::Socket::SSL; IO::Socket::SSL->VERSION(1.42)}) {
Packit 3f4df8
        $ok = 0;
Packit 3f4df8
        $reason .= qq/IO::Socket::SSL 1.42 must be installed for https support\n/;
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    # Need Net::SSLeay 1.49 for MODE_AUTO_RETRY
Packit 3f4df8
    unless (eval {require Net::SSLeay; Net::SSLeay->VERSION(1.49)}) {
Packit 3f4df8
        $ok = 0;
Packit 3f4df8
        $reason .= qq/Net::SSLeay 1.49 must be installed for https support\n/;
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    # If an object, check that SSL config lets us get a CA if necessary
Packit 3f4df8
    if ( ref($self) && ( $self->{verify_SSL} || $self->{SSL_options}{SSL_verify_mode} ) ) {
Packit 3f4df8
        my $handle = HTTP::Tiny::Handle->new(
Packit 3f4df8
            SSL_options => $self->{SSL_options},
Packit 3f4df8
            verify_SSL  => $self->{verify_SSL},
Packit 3f4df8
        );
Packit 3f4df8
        unless ( eval { $handle->_find_CA_file; 1 } ) {
Packit 3f4df8
            $ok = 0;
Packit 3f4df8
            $reason .= "$@";
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    wantarray ? ($ok, $reason) : $ok;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
#pod =method connected
Packit 3f4df8
#pod
Packit 3f4df8
#pod     $host = $http->connected;
Packit 3f4df8
#pod     ($host, $port) = $http->connected;
Packit 3f4df8
#pod
Packit 3f4df8
#pod Indicates if a connection to a peer is being kept alive, per the C<keep_alive>
Packit 3f4df8
#pod option.
Packit 3f4df8
#pod
Packit 3f4df8
#pod In scalar context, returns the peer host and port, joined with a colon, or
Packit 3f4df8
#pod C<undef> (if no peer is connected).
Packit 3f4df8
#pod In list context, returns the peer host and port or an empty list (if no peer
Packit 3f4df8
#pod is connected).
Packit 3f4df8
#pod
Packit 3f4df8
#pod B<Note>: This method cannot reliably be used to discover whether the remote
Packit 3f4df8
#pod host has closed its end of the socket.
Packit 3f4df8
#pod
Packit 3f4df8
#pod =cut
Packit 3f4df8
Packit 3f4df8
sub connected {
Packit 3f4df8
    my ($self) = @_;
Packit 3f4df8
Packit 3f4df8
    # If a socket exists...
Packit 3f4df8
    if ($self->{handle} && $self->{handle}{fh}) {
Packit 3f4df8
        my $socket = $self->{handle}{fh};
Packit 3f4df8
Packit 3f4df8
        # ...and is connected, return the peer host and port.
Packit 3f4df8
        if ($socket->connected) {
Packit 3f4df8
            return wantarray
Packit 3f4df8
                ? ($socket->peerhost, $socket->peerport)
Packit 3f4df8
                : join(':', $socket->peerhost, $socket->peerport);
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
    return;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
#--------------------------------------------------------------------------#
Packit 3f4df8
# private methods
Packit 3f4df8
#--------------------------------------------------------------------------#
Packit 3f4df8
Packit 3f4df8
my %DefaultPort = (
Packit 3f4df8
    http => 80,
Packit 3f4df8
    https => 443,
Packit 3f4df8
);
Packit 3f4df8
Packit 3f4df8
sub _agent {
Packit 3f4df8
    my $class = ref($_[0]) || $_[0];
Packit 3f4df8
    (my $default_agent = $class) =~ s{::}{-}g;
Packit 3f4df8
    return $default_agent . "/" . $class->VERSION;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _request {
Packit 3f4df8
    my ($self, $method, $url, $args) = @_;
Packit 3f4df8
Packit 3f4df8
    my ($scheme, $host, $port, $path_query, $auth) = $self->_split_url($url);
Packit 3f4df8
Packit 3f4df8
    my $request = {
Packit 3f4df8
        method    => $method,
Packit 3f4df8
        scheme    => $scheme,
Packit 3f4df8
        host      => $host,
Packit 3f4df8
        port      => $port,
Packit 3f4df8
        host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
Packit 3f4df8
        uri       => $path_query,
Packit 3f4df8
        headers   => {},
Packit 3f4df8
    };
Packit 3f4df8
Packit 3f4df8
    my $peer = $args->{peer} || $host;
Packit 3f4df8
Packit 3f4df8
    # We remove the cached handle so it is not reused in the case of redirect.
Packit 3f4df8
    # If all is well, it will be recached at the end of _request.  We only
Packit 3f4df8
    # reuse for the same scheme, host and port
Packit 3f4df8
    my $handle = delete $self->{handle};
Packit 3f4df8
    if ( $handle ) {
Packit 3f4df8
        unless ( $handle->can_reuse( $scheme, $host, $port, $peer ) ) {
Packit 3f4df8
            $handle->close;
Packit 3f4df8
            undef $handle;
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
    $handle ||= $self->_open_handle( $request, $scheme, $host, $port, $peer );
Packit 3f4df8
Packit 3f4df8
    $self->_prepare_headers_and_cb($request, $args, $url, $auth);
Packit 3f4df8
    $handle->write_request($request);
Packit 3f4df8
Packit 3f4df8
    my $response;
Packit 3f4df8
    do { $response = $handle->read_response_header }
Packit 3f4df8
        until (substr($response->{status},0,1) ne '1');
Packit 3f4df8
Packit 3f4df8
    $self->_update_cookie_jar( $url, $response ) if $self->{cookie_jar};
Packit 3f4df8
    my @redir_args = $self->_maybe_redirect($request, $response, $args);
Packit 3f4df8
Packit 3f4df8
    my $known_message_length;
Packit 3f4df8
    if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) {
Packit 3f4df8
        # response has no message body
Packit 3f4df8
        $known_message_length = 1;
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        # Ignore any data callbacks during redirection.
Packit 3f4df8
        my $cb_args = @redir_args ? +{} : $args;
Packit 3f4df8
        my $data_cb = $self->_prepare_data_cb($response, $cb_args);
Packit 3f4df8
        $known_message_length = $handle->read_body($data_cb, $response);
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    if ( $self->{keep_alive}
Packit 3f4df8
        && $known_message_length
Packit 3f4df8
        && $response->{protocol} eq 'HTTP/1.1'
Packit 3f4df8
        && ($response->{headers}{connection} || '') ne 'close'
Packit 3f4df8
    ) {
Packit 3f4df8
        $self->{handle} = $handle;
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        $handle->close;
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    $response->{success} = substr( $response->{status}, 0, 1 ) eq '2';
Packit 3f4df8
    $response->{url} = $url;
Packit 3f4df8
Packit 3f4df8
    # Push the current response onto the stack of redirects if redirecting.
Packit 3f4df8
    if (@redir_args) {
Packit 3f4df8
        push @{$args->{_redirects}}, $response;
Packit 3f4df8
        return $self->_request(@redir_args, $args);
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    # Copy the stack of redirects into the response before returning.
Packit 3f4df8
    $response->{redirects} = delete $args->{_redirects}
Packit 3f4df8
      if @{$args->{_redirects}};
Packit 3f4df8
    return $response;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _open_handle {
Packit 3f4df8
    my ($self, $request, $scheme, $host, $port, $peer) = @_;
Packit 3f4df8
Packit 3f4df8
    my $handle  = HTTP::Tiny::Handle->new(
Packit 3f4df8
        timeout         => $self->{timeout},
Packit 3f4df8
        SSL_options     => $self->{SSL_options},
Packit 3f4df8
        verify_SSL      => $self->{verify_SSL},
Packit 3f4df8
        local_address   => $self->{local_address},
Packit 3f4df8
        keep_alive      => $self->{keep_alive}
Packit 3f4df8
    );
Packit 3f4df8
Packit 3f4df8
    if ($self->{_has_proxy}{$scheme} && ! grep { $host =~ /\Q$_\E$/ } @{$self->{no_proxy}}) {
Packit 3f4df8
        return $self->_proxy_connect( $request, $handle );
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        return $handle->connect($scheme, $host, $port, $peer);
Packit 3f4df8
    }
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _proxy_connect {
Packit 3f4df8
    my ($self, $request, $handle) = @_;
Packit 3f4df8
Packit 3f4df8
    my @proxy_vars;
Packit 3f4df8
    if ( $request->{scheme} eq 'https' ) {
Packit 3f4df8
        _croak(qq{No https_proxy defined}) unless $self->{https_proxy};
Packit 3f4df8
        @proxy_vars = $self->_split_proxy( https_proxy => $self->{https_proxy} );
Packit 3f4df8
        if ( $proxy_vars[0] eq 'https' ) {
Packit 3f4df8
            _croak(qq{Can't proxy https over https: $request->{uri} via $self->{https_proxy}});
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        _croak(qq{No http_proxy defined}) unless $self->{http_proxy};
Packit 3f4df8
        @proxy_vars = $self->_split_proxy( http_proxy => $self->{http_proxy} );
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    my ($p_scheme, $p_host, $p_port, $p_auth) = @proxy_vars;
Packit 3f4df8
Packit 3f4df8
    if ( length $p_auth && ! defined $request->{headers}{'proxy-authorization'} ) {
Packit 3f4df8
        $self->_add_basic_auth_header( $request, 'proxy-authorization' => $p_auth );
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    $handle->connect($p_scheme, $p_host, $p_port, $p_host);
Packit 3f4df8
Packit 3f4df8
    if ($request->{scheme} eq 'https') {
Packit 3f4df8
        $self->_create_proxy_tunnel( $request, $handle );
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        # non-tunneled proxy requires absolute URI
Packit 3f4df8
        $request->{uri} = "$request->{scheme}://$request->{host_port}$request->{uri}";
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    return $handle;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _split_proxy {
Packit 3f4df8
    my ($self, $type, $proxy) = @_;
Packit 3f4df8
Packit 3f4df8
    my ($scheme, $host, $port, $path_query, $auth) = eval { $self->_split_url($proxy) };
Packit 3f4df8
Packit 3f4df8
    unless(
Packit 3f4df8
        defined($scheme) && length($scheme) && length($host) && length($port)
Packit 3f4df8
        && $path_query eq '/'
Packit 3f4df8
    ) {
Packit 3f4df8
        _croak(qq{$type URL must be in format http[s]://[auth@]<host>:<port>/\n});
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    return ($scheme, $host, $port, $auth);
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _create_proxy_tunnel {
Packit 3f4df8
    my ($self, $request, $handle) = @_;
Packit 3f4df8
Packit 3f4df8
    $handle->_assert_ssl;
Packit 3f4df8
Packit 3f4df8
    my $agent = exists($request->{headers}{'user-agent'})
Packit 3f4df8
        ? $request->{headers}{'user-agent'} : $self->{agent};
Packit 3f4df8
Packit 3f4df8
    my $connect_request = {
Packit 3f4df8
        method    => 'CONNECT',
Packit 3f4df8
        uri       => "$request->{host}:$request->{port}",
Packit 3f4df8
        headers   => {
Packit 3f4df8
            host => "$request->{host}:$request->{port}",
Packit 3f4df8
            'user-agent' => $agent,
Packit 3f4df8
        }
Packit 3f4df8
    };
Packit 3f4df8
Packit 3f4df8
    if ( $request->{headers}{'proxy-authorization'} ) {
Packit 3f4df8
        $connect_request->{headers}{'proxy-authorization'} =
Packit 3f4df8
            delete $request->{headers}{'proxy-authorization'};
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    $handle->write_request($connect_request);
Packit 3f4df8
    my $response;
Packit 3f4df8
    do { $response = $handle->read_response_header }
Packit 3f4df8
        until (substr($response->{status},0,1) ne '1');
Packit 3f4df8
Packit 3f4df8
    # if CONNECT failed, throw the response so it will be
Packit 3f4df8
    # returned from the original request() method;
Packit 3f4df8
    unless (substr($response->{status},0,1) eq '2') {
Packit 3f4df8
        die $response;
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    # tunnel established, so start SSL handshake
Packit 3f4df8
    $handle->start_ssl( $request->{host} );
Packit 3f4df8
Packit 3f4df8
    return;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _prepare_headers_and_cb {
Packit 3f4df8
    my ($self, $request, $args, $url, $auth) = @_;
Packit 3f4df8
Packit 3f4df8
    for ($self->{default_headers}, $args->{headers}) {
Packit 3f4df8
        next unless defined;
Packit 3f4df8
        while (my ($k, $v) = each %$_) {
Packit 3f4df8
            $request->{headers}{lc $k} = $v;
Packit 3f4df8
            $request->{header_case}{lc $k} = $k;
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    if (exists $request->{headers}{'host'}) {
Packit 3f4df8
        die(qq/The 'Host' header must not be provided as header option\n/);
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    $request->{headers}{'host'}         = $request->{host_port};
Packit 3f4df8
    $request->{headers}{'user-agent'} ||= $self->{agent};
Packit 3f4df8
    $request->{headers}{'connection'}   = "close"
Packit 3f4df8
        unless $self->{keep_alive};
Packit 3f4df8
Packit 3f4df8
    if ( defined $args->{content} ) {
Packit 3f4df8
        if (ref $args->{content} eq 'CODE') {
Packit 3f4df8
            $request->{headers}{'content-type'} ||= "application/octet-stream";
Packit 3f4df8
            $request->{headers}{'transfer-encoding'} = 'chunked'
Packit 3f4df8
              unless $request->{headers}{'content-length'}
Packit 3f4df8
                  || $request->{headers}{'transfer-encoding'};
Packit 3f4df8
            $request->{cb} = $args->{content};
Packit 3f4df8
        }
Packit 3f4df8
        elsif ( length $args->{content} ) {
Packit 3f4df8
            my $content = $args->{content};
Packit 3f4df8
            if ( $] ge '5.008' ) {
Packit 3f4df8
                utf8::downgrade($content, 1)
Packit 3f4df8
                    or die(qq/Wide character in request message body\n/);
Packit 3f4df8
            }
Packit 3f4df8
            $request->{headers}{'content-type'} ||= "application/octet-stream";
Packit 3f4df8
            $request->{headers}{'content-length'} = length $content
Packit 3f4df8
              unless $request->{headers}{'content-length'}
Packit 3f4df8
                  || $request->{headers}{'transfer-encoding'};
Packit 3f4df8
            $request->{cb} = sub { substr $content, 0, length $content, '' };
Packit 3f4df8
        }
Packit 3f4df8
        $request->{trailer_cb} = $args->{trailer_callback}
Packit 3f4df8
            if ref $args->{trailer_callback} eq 'CODE';
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    ### If we have a cookie jar, then maybe add relevant cookies
Packit 3f4df8
    if ( $self->{cookie_jar} ) {
Packit 3f4df8
        my $cookies = $self->cookie_jar->cookie_header( $url );
Packit 3f4df8
        $request->{headers}{cookie} = $cookies if length $cookies;
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    # if we have Basic auth parameters, add them
Packit 3f4df8
    if ( length $auth && ! defined $request->{headers}{authorization} ) {
Packit 3f4df8
        $self->_add_basic_auth_header( $request, 'authorization' => $auth );
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    return;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _add_basic_auth_header {
Packit 3f4df8
    my ($self, $request, $header, $auth) = @_;
Packit 3f4df8
    require MIME::Base64;
Packit 3f4df8
    $request->{headers}{$header} =
Packit 3f4df8
        "Basic " . MIME::Base64::encode_base64($auth, "");
Packit 3f4df8
    return;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _prepare_data_cb {
Packit 3f4df8
    my ($self, $response, $args) = @_;
Packit 3f4df8
    my $data_cb = $args->{data_callback};
Packit 3f4df8
    $response->{content} = '';
Packit 3f4df8
Packit 3f4df8
    if (!$data_cb || $response->{status} !~ /^2/) {
Packit 3f4df8
        if (defined $self->{max_size}) {
Packit 3f4df8
            $data_cb = sub {
Packit 3f4df8
                $_[1]->{content} .= $_[0];
Packit 3f4df8
                die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/)
Packit 3f4df8
                  if length $_[1]->{content} > $self->{max_size};
Packit 3f4df8
            };
Packit 3f4df8
        }
Packit 3f4df8
        else {
Packit 3f4df8
            $data_cb = sub { $_[1]->{content} .= $_[0] };
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
    return $data_cb;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _update_cookie_jar {
Packit 3f4df8
    my ($self, $url, $response) = @_;
Packit 3f4df8
Packit 3f4df8
    my $cookies = $response->{headers}->{'set-cookie'};
Packit 3f4df8
    return unless defined $cookies;
Packit 3f4df8
Packit 3f4df8
    my @cookies = ref $cookies ? @$cookies : $cookies;
Packit 3f4df8
Packit 3f4df8
    $self->cookie_jar->add( $url, $_ ) for @cookies;
Packit 3f4df8
Packit 3f4df8
    return;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _validate_cookie_jar {
Packit 3f4df8
    my ($class, $jar) = @_;
Packit 3f4df8
Packit 3f4df8
    # duck typing
Packit 3f4df8
    for my $method ( qw/add cookie_header/ ) {
Packit 3f4df8
        _croak(qq/Cookie jar must provide the '$method' method\n/)
Packit 3f4df8
            unless ref($jar) && ref($jar)->can($method);
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    return;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _maybe_redirect {
Packit 3f4df8
    my ($self, $request, $response, $args) = @_;
Packit 3f4df8
    my $headers = $response->{headers};
Packit 3f4df8
    my ($status, $method) = ($response->{status}, $request->{method});
Packit 3f4df8
    $args->{_redirects} ||= [];
Packit 3f4df8
Packit 3f4df8
    if (($status eq '303' or ($status =~ /^30[1278]/ && $method =~ /^GET|HEAD$/))
Packit 3f4df8
        and $headers->{location}
Packit 3f4df8
        and @{$args->{_redirects}} < $self->{max_redirect}
Packit 3f4df8
    ) {
Packit 3f4df8
        my $location = ($headers->{location} =~ /^\//)
Packit 3f4df8
            ? "$request->{scheme}://$request->{host_port}$headers->{location}"
Packit 3f4df8
            : $headers->{location} ;
Packit 3f4df8
        return (($status eq '303' ? 'GET' : $method), $location);
Packit 3f4df8
    }
Packit 3f4df8
    return;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _split_url {
Packit 3f4df8
    my $url = pop;
Packit 3f4df8
Packit 3f4df8
    # URI regex adapted from the URI module
Packit 3f4df8
    my ($scheme, $host, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)>
Packit 3f4df8
      or die(qq/Cannot parse URL: '$url'\n/);
Packit 3f4df8
Packit 3f4df8
    $scheme     = lc $scheme;
Packit 3f4df8
    $path_query = "/$path_query" unless $path_query =~ m<\A/>;
Packit 3f4df8
Packit 3f4df8
    my $auth = '';
Packit 3f4df8
    if ( (my $i = index $host, '@') != -1 ) {
Packit 3f4df8
        # user:pass@host
Packit 3f4df8
        $auth = substr $host, 0, $i, ''; # take up to the @ for auth
Packit 3f4df8
        substr $host, 0, 1, '';          # knock the @ off the host
Packit 3f4df8
Packit 3f4df8
        # userinfo might be percent escaped, so recover real auth info
Packit 3f4df8
        $auth =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
Packit 3f4df8
    }
Packit 3f4df8
    my $port = $host =~ s/:(\d*)\z// && length $1 ? $1
Packit 3f4df8
             : $scheme eq 'http'                  ? 80
Packit 3f4df8
             : $scheme eq 'https'                 ? 443
Packit 3f4df8
             : undef;
Packit 3f4df8
Packit 3f4df8
    return ($scheme, (length $host ? lc $host : "localhost") , $port, $path_query, $auth);
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
# Date conversions adapted from HTTP::Date
Packit 3f4df8
my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat";
Packit 3f4df8
my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
Packit 3f4df8
sub _http_date {
Packit 3f4df8
    my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]);
Packit 3f4df8
    return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
Packit 3f4df8
        substr($DoW,$wday*4,3),
Packit 3f4df8
        $mday, substr($MoY,$mon*4,3), $year+1900,
Packit 3f4df8
        $hour, $min, $sec
Packit 3f4df8
    );
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _parse_http_date {
Packit 3f4df8
    my ($self, $str) = @_;
Packit 3f4df8
    require Time::Local;
Packit 3f4df8
    my @tl_parts;
Packit 3f4df8
    if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) {
Packit 3f4df8
        @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
Packit 3f4df8
    }
Packit 3f4df8
    elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) {
Packit 3f4df8
        @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
Packit 3f4df8
    }
Packit 3f4df8
    elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) {
Packit 3f4df8
        @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6);
Packit 3f4df8
    }
Packit 3f4df8
    return eval {
Packit 3f4df8
        my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1;
Packit 3f4df8
        $t < 0 ? undef : $t;
Packit 3f4df8
    };
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
# URI escaping adapted from URI::Escape
Packit 3f4df8
# c.f. http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
Packit 3f4df8
# perl 5.6 ready UTF-8 encoding adapted from JSON::PP
Packit 3f4df8
my %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255;
Packit 3f4df8
$escapes{' '}="+";
Packit 3f4df8
my $unsafe_char = qr/[^A-Za-z0-9\-\._~]/;
Packit 3f4df8
Packit 3f4df8
sub _uri_escape {
Packit 3f4df8
    my ($self, $str) = @_;
Packit 3f4df8
    if ( $] ge '5.008' ) {
Packit 3f4df8
        utf8::encode($str);
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        $str = pack("U*", unpack("C*", $str)) # UTF-8 encode a byte string
Packit 3f4df8
            if ( length $str == do { use bytes; length $str } );
Packit 3f4df8
        $str = pack("C*", unpack("C*", $str)); # clear UTF-8 flag
Packit 3f4df8
    }
Packit 3f4df8
    $str =~ s/($unsafe_char)/$escapes{$1}/g;
Packit 3f4df8
    return $str;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
package
Packit 3f4df8
    HTTP::Tiny::Handle; # hide from PAUSE/indexers
Packit 3f4df8
use strict;
Packit 3f4df8
use warnings;
Packit 3f4df8
Packit 3f4df8
use Errno      qw[EINTR EPIPE];
Packit 3f4df8
use IO::Socket qw[SOCK_STREAM];
Packit 3f4df8
use Socket     qw[SOL_SOCKET SO_KEEPALIVE];
Packit 3f4df8
Packit 3f4df8
# PERL_HTTP_TINY_IPV4_ONLY is a private environment variable to force old
Packit 3f4df8
# behavior if someone is unable to boostrap CPAN from a new perl install; it is
Packit 3f4df8
# not intended for general, per-client use and may be removed in the future
Packit 3f4df8
my $SOCKET_CLASS =
Packit 3f4df8
    $ENV{PERL_HTTP_TINY_IPV4_ONLY} ? 'IO::Socket::INET' :
Packit 3f4df8
    eval { require IO::Socket::IP; IO::Socket::IP->VERSION(0.25) } ? 'IO::Socket::IP' :
Packit 3f4df8
    'IO::Socket::INET';
Packit 3f4df8
Packit 3f4df8
sub BUFSIZE () { 32768 } ## no critic
Packit 3f4df8
Packit 3f4df8
my $Printable = sub {
Packit 3f4df8
    local $_ = shift;
Packit 3f4df8
    s/\r/\\r/g;
Packit 3f4df8
    s/\n/\\n/g;
Packit 3f4df8
    s/\t/\\t/g;
Packit 3f4df8
    s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
Packit 3f4df8
    $_;
Packit 3f4df8
};
Packit 3f4df8
Packit 3f4df8
my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
Packit 3f4df8
my $Field_Content = qr/[[:print:]]+ (?: [\x20\x09]+ [[:print:]]+ )*/x;
Packit 3f4df8
Packit 3f4df8
sub new {
Packit 3f4df8
    my ($class, %args) = @_;
Packit 3f4df8
    return bless {
Packit 3f4df8
        rbuf             => '',
Packit 3f4df8
        timeout          => 60,
Packit 3f4df8
        max_line_size    => 16384,
Packit 3f4df8
        max_header_lines => 64,
Packit 3f4df8
        verify_SSL       => 0,
Packit 3f4df8
        SSL_options      => {},
Packit 3f4df8
        %args
Packit 3f4df8
    }, $class;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub timeout {
Packit 3f4df8
    my ($self, $timeout) = @_;
Packit 3f4df8
    if ( @_ > 1 ) {
Packit 3f4df8
        $self->{timeout} = $timeout;
Packit 3f4df8
        if ( $self->{fh} && $self->{fh}->can('timeout') ) {
Packit 3f4df8
            $self->{fh}->timeout($timeout);
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
    return $self->{timeout};
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub connect {
Packit 3f4df8
    @_ == 5 || die(q/Usage: $handle->connect(scheme, host, port, peer)/ . "\n");
Packit 3f4df8
    my ($self, $scheme, $host, $port, $peer) = @_;
Packit 3f4df8
Packit 3f4df8
    if ( $scheme eq 'https' ) {
Packit 3f4df8
        $self->_assert_ssl;
Packit 3f4df8
    }
Packit 3f4df8
    elsif ( $scheme ne 'http' ) {
Packit 3f4df8
      die(qq/Unsupported URL scheme '$scheme'\n/);
Packit 3f4df8
    }
Packit 3f4df8
    $self->{fh} = $SOCKET_CLASS->new(
Packit 3f4df8
        PeerHost  => $peer,
Packit 3f4df8
        PeerPort  => $port,
Packit 3f4df8
        $self->{local_address} ?
Packit 3f4df8
            ( LocalAddr => $self->{local_address} ) : (),
Packit 3f4df8
        Proto     => 'tcp',
Packit 3f4df8
        Type      => SOCK_STREAM,
Packit 3f4df8
        Timeout   => $self->{timeout},
Packit 3f4df8
    ) or die(qq/Could not connect to '$host:$port': $@\n/);
Packit 3f4df8
Packit 3f4df8
    binmode($self->{fh})
Packit 3f4df8
      or die(qq/Could not binmode() socket: '$!'\n/);
Packit 3f4df8
Packit 3f4df8
    if ( $self->{keep_alive} ) {
Packit 3f4df8
        unless ( defined( $self->{fh}->setsockopt( SOL_SOCKET, SO_KEEPALIVE, 1 ) ) ) {
Packit 3f4df8
            CORE::close($self->{fh});
Packit 3f4df8
            die(qq/Could not set SO_KEEPALIVE on socket: '$!'\n/);
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    $self->start_ssl($host) if $scheme eq 'https';
Packit 3f4df8
Packit 3f4df8
    $self->{scheme} = $scheme;
Packit 3f4df8
    $self->{host} = $host;
Packit 3f4df8
    $self->{peer} = $peer;
Packit 3f4df8
    $self->{port} = $port;
Packit 3f4df8
    $self->{pid} = $$;
Packit 3f4df8
    $self->{tid} = _get_tid();
Packit 3f4df8
Packit 3f4df8
    return $self;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub start_ssl {
Packit 3f4df8
    my ($self, $host) = @_;
Packit 3f4df8
Packit 3f4df8
    # As this might be used via CONNECT after an SSL session
Packit 3f4df8
    # to a proxy, we shut down any existing SSL before attempting
Packit 3f4df8
    # the handshake
Packit 3f4df8
    if ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
Packit 3f4df8
        unless ( $self->{fh}->stop_SSL ) {
Packit 3f4df8
            my $ssl_err = IO::Socket::SSL->errstr;
Packit 3f4df8
            die(qq/Error halting prior SSL connection: $ssl_err/);
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    my $ssl_args = $self->_ssl_args($host);
Packit 3f4df8
    IO::Socket::SSL->start_SSL(
Packit 3f4df8
        $self->{fh},
Packit 3f4df8
        %$ssl_args,
Packit 3f4df8
        SSL_create_ctx_callback => sub {
Packit 3f4df8
            my $ctx = shift;
Packit 3f4df8
            Net::SSLeay::CTX_set_mode($ctx, Net::SSLeay::MODE_AUTO_RETRY());
Packit 3f4df8
        },
Packit 3f4df8
    );
Packit 3f4df8
Packit 3f4df8
    unless ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
Packit 3f4df8
        my $ssl_err = IO::Socket::SSL->errstr;
Packit 3f4df8
        die(qq/SSL connection failed for $host: $ssl_err\n/);
Packit 3f4df8
    }
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub close {
Packit 3f4df8
    @_ == 1 || die(q/Usage: $handle->close()/ . "\n");
Packit 3f4df8
    my ($self) = @_;
Packit 3f4df8
    CORE::close($self->{fh})
Packit 3f4df8
      or die(qq/Could not close socket: '$!'\n/);
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub write {
Packit 3f4df8
    @_ == 2 || die(q/Usage: $handle->write(buf)/ . "\n");
Packit 3f4df8
    my ($self, $buf) = @_;
Packit 3f4df8
Packit 3f4df8
    if ( $] ge '5.008' ) {
Packit 3f4df8
        utf8::downgrade($buf, 1)
Packit 3f4df8
            or die(qq/Wide character in write()\n/);
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    my $len = length $buf;
Packit 3f4df8
    my $off = 0;
Packit 3f4df8
Packit 3f4df8
    local $SIG{PIPE} = 'IGNORE';
Packit 3f4df8
Packit 3f4df8
    while () {
Packit 3f4df8
        $self->can_write
Packit 3f4df8
          or die(qq/Timed out while waiting for socket to become ready for writing\n/);
Packit 3f4df8
        my $r = syswrite($self->{fh}, $buf, $len, $off);
Packit 3f4df8
        if (defined $r) {
Packit 3f4df8
            $len -= $r;
Packit 3f4df8
            $off += $r;
Packit 3f4df8
            last unless $len > 0;
Packit 3f4df8
        }
Packit 3f4df8
        elsif ($! == EPIPE) {
Packit 3f4df8
            die(qq/Socket closed by remote server: $!\n/);
Packit 3f4df8
        }
Packit 3f4df8
        elsif ($! != EINTR) {
Packit 3f4df8
            if ($self->{fh}->can('errstr')){
Packit 3f4df8
                my $err = $self->{fh}->errstr();
Packit 3f4df8
                die (qq/Could not write to SSL socket: '$err'\n /);
Packit 3f4df8
            }
Packit 3f4df8
            else {
Packit 3f4df8
                die(qq/Could not write to socket: '$!'\n/);
Packit 3f4df8
            }
Packit 3f4df8
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
    return $off;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub read {
Packit 3f4df8
    @_ == 2 || @_ == 3 || die(q/Usage: $handle->read(len [, allow_partial])/ . "\n");
Packit 3f4df8
    my ($self, $len, $allow_partial) = @_;
Packit 3f4df8
Packit 3f4df8
    my $buf  = '';
Packit 3f4df8
    my $got = length $self->{rbuf};
Packit 3f4df8
Packit 3f4df8
    if ($got) {
Packit 3f4df8
        my $take = ($got < $len) ? $got : $len;
Packit 3f4df8
        $buf  = substr($self->{rbuf}, 0, $take, '');
Packit 3f4df8
        $len -= $take;
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    while ($len > 0) {
Packit 3f4df8
        $self->can_read
Packit 3f4df8
          or die(q/Timed out while waiting for socket to become ready for reading/ . "\n");
Packit 3f4df8
        my $r = sysread($self->{fh}, $buf, $len, length $buf);
Packit 3f4df8
        if (defined $r) {
Packit 3f4df8
            last unless $r;
Packit 3f4df8
            $len -= $r;
Packit 3f4df8
        }
Packit 3f4df8
        elsif ($! != EINTR) {
Packit 3f4df8
            if ($self->{fh}->can('errstr')){
Packit 3f4df8
                my $err = $self->{fh}->errstr();
Packit 3f4df8
                die (qq/Could not read from SSL socket: '$err'\n /);
Packit 3f4df8
            }
Packit 3f4df8
            else {
Packit 3f4df8
                die(qq/Could not read from socket: '$!'\n/);
Packit 3f4df8
            }
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
    if ($len && !$allow_partial) {
Packit 3f4df8
        die(qq/Unexpected end of stream\n/);
Packit 3f4df8
    }
Packit 3f4df8
    return $buf;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub readline {
Packit 3f4df8
    @_ == 1 || die(q/Usage: $handle->readline()/ . "\n");
Packit 3f4df8
    my ($self) = @_;
Packit 3f4df8
Packit 3f4df8
    while () {
Packit 3f4df8
        if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
Packit 3f4df8
            return $1;
Packit 3f4df8
        }
Packit 3f4df8
        if (length $self->{rbuf} >= $self->{max_line_size}) {
Packit 3f4df8
            die(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}\n/);
Packit 3f4df8
        }
Packit 3f4df8
        $self->can_read
Packit 3f4df8
          or die(qq/Timed out while waiting for socket to become ready for reading\n/);
Packit 3f4df8
        my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
Packit 3f4df8
        if (defined $r) {
Packit 3f4df8
            last unless $r;
Packit 3f4df8
        }
Packit 3f4df8
        elsif ($! != EINTR) {
Packit 3f4df8
            if ($self->{fh}->can('errstr')){
Packit 3f4df8
                my $err = $self->{fh}->errstr();
Packit 3f4df8
                die (qq/Could not read from SSL socket: '$err'\n /);
Packit 3f4df8
            }
Packit 3f4df8
            else {
Packit 3f4df8
                die(qq/Could not read from socket: '$!'\n/);
Packit 3f4df8
            }
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
    die(qq/Unexpected end of stream while looking for line\n/);
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub read_header_lines {
Packit 3f4df8
    @_ == 1 || @_ == 2 || die(q/Usage: $handle->read_header_lines([headers])/ . "\n");
Packit 3f4df8
    my ($self, $headers) = @_;
Packit 3f4df8
    $headers ||= {};
Packit 3f4df8
    my $lines   = 0;
Packit 3f4df8
    my $val;
Packit 3f4df8
Packit 3f4df8
    while () {
Packit 3f4df8
         my $line = $self->readline;
Packit 3f4df8
Packit 3f4df8
         if (++$lines >= $self->{max_header_lines}) {
Packit 3f4df8
             die(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}\n/);
Packit 3f4df8
         }
Packit 3f4df8
         elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
Packit 3f4df8
             my ($field_name) = lc $1;
Packit 3f4df8
             if (exists $headers->{$field_name}) {
Packit 3f4df8
                 for ($headers->{$field_name}) {
Packit 3f4df8
                     $_ = [$_] unless ref $_ eq "ARRAY";
Packit 3f4df8
                     push @$_, $2;
Packit 3f4df8
                     $val = \$_->[-1];
Packit 3f4df8
                 }
Packit 3f4df8
             }
Packit 3f4df8
             else {
Packit 3f4df8
                 $val = \($headers->{$field_name} = $2);
Packit 3f4df8
             }
Packit 3f4df8
         }
Packit 3f4df8
         elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
Packit 3f4df8
             $val
Packit 3f4df8
               or die(qq/Unexpected header continuation line\n/);
Packit 3f4df8
             next unless length $1;
Packit 3f4df8
             $$val .= ' ' if length $$val;
Packit 3f4df8
             $$val .= $1;
Packit 3f4df8
         }
Packit 3f4df8
         elsif ($line =~ /\A \x0D?\x0A \z/x) {
Packit 3f4df8
            last;
Packit 3f4df8
         }
Packit 3f4df8
         else {
Packit 3f4df8
            die(q/Malformed header line: / . $Printable->($line) . "\n");
Packit 3f4df8
         }
Packit 3f4df8
    }
Packit 3f4df8
    return $headers;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub write_request {
Packit 3f4df8
    @_ == 2 || die(q/Usage: $handle->write_request(request)/ . "\n");
Packit 3f4df8
    my($self, $request) = @_;
Packit 3f4df8
    $self->write_request_header(@{$request}{qw/method uri headers header_case/});
Packit 3f4df8
    $self->write_body($request) if $request->{cb};
Packit 3f4df8
    return;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
# Standard request header names/case from HTTP/1.1 RFCs
Packit 3f4df8
my @rfc_request_headers = qw(
Packit 3f4df8
  Accept Accept-Charset Accept-Encoding Accept-Language Authorization
Packit 3f4df8
  Cache-Control Connection Content-Length Expect From Host
Packit 3f4df8
  If-Match If-Modified-Since If-None-Match If-Range If-Unmodified-Since
Packit 3f4df8
  Max-Forwards Pragma Proxy-Authorization Range Referer TE Trailer
Packit 3f4df8
  Transfer-Encoding Upgrade User-Agent Via
Packit 3f4df8
);
Packit 3f4df8
Packit 3f4df8
my @other_request_headers = qw(
Packit 3f4df8
  Content-Encoding Content-MD5 Content-Type Cookie DNT Date Origin
Packit 3f4df8
  X-XSS-Protection
Packit 3f4df8
);
Packit 3f4df8
Packit 3f4df8
my %HeaderCase = map { lc($_) => $_ } @rfc_request_headers, @other_request_headers;
Packit 3f4df8
Packit 3f4df8
# to avoid multiple small writes and hence nagle, you can pass the method line or anything else to
Packit 3f4df8
# combine writes.
Packit 3f4df8
sub write_header_lines {
Packit 3f4df8
    (@_ >= 2 && @_ <= 4 && ref $_[1] eq 'HASH') || die(q/Usage: $handle->write_header_lines(headers, [header_case, prefix])/ . "\n");
Packit 3f4df8
    my($self, $headers, $header_case, $prefix_data) = @_;
Packit 3f4df8
    $header_case ||= {};
Packit 3f4df8
Packit 3f4df8
    my $buf = (defined $prefix_data ? $prefix_data : '');
Packit 3f4df8
Packit 3f4df8
    # Per RFC, control fields should be listed first
Packit 3f4df8
    my %seen;
Packit 3f4df8
    for my $k ( qw/host cache-control expect max-forwards pragma range te/ ) {
Packit 3f4df8
        next unless exists $headers->{$k};
Packit 3f4df8
        $seen{$k}++;
Packit 3f4df8
        my $field_name = $HeaderCase{$k};
Packit 3f4df8
        my $v = $headers->{$k};
Packit 3f4df8
        for (ref $v eq 'ARRAY' ? @$v : $v) {
Packit 3f4df8
            $_ = '' unless defined $_;
Packit 3f4df8
            $buf .= "$field_name: $_\x0D\x0A";
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    # Other headers sent in arbitrary order
Packit 3f4df8
    while (my ($k, $v) = each %$headers) {
Packit 3f4df8
        my $field_name = lc $k;
Packit 3f4df8
        next if $seen{$field_name};
Packit 3f4df8
        if (exists $HeaderCase{$field_name}) {
Packit 3f4df8
            $field_name = $HeaderCase{$field_name};
Packit 3f4df8
        }
Packit 3f4df8
        else {
Packit 3f4df8
            if (exists $header_case->{$field_name}) {
Packit 3f4df8
                $field_name = $header_case->{$field_name};
Packit 3f4df8
            }
Packit 3f4df8
            else {
Packit 3f4df8
                $field_name =~ s/\b(\w)/\u$1/g;
Packit 3f4df8
            }
Packit 3f4df8
            $field_name =~ /\A $Token+ \z/xo
Packit 3f4df8
              or die(q/Invalid HTTP header field name: / . $Printable->($field_name) . "\n");
Packit 3f4df8
            $HeaderCase{lc $field_name} = $field_name;
Packit 3f4df8
        }
Packit 3f4df8
        for (ref $v eq 'ARRAY' ? @$v : $v) {
Packit 3f4df8
            # unwrap a field value if pre-wrapped by user
Packit 3f4df8
            s/\x0D?\x0A\s+/ /g;
Packit 3f4df8
            die(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_). "\n")
Packit 3f4df8
              unless $_ eq '' || /\A $Field_Content \z/xo;
Packit 3f4df8
            $_ = '' unless defined $_;
Packit 3f4df8
            $buf .= "$field_name: $_\x0D\x0A";
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
    $buf .= "\x0D\x0A";
Packit 3f4df8
    return $self->write($buf);
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
# return value indicates whether message length was defined; this is generally
Packit 3f4df8
# true unless there was no content-length header and we just read until EOF.
Packit 3f4df8
# Other message length errors are thrown as exceptions
Packit 3f4df8
sub read_body {
Packit 3f4df8
    @_ == 3 || die(q/Usage: $handle->read_body(callback, response)/ . "\n");
Packit 3f4df8
    my ($self, $cb, $response) = @_;
Packit 3f4df8
    my $te = $response->{headers}{'transfer-encoding'} || '';
Packit 3f4df8
    my $chunked = grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ;
Packit 3f4df8
    return $chunked
Packit 3f4df8
        ? $self->read_chunked_body($cb, $response)
Packit 3f4df8
        : $self->read_content_body($cb, $response);
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub write_body {
Packit 3f4df8
    @_ == 2 || die(q/Usage: $handle->write_body(request)/ . "\n");
Packit 3f4df8
    my ($self, $request) = @_;
Packit 3f4df8
    if ($request->{headers}{'content-length'}) {
Packit 3f4df8
        return $self->write_content_body($request);
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        return $self->write_chunked_body($request);
Packit 3f4df8
    }
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub read_content_body {
Packit 3f4df8
    @_ == 3 || @_ == 4 || die(q/Usage: $handle->read_content_body(callback, response, [read_length])/ . "\n");
Packit 3f4df8
    my ($self, $cb, $response, $content_length) = @_;
Packit 3f4df8
    $content_length ||= $response->{headers}{'content-length'};
Packit 3f4df8
Packit 3f4df8
    if ( defined $content_length ) {
Packit 3f4df8
        my $len = $content_length;
Packit 3f4df8
        while ($len > 0) {
Packit 3f4df8
            my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
Packit 3f4df8
            $cb->($self->read($read, 0), $response);
Packit 3f4df8
            $len -= $read;
Packit 3f4df8
        }
Packit 3f4df8
        return length($self->{rbuf}) == 0;
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    my $chunk;
Packit 3f4df8
    $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) );
Packit 3f4df8
Packit 3f4df8
    return;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub write_content_body {
Packit 3f4df8
    @_ == 2 || die(q/Usage: $handle->write_content_body(request)/ . "\n");
Packit 3f4df8
    my ($self, $request) = @_;
Packit 3f4df8
Packit 3f4df8
    my ($len, $content_length) = (0, $request->{headers}{'content-length'});
Packit 3f4df8
    while () {
Packit 3f4df8
        my $data = $request->{cb}->();
Packit 3f4df8
Packit 3f4df8
        defined $data && length $data
Packit 3f4df8
          or last;
Packit 3f4df8
Packit 3f4df8
        if ( $] ge '5.008' ) {
Packit 3f4df8
            utf8::downgrade($data, 1)
Packit 3f4df8
                or die(qq/Wide character in write_content()\n/);
Packit 3f4df8
        }
Packit 3f4df8
Packit 3f4df8
        $len += $self->write($data);
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    $len == $content_length
Packit 3f4df8
      or die(qq/Content-Length mismatch (got: $len expected: $content_length)\n/);
Packit 3f4df8
Packit 3f4df8
    return $len;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub read_chunked_body {
Packit 3f4df8
    @_ == 3 || die(q/Usage: $handle->read_chunked_body(callback, $response)/ . "\n");
Packit 3f4df8
    my ($self, $cb, $response) = @_;
Packit 3f4df8
Packit 3f4df8
    while () {
Packit 3f4df8
        my $head = $self->readline;
Packit 3f4df8
Packit 3f4df8
        $head =~ /\A ([A-Fa-f0-9]+)/x
Packit 3f4df8
          or die(q/Malformed chunk head: / . $Printable->($head) . "\n");
Packit 3f4df8
Packit 3f4df8
        my $len = hex($1)
Packit 3f4df8
          or last;
Packit 3f4df8
Packit 3f4df8
        $self->read_content_body($cb, $response, $len);
Packit 3f4df8
Packit 3f4df8
        $self->read(2) eq "\x0D\x0A"
Packit 3f4df8
          or die(qq/Malformed chunk: missing CRLF after chunk data\n/);
Packit 3f4df8
    }
Packit 3f4df8
    $self->read_header_lines($response->{headers});
Packit 3f4df8
    return 1;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub write_chunked_body {
Packit 3f4df8
    @_ == 2 || die(q/Usage: $handle->write_chunked_body(request)/ . "\n");
Packit 3f4df8
    my ($self, $request) = @_;
Packit 3f4df8
Packit 3f4df8
    my $len = 0;
Packit 3f4df8
    while () {
Packit 3f4df8
        my $data = $request->{cb}->();
Packit 3f4df8
Packit 3f4df8
        defined $data && length $data
Packit 3f4df8
          or last;
Packit 3f4df8
Packit 3f4df8
        if ( $] ge '5.008' ) {
Packit 3f4df8
            utf8::downgrade($data, 1)
Packit 3f4df8
                or die(qq/Wide character in write_chunked_body()\n/);
Packit 3f4df8
        }
Packit 3f4df8
Packit 3f4df8
        $len += length $data;
Packit 3f4df8
Packit 3f4df8
        my $chunk  = sprintf '%X', length $data;
Packit 3f4df8
           $chunk .= "\x0D\x0A";
Packit 3f4df8
           $chunk .= $data;
Packit 3f4df8
           $chunk .= "\x0D\x0A";
Packit 3f4df8
Packit 3f4df8
        $self->write($chunk);
Packit 3f4df8
    }
Packit 3f4df8
    $self->write("0\x0D\x0A");
Packit 3f4df8
    if ( ref $request->{trailer_cb} eq 'CODE' ) {
Packit 3f4df8
        $self->write_header_lines($request->{trailer_cb}->())
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        $self->write("\x0D\x0A");
Packit 3f4df8
    }
Packit 3f4df8
    return $len;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub read_response_header {
Packit 3f4df8
    @_ == 1 || die(q/Usage: $handle->read_response_header()/ . "\n");
Packit 3f4df8
    my ($self) = @_;
Packit 3f4df8
Packit 3f4df8
    my $line = $self->readline;
Packit 3f4df8
Packit 3f4df8
    $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
Packit 3f4df8
      or die(q/Malformed Status-Line: / . $Printable->($line). "\n");
Packit 3f4df8
Packit 3f4df8
    my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
Packit 3f4df8
Packit 3f4df8
    die (qq/Unsupported HTTP protocol: $protocol\n/)
Packit 3f4df8
        unless $version =~ /0*1\.0*[01]/;
Packit 3f4df8
Packit 3f4df8
    return {
Packit 3f4df8
        status       => $status,
Packit 3f4df8
        reason       => $reason,
Packit 3f4df8
        headers      => $self->read_header_lines,
Packit 3f4df8
        protocol     => $protocol,
Packit 3f4df8
    };
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub write_request_header {
Packit 3f4df8
    @_ == 5 || die(q/Usage: $handle->write_request_header(method, request_uri, headers, header_case)/ . "\n");
Packit 3f4df8
    my ($self, $method, $request_uri, $headers, $header_case) = @_;
Packit 3f4df8
Packit 3f4df8
    return $self->write_header_lines($headers, $header_case, "$method $request_uri HTTP/1.1\x0D\x0A");
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _do_timeout {
Packit 3f4df8
    my ($self, $type, $timeout) = @_;
Packit 3f4df8
    $timeout = $self->{timeout}
Packit 3f4df8
        unless defined $timeout && $timeout >= 0;
Packit 3f4df8
Packit 3f4df8
    my $fd = fileno $self->{fh};
Packit 3f4df8
    defined $fd && $fd >= 0
Packit 3f4df8
      or die(qq/select(2): 'Bad file descriptor'\n/);
Packit 3f4df8
Packit 3f4df8
    my $initial = time;
Packit 3f4df8
    my $pending = $timeout;
Packit 3f4df8
    my $nfound;
Packit 3f4df8
Packit 3f4df8
    vec(my $fdset = '', $fd, 1) = 1;
Packit 3f4df8
Packit 3f4df8
    while () {
Packit 3f4df8
        $nfound = ($type eq 'read')
Packit 3f4df8
            ? select($fdset, undef, undef, $pending)
Packit 3f4df8
            : select(undef, $fdset, undef, $pending) ;
Packit 3f4df8
        if ($nfound == -1) {
Packit 3f4df8
            $! == EINTR
Packit 3f4df8
              or die(qq/select(2): '$!'\n/);
Packit 3f4df8
            redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
Packit 3f4df8
            $nfound = 0;
Packit 3f4df8
        }
Packit 3f4df8
        last;
Packit 3f4df8
    }
Packit 3f4df8
    $! = 0;
Packit 3f4df8
    return $nfound;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub can_read {
Packit 3f4df8
    @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_read([timeout])/ . "\n");
Packit 3f4df8
    my $self = shift;
Packit 3f4df8
    if ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
Packit 3f4df8
        return 1 if $self->{fh}->pending;
Packit 3f4df8
    }
Packit 3f4df8
    return $self->_do_timeout('read', @_)
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub can_write {
Packit 3f4df8
    @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_write([timeout])/ . "\n");
Packit 3f4df8
    my $self = shift;
Packit 3f4df8
    return $self->_do_timeout('write', @_)
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _assert_ssl {
Packit 3f4df8
    my($ok, $reason) = HTTP::Tiny->can_ssl();
Packit 3f4df8
    die $reason unless $ok;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub can_reuse {
Packit 3f4df8
    my ($self,$scheme,$host,$port,$peer) = @_;
Packit 3f4df8
    return 0 if
Packit 3f4df8
        $self->{pid} != $$
Packit 3f4df8
        || $self->{tid} != _get_tid()
Packit 3f4df8
        || length($self->{rbuf})
Packit 3f4df8
        || $scheme ne $self->{scheme}
Packit 3f4df8
        || $host ne $self->{host}
Packit 3f4df8
        || $port ne $self->{port}
Packit 3f4df8
        || $peer ne $self->{peer}
Packit 3f4df8
        || eval { $self->can_read(0) }
Packit 3f4df8
        || $@ ;
Packit 3f4df8
        return 1;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
# Try to find a CA bundle to validate the SSL cert,
Packit 3f4df8
# prefer Mozilla::CA or fallback to a system file
Packit 3f4df8
sub _find_CA_file {
Packit 3f4df8
    my $self = shift();
Packit 3f4df8
Packit 3f4df8
    my $ca_file =
Packit 3f4df8
      defined( $self->{SSL_options}->{SSL_ca_file} )
Packit 3f4df8
      ? $self->{SSL_options}->{SSL_ca_file}
Packit 3f4df8
      : $ENV{SSL_CERT_FILE};
Packit 3f4df8
Packit 3f4df8
    if ( defined $ca_file ) {
Packit 3f4df8
        unless ( -r $ca_file ) {
Packit 3f4df8
            die qq/SSL_ca_file '$ca_file' not found or not readable\n/;
Packit 3f4df8
        }
Packit 3f4df8
        return $ca_file;
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    local @INC = @INC;
Packit 3f4df8
    pop @INC if $INC[-1] eq '.';
Packit 3f4df8
    return Mozilla::CA::SSL_ca_file()
Packit 3f4df8
        if eval { require Mozilla::CA; 1 };
Packit 3f4df8
Packit 3f4df8
    # cert list copied from golang src/crypto/x509/root_unix.go
Packit 3f4df8
    foreach my $ca_bundle (
Packit 3f4df8
        "/etc/ssl/certs/ca-certificates.crt",     # Debian/Ubuntu/Gentoo etc.
Packit 3f4df8
        "/etc/pki/tls/certs/ca-bundle.crt",       # Fedora/RHEL
Packit 3f4df8
        "/etc/ssl/ca-bundle.pem",                 # OpenSUSE
Packit 3f4df8
        "/etc/openssl/certs/ca-certificates.crt", # NetBSD
Packit 3f4df8
        "/etc/ssl/cert.pem",                      # OpenBSD
Packit 3f4df8
        "/usr/local/share/certs/ca-root-nss.crt", # FreeBSD/DragonFly
Packit 3f4df8
        "/etc/pki/tls/cacert.pem",                # OpenELEC
Packit 3f4df8
        "/etc/certs/ca-certificates.crt",         # Solaris 11.2+
Packit 3f4df8
    ) {
Packit 3f4df8
        return $ca_bundle if -e $ca_bundle;
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    die qq/Couldn't find a CA bundle with which to verify the SSL certificate.\n/
Packit 3f4df8
      . qq/Try installing Mozilla::CA from CPAN\n/;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
# for thread safety, we need to know thread id if threads are loaded
Packit 3f4df8
sub _get_tid {
Packit 3f4df8
    no warnings 'reserved'; # for 'threads'
Packit 3f4df8
    return threads->can("tid") ? threads->tid : 0;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
sub _ssl_args {
Packit 3f4df8
    my ($self, $host) = @_;
Packit 3f4df8
Packit 3f4df8
    my %ssl_args;
Packit 3f4df8
Packit 3f4df8
    # This test reimplements IO::Socket::SSL::can_client_sni(), which wasn't
Packit 3f4df8
    # added until IO::Socket::SSL 1.84
Packit 3f4df8
    if ( Net::SSLeay::OPENSSL_VERSION_NUMBER() >= 0x01000000 ) {
Packit 3f4df8
        $ssl_args{SSL_hostname} = $host,          # Sane SNI support
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    if ($self->{verify_SSL}) {
Packit 3f4df8
        $ssl_args{SSL_verifycn_scheme}  = 'http'; # enable CN validation
Packit 3f4df8
        $ssl_args{SSL_verifycn_name}    = $host;  # set validation hostname
Packit 3f4df8
        $ssl_args{SSL_verify_mode}      = 0x01;   # enable cert validation
Packit 3f4df8
        $ssl_args{SSL_ca_file}          = $self->_find_CA_file;
Packit 3f4df8
    }
Packit 3f4df8
    else {
Packit 3f4df8
        $ssl_args{SSL_verifycn_scheme}  = 'none'; # disable CN validation
Packit 3f4df8
        $ssl_args{SSL_verify_mode}      = 0x00;   # disable cert validation
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    # user options override settings from verify_SSL
Packit 3f4df8
    for my $k ( keys %{$self->{SSL_options}} ) {
Packit 3f4df8
        $ssl_args{$k} = $self->{SSL_options}{$k} if $k =~ m/^SSL_/;
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    return \%ssl_args;
Packit 3f4df8
}
Packit 3f4df8
Packit 3f4df8
1;
Packit 3f4df8
Packit 3f4df8
__END__
Packit 3f4df8
Packit 3f4df8
=pod
Packit 3f4df8
Packit 3f4df8
=encoding UTF-8
Packit 3f4df8
Packit 3f4df8
=head1 NAME
Packit 3f4df8
Packit 3f4df8
HTTP::Tiny - A small, simple, correct HTTP/1.1 client
Packit 3f4df8
Packit 3f4df8
=head1 VERSION
Packit 3f4df8
Packit 3f4df8
version 0.074
Packit 3f4df8
Packit 3f4df8
=head1 SYNOPSIS
Packit 3f4df8
Packit 3f4df8
    use HTTP::Tiny;
Packit 3f4df8
Packit 3f4df8
    my $response = HTTP::Tiny->new->get('http://example.com/');
Packit 3f4df8
Packit 3f4df8
    die "Failed!\n" unless $response->{success};
Packit 3f4df8
Packit 3f4df8
    print "$response->{status} $response->{reason}\n";
Packit 3f4df8
Packit 3f4df8
    while (my ($k, $v) = each %{$response->{headers}}) {
Packit 3f4df8
        for (ref $v eq 'ARRAY' ? @$v : $v) {
Packit 3f4df8
            print "$k: $_\n";
Packit 3f4df8
        }
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
    print $response->{content} if length $response->{content};
Packit 3f4df8
Packit 3f4df8
=head1 DESCRIPTION
Packit 3f4df8
Packit 3f4df8
This is a very simple HTTP/1.1 client, designed for doing simple
Packit 3f4df8
requests without the overhead of a large framework like L<LWP::UserAgent>.
Packit 3f4df8
Packit 3f4df8
It is more correct and more complete than L<HTTP::Lite>.  It supports
Packit 3f4df8
proxies and redirection.  It also correctly resumes after EINTR.
Packit 3f4df8
Packit 3f4df8
If L<IO::Socket::IP> 0.25 or later is installed, HTTP::Tiny will use it instead
Packit 3f4df8
of L<IO::Socket::INET> for transparent support for both IPv4 and IPv6.
Packit 3f4df8
Packit 3f4df8
Cookie support requires L<HTTP::CookieJar> or an equivalent class.
Packit 3f4df8
Packit 3f4df8
=head1 METHODS
Packit 3f4df8
Packit 3f4df8
=head2 new
Packit 3f4df8
Packit 3f4df8
    $http = HTTP::Tiny->new( %attributes );
Packit 3f4df8
Packit 3f4df8
This constructor returns a new HTTP::Tiny object.  Valid attributes include:
Packit 3f4df8
Packit 3f4df8
=over 4
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<agent> — A user-agent string (defaults to 'HTTP-Tiny/$VERSION'). If C<agent> — ends in a space character, the default user-agent string is appended.
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<cookie_jar> — An instance of L<HTTP::CookieJar> — or equivalent class that supports the C<add> and C<cookie_header> methods
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<default_headers> — A hashref of default headers to apply to requests
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<local_address> — The local IP address to bind to
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<keep_alive> — Whether to reuse the last connection (if for the same scheme, host and port) (defaults to 1)
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<max_redirect> — Maximum number of redirects allowed (defaults to 5)
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<max_size> — Maximum response size in bytes (only when not using a data callback).  If defined, responses larger than this will return an exception.
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<http_proxy> — URL of a proxy server to use for HTTP connections (default is C<$ENV{http_proxy}> — if set)
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<https_proxy> — URL of a proxy server to use for HTTPS connections (default is C<$ENV{https_proxy}> — if set)
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<proxy> — URL of a generic proxy server for both HTTP and HTTPS connections (default is C<$ENV{all_proxy}> — if set)
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<no_proxy> — List of domain suffixes that should not be proxied.  Must be a comma-separated string or an array reference. (default is C<$ENV{no_proxy}> —)
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<timeout> — Request timeout in seconds (default is 60) If a socket open, read or write takes longer than the timeout, an exception is thrown.
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<verify_SSL> — A boolean that indicates whether to validate the SSL certificate of an C<https> — connection (default is false)
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<SSL_options> — A hashref of C<SSL_*> — options to pass through to L<IO::Socket::SSL>
Packit 3f4df8
Packit 3f4df8
=back
Packit 3f4df8
Packit 3f4df8
Passing an explicit C<undef> for C<proxy>, C<http_proxy> or C<https_proxy> will
Packit 3f4df8
prevent getting the corresponding proxies from the environment.
Packit 3f4df8
Packit 3f4df8
Exceptions from C<max_size>, C<timeout> or other errors will result in a
Packit 3f4df8
pseudo-HTTP status code of 599 and a reason of "Internal Exception". The
Packit 3f4df8
content field in the response will contain the text of the exception.
Packit 3f4df8
Packit 3f4df8
The C<keep_alive> parameter enables a persistent connection, but only to a
Packit 3f4df8
single destination scheme, host and port.  Also, if any connection-relevant
Packit 3f4df8
attributes are modified, or if the process ID or thread ID change, the
Packit 3f4df8
persistent connection will be dropped.  If you want persistent connections
Packit 3f4df8
across multiple destinations, use multiple HTTP::Tiny objects.
Packit 3f4df8
Packit 3f4df8
See L</SSL SUPPORT> for more on the C<verify_SSL> and C<SSL_options> attributes.
Packit 3f4df8
Packit 3f4df8
=head2 get|head|put|post|delete
Packit 3f4df8
Packit 3f4df8
    $response = $http->get($url);
Packit 3f4df8
    $response = $http->get($url, \%options);
Packit 3f4df8
    $response = $http->head($url);
Packit 3f4df8
Packit 3f4df8
These methods are shorthand for calling C<request()> for the given method.  The
Packit 3f4df8
URL must have unsafe characters escaped and international domain names encoded.
Packit 3f4df8
See C<request()> for valid options and a description of the response.
Packit 3f4df8
Packit 3f4df8
The C<success> field of the response will be true if the status code is 2XX.
Packit 3f4df8
Packit 3f4df8
=head2 post_form
Packit 3f4df8
Packit 3f4df8
    $response = $http->post_form($url, $form_data);
Packit 3f4df8
    $response = $http->post_form($url, $form_data, \%options);
Packit 3f4df8
Packit 3f4df8
This method executes a C<POST> request and sends the key/value pairs from a
Packit 3f4df8
form data hash or array reference to the given URL with a C<content-type> of
Packit 3f4df8
C<application/x-www-form-urlencoded>.  If data is provided as an array
Packit 3f4df8
reference, the order is preserved; if provided as a hash reference, the terms
Packit 3f4df8
are sorted on key and value for consistency.  See documentation for the
Packit 3f4df8
C<www_form_urlencode> method for details on the encoding.
Packit 3f4df8
Packit 3f4df8
The URL must have unsafe characters escaped and international domain names
Packit 3f4df8
encoded.  See C<request()> for valid options and a description of the response.
Packit 3f4df8
Any C<content-type> header or content in the options hashref will be ignored.
Packit 3f4df8
Packit 3f4df8
The C<success> field of the response will be true if the status code is 2XX.
Packit 3f4df8
Packit 3f4df8
=head2 mirror
Packit 3f4df8
Packit 3f4df8
    $response = $http->mirror($url, $file, \%options)
Packit 3f4df8
    if ( $response->{success} ) {
Packit 3f4df8
        print "$file is up to date\n";
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
Executes a C<GET> request for the URL and saves the response body to the file
Packit 3f4df8
name provided.  The URL must have unsafe characters escaped and international
Packit 3f4df8
domain names encoded.  If the file already exists, the request will include an
Packit 3f4df8
C<If-Modified-Since> header with the modification timestamp of the file.  You
Packit 3f4df8
may specify a different C<If-Modified-Since> header yourself in the C<<
Packit 3f4df8
$options->{headers} >> hash.
Packit 3f4df8
Packit 3f4df8
The C<success> field of the response will be true if the status code is 2XX
Packit 3f4df8
or if the status code is 304 (unmodified).
Packit 3f4df8
Packit 3f4df8
If the file was modified and the server response includes a properly
Packit 3f4df8
formatted C<Last-Modified> header, the file modification time will
Packit 3f4df8
be updated accordingly.
Packit 3f4df8
Packit 3f4df8
=head2 request
Packit 3f4df8
Packit 3f4df8
    $response = $http->request($method, $url);
Packit 3f4df8
    $response = $http->request($method, $url, \%options);
Packit 3f4df8
Packit 3f4df8
Executes an HTTP request of the given method type ('GET', 'HEAD', 'POST',
Packit 3f4df8
'PUT', etc.) on the given URL.  The URL must have unsafe characters escaped and
Packit 3f4df8
international domain names encoded.
Packit 3f4df8
Packit 3f4df8
B<NOTE>: Method names are B<case-sensitive> per the HTTP/1.1 specification.
Packit 3f4df8
Don't use C<get> when you really want C<GET>.  See L<LIMITATIONS> for
Packit 3f4df8
how this applies to redirection.
Packit 3f4df8
Packit 3f4df8
If the URL includes a "user:password" stanza, they will be used for Basic-style
Packit 3f4df8
authorization headers.  (Authorization headers will not be included in a
Packit 3f4df8
redirected request.) For example:
Packit 3f4df8
Packit 3f4df8
    $http->request('GET', 'http://Aladdin:open sesame@example.com/');
Packit 3f4df8
Packit 3f4df8
If the "user:password" stanza contains reserved characters, they must
Packit 3f4df8
be percent-escaped:
Packit 3f4df8
Packit 3f4df8
    $http->request('GET', 'http://john%40example.com:password@example.com/');
Packit 3f4df8
Packit 3f4df8
A hashref of options may be appended to modify the request.
Packit 3f4df8
Packit 3f4df8
Valid options are:
Packit 3f4df8
Packit 3f4df8
=over 4
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<headers> — A hashref containing headers to include with the request.  If the value for a header is an array reference, the header will be output multiple times with each value in the array.  These headers over-write any default headers.
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<content> — A scalar to include as the body of the request OR a code reference that will be called iteratively to produce the body of the request
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<trailer_callback> — A code reference that will be called if it exists to provide a hashref of trailing headers (only used with chunked transfer-encoding)
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<data_callback> — A code reference that will be called for each chunks of the response body received.
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<peer> — Override host resolution and force all connections to go only to a specific peer address, regardless of the URL of the request.  This will include any redirections!  This options should be used with extreme caution (e.g. debugging or very special circumstances).
Packit 3f4df8
Packit 3f4df8
=back
Packit 3f4df8
Packit 3f4df8
The C<Host> header is generated from the URL in accordance with RFC 2616.  It
Packit 3f4df8
is a fatal error to specify C<Host> in the C<headers> option.  Other headers
Packit 3f4df8
may be ignored or overwritten if necessary for transport compliance.
Packit 3f4df8
Packit 3f4df8
If the C<content> option is a code reference, it will be called iteratively
Packit 3f4df8
to provide the content body of the request.  It should return the empty
Packit 3f4df8
string or undef when the iterator is exhausted.
Packit 3f4df8
Packit 3f4df8
If the C<content> option is the empty string, no C<content-type> or
Packit 3f4df8
C<content-length> headers will be generated.
Packit 3f4df8
Packit 3f4df8
If the C<data_callback> option is provided, it will be called iteratively until
Packit 3f4df8
the entire response body is received.  The first argument will be a string
Packit 3f4df8
containing a chunk of the response body, the second argument will be the
Packit 3f4df8
in-progress response hash reference, as described below.  (This allows
Packit 3f4df8
customizing the action of the callback based on the C<status> or C<headers>
Packit 3f4df8
received prior to the content body.)
Packit 3f4df8
Packit 3f4df8
The C<request> method returns a hashref containing the response.  The hashref
Packit 3f4df8
will have the following keys:
Packit 3f4df8
Packit 3f4df8
=over 4
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<success> — Boolean indicating whether the operation returned a 2XX status code
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<url> — URL that provided the response. This is the URL of the request unless there were redirections, in which case it is the last URL queried in a redirection chain
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<status> — The HTTP status code of the response
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<reason> — The response phrase returned by the server
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<content> — The body of the response.  If the response does not have any content or if a data callback is provided to consume the response body, this will be the empty string
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<headers> — A hashref of header fields.  All header field names will be normalized to be lower case. If a header is repeated, the value will be an arrayref; it will otherwise be a scalar string containing the value
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<protocol> - If this field exists, it is the protocol of the response such as HTTP/1.0 or HTTP/1.1
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
C<redirects> If this field exists, it is an arrayref of response hash references from redirects in the same order that redirections occurred.  If it does not exist, then no redirections occurred.
Packit 3f4df8
Packit 3f4df8
=back
Packit 3f4df8
Packit 3f4df8
On an exception during the execution of the request, the C<status> field will
Packit 3f4df8
contain 599, and the C<content> field will contain the text of the exception.
Packit 3f4df8
Packit 3f4df8
=head2 www_form_urlencode
Packit 3f4df8
Packit 3f4df8
    $params = $http->www_form_urlencode( $data );
Packit 3f4df8
    $response = $http->get("http://example.com/query?$params");
Packit 3f4df8
Packit 3f4df8
This method converts the key/value pairs from a data hash or array reference
Packit 3f4df8
into a C<x-www-form-urlencoded> string.  The keys and values from the data
Packit 3f4df8
reference will be UTF-8 encoded and escaped per RFC 3986.  If a value is an
Packit 3f4df8
array reference, the key will be repeated with each of the values of the array
Packit 3f4df8
reference.  If data is provided as a hash reference, the key/value pairs in the
Packit 3f4df8
resulting string will be sorted by key and value for consistent ordering.
Packit 3f4df8
Packit 3f4df8
=head2 can_ssl
Packit 3f4df8
Packit 3f4df8
    $ok         = HTTP::Tiny->can_ssl;
Packit 3f4df8
    ($ok, $why) = HTTP::Tiny->can_ssl;
Packit 3f4df8
    ($ok, $why) = $http->can_ssl;
Packit 3f4df8
Packit 3f4df8
Indicates if SSL support is available.  When called as a class object, it
Packit 3f4df8
checks for the correct version of L<Net::SSLeay> and L<IO::Socket::SSL>.
Packit 3f4df8
When called as an object methods, if C<SSL_verify> is true or if C<SSL_verify_mode>
Packit 3f4df8
is set in C<SSL_options>, it checks that a CA file is available.
Packit 3f4df8
Packit 3f4df8
In scalar context, returns a boolean indicating if SSL is available.
Packit 3f4df8
In list context, returns the boolean and a (possibly multi-line) string of
Packit 3f4df8
errors indicating why SSL isn't available.
Packit 3f4df8
Packit 3f4df8
=head2 connected
Packit 3f4df8
Packit 3f4df8
    $host = $http->connected;
Packit 3f4df8
    ($host, $port) = $http->connected;
Packit 3f4df8
Packit 3f4df8
Indicates if a connection to a peer is being kept alive, per the C<keep_alive>
Packit 3f4df8
option.
Packit 3f4df8
Packit 3f4df8
In scalar context, returns the peer host and port, joined with a colon, or
Packit 3f4df8
C<undef> (if no peer is connected).
Packit 3f4df8
In list context, returns the peer host and port or an empty list (if no peer
Packit 3f4df8
is connected).
Packit 3f4df8
Packit 3f4df8
B<Note>: This method cannot reliably be used to discover whether the remote
Packit 3f4df8
host has closed its end of the socket.
Packit 3f4df8
Packit 3f4df8
=for Pod::Coverage SSL_options
Packit 3f4df8
agent
Packit 3f4df8
cookie_jar
Packit 3f4df8
default_headers
Packit 3f4df8
http_proxy
Packit 3f4df8
https_proxy
Packit 3f4df8
keep_alive
Packit 3f4df8
local_address
Packit 3f4df8
max_redirect
Packit 3f4df8
max_size
Packit 3f4df8
no_proxy
Packit 3f4df8
proxy
Packit 3f4df8
timeout
Packit 3f4df8
verify_SSL
Packit 3f4df8
Packit 3f4df8
=head1 SSL SUPPORT
Packit 3f4df8
Packit 3f4df8
Direct C<https> connections are supported only if L<IO::Socket::SSL> 1.56 or
Packit 3f4df8
greater and L<Net::SSLeay> 1.49 or greater are installed. An exception will be
Packit 3f4df8
thrown if new enough versions of these modules are not installed or if the SSL
Packit 3f4df8
encryption fails. You can also use C<HTTP::Tiny::can_ssl()> utility function
Packit 3f4df8
that returns boolean to see if the required modules are installed.
Packit 3f4df8
Packit 3f4df8
An C<https> connection may be made via an C<http> proxy that supports the CONNECT
Packit 3f4df8
command (i.e. RFC 2817).  You may not proxy C<https> via a proxy that itself
Packit 3f4df8
requires C<https> to communicate.
Packit 3f4df8
Packit 3f4df8
SSL provides two distinct capabilities:
Packit 3f4df8
Packit 3f4df8
=over 4
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Encrypted communication channel
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Verification of server identity
Packit 3f4df8
Packit 3f4df8
=back
Packit 3f4df8
Packit 3f4df8
B<By default, HTTP::Tiny does not verify server identity>.
Packit 3f4df8
Packit 3f4df8
Server identity verification is controversial and potentially tricky because it
Packit 3f4df8
depends on a (usually paid) third-party Certificate Authority (CA) trust model
Packit 3f4df8
to validate a certificate as legitimate.  This discriminates against servers
Packit 3f4df8
with self-signed certificates or certificates signed by free, community-driven
Packit 3f4df8
CA's such as L<CAcert.org|http://cacert.org>.
Packit 3f4df8
Packit 3f4df8
By default, HTTP::Tiny does not make any assumptions about your trust model,
Packit 3f4df8
threat level or risk tolerance.  It just aims to give you an encrypted channel
Packit 3f4df8
when you need one.
Packit 3f4df8
Packit 3f4df8
Setting the C<verify_SSL> attribute to a true value will make HTTP::Tiny verify
Packit 3f4df8
that an SSL connection has a valid SSL certificate corresponding to the host
Packit 3f4df8
name of the connection and that the SSL certificate has been verified by a CA.
Packit 3f4df8
Assuming you trust the CA, this will protect against a L
Packit 3f4df8
attack|http://en.wikipedia.org/wiki/Man-in-the-middle_attack>.  If you are
Packit 3f4df8
concerned about security, you should enable this option.
Packit 3f4df8
Packit 3f4df8
Certificate verification requires a file containing trusted CA certificates.
Packit 3f4df8
Packit 3f4df8
If the environment variable C<SSL_CERT_FILE> is present, HTTP::Tiny
Packit 3f4df8
will try to find a CA certificate file in that location.
Packit 3f4df8
Packit 3f4df8
If the L<Mozilla::CA> module is installed, HTTP::Tiny will use the CA file
Packit 3f4df8
included with it as a source of trusted CA's.  (This means you trust Mozilla,
Packit 3f4df8
the author of Mozilla::CA, the CPAN mirror where you got Mozilla::CA, the
Packit 3f4df8
toolchain used to install it, and your operating system security, right?)
Packit 3f4df8
Packit 3f4df8
If that module is not available, then HTTP::Tiny will search several
Packit 3f4df8
system-specific default locations for a CA certificate file:
Packit 3f4df8
Packit 3f4df8
=over 4
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
/etc/ssl/certs/ca-certificates.crt
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
/etc/pki/tls/certs/ca-bundle.crt
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
/etc/ssl/ca-bundle.pem
Packit 3f4df8
Packit 3f4df8
=back
Packit 3f4df8
Packit 3f4df8
An exception will be raised if C<verify_SSL> is true and no CA certificate file
Packit 3f4df8
is available.
Packit 3f4df8
Packit 3f4df8
If you desire complete control over SSL connections, the C<SSL_options> attribute
Packit 3f4df8
lets you provide a hash reference that will be passed through to
Packit 3f4df8
C<IO::Socket::SSL::start_SSL()>, overriding any options set by HTTP::Tiny. For
Packit 3f4df8
example, to provide your own trusted CA file:
Packit 3f4df8
Packit 3f4df8
    SSL_options => {
Packit 3f4df8
        SSL_ca_file => $file_path,
Packit 3f4df8
    }
Packit 3f4df8
Packit 3f4df8
The C<SSL_options> attribute could also be used for such things as providing a
Packit 3f4df8
client certificate for authentication to a server or controlling the choice of
Packit 3f4df8
cipher used for the SSL connection. See L<IO::Socket::SSL> documentation for
Packit 3f4df8
details.
Packit 3f4df8
Packit 3f4df8
=head1 PROXY SUPPORT
Packit 3f4df8
Packit 3f4df8
HTTP::Tiny can proxy both C<http> and C<https> requests.  Only Basic proxy
Packit 3f4df8
authorization is supported and it must be provided as part of the proxy URL:
Packit 3f4df8
C<http://user:pass@proxy.example.com/>.
Packit 3f4df8
Packit 3f4df8
HTTP::Tiny supports the following proxy environment variables:
Packit 3f4df8
Packit 3f4df8
=over 4
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
http_proxy or HTTP_PROXY
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
https_proxy or HTTPS_PROXY
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
all_proxy or ALL_PROXY
Packit 3f4df8
Packit 3f4df8
=back
Packit 3f4df8
Packit 3f4df8
If the C<REQUEST_METHOD> environment variable is set, then this might be a CGI
Packit 3f4df8
process and C<HTTP_PROXY> would be set from the C<Proxy:> header, which is a
Packit 3f4df8
security risk.  If C<REQUEST_METHOD> is set, C<HTTP_PROXY> (the upper case
Packit 3f4df8
variant only) is ignored.
Packit 3f4df8
Packit 3f4df8
Tunnelling C<https> over an C<http> proxy using the CONNECT method is
Packit 3f4df8
supported.  If your proxy uses C<https> itself, you can not tunnel C<https>
Packit 3f4df8
over it.
Packit 3f4df8
Packit 3f4df8
Be warned that proxying an C<https> connection opens you to the risk of a
Packit 3f4df8
man-in-the-middle attack by the proxy server.
Packit 3f4df8
Packit 3f4df8
The C<no_proxy> environment variable is supported in the format of a
Packit 3f4df8
comma-separated list of domain extensions proxy should not be used for.
Packit 3f4df8
Packit 3f4df8
Proxy arguments passed to C<new> will override their corresponding
Packit 3f4df8
environment variables.
Packit 3f4df8
Packit 3f4df8
=head1 LIMITATIONS
Packit 3f4df8
Packit 3f4df8
HTTP::Tiny is I<conditionally compliant> with the
Packit 3f4df8
L<HTTP/1.1 specifications|http://www.w3.org/Protocols/>:
Packit 3f4df8
Packit 3f4df8
=over 4
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
"Message Syntax and Routing" [RFC7230]
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
"Semantics and Content" [RFC7231]
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
"Conditional Requests" [RFC7232]
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
"Range Requests" [RFC7233]
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
"Caching" [RFC7234]
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
"Authentication" [RFC7235]
Packit 3f4df8
Packit 3f4df8
=back
Packit 3f4df8
Packit 3f4df8
It attempts to meet all "MUST" requirements of the specification, but does not
Packit 3f4df8
implement all "SHOULD" requirements.  (Note: it was developed against the
Packit 3f4df8
earlier RFC 2616 specification and may not yet meet the revised RFC 7230-7235
Packit 3f4df8
spec.)
Packit 3f4df8
Packit 3f4df8
Some particular limitations of note include:
Packit 3f4df8
Packit 3f4df8
=over
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
HTTP::Tiny focuses on correct transport.  Users are responsible for ensuring
Packit 3f4df8
that user-defined headers and content are compliant with the HTTP/1.1
Packit 3f4df8
specification.
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Users must ensure that URLs are properly escaped for unsafe characters and that
Packit 3f4df8
international domain names are properly encoded to ASCII. See L<URI::Escape>,
Packit 3f4df8
L<URI::_punycode> and L<Net::IDN::Encode>.
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Redirection is very strict against the specification.  Redirection is only
Packit 3f4df8
automatic for response codes 301, 302, 307 and 308 if the request method is
Packit 3f4df8
'GET' or 'HEAD'.  Response code 303 is always converted into a 'GET'
Packit 3f4df8
redirection, as mandated by the specification.  There is no automatic support
Packit 3f4df8
for status 305 ("Use proxy") redirections.
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
There is no provision for delaying a request body using an C<Expect> header.
Packit 3f4df8
Unexpected C<1XX> responses are silently ignored as per the specification.
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Only 'chunked' C<Transfer-Encoding> is supported.
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
There is no support for a Request-URI of '*' for the 'OPTIONS' request.
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Headers mentioned in the RFCs and some other, well-known headers are
Packit 3f4df8
generated with their canonical case.  Other headers are sent in the
Packit 3f4df8
case provided by the user.  Except for control headers (which are sent first),
Packit 3f4df8
headers are sent in arbitrary order.
Packit 3f4df8
Packit 3f4df8
=back
Packit 3f4df8
Packit 3f4df8
Despite the limitations listed above, HTTP::Tiny is considered
Packit 3f4df8
feature-complete.  New feature requests should be directed to
Packit 3f4df8
L<HTTP::Tiny::UA>.
Packit 3f4df8
Packit 3f4df8
=head1 SEE ALSO
Packit 3f4df8
Packit 3f4df8
=over 4
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
L<HTTP::Tiny::UA> - Higher level UA features for HTTP::Tiny
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
L<HTTP::Thin> - HTTP::Tiny wrapper with L<HTTP::Request>/L<HTTP::Response> compatibility
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
L<HTTP::Tiny::Mech> - Wrap L<WWW::Mechanize> instance in HTTP::Tiny compatible interface
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
L<IO::Socket::IP> - Required for IPv6 support
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
L<IO::Socket::SSL> - Required for SSL support
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
L<LWP::UserAgent> - If HTTP::Tiny isn't enough for you, this is the "standard" way to do things
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
L<Mozilla::CA> - Required if you want to validate SSL certificates
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
L<Net::SSLeay> - Required for SSL support
Packit 3f4df8
Packit 3f4df8
=back
Packit 3f4df8
Packit 3f4df8
=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
Packit 3f4df8
Packit 3f4df8
=head1 SUPPORT
Packit 3f4df8
Packit 3f4df8
=head2 Bugs / Feature Requests
Packit 3f4df8
Packit 3f4df8
Please report any bugs or feature requests through the issue tracker
Packit 3f4df8
at L<https://github.com/chansen/p5-http-tiny/issues>.
Packit 3f4df8
You will be notified automatically of any progress on your issue.
Packit 3f4df8
Packit 3f4df8
=head2 Source Code
Packit 3f4df8
Packit 3f4df8
This is open source software.  The code repository is available for
Packit 3f4df8
public review and contribution under the terms of the license.
Packit 3f4df8
Packit 3f4df8
L<https://github.com/chansen/p5-http-tiny>
Packit 3f4df8
Packit 3f4df8
  git clone https://github.com/chansen/p5-http-tiny.git
Packit 3f4df8
Packit 3f4df8
=head1 AUTHORS
Packit 3f4df8
Packit 3f4df8
=over 4
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Christian Hansen <chansen@cpan.org>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
David Golden <dagolden@cpan.org>
Packit 3f4df8
Packit 3f4df8
=back
Packit 3f4df8
Packit 3f4df8
=head1 CONTRIBUTORS
Packit 3f4df8
Packit 3f4df8
=for stopwords Alan Gardner Alessandro Ghedini A. Sinan Unur Brad Gilbert brian m. carlson Chris Nehren Weyl Claes Jakobsson Clinton Gormley Craig Berry David Golden Mitchell Dean Pearce Edward Zborowski James Raspass Jeremy Mates Jess Robinson Karen Etheridge Lukas Eklund Martin J. Evans Martin-Louis Bright Mike Doherty Nicolas Rochelemagne Olaf Alders Olivier Mengué Petr Písař Serguei Trouchelle Shoichi Kaji SkyMarshal Sören Kornetzki Steve Grazzini Syohei YOSHIDA Tatsuhiko Miyagawa Tom Hukins Tony Cook
Packit 3f4df8
Packit 3f4df8
=over 4
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Alan Gardner <gardner@pythian.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Alessandro Ghedini <al3xbio@gmail.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
A. Sinan Unur <nanis@cpan.org>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Brad Gilbert <bgills@cpan.org>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
brian m. carlson <sandals@crustytoothpaste.net>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Chris Nehren <apeiron@cpan.org>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Chris Weyl <cweyl@alumni.drew.edu>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Claes Jakobsson <claes@surfar.nu>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Clinton Gormley <clint@traveljury.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Craig A. Berry <craigberry@mac.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Craig Berry <cberry@cpan.org>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
David Golden <xdg@xdg.me>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
David Mitchell <davem@iabyn.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Dean Pearce <pearce@pythian.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Edward Zborowski <ed@rubensteintech.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
James Raspass <jraspass@gmail.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Jeremy Mates <jmates@cpan.org>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Jess Robinson <castaway@desert-island.me.uk>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Karen Etheridge <ether@cpan.org>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Lukas Eklund <leklund@gmail.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Martin J. Evans <mjegh@ntlworld.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Martin-Louis Bright <mlbright@gmail.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Mike Doherty <doherty@cpan.org>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Nicolas Rochelemagne <rochelemagne@cpanel.net>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Olaf Alders <olaf@wundersolutions.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Olivier Mengué <dolmen@cpan.org>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Petr Písař <ppisar@redhat.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Serguei Trouchelle <stro@cpan.org>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Shoichi Kaji <skaji@cpan.org>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
SkyMarshal <skymarshal1729@gmail.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Sören Kornetzki <soeren.kornetzki@delti.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Steve Grazzini <steve.grazzini@grantstreet.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Syohei YOSHIDA <syohex@gmail.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Tatsuhiko Miyagawa <miyagawa@bulknews.net>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Tom Hukins <tom@eborcom.com>
Packit 3f4df8
Packit 3f4df8
=item *
Packit 3f4df8
Packit 3f4df8
Tony Cook <tony@develop-help.com>
Packit 3f4df8
Packit 3f4df8
=back
Packit 3f4df8
Packit 3f4df8
=head1 COPYRIGHT AND LICENSE
Packit 3f4df8
Packit 3f4df8
This software is copyright (c) 2018 by Christian Hansen.
Packit 3f4df8
Packit 3f4df8
This is free software; you can redistribute it and/or modify it under
Packit 3f4df8
the same terms as the Perl 5 programming language system itself.
Packit 3f4df8
Packit 3f4df8
=cut