xref: /openbsd-src/gnu/usr.bin/perl/cpan/HTTP-Tiny/lib/HTTP/Tiny.pm (revision 3d61058aa5c692477b6d18acfbbdb653a9930ff9)
1898184e3Ssthen# vim: ts=4 sts=4 sw=4 et:
2898184e3Ssthenpackage HTTP::Tiny;
3898184e3Ssthenuse strict;
4898184e3Ssthenuse warnings;
5898184e3Ssthen# ABSTRACT: A small, simple, correct HTTP/1.1 client
6b8851fccSafresh1
7*3d61058aSafresh1our $VERSION = '0.088';
8898184e3Ssthen
99f11ffb7Safresh1sub _croak { require Carp; Carp::croak(@_) }
10898184e3Ssthen
11b8851fccSafresh1#pod =method new
12b8851fccSafresh1#pod
13b8851fccSafresh1#pod     $http = HTTP::Tiny->new( %attributes );
14b8851fccSafresh1#pod
15b8851fccSafresh1#pod This constructor returns a new HTTP::Tiny object.  Valid attributes include:
16b8851fccSafresh1#pod
17b8851fccSafresh1#pod =for :list
189f11ffb7Safresh1#pod * C<agent> — A user-agent string (defaults to 'HTTP-Tiny/$VERSION'). If
199f11ffb7Safresh1#pod   C<agent> — ends in a space character, the default user-agent string is
209f11ffb7Safresh1#pod   appended.
219f11ffb7Safresh1#pod * C<cookie_jar> — An instance of L<HTTP::CookieJar> — or equivalent class
229f11ffb7Safresh1#pod   that supports the C<add> and C<cookie_header> methods
239f11ffb7Safresh1#pod * C<default_headers> — A hashref of default headers to apply to requests
249f11ffb7Safresh1#pod * C<local_address> — The local IP address to bind to
259f11ffb7Safresh1#pod * C<keep_alive> — Whether to reuse the last connection (if for the same
269f11ffb7Safresh1#pod   scheme, host and port) (defaults to 1)
279f11ffb7Safresh1#pod * C<max_redirect> — Maximum number of redirects allowed (defaults to 5)
289f11ffb7Safresh1#pod * C<max_size> — Maximum response size in bytes (only when not using a data
29eac174f2Safresh1#pod   callback).  If defined, requests with responses larger than this will return
30eac174f2Safresh1#pod   a 599 status code.
319f11ffb7Safresh1#pod * C<http_proxy> — URL of a proxy server to use for HTTP connections
329f11ffb7Safresh1#pod   (default is C<$ENV{http_proxy}> — if set)
339f11ffb7Safresh1#pod * C<https_proxy> — URL of a proxy server to use for HTTPS connections
349f11ffb7Safresh1#pod   (default is C<$ENV{https_proxy}> — if set)
359f11ffb7Safresh1#pod * C<proxy> — URL of a generic proxy server for both HTTP and HTTPS
369f11ffb7Safresh1#pod   connections (default is C<$ENV{all_proxy}> — if set)
379f11ffb7Safresh1#pod * C<no_proxy> — List of domain suffixes that should not be proxied.  Must
389f11ffb7Safresh1#pod   be a comma-separated string or an array reference. (default is
399f11ffb7Safresh1#pod   C<$ENV{no_proxy}> —)
409f11ffb7Safresh1#pod * C<timeout> — Request timeout in seconds (default is 60) If a socket open,
41eac174f2Safresh1#pod   read or write takes longer than the timeout, the request response status code
42eac174f2Safresh1#pod   will be 599.
43e0680481Safresh1#pod * C<verify_SSL> — A boolean that indicates whether to validate the TLS/SSL
44e0680481Safresh1#pod   certificate of an C<https> — connection (default is true). Changed from false
45e0680481Safresh1#pod   to true in version 0.083.
469f11ffb7Safresh1#pod * C<SSL_options> — A hashref of C<SSL_*> — options to pass through to
479f11ffb7Safresh1#pod   L<IO::Socket::SSL>
48e0680481Safresh1#pod * C<$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}> - Changes the default
49e0680481Safresh1#pod   certificate verification behavior to not check server identity if set to 1.
50e0680481Safresh1#pod   Only effective if C<verify_SSL> is not set. Added in version 0.083.
51e0680481Safresh1#pod
52b8851fccSafresh1#pod
53eac174f2Safresh1#pod An accessor/mutator method exists for each attribute.
54eac174f2Safresh1#pod
55b8851fccSafresh1#pod Passing an explicit C<undef> for C<proxy>, C<http_proxy> or C<https_proxy> will
56b8851fccSafresh1#pod prevent getting the corresponding proxies from the environment.
57b8851fccSafresh1#pod
58eac174f2Safresh1#pod Errors during request execution will result in a pseudo-HTTP status code of 599
59eac174f2Safresh1#pod and a reason of "Internal Exception". The content field in the response will
60eac174f2Safresh1#pod contain the text of the error.
61b8851fccSafresh1#pod
62b8851fccSafresh1#pod The C<keep_alive> parameter enables a persistent connection, but only to a
63eac174f2Safresh1#pod single destination scheme, host and port.  If any connection-relevant
64eac174f2Safresh1#pod attributes are modified via accessor, or if the process ID or thread ID change,
65eac174f2Safresh1#pod the persistent connection will be dropped.  If you want persistent connections
66b8851fccSafresh1#pod across multiple destinations, use multiple HTTP::Tiny objects.
67b8851fccSafresh1#pod
68b8851fccSafresh1#pod See L</SSL SUPPORT> for more on the C<verify_SSL> and C<SSL_options> attributes.
69b8851fccSafresh1#pod
70b8851fccSafresh1#pod =cut
71898184e3Ssthen
72898184e3Ssthenmy @attributes;
73898184e3SsthenBEGIN {
746fb12b70Safresh1    @attributes = qw(
756fb12b70Safresh1        cookie_jar default_headers http_proxy https_proxy keep_alive
769f11ffb7Safresh1        local_address max_redirect max_size proxy no_proxy
776fb12b70Safresh1        SSL_options verify_SSL
786fb12b70Safresh1    );
796fb12b70Safresh1    my %persist_ok = map {; $_ => 1 } qw(
806fb12b70Safresh1        cookie_jar default_headers max_redirect max_size
816fb12b70Safresh1    );
82898184e3Ssthen    no strict 'refs';
836fb12b70Safresh1    no warnings 'uninitialized';
84898184e3Ssthen    for my $accessor ( @attributes ) {
85898184e3Ssthen        *{$accessor} = sub {
866fb12b70Safresh1            @_ > 1
876fb12b70Safresh1                ? do {
886fb12b70Safresh1                    delete $_[0]->{handle} if !$persist_ok{$accessor} && $_[1] ne $_[0]->{$accessor};
896fb12b70Safresh1                    $_[0]->{$accessor} = $_[1]
906fb12b70Safresh1                }
916fb12b70Safresh1                : $_[0]->{$accessor};
92898184e3Ssthen        };
93898184e3Ssthen    }
94898184e3Ssthen}
95898184e3Ssthen
966fb12b70Safresh1sub agent {
976fb12b70Safresh1    my($self, $agent) = @_;
986fb12b70Safresh1    if( @_ > 1 ){
996fb12b70Safresh1        $self->{agent} =
1006fb12b70Safresh1            (defined $agent && $agent =~ / $/) ? $agent . $self->_agent : $agent;
1016fb12b70Safresh1    }
1026fb12b70Safresh1    return $self->{agent};
1036fb12b70Safresh1}
1046fb12b70Safresh1
1059f11ffb7Safresh1sub timeout {
1069f11ffb7Safresh1    my ($self, $timeout) = @_;
1079f11ffb7Safresh1    if ( @_ > 1 ) {
1089f11ffb7Safresh1        $self->{timeout} = $timeout;
1099f11ffb7Safresh1        if ($self->{handle}) {
1109f11ffb7Safresh1            $self->{handle}->timeout($timeout);
1119f11ffb7Safresh1        }
1129f11ffb7Safresh1    }
1139f11ffb7Safresh1    return $self->{timeout};
1149f11ffb7Safresh1}
1159f11ffb7Safresh1
116898184e3Ssthensub new {
117898184e3Ssthen    my($class, %args) = @_;
11891f110e0Safresh1
119e0680481Safresh1    # Support lower case verify_ssl argument, but only if verify_SSL is not
120e0680481Safresh1    # true.
121e0680481Safresh1    if ( exists $args{verify_ssl} ) {
122e0680481Safresh1        $args{verify_SSL}  ||= $args{verify_ssl};
123e0680481Safresh1    }
124e0680481Safresh1
125898184e3Ssthen    my $self = {
126898184e3Ssthen        max_redirect => 5,
1279f11ffb7Safresh1        timeout      => defined $args{timeout} ? $args{timeout} : 60,
1286fb12b70Safresh1        keep_alive   => 1,
129e0680481Safresh1        verify_SSL   => defined $args{verify_SSL} ? $args{verify_SSL} : _verify_SSL_default(),
1306fb12b70Safresh1        no_proxy     => $ENV{no_proxy},
131898184e3Ssthen    };
13291f110e0Safresh1
1336fb12b70Safresh1    bless $self, $class;
1346fb12b70Safresh1
1356fb12b70Safresh1    $class->_validate_cookie_jar( $args{cookie_jar} ) if $args{cookie_jar};
13691f110e0Safresh1
137898184e3Ssthen    for my $key ( @attributes ) {
138898184e3Ssthen        $self->{$key} = $args{$key} if exists $args{$key}
139898184e3Ssthen    }
140898184e3Ssthen
1416fb12b70Safresh1    $self->agent( exists $args{agent} ? $args{agent} : $class->_agent );
1426fb12b70Safresh1
1436fb12b70Safresh1    $self->_set_proxies;
1446fb12b70Safresh1
1456fb12b70Safresh1    return $self;
1466fb12b70Safresh1}
1476fb12b70Safresh1
148e0680481Safresh1sub _verify_SSL_default {
149e0680481Safresh1    my ($self) = @_;
150e0680481Safresh1    # Check if insecure default certificate verification behaviour has been
151e0680481Safresh1    # changed by the user by setting PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT=1
152e0680481Safresh1    return (($ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT} || '') eq '1') ? 0 : 1;
153e0680481Safresh1}
154e0680481Safresh1
1556fb12b70Safresh1sub _set_proxies {
1566fb12b70Safresh1    my ($self) = @_;
1576fb12b70Safresh1
158b8851fccSafresh1    # get proxies from %ENV only if not provided; explicit undef will disable
159b8851fccSafresh1    # getting proxies from the environment
160b8851fccSafresh1
161b8851fccSafresh1    # generic proxy
162b8851fccSafresh1    if (! exists $self->{proxy} ) {
1636fb12b70Safresh1        $self->{proxy} = $ENV{all_proxy} || $ENV{ALL_PROXY};
164b8851fccSafresh1    }
165b8851fccSafresh1
1666fb12b70Safresh1    if ( defined $self->{proxy} ) {
1676fb12b70Safresh1        $self->_split_proxy( 'generic proxy' => $self->{proxy} ); # validate
168898184e3Ssthen    }
169898184e3Ssthen    else {
1706fb12b70Safresh1        delete $self->{proxy};
171898184e3Ssthen    }
172b8851fccSafresh1
173b8851fccSafresh1    # http proxy
174b8851fccSafresh1    if (! exists $self->{http_proxy} ) {
175b8851fccSafresh1        # under CGI, bypass HTTP_PROXY as request sets it from Proxy header
176eac174f2Safresh1        local $ENV{HTTP_PROXY} = ($ENV{CGI_HTTP_PROXY} || "") if $ENV{REQUEST_METHOD};
177b8851fccSafresh1        $self->{http_proxy} = $ENV{http_proxy} || $ENV{HTTP_PROXY} || $self->{proxy};
178898184e3Ssthen    }
179898184e3Ssthen
1806fb12b70Safresh1    if ( defined $self->{http_proxy} ) {
1816fb12b70Safresh1        $self->_split_proxy( http_proxy => $self->{http_proxy} ); # validate
1826fb12b70Safresh1        $self->{_has_proxy}{http} = 1;
1836fb12b70Safresh1    }
1846fb12b70Safresh1    else {
1856fb12b70Safresh1        delete $self->{http_proxy};
1866fb12b70Safresh1    }
187b8851fccSafresh1
188b8851fccSafresh1    # https proxy
189b8851fccSafresh1    if (! exists $self->{https_proxy} ) {
190b8851fccSafresh1        $self->{https_proxy} = $ENV{https_proxy} || $ENV{HTTPS_PROXY} || $self->{proxy};
191898184e3Ssthen    }
192898184e3Ssthen
1936fb12b70Safresh1    if ( $self->{https_proxy} ) {
1946fb12b70Safresh1        $self->_split_proxy( https_proxy => $self->{https_proxy} ); # validate
1956fb12b70Safresh1        $self->{_has_proxy}{https} = 1;
1966fb12b70Safresh1    }
1976fb12b70Safresh1    else {
1986fb12b70Safresh1        delete $self->{https_proxy};
1996fb12b70Safresh1    }
2006fb12b70Safresh1
2016fb12b70Safresh1    # Split no_proxy to array reference if not provided as such
2026fb12b70Safresh1    unless ( ref $self->{no_proxy} eq 'ARRAY' ) {
2036fb12b70Safresh1        $self->{no_proxy} =
2046fb12b70Safresh1            (defined $self->{no_proxy}) ? [ split /\s*,\s*/, $self->{no_proxy} ] : [];
2056fb12b70Safresh1    }
2066fb12b70Safresh1
2076fb12b70Safresh1    return;
2086fb12b70Safresh1}
2096fb12b70Safresh1
210eac174f2Safresh1#pod =method get|head|put|post|patch|delete
211b8851fccSafresh1#pod
212b8851fccSafresh1#pod     $response = $http->get($url);
213b8851fccSafresh1#pod     $response = $http->get($url, \%options);
214b8851fccSafresh1#pod     $response = $http->head($url);
215b8851fccSafresh1#pod
216b8851fccSafresh1#pod These methods are shorthand for calling C<request()> for the given method.  The
217b8851fccSafresh1#pod URL must have unsafe characters escaped and international domain names encoded.
218b8851fccSafresh1#pod See C<request()> for valid options and a description of the response.
219b8851fccSafresh1#pod
220b8851fccSafresh1#pod The C<success> field of the response will be true if the status code is 2XX.
221b8851fccSafresh1#pod
222b8851fccSafresh1#pod =cut
223898184e3Ssthen
224eac174f2Safresh1for my $sub_name ( qw/get head put post patch delete/ ) {
225898184e3Ssthen    my $req_method = uc $sub_name;
226898184e3Ssthen    no strict 'refs';
227898184e3Ssthen    eval <<"HERE"; ## no critic
228898184e3Ssthen    sub $sub_name {
229898184e3Ssthen        my (\$self, \$url, \$args) = \@_;
230898184e3Ssthen        \@_ == 2 || (\@_ == 3 && ref \$args eq 'HASH')
2319f11ffb7Safresh1        or _croak(q/Usage: \$http->$sub_name(URL, [HASHREF])/ . "\n");
232898184e3Ssthen        return \$self->request('$req_method', \$url, \$args || {});
233898184e3Ssthen    }
234898184e3SsthenHERE
235898184e3Ssthen}
236898184e3Ssthen
237b8851fccSafresh1#pod =method post_form
238b8851fccSafresh1#pod
239b8851fccSafresh1#pod     $response = $http->post_form($url, $form_data);
240b8851fccSafresh1#pod     $response = $http->post_form($url, $form_data, \%options);
241b8851fccSafresh1#pod
242b8851fccSafresh1#pod This method executes a C<POST> request and sends the key/value pairs from a
243b8851fccSafresh1#pod form data hash or array reference to the given URL with a C<content-type> of
244b8851fccSafresh1#pod C<application/x-www-form-urlencoded>.  If data is provided as an array
245b8851fccSafresh1#pod reference, the order is preserved; if provided as a hash reference, the terms
246b8851fccSafresh1#pod are sorted on key and value for consistency.  See documentation for the
247b8851fccSafresh1#pod C<www_form_urlencode> method for details on the encoding.
248b8851fccSafresh1#pod
249b8851fccSafresh1#pod The URL must have unsafe characters escaped and international domain names
250b8851fccSafresh1#pod encoded.  See C<request()> for valid options and a description of the response.
251b8851fccSafresh1#pod Any C<content-type> header or content in the options hashref will be ignored.
252b8851fccSafresh1#pod
253b8851fccSafresh1#pod The C<success> field of the response will be true if the status code is 2XX.
254b8851fccSafresh1#pod
255b8851fccSafresh1#pod =cut
256898184e3Ssthen
257898184e3Ssthensub post_form {
258898184e3Ssthen    my ($self, $url, $data, $args) = @_;
259898184e3Ssthen    (@_ == 3 || @_ == 4 && ref $args eq 'HASH')
2609f11ffb7Safresh1        or _croak(q/Usage: $http->post_form(URL, DATAREF, [HASHREF])/ . "\n");
261898184e3Ssthen
262898184e3Ssthen    my $headers = {};
263898184e3Ssthen    while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
264898184e3Ssthen        $headers->{lc $key} = $value;
265898184e3Ssthen    }
266898184e3Ssthen
267898184e3Ssthen    return $self->request('POST', $url, {
268e0680481Safresh1            # Any existing 'headers' key in $args will be overridden with a
269e0680481Safresh1            # normalized version below.
270898184e3Ssthen            %$args,
271898184e3Ssthen            content => $self->www_form_urlencode($data),
272898184e3Ssthen            headers => {
273898184e3Ssthen                %$headers,
274898184e3Ssthen                'content-type' => 'application/x-www-form-urlencoded'
275898184e3Ssthen            },
276898184e3Ssthen        }
277898184e3Ssthen    );
278898184e3Ssthen}
279898184e3Ssthen
280b8851fccSafresh1#pod =method mirror
281b8851fccSafresh1#pod
282b8851fccSafresh1#pod     $response = $http->mirror($url, $file, \%options)
283b8851fccSafresh1#pod     if ( $response->{success} ) {
284b8851fccSafresh1#pod         print "$file is up to date\n";
285b8851fccSafresh1#pod     }
286b8851fccSafresh1#pod
287b8851fccSafresh1#pod Executes a C<GET> request for the URL and saves the response body to the file
288b8851fccSafresh1#pod name provided.  The URL must have unsafe characters escaped and international
289b8851fccSafresh1#pod domain names encoded.  If the file already exists, the request will include an
290b8851fccSafresh1#pod C<If-Modified-Since> header with the modification timestamp of the file.  You
291b8851fccSafresh1#pod may specify a different C<If-Modified-Since> header yourself in the C<<
292b8851fccSafresh1#pod $options->{headers} >> hash.
293b8851fccSafresh1#pod
294b8851fccSafresh1#pod The C<success> field of the response will be true if the status code is 2XX
295b8851fccSafresh1#pod or if the status code is 304 (unmodified).
296b8851fccSafresh1#pod
297b8851fccSafresh1#pod If the file was modified and the server response includes a properly
298b8851fccSafresh1#pod formatted C<Last-Modified> header, the file modification time will
299b8851fccSafresh1#pod be updated accordingly.
300b8851fccSafresh1#pod
301b8851fccSafresh1#pod =cut
302898184e3Ssthen
303898184e3Ssthensub mirror {
304898184e3Ssthen    my ($self, $url, $file, $args) = @_;
305898184e3Ssthen    @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
3069f11ffb7Safresh1      or _croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/ . "\n");
3079f11ffb7Safresh1
3089f11ffb7Safresh1    if ( exists $args->{headers} ) {
3099f11ffb7Safresh1        my $headers = {};
3109f11ffb7Safresh1        while ( my ($key, $value) = each %{$args->{headers} || {}} ) {
3119f11ffb7Safresh1            $headers->{lc $key} = $value;
3129f11ffb7Safresh1        }
3139f11ffb7Safresh1        $args->{headers} = $headers;
3149f11ffb7Safresh1    }
3159f11ffb7Safresh1
316898184e3Ssthen    if ( -e $file and my $mtime = (stat($file))[9] ) {
317898184e3Ssthen        $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
318898184e3Ssthen    }
319898184e3Ssthen    my $tempfile = $file . int(rand(2**31));
3206fb12b70Safresh1
3216fb12b70Safresh1    require Fcntl;
3226fb12b70Safresh1    sysopen my $fh, $tempfile, Fcntl::O_CREAT()|Fcntl::O_EXCL()|Fcntl::O_WRONLY()
3239f11ffb7Safresh1       or _croak(qq/Error: Could not create temporary file $tempfile for downloading: $!\n/);
324898184e3Ssthen    binmode $fh;
325898184e3Ssthen    $args->{data_callback} = sub { print {$fh} $_[0] };
326898184e3Ssthen    my $response = $self->request('GET', $url, $args);
327898184e3Ssthen    close $fh
3289f11ffb7Safresh1        or _croak(qq/Error: Caught error closing temporary file $tempfile: $!\n/);
3296fb12b70Safresh1
330898184e3Ssthen    if ( $response->{success} ) {
331898184e3Ssthen        rename $tempfile, $file
3329f11ffb7Safresh1            or _croak(qq/Error replacing $file with $tempfile: $!\n/);
333898184e3Ssthen        my $lm = $response->{headers}{'last-modified'};
334898184e3Ssthen        if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
335898184e3Ssthen            utime $mtime, $mtime, $file;
336898184e3Ssthen        }
337898184e3Ssthen    }
338898184e3Ssthen    $response->{success} ||= $response->{status} eq '304';
339898184e3Ssthen    unlink $tempfile;
340898184e3Ssthen    return $response;
341898184e3Ssthen}
342898184e3Ssthen
343b8851fccSafresh1#pod =method request
344b8851fccSafresh1#pod
345b8851fccSafresh1#pod     $response = $http->request($method, $url);
346b8851fccSafresh1#pod     $response = $http->request($method, $url, \%options);
347b8851fccSafresh1#pod
348b8851fccSafresh1#pod Executes an HTTP request of the given method type ('GET', 'HEAD', 'POST',
349b8851fccSafresh1#pod 'PUT', etc.) on the given URL.  The URL must have unsafe characters escaped and
350b8851fccSafresh1#pod international domain names encoded.
351b8851fccSafresh1#pod
352b46d8ef2Safresh1#pod B<NOTE>: Method names are B<case-sensitive> per the HTTP/1.1 specification.
353b46d8ef2Safresh1#pod Don't use C<get> when you really want C<GET>.  See L<LIMITATIONS> for
354b46d8ef2Safresh1#pod how this applies to redirection.
355b46d8ef2Safresh1#pod
356b8851fccSafresh1#pod If the URL includes a "user:password" stanza, they will be used for Basic-style
357b8851fccSafresh1#pod authorization headers.  (Authorization headers will not be included in a
358b8851fccSafresh1#pod redirected request.) For example:
359b8851fccSafresh1#pod
360b8851fccSafresh1#pod     $http->request('GET', 'http://Aladdin:open sesame@example.com/');
361b8851fccSafresh1#pod
362b8851fccSafresh1#pod If the "user:password" stanza contains reserved characters, they must
363b8851fccSafresh1#pod be percent-escaped:
364b8851fccSafresh1#pod
365b8851fccSafresh1#pod     $http->request('GET', 'http://john%40example.com:password@example.com/');
366b8851fccSafresh1#pod
367b8851fccSafresh1#pod A hashref of options may be appended to modify the request.
368b8851fccSafresh1#pod
369b8851fccSafresh1#pod Valid options are:
370b8851fccSafresh1#pod
371b8851fccSafresh1#pod =for :list
372b8851fccSafresh1#pod * C<headers> —
373b8851fccSafresh1#pod     A hashref containing headers to include with the request.  If the value for
374b8851fccSafresh1#pod     a header is an array reference, the header will be output multiple times with
375b8851fccSafresh1#pod     each value in the array.  These headers over-write any default headers.
376b8851fccSafresh1#pod * C<content> —
377b8851fccSafresh1#pod     A scalar to include as the body of the request OR a code reference
378b8851fccSafresh1#pod     that will be called iteratively to produce the body of the request
379b8851fccSafresh1#pod * C<trailer_callback> —
380b8851fccSafresh1#pod     A code reference that will be called if it exists to provide a hashref
381b8851fccSafresh1#pod     of trailing headers (only used with chunked transfer-encoding)
382b8851fccSafresh1#pod * C<data_callback> —
383b8851fccSafresh1#pod     A code reference that will be called for each chunks of the response
384b8851fccSafresh1#pod     body received.
3859f11ffb7Safresh1#pod * C<peer> —
3869f11ffb7Safresh1#pod     Override host resolution and force all connections to go only to a
3879f11ffb7Safresh1#pod     specific peer address, regardless of the URL of the request.  This will
3889f11ffb7Safresh1#pod     include any redirections!  This options should be used with extreme
389b46d8ef2Safresh1#pod     caution (e.g. debugging or very special circumstances). It can be given as
390b46d8ef2Safresh1#pod     either a scalar or a code reference that will receive the hostname and
391b46d8ef2Safresh1#pod     whose response will be taken as the address.
392b8851fccSafresh1#pod
393b8851fccSafresh1#pod The C<Host> header is generated from the URL in accordance with RFC 2616.  It
394b8851fccSafresh1#pod is a fatal error to specify C<Host> in the C<headers> option.  Other headers
395b8851fccSafresh1#pod may be ignored or overwritten if necessary for transport compliance.
396b8851fccSafresh1#pod
397b8851fccSafresh1#pod If the C<content> option is a code reference, it will be called iteratively
398b8851fccSafresh1#pod to provide the content body of the request.  It should return the empty
399b8851fccSafresh1#pod string or undef when the iterator is exhausted.
400b8851fccSafresh1#pod
401b8851fccSafresh1#pod If the C<content> option is the empty string, no C<content-type> or
402b8851fccSafresh1#pod C<content-length> headers will be generated.
403b8851fccSafresh1#pod
404b8851fccSafresh1#pod If the C<data_callback> option is provided, it will be called iteratively until
405b8851fccSafresh1#pod the entire response body is received.  The first argument will be a string
406b8851fccSafresh1#pod containing a chunk of the response body, the second argument will be the
407b8851fccSafresh1#pod in-progress response hash reference, as described below.  (This allows
408b8851fccSafresh1#pod customizing the action of the callback based on the C<status> or C<headers>
409b8851fccSafresh1#pod received prior to the content body.)
410b8851fccSafresh1#pod
411e0680481Safresh1#pod Content data in the request/response is handled as "raw bytes".  Any
412e0680481Safresh1#pod encoding/decoding (with associated headers) are the responsibility of the
413e0680481Safresh1#pod caller.
414e0680481Safresh1#pod
415b8851fccSafresh1#pod The C<request> method returns a hashref containing the response.  The hashref
416b8851fccSafresh1#pod will have the following keys:
417b8851fccSafresh1#pod
418b8851fccSafresh1#pod =for :list
419b8851fccSafresh1#pod * C<success> —
420b8851fccSafresh1#pod     Boolean indicating whether the operation returned a 2XX status code
421b8851fccSafresh1#pod * C<url> —
422b8851fccSafresh1#pod     URL that provided the response. This is the URL of the request unless
423b8851fccSafresh1#pod     there were redirections, in which case it is the last URL queried
424b8851fccSafresh1#pod     in a redirection chain
425b8851fccSafresh1#pod * C<status> —
426b8851fccSafresh1#pod     The HTTP status code of the response
427b8851fccSafresh1#pod * C<reason> —
428b8851fccSafresh1#pod     The response phrase returned by the server
429b8851fccSafresh1#pod * C<content> —
430b8851fccSafresh1#pod     The body of the response.  If the response does not have any content
431b8851fccSafresh1#pod     or if a data callback is provided to consume the response body,
432b8851fccSafresh1#pod     this will be the empty string
433b8851fccSafresh1#pod * C<headers> —
434b8851fccSafresh1#pod     A hashref of header fields.  All header field names will be normalized
435b8851fccSafresh1#pod     to be lower case. If a header is repeated, the value will be an arrayref;
436b8851fccSafresh1#pod     it will otherwise be a scalar string containing the value
437b46d8ef2Safresh1#pod * C<protocol> -
438b46d8ef2Safresh1#pod     If this field exists, it is the protocol of the response
439b46d8ef2Safresh1#pod     such as HTTP/1.0 or HTTP/1.1
4409f11ffb7Safresh1#pod * C<redirects>
4419f11ffb7Safresh1#pod     If this field exists, it is an arrayref of response hash references from
4429f11ffb7Safresh1#pod     redirects in the same order that redirections occurred.  If it does
4439f11ffb7Safresh1#pod     not exist, then no redirections occurred.
444b8851fccSafresh1#pod
445eac174f2Safresh1#pod On an error during the execution of the request, the C<status> field will
446eac174f2Safresh1#pod contain 599, and the C<content> field will contain the text of the error.
447b8851fccSafresh1#pod
448b8851fccSafresh1#pod =cut
449898184e3Ssthen
450898184e3Ssthenmy %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/;
451898184e3Ssthen
452898184e3Ssthensub request {
453898184e3Ssthen    my ($self, $method, $url, $args) = @_;
454898184e3Ssthen    @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
4559f11ffb7Safresh1      or _croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/ . "\n");
456898184e3Ssthen    $args ||= {}; # we keep some state in this during _request
457898184e3Ssthen
458898184e3Ssthen    # RFC 2616 Section 8.1.4 mandates a single retry on broken socket
459898184e3Ssthen    my $response;
460898184e3Ssthen    for ( 0 .. 1 ) {
461898184e3Ssthen        $response = eval { $self->_request($method, $url, $args) };
462898184e3Ssthen        last unless $@ && $idempotent{$method}
463eac174f2Safresh1            && $@ =~ m{^(?:Socket closed|Unexpected end|SSL read error)};
464898184e3Ssthen    }
465898184e3Ssthen
4666fb12b70Safresh1    if (my $e = $@) {
4676fb12b70Safresh1        # maybe we got a response hash thrown from somewhere deep
4686fb12b70Safresh1        if ( ref $e eq 'HASH' && exists $e->{status} ) {
4699f11ffb7Safresh1            $e->{redirects} = delete $args->{_redirects} if @{ $args->{_redirects} || []};
4706fb12b70Safresh1            return $e;
4716fb12b70Safresh1        }
4726fb12b70Safresh1
4736fb12b70Safresh1        # otherwise, stringify it
4746fb12b70Safresh1        $e = "$e";
475898184e3Ssthen        $response = {
47691f110e0Safresh1            url     => $url,
477898184e3Ssthen            success => q{},
478898184e3Ssthen            status  => 599,
479898184e3Ssthen            reason  => 'Internal Exception',
480898184e3Ssthen            content => $e,
481898184e3Ssthen            headers => {
482898184e3Ssthen                'content-type'   => 'text/plain',
483898184e3Ssthen                'content-length' => length $e,
4849f11ffb7Safresh1            },
4859f11ffb7Safresh1            ( @{$args->{_redirects} || []} ? (redirects => delete $args->{_redirects}) : () ),
486898184e3Ssthen        };
487898184e3Ssthen    }
488898184e3Ssthen    return $response;
489898184e3Ssthen}
490898184e3Ssthen
491b8851fccSafresh1#pod =method www_form_urlencode
492b8851fccSafresh1#pod
493b8851fccSafresh1#pod     $params = $http->www_form_urlencode( $data );
494b8851fccSafresh1#pod     $response = $http->get("http://example.com/query?$params");
495b8851fccSafresh1#pod
496b8851fccSafresh1#pod This method converts the key/value pairs from a data hash or array reference
497b8851fccSafresh1#pod into a C<x-www-form-urlencoded> string.  The keys and values from the data
498b8851fccSafresh1#pod reference will be UTF-8 encoded and escaped per RFC 3986.  If a value is an
499b8851fccSafresh1#pod array reference, the key will be repeated with each of the values of the array
500b8851fccSafresh1#pod reference.  If data is provided as a hash reference, the key/value pairs in the
501b8851fccSafresh1#pod resulting string will be sorted by key and value for consistent ordering.
502b8851fccSafresh1#pod
503b8851fccSafresh1#pod =cut
504898184e3Ssthen
505898184e3Ssthensub www_form_urlencode {
506898184e3Ssthen    my ($self, $data) = @_;
507898184e3Ssthen    (@_ == 2 && ref $data)
5089f11ffb7Safresh1        or _croak(q/Usage: $http->www_form_urlencode(DATAREF)/ . "\n");
509898184e3Ssthen    (ref $data eq 'HASH' || ref $data eq 'ARRAY')
5109f11ffb7Safresh1        or _croak("form data must be a hash or array reference\n");
511898184e3Ssthen
512898184e3Ssthen    my @params = ref $data eq 'HASH' ? %$data : @$data;
513898184e3Ssthen    @params % 2 == 0
5149f11ffb7Safresh1        or _croak("form data reference must have an even number of terms\n");
515898184e3Ssthen
516898184e3Ssthen    my @terms;
517898184e3Ssthen    while( @params ) {
518898184e3Ssthen        my ($key, $value) = splice(@params, 0, 2);
519eac174f2Safresh1        _croak("form data keys must not be undef")
520eac174f2Safresh1            if !defined($key);
521898184e3Ssthen        if ( ref $value eq 'ARRAY' ) {
522898184e3Ssthen            unshift @params, map { $key => $_ } @$value;
523898184e3Ssthen        }
524898184e3Ssthen        else {
525898184e3Ssthen            push @terms, join("=", map { $self->_uri_escape($_) } $key, $value);
526898184e3Ssthen        }
527898184e3Ssthen    }
528898184e3Ssthen
5296fb12b70Safresh1    return join("&", (ref $data eq 'ARRAY') ? (@terms) : (sort @terms) );
530898184e3Ssthen}
531898184e3Ssthen
532b8851fccSafresh1#pod =method can_ssl
533b8851fccSafresh1#pod
534b8851fccSafresh1#pod     $ok         = HTTP::Tiny->can_ssl;
535b8851fccSafresh1#pod     ($ok, $why) = HTTP::Tiny->can_ssl;
536b8851fccSafresh1#pod     ($ok, $why) = $http->can_ssl;
537b8851fccSafresh1#pod
538b8851fccSafresh1#pod Indicates if SSL support is available.  When called as a class object, it
539b8851fccSafresh1#pod checks for the correct version of L<Net::SSLeay> and L<IO::Socket::SSL>.
540b8851fccSafresh1#pod When called as an object methods, if C<SSL_verify> is true or if C<SSL_verify_mode>
541b8851fccSafresh1#pod is set in C<SSL_options>, it checks that a CA file is available.
542b8851fccSafresh1#pod
543b8851fccSafresh1#pod In scalar context, returns a boolean indicating if SSL is available.
544b8851fccSafresh1#pod In list context, returns the boolean and a (possibly multi-line) string of
545b8851fccSafresh1#pod errors indicating why SSL isn't available.
546b8851fccSafresh1#pod
547b8851fccSafresh1#pod =cut
548b8851fccSafresh1
549b8851fccSafresh1sub can_ssl {
550b8851fccSafresh1    my ($self) = @_;
551b8851fccSafresh1
552b8851fccSafresh1    my($ok, $reason) = (1, '');
553b8851fccSafresh1
554b8851fccSafresh1    # Need IO::Socket::SSL 1.42 for SSL_create_ctx_callback
555b8851fccSafresh1    local @INC = @INC;
556b8851fccSafresh1    pop @INC if $INC[-1] eq '.';
557b8851fccSafresh1    unless (eval {require IO::Socket::SSL; IO::Socket::SSL->VERSION(1.42)}) {
558b8851fccSafresh1        $ok = 0;
559b8851fccSafresh1        $reason .= qq/IO::Socket::SSL 1.42 must be installed for https support\n/;
560b8851fccSafresh1    }
561b8851fccSafresh1
562b8851fccSafresh1    # Need Net::SSLeay 1.49 for MODE_AUTO_RETRY
563b8851fccSafresh1    unless (eval {require Net::SSLeay; Net::SSLeay->VERSION(1.49)}) {
564b8851fccSafresh1        $ok = 0;
565b8851fccSafresh1        $reason .= qq/Net::SSLeay 1.49 must be installed for https support\n/;
566b8851fccSafresh1    }
567b8851fccSafresh1
568b8851fccSafresh1    # If an object, check that SSL config lets us get a CA if necessary
569b8851fccSafresh1    if ( ref($self) && ( $self->{verify_SSL} || $self->{SSL_options}{SSL_verify_mode} ) ) {
570b8851fccSafresh1        my $handle = HTTP::Tiny::Handle->new(
571b8851fccSafresh1            SSL_options => $self->{SSL_options},
572b8851fccSafresh1            verify_SSL  => $self->{verify_SSL},
573b8851fccSafresh1        );
574b8851fccSafresh1        unless ( eval { $handle->_find_CA_file; 1 } ) {
575b8851fccSafresh1            $ok = 0;
576b8851fccSafresh1            $reason .= "$@";
577b8851fccSafresh1        }
578b8851fccSafresh1    }
579b8851fccSafresh1
580b8851fccSafresh1    wantarray ? ($ok, $reason) : $ok;
581b8851fccSafresh1}
582b8851fccSafresh1
5839f11ffb7Safresh1#pod =method connected
5849f11ffb7Safresh1#pod
5859f11ffb7Safresh1#pod     $host = $http->connected;
5869f11ffb7Safresh1#pod     ($host, $port) = $http->connected;
5879f11ffb7Safresh1#pod
5889f11ffb7Safresh1#pod Indicates if a connection to a peer is being kept alive, per the C<keep_alive>
5899f11ffb7Safresh1#pod option.
5909f11ffb7Safresh1#pod
5919f11ffb7Safresh1#pod In scalar context, returns the peer host and port, joined with a colon, or
5929f11ffb7Safresh1#pod C<undef> (if no peer is connected).
5939f11ffb7Safresh1#pod In list context, returns the peer host and port or an empty list (if no peer
5949f11ffb7Safresh1#pod is connected).
5959f11ffb7Safresh1#pod
5969f11ffb7Safresh1#pod B<Note>: This method cannot reliably be used to discover whether the remote
5979f11ffb7Safresh1#pod host has closed its end of the socket.
5989f11ffb7Safresh1#pod
5999f11ffb7Safresh1#pod =cut
6009f11ffb7Safresh1
6019f11ffb7Safresh1sub connected {
6029f11ffb7Safresh1    my ($self) = @_;
6039f11ffb7Safresh1
604eac174f2Safresh1    if ( $self->{handle} ) {
605eac174f2Safresh1        return $self->{handle}->connected;
6069f11ffb7Safresh1    }
6079f11ffb7Safresh1    return;
6089f11ffb7Safresh1}
6099f11ffb7Safresh1
610898184e3Ssthen#--------------------------------------------------------------------------#
611898184e3Ssthen# private methods
612898184e3Ssthen#--------------------------------------------------------------------------#
613898184e3Ssthen
614898184e3Ssthenmy %DefaultPort = (
615898184e3Ssthen    http => 80,
616898184e3Ssthen    https => 443,
617898184e3Ssthen);
618898184e3Ssthen
6196fb12b70Safresh1sub _agent {
6206fb12b70Safresh1    my $class = ref($_[0]) || $_[0];
6216fb12b70Safresh1    (my $default_agent = $class) =~ s{::}{-}g;
622eac174f2Safresh1    my $version = $class->VERSION;
623eac174f2Safresh1    $default_agent .= "/$version" if defined $version;
624eac174f2Safresh1    return $default_agent;
6256fb12b70Safresh1}
6266fb12b70Safresh1
627898184e3Ssthensub _request {
628898184e3Ssthen    my ($self, $method, $url, $args) = @_;
629898184e3Ssthen
6306fb12b70Safresh1    my ($scheme, $host, $port, $path_query, $auth) = $self->_split_url($url);
631898184e3Ssthen
632eac174f2Safresh1    if ($scheme ne 'http' && $scheme ne 'https') {
633eac174f2Safresh1      die(qq/Unsupported URL scheme '$scheme'\n/);
634eac174f2Safresh1    }
635eac174f2Safresh1
636898184e3Ssthen    my $request = {
637898184e3Ssthen        method    => $method,
638898184e3Ssthen        scheme    => $scheme,
6396fb12b70Safresh1        host      => $host,
640b8851fccSafresh1        port      => $port,
641898184e3Ssthen        host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
642898184e3Ssthen        uri       => $path_query,
643898184e3Ssthen        headers   => {},
644898184e3Ssthen    };
645898184e3Ssthen
6469f11ffb7Safresh1    my $peer = $args->{peer} || $host;
6479f11ffb7Safresh1
648b46d8ef2Safresh1    # Allow 'peer' to be a coderef.
649b46d8ef2Safresh1    if ('CODE' eq ref $peer) {
650b46d8ef2Safresh1        $peer = $peer->($host);
651b46d8ef2Safresh1    }
652b46d8ef2Safresh1
6536fb12b70Safresh1    # We remove the cached handle so it is not reused in the case of redirect.
6546fb12b70Safresh1    # If all is well, it will be recached at the end of _request.  We only
6556fb12b70Safresh1    # reuse for the same scheme, host and port
6566fb12b70Safresh1    my $handle = delete $self->{handle};
6576fb12b70Safresh1    if ( $handle ) {
6589f11ffb7Safresh1        unless ( $handle->can_reuse( $scheme, $host, $port, $peer ) ) {
6596fb12b70Safresh1            $handle->close;
6606fb12b70Safresh1            undef $handle;
661898184e3Ssthen        }
662898184e3Ssthen    }
6639f11ffb7Safresh1    $handle ||= $self->_open_handle( $request, $scheme, $host, $port, $peer );
664898184e3Ssthen
6656fb12b70Safresh1    $self->_prepare_headers_and_cb($request, $args, $url, $auth);
666898184e3Ssthen    $handle->write_request($request);
667898184e3Ssthen
668898184e3Ssthen    my $response;
669898184e3Ssthen    do { $response = $handle->read_response_header }
670898184e3Ssthen        until (substr($response->{status},0,1) ne '1');
671898184e3Ssthen
6726fb12b70Safresh1    $self->_update_cookie_jar( $url, $response ) if $self->{cookie_jar};
6739f11ffb7Safresh1    my @redir_args = $self->_maybe_redirect($request, $response, $args);
674898184e3Ssthen
6756fb12b70Safresh1    my $known_message_length;
676898184e3Ssthen    if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) {
677898184e3Ssthen        # response has no message body
6786fb12b70Safresh1        $known_message_length = 1;
679898184e3Ssthen    }
680898184e3Ssthen    else {
6819f11ffb7Safresh1        # Ignore any data callbacks during redirection.
6829f11ffb7Safresh1        my $cb_args = @redir_args ? +{} : $args;
6839f11ffb7Safresh1        my $data_cb = $self->_prepare_data_cb($response, $cb_args);
6846fb12b70Safresh1        $known_message_length = $handle->read_body($data_cb, $response);
685898184e3Ssthen    }
686898184e3Ssthen
6876fb12b70Safresh1    if ( $self->{keep_alive}
688eac174f2Safresh1        && $handle->connected
6896fb12b70Safresh1        && $known_message_length
6906fb12b70Safresh1        && $response->{protocol} eq 'HTTP/1.1'
6916fb12b70Safresh1        && ($response->{headers}{connection} || '') ne 'close'
6926fb12b70Safresh1    ) {
6936fb12b70Safresh1        $self->{handle} = $handle;
6946fb12b70Safresh1    }
6956fb12b70Safresh1    else {
696898184e3Ssthen        $handle->close;
6976fb12b70Safresh1    }
6986fb12b70Safresh1
699898184e3Ssthen    $response->{success} = substr( $response->{status}, 0, 1 ) eq '2';
70091f110e0Safresh1    $response->{url} = $url;
7019f11ffb7Safresh1
7029f11ffb7Safresh1    # Push the current response onto the stack of redirects if redirecting.
7039f11ffb7Safresh1    if (@redir_args) {
7049f11ffb7Safresh1        push @{$args->{_redirects}}, $response;
7059f11ffb7Safresh1        return $self->_request(@redir_args, $args);
7069f11ffb7Safresh1    }
7079f11ffb7Safresh1
7089f11ffb7Safresh1    # Copy the stack of redirects into the response before returning.
7099f11ffb7Safresh1    $response->{redirects} = delete $args->{_redirects}
7109f11ffb7Safresh1      if @{$args->{_redirects}};
711898184e3Ssthen    return $response;
712898184e3Ssthen}
713898184e3Ssthen
7146fb12b70Safresh1sub _open_handle {
7159f11ffb7Safresh1    my ($self, $request, $scheme, $host, $port, $peer) = @_;
7166fb12b70Safresh1
7176fb12b70Safresh1    my $handle  = HTTP::Tiny::Handle->new(
7186fb12b70Safresh1        timeout         => $self->{timeout},
7196fb12b70Safresh1        SSL_options     => $self->{SSL_options},
7206fb12b70Safresh1        verify_SSL      => $self->{verify_SSL},
7216fb12b70Safresh1        local_address   => $self->{local_address},
7226fb12b70Safresh1        keep_alive      => $self->{keep_alive}
7236fb12b70Safresh1    );
7246fb12b70Safresh1
7256fb12b70Safresh1    if ($self->{_has_proxy}{$scheme} && ! grep { $host =~ /\Q$_\E$/ } @{$self->{no_proxy}}) {
7266fb12b70Safresh1        return $self->_proxy_connect( $request, $handle );
7276fb12b70Safresh1    }
7286fb12b70Safresh1    else {
7299f11ffb7Safresh1        return $handle->connect($scheme, $host, $port, $peer);
7306fb12b70Safresh1    }
7316fb12b70Safresh1}
7326fb12b70Safresh1
7336fb12b70Safresh1sub _proxy_connect {
7346fb12b70Safresh1    my ($self, $request, $handle) = @_;
7356fb12b70Safresh1
7366fb12b70Safresh1    my @proxy_vars;
7376fb12b70Safresh1    if ( $request->{scheme} eq 'https' ) {
7389f11ffb7Safresh1        _croak(qq{No https_proxy defined}) unless $self->{https_proxy};
7396fb12b70Safresh1        @proxy_vars = $self->_split_proxy( https_proxy => $self->{https_proxy} );
7406fb12b70Safresh1        if ( $proxy_vars[0] eq 'https' ) {
7419f11ffb7Safresh1            _croak(qq{Can't proxy https over https: $request->{uri} via $self->{https_proxy}});
7426fb12b70Safresh1        }
7436fb12b70Safresh1    }
7446fb12b70Safresh1    else {
7459f11ffb7Safresh1        _croak(qq{No http_proxy defined}) unless $self->{http_proxy};
7466fb12b70Safresh1        @proxy_vars = $self->_split_proxy( http_proxy => $self->{http_proxy} );
7476fb12b70Safresh1    }
7486fb12b70Safresh1
7496fb12b70Safresh1    my ($p_scheme, $p_host, $p_port, $p_auth) = @proxy_vars;
7506fb12b70Safresh1
7516fb12b70Safresh1    if ( length $p_auth && ! defined $request->{headers}{'proxy-authorization'} ) {
7526fb12b70Safresh1        $self->_add_basic_auth_header( $request, 'proxy-authorization' => $p_auth );
7536fb12b70Safresh1    }
7546fb12b70Safresh1
7559f11ffb7Safresh1    $handle->connect($p_scheme, $p_host, $p_port, $p_host);
7566fb12b70Safresh1
7576fb12b70Safresh1    if ($request->{scheme} eq 'https') {
7586fb12b70Safresh1        $self->_create_proxy_tunnel( $request, $handle );
7596fb12b70Safresh1    }
7606fb12b70Safresh1    else {
7616fb12b70Safresh1        # non-tunneled proxy requires absolute URI
7626fb12b70Safresh1        $request->{uri} = "$request->{scheme}://$request->{host_port}$request->{uri}";
7636fb12b70Safresh1    }
7646fb12b70Safresh1
7656fb12b70Safresh1    return $handle;
7666fb12b70Safresh1}
7676fb12b70Safresh1
7686fb12b70Safresh1sub _split_proxy {
7696fb12b70Safresh1    my ($self, $type, $proxy) = @_;
7706fb12b70Safresh1
7716fb12b70Safresh1    my ($scheme, $host, $port, $path_query, $auth) = eval { $self->_split_url($proxy) };
7726fb12b70Safresh1
7736fb12b70Safresh1    unless(
7746fb12b70Safresh1        defined($scheme) && length($scheme) && length($host) && length($port)
7756fb12b70Safresh1        && $path_query eq '/'
7766fb12b70Safresh1    ) {
7779f11ffb7Safresh1        _croak(qq{$type URL must be in format http[s]://[auth@]<host>:<port>/\n});
7786fb12b70Safresh1    }
7796fb12b70Safresh1
7806fb12b70Safresh1    return ($scheme, $host, $port, $auth);
7816fb12b70Safresh1}
7826fb12b70Safresh1
7836fb12b70Safresh1sub _create_proxy_tunnel {
7846fb12b70Safresh1    my ($self, $request, $handle) = @_;
7856fb12b70Safresh1
7866fb12b70Safresh1    $handle->_assert_ssl;
7876fb12b70Safresh1
7886fb12b70Safresh1    my $agent = exists($request->{headers}{'user-agent'})
7896fb12b70Safresh1        ? $request->{headers}{'user-agent'} : $self->{agent};
7906fb12b70Safresh1
7916fb12b70Safresh1    my $connect_request = {
7926fb12b70Safresh1        method    => 'CONNECT',
793b8851fccSafresh1        uri       => "$request->{host}:$request->{port}",
7946fb12b70Safresh1        headers   => {
795b8851fccSafresh1            host => "$request->{host}:$request->{port}",
7966fb12b70Safresh1            'user-agent' => $agent,
7976fb12b70Safresh1        }
7986fb12b70Safresh1    };
7996fb12b70Safresh1
8006fb12b70Safresh1    if ( $request->{headers}{'proxy-authorization'} ) {
8016fb12b70Safresh1        $connect_request->{headers}{'proxy-authorization'} =
8026fb12b70Safresh1            delete $request->{headers}{'proxy-authorization'};
8036fb12b70Safresh1    }
8046fb12b70Safresh1
8056fb12b70Safresh1    $handle->write_request($connect_request);
8066fb12b70Safresh1    my $response;
8076fb12b70Safresh1    do { $response = $handle->read_response_header }
8086fb12b70Safresh1        until (substr($response->{status},0,1) ne '1');
8096fb12b70Safresh1
8106fb12b70Safresh1    # if CONNECT failed, throw the response so it will be
8116fb12b70Safresh1    # returned from the original request() method;
8126fb12b70Safresh1    unless (substr($response->{status},0,1) eq '2') {
8136fb12b70Safresh1        die $response;
8146fb12b70Safresh1    }
8156fb12b70Safresh1
8166fb12b70Safresh1    # tunnel established, so start SSL handshake
8176fb12b70Safresh1    $handle->start_ssl( $request->{host} );
8186fb12b70Safresh1
8196fb12b70Safresh1    return;
8206fb12b70Safresh1}
8216fb12b70Safresh1
822898184e3Ssthensub _prepare_headers_and_cb {
8236fb12b70Safresh1    my ($self, $request, $args, $url, $auth) = @_;
824898184e3Ssthen
825898184e3Ssthen    for ($self->{default_headers}, $args->{headers}) {
826898184e3Ssthen        next unless defined;
827898184e3Ssthen        while (my ($k, $v) = each %$_) {
828898184e3Ssthen            $request->{headers}{lc $k} = $v;
8299f11ffb7Safresh1            $request->{header_case}{lc $k} = $k;
830898184e3Ssthen        }
831898184e3Ssthen    }
832b8851fccSafresh1
833b8851fccSafresh1    if (exists $request->{headers}{'host'}) {
834b8851fccSafresh1        die(qq/The 'Host' header must not be provided as header option\n/);
835b8851fccSafresh1    }
836b8851fccSafresh1
837898184e3Ssthen    $request->{headers}{'host'}         = $request->{host_port};
838898184e3Ssthen    $request->{headers}{'user-agent'} ||= $self->{agent};
8396fb12b70Safresh1    $request->{headers}{'connection'}   = "close"
8406fb12b70Safresh1        unless $self->{keep_alive};
841898184e3Ssthen
842eac174f2Safresh1    # Some servers error on an empty-body PUT/POST without a content-length
843eac174f2Safresh1    if ( $request->{method} eq 'PUT' || $request->{method} eq 'POST' ) {
844eac174f2Safresh1        if (!defined($args->{content}) || !length($args->{content}) ) {
845eac174f2Safresh1            $request->{headers}{'content-length'} = 0;
846eac174f2Safresh1        }
847eac174f2Safresh1    }
848eac174f2Safresh1
849898184e3Ssthen    if ( defined $args->{content} ) {
850898184e3Ssthen        if ( ref $args->{content} eq 'CODE' ) {
851eac174f2Safresh1            if ( exists $request->{'content-length'} && $request->{'content-length'} == 0 ) {
852eac174f2Safresh1                $request->{cb} = sub { "" };
853eac174f2Safresh1            }
854eac174f2Safresh1            else {
8556fb12b70Safresh1                $request->{headers}{'content-type'} ||= "application/octet-stream";
856898184e3Ssthen                $request->{headers}{'transfer-encoding'} = 'chunked'
857eac174f2Safresh1                  unless exists $request->{headers}{'content-length'}
858898184e3Ssthen                  || $request->{headers}{'transfer-encoding'};
859898184e3Ssthen                $request->{cb} = $args->{content};
860898184e3Ssthen            }
861eac174f2Safresh1        }
8626fb12b70Safresh1        elsif ( length $args->{content} ) {
863898184e3Ssthen            my $content = $args->{content};
864898184e3Ssthen            if ( $] ge '5.008' ) {
865898184e3Ssthen                utf8::downgrade($content, 1)
866898184e3Ssthen                    or die(qq/Wide character in request message body\n/);
867898184e3Ssthen            }
8686fb12b70Safresh1            $request->{headers}{'content-type'} ||= "application/octet-stream";
869898184e3Ssthen            $request->{headers}{'content-length'} = length $content
870898184e3Ssthen              unless $request->{headers}{'content-length'}
871898184e3Ssthen                  || $request->{headers}{'transfer-encoding'};
872898184e3Ssthen            $request->{cb} = sub { substr $content, 0, length $content, '' };
873898184e3Ssthen        }
874898184e3Ssthen        $request->{trailer_cb} = $args->{trailer_callback}
875898184e3Ssthen            if ref $args->{trailer_callback} eq 'CODE';
876898184e3Ssthen    }
8776fb12b70Safresh1
8786fb12b70Safresh1    ### If we have a cookie jar, then maybe add relevant cookies
8796fb12b70Safresh1    if ( $self->{cookie_jar} ) {
8806fb12b70Safresh1        my $cookies = $self->cookie_jar->cookie_header( $url );
8816fb12b70Safresh1        $request->{headers}{cookie} = $cookies if length $cookies;
8826fb12b70Safresh1    }
8836fb12b70Safresh1
8846fb12b70Safresh1    # if we have Basic auth parameters, add them
8856fb12b70Safresh1    if ( length $auth && ! defined $request->{headers}{authorization} ) {
8866fb12b70Safresh1        $self->_add_basic_auth_header( $request, 'authorization' => $auth );
8876fb12b70Safresh1    }
8886fb12b70Safresh1
8896fb12b70Safresh1    return;
8906fb12b70Safresh1}
8916fb12b70Safresh1
8926fb12b70Safresh1sub _add_basic_auth_header {
8936fb12b70Safresh1    my ($self, $request, $header, $auth) = @_;
8946fb12b70Safresh1    require MIME::Base64;
8956fb12b70Safresh1    $request->{headers}{$header} =
8966fb12b70Safresh1        "Basic " . MIME::Base64::encode_base64($auth, "");
897898184e3Ssthen    return;
898898184e3Ssthen}
899898184e3Ssthen
900898184e3Ssthensub _prepare_data_cb {
901898184e3Ssthen    my ($self, $response, $args) = @_;
902898184e3Ssthen    my $data_cb = $args->{data_callback};
903898184e3Ssthen    $response->{content} = '';
904898184e3Ssthen
905898184e3Ssthen    if (!$data_cb || $response->{status} !~ /^2/) {
906898184e3Ssthen        if (defined $self->{max_size}) {
907898184e3Ssthen            $data_cb = sub {
908898184e3Ssthen                $_[1]->{content} .= $_[0];
909898184e3Ssthen                die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/)
910898184e3Ssthen                  if length $_[1]->{content} > $self->{max_size};
911898184e3Ssthen            };
912898184e3Ssthen        }
913898184e3Ssthen        else {
914898184e3Ssthen            $data_cb = sub { $_[1]->{content} .= $_[0] };
915898184e3Ssthen        }
916898184e3Ssthen    }
917898184e3Ssthen    return $data_cb;
918898184e3Ssthen}
919898184e3Ssthen
9206fb12b70Safresh1sub _update_cookie_jar {
9216fb12b70Safresh1    my ($self, $url, $response) = @_;
9226fb12b70Safresh1
9236fb12b70Safresh1    my $cookies = $response->{headers}->{'set-cookie'};
9246fb12b70Safresh1    return unless defined $cookies;
9256fb12b70Safresh1
9266fb12b70Safresh1    my @cookies = ref $cookies ? @$cookies : $cookies;
9276fb12b70Safresh1
9286fb12b70Safresh1    $self->cookie_jar->add( $url, $_ ) for @cookies;
9296fb12b70Safresh1
9306fb12b70Safresh1    return;
9316fb12b70Safresh1}
9326fb12b70Safresh1
9336fb12b70Safresh1sub _validate_cookie_jar {
9346fb12b70Safresh1    my ($class, $jar) = @_;
9356fb12b70Safresh1
9366fb12b70Safresh1    # duck typing
9376fb12b70Safresh1    for my $method ( qw/add cookie_header/ ) {
9389f11ffb7Safresh1        _croak(qq/Cookie jar must provide the '$method' method\n/)
9396fb12b70Safresh1            unless ref($jar) && ref($jar)->can($method);
9406fb12b70Safresh1    }
9416fb12b70Safresh1
9426fb12b70Safresh1    return;
9436fb12b70Safresh1}
9446fb12b70Safresh1
945898184e3Ssthensub _maybe_redirect {
946898184e3Ssthen    my ($self, $request, $response, $args) = @_;
947898184e3Ssthen    my $headers = $response->{headers};
948898184e3Ssthen    my ($status, $method) = ($response->{status}, $request->{method});
9499f11ffb7Safresh1    $args->{_redirects} ||= [];
9509f11ffb7Safresh1
951b8851fccSafresh1    if (($status eq '303' or ($status =~ /^30[1278]/ && $method =~ /^GET|HEAD$/))
952898184e3Ssthen        and $headers->{location}
9539f11ffb7Safresh1        and @{$args->{_redirects}} < $self->{max_redirect}
954898184e3Ssthen    ) {
955898184e3Ssthen        my $location = ($headers->{location} =~ /^\//)
956898184e3Ssthen            ? "$request->{scheme}://$request->{host_port}$headers->{location}"
957898184e3Ssthen            : $headers->{location} ;
958898184e3Ssthen        return (($status eq '303' ? 'GET' : $method), $location);
959898184e3Ssthen    }
960898184e3Ssthen    return;
961898184e3Ssthen}
962898184e3Ssthen
963898184e3Ssthensub _split_url {
964898184e3Ssthen    my $url = pop;
965898184e3Ssthen
966898184e3Ssthen    # URI regex adapted from the URI module
967b8851fccSafresh1    my ($scheme, $host, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)>
968898184e3Ssthen      or die(qq/Cannot parse URL: '$url'\n/);
969898184e3Ssthen
970898184e3Ssthen    $scheme     = lc $scheme;
971898184e3Ssthen    $path_query = "/$path_query" unless $path_query =~ m<\A/>;
972898184e3Ssthen
973b8851fccSafresh1    my $auth = '';
974b8851fccSafresh1    if ( (my $i = index $host, '@') != -1 ) {
975b8851fccSafresh1        # user:pass@host
976b8851fccSafresh1        $auth = substr $host, 0, $i, ''; # take up to the @ for auth
977b8851fccSafresh1        substr $host, 0, 1, '';          # knock the @ off the host
978b8851fccSafresh1
9796fb12b70Safresh1        # userinfo might be percent escaped, so recover real auth info
9806fb12b70Safresh1        $auth =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
9816fb12b70Safresh1    }
982b8851fccSafresh1    my $port = $host =~ s/:(\d*)\z// && length $1 ? $1
983b8851fccSafresh1             : $scheme eq 'http'                  ? 80
984b8851fccSafresh1             : $scheme eq 'https'                 ? 443
985b8851fccSafresh1             : undef;
986898184e3Ssthen
987b8851fccSafresh1    return ($scheme, (length $host ? lc $host : "localhost") , $port, $path_query, $auth);
988898184e3Ssthen}
989898184e3Ssthen
990898184e3Ssthen# Date conversions adapted from HTTP::Date
991898184e3Ssthenmy $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat";
992898184e3Ssthenmy $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
993898184e3Ssthensub _http_date {
994898184e3Ssthen    my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]);
995898184e3Ssthen    return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
996898184e3Ssthen        substr($DoW,$wday*4,3),
997898184e3Ssthen        $mday, substr($MoY,$mon*4,3), $year+1900,
998898184e3Ssthen        $hour, $min, $sec
999898184e3Ssthen    );
1000898184e3Ssthen}
1001898184e3Ssthen
1002898184e3Ssthensub _parse_http_date {
1003898184e3Ssthen    my ($self, $str) = @_;
1004898184e3Ssthen    require Time::Local;
1005898184e3Ssthen    my @tl_parts;
1006898184e3Ssthen    if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) {
1007898184e3Ssthen        @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
1008898184e3Ssthen    }
1009898184e3Ssthen    elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) {
1010898184e3Ssthen        @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
1011898184e3Ssthen    }
1012898184e3Ssthen    elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) {
1013898184e3Ssthen        @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6);
1014898184e3Ssthen    }
1015898184e3Ssthen    return eval {
1016898184e3Ssthen        my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1;
1017898184e3Ssthen        $t < 0 ? undef : $t;
1018898184e3Ssthen    };
1019898184e3Ssthen}
1020898184e3Ssthen
1021898184e3Ssthen# URI escaping adapted from URI::Escape
1022898184e3Ssthen# c.f. http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
1023898184e3Ssthen# perl 5.6 ready UTF-8 encoding adapted from JSON::PP
1024898184e3Ssthenmy %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255;
1025898184e3Ssthen$escapes{' '}="+";
1026898184e3Ssthenmy $unsafe_char = qr/[^A-Za-z0-9\-\._~]/;
1027898184e3Ssthen
1028898184e3Ssthensub _uri_escape {
1029898184e3Ssthen    my ($self, $str) = @_;
1030eac174f2Safresh1    return "" if !defined $str;
1031898184e3Ssthen    if ( $] ge '5.008' ) {
1032898184e3Ssthen        utf8::encode($str);
1033898184e3Ssthen    }
1034898184e3Ssthen    else {
1035898184e3Ssthen        $str = pack("U*", unpack("C*", $str)) # UTF-8 encode a byte string
1036898184e3Ssthen            if ( length $str == do { use bytes; length $str } );
1037898184e3Ssthen        $str = pack("C*", unpack("C*", $str)); # clear UTF-8 flag
1038898184e3Ssthen    }
1039b46d8ef2Safresh1    $str =~ s/($unsafe_char)/$escapes{$1}/g;
1040898184e3Ssthen    return $str;
1041898184e3Ssthen}
1042898184e3Ssthen
1043898184e3Ssthenpackage
1044898184e3Ssthen    HTTP::Tiny::Handle; # hide from PAUSE/indexers
1045898184e3Ssthenuse strict;
1046898184e3Ssthenuse warnings;
1047898184e3Ssthen
1048898184e3Ssthenuse Errno      qw[EINTR EPIPE];
1049898184e3Ssthenuse IO::Socket qw[SOCK_STREAM];
10509f11ffb7Safresh1use Socket     qw[SOL_SOCKET SO_KEEPALIVE];
1051898184e3Ssthen
10526fb12b70Safresh1# PERL_HTTP_TINY_IPV4_ONLY is a private environment variable to force old
10536fb12b70Safresh1# behavior if someone is unable to boostrap CPAN from a new perl install; it is
10546fb12b70Safresh1# not intended for general, per-client use and may be removed in the future
10556fb12b70Safresh1my $SOCKET_CLASS =
10566fb12b70Safresh1    $ENV{PERL_HTTP_TINY_IPV4_ONLY} ? 'IO::Socket::INET' :
1057eac174f2Safresh1    eval { require IO::Socket::IP; IO::Socket::IP->VERSION(0.32) } ? 'IO::Socket::IP' :
10586fb12b70Safresh1    'IO::Socket::INET';
10596fb12b70Safresh1
1060898184e3Ssthensub BUFSIZE () { 32768 } ## no critic
1061898184e3Ssthen
1062898184e3Ssthenmy $Printable = sub {
1063898184e3Ssthen    local $_ = shift;
1064898184e3Ssthen    s/\r/\\r/g;
1065898184e3Ssthen    s/\n/\\n/g;
1066898184e3Ssthen    s/\t/\\t/g;
1067898184e3Ssthen    s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
1068898184e3Ssthen    $_;
1069898184e3Ssthen};
1070898184e3Ssthen
1071898184e3Ssthenmy $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
10729f11ffb7Safresh1my $Field_Content = qr/[[:print:]]+ (?: [\x20\x09]+ [[:print:]]+ )*/x;
1073898184e3Ssthen
1074898184e3Ssthensub new {
1075898184e3Ssthen    my ($class, %args) = @_;
1076898184e3Ssthen    return bless {
1077898184e3Ssthen        rbuf             => '',
1078898184e3Ssthen        timeout          => 60,
1079898184e3Ssthen        max_line_size    => 16384,
1080898184e3Ssthen        max_header_lines => 64,
1081e0680481Safresh1        verify_SSL       => HTTP::Tiny::_verify_SSL_default(),
108291f110e0Safresh1        SSL_options      => {},
1083898184e3Ssthen        %args
1084898184e3Ssthen    }, $class;
1085898184e3Ssthen}
1086898184e3Ssthen
10879f11ffb7Safresh1sub timeout {
10889f11ffb7Safresh1    my ($self, $timeout) = @_;
10899f11ffb7Safresh1    if ( @_ > 1 ) {
10909f11ffb7Safresh1        $self->{timeout} = $timeout;
10919f11ffb7Safresh1        if ( $self->{fh} && $self->{fh}->can('timeout') ) {
10929f11ffb7Safresh1            $self->{fh}->timeout($timeout);
10939f11ffb7Safresh1        }
10949f11ffb7Safresh1    }
10959f11ffb7Safresh1    return $self->{timeout};
10969f11ffb7Safresh1}
10979f11ffb7Safresh1
1098898184e3Ssthensub connect {
10999f11ffb7Safresh1    @_ == 5 || die(q/Usage: $handle->connect(scheme, host, port, peer)/ . "\n");
11009f11ffb7Safresh1    my ($self, $scheme, $host, $port, $peer) = @_;
1101898184e3Ssthen
1102898184e3Ssthen    if ( $scheme eq 'https' ) {
11036fb12b70Safresh1        $self->_assert_ssl;
1104898184e3Ssthen    }
1105eac174f2Safresh1
11066fb12b70Safresh1    $self->{fh} = $SOCKET_CLASS->new(
11079f11ffb7Safresh1        PeerHost  => $peer,
1108898184e3Ssthen        PeerPort  => $port,
110991f110e0Safresh1        $self->{local_address} ?
111091f110e0Safresh1            ( LocalAddr => $self->{local_address} ) : (),
1111898184e3Ssthen        Proto     => 'tcp',
1112898184e3Ssthen        Type      => SOCK_STREAM,
11136fb12b70Safresh1        Timeout   => $self->{timeout},
1114898184e3Ssthen    ) or die(qq/Could not connect to '$host:$port': $@\n/);
1115898184e3Ssthen
1116898184e3Ssthen    binmode($self->{fh})
1117898184e3Ssthen      or die(qq/Could not binmode() socket: '$!'\n/);
1118898184e3Ssthen
11199f11ffb7Safresh1    if ( $self->{keep_alive} ) {
11209f11ffb7Safresh1        unless ( defined( $self->{fh}->setsockopt( SOL_SOCKET, SO_KEEPALIVE, 1 ) ) ) {
11219f11ffb7Safresh1            CORE::close($self->{fh});
11229f11ffb7Safresh1            die(qq/Could not set SO_KEEPALIVE on socket: '$!'\n/);
11239f11ffb7Safresh1        }
11249f11ffb7Safresh1    }
11259f11ffb7Safresh1
11266fb12b70Safresh1    $self->start_ssl($host) if $scheme eq 'https';
11276fb12b70Safresh1
11286fb12b70Safresh1    $self->{scheme} = $scheme;
11296fb12b70Safresh1    $self->{host} = $host;
11309f11ffb7Safresh1    $self->{peer} = $peer;
11316fb12b70Safresh1    $self->{port} = $port;
1132b8851fccSafresh1    $self->{pid} = $$;
1133b8851fccSafresh1    $self->{tid} = _get_tid();
11346fb12b70Safresh1
11356fb12b70Safresh1    return $self;
11366fb12b70Safresh1}
11376fb12b70Safresh1
1138eac174f2Safresh1sub connected {
1139eac174f2Safresh1    my ($self) = @_;
1140eac174f2Safresh1    if ( $self->{fh} && $self->{fh}->connected ) {
1141eac174f2Safresh1        return wantarray
1142eac174f2Safresh1          ? ( $self->{fh}->peerhost, $self->{fh}->peerport )
1143eac174f2Safresh1          : join( ':', $self->{fh}->peerhost, $self->{fh}->peerport );
1144eac174f2Safresh1    }
1145eac174f2Safresh1    return;
1146eac174f2Safresh1}
1147eac174f2Safresh1
11486fb12b70Safresh1sub start_ssl {
11496fb12b70Safresh1    my ($self, $host) = @_;
11506fb12b70Safresh1
11516fb12b70Safresh1    # As this might be used via CONNECT after an SSL session
11526fb12b70Safresh1    # to a proxy, we shut down any existing SSL before attempting
11536fb12b70Safresh1    # the handshake
11546fb12b70Safresh1    if ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
11556fb12b70Safresh1        unless ( $self->{fh}->stop_SSL ) {
11566fb12b70Safresh1            my $ssl_err = IO::Socket::SSL->errstr;
11576fb12b70Safresh1            die(qq/Error halting prior SSL connection: $ssl_err/);
11586fb12b70Safresh1        }
11596fb12b70Safresh1    }
11606fb12b70Safresh1
116191f110e0Safresh1    my $ssl_args = $self->_ssl_args($host);
116291f110e0Safresh1    IO::Socket::SSL->start_SSL(
116391f110e0Safresh1        $self->{fh},
116491f110e0Safresh1        %$ssl_args,
116591f110e0Safresh1        SSL_create_ctx_callback => sub {
116691f110e0Safresh1            my $ctx = shift;
116791f110e0Safresh1            Net::SSLeay::CTX_set_mode($ctx, Net::SSLeay::MODE_AUTO_RETRY());
116891f110e0Safresh1        },
116991f110e0Safresh1    );
117091f110e0Safresh1
117191f110e0Safresh1    unless ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
117291f110e0Safresh1        my $ssl_err = IO::Socket::SSL->errstr;
117391f110e0Safresh1        die(qq/SSL connection failed for $host: $ssl_err\n/);
117491f110e0Safresh1    }
1175898184e3Ssthen}
1176898184e3Ssthen
1177898184e3Ssthensub close {
1178898184e3Ssthen    @_ == 1 || die(q/Usage: $handle->close()/ . "\n");
1179898184e3Ssthen    my ($self) = @_;
1180898184e3Ssthen    CORE::close($self->{fh})
1181898184e3Ssthen      or die(qq/Could not close socket: '$!'\n/);
1182898184e3Ssthen}
1183898184e3Ssthen
1184898184e3Ssthensub write {
1185898184e3Ssthen    @_ == 2 || die(q/Usage: $handle->write(buf)/ . "\n");
1186898184e3Ssthen    my ($self, $buf) = @_;
1187898184e3Ssthen
1188898184e3Ssthen    if ( $] ge '5.008' ) {
1189898184e3Ssthen        utf8::downgrade($buf, 1)
1190898184e3Ssthen            or die(qq/Wide character in write()\n/);
1191898184e3Ssthen    }
1192898184e3Ssthen
1193898184e3Ssthen    my $len = length $buf;
1194898184e3Ssthen    my $off = 0;
1195898184e3Ssthen
1196898184e3Ssthen    local $SIG{PIPE} = 'IGNORE';
1197898184e3Ssthen
1198898184e3Ssthen    while () {
1199898184e3Ssthen        $self->can_write
1200898184e3Ssthen          or die(qq/Timed out while waiting for socket to become ready for writing\n/);
1201898184e3Ssthen        my $r = syswrite($self->{fh}, $buf, $len, $off);
1202898184e3Ssthen        if (defined $r) {
1203898184e3Ssthen            $len -= $r;
1204898184e3Ssthen            $off += $r;
1205898184e3Ssthen            last unless $len > 0;
1206898184e3Ssthen        }
1207898184e3Ssthen        elsif ($! == EPIPE) {
1208898184e3Ssthen            die(qq/Socket closed by remote server: $!\n/);
1209898184e3Ssthen        }
1210898184e3Ssthen        elsif ($! != EINTR) {
121191f110e0Safresh1            if ($self->{fh}->can('errstr')){
121291f110e0Safresh1                my $err = $self->{fh}->errstr();
121391f110e0Safresh1                die (qq/Could not write to SSL socket: '$err'\n /);
121491f110e0Safresh1            }
121591f110e0Safresh1            else {
1216898184e3Ssthen                die(qq/Could not write to socket: '$!'\n/);
1217898184e3Ssthen            }
121891f110e0Safresh1
121991f110e0Safresh1        }
1220898184e3Ssthen    }
1221898184e3Ssthen    return $off;
1222898184e3Ssthen}
1223898184e3Ssthen
1224898184e3Ssthensub read {
1225898184e3Ssthen    @_ == 2 || @_ == 3 || die(q/Usage: $handle->read(len [, allow_partial])/ . "\n");
1226898184e3Ssthen    my ($self, $len, $allow_partial) = @_;
1227898184e3Ssthen
1228898184e3Ssthen    my $buf  = '';
1229898184e3Ssthen    my $got = length $self->{rbuf};
1230898184e3Ssthen
1231898184e3Ssthen    if ($got) {
1232898184e3Ssthen        my $take = ($got < $len) ? $got : $len;
1233898184e3Ssthen        $buf  = substr($self->{rbuf}, 0, $take, '');
1234898184e3Ssthen        $len -= $take;
1235898184e3Ssthen    }
1236898184e3Ssthen
1237eac174f2Safresh1    # Ignore SIGPIPE because SSL reads can result in writes that might error.
1238eac174f2Safresh1    # See "Expecting exactly the same behavior as plain sockets" in
1239eac174f2Safresh1    # https://metacpan.org/dist/IO-Socket-SSL/view/lib/IO/Socket/SSL.pod#Common-Usage-Errors
1240eac174f2Safresh1    local $SIG{PIPE} = 'IGNORE';
1241eac174f2Safresh1
1242898184e3Ssthen    while ($len > 0) {
1243898184e3Ssthen        $self->can_read
1244898184e3Ssthen          or die(q/Timed out while waiting for socket to become ready for reading/ . "\n");
1245898184e3Ssthen        my $r = sysread($self->{fh}, $buf, $len, length $buf);
1246898184e3Ssthen        if (defined $r) {
1247898184e3Ssthen            last unless $r;
1248898184e3Ssthen            $len -= $r;
1249898184e3Ssthen        }
1250898184e3Ssthen        elsif ($! != EINTR) {
125191f110e0Safresh1            if ($self->{fh}->can('errstr')){
125291f110e0Safresh1                my $err = $self->{fh}->errstr();
125391f110e0Safresh1                die (qq/Could not read from SSL socket: '$err'\n /);
125491f110e0Safresh1            }
125591f110e0Safresh1            else {
1256898184e3Ssthen                die(qq/Could not read from socket: '$!'\n/);
1257898184e3Ssthen            }
1258898184e3Ssthen        }
125991f110e0Safresh1    }
1260898184e3Ssthen    if ($len && !$allow_partial) {
1261898184e3Ssthen        die(qq/Unexpected end of stream\n/);
1262898184e3Ssthen    }
1263898184e3Ssthen    return $buf;
1264898184e3Ssthen}
1265898184e3Ssthen
1266898184e3Ssthensub readline {
1267898184e3Ssthen    @_ == 1 || die(q/Usage: $handle->readline()/ . "\n");
1268898184e3Ssthen    my ($self) = @_;
1269898184e3Ssthen
1270898184e3Ssthen    while () {
1271898184e3Ssthen        if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
1272898184e3Ssthen            return $1;
1273898184e3Ssthen        }
1274898184e3Ssthen        if (length $self->{rbuf} >= $self->{max_line_size}) {
1275898184e3Ssthen            die(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}\n/);
1276898184e3Ssthen        }
1277898184e3Ssthen        $self->can_read
1278898184e3Ssthen          or die(qq/Timed out while waiting for socket to become ready for reading\n/);
1279898184e3Ssthen        my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
1280898184e3Ssthen        if (defined $r) {
1281898184e3Ssthen            last unless $r;
1282898184e3Ssthen        }
1283898184e3Ssthen        elsif ($! != EINTR) {
128491f110e0Safresh1            if ($self->{fh}->can('errstr')){
128591f110e0Safresh1                my $err = $self->{fh}->errstr();
128691f110e0Safresh1                die (qq/Could not read from SSL socket: '$err'\n /);
128791f110e0Safresh1            }
128891f110e0Safresh1            else {
1289898184e3Ssthen                die(qq/Could not read from socket: '$!'\n/);
1290898184e3Ssthen            }
1291898184e3Ssthen        }
129291f110e0Safresh1    }
1293898184e3Ssthen    die(qq/Unexpected end of stream while looking for line\n/);
1294898184e3Ssthen}
1295898184e3Ssthen
1296898184e3Ssthensub read_header_lines {
1297898184e3Ssthen    @_ == 1 || @_ == 2 || die(q/Usage: $handle->read_header_lines([headers])/ . "\n");
1298898184e3Ssthen    my ($self, $headers) = @_;
1299898184e3Ssthen    $headers ||= {};
1300898184e3Ssthen    my $lines   = 0;
1301898184e3Ssthen    my $val;
1302898184e3Ssthen
1303898184e3Ssthen    while () {
1304898184e3Ssthen         my $line = $self->readline;
1305898184e3Ssthen
1306898184e3Ssthen         if (++$lines >= $self->{max_header_lines}) {
1307898184e3Ssthen             die(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}\n/);
1308898184e3Ssthen         }
1309898184e3Ssthen         elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
1310898184e3Ssthen             my ($field_name) = lc $1;
1311898184e3Ssthen             if (exists $headers->{$field_name}) {
1312898184e3Ssthen                 for ($headers->{$field_name}) {
1313898184e3Ssthen                     $_ = [$_] unless ref $_ eq "ARRAY";
1314898184e3Ssthen                     push @$_, $2;
1315898184e3Ssthen                     $val = \$_->[-1];
1316898184e3Ssthen                 }
1317898184e3Ssthen             }
1318898184e3Ssthen             else {
1319898184e3Ssthen                 $val = \($headers->{$field_name} = $2);
1320898184e3Ssthen             }
1321898184e3Ssthen         }
1322898184e3Ssthen         elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
1323898184e3Ssthen             $val
1324898184e3Ssthen               or die(qq/Unexpected header continuation line\n/);
1325898184e3Ssthen             next unless length $1;
1326898184e3Ssthen             $$val .= ' ' if length $$val;
1327898184e3Ssthen             $$val .= $1;
1328898184e3Ssthen         }
1329898184e3Ssthen         elsif ($line =~ /\A \x0D?\x0A \z/x) {
1330898184e3Ssthen            last;
1331898184e3Ssthen         }
1332898184e3Ssthen         else {
1333898184e3Ssthen            die(q/Malformed header line: / . $Printable->($line) . "\n");
1334898184e3Ssthen         }
1335898184e3Ssthen    }
1336898184e3Ssthen    return $headers;
1337898184e3Ssthen}
1338898184e3Ssthen
1339898184e3Ssthensub write_request {
1340898184e3Ssthen    @_ == 2 || die(q/Usage: $handle->write_request(request)/ . "\n");
1341898184e3Ssthen    my($self, $request) = @_;
13429f11ffb7Safresh1    $self->write_request_header(@{$request}{qw/method uri headers header_case/});
1343898184e3Ssthen    $self->write_body($request) if $request->{cb};
1344898184e3Ssthen    return;
1345898184e3Ssthen}
1346898184e3Ssthen
13479f11ffb7Safresh1# Standard request header names/case from HTTP/1.1 RFCs
13489f11ffb7Safresh1my @rfc_request_headers = qw(
13499f11ffb7Safresh1  Accept Accept-Charset Accept-Encoding Accept-Language Authorization
13509f11ffb7Safresh1  Cache-Control Connection Content-Length Expect From Host
13519f11ffb7Safresh1  If-Match If-Modified-Since If-None-Match If-Range If-Unmodified-Since
13529f11ffb7Safresh1  Max-Forwards Pragma Proxy-Authorization Range Referer TE Trailer
13539f11ffb7Safresh1  Transfer-Encoding Upgrade User-Agent Via
1354898184e3Ssthen);
1355898184e3Ssthen
13569f11ffb7Safresh1my @other_request_headers = qw(
13579f11ffb7Safresh1  Content-Encoding Content-MD5 Content-Type Cookie DNT Date Origin
13589f11ffb7Safresh1  X-XSS-Protection
13599f11ffb7Safresh1);
13609f11ffb7Safresh1
13619f11ffb7Safresh1my %HeaderCase = map { lc($_) => $_ } @rfc_request_headers, @other_request_headers;
13629f11ffb7Safresh1
13636fb12b70Safresh1# to avoid multiple small writes and hence nagle, you can pass the method line or anything else to
13646fb12b70Safresh1# combine writes.
1365898184e3Ssthensub write_header_lines {
13669f11ffb7Safresh1    (@_ >= 2 && @_ <= 4 && ref $_[1] eq 'HASH') || die(q/Usage: $handle->write_header_lines(headers, [header_case, prefix])/ . "\n");
13679f11ffb7Safresh1    my($self, $headers, $header_case, $prefix_data) = @_;
13689f11ffb7Safresh1    $header_case ||= {};
1369898184e3Ssthen
13706fb12b70Safresh1    my $buf = (defined $prefix_data ? $prefix_data : '');
13719f11ffb7Safresh1
13729f11ffb7Safresh1    # Per RFC, control fields should be listed first
13739f11ffb7Safresh1    my %seen;
13749f11ffb7Safresh1    for my $k ( qw/host cache-control expect max-forwards pragma range te/ ) {
13759f11ffb7Safresh1        next unless exists $headers->{$k};
13769f11ffb7Safresh1        $seen{$k}++;
13779f11ffb7Safresh1        my $field_name = $HeaderCase{$k};
13789f11ffb7Safresh1        my $v = $headers->{$k};
13799f11ffb7Safresh1        for (ref $v eq 'ARRAY' ? @$v : $v) {
13809f11ffb7Safresh1            $_ = '' unless defined $_;
13819f11ffb7Safresh1            $buf .= "$field_name: $_\x0D\x0A";
13829f11ffb7Safresh1        }
13839f11ffb7Safresh1    }
13849f11ffb7Safresh1
13859f11ffb7Safresh1    # Other headers sent in arbitrary order
1386898184e3Ssthen    while (my ($k, $v) = each %$headers) {
1387898184e3Ssthen        my $field_name = lc $k;
13889f11ffb7Safresh1        next if $seen{$field_name};
1389898184e3Ssthen        if (exists $HeaderCase{$field_name}) {
1390898184e3Ssthen            $field_name = $HeaderCase{$field_name};
1391898184e3Ssthen        }
1392898184e3Ssthen        else {
13939f11ffb7Safresh1            if (exists $header_case->{$field_name}) {
13949f11ffb7Safresh1                $field_name = $header_case->{$field_name};
13959f11ffb7Safresh1            }
13969f11ffb7Safresh1            else {
13979f11ffb7Safresh1                $field_name =~ s/\b(\w)/\u$1/g;
13989f11ffb7Safresh1            }
1399898184e3Ssthen            $field_name =~ /\A $Token+ \z/xo
1400898184e3Ssthen              or die(q/Invalid HTTP header field name: / . $Printable->($field_name) . "\n");
1401898184e3Ssthen            $HeaderCase{lc $field_name} = $field_name;
1402898184e3Ssthen        }
1403898184e3Ssthen        for (ref $v eq 'ARRAY' ? @$v : $v) {
14049f11ffb7Safresh1            # unwrap a field value if pre-wrapped by user
14059f11ffb7Safresh1            s/\x0D?\x0A\s+/ /g;
14069f11ffb7Safresh1            die(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_). "\n")
14079f11ffb7Safresh1              unless $_ eq '' || /\A $Field_Content \z/xo;
1408b8851fccSafresh1            $_ = '' unless defined $_;
1409898184e3Ssthen            $buf .= "$field_name: $_\x0D\x0A";
1410898184e3Ssthen        }
1411898184e3Ssthen    }
1412898184e3Ssthen    $buf .= "\x0D\x0A";
1413898184e3Ssthen    return $self->write($buf);
1414898184e3Ssthen}
1415898184e3Ssthen
14166fb12b70Safresh1# return value indicates whether message length was defined; this is generally
14176fb12b70Safresh1# true unless there was no content-length header and we just read until EOF.
14186fb12b70Safresh1# Other message length errors are thrown as exceptions
1419898184e3Ssthensub read_body {
1420898184e3Ssthen    @_ == 3 || die(q/Usage: $handle->read_body(callback, response)/ . "\n");
1421898184e3Ssthen    my ($self, $cb, $response) = @_;
1422898184e3Ssthen    my $te = $response->{headers}{'transfer-encoding'} || '';
14236fb12b70Safresh1    my $chunked = grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ;
14246fb12b70Safresh1    return $chunked
14256fb12b70Safresh1        ? $self->read_chunked_body($cb, $response)
14266fb12b70Safresh1        : $self->read_content_body($cb, $response);
1427898184e3Ssthen}
1428898184e3Ssthen
1429898184e3Ssthensub write_body {
1430898184e3Ssthen    @_ == 2 || die(q/Usage: $handle->write_body(request)/ . "\n");
1431898184e3Ssthen    my ($self, $request) = @_;
1432eac174f2Safresh1    if (exists $request->{headers}{'content-length'}) {
1433eac174f2Safresh1        return unless $request->{headers}{'content-length'};
1434898184e3Ssthen        return $self->write_content_body($request);
1435898184e3Ssthen    }
1436898184e3Ssthen    else {
1437898184e3Ssthen        return $self->write_chunked_body($request);
1438898184e3Ssthen    }
1439898184e3Ssthen}
1440898184e3Ssthen
1441898184e3Ssthensub read_content_body {
1442898184e3Ssthen    @_ == 3 || @_ == 4 || die(q/Usage: $handle->read_content_body(callback, response, [read_length])/ . "\n");
1443898184e3Ssthen    my ($self, $cb, $response, $content_length) = @_;
1444898184e3Ssthen    $content_length ||= $response->{headers}{'content-length'};
1445898184e3Ssthen
14466fb12b70Safresh1    if ( defined $content_length ) {
1447898184e3Ssthen        my $len = $content_length;
1448898184e3Ssthen        while ($len > 0) {
1449898184e3Ssthen            my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
1450898184e3Ssthen            $cb->($self->read($read, 0), $response);
1451898184e3Ssthen            $len -= $read;
1452898184e3Ssthen        }
14536fb12b70Safresh1        return length($self->{rbuf}) == 0;
1454898184e3Ssthen    }
14556fb12b70Safresh1
1456898184e3Ssthen    my $chunk;
1457898184e3Ssthen    $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) );
1458898184e3Ssthen
1459898184e3Ssthen    return;
1460898184e3Ssthen}
1461898184e3Ssthen
1462898184e3Ssthensub write_content_body {
1463898184e3Ssthen    @_ == 2 || die(q/Usage: $handle->write_content_body(request)/ . "\n");
1464898184e3Ssthen    my ($self, $request) = @_;
1465898184e3Ssthen
1466898184e3Ssthen    my ($len, $content_length) = (0, $request->{headers}{'content-length'});
1467898184e3Ssthen    while () {
1468898184e3Ssthen        my $data = $request->{cb}->();
1469898184e3Ssthen
1470898184e3Ssthen        defined $data && length $data
1471898184e3Ssthen          or last;
1472898184e3Ssthen
1473898184e3Ssthen        if ( $] ge '5.008' ) {
1474898184e3Ssthen            utf8::downgrade($data, 1)
1475898184e3Ssthen                or die(qq/Wide character in write_content()\n/);
1476898184e3Ssthen        }
1477898184e3Ssthen
1478898184e3Ssthen        $len += $self->write($data);
1479898184e3Ssthen    }
1480898184e3Ssthen
1481898184e3Ssthen    $len == $content_length
1482b8851fccSafresh1      or die(qq/Content-Length mismatch (got: $len expected: $content_length)\n/);
1483898184e3Ssthen
1484898184e3Ssthen    return $len;
1485898184e3Ssthen}
1486898184e3Ssthen
1487898184e3Ssthensub read_chunked_body {
1488898184e3Ssthen    @_ == 3 || die(q/Usage: $handle->read_chunked_body(callback, $response)/ . "\n");
1489898184e3Ssthen    my ($self, $cb, $response) = @_;
1490898184e3Ssthen
1491898184e3Ssthen    while () {
1492898184e3Ssthen        my $head = $self->readline;
1493898184e3Ssthen
1494898184e3Ssthen        $head =~ /\A ([A-Fa-f0-9]+)/x
1495898184e3Ssthen          or die(q/Malformed chunk head: / . $Printable->($head) . "\n");
1496898184e3Ssthen
1497898184e3Ssthen        my $len = hex($1)
1498898184e3Ssthen          or last;
1499898184e3Ssthen
1500898184e3Ssthen        $self->read_content_body($cb, $response, $len);
1501898184e3Ssthen
1502898184e3Ssthen        $self->read(2) eq "\x0D\x0A"
1503898184e3Ssthen          or die(qq/Malformed chunk: missing CRLF after chunk data\n/);
1504898184e3Ssthen    }
1505898184e3Ssthen    $self->read_header_lines($response->{headers});
15066fb12b70Safresh1    return 1;
1507898184e3Ssthen}
1508898184e3Ssthen
1509898184e3Ssthensub write_chunked_body {
1510898184e3Ssthen    @_ == 2 || die(q/Usage: $handle->write_chunked_body(request)/ . "\n");
1511898184e3Ssthen    my ($self, $request) = @_;
1512898184e3Ssthen
1513898184e3Ssthen    my $len = 0;
1514898184e3Ssthen    while () {
1515898184e3Ssthen        my $data = $request->{cb}->();
1516898184e3Ssthen
1517898184e3Ssthen        defined $data && length $data
1518898184e3Ssthen          or last;
1519898184e3Ssthen
1520898184e3Ssthen        if ( $] ge '5.008' ) {
1521898184e3Ssthen            utf8::downgrade($data, 1)
1522898184e3Ssthen                or die(qq/Wide character in write_chunked_body()\n/);
1523898184e3Ssthen        }
1524898184e3Ssthen
1525898184e3Ssthen        $len += length $data;
1526898184e3Ssthen
1527898184e3Ssthen        my $chunk  = sprintf '%X', length $data;
1528898184e3Ssthen           $chunk .= "\x0D\x0A";
1529898184e3Ssthen           $chunk .= $data;
1530898184e3Ssthen           $chunk .= "\x0D\x0A";
1531898184e3Ssthen
1532898184e3Ssthen        $self->write($chunk);
1533898184e3Ssthen    }
1534898184e3Ssthen    $self->write("0\x0D\x0A");
15359f11ffb7Safresh1    if ( ref $request->{trailer_cb} eq 'CODE' ) {
1536898184e3Ssthen        $self->write_header_lines($request->{trailer_cb}->())
15379f11ffb7Safresh1    }
15389f11ffb7Safresh1    else {
15399f11ffb7Safresh1        $self->write("\x0D\x0A");
15409f11ffb7Safresh1    }
1541898184e3Ssthen    return $len;
1542898184e3Ssthen}
1543898184e3Ssthen
1544898184e3Ssthensub read_response_header {
1545898184e3Ssthen    @_ == 1 || die(q/Usage: $handle->read_response_header()/ . "\n");
1546898184e3Ssthen    my ($self) = @_;
1547898184e3Ssthen
1548898184e3Ssthen    my $line = $self->readline;
1549898184e3Ssthen
1550eac174f2Safresh1    $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) (?: [\x09\x20]+ ([^\x0D\x0A]*) )? \x0D?\x0A/x
1551898184e3Ssthen      or die(q/Malformed Status-Line: / . $Printable->($line). "\n");
1552898184e3Ssthen
1553898184e3Ssthen    my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
1554eac174f2Safresh1    $reason = "" unless defined $reason;
1555898184e3Ssthen
1556898184e3Ssthen    die (qq/Unsupported HTTP protocol: $protocol\n/)
1557898184e3Ssthen        unless $version =~ /0*1\.0*[01]/;
1558898184e3Ssthen
1559898184e3Ssthen    return {
1560898184e3Ssthen        status       => $status,
1561898184e3Ssthen        reason       => $reason,
1562898184e3Ssthen        headers      => $self->read_header_lines,
1563898184e3Ssthen        protocol     => $protocol,
1564898184e3Ssthen    };
1565898184e3Ssthen}
1566898184e3Ssthen
1567898184e3Ssthensub write_request_header {
15689f11ffb7Safresh1    @_ == 5 || die(q/Usage: $handle->write_request_header(method, request_uri, headers, header_case)/ . "\n");
15699f11ffb7Safresh1    my ($self, $method, $request_uri, $headers, $header_case) = @_;
1570898184e3Ssthen
15719f11ffb7Safresh1    return $self->write_header_lines($headers, $header_case, "$method $request_uri HTTP/1.1\x0D\x0A");
1572898184e3Ssthen}
1573898184e3Ssthen
1574898184e3Ssthensub _do_timeout {
1575898184e3Ssthen    my ($self, $type, $timeout) = @_;
1576898184e3Ssthen    $timeout = $self->{timeout}
1577898184e3Ssthen        unless defined $timeout && $timeout >= 0;
1578898184e3Ssthen
1579898184e3Ssthen    my $fd = fileno $self->{fh};
1580898184e3Ssthen    defined $fd && $fd >= 0
1581898184e3Ssthen      or die(qq/select(2): 'Bad file descriptor'\n/);
1582898184e3Ssthen
1583898184e3Ssthen    my $initial = time;
1584898184e3Ssthen    my $pending = $timeout;
1585898184e3Ssthen    my $nfound;
1586898184e3Ssthen
1587898184e3Ssthen    vec(my $fdset = '', $fd, 1) = 1;
1588898184e3Ssthen
1589898184e3Ssthen    while () {
1590898184e3Ssthen        $nfound = ($type eq 'read')
1591898184e3Ssthen            ? select($fdset, undef, undef, $pending)
1592898184e3Ssthen            : select(undef, $fdset, undef, $pending) ;
1593898184e3Ssthen        if ($nfound == -1) {
1594898184e3Ssthen            $! == EINTR
1595898184e3Ssthen              or die(qq/select(2): '$!'\n/);
1596898184e3Ssthen            redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
1597898184e3Ssthen            $nfound = 0;
1598898184e3Ssthen        }
1599898184e3Ssthen        last;
1600898184e3Ssthen    }
1601898184e3Ssthen    $! = 0;
1602898184e3Ssthen    return $nfound;
1603898184e3Ssthen}
1604898184e3Ssthen
1605898184e3Ssthensub can_read {
1606898184e3Ssthen    @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_read([timeout])/ . "\n");
1607898184e3Ssthen    my $self = shift;
16086fb12b70Safresh1    if ( ref($self->{fh}) eq 'IO::Socket::SSL' ) {
16096fb12b70Safresh1        return 1 if $self->{fh}->pending;
16106fb12b70Safresh1    }
1611898184e3Ssthen    return $self->_do_timeout('read', @_)
1612898184e3Ssthen}
1613898184e3Ssthen
1614898184e3Ssthensub can_write {
1615898184e3Ssthen    @_ == 1 || @_ == 2 || die(q/Usage: $handle->can_write([timeout])/ . "\n");
1616898184e3Ssthen    my $self = shift;
1617898184e3Ssthen    return $self->_do_timeout('write', @_)
1618898184e3Ssthen}
1619898184e3Ssthen
16206fb12b70Safresh1sub _assert_ssl {
1621b8851fccSafresh1    my($ok, $reason) = HTTP::Tiny->can_ssl();
1622b8851fccSafresh1    die $reason unless $ok;
16236fb12b70Safresh1}
16246fb12b70Safresh1
16256fb12b70Safresh1sub can_reuse {
16269f11ffb7Safresh1    my ($self,$scheme,$host,$port,$peer) = @_;
16276fb12b70Safresh1    return 0 if
1628b8851fccSafresh1        $self->{pid} != $$
1629b8851fccSafresh1        || $self->{tid} != _get_tid()
1630b8851fccSafresh1        || length($self->{rbuf})
16316fb12b70Safresh1        || $scheme ne $self->{scheme}
16326fb12b70Safresh1        || $host ne $self->{host}
16336fb12b70Safresh1        || $port ne $self->{port}
16349f11ffb7Safresh1        || $peer ne $self->{peer}
16356fb12b70Safresh1        || eval { $self->can_read(0) }
16366fb12b70Safresh1        || $@ ;
16376fb12b70Safresh1        return 1;
16386fb12b70Safresh1}
16396fb12b70Safresh1
164091f110e0Safresh1# Try to find a CA bundle to validate the SSL cert,
164191f110e0Safresh1# prefer Mozilla::CA or fallback to a system file
164291f110e0Safresh1sub _find_CA_file {
164391f110e0Safresh1    my $self = shift();
164491f110e0Safresh1
16459f11ffb7Safresh1    my $ca_file =
16469f11ffb7Safresh1      defined( $self->{SSL_options}->{SSL_ca_file} )
16479f11ffb7Safresh1      ? $self->{SSL_options}->{SSL_ca_file}
16489f11ffb7Safresh1      : $ENV{SSL_CERT_FILE};
16499f11ffb7Safresh1
16509f11ffb7Safresh1    if ( defined $ca_file ) {
16519f11ffb7Safresh1        unless ( -r $ca_file ) {
16529f11ffb7Safresh1            die qq/SSL_ca_file '$ca_file' not found or not readable\n/;
1653b8851fccSafresh1        }
16549f11ffb7Safresh1        return $ca_file;
1655b8851fccSafresh1    }
165691f110e0Safresh1
16570b7734b3Safresh1    local @INC = @INC;
16580b7734b3Safresh1    pop @INC if $INC[-1] eq '.';
165991f110e0Safresh1    return Mozilla::CA::SSL_ca_file()
1660b8851fccSafresh1        if eval { require Mozilla::CA; 1 };
166191f110e0Safresh1
1662b8851fccSafresh1    # cert list copied from golang src/crypto/x509/root_unix.go
1663b8851fccSafresh1    foreach my $ca_bundle (
1664b8851fccSafresh1        "/etc/ssl/certs/ca-certificates.crt",     # Debian/Ubuntu/Gentoo etc.
1665b8851fccSafresh1        "/etc/pki/tls/certs/ca-bundle.crt",       # Fedora/RHEL
1666b8851fccSafresh1        "/etc/ssl/ca-bundle.pem",                 # OpenSUSE
1667b8851fccSafresh1        "/etc/openssl/certs/ca-certificates.crt", # NetBSD
1668b8851fccSafresh1        "/etc/ssl/cert.pem",                      # OpenBSD
1669b8851fccSafresh1        "/usr/local/share/certs/ca-root-nss.crt", # FreeBSD/DragonFly
1670b8851fccSafresh1        "/etc/pki/tls/cacert.pem",                # OpenELEC
1671b8851fccSafresh1        "/etc/certs/ca-certificates.crt",         # Solaris 11.2+
167291f110e0Safresh1    ) {
167391f110e0Safresh1        return $ca_bundle if -e $ca_bundle;
167491f110e0Safresh1    }
167591f110e0Safresh1
167691f110e0Safresh1    die qq/Couldn't find a CA bundle with which to verify the SSL certificate.\n/
167791f110e0Safresh1      . qq/Try installing Mozilla::CA from CPAN\n/;
167891f110e0Safresh1}
167991f110e0Safresh1
1680b8851fccSafresh1# for thread safety, we need to know thread id if threads are loaded
1681b8851fccSafresh1sub _get_tid {
1682b8851fccSafresh1    no warnings 'reserved'; # for 'threads'
1683b8851fccSafresh1    return threads->can("tid") ? threads->tid : 0;
1684b8851fccSafresh1}
1685b8851fccSafresh1
168691f110e0Safresh1sub _ssl_args {
168791f110e0Safresh1    my ($self, $host) = @_;
168891f110e0Safresh1
16896fb12b70Safresh1    my %ssl_args;
16906fb12b70Safresh1
16916fb12b70Safresh1    # This test reimplements IO::Socket::SSL::can_client_sni(), which wasn't
16926fb12b70Safresh1    # added until IO::Socket::SSL 1.84
16936fb12b70Safresh1    if ( Net::SSLeay::OPENSSL_VERSION_NUMBER() >= 0x01000000 ) {
16946fb12b70Safresh1        $ssl_args{SSL_hostname} = $host,          # Sane SNI support
16956fb12b70Safresh1    }
169691f110e0Safresh1
169791f110e0Safresh1    if ($self->{verify_SSL}) {
169891f110e0Safresh1        $ssl_args{SSL_verifycn_scheme}  = 'http'; # enable CN validation
169991f110e0Safresh1        $ssl_args{SSL_verifycn_name}    = $host;  # set validation hostname
170091f110e0Safresh1        $ssl_args{SSL_verify_mode}      = 0x01;   # enable cert validation
170191f110e0Safresh1        $ssl_args{SSL_ca_file}          = $self->_find_CA_file;
170291f110e0Safresh1    }
170391f110e0Safresh1    else {
170491f110e0Safresh1        $ssl_args{SSL_verifycn_scheme}  = 'none'; # disable CN validation
170591f110e0Safresh1        $ssl_args{SSL_verify_mode}      = 0x00;   # disable cert validation
170691f110e0Safresh1    }
170791f110e0Safresh1
170891f110e0Safresh1    # user options override settings from verify_SSL
170991f110e0Safresh1    for my $k ( keys %{$self->{SSL_options}} ) {
171091f110e0Safresh1        $ssl_args{$k} = $self->{SSL_options}{$k} if $k =~ m/^SSL_/;
171191f110e0Safresh1    }
171291f110e0Safresh1
171391f110e0Safresh1    return \%ssl_args;
171491f110e0Safresh1}
171591f110e0Safresh1
1716898184e3Ssthen1;
1717898184e3Ssthen
1718898184e3Ssthen__END__
171991f110e0Safresh1
1720898184e3Ssthen=pod
1721898184e3Ssthen
17226fb12b70Safresh1=encoding UTF-8
17236fb12b70Safresh1
1724898184e3Ssthen=head1 NAME
1725898184e3Ssthen
1726898184e3SsthenHTTP::Tiny - A small, simple, correct HTTP/1.1 client
1727898184e3Ssthen
1728898184e3Ssthen=head1 VERSION
1729898184e3Ssthen
1730*3d61058aSafresh1version 0.088
1731898184e3Ssthen
1732898184e3Ssthen=head1 SYNOPSIS
1733898184e3Ssthen
1734898184e3Ssthen    use HTTP::Tiny;
1735898184e3Ssthen
1736898184e3Ssthen    my $response = HTTP::Tiny->new->get('http://example.com/');
1737898184e3Ssthen
1738898184e3Ssthen    die "Failed!\n" unless $response->{success};
1739898184e3Ssthen
1740898184e3Ssthen    print "$response->{status} $response->{reason}\n";
1741898184e3Ssthen
1742898184e3Ssthen    while (my ($k, $v) = each %{$response->{headers}}) {
1743898184e3Ssthen        for (ref $v eq 'ARRAY' ? @$v : $v) {
1744898184e3Ssthen            print "$k: $_\n";
1745898184e3Ssthen        }
1746898184e3Ssthen    }
1747898184e3Ssthen
1748898184e3Ssthen    print $response->{content} if length $response->{content};
1749898184e3Ssthen
1750898184e3Ssthen=head1 DESCRIPTION
1751898184e3Ssthen
17526fb12b70Safresh1This is a very simple HTTP/1.1 client, designed for doing simple
1753898184e3Ssthenrequests without the overhead of a large framework like L<LWP::UserAgent>.
1754898184e3Ssthen
1755898184e3SsthenIt is more correct and more complete than L<HTTP::Lite>.  It supports
17566fb12b70Safresh1proxies and redirection.  It also correctly resumes after EINTR.
17576fb12b70Safresh1
17586fb12b70Safresh1If L<IO::Socket::IP> 0.25 or later is installed, HTTP::Tiny will use it instead
17596fb12b70Safresh1of L<IO::Socket::INET> for transparent support for both IPv4 and IPv6.
17606fb12b70Safresh1
17616fb12b70Safresh1Cookie support requires L<HTTP::CookieJar> or an equivalent class.
1762898184e3Ssthen
1763898184e3Ssthen=head1 METHODS
1764898184e3Ssthen
1765898184e3Ssthen=head2 new
1766898184e3Ssthen
1767898184e3Ssthen    $http = HTTP::Tiny->new( %attributes );
1768898184e3Ssthen
1769898184e3SsthenThis constructor returns a new HTTP::Tiny object.  Valid attributes include:
1770898184e3Ssthen
1771898184e3Ssthen=over 4
1772898184e3Ssthen
1773898184e3Ssthen=item *
1774898184e3Ssthen
1775b8851fccSafresh1C<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.
1776898184e3Ssthen
1777898184e3Ssthen=item *
1778898184e3Ssthen
1779b8851fccSafresh1C<cookie_jar> — An instance of L<HTTP::CookieJar> — or equivalent class that supports the C<add> and C<cookie_header> methods
17806fb12b70Safresh1
17816fb12b70Safresh1=item *
17826fb12b70Safresh1
1783b8851fccSafresh1C<default_headers> — A hashref of default headers to apply to requests
1784898184e3Ssthen
1785898184e3Ssthen=item *
1786898184e3Ssthen
1787b8851fccSafresh1C<local_address> — The local IP address to bind to
178891f110e0Safresh1
178991f110e0Safresh1=item *
179091f110e0Safresh1
1791b8851fccSafresh1C<keep_alive> — Whether to reuse the last connection (if for the same scheme, host and port) (defaults to 1)
17926fb12b70Safresh1
17936fb12b70Safresh1=item *
17946fb12b70Safresh1
1795b8851fccSafresh1C<max_redirect> — Maximum number of redirects allowed (defaults to 5)
1796898184e3Ssthen
1797898184e3Ssthen=item *
1798898184e3Ssthen
1799eac174f2Safresh1C<max_size> — Maximum response size in bytes (only when not using a data callback).  If defined, requests with responses larger than this will return a 599 status code.
18006fb12b70Safresh1
18016fb12b70Safresh1=item *
18026fb12b70Safresh1
1803b8851fccSafresh1C<http_proxy> — URL of a proxy server to use for HTTP connections (default is C<$ENV{http_proxy}> — if set)
18046fb12b70Safresh1
18056fb12b70Safresh1=item *
18066fb12b70Safresh1
1807b8851fccSafresh1C<https_proxy> — URL of a proxy server to use for HTTPS connections (default is C<$ENV{https_proxy}> — if set)
1808898184e3Ssthen
1809898184e3Ssthen=item *
1810898184e3Ssthen
1811b8851fccSafresh1C<proxy> — URL of a generic proxy server for both HTTP and HTTPS connections (default is C<$ENV{all_proxy}> — if set)
18126fb12b70Safresh1
18136fb12b70Safresh1=item *
18146fb12b70Safresh1
1815b8851fccSafresh1C<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}> —)
1816898184e3Ssthen
1817898184e3Ssthen=item *
1818898184e3Ssthen
1819eac174f2Safresh1C<timeout> — Request timeout in seconds (default is 60) If a socket open, read or write takes longer than the timeout, the request response status code will be 599.
1820898184e3Ssthen
182191f110e0Safresh1=item *
182291f110e0Safresh1
1823e0680481Safresh1C<verify_SSL> — A boolean that indicates whether to validate the TLS/SSL certificate of an C<https> — connection (default is true). Changed from false to true in version 0.083.
182491f110e0Safresh1
182591f110e0Safresh1=item *
182691f110e0Safresh1
1827b8851fccSafresh1C<SSL_options> — A hashref of C<SSL_*> — options to pass through to L<IO::Socket::SSL>
182891f110e0Safresh1
1829e0680481Safresh1=item *
1830e0680481Safresh1
1831e0680481Safresh1C<$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}> - Changes the default certificate verification behavior to not check server identity if set to 1. Only effective if C<verify_SSL> is not set. Added in version 0.083.
1832e0680481Safresh1
1833898184e3Ssthen=back
1834898184e3Ssthen
1835eac174f2Safresh1An accessor/mutator method exists for each attribute.
1836eac174f2Safresh1
1837b8851fccSafresh1Passing an explicit C<undef> for C<proxy>, C<http_proxy> or C<https_proxy> will
1838b8851fccSafresh1prevent getting the corresponding proxies from the environment.
1839b8851fccSafresh1
1840eac174f2Safresh1Errors during request execution will result in a pseudo-HTTP status code of 599
1841eac174f2Safresh1and a reason of "Internal Exception". The content field in the response will
1842eac174f2Safresh1contain the text of the error.
1843898184e3Ssthen
18446fb12b70Safresh1The C<keep_alive> parameter enables a persistent connection, but only to a
1845eac174f2Safresh1single destination scheme, host and port.  If any connection-relevant
1846eac174f2Safresh1attributes are modified via accessor, or if the process ID or thread ID change,
1847eac174f2Safresh1the persistent connection will be dropped.  If you want persistent connections
1848b8851fccSafresh1across multiple destinations, use multiple HTTP::Tiny objects.
18496fb12b70Safresh1
185091f110e0Safresh1See L</SSL SUPPORT> for more on the C<verify_SSL> and C<SSL_options> attributes.
185191f110e0Safresh1
1852eac174f2Safresh1=head2 get|head|put|post|patch|delete
1853898184e3Ssthen
1854898184e3Ssthen    $response = $http->get($url);
1855898184e3Ssthen    $response = $http->get($url, \%options);
1856898184e3Ssthen    $response = $http->head($url);
1857898184e3Ssthen
1858898184e3SsthenThese methods are shorthand for calling C<request()> for the given method.  The
1859898184e3SsthenURL must have unsafe characters escaped and international domain names encoded.
1860898184e3SsthenSee C<request()> for valid options and a description of the response.
1861898184e3Ssthen
1862898184e3SsthenThe C<success> field of the response will be true if the status code is 2XX.
1863898184e3Ssthen
1864898184e3Ssthen=head2 post_form
1865898184e3Ssthen
1866898184e3Ssthen    $response = $http->post_form($url, $form_data);
1867898184e3Ssthen    $response = $http->post_form($url, $form_data, \%options);
1868898184e3Ssthen
1869898184e3SsthenThis method executes a C<POST> request and sends the key/value pairs from a
1870898184e3Ssthenform data hash or array reference to the given URL with a C<content-type> of
18716fb12b70Safresh1C<application/x-www-form-urlencoded>.  If data is provided as an array
18726fb12b70Safresh1reference, the order is preserved; if provided as a hash reference, the terms
18736fb12b70Safresh1are sorted on key and value for consistency.  See documentation for the
1874898184e3SsthenC<www_form_urlencode> method for details on the encoding.
1875898184e3Ssthen
1876898184e3SsthenThe URL must have unsafe characters escaped and international domain names
1877898184e3Ssthenencoded.  See C<request()> for valid options and a description of the response.
1878898184e3SsthenAny C<content-type> header or content in the options hashref will be ignored.
1879898184e3Ssthen
1880898184e3SsthenThe C<success> field of the response will be true if the status code is 2XX.
1881898184e3Ssthen
1882898184e3Ssthen=head2 mirror
1883898184e3Ssthen
1884898184e3Ssthen    $response = $http->mirror($url, $file, \%options)
1885898184e3Ssthen    if ( $response->{success} ) {
1886898184e3Ssthen        print "$file is up to date\n";
1887898184e3Ssthen    }
1888898184e3Ssthen
1889898184e3SsthenExecutes a C<GET> request for the URL and saves the response body to the file
1890898184e3Ssthenname provided.  The URL must have unsafe characters escaped and international
18916fb12b70Safresh1domain names encoded.  If the file already exists, the request will include an
1892898184e3SsthenC<If-Modified-Since> header with the modification timestamp of the file.  You
1893898184e3Ssthenmay specify a different C<If-Modified-Since> header yourself in the C<<
1894898184e3Ssthen$options->{headers} >> hash.
1895898184e3Ssthen
1896898184e3SsthenThe C<success> field of the response will be true if the status code is 2XX
1897898184e3Ssthenor if the status code is 304 (unmodified).
1898898184e3Ssthen
1899898184e3SsthenIf the file was modified and the server response includes a properly
1900898184e3Ssthenformatted C<Last-Modified> header, the file modification time will
1901898184e3Ssthenbe updated accordingly.
1902898184e3Ssthen
1903898184e3Ssthen=head2 request
1904898184e3Ssthen
1905898184e3Ssthen    $response = $http->request($method, $url);
1906898184e3Ssthen    $response = $http->request($method, $url, \%options);
1907898184e3Ssthen
1908898184e3SsthenExecutes an HTTP request of the given method type ('GET', 'HEAD', 'POST',
1909898184e3Ssthen'PUT', etc.) on the given URL.  The URL must have unsafe characters escaped and
19106fb12b70Safresh1international domain names encoded.
19116fb12b70Safresh1
1912b46d8ef2Safresh1B<NOTE>: Method names are B<case-sensitive> per the HTTP/1.1 specification.
1913b46d8ef2Safresh1Don't use C<get> when you really want C<GET>.  See L<LIMITATIONS> for
1914b46d8ef2Safresh1how this applies to redirection.
1915b46d8ef2Safresh1
19166fb12b70Safresh1If the URL includes a "user:password" stanza, they will be used for Basic-style
19176fb12b70Safresh1authorization headers.  (Authorization headers will not be included in a
19186fb12b70Safresh1redirected request.) For example:
19196fb12b70Safresh1
19206fb12b70Safresh1    $http->request('GET', 'http://Aladdin:open sesame@example.com/');
19216fb12b70Safresh1
19226fb12b70Safresh1If the "user:password" stanza contains reserved characters, they must
19236fb12b70Safresh1be percent-escaped:
19246fb12b70Safresh1
19256fb12b70Safresh1    $http->request('GET', 'http://john%40example.com:password@example.com/');
19266fb12b70Safresh1
19276fb12b70Safresh1A hashref of options may be appended to modify the request.
1928898184e3Ssthen
1929898184e3SsthenValid options are:
1930898184e3Ssthen
1931898184e3Ssthen=over 4
1932898184e3Ssthen
1933898184e3Ssthen=item *
1934898184e3Ssthen
1935b8851fccSafresh1C<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.
1936898184e3Ssthen
1937898184e3Ssthen=item *
1938898184e3Ssthen
1939b8851fccSafresh1C<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
1940898184e3Ssthen
1941898184e3Ssthen=item *
1942898184e3Ssthen
1943b8851fccSafresh1C<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)
1944898184e3Ssthen
1945898184e3Ssthen=item *
1946898184e3Ssthen
1947b8851fccSafresh1C<data_callback> — A code reference that will be called for each chunks of the response body received.
1948898184e3Ssthen
19499f11ffb7Safresh1=item *
19509f11ffb7Safresh1
1951b46d8ef2Safresh1C<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). It can be given as either a scalar or a code reference that will receive the hostname and whose response will be taken as the address.
19529f11ffb7Safresh1
1953898184e3Ssthen=back
1954898184e3Ssthen
1955b8851fccSafresh1The C<Host> header is generated from the URL in accordance with RFC 2616.  It
1956b8851fccSafresh1is a fatal error to specify C<Host> in the C<headers> option.  Other headers
1957b8851fccSafresh1may be ignored or overwritten if necessary for transport compliance.
1958b8851fccSafresh1
1959898184e3SsthenIf the C<content> option is a code reference, it will be called iteratively
1960898184e3Ssthento provide the content body of the request.  It should return the empty
1961898184e3Ssthenstring or undef when the iterator is exhausted.
1962898184e3Ssthen
19636fb12b70Safresh1If the C<content> option is the empty string, no C<content-type> or
19646fb12b70Safresh1C<content-length> headers will be generated.
19656fb12b70Safresh1
1966898184e3SsthenIf the C<data_callback> option is provided, it will be called iteratively until
1967898184e3Ssthenthe entire response body is received.  The first argument will be a string
1968898184e3Ssthencontaining a chunk of the response body, the second argument will be the
1969898184e3Ssthenin-progress response hash reference, as described below.  (This allows
1970898184e3Ssthencustomizing the action of the callback based on the C<status> or C<headers>
1971898184e3Ssthenreceived prior to the content body.)
1972898184e3Ssthen
1973e0680481Safresh1Content data in the request/response is handled as "raw bytes".  Any
1974e0680481Safresh1encoding/decoding (with associated headers) are the responsibility of the
1975e0680481Safresh1caller.
1976e0680481Safresh1
1977898184e3SsthenThe C<request> method returns a hashref containing the response.  The hashref
1978898184e3Ssthenwill have the following keys:
1979898184e3Ssthen
1980898184e3Ssthen=over 4
1981898184e3Ssthen
1982898184e3Ssthen=item *
1983898184e3Ssthen
1984b8851fccSafresh1C<success> — Boolean indicating whether the operation returned a 2XX status code
1985898184e3Ssthen
1986898184e3Ssthen=item *
1987898184e3Ssthen
1988b8851fccSafresh1C<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
198991f110e0Safresh1
199091f110e0Safresh1=item *
199191f110e0Safresh1
1992b8851fccSafresh1C<status> — The HTTP status code of the response
1993898184e3Ssthen
1994898184e3Ssthen=item *
1995898184e3Ssthen
1996b8851fccSafresh1C<reason> — The response phrase returned by the server
1997898184e3Ssthen
1998898184e3Ssthen=item *
1999898184e3Ssthen
2000b8851fccSafresh1C<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
2001898184e3Ssthen
2002898184e3Ssthen=item *
2003898184e3Ssthen
2004b8851fccSafresh1C<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
2005898184e3Ssthen
20069f11ffb7Safresh1=item *
20079f11ffb7Safresh1
2008b46d8ef2Safresh1C<protocol> - If this field exists, it is the protocol of the response such as HTTP/1.0 or HTTP/1.1
2009b46d8ef2Safresh1
2010b46d8ef2Safresh1=item *
2011b46d8ef2Safresh1
20129f11ffb7Safresh1C<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.
20139f11ffb7Safresh1
2014898184e3Ssthen=back
2015898184e3Ssthen
2016eac174f2Safresh1On an error during the execution of the request, the C<status> field will
2017eac174f2Safresh1contain 599, and the C<content> field will contain the text of the error.
2018898184e3Ssthen
2019898184e3Ssthen=head2 www_form_urlencode
2020898184e3Ssthen
2021898184e3Ssthen    $params = $http->www_form_urlencode( $data );
2022898184e3Ssthen    $response = $http->get("http://example.com/query?$params");
2023898184e3Ssthen
2024898184e3SsthenThis method converts the key/value pairs from a data hash or array reference
2025898184e3Sstheninto a C<x-www-form-urlencoded> string.  The keys and values from the data
2026898184e3Ssthenreference will be UTF-8 encoded and escaped per RFC 3986.  If a value is an
2027898184e3Ssthenarray reference, the key will be repeated with each of the values of the array
20286fb12b70Safresh1reference.  If data is provided as a hash reference, the key/value pairs in the
20296fb12b70Safresh1resulting string will be sorted by key and value for consistent ordering.
2030898184e3Ssthen
2031b8851fccSafresh1=head2 can_ssl
2032b8851fccSafresh1
2033b8851fccSafresh1    $ok         = HTTP::Tiny->can_ssl;
2034b8851fccSafresh1    ($ok, $why) = HTTP::Tiny->can_ssl;
2035b8851fccSafresh1    ($ok, $why) = $http->can_ssl;
2036b8851fccSafresh1
2037b8851fccSafresh1Indicates if SSL support is available.  When called as a class object, it
2038b8851fccSafresh1checks for the correct version of L<Net::SSLeay> and L<IO::Socket::SSL>.
2039b8851fccSafresh1When called as an object methods, if C<SSL_verify> is true or if C<SSL_verify_mode>
2040b8851fccSafresh1is set in C<SSL_options>, it checks that a CA file is available.
2041b8851fccSafresh1
2042b8851fccSafresh1In scalar context, returns a boolean indicating if SSL is available.
2043b8851fccSafresh1In list context, returns the boolean and a (possibly multi-line) string of
2044b8851fccSafresh1errors indicating why SSL isn't available.
2045b8851fccSafresh1
20469f11ffb7Safresh1=head2 connected
20479f11ffb7Safresh1
20489f11ffb7Safresh1    $host = $http->connected;
20499f11ffb7Safresh1    ($host, $port) = $http->connected;
20509f11ffb7Safresh1
20519f11ffb7Safresh1Indicates if a connection to a peer is being kept alive, per the C<keep_alive>
20529f11ffb7Safresh1option.
20539f11ffb7Safresh1
20549f11ffb7Safresh1In scalar context, returns the peer host and port, joined with a colon, or
20559f11ffb7Safresh1C<undef> (if no peer is connected).
20569f11ffb7Safresh1In list context, returns the peer host and port or an empty list (if no peer
20579f11ffb7Safresh1is connected).
20589f11ffb7Safresh1
20599f11ffb7Safresh1B<Note>: This method cannot reliably be used to discover whether the remote
20609f11ffb7Safresh1host has closed its end of the socket.
20619f11ffb7Safresh1
20626fb12b70Safresh1=for Pod::Coverage SSL_options
20636fb12b70Safresh1agent
20646fb12b70Safresh1cookie_jar
2065898184e3Ssthendefault_headers
20666fb12b70Safresh1http_proxy
20676fb12b70Safresh1https_proxy
20686fb12b70Safresh1keep_alive
206991f110e0Safresh1local_address
2070898184e3Ssthenmax_redirect
2071898184e3Ssthenmax_size
20726fb12b70Safresh1no_proxy
2073898184e3Ssthenproxy
2074898184e3Ssthentimeout
207591f110e0Safresh1verify_SSL
207691f110e0Safresh1
2077e0680481Safresh1=head1 TLS/SSL SUPPORT
207891f110e0Safresh1
207991f110e0Safresh1Direct C<https> connections are supported only if L<IO::Socket::SSL> 1.56 or
2080eac174f2Safresh1greater and L<Net::SSLeay> 1.49 or greater are installed. An error will occur
2081e0680481Safresh1if new enough versions of these modules are not installed or if the TLS
2082b8851fccSafresh1encryption fails. You can also use C<HTTP::Tiny::can_ssl()> utility function
2083b8851fccSafresh1that returns boolean to see if the required modules are installed.
2084b8851fccSafresh1
2085b8851fccSafresh1An C<https> connection may be made via an C<http> proxy that supports the CONNECT
2086b8851fccSafresh1command (i.e. RFC 2817).  You may not proxy C<https> via a proxy that itself
2087b8851fccSafresh1requires C<https> to communicate.
208891f110e0Safresh1
2089e0680481Safresh1TLS/SSL provides two distinct capabilities:
209091f110e0Safresh1
209191f110e0Safresh1=over 4
209291f110e0Safresh1
209391f110e0Safresh1=item *
209491f110e0Safresh1
209591f110e0Safresh1Encrypted communication channel
209691f110e0Safresh1
209791f110e0Safresh1=item *
209891f110e0Safresh1
209991f110e0Safresh1Verification of server identity
210091f110e0Safresh1
210191f110e0Safresh1=back
210291f110e0Safresh1
2103e0680481Safresh1B<By default, HTTP::Tiny verifies server identity>.
210491f110e0Safresh1
2105e0680481Safresh1This was changed in version 0.083 due to security concerns. The previous default
2106e0680481Safresh1behavior can be enabled by setting C<$ENV{PERL_HTTP_TINY_SSL_INSECURE_BY_DEFAULT}>
2107e0680481Safresh1to 1.
210891f110e0Safresh1
2109e0680481Safresh1Verification is done by checking that that the TLS/SSL connection has a valid
2110e0680481Safresh1certificate corresponding to the host name of the connection and that the
2111e0680481Safresh1certificate has been verified by a CA. Assuming you trust the CA, this will
2112e0680481Safresh1protect against L<machine-in-the-middle
2113e0680481Safresh1attacks|http://en.wikipedia.org/wiki/Machine-in-the-middle_attack>.
211491f110e0Safresh1
211591f110e0Safresh1Certificate verification requires a file containing trusted CA certificates.
21169f11ffb7Safresh1
21179f11ffb7Safresh1If the environment variable C<SSL_CERT_FILE> is present, HTTP::Tiny
21189f11ffb7Safresh1will try to find a CA certificate file in that location.
21199f11ffb7Safresh1
212091f110e0Safresh1If the L<Mozilla::CA> module is installed, HTTP::Tiny will use the CA file
2121e0680481Safresh1included with it as a source of trusted CA's.
212291f110e0Safresh1
212391f110e0Safresh1If that module is not available, then HTTP::Tiny will search several
212491f110e0Safresh1system-specific default locations for a CA certificate file:
212591f110e0Safresh1
212691f110e0Safresh1=over 4
212791f110e0Safresh1
212891f110e0Safresh1=item *
212991f110e0Safresh1
213091f110e0Safresh1/etc/ssl/certs/ca-certificates.crt
213191f110e0Safresh1
213291f110e0Safresh1=item *
213391f110e0Safresh1
213491f110e0Safresh1/etc/pki/tls/certs/ca-bundle.crt
213591f110e0Safresh1
213691f110e0Safresh1=item *
213791f110e0Safresh1
213891f110e0Safresh1/etc/ssl/ca-bundle.pem
213991f110e0Safresh1
2140e0680481Safresh1=item *
2141e0680481Safresh1
2142e0680481Safresh1/etc/openssl/certs/ca-certificates.crt
2143e0680481Safresh1
2144e0680481Safresh1=item *
2145e0680481Safresh1
2146e0680481Safresh1/etc/ssl/cert.pem
2147e0680481Safresh1
2148e0680481Safresh1=item *
2149e0680481Safresh1
2150e0680481Safresh1/usr/local/share/certs/ca-root-nss.crt
2151e0680481Safresh1
2152e0680481Safresh1=item *
2153e0680481Safresh1
2154e0680481Safresh1/etc/pki/tls/cacert.pem
2155e0680481Safresh1
2156e0680481Safresh1=item *
2157e0680481Safresh1
2158e0680481Safresh1/etc/certs/ca-certificates.crt
2159e0680481Safresh1
216091f110e0Safresh1=back
216191f110e0Safresh1
2162eac174f2Safresh1An error will be occur if C<verify_SSL> is true and no CA certificate file
216391f110e0Safresh1is available.
216491f110e0Safresh1
2165e0680481Safresh1If you desire complete control over TLS/SSL connections, the C<SSL_options>
2166e0680481Safresh1attribute lets you provide a hash reference that will be passed through to
216791f110e0Safresh1C<IO::Socket::SSL::start_SSL()>, overriding any options set by HTTP::Tiny. For
216891f110e0Safresh1example, to provide your own trusted CA file:
216991f110e0Safresh1
217091f110e0Safresh1    SSL_options => {
217191f110e0Safresh1        SSL_ca_file => $file_path,
217291f110e0Safresh1    }
217391f110e0Safresh1
217491f110e0Safresh1The C<SSL_options> attribute could also be used for such things as providing a
217591f110e0Safresh1client certificate for authentication to a server or controlling the choice of
2176e0680481Safresh1cipher used for the TLS/SSL connection. See L<IO::Socket::SSL> documentation for
217791f110e0Safresh1details.
2178898184e3Ssthen
21796fb12b70Safresh1=head1 PROXY SUPPORT
21806fb12b70Safresh1
21816fb12b70Safresh1HTTP::Tiny can proxy both C<http> and C<https> requests.  Only Basic proxy
21826fb12b70Safresh1authorization is supported and it must be provided as part of the proxy URL:
21836fb12b70Safresh1C<http://user:pass@proxy.example.com/>.
21846fb12b70Safresh1
21856fb12b70Safresh1HTTP::Tiny supports the following proxy environment variables:
21866fb12b70Safresh1
21876fb12b70Safresh1=over 4
21886fb12b70Safresh1
21896fb12b70Safresh1=item *
21906fb12b70Safresh1
2191b8851fccSafresh1http_proxy or HTTP_PROXY
21926fb12b70Safresh1
21936fb12b70Safresh1=item *
21946fb12b70Safresh1
21956fb12b70Safresh1https_proxy or HTTPS_PROXY
21966fb12b70Safresh1
21976fb12b70Safresh1=item *
21986fb12b70Safresh1
21996fb12b70Safresh1all_proxy or ALL_PROXY
22006fb12b70Safresh1
22016fb12b70Safresh1=back
22026fb12b70Safresh1
2203b8851fccSafresh1If the C<REQUEST_METHOD> environment variable is set, then this might be a CGI
2204b8851fccSafresh1process and C<HTTP_PROXY> would be set from the C<Proxy:> header, which is a
2205b8851fccSafresh1security risk.  If C<REQUEST_METHOD> is set, C<HTTP_PROXY> (the upper case
2206eac174f2Safresh1variant only) is ignored, but C<CGI_HTTP_PROXY> is considered instead.
2207b8851fccSafresh1
22086fb12b70Safresh1Tunnelling C<https> over an C<http> proxy using the CONNECT method is
22096fb12b70Safresh1supported.  If your proxy uses C<https> itself, you can not tunnel C<https>
22106fb12b70Safresh1over it.
22116fb12b70Safresh1
22126fb12b70Safresh1Be warned that proxying an C<https> connection opens you to the risk of a
22136fb12b70Safresh1man-in-the-middle attack by the proxy server.
22146fb12b70Safresh1
22156fb12b70Safresh1The C<no_proxy> environment variable is supported in the format of a
22166fb12b70Safresh1comma-separated list of domain extensions proxy should not be used for.
22176fb12b70Safresh1
22186fb12b70Safresh1Proxy arguments passed to C<new> will override their corresponding
22196fb12b70Safresh1environment variables.
22206fb12b70Safresh1
2221898184e3Ssthen=head1 LIMITATIONS
2222898184e3Ssthen
2223898184e3SsthenHTTP::Tiny is I<conditionally compliant> with the
2224b8851fccSafresh1L<HTTP/1.1 specifications|http://www.w3.org/Protocols/>:
2225b8851fccSafresh1
2226b8851fccSafresh1=over 4
2227b8851fccSafresh1
2228b8851fccSafresh1=item *
2229b8851fccSafresh1
2230b8851fccSafresh1"Message Syntax and Routing" [RFC7230]
2231b8851fccSafresh1
2232b8851fccSafresh1=item *
2233b8851fccSafresh1
2234b8851fccSafresh1"Semantics and Content" [RFC7231]
2235b8851fccSafresh1
2236b8851fccSafresh1=item *
2237b8851fccSafresh1
2238b8851fccSafresh1"Conditional Requests" [RFC7232]
2239b8851fccSafresh1
2240b8851fccSafresh1=item *
2241b8851fccSafresh1
2242b8851fccSafresh1"Range Requests" [RFC7233]
2243b8851fccSafresh1
2244b8851fccSafresh1=item *
2245b8851fccSafresh1
2246b8851fccSafresh1"Caching" [RFC7234]
2247b8851fccSafresh1
2248b8851fccSafresh1=item *
2249b8851fccSafresh1
2250b8851fccSafresh1"Authentication" [RFC7235]
2251b8851fccSafresh1
2252b8851fccSafresh1=back
2253b8851fccSafresh1
2254898184e3SsthenIt attempts to meet all "MUST" requirements of the specification, but does not
2255b8851fccSafresh1implement all "SHOULD" requirements.  (Note: it was developed against the
2256b8851fccSafresh1earlier RFC 2616 specification and may not yet meet the revised RFC 7230-7235
2257eac174f2Safresh1spec.) Additionally, HTTP::Tiny supports the C<PATCH> method of RFC 5789.
2258898184e3Ssthen
2259898184e3SsthenSome particular limitations of note include:
2260898184e3Ssthen
2261898184e3Ssthen=over
2262898184e3Ssthen
2263898184e3Ssthen=item *
2264898184e3Ssthen
2265898184e3SsthenHTTP::Tiny focuses on correct transport.  Users are responsible for ensuring
2266898184e3Ssthenthat user-defined headers and content are compliant with the HTTP/1.1
2267898184e3Ssthenspecification.
2268898184e3Ssthen
2269898184e3Ssthen=item *
2270898184e3Ssthen
2271898184e3SsthenUsers must ensure that URLs are properly escaped for unsafe characters and that
2272898184e3Sstheninternational domain names are properly encoded to ASCII. See L<URI::Escape>,
2273898184e3SsthenL<URI::_punycode> and L<Net::IDN::Encode>.
2274898184e3Ssthen
2275898184e3Ssthen=item *
2276898184e3Ssthen
2277898184e3SsthenRedirection is very strict against the specification.  Redirection is only
2278b8851fccSafresh1automatic for response codes 301, 302, 307 and 308 if the request method is
2279b8851fccSafresh1'GET' or 'HEAD'.  Response code 303 is always converted into a 'GET'
2280b8851fccSafresh1redirection, as mandated by the specification.  There is no automatic support
2281b8851fccSafresh1for status 305 ("Use proxy") redirections.
2282898184e3Ssthen
2283898184e3Ssthen=item *
2284898184e3Ssthen
2285898184e3SsthenThere is no provision for delaying a request body using an C<Expect> header.
2286898184e3SsthenUnexpected C<1XX> responses are silently ignored as per the specification.
2287898184e3Ssthen
2288898184e3Ssthen=item *
2289898184e3Ssthen
2290898184e3SsthenOnly 'chunked' C<Transfer-Encoding> is supported.
2291898184e3Ssthen
2292898184e3Ssthen=item *
2293898184e3Ssthen
2294898184e3SsthenThere is no support for a Request-URI of '*' for the 'OPTIONS' request.
2295898184e3Ssthen
22969f11ffb7Safresh1=item *
22979f11ffb7Safresh1
22989f11ffb7Safresh1Headers mentioned in the RFCs and some other, well-known headers are
22999f11ffb7Safresh1generated with their canonical case.  Other headers are sent in the
23009f11ffb7Safresh1case provided by the user.  Except for control headers (which are sent first),
23019f11ffb7Safresh1headers are sent in arbitrary order.
23029f11ffb7Safresh1
2303898184e3Ssthen=back
2304898184e3Ssthen
23056fb12b70Safresh1Despite the limitations listed above, HTTP::Tiny is considered
23066fb12b70Safresh1feature-complete.  New feature requests should be directed to
23076fb12b70Safresh1L<HTTP::Tiny::UA>.
23086fb12b70Safresh1
2309898184e3Ssthen=head1 SEE ALSO
2310898184e3Ssthen
2311898184e3Ssthen=over 4
2312898184e3Ssthen
2313898184e3Ssthen=item *
2314898184e3Ssthen
23156fb12b70Safresh1L<HTTP::Tiny::UA> - Higher level UA features for HTTP::Tiny
2316898184e3Ssthen
231791f110e0Safresh1=item *
231891f110e0Safresh1
23196fb12b70Safresh1L<HTTP::Thin> - HTTP::Tiny wrapper with L<HTTP::Request>/L<HTTP::Response> compatibility
232091f110e0Safresh1
232191f110e0Safresh1=item *
232291f110e0Safresh1
23236fb12b70Safresh1L<HTTP::Tiny::Mech> - Wrap L<WWW::Mechanize> instance in HTTP::Tiny compatible interface
232491f110e0Safresh1
232591f110e0Safresh1=item *
232691f110e0Safresh1
23276fb12b70Safresh1L<IO::Socket::IP> - Required for IPv6 support
23286fb12b70Safresh1
23296fb12b70Safresh1=item *
23306fb12b70Safresh1
23316fb12b70Safresh1L<IO::Socket::SSL> - Required for SSL support
23326fb12b70Safresh1
23336fb12b70Safresh1=item *
23346fb12b70Safresh1
23356fb12b70Safresh1L<LWP::UserAgent> - If HTTP::Tiny isn't enough for you, this is the "standard" way to do things
23366fb12b70Safresh1
23376fb12b70Safresh1=item *
23386fb12b70Safresh1
23396fb12b70Safresh1L<Mozilla::CA> - Required if you want to validate SSL certificates
23406fb12b70Safresh1
23416fb12b70Safresh1=item *
23426fb12b70Safresh1
23436fb12b70Safresh1L<Net::SSLeay> - Required for SSL support
234491f110e0Safresh1
2345898184e3Ssthen=back
2346898184e3Ssthen
2347eac174f2Safresh1=for :stopwords cpan testmatrix url bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
2348898184e3Ssthen
2349898184e3Ssthen=head1 SUPPORT
2350898184e3Ssthen
2351898184e3Ssthen=head2 Bugs / Feature Requests
2352898184e3Ssthen
2353898184e3SsthenPlease report any bugs or feature requests through the issue tracker
2354*3d61058aSafresh1at L<https://github.com/Perl-Toolchain-Gang/HTTP-Tiny/issues>.
2355898184e3SsthenYou will be notified automatically of any progress on your issue.
2356898184e3Ssthen
2357898184e3Ssthen=head2 Source Code
2358898184e3Ssthen
2359898184e3SsthenThis is open source software.  The code repository is available for
2360898184e3Ssthenpublic review and contribution under the terms of the license.
2361898184e3Ssthen
2362*3d61058aSafresh1L<https://github.com/Perl-Toolchain-Gang/HTTP-Tiny>
2363898184e3Ssthen
2364*3d61058aSafresh1  git clone https://github.com/Perl-Toolchain-Gang/HTTP-Tiny.git
2365898184e3Ssthen
2366898184e3Ssthen=head1 AUTHORS
2367898184e3Ssthen
2368898184e3Ssthen=over 4
2369898184e3Ssthen
2370898184e3Ssthen=item *
2371898184e3Ssthen
2372898184e3SsthenChristian Hansen <chansen@cpan.org>
2373898184e3Ssthen
2374898184e3Ssthen=item *
2375898184e3Ssthen
2376898184e3SsthenDavid Golden <dagolden@cpan.org>
2377898184e3Ssthen
23786fb12b70Safresh1=back
23796fb12b70Safresh1
23806fb12b70Safresh1=head1 CONTRIBUTORS
23816fb12b70Safresh1
2382e0680481Safresh1=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 Felipe Gasper Graham Knop Greg Kennedy James E Keenan Raspass Jeremy Mates Jess Robinson Karen Etheridge Lukas Eklund Martin J. Evans Martin-Louis Bright Matthew Horsfall Michael R. Davis Mike Doherty Nicolas Rochelemagne Olaf Alders Olivier Mengué Petr Písař sanjay-cpu Serguei Trouchelle Shoichi Kaji SkyMarshal Sören Kornetzki Steve Grazzini Stig Palmquist Syohei YOSHIDA Tatsuhiko Miyagawa Tom Hukins Tony Cook Xavier Guimard
2383b8851fccSafresh1
23846fb12b70Safresh1=over 4
23856fb12b70Safresh1
23866fb12b70Safresh1=item *
23876fb12b70Safresh1
23886fb12b70Safresh1Alan Gardner <gardner@pythian.com>
23896fb12b70Safresh1
23906fb12b70Safresh1=item *
23916fb12b70Safresh1
23926fb12b70Safresh1Alessandro Ghedini <al3xbio@gmail.com>
23936fb12b70Safresh1
23946fb12b70Safresh1=item *
23956fb12b70Safresh1
23969f11ffb7Safresh1A. Sinan Unur <nanis@cpan.org>
23979f11ffb7Safresh1
23989f11ffb7Safresh1=item *
23999f11ffb7Safresh1
24006fb12b70Safresh1Brad Gilbert <bgills@cpan.org>
24016fb12b70Safresh1
24026fb12b70Safresh1=item *
24036fb12b70Safresh1
24049f11ffb7Safresh1brian m. carlson <sandals@crustytoothpaste.net>
24059f11ffb7Safresh1
24069f11ffb7Safresh1=item *
24079f11ffb7Safresh1
24086fb12b70Safresh1Chris Nehren <apeiron@cpan.org>
24096fb12b70Safresh1
24106fb12b70Safresh1=item *
24116fb12b70Safresh1
24126fb12b70Safresh1Chris Weyl <cweyl@alumni.drew.edu>
24136fb12b70Safresh1
24146fb12b70Safresh1=item *
24156fb12b70Safresh1
24166fb12b70Safresh1Claes Jakobsson <claes@surfar.nu>
24176fb12b70Safresh1
24186fb12b70Safresh1=item *
24196fb12b70Safresh1
24206fb12b70Safresh1Clinton Gormley <clint@traveljury.com>
24216fb12b70Safresh1
24226fb12b70Safresh1=item *
24236fb12b70Safresh1
24249f11ffb7Safresh1Craig A. Berry <craigberry@mac.com>
24259f11ffb7Safresh1
24269f11ffb7Safresh1=item *
24279f11ffb7Safresh1
2428b46d8ef2Safresh1Craig Berry <cberry@cpan.org>
2429b46d8ef2Safresh1
2430b46d8ef2Safresh1=item *
2431b46d8ef2Safresh1
24329f11ffb7Safresh1David Golden <xdg@xdg.me>
24339f11ffb7Safresh1
24349f11ffb7Safresh1=item *
24359f11ffb7Safresh1
2436b46d8ef2Safresh1David Mitchell <davem@iabyn.com>
2437b46d8ef2Safresh1
2438b46d8ef2Safresh1=item *
2439b46d8ef2Safresh1
2440b8851fccSafresh1Dean Pearce <pearce@pythian.com>
24416fb12b70Safresh1
24426fb12b70Safresh1=item *
24436fb12b70Safresh1
24446fb12b70Safresh1Edward Zborowski <ed@rubensteintech.com>
24456fb12b70Safresh1
24466fb12b70Safresh1=item *
24476fb12b70Safresh1
2448b46d8ef2Safresh1Felipe Gasper <felipe@felipegasper.com>
2449b46d8ef2Safresh1
2450b46d8ef2Safresh1=item *
2451b46d8ef2Safresh1
2452e0680481Safresh1Graham Knop <haarg@haarg.org>
2453e0680481Safresh1
2454e0680481Safresh1=item *
2455e0680481Safresh1
2456eac174f2Safresh1Greg Kennedy <kennedy.greg@gmail.com>
2457eac174f2Safresh1
2458eac174f2Safresh1=item *
2459eac174f2Safresh1
2460eac174f2Safresh1James E Keenan <jkeenan@cpan.org>
2461eac174f2Safresh1
2462eac174f2Safresh1=item *
2463eac174f2Safresh1
2464b8851fccSafresh1James Raspass <jraspass@gmail.com>
2465b8851fccSafresh1
2466b8851fccSafresh1=item *
2467b8851fccSafresh1
2468b8851fccSafresh1Jeremy Mates <jmates@cpan.org>
2469b8851fccSafresh1
2470b8851fccSafresh1=item *
2471b8851fccSafresh1
24726fb12b70Safresh1Jess Robinson <castaway@desert-island.me.uk>
24736fb12b70Safresh1
24746fb12b70Safresh1=item *
24756fb12b70Safresh1
24769f11ffb7Safresh1Karen Etheridge <ether@cpan.org>
24779f11ffb7Safresh1
24789f11ffb7Safresh1=item *
24799f11ffb7Safresh1
24806fb12b70Safresh1Lukas Eklund <leklund@gmail.com>
24816fb12b70Safresh1
24826fb12b70Safresh1=item *
24836fb12b70Safresh1
24846fb12b70Safresh1Martin J. Evans <mjegh@ntlworld.com>
24856fb12b70Safresh1
24866fb12b70Safresh1=item *
24876fb12b70Safresh1
24886fb12b70Safresh1Martin-Louis Bright <mlbright@gmail.com>
24896fb12b70Safresh1
249091f110e0Safresh1=item *
249191f110e0Safresh1
2492eac174f2Safresh1Matthew Horsfall <wolfsage@gmail.com>
2493eac174f2Safresh1
2494eac174f2Safresh1=item *
2495eac174f2Safresh1
2496eac174f2Safresh1Michael R. Davis <mrdvt92@users.noreply.github.com>
2497eac174f2Safresh1
2498eac174f2Safresh1=item *
2499eac174f2Safresh1
250091f110e0Safresh1Mike Doherty <doherty@cpan.org>
250191f110e0Safresh1
25026fb12b70Safresh1=item *
25036fb12b70Safresh1
25049f11ffb7Safresh1Nicolas Rochelemagne <rochelemagne@cpanel.net>
25059f11ffb7Safresh1
25069f11ffb7Safresh1=item *
25079f11ffb7Safresh1
2508b8851fccSafresh1Olaf Alders <olaf@wundersolutions.com>
2509b8851fccSafresh1
2510b8851fccSafresh1=item *
2511b8851fccSafresh1
2512b8851fccSafresh1Olivier Mengué <dolmen@cpan.org>
2513b8851fccSafresh1
2514b8851fccSafresh1=item *
2515b8851fccSafresh1
25166fb12b70Safresh1Petr Písař <ppisar@redhat.com>
25176fb12b70Safresh1
25186fb12b70Safresh1=item *
25196fb12b70Safresh1
2520eac174f2Safresh1sanjay-cpu <snjkmr32@gmail.com>
2521eac174f2Safresh1
2522eac174f2Safresh1=item *
2523eac174f2Safresh1
2524b46d8ef2Safresh1Serguei Trouchelle <stro@cpan.org>
2525b46d8ef2Safresh1
2526b46d8ef2Safresh1=item *
2527b46d8ef2Safresh1
2528b46d8ef2Safresh1Shoichi Kaji <skaji@cpan.org>
2529b46d8ef2Safresh1
2530b46d8ef2Safresh1=item *
2531b46d8ef2Safresh1
25329f11ffb7Safresh1SkyMarshal <skymarshal1729@gmail.com>
25339f11ffb7Safresh1
25349f11ffb7Safresh1=item *
25359f11ffb7Safresh1
2536b8851fccSafresh1Sören Kornetzki <soeren.kornetzki@delti.com>
25376fb12b70Safresh1
25386fb12b70Safresh1=item *
25396fb12b70Safresh1
25409f11ffb7Safresh1Steve Grazzini <steve.grazzini@grantstreet.com>
25419f11ffb7Safresh1
25429f11ffb7Safresh1=item *
25439f11ffb7Safresh1
2544e0680481Safresh1Stig Palmquist <git@stig.io>
2545e0680481Safresh1
2546e0680481Safresh1=item *
2547e0680481Safresh1
25486fb12b70Safresh1Syohei YOSHIDA <syohex@gmail.com>
25496fb12b70Safresh1
25506fb12b70Safresh1=item *
25516fb12b70Safresh1
2552b8851fccSafresh1Tatsuhiko Miyagawa <miyagawa@bulknews.net>
2553b8851fccSafresh1
2554b8851fccSafresh1=item *
2555b8851fccSafresh1
2556b8851fccSafresh1Tom Hukins <tom@eborcom.com>
2557b8851fccSafresh1
2558b8851fccSafresh1=item *
2559b8851fccSafresh1
25606fb12b70Safresh1Tony Cook <tony@develop-help.com>
25616fb12b70Safresh1
2562eac174f2Safresh1=item *
2563eac174f2Safresh1
2564eac174f2Safresh1Xavier Guimard <yadd@debian.org>
2565eac174f2Safresh1
2566898184e3Ssthen=back
2567898184e3Ssthen
2568898184e3Ssthen=head1 COPYRIGHT AND LICENSE
2569898184e3Ssthen
2570e0680481Safresh1This software is copyright (c) 2023 by Christian Hansen.
2571898184e3Ssthen
2572898184e3SsthenThis is free software; you can redistribute it and/or modify it under
2573898184e3Ssthenthe same terms as the Perl 5 programming language system itself.
2574898184e3Ssthen
2575898184e3Ssthen=cut
2576