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