summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MANIFEST56
-rwxr-xr-xPorting/Maintainers.pl13
-rw-r--r--cpan/HTTP-Tiny/lib/HTTP/Tiny.pm1069
-rw-r--r--cpan/HTTP-Tiny/t/00-compile.t56
-rw-r--r--cpan/HTTP-Tiny/t/000_load.t21
-rw-r--r--cpan/HTTP-Tiny/t/001_api.t34
-rw-r--r--cpan/HTTP-Tiny/t/002_croakage.t47
-rw-r--r--cpan/HTTP-Tiny/t/010_url.t42
-rw-r--r--cpan/HTTP-Tiny/t/020_headers.t59
-rw-r--r--cpan/HTTP-Tiny/t/030_response.t43
-rw-r--r--cpan/HTTP-Tiny/t/040_content.t48
-rw-r--r--cpan/HTTP-Tiny/t/050_chunked_body.t66
-rw-r--r--cpan/HTTP-Tiny/t/060_http_date.t37
-rw-r--r--cpan/HTTP-Tiny/t/100_get.t113
-rw-r--r--cpan/HTTP-Tiny/t/110_mirror.t98
-rw-r--r--cpan/HTTP-Tiny/t/120_put.t82
-rw-r--r--cpan/HTTP-Tiny/t/130_redirect.t78
-rw-r--r--cpan/HTTP-Tiny/t/Util.pm177
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-01.txt17
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-02.txt22
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-03.txt13
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-04.txt17
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-05.txt21
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-06.txt19
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-07.txt19
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-08.txt21
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-09.txt16
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-10.txt21
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-11.txt22
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-12.txt17
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-13.txt21
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-14.txt22
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-15.txt17
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-16.txt19
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-17.txt19
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-18.txt36
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-19.txt22
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-20.txt21
-rw-r--r--cpan/HTTP-Tiny/t/cases/get-21.txt23
-rw-r--r--cpan/HTTP-Tiny/t/cases/mirror-01.txt18
-rw-r--r--cpan/HTTP-Tiny/t/cases/mirror-02.txt14
-rw-r--r--cpan/HTTP-Tiny/t/cases/mirror-03.txt17
-rw-r--r--cpan/HTTP-Tiny/t/cases/mirror-04.txt13
-rw-r--r--cpan/HTTP-Tiny/t/cases/mirror-05.txt20
-rw-r--r--cpan/HTTP-Tiny/t/cases/put-01.txt22
-rw-r--r--cpan/HTTP-Tiny/t/cases/put-02.txt24
-rw-r--r--cpan/HTTP-Tiny/t/cases/put-03.txt25
-rw-r--r--cpan/HTTP-Tiny/t/cases/put-04.txt19
-rw-r--r--cpan/HTTP-Tiny/t/cases/put-05.txt27
-rw-r--r--cpan/HTTP-Tiny/t/cases/redirect-01.txt33
-rw-r--r--cpan/HTTP-Tiny/t/cases/redirect-02.txt50
-rw-r--r--cpan/HTTP-Tiny/t/cases/redirect-03.txt50
-rw-r--r--cpan/HTTP-Tiny/t/cases/redirect-04.txt50
-rw-r--r--cpan/HTTP-Tiny/t/cases/redirect-05.txt48
-rw-r--r--cpan/HTTP-Tiny/t/cases/redirect-06.txt33
-rw-r--r--cpan/HTTP-Tiny/t/cases/redirect-07.txt33
-rw-r--r--cpan/HTTP-Tiny/t/cases/redirect-08.txt19
-rw-r--r--cpan/HTTP-Tiny/t/cases/redirect-09.txt35
-rw-r--r--lib/.gitignore1
-rw-r--r--pod/perldelta.pod8
60 files changed, 3123 insertions, 0 deletions
diff --git a/MANIFEST b/MANIFEST
index a031bc0893..20d71f60b7 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1029,6 +1029,62 @@ cpan/Getopt-Long/t/gol-linkage.t See if Getopt::Long works
cpan/Getopt-Long/t/gol-oo.t See if Getopt::Long works
cpan/Getopt-Long/t/gol-xargv.t See if Getopt::Long works
cpan/Getopt-Long/t/gol-xstring.t See if Getopt::Long works
+cpan/HTTP-Tiny/lib/HTTP/Tiny.pm
+cpan/HTTP-Tiny/t/000_load.t
+cpan/HTTP-Tiny/t/001_api.t
+cpan/HTTP-Tiny/t/002_croakage.t
+cpan/HTTP-Tiny/t/00-compile.t
+cpan/HTTP-Tiny/t/010_url.t
+cpan/HTTP-Tiny/t/020_headers.t
+cpan/HTTP-Tiny/t/030_response.t
+cpan/HTTP-Tiny/t/040_content.t
+cpan/HTTP-Tiny/t/050_chunked_body.t
+cpan/HTTP-Tiny/t/060_http_date.t
+cpan/HTTP-Tiny/t/100_get.t
+cpan/HTTP-Tiny/t/110_mirror.t
+cpan/HTTP-Tiny/t/120_put.t
+cpan/HTTP-Tiny/t/130_redirect.t
+cpan/HTTP-Tiny/t/cases/get-01.txt
+cpan/HTTP-Tiny/t/cases/get-02.txt
+cpan/HTTP-Tiny/t/cases/get-03.txt
+cpan/HTTP-Tiny/t/cases/get-04.txt
+cpan/HTTP-Tiny/t/cases/get-05.txt
+cpan/HTTP-Tiny/t/cases/get-06.txt
+cpan/HTTP-Tiny/t/cases/get-07.txt
+cpan/HTTP-Tiny/t/cases/get-08.txt
+cpan/HTTP-Tiny/t/cases/get-09.txt
+cpan/HTTP-Tiny/t/cases/get-10.txt
+cpan/HTTP-Tiny/t/cases/get-11.txt
+cpan/HTTP-Tiny/t/cases/get-12.txt
+cpan/HTTP-Tiny/t/cases/get-13.txt
+cpan/HTTP-Tiny/t/cases/get-14.txt
+cpan/HTTP-Tiny/t/cases/get-15.txt
+cpan/HTTP-Tiny/t/cases/get-16.txt
+cpan/HTTP-Tiny/t/cases/get-17.txt
+cpan/HTTP-Tiny/t/cases/get-18.txt
+cpan/HTTP-Tiny/t/cases/get-19.txt
+cpan/HTTP-Tiny/t/cases/get-20.txt
+cpan/HTTP-Tiny/t/cases/get-21.txt
+cpan/HTTP-Tiny/t/cases/mirror-01.txt
+cpan/HTTP-Tiny/t/cases/mirror-02.txt
+cpan/HTTP-Tiny/t/cases/mirror-03.txt
+cpan/HTTP-Tiny/t/cases/mirror-04.txt
+cpan/HTTP-Tiny/t/cases/mirror-05.txt
+cpan/HTTP-Tiny/t/cases/put-01.txt
+cpan/HTTP-Tiny/t/cases/put-02.txt
+cpan/HTTP-Tiny/t/cases/put-03.txt
+cpan/HTTP-Tiny/t/cases/put-04.txt
+cpan/HTTP-Tiny/t/cases/put-05.txt
+cpan/HTTP-Tiny/t/cases/redirect-01.txt
+cpan/HTTP-Tiny/t/cases/redirect-02.txt
+cpan/HTTP-Tiny/t/cases/redirect-03.txt
+cpan/HTTP-Tiny/t/cases/redirect-04.txt
+cpan/HTTP-Tiny/t/cases/redirect-05.txt
+cpan/HTTP-Tiny/t/cases/redirect-06.txt
+cpan/HTTP-Tiny/t/cases/redirect-07.txt
+cpan/HTTP-Tiny/t/cases/redirect-08.txt
+cpan/HTTP-Tiny/t/cases/redirect-09.txt
+cpan/HTTP-Tiny/t/Util.pm
cpan/IO-Compress/Changes IO::Compress
cpan/IO-Compress/examples/compress-zlib/filtdef IO::Compress
cpan/IO-Compress/examples/compress-zlib/filtinf IO::Compress
diff --git a/Porting/Maintainers.pl b/Porting/Maintainers.pl
index 6057edabe6..a212efcc69 100755
--- a/Porting/Maintainers.pl
+++ b/Porting/Maintainers.pl
@@ -765,6 +765,19 @@ use File::Glob qw(:case);
'UPSTREAM' => 'cpan',
},
+ 'HTTP::Tiny' =>
+ {
+ 'MAINTAINER' => 'dagolden',
+ 'DISTRIBUTION' => 'DAGOLDEN/HTTP-Tiny-0.008.tar.gz',
+ 'FILES' => q[cpan/HTTP-Tiny],
+ 'EXCLUDED' => [
+ 't/200_live.t',
+ qr/^eg/,
+ qr/^xt/
+ ],
+ 'UPSTREAM' => 'cpan',
+ },
+
'I18N::Collate' =>
{
'MAINTAINER' => 'p5p',
diff --git a/cpan/HTTP-Tiny/lib/HTTP/Tiny.pm b/cpan/HTTP-Tiny/lib/HTTP/Tiny.pm
new file mode 100644
index 0000000000..5a22982ea8
--- /dev/null
+++ b/cpan/HTTP-Tiny/lib/HTTP/Tiny.pm
@@ -0,0 +1,1069 @@
+# vim: ts=4 sts=4 sw=4 et:
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+package HTTP::Tiny;
+BEGIN {
+ $HTTP::Tiny::VERSION = '0.008';
+}
+use strict;
+use warnings;
+# ABSTRACT: A small, simple, correct HTTP/1.1 client
+
+use Carp ();
+
+
+my @attributes;
+BEGIN {
+ @attributes = qw(agent default_headers max_redirect max_size proxy timeout);
+ no strict 'refs';
+ for my $accessor ( @attributes ) {
+ *{$accessor} = sub {
+ @_ > 1 ? $_[0]->{$accessor} = $_[1] : $_[0]->{$accessor};
+ };
+ }
+}
+
+sub new {
+ my($class, %args) = @_;
+ (my $agent = $class) =~ s{::}{-}g;
+ my $self = {
+ agent => $agent . "/" . ($class->VERSION || 0),
+ max_redirect => 5,
+ timeout => 60,
+ };
+ for my $key ( @attributes ) {
+ $self->{$key} = $args{$key} if exists $args{$key}
+ }
+ return bless $self, $class;
+}
+
+
+sub get {
+ my ($self, $url, $args) = @_;
+ @_ == 2 || (@_ == 3 && ref $args eq 'HASH')
+ or Carp::croak(q/Usage: $http->get(URL, [HASHREF])/);
+ return $self->request('GET', $url, $args || {});
+}
+
+
+sub mirror {
+ my ($self, $url, $file, $args) = @_;
+ @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
+ or Carp::croak(q/Usage: $http->mirror(URL, FILE, [HASHREF])/);
+ if ( -e $file and my $mtime = (stat($file))[9] ) {
+ $args->{headers}{'if-modified-since'} ||= $self->_http_date($mtime);
+ }
+ my $tempfile = $file . int(rand(2**31));
+ open my $fh, ">", $tempfile
+ or Carp::croak(qq/Error: Could not open temporary file $tempfile for downloading: $!/);
+ $args->{data_callback} = sub { print {$fh} $_[0] };
+ my $response = $self->request('GET', $url, $args);
+ close $fh
+ or Carp::croak(qq/Error: Could not close temporary file $tempfile: $!/);
+ if ( $response->{success} ) {
+ rename $tempfile, $file
+ or Carp::croak "Error replacing $file with $tempfile: $!\n";
+ my $lm = $response->{headers}{'last-modified'};
+ if ( $lm and my $mtime = $self->_parse_http_date($lm) ) {
+ utime $mtime, $mtime, $file;
+ }
+ }
+ $response->{success} ||= $response->{status} eq '304';
+ unlink $tempfile;
+ return $response;
+}
+
+
+my %idempotent = map { $_ => 1 } qw/GET HEAD PUT DELETE OPTIONS TRACE/;
+
+sub request {
+ my ($self, $method, $url, $args) = @_;
+ @_ == 3 || (@_ == 4 && ref $args eq 'HASH')
+ or Carp::croak(q/Usage: $http->request(METHOD, URL, [HASHREF])/);
+ $args ||= {}; # we keep some state in this during _request
+
+ # RFC 2616 Section 8.1.4 mandates a single retry on broken socket
+ my $response;
+ for ( 0 .. 1 ) {
+ $response = eval { $self->_request($method, $url, $args) };
+ last unless $@ && $idempotent{$method}
+ && $@ =~ m{^(?:Socket closed|Unexpected end)};
+ }
+
+ if (my $e = "$@") {
+ $response = {
+ success => q{},
+ status => 599,
+ reason => 'Internal Exception',
+ content => $e,
+ headers => {
+ 'content-type' => 'text/plain',
+ 'content-length' => length $e,
+ }
+ };
+ }
+ return $response;
+}
+
+my %DefaultPort = (
+ http => 80,
+ https => 443,
+);
+
+sub _request {
+ my ($self, $method, $url, $args) = @_;
+
+ my ($scheme, $host, $port, $path_query) = $self->_split_url($url);
+
+ my $request = {
+ method => $method,
+ scheme => $scheme,
+ host_port => ($port == $DefaultPort{$scheme} ? $host : "$host:$port"),
+ uri => $path_query,
+ headers => {},
+ };
+
+ my $handle = HTTP::Tiny::Handle->new(timeout => $self->{timeout});
+
+ if ($self->{proxy}) {
+ $request->{uri} = "$scheme://$request->{host_port}$path_query";
+ croak(qq/HTTPS via proxy is not supported/)
+ if $request->{scheme} eq 'https';
+ $handle->connect(($self->_split_url($self->{proxy}))[0..2]);
+ }
+ else {
+ $handle->connect($scheme, $host, $port);
+ }
+
+ $self->_prepare_headers_and_cb($request, $args);
+ $handle->write_request($request);
+
+ my $response;
+ do { $response = $handle->read_response_header }
+ until (substr($response->{status},0,1) ne '1');
+
+ if ( my @redir_args = $self->_maybe_redirect($request, $response, $args) ) {
+ $handle->close;
+ return $self->_request(@redir_args, $args);
+ }
+
+ if ($method eq 'HEAD' || $response->{status} =~ /^[23]04/) {
+ # response has no message body
+ }
+ else {
+ my $data_cb = $self->_prepare_data_cb($response, $args);
+ $handle->read_body($data_cb, $response);
+ }
+
+ $handle->close;
+ $response->{success} = substr($response->{status},0,1) eq '2';
+ return $response;
+}
+
+sub _prepare_headers_and_cb {
+ my ($self, $request, $args) = @_;
+
+ for ($self->{default_headers}, $args->{headers}) {
+ next unless defined;
+ while (my ($k, $v) = each %$_) {
+ $request->{headers}{lc $k} = $v;
+ }
+ }
+ $request->{headers}{'host'} = $request->{host_port};
+ $request->{headers}{'connection'} = "close";
+ $request->{headers}{'user-agent'} ||= $self->{agent};
+
+ if (defined $args->{content}) {
+ $request->{headers}{'content-type'} ||= "application/octet-stream";
+ if (ref $args->{content} eq 'CODE') {
+ $request->{headers}{'transfer-encoding'} = 'chunked'
+ unless $request->{headers}{'content-length'}
+ || $request->{headers}{'transfer-encoding'};
+ $request->{cb} = $args->{content};
+ }
+ else {
+ my $content = $args->{content};
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($content, 1)
+ or Carp::croak(q/Wide character in request message body/);
+ }
+ $request->{headers}{'content-length'} = length $content
+ unless $request->{headers}{'content-length'}
+ || $request->{headers}{'transfer-encoding'};
+ $request->{cb} = sub { substr $content, 0, length $content, '' };
+ }
+ $request->{trailer_cb} = $args->{trailer_callback}
+ if ref $args->{trailer_callback} eq 'CODE';
+ }
+ return;
+}
+
+sub _prepare_data_cb {
+ my ($self, $response, $args) = @_;
+ my $data_cb = $args->{data_callback};
+ $response->{content} = '';
+
+ if (!$data_cb || $response->{status} !~ /^2/) {
+ if (defined $self->{max_size}) {
+ $data_cb = sub {
+ $_[1]->{content} .= $_[0];
+ die(qq/Size of response body exceeds the maximum allowed of $self->{max_size}\n/)
+ if length $_[1]->{content} > $self->{max_size};
+ };
+ }
+ else {
+ $data_cb = sub { $_[1]->{content} .= $_[0] };
+ }
+ }
+ return $data_cb;
+}
+
+sub _maybe_redirect {
+ my ($self, $request, $response, $args) = @_;
+ my $headers = $response->{headers};
+ my ($status, $method) = ($response->{status}, $request->{method});
+ if (($status eq '303' or ($status =~ /^30[127]/ && $method =~ /^GET|HEAD$/))
+ and $headers->{location}
+ and ++$args->{redirects} <= $self->{max_redirect}
+ ) {
+ my $location = ($headers->{location} =~ /^\//)
+ ? "$request->{scheme}://$request->{host_port}$headers->{location}"
+ : $headers->{location} ;
+ return (($status eq '303' ? 'GET' : $method), $location);
+ }
+ return;
+}
+
+sub _split_url {
+ my $url = pop;
+
+ # URI regex adapted from the URI module
+ my ($scheme, $authority, $path_query) = $url =~ m<\A([^:/?#]+)://([^/?#]*)([^#]*)>
+ or Carp::croak(qq/Cannot parse URL: '$url'/);
+
+ $scheme = lc $scheme;
+ $path_query = "/$path_query" unless $path_query =~ m<\A/>;
+
+ my $host = (length($authority)) ? lc $authority : 'localhost';
+ $host =~ s/\A[^@]*@//; # userinfo
+ my $port = do {
+ $host =~ s/:([0-9]*)\z// && length $1
+ ? $1
+ : ($scheme eq 'http' ? 80 : $scheme eq 'https' ? 443 : undef);
+ };
+
+ return ($scheme, $host, $port, $path_query);
+}
+
+# Date conversions adapted from HTTP::Date
+my $DoW = "Sun|Mon|Tue|Wed|Thu|Fri|Sat";
+my $MoY = "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec";
+sub _http_date {
+ my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($_[1]);
+ return sprintf("%s, %02d %s %04d %02d:%02d:%02d GMT",
+ substr($DoW,$wday*4,3),
+ $mday, substr($MoY,$mon*4,3), $year+1900,
+ $hour, $min, $sec
+ );
+}
+
+sub _parse_http_date {
+ my ($self, $str) = @_;
+ require Time::Local;
+ my @tl_parts;
+ if ($str =~ /^[SMTWF][a-z]+, +(\d{1,2}) ($MoY) +(\d\d\d\d) +(\d\d):(\d\d):(\d\d) +GMT$/) {
+ @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
+ }
+ elsif ($str =~ /^[SMTWF][a-z]+, +(\d\d)-($MoY)-(\d{2,4}) +(\d\d):(\d\d):(\d\d) +GMT$/ ) {
+ @tl_parts = ($6, $5, $4, $1, (index($MoY,$2)/4), $3);
+ }
+ elsif ($str =~ /^[SMTWF][a-z]+ +($MoY) +(\d{1,2}) +(\d\d):(\d\d):(\d\d) +(?:[^0-9]+ +)?(\d\d\d\d)$/ ) {
+ @tl_parts = ($5, $4, $3, $2, (index($MoY,$1)/4), $6);
+ }
+ return eval {
+ my $t = @tl_parts ? Time::Local::timegm(@tl_parts) : -1;
+ $t < 0 ? undef : $t;
+ };
+}
+
+package
+ HTTP::Tiny::Handle; # hide from PAUSE/indexers
+use strict;
+use warnings;
+
+use Carp qw[croak];
+use Errno qw[EINTR EPIPE];
+use IO::Socket qw[SOCK_STREAM];
+
+sub BUFSIZE () { 32768 }
+
+my $Printable = sub {
+ local $_ = shift;
+ s/\r/\\r/g;
+ s/\n/\\n/g;
+ s/\t/\\t/g;
+ s/([^\x20-\x7E])/sprintf('\\x%.2X', ord($1))/ge;
+ $_;
+};
+
+my $Token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
+
+sub new {
+ my ($class, %args) = @_;
+ return bless {
+ rbuf => '',
+ timeout => 60,
+ max_line_size => 16384,
+ max_header_lines => 64,
+ %args
+ }, $class;
+}
+
+sub connect {
+ @_ == 4 || croak(q/Usage: $handle->connect(scheme, host, port)/);
+ my ($self, $scheme, $host, $port) = @_;
+
+ if ( $scheme eq 'https' ) {
+ eval "require IO::Socket::SSL"
+ unless exists $INC{'IO/Socket/SSL.pm'};
+ croak(qq/IO::Socket::SSL must be installed for https support\n/)
+ unless $INC{'IO/Socket/SSL.pm'};
+ }
+ elsif ( $scheme ne 'http' ) {
+ croak(qq/Unsupported URL scheme '$scheme'/);
+ }
+
+ $self->{fh} = 'IO::Socket::INET'->new(
+ PeerHost => $host,
+ PeerPort => $port,
+ Proto => 'tcp',
+ Type => SOCK_STREAM,
+ Timeout => $self->{timeout}
+ ) or croak(qq/Could not connect to '$host:$port': $@/);
+
+ binmode($self->{fh})
+ or croak(qq/Could not binmode() socket: '$!'/);
+
+ if ( $scheme eq 'https') {
+ IO::Socket::SSL->start_SSL($self->{fh});
+ ref($self->{fh}) eq 'IO::Socket::SSL'
+ and $self->{fh}->verify_hostname( $host, 'http' )
+ or croak(qq/SSL connection failed for $host\n/);
+ }
+
+ $self->{host} = $host;
+ $self->{port} = $port;
+
+ return $self;
+}
+
+sub close {
+ @_ == 1 || croak(q/Usage: $handle->close()/);
+ my ($self) = @_;
+ CORE::close($self->{fh})
+ or croak(qq/Could not close socket: '$!'/);
+}
+
+sub write {
+ @_ == 2 || croak(q/Usage: $handle->write(buf)/);
+ my ($self, $buf) = @_;
+
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($buf, 1)
+ or croak(q/Wide character in write()/);
+ }
+
+ my $len = length $buf;
+ my $off = 0;
+
+ local $SIG{PIPE} = 'IGNORE';
+
+ while () {
+ $self->can_write
+ or croak(q/Timed out while waiting for socket to become ready for writing/);
+ my $r = syswrite($self->{fh}, $buf, $len, $off);
+ if (defined $r) {
+ $len -= $r;
+ $off += $r;
+ last unless $len > 0;
+ }
+ elsif ($! == EPIPE) {
+ croak(qq/Socket closed by remote server: $!/);
+ }
+ elsif ($! != EINTR) {
+ croak(qq/Could not write to socket: '$!'/);
+ }
+ }
+ return $off;
+}
+
+sub read {
+ @_ == 2 || @_ == 3 || croak(q/Usage: $handle->read(len [, allow_partial])/);
+ my ($self, $len, $allow_partial) = @_;
+
+ my $buf = '';
+ my $got = length $self->{rbuf};
+
+ if ($got) {
+ my $take = ($got < $len) ? $got : $len;
+ $buf = substr($self->{rbuf}, 0, $take, '');
+ $len -= $take;
+ }
+
+ while ($len > 0) {
+ $self->can_read
+ or croak(q/Timed out while waiting for socket to become ready for reading/);
+ my $r = sysread($self->{fh}, $buf, $len, length $buf);
+ if (defined $r) {
+ last unless $r;
+ $len -= $r;
+ }
+ elsif ($! != EINTR) {
+ croak(qq/Could not read from socket: '$!'/);
+ }
+ }
+ if ($len && !$allow_partial) {
+ croak(q/Unexpected end of stream/);
+ }
+ return $buf;
+}
+
+sub readline {
+ @_ == 1 || croak(q/Usage: $handle->readline()/);
+ my ($self) = @_;
+
+ while () {
+ if ($self->{rbuf} =~ s/\A ([^\x0D\x0A]* \x0D?\x0A)//x) {
+ return $1;
+ }
+ if (length $self->{rbuf} >= $self->{max_line_size}) {
+ croak(qq/Line size exceeds the maximum allowed size of $self->{max_line_size}/);
+ }
+ $self->can_read
+ or croak(q/Timed out while waiting for socket to become ready for reading/);
+ my $r = sysread($self->{fh}, $self->{rbuf}, BUFSIZE, length $self->{rbuf});
+ if (defined $r) {
+ last unless $r;
+ }
+ elsif ($! != EINTR) {
+ croak(qq/Could not read from socket: '$!'/);
+ }
+ }
+ croak(q/Unexpected end of stream while looking for line/);
+}
+
+sub read_header_lines {
+ @_ == 1 || @_ == 2 || croak(q/Usage: $handle->read_header_lines([headers])/);
+ my ($self, $headers) = @_;
+ $headers ||= {};
+ my $lines = 0;
+ my $val;
+
+ while () {
+ my $line = $self->readline;
+
+ if (++$lines >= $self->{max_header_lines}) {
+ croak(qq/Header lines exceeds maximum number allowed of $self->{max_header_lines}/);
+ }
+ elsif ($line =~ /\A ([^\x00-\x1F\x7F:]+) : [\x09\x20]* ([^\x0D\x0A]*)/x) {
+ my ($field_name) = lc $1;
+ if (exists $headers->{$field_name}) {
+ for ($headers->{$field_name}) {
+ $_ = [$_] unless ref $_ eq "ARRAY";
+ push @$_, $2;
+ $val = \$_->[-1];
+ }
+ }
+ else {
+ $val = \($headers->{$field_name} = $2);
+ }
+ }
+ elsif ($line =~ /\A [\x09\x20]+ ([^\x0D\x0A]*)/x) {
+ $val
+ or croak(q/Unexpected header continuation line/);
+ next unless length $1;
+ $$val .= ' ' if length $$val;
+ $$val .= $1;
+ }
+ elsif ($line =~ /\A \x0D?\x0A \z/x) {
+ last;
+ }
+ else {
+ croak(q/Malformed header line: / . $Printable->($line));
+ }
+ }
+ return $headers;
+}
+
+sub write_request {
+ @_ == 2 || croak(q/Usage: $handle->write_request(request)/);
+ my($self, $request) = @_;
+ $self->write_request_header(@{$request}{qw/method uri headers/});
+ $self->write_body($request) if $request->{cb};
+ return;
+}
+
+my %HeaderCase = (
+ 'content-md5' => 'Content-MD5',
+ 'etag' => 'ETag',
+ 'te' => 'TE',
+ 'www-authenticate' => 'WWW-Authenticate',
+ 'x-xss-protection' => 'X-XSS-Protection',
+);
+
+sub write_header_lines {
+ (@_ == 2 && ref $_[1] eq 'HASH') || croak(q/Usage: $handle->write_header_lines(headers)/);
+ my($self, $headers) = @_;
+
+ my $buf = '';
+ while (my ($k, $v) = each %$headers) {
+ my $field_name = lc $k;
+ if (exists $HeaderCase{$field_name}) {
+ $field_name = $HeaderCase{$field_name};
+ }
+ else {
+ $field_name =~ /\A $Token+ \z/xo
+ or croak(q/Invalid HTTP header field name: / . $Printable->($field_name));
+ $field_name =~ s/\b(\w)/\u$1/g;
+ $HeaderCase{lc $field_name} = $field_name;
+ }
+ for (ref $v eq 'ARRAY' ? @$v : $v) {
+ /[^\x0D\x0A]/
+ or croak(qq/Invalid HTTP header field value ($field_name): / . $Printable->($_));
+ $buf .= "$field_name: $_\x0D\x0A";
+ }
+ }
+ $buf .= "\x0D\x0A";
+ return $self->write($buf);
+}
+
+sub read_body {
+ @_ == 3 || croak(q/Usage: $handle->read_body(callback, response)/);
+ my ($self, $cb, $response) = @_;
+ my $te = $response->{headers}{'transfer-encoding'} || '';
+ if ( grep { /chunked/i } ( ref $te eq 'ARRAY' ? @$te : $te ) ) {
+ $self->read_chunked_body($cb, $response);
+ }
+ else {
+ $self->read_content_body($cb, $response);
+ }
+ return;
+}
+
+sub write_body {
+ @_ == 2 || croak(q/Usage: $handle->write_body(request)/);
+ my ($self, $request) = @_;
+ if ($request->{headers}{'content-length'}) {
+ return $self->write_content_body($request);
+ }
+ else {
+ return $self->write_chunked_body($request);
+ }
+}
+
+sub read_content_body {
+ @_ == 3 || @_ == 4 || croak(q/Usage: $handle->read_content_body(callback, response, [read_length])/);
+ my ($self, $cb, $response, $content_length) = @_;
+ $content_length ||= $response->{headers}{'content-length'};
+
+ if ( $content_length ) {
+ my $len = $content_length;
+ while ($len > 0) {
+ my $read = ($len > BUFSIZE) ? BUFSIZE : $len;
+ $cb->($self->read($read, 0), $response);
+ $len -= $read;
+ }
+ }
+ else {
+ my $chunk;
+ $cb->($chunk, $response) while length( $chunk = $self->read(BUFSIZE, 1) );
+ }
+
+ return;
+}
+
+sub write_content_body {
+ @_ == 2 || croak(q/Usage: $handle->write_content_body(request)/);
+ my ($self, $request) = @_;
+
+ my ($len, $content_length) = (0, $request->{headers}{'content-length'});
+ while () {
+ my $data = $request->{cb}->();
+
+ defined $data && length $data
+ or last;
+
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($data, 1)
+ or croak(q/Wide character in write_content()/);
+ }
+
+ $len += $self->write($data);
+ }
+
+ $len == $content_length
+ or croak(qq/Content-Length missmatch (got: $len expected: $content_length)/);
+
+ return $len;
+}
+
+sub read_chunked_body {
+ @_ == 3 || croak(q/Usage: $handle->read_chunked_body(callback, $response)/);
+ my ($self, $cb, $response) = @_;
+
+ while () {
+ my $head = $self->readline;
+
+ $head =~ /\A ([A-Fa-f0-9]+)/x
+ or croak(q/Malformed chunk head: / . $Printable->($head));
+
+ my $len = hex($1)
+ or last;
+
+ $self->read_content_body($cb, $response, $len);
+
+ $self->read(2) eq "\x0D\x0A"
+ or croak(q/Malformed chunk: missing CRLF after chunk data/);
+ }
+ $self->read_header_lines($response->{headers});
+ return;
+}
+
+sub write_chunked_body {
+ @_ == 2 || croak(q/Usage: $handle->write_chunked_body(request)/);
+ my ($self, $request) = @_;
+
+ my $len = 0;
+ while () {
+ my $data = $request->{cb}->();
+
+ defined $data && length $data
+ or last;
+
+ if ( $] ge '5.008' ) {
+ utf8::downgrade($data, 1)
+ or croak(q/Wide character in write_chunked_body()/);
+ }
+
+ $len += length $data;
+
+ my $chunk = sprintf '%X', length $data;
+ $chunk .= "\x0D\x0A";
+ $chunk .= $data;
+ $chunk .= "\x0D\x0A";
+
+ $self->write($chunk);
+ }
+ $self->write("0\x0D\x0A");
+ $self->write_header_lines($request->{trailer_cb}->())
+ if ref $request->{trailer_cb} eq 'CODE';
+ return $len;
+}
+
+sub read_response_header {
+ @_ == 1 || croak(q/Usage: $handle->read_response_header()/);
+ my ($self) = @_;
+
+ my $line = $self->readline;
+
+ $line =~ /\A (HTTP\/(0*\d+\.0*\d+)) [\x09\x20]+ ([0-9]{3}) [\x09\x20]+ ([^\x0D\x0A]*) \x0D?\x0A/x
+ or croak(q/Malformed Status-Line: / . $Printable->($line));
+
+ my ($protocol, $version, $status, $reason) = ($1, $2, $3, $4);
+
+ croak (qq/Unsupported HTTP protocol: $protocol/)
+ unless $version =~ /0*1\.0*[01]/;
+
+ return {
+ status => $status,
+ reason => $reason,
+ headers => $self->read_header_lines,
+ protocol => $protocol,
+ };
+}
+
+sub write_request_header {
+ @_ == 4 || croak(q/Usage: $handle->write_request_header(method, request_uri, headers)/);
+ my ($self, $method, $request_uri, $headers) = @_;
+
+ return $self->write("$method $request_uri HTTP/1.1\x0D\x0A")
+ + $self->write_header_lines($headers);
+}
+
+sub _do_timeout {
+ my ($self, $type, $timeout) = @_;
+ $timeout = $self->{timeout}
+ unless defined $timeout && $timeout >= 0;
+
+ my $fd = fileno $self->{fh};
+ defined $fd && $fd >= 0
+ or croak(q/select(2): 'Bad file descriptor'/);
+
+ my $initial = time;
+ my $pending = $timeout;
+ my $nfound;
+
+ vec(my $fdset = '', $fd, 1) = 1;
+
+ while () {
+ $nfound = ($type eq 'read')
+ ? select($fdset, undef, undef, $pending)
+ : select(undef, $fdset, undef, $pending) ;
+ if ($nfound == -1) {
+ $! == EINTR
+ or croak(qq/select(2): '$!'/);
+ redo if !$timeout || ($pending = $timeout - (time - $initial)) > 0;
+ $nfound = 0;
+ }
+ last;
+ }
+ $! = 0;
+ return $nfound;
+}
+
+sub can_read {
+ @_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_read([timeout])/);
+ my $self = shift;
+ return $self->_do_timeout('read', @_)
+}
+
+sub can_write {
+ @_ == 1 || @_ == 2 || croak(q/Usage: $handle->can_write([timeout])/);
+ my $self = shift;
+ return $self->_do_timeout('write', @_)
+}
+
+1;
+
+
+
+__END__
+=pod
+
+=head1 NAME
+
+HTTP::Tiny - A small, simple, correct HTTP/1.1 client
+
+=head1 VERSION
+
+version 0.008
+
+=head1 SYNOPSIS
+
+ use HTTP::Tiny;
+
+ my $response = HTTP::Tiny->new->get('http://example.com/');
+
+ die "Failed!\n" unless $response->{success};
+
+ print "$response->{status} $response->{reason}\n";
+
+ while (my ($k, $v) = each %{$response->{headers}}) {
+ for (ref $v eq 'ARRAY' ? @$v : $v) {
+ print "$k: $_\n";
+ }
+ }
+
+ print $response->{content} if length $response->{content};
+
+=head1 DESCRIPTION
+
+This is a very simple HTTP/1.1 client, designed primarily for doing simple GET
+requests without the overhead of a large framework like L<LWP::UserAgent>.
+
+It is more correct and more complete than L<HTTP::Lite>. It supports
+proxies (currently only non-authenticating ones) and redirection. It
+also correctly resumes after EINTR.
+
+=head1 METHODS
+
+=head2 new
+
+ $http = HTTP::Tiny->new( %attributes );
+
+This constructor returns a new HTTP::Tiny object. Valid attributes include:
+
+=over 4
+
+=item *
+
+agent
+
+A user-agent string (defaults to 'HTTP::Tiny/$VERSION')
+
+=item *
+
+default_headers
+
+A hashref of default headers to apply to requests
+
+=item *
+
+max_redirect
+
+Maximum number of redirects allowed (defaults to 5)
+
+=item *
+
+max_size
+
+Maximum response size (only when not using a data callback). If defined,
+responses larger than this will die with an error message
+
+=item *
+
+proxy
+
+URL of a proxy server to use.
+
+=item *
+
+timeout
+
+Request timeout in seconds (default is 60)
+
+=back
+
+=head2 get
+
+ $response = $http->get($url);
+ $response = $http->get($url, \%options);
+
+Executes a C<GET> request for the given URL. The URL must have unsafe
+characters escaped and international domain names encoded. Internally, it just
+calls C<request()> with 'GET' as the method. See C<request()> for valid
+options and a description of the response.
+
+=head2 mirror
+
+ $response = $http->mirror($url, $file, \%options)
+ if ( $response->{success} ) {
+ print "$file is up to date\n";
+ }
+
+Executes a C<GET> request for the URL and saves the response body to the file
+name provided. The URL must have unsafe characters escaped and international
+domain names encoded. If the file already exists, the request will includes an
+C<If-Modified-Since> header with the modification timestamp of the file. You
+may specificy a different C<If-Modified-Since> header yourself in the C<<
+$options->{headers} >> hash.
+
+The C<success> field of the response will be true if the status code is 2XX
+or 304 (unmodified).
+
+If the file was modified and the server response includes a properly
+formatted C<Last-Modified> header, the file modification time will
+be updated accordingly.
+
+=head2 request
+
+ $response = $http->request($method, $url);
+ $response = $http->request($method, $url, \%options);
+
+Executes an HTTP request of the given method type ('GET', 'HEAD', 'PUT', etc.)
+on the given URL. The URL must have unsafe characters escaped and
+international domain names encoded. A hashref of options may be appended to
+modify the request.
+
+Valid options are:
+
+=over 4
+
+=item *
+
+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.
+
+=item *
+
+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 response
+
+=item *
+
+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)
+
+=item *
+
+data_callback
+
+A code reference that will be called for each chunks of the response
+body received.
+
+=back
+
+If the C<content> option is a code reference, it will be called iteratively
+to provide the content body of the request. It should return the empty
+string or undef when the iterator is exhausted.
+
+If the C<data_callback> option is provided, it will be called iteratively until
+the entire response body is received. The first argument will be a string
+containing a chunk of the response body, the second argument will be the
+in-progress response hash reference, as described below. (This allows
+customizing the action of the callback based on the C<status> or C<headers>
+received prior to the content body.)
+
+The C<request> method returns a hashref containing the response. The hashref
+will have the following keys:
+
+=over 4
+
+=item *
+
+success
+
+Boolean indicating whether the operation returned a 2XX status code
+
+=item *
+
+status
+
+The HTTP status code of the response
+
+=item *
+
+reason
+
+The response phrase returned by the server
+
+=item *
+
+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
+
+=item *
+
+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
+
+=back
+
+On an exception during the execution of the request, the C<status> field will
+contain 599, and the C<content> field will contain the text of the exception.
+
+=for Pod::Coverage agent
+default_headers
+max_redirect
+max_size
+proxy
+timeout
+
+=head1 LIMITATIONS
+
+HTTP::Tiny is I<conditionally compliant> with the
+L<HTTP/1.1 specification|http://www.w3.org/Protocols/rfc2616/rfc2616.html>.
+It attempts to meet all "MUST" requirements of the specification, but does not
+implement all "SHOULD" requirements.
+
+Some particular limitations of note include:
+
+=over
+
+=item *
+
+HTTP::Tiny focuses on correct transport. Users are responsible for ensuring
+that user-defined headers and content are compliant with the HTTP/1.1
+specification.
+
+=item *
+
+Users must ensure that URLs are properly escaped for unsafe characters and that
+international domain names are properly encoded to ASCII. See L<URI::Escape>,
+L<URI::_punycode> and L<Net::IDN::Encode>.
+
+=item *
+
+Redirection is very strict against the specification. Redirection is only
+automatic for response codes 301, 302 and 307 if the request method is 'GET' or
+'HEAD'. Response code 303 is always converted into a 'GET' redirection, as
+mandated by the specification. There is no automatic support for status 305
+("Use proxy") redirections.
+
+=item *
+
+Persistant connections are not supported. The C<Connection> header will
+always be set to C<close>.
+
+=item *
+
+Direct C<https> connections are supported only if L<IO::Socket::SSL> is
+installed. There is no support for C<https> connections via proxy.
+
+=item *
+
+Cookies are not directly supported. Users that set a C<Cookie> header
+should also set C<max_redirect> to zero to ensure cookies are not
+inappropriately re-transmitted.
+
+=item *
+
+Proxy environment variables are not supported.
+
+=item *
+
+There is no provision for delaying a request body using an C<Expect> header.
+Unexpected C<1XX> responses are silently ignored as per the specification.
+
+=item *
+
+Only 'chunked' C<Transfer-Encoding> is supported.
+
+=item *
+
+There is no support for a Request-URI of '*' for the 'OPTIONS' request.
+
+=back
+
+=head1 SEE ALSO
+
+=over 4
+
+=item *
+
+L<LWP::UserAgent>
+
+=back
+
+=head1 AUTHORS
+
+=over 4
+
+=item *
+
+Christian Hansen <chansen@cpan.org>
+
+=item *
+
+David Golden <dagolden@cpan.org>
+
+=back
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2011 by Christian Hansen.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
+
diff --git a/cpan/HTTP-Tiny/t/00-compile.t b/cpan/HTTP-Tiny/t/00-compile.t
new file mode 100644
index 0000000000..277185d8e5
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/00-compile.t
@@ -0,0 +1,56 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use Test::More;
+
+
+
+use File::Find;
+use File::Temp qw{ tempdir };
+
+my @modules;
+find(
+ sub {
+ return if $File::Find::name !~ /\.pm\z/;
+ my $found = $File::Find::name;
+ $found =~ s{^lib/}{};
+ $found =~ s{[/\\]}{::}g;
+ $found =~ s/\.pm$//;
+ # nothing to skip
+ push @modules, $found;
+ },
+ 'lib',
+);
+
+my @scripts = glob "bin/*";
+
+my $plan = scalar(@modules) + scalar(@scripts);
+$plan ? (plan tests => $plan) : (plan skip_all => "no tests to run");
+
+{
+ # fake home for cpan-testers
+ local $ENV{HOME} = tempdir( CLEANUP => 1 );
+
+ like( qx{ $^X -Ilib -e "require $_; print '$_ ok'" }, qr/^\s*$_ ok/s, "$_ loaded ok" )
+ for sort @modules;
+
+ SKIP: {
+ eval "use Test::Script 1.05; 1;";
+ skip "Test::Script needed to test script compilation", scalar(@scripts) if $@;
+ foreach my $file ( @scripts ) {
+ my $script = $file;
+ $script =~ s!.*/!!;
+ script_compiles( $file, "$script script compiles" );
+ }
+ }
+}
diff --git a/cpan/HTTP-Tiny/t/000_load.t b/cpan/HTTP-Tiny/t/000_load.t
new file mode 100644
index 0000000000..b625b15d3a
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/000_load.t
@@ -0,0 +1,21 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+BEGIN {
+ use_ok('HTTP::Tiny');
+}
+
+diag("HTTP::Tiny $HTTP::Tiny::VERSION, Perl $], $^X");
+
diff --git a/cpan/HTTP-Tiny/t/001_api.t b/cpan/HTTP-Tiny/t/001_api.t
new file mode 100644
index 0000000000..12050a07a0
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/001_api.t
@@ -0,0 +1,34 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+use HTTP::Tiny;
+
+my @accessors = qw(agent default_headers max_redirect max_size proxy timeout);
+my @methods = qw(new get request mirror);
+
+my %api;
+@api{@accessors} = (1) x @accessors;
+@api{@methods} = (1) x @accessors;
+
+can_ok('HTTP::Tiny', @methods, @accessors);
+
+my @extra =
+ grep {! $api{$_} }
+ grep { $_ !~ /\A_/ }
+ grep {; no strict 'refs'; *{"HTTP::Tiny::$_"}{CODE} }
+ sort keys %HTTP::Tiny::;
+
+ok( ! scalar @extra, "No unexpected subroutines defined" )
+ or diag "Found: @extra";
+
diff --git a/cpan/HTTP-Tiny/t/002_croakage.t b/cpan/HTTP-Tiny/t/002_croakage.t
new file mode 100644
index 0000000000..317f0a7cb4
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/002_croakage.t
@@ -0,0 +1,47 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use Test::More;
+use HTTP::Tiny;
+
+my %usage = (
+ 'get' => q/Usage: $http->get(URL, [HASHREF])/,
+ 'mirror' => q/Usage: $http->mirror(URL, FILE, [HASHREF])/,
+ 'request' => q/Usage: $http->request(METHOD, URL, [HASHREF])/,
+);
+
+my @cases = (
+ ['get'],
+ ['get','http://www.example.com/','extra'],
+ ['get','http://www.example.com/','extra', 'extra'],
+ ['mirror'],
+ ['mirror','http://www.example.com/',],
+ ['mirror','http://www.example.com/','extra', 'extra'],
+ ['mirror','http://www.example.com/','extra', 'extra', 'extra'],
+ ['request'],
+ ['request','GET'],
+ ['request','GET','http://www.example.com/','extra'],
+ ['request','GET','http://www.example.com/','extra', 'extra'],
+);
+
+my $http = HTTP::Tiny->new;
+
+for my $c ( @cases ) {
+ my ($method, @args) = @$c;
+ eval {$http->$method(@args)};
+ my $err = $@;
+ like ($err, qr/\Q$usage{$method}\E/, join("|",@$c) );
+}
+
+done_testing;
+
diff --git a/cpan/HTTP-Tiny/t/010_url.t b/cpan/HTTP-Tiny/t/010_url.t
new file mode 100644
index 0000000000..58ba1179ab
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/010_url.t
@@ -0,0 +1,42 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use Test::More;
+use HTTP::Tiny;
+
+my @tests = (
+ [ 'HtTp://Example.COM/', 'http', 'example.com', 80, '/' ],
+ [ 'HtTp://Example.com:1024/', 'http', 'example.com', 1024, '/' ],
+ [ 'http://example.com', 'http', 'example.com', 80, '/' ],
+ [ 'http://example.com:', 'http', 'example.com', 80, '/' ],
+ [ 'http://foo@example.com:', 'http', 'example.com', 80, '/' ],
+ [ 'http://@example.com:', 'http', 'example.com', 80, '/' ],
+ [ 'http://example.com?foo=bar', 'http', 'example.com', 80, '/?foo=bar' ],
+ [ 'http://example.com?foo=bar#fragment', 'http', 'example.com', 80, '/?foo=bar' ],
+ [ 'http://example.com/path?foo=bar', 'http', 'example.com', 80, '/path?foo=bar' ],
+ [ 'http:///path?foo=bar', 'http', 'localhost', 80, '/path?foo=bar' ],
+ [ 'HTTPS://example.com/', 'https', 'example.com', 443, '/' ],
+ [ 'http://[::]:1024', 'http', '[::]', 1024, '/' ],
+ [ 'xxx://foo/', 'xxx', 'foo', undef, '/' ],
+);
+
+plan tests => scalar @tests;
+
+for my $test (@tests) {
+ my $url = shift(@$test);
+ my $got = [ HTTP::Tiny->_split_url($url) ];
+ my $exp = $test;
+ is_deeply($got, $exp, "->split_url('$url')");
+}
+
+
diff --git a/cpan/HTTP-Tiny/t/020_headers.t b/cpan/HTTP-Tiny/t/020_headers.t
new file mode 100644
index 0000000000..3a6fc63b8d
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/020_headers.t
@@ -0,0 +1,59 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use Test::More qw[no_plan];
+use t::Util qw[tmpfile rewind $CRLF $LF];
+use HTTP::Tiny;
+
+{
+ no warnings 'redefine';
+ sub HTTP::Tiny::Handle::can_read { 1 };
+ sub HTTP::Tiny::Handle::can_write { 1 };
+}
+
+{
+ my $header = join $CRLF, 'Foo: Foo', 'Foo: Baz', 'Bar: Bar', '', '';
+ my $fh = tmpfile($header);
+ my $exp = { foo => ['Foo', 'Baz'], bar => 'Bar' };
+ my $handle = HTTP::Tiny::Handle->new(fh => $fh);
+ my $got = $handle->read_header_lines;
+ is_deeply($got, $exp, "->read_header_lines() CRLF");
+}
+
+{
+ my $header = join $LF, 'Foo: Foo', 'Foo: Baz', 'Bar: Bar', '', '';
+ my $fh = tmpfile($header);
+ my $exp = { foo => ['Foo', 'Baz'], bar => 'Bar' };
+ my $handle = HTTP::Tiny::Handle->new(fh => $fh);
+ my $got = $handle->read_header_lines;
+ is_deeply($got, $exp, "->read_header_lines() LF");
+}
+
+{
+ my $header = "Foo: $CRLF\x09Bar$CRLF\x09$CRLF\x09Baz$CRLF$CRLF";
+ my $fh = tmpfile($header);
+ my $exp = { foo => 'Bar Baz' };
+ my $handle = HTTP::Tiny::Handle->new(fh => $fh);
+ my $got = $handle->read_header_lines;
+ is_deeply($got, $exp, "->read_header_lines() insane continuations");
+}
+
+{
+ my $fh = tmpfile();
+ my $handle = HTTP::Tiny::Handle->new(fh => $fh);
+ my $headers = { foo => ['Foo', 'Baz'], bar => 'Bar' };
+ $handle->write_header_lines($headers);
+ rewind($fh);
+ is_deeply($handle->read_header_lines, $headers, "roundtrip header lines");
+}
+
diff --git a/cpan/HTTP-Tiny/t/030_response.t b/cpan/HTTP-Tiny/t/030_response.t
new file mode 100644
index 0000000000..fbeea58133
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/030_response.t
@@ -0,0 +1,43 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use Test::More qw[no_plan];
+use t::Util qw[tmpfile rewind $CRLF $LF];
+use HTTP::Tiny;
+
+sub _header {
+ return [ @{$_[0]}{qw/status reason headers protocol/} ]
+}
+
+{
+ no warnings 'redefine';
+ sub HTTP::Tiny::Handle::can_read { 1 };
+ sub HTTP::Tiny::Handle::can_write { 1 };
+}
+
+{
+ my $response = join $CRLF, 'HTTP/1.1 200 OK', 'Foo: Foo', 'Bar: Bar', '', '';
+ my $fh = tmpfile($response);
+ my $handle = HTTP::Tiny::Handle->new(fh => $fh);
+ my $exp = [ 200, 'OK', { foo => 'Foo', bar => 'Bar' }, 'HTTP/1.1' ];
+ is_deeply(_header($handle->read_response_header), $exp, "->read_response_header CRLF");
+}
+
+{
+ my $response = join $LF, 'HTTP/1.1 200 OK', 'Foo: Foo', 'Bar: Bar', '', '';
+ my $fh = tmpfile($response);
+ my $handle = HTTP::Tiny::Handle->new(fh => $fh);
+ my $exp = [ 200, 'OK', { foo => 'Foo', bar => 'Bar' }, 'HTTP/1.1' ];
+ is_deeply(_header($handle->read_response_header), $exp, "->read_response_header LF");
+}
+
diff --git a/cpan/HTTP-Tiny/t/040_content.t b/cpan/HTTP-Tiny/t/040_content.t
new file mode 100644
index 0000000000..e69bbedc4b
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/040_content.t
@@ -0,0 +1,48 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use Test::More qw[no_plan];
+use t::Util qw[tmpfile rewind $CRLF $LF];
+use HTTP::Tiny;
+
+{
+ no warnings 'redefine';
+ sub HTTP::Tiny::Handle::can_read { 1 };
+ sub HTTP::Tiny::Handle::can_write { 1 };
+}
+
+{
+ my $chunk = join('', '0' .. '9', 'A' .. 'Z', 'a' .. 'z', '_', $LF) x 16; # 1024
+ my $fh = tmpfile();
+ my $handle = HTTP::Tiny::Handle->new(fh => $fh);
+ my $nchunks = 128;
+ my $length = $nchunks * length $chunk;
+
+ {
+ my $request = {
+ cb => sub { $nchunks-- ? $chunk : undef },
+ headers => { 'content-length' => $length }
+ };
+ my $got = $handle->write_content_body($request);
+ is($got, $length, "written $length octets");
+ }
+
+ rewind($fh);
+
+ {
+ my $got = 0;
+ $handle->read_content_body(sub { $got += length $_[0] }, {}, $length);
+ is($got, $length, "read $length octets");
+ }
+}
+
diff --git a/cpan/HTTP-Tiny/t/050_chunked_body.t b/cpan/HTTP-Tiny/t/050_chunked_body.t
new file mode 100644
index 0000000000..16c9cf7a7c
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/050_chunked_body.t
@@ -0,0 +1,66 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use Test::More qw[no_plan];
+use t::Util qw[tmpfile rewind $CRLF];
+use HTTP::Tiny;
+
+{
+ no warnings 'redefine';
+ sub HTTP::Tiny::Handle::can_read { 1 };
+ sub HTTP::Tiny::Handle::can_write { 1 };
+}
+
+{
+ my $body = join($CRLF, map { sprintf('%x', length $_) . $CRLF . $_ } 'A'..'Z', '') . $CRLF;
+ my $fh = tmpfile($body);
+ my $handle = HTTP::Tiny::Handle->new(fh => $fh);
+ my $exp = ['A'..'Z'];
+ my $got = [];
+ my $cb = sub { push @$got, $_[0] };
+ my $response = { headers => {} };
+ $handle->read_chunked_body($cb, $response);
+ is_deeply($response->{headers}, {}, 'chunked trailers');
+ is_deeply($got, $exp, "chunked chunks");
+}
+
+{
+ my $fh = tmpfile();
+ my $handle = HTTP::Tiny::Handle->new(fh => $fh);
+
+ my $exp = ['A'..'Z'];
+ my $trailers = { foo => 'Bar', bar => 'Baz' };
+ my $got = [];
+
+ {
+ my @chunks = @$exp;
+ my $request = {
+ cb => sub { shift @chunks },
+ trailer_cb => sub { $trailers },
+ };
+ $handle->write_chunked_body($request);
+ }
+
+ rewind($fh);
+
+ {
+ my $cb = sub { push @$got, $_[0] };
+ my $response = { headers => {} };
+ $handle->read_chunked_body($cb, $response);
+ is_deeply($response->{headers}, $trailers, 'roundtrip chunked trailers');
+ }
+
+ is_deeply($got, $exp, "roundtrip chunked chunks");
+}
+
+
diff --git a/cpan/HTTP-Tiny/t/060_http_date.t b/cpan/HTTP-Tiny/t/060_http_date.t
new file mode 100644
index 0000000000..bd7077a05d
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/060_http_date.t
@@ -0,0 +1,37 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use Test::More;
+use HTTP::Tiny;
+
+# test cases adapted from HTTP::Date
+my $epoch = 760233600;
+
+my @cases = (
+ ['Thu, 03 Feb 1994 00:00:00 GMT', 'RFC822+RFC1123'],
+ ['Thu, 3 Feb 1994 00:00:00 GMT', 'broken RFC822+RFC1123'],
+ ['Thursday, 03-Feb-94 00:00:00 GMT', 'old rfc850 HTTP format'],
+ ['Thursday, 03-Feb-1994 00:00:00 GMT', 'broken rfc850 HTTP format'],
+ ['Thu Feb 3 00:00:00 GMT 1994', 'ctime format'],
+ ['Thu Feb 3 00:00:00 1994', 'same as ctime, except no TZ'],
+);
+
+plan tests => 1 + @cases;
+
+is(HTTP::Tiny->_http_date($epoch), $cases[0][0], "epoch -> RFC822/RFC1123");
+
+for my $c ( @cases ) {
+ is( HTTP::Tiny->_parse_http_date($c->[0]), $epoch, $c->[1] . " -> epoch");
+}
+
+
diff --git a/cpan/HTTP-Tiny/t/100_get.t b/cpan/HTTP-Tiny/t/100_get.t
new file mode 100644
index 0000000000..1d9dd91f12
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/100_get.t
@@ -0,0 +1,113 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use File::Basename;
+use Test::More 0.88;
+use t::Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case
+ hashify connect_args set_socket_source sort_headers $CRLF $LF];
+
+use HTTP::Tiny;
+BEGIN { monkey_patch() }
+
+for my $file ( dir_list("t/cases", qr/^get/ ) ) {
+ my $label = basename($file);
+ my $data = do { local (@ARGV,$/) = $file; <> };
+ my ($params, $expect_req, $give_res) = split /--+\n/, $data;
+ my $case = parse_case($params);
+
+ my $url = $case->{url}[0];
+ my %headers = hashify( $case->{headers} );
+ my %new_args = hashify( $case->{new_args} );
+
+ my %options;
+ $options{headers} = \%headers if %headers;
+ if ( $case->{data_cb} ) {
+ $main::data = '';
+ $options{data_callback} = eval join "\n", @{$case->{data_cb}};
+ die unless ref( $options{data_callback} ) eq 'CODE';
+ }
+
+ my $version = HTTP::Tiny->VERSION || 0;
+ my $agent = $new_args{agent} || "HTTP-Tiny/$version";
+
+ # cleanup source data
+ $expect_req =~ s{HTTP-Tiny/VERSION}{$agent};
+ s{\n}{$CRLF}g for ($expect_req, $give_res);
+
+ # setup mocking and test
+ my $res_fh = tmpfile($give_res);
+ my $req_fh = tmpfile();
+
+ my $http = HTTP::Tiny->new(%new_args);
+ set_socket_source($req_fh, $res_fh);
+
+ (my $url_basename = $url) =~ s{.*/}{};
+
+ my @call_args = %options ? ($url, \%options) : ($url);
+ my $response = $http->get(@call_args);
+
+ my ($got_host, $got_port) = connect_args();
+ my ($exp_host, $exp_port) = (
+ ($new_args{proxy} || $url ) =~ m{^http://([^:/]+?):?(\d*)/}g
+ );
+ $exp_host ||= 'localhost';
+ $exp_port ||= 80;
+
+ my $got_req = slurp($req_fh);
+
+ is ($got_host, $exp_host, "$label host $exp_host");
+ is ($got_port, $exp_port, "$label port $exp_port");
+ is( sort_headers($got_req), sort_headers($expect_req), "$label request data");
+
+ my ($rc) = $give_res =~ m{\S+\s+(\d+)}g;
+ # maybe override
+ $rc = $case->{expected_rc}[0] if defined $case->{expected_rc};
+
+ is( $response->{status}, $rc, "$label response code $rc" )
+ or diag $response->{content};
+
+ if ( substr($rc,0,1) eq '2' ) {
+ ok( $response->{success}, "$label success flag true" );
+ }
+ else {
+ ok( ! $response->{success}, "$label success flag false" );
+ }
+
+ if (defined $case->{expected_headers}) {
+ my %expected = hashify( $case->{expected_headers} );
+ is_deeply($response->{headers}, \%expected, "$label expected headers");
+ }
+
+ my $check_expected = $case->{expected_like}
+ ? sub {
+ my ($text, $msg) = @_;
+ like( $text, "/".$case->{expected_like}[0]."/", $msg );
+ }
+ : sub {
+ my ($text, $msg) = @_;
+ my $exp_content =
+ $case->{expected} ? join("$CRLF", @{$case->{expected}}, '') : '';
+ is ( $text, $exp_content, $msg );
+ }
+ ;
+
+ if ( $options{data_callback} ) {
+ $check_expected->( $main::data, "$label cb got content" );
+ is ( $response->{content}, '', "$label resp content empty" );
+ }
+ else {
+ $check_expected->( $response->{content}, "$label content" );
+ }
+}
+
+done_testing;
diff --git a/cpan/HTTP-Tiny/t/110_mirror.t b/cpan/HTTP-Tiny/t/110_mirror.t
new file mode 100644
index 0000000000..34e0c90666
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/110_mirror.t
@@ -0,0 +1,98 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use File::Basename;
+use Test::More 0.88;
+use t::Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case
+ set_socket_source sort_headers $CRLF $LF];
+use HTTP::Tiny;
+use File::Temp qw/tempdir/;
+
+BEGIN { monkey_patch() }
+
+my $tempdir = tempdir( TMPDIR => 1, CLEANUP => 1 );
+my $tempfile = $tempdir . "/tempfile.txt";
+
+my $known_epoch = 760233600;
+my $day = 24*3600;
+
+my %timestamp = (
+ 'modified.txt' => $known_epoch - 2 * $day,
+ 'not-modified.txt' => $known_epoch - 2 * $day,
+);
+
+for my $file ( dir_list("t/cases", qr/^mirror/ ) ) {
+ unlink $tempfile;
+ my $data = do { local (@ARGV,$/) = $file; <> };
+ my ($params, $expect_req, $give_res) = split /--+\n/, $data;
+ # cleanup source data
+ my $version = HTTP::Tiny->VERSION || 0;
+ $expect_req =~ s{VERSION}{$version};
+ s{\n}{$CRLF}g for ($expect_req, $give_res);
+
+ # figure out what request to make
+ my $case = parse_case($params);
+ my $url = $case->{url}->[0];
+ my %options;
+
+ my %headers;
+ for my $line ( @{ $case->{headers} } ) {
+ my ($k,$v) = ($line =~ m{^([^:]+): (.*)$}g);
+ $headers{$k} = $v;
+ }
+ $options{headers} = \%headers if %headers;
+
+ # maybe create a file
+ (my $url_basename = $url) =~ s{.*/}{};
+ if ( my $mtime = $timestamp{$url_basename} ) {
+ open my $fh, ">", $tempfile;
+ close $fh;
+ utime $mtime, $mtime, $tempfile;
+ }
+
+ # setup mocking and test
+ my $res_fh = tmpfile($give_res);
+ my $req_fh = tmpfile();
+
+ my $http = HTTP::Tiny->new;
+ set_socket_source($req_fh, $res_fh);
+
+ my @call_args = %options ? ($url, $tempfile, \%options) : ($url, $tempfile);
+ my $response = $http->mirror(@call_args);
+
+ my $got_req = slurp($req_fh);
+
+ my $label = basename($file);
+
+ is( sort_headers($got_req), sort_headers($expect_req), "$label request" );
+
+ my ($rc) = $give_res =~ m{\S+\s+(\d+)}g;
+ is( $response->{status}, $rc, "$label response code $rc" )
+ or diag $response->{content};
+
+ if ( substr($rc,0,1) eq '2' ) {
+ ok( $response->{success}, "$label success flag true" );
+ ok( -e $tempfile, "$label file created" );
+ }
+ elsif ( $rc eq '304' ) {
+ ok( $response->{success}, "$label success flag true" );
+ is( (stat($tempfile))[9], $timestamp{$url_basename},
+ "$label file not overwritten" );
+ }
+ else {
+ ok( ! $response->{success}, "$label success flag false" );
+ ok( ! -e $tempfile, "$label file not created" );
+ }
+}
+
+done_testing;
diff --git a/cpan/HTTP-Tiny/t/120_put.t b/cpan/HTTP-Tiny/t/120_put.t
new file mode 100644
index 0000000000..07fa27dc5c
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/120_put.t
@@ -0,0 +1,82 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use File::Basename;
+use Test::More 0.88;
+use t::Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case
+ set_socket_source sort_headers $CRLF $LF];
+use HTTP::Tiny;
+BEGIN { monkey_patch() }
+
+for my $file ( dir_list("t/cases", qr/^put/ ) ) {
+ my $data = do { local (@ARGV,$/) = $file; <> };
+ my ($params, $expect_req, $give_res) = split /--+\n/, $data;
+ # cleanup source data
+ my $version = HTTP::Tiny->VERSION || 0;
+ $expect_req =~ s{VERSION}{$version};
+ s{\n}{$CRLF}g for ($expect_req, $give_res);
+
+ # figure out what request to make
+ my $case = parse_case($params);
+ my $url = $case->{url}[0];
+ my %options;
+
+ my %headers;
+ for my $line ( @{ $case->{headers} } ) {
+ my ($k,$v) = ($line =~ m{^([^:]+): (.*)$}g);
+ $headers{$k} = $v;
+ }
+ $options{headers} = \%headers if %headers;
+
+ if ( $case->{content} ) {
+ $options{content} = $case->{content}[0];
+ }
+ elsif ( $case->{content_cb} ) {
+ $options{content} = eval join "\n", @{$case->{content_cb}};
+ }
+
+ if ( $case->{trailer_cb} ) {
+ $options{trailer_callback} = eval join "\n", @{$case->{trailer_cb}};
+ }
+
+ # setup mocking and test
+ my $res_fh = tmpfile($give_res);
+ my $req_fh = tmpfile();
+
+ my $http = HTTP::Tiny->new;
+ set_socket_source($req_fh, $res_fh);
+
+ (my $url_basename = $url) =~ s{.*/}{};
+
+ my @call_args = %options ? ($url, \%options) : ($url);
+ my $response = $http->request('PUT',@call_args);
+
+ my $got_req = slurp($req_fh);
+
+ my $label = basename($file);
+
+ is( sort_headers($got_req), sort_headers($expect_req), "$label request" );
+
+ my ($rc) = $give_res =~ m{\S+\s+(\d+)}g;
+ is( $response->{status}, $rc, "$label response code $rc" )
+ or diag $response->{content};
+
+ if ( substr($rc,0,1) eq '2' ) {
+ ok( $response->{success}, "$label success flag true" );
+ }
+ else {
+ ok( ! $response->{success}, "$label success flag false" );
+ }
+}
+
+done_testing;
diff --git a/cpan/HTTP-Tiny/t/130_redirect.t b/cpan/HTTP-Tiny/t/130_redirect.t
new file mode 100644
index 0000000000..9848321043
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/130_redirect.t
@@ -0,0 +1,78 @@
+#!perl
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+
+use strict;
+use warnings;
+
+use File::Basename;
+use Test::More 0.88;
+use t::Util qw[tmpfile rewind slurp monkey_patch dir_list parse_case
+ hashify connect_args clear_socket_source set_socket_source sort_headers
+ $CRLF $LF];
+
+use HTTP::Tiny;
+BEGIN { monkey_patch() }
+
+for my $file ( dir_list("t/cases", qr/^redirect/ ) ) {
+ my $label = basename($file);
+ my $data = do { local (@ARGV,$/) = $file; <> };
+ my ($params, @case_pairs) = split /--+\n/, $data;
+ my $case = parse_case($params);
+
+ my $url = $case->{url}[0];
+ my $method = $case->{method}[0] || 'GET';
+ my %headers = hashify( $case->{headers} );
+ my %new_args = hashify( $case->{new_args} );
+
+ my %options;
+ $options{headers} = \%headers if %headers;
+ my $call_args = %options ? [$method, $url, \%options] : [$method, $url];
+
+ my $version = HTTP::Tiny->VERSION || 0;
+ my $agent = $new_args{agent} || "HTTP-Tiny/$version";
+
+ my (@socket_pairs);
+ while ( @case_pairs ) {
+ my ($expect_req, $give_res) = splice( @case_pairs, 0, 2 );
+ # cleanup source data
+ $expect_req =~ s{HTTP-Tiny/VERSION}{$agent};
+ s{\n}{$CRLF}g for ($expect_req, $give_res);
+
+ # setup mocking and test
+ my $req_fh = tmpfile();
+ my $res_fh = tmpfile($give_res);
+
+ push @socket_pairs, [$req_fh, $res_fh, $expect_req];
+ }
+
+ clear_socket_source();
+ set_socket_source(@$_) for @socket_pairs;
+
+ my $http = HTTP::Tiny->new(%new_args);
+ my $response = $http->request(@$call_args);
+
+ my $calls = 0
+ + (defined($new_args{max_redirect}) ? $new_args{max_redirect} : 5);
+
+ for my $i ( 0 .. $calls ) {
+ last unless @socket_pairs;
+ my ($req_fh, $res_fh, $expect_req) = @{ shift @socket_pairs };
+ my $got_req = slurp($req_fh);
+ is( sort_headers($got_req), sort_headers($expect_req), "$label request ($i)");
+ $i++;
+ }
+
+ my $exp_content = $case->{expected}
+ ? join("$CRLF", @{$case->{expected}}) : '';
+
+ is ( $response->{content}, $exp_content, "$label content" );
+}
+
+done_testing;
diff --git a/cpan/HTTP-Tiny/t/Util.pm b/cpan/HTTP-Tiny/t/Util.pm
new file mode 100644
index 0000000000..f6c5f321de
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/Util.pm
@@ -0,0 +1,177 @@
+#
+# This file is part of HTTP-Tiny
+#
+# This software is copyright (c) 2011 by Christian Hansen.
+#
+# This is free software; you can redistribute it and/or modify it under
+# the same terms as the Perl 5 programming language system itself.
+#
+package t::Util;
+
+use strict;
+use warnings;
+
+use IO::File q[SEEK_SET];
+use IO::Dir;
+
+BEGIN {
+ our @EXPORT_OK = qw(
+ rewind
+ tmpfile
+ dir_list
+ slurp
+ parse_case
+ hashify
+ sort_headers
+ connect_args
+ clear_socket_source
+ set_socket_source
+ monkey_patch
+ $CRLF
+ $LF
+ );
+
+ require Exporter;
+ *import = \&Exporter::import;
+}
+
+our $CRLF = "\x0D\x0A";
+our $LF = "\x0A";
+
+sub rewind(*) {
+ seek($_[0], 0, SEEK_SET)
+ || die(qq/Couldn't rewind file handle: '$!'/);
+}
+
+sub tmpfile {
+ my $fh = IO::File->new_tmpfile
+ || die(qq/Couldn't create a new temporary file: '$!'/);
+
+ binmode($fh)
+ || die(qq/Couldn't binmode temporary file handle: '$!'/);
+
+ if (@_) {
+ print({$fh} @_)
+ || die(qq/Couldn't write to temporary file handle: '$!'/);
+
+ seek($fh, 0, SEEK_SET)
+ || die(qq/Couldn't rewind temporary file handle: '$!'/);
+ }
+
+ return $fh;
+}
+
+sub dir_list {
+ my ($dir, $filter) = @_;
+ $filter ||= qr/./;
+ my $d = IO::Dir->new($dir)
+ or return;
+ return map { "$dir/$_" } sort grep { /$filter/ } grep { /^[^.]/ } $d->read;
+}
+
+sub slurp (*) {
+ my ($fh) = @_;
+
+ rewind($fh);
+
+ binmode($fh)
+ || die(qq/Couldn't binmode file handle: '$!'/);
+
+ my $exp = -s $fh;
+ my $buf = do { local $/; <$fh> };
+ my $got = length $buf;
+
+ ($exp == $got)
+ || die(qq[I/O read mismatch (expexted: $exp got: $got)]);
+
+ return $buf;
+}
+
+sub parse_case {
+ my ($case) = @_;
+ my %args;
+ my $key = '';
+ for my $line ( split "\n", $case ) {
+ chomp $line;
+ if ( substr($line,0,1) eq q{ } ) {
+ $line =~ s/^\s+//;
+ push @{$args{$key}}, $line;
+ }
+ else {
+ $key = $line;
+ }
+ }
+ return \%args;
+}
+
+sub hashify {
+ my ($lines) = @_;
+ return unless $lines;
+ my %hash;
+ for my $line ( @$lines ) {
+ my ($k,$v) = ($line =~ m{^([^:]+): (.*)$}g);
+ $hash{$k} = [ $hash{$k} ] if exists $hash{$k} && ref $hash{$k} ne 'ARRAY';
+ if ( ref($hash{$k}) eq 'ARRAY' ) {
+ push @{$hash{$k}}, $v;
+ }
+ else {
+ $hash{$k} = $v;
+ }
+ }
+ return %hash;
+}
+
+sub sort_headers {
+ my ($text) = shift;
+ my @lines = split /$CRLF/, $text;
+ my $request = shift(@lines) || '';
+ my @headers;
+ while (my $line = shift @lines) {
+ last unless length $line;
+ push @headers, $line;
+ }
+ @headers = sort @headers;
+ return join($CRLF, $request, @headers, '', @lines);
+}
+
+{
+ my (@req_fh, @res_fh, $monkey_host, $monkey_port);
+
+ sub clear_socket_source {
+ @req_fh = ();
+ @res_fh = ();
+ }
+
+ sub set_socket_source {
+ my ($req_fh, $res_fh) = @_;
+ push @req_fh, $req_fh;
+ push @res_fh, $res_fh;
+ }
+
+ sub connect_args { return ($monkey_host, $monkey_port) }
+
+ sub monkey_patch {
+ no warnings qw/redefine once/;
+ *HTTP::Tiny::Handle::can_read = sub {1};
+ *HTTP::Tiny::Handle::can_write = sub {1};
+ *HTTP::Tiny::Handle::connect = sub {
+ my ($self, $scheme, $host, $port) = @_;
+ $self->{host} = $monkey_host = $host;
+ $self->{port} = $monkey_port = $port;
+ $self->{fh} = shift @req_fh;
+ return $self;
+ };
+ my $original_write_request = \&HTTP::Tiny::Handle::write_request;
+ *HTTP::Tiny::Handle::write_request = sub {
+ my ($self, $request) = @_;
+ $original_write_request->($self, $request);
+ $self->{fh} = shift @res_fh;
+ };
+ *HTTP::Tiny::Handle::close = sub { 1 }; # don't close our temps
+ }
+}
+
+1;
+
+
+# vim: et ts=4 sts=4 sw=4:
diff --git a/cpan/HTTP-Tiny/t/cases/get-01.txt b/cpan/HTTP-Tiny/t/cases/get-01.txt
new file mode 100644
index 0000000000..e03fb3876c
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-01.txt
@@ -0,0 +1,17 @@
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-02.txt b/cpan/HTTP-Tiny/t/cases/get-02.txt
new file mode 100644
index 0000000000..4b540f4462
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-02.txt
@@ -0,0 +1,22 @@
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+headers
+ Accept: */*
+ X-Custom: This is a custom header
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Accept: */*
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+X-Custom: This is a custom header
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-03.txt b/cpan/HTTP-Tiny/t/cases/get-03.txt
new file mode 100644
index 0000000000..e5eed63135
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-03.txt
@@ -0,0 +1,13 @@
+url
+ http://example.com/missing.html
+----------
+GET /missing.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 404 Not Found
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Length: 0
+
diff --git a/cpan/HTTP-Tiny/t/cases/get-04.txt b/cpan/HTTP-Tiny/t/cases/get-04.txt
new file mode 100644
index 0000000000..71698f9fe9
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-04.txt
@@ -0,0 +1,17 @@
+url
+ http://example.com:9000/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /index.html HTTP/1.1
+Host: example.com:9000
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-05.txt b/cpan/HTTP-Tiny/t/cases/get-05.txt
new file mode 100644
index 0000000000..b689aaab0c
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-05.txt
@@ -0,0 +1,21 @@
+url
+ http://example.com/chunked.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /chunked.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Transfer-Encoding: chunked
+
+2C
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
+0
+
diff --git a/cpan/HTTP-Tiny/t/cases/get-06.txt b/cpan/HTTP-Tiny/t/cases/get-06.txt
new file mode 100644
index 0000000000..131bb58e3b
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-06.txt
@@ -0,0 +1,19 @@
+url
+ http://example.com/cb.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+data_cb
+ sub { $main::data .= $_[0] }
+----------
+GET /cb.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-07.txt b/cpan/HTTP-Tiny/t/cases/get-07.txt
new file mode 100644
index 0000000000..dec18fdf80
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-07.txt
@@ -0,0 +1,19 @@
+new_args
+ proxy: http://proxy.example.com:8080/
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET http://example.com/index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-08.txt b/cpan/HTTP-Tiny/t/cases/get-08.txt
new file mode 100644
index 0000000000..3044db1c78
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-08.txt
@@ -0,0 +1,21 @@
+new_args
+ max_size: 26
+url
+ http://example.com/index.html
+expected_rc
+ 599
+expected_like
+ Size of response body exceeds the maximum allowed of 26
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-09.txt b/cpan/HTTP-Tiny/t/cases/get-09.txt
new file mode 100644
index 0000000000..0d5eb5dbe2
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-09.txt
@@ -0,0 +1,16 @@
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-10.txt b/cpan/HTTP-Tiny/t/cases/get-10.txt
new file mode 100644
index 0000000000..23c0163547
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-10.txt
@@ -0,0 +1,21 @@
+url
+ http://example.com/chunked.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /chunked.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Transfer-Encoding: CHUNKED
+
+2C
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
+0
+
diff --git a/cpan/HTTP-Tiny/t/cases/get-11.txt b/cpan/HTTP-Tiny/t/cases/get-11.txt
new file mode 100644
index 0000000000..dca6d14ce1
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-11.txt
@@ -0,0 +1,22 @@
+url
+ http://example.com/chunked.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /chunked.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 1024
+Transfer-Encoding: chunked
+
+2C
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
+0
+
diff --git a/cpan/HTTP-Tiny/t/cases/get-12.txt b/cpan/HTTP-Tiny/t/cases/get-12.txt
new file mode 100644
index 0000000000..9cf2bf72e1
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-12.txt
@@ -0,0 +1,17 @@
+url
+ http:///index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /index.html HTTP/1.1
+Host: localhost
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-13.txt b/cpan/HTTP-Tiny/t/cases/get-13.txt
new file mode 100644
index 0000000000..7e437559f7
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-13.txt
@@ -0,0 +1,21 @@
+url
+ http://example.com/chunked.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /chunked.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Transfer-Encoding: chunked
+
+2C; this_extension=foo
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
+0
+
diff --git a/cpan/HTTP-Tiny/t/cases/get-14.txt b/cpan/HTTP-Tiny/t/cases/get-14.txt
new file mode 100644
index 0000000000..e232aa2a33
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-14.txt
@@ -0,0 +1,22 @@
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+headers
+ X-Foo: one
+ X-Foo: two
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+X-Foo: one
+X-Foo: two
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-15.txt b/cpan/HTTP-Tiny/t/cases/get-15.txt
new file mode 100644
index 0000000000..0bd5065d71
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-15.txt
@@ -0,0 +1,17 @@
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/01.01 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-16.txt b/cpan/HTTP-Tiny/t/cases/get-16.txt
new file mode 100644
index 0000000000..e17076de18
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-16.txt
@@ -0,0 +1,19 @@
+url
+ http://example.com/index.html
+expected_rc
+ 599
+expected_like
+ Malformed Status-Line
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/0 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-17.txt b/cpan/HTTP-Tiny/t/cases/get-17.txt
new file mode 100644
index 0000000000..e5d3d12eeb
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-17.txt
@@ -0,0 +1,19 @@
+url
+ http://example.com/index.html
+expected_rc
+ 599
+expected_like
+ Unsupported HTTP protocol
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.2 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-18.txt b/cpan/HTTP-Tiny/t/cases/get-18.txt
new file mode 100644
index 0000000000..f46f48cea3
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-18.txt
@@ -0,0 +1,36 @@
+url
+ http://example.com/chunked.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+expected_headers
+ transfer-encoding: chunked
+ x-a: Foo
+ x-a: Bar
+ x-a: Baz
+ x-b: Foo
+ x-b: Bar
+ x-b: Baz
+ x-c: Foo
+
+----------
+GET /chunked.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Transfer-Encoding: chunked
+X-A: Foo
+X-B: Foo
+X-B: Bar
+
+2C
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
+0
+X-A: Bar
+X-A: Baz
+X-B: Baz
+X-C: Foo
+
diff --git a/cpan/HTTP-Tiny/t/cases/get-19.txt b/cpan/HTTP-Tiny/t/cases/get-19.txt
new file mode 100644
index 0000000000..2ebb9a84b4
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-19.txt
@@ -0,0 +1,22 @@
+url
+ http://example.com/chunked.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /chunked.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Transfer-Encoding: wacky
+Transfer-Encoding: chunked
+
+2C
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
+0
+
diff --git a/cpan/HTTP-Tiny/t/cases/get-20.txt b/cpan/HTTP-Tiny/t/cases/get-20.txt
new file mode 100644
index 0000000000..8873793665
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-20.txt
@@ -0,0 +1,21 @@
+url
+ http://example.com/index.html
+headers
+ connection: X-Foo
+ X-Foo: bar
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+X-Foo: bar
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/get-21.txt b/cpan/HTTP-Tiny/t/cases/get-21.txt
new file mode 100644
index 0000000000..df207f6c02
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/get-21.txt
@@ -0,0 +1,23 @@
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+expected_rc
+ 200
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 100 Continue
+
+HTTP/1.1 110 Arbitrary 1XX status code
+
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 44
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
diff --git a/cpan/HTTP-Tiny/t/cases/mirror-01.txt b/cpan/HTTP-Tiny/t/cases/mirror-01.txt
new file mode 100644
index 0000000000..5ac0eb5cad
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/mirror-01.txt
@@ -0,0 +1,18 @@
+url
+ http://example.com/modified.txt
+----------
+GET /modified.txt HTTP/1.1
+Host: example.com
+Connection: close
+If-Modified-Since: Tue, 01 Feb 1994 00:00:00 GMT
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Last-Modified: Wed, 02 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
diff --git a/cpan/HTTP-Tiny/t/cases/mirror-02.txt b/cpan/HTTP-Tiny/t/cases/mirror-02.txt
new file mode 100644
index 0000000000..4799a2e777
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/mirror-02.txt
@@ -0,0 +1,14 @@
+url
+ http://example.com/not-modified.txt
+----------
+GET /not-modified.txt HTTP/1.1
+Host: example.com
+Connection: close
+If-Modified-Since: Tue, 01 Feb 1994 00:00:00 GMT
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 304 Not Modified
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Last-Modified: Tue, 01 Feb 1994 00:00:00 GMT
+
diff --git a/cpan/HTTP-Tiny/t/cases/mirror-03.txt b/cpan/HTTP-Tiny/t/cases/mirror-03.txt
new file mode 100644
index 0000000000..711710d90c
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/mirror-03.txt
@@ -0,0 +1,17 @@
+url
+ http://example.com/new.txt
+----------
+GET /new.txt HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Last-Modified: Tue, 01 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
diff --git a/cpan/HTTP-Tiny/t/cases/mirror-04.txt b/cpan/HTTP-Tiny/t/cases/mirror-04.txt
new file mode 100644
index 0000000000..9beeeb3019
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/mirror-04.txt
@@ -0,0 +1,13 @@
+url
+ http://example.com/missing.txt
+----------
+GET /missing.txt HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 404 Not Found
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Length: 0
+
diff --git a/cpan/HTTP-Tiny/t/cases/mirror-05.txt b/cpan/HTTP-Tiny/t/cases/mirror-05.txt
new file mode 100644
index 0000000000..f1f3573627
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/mirror-05.txt
@@ -0,0 +1,20 @@
+url
+ http://example.com/modified.txt
+headers
+ if-modified-since: Tue, 01 Feb 1994 12:00:00 GMT
+----------
+GET /modified.txt HTTP/1.1
+Host: example.com
+Connection: close
+If-Modified-Since: Tue, 01 Feb 1994 12:00:00 GMT
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Last-Modified: Wed, 02 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
diff --git a/cpan/HTTP-Tiny/t/cases/put-01.txt b/cpan/HTTP-Tiny/t/cases/put-01.txt
new file mode 100644
index 0000000000..b8d6286fa1
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/put-01.txt
@@ -0,0 +1,22 @@
+url
+ http://example.com/new.txt
+headers
+ Content-Type: text/plain
+content
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+PUT /new.txt HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
+----------
+HTTP/1.1 201 Created
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Location: http://example.com/new.txt
+Content-Length: 0
+
diff --git a/cpan/HTTP-Tiny/t/cases/put-02.txt b/cpan/HTTP-Tiny/t/cases/put-02.txt
new file mode 100644
index 0000000000..04d267563b
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/put-02.txt
@@ -0,0 +1,24 @@
+url
+ http://example.com/callback.txt
+headers
+ Content-Type: text/plain
+ Content-Length: 42
+content_cb
+ my @content = qq{abcdefghijklmnopqrstuvwxyz1234567890abcdef};
+ sub { shift @content }
+----------
+PUT /callback.txt HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
+----------
+HTTP/1.1 201 Created
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Location: http://example.com/callback.txt
+Content-Length: 0
+
diff --git a/cpan/HTTP-Tiny/t/cases/put-03.txt b/cpan/HTTP-Tiny/t/cases/put-03.txt
new file mode 100644
index 0000000000..99fe188f4e
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/put-03.txt
@@ -0,0 +1,25 @@
+url
+ http://example.com/chunked.txt
+headers
+ Content-Type: text/plain
+content_cb
+ my @content = qq{abcdefghijklmnopqrstuvwxyz1234567890abcdef};
+ sub { shift @content }
+----------
+PUT /chunked.txt HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+Content-Type: text/plain
+Transfer-Encoding: chunked
+
+2A
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+0
+
+----------
+HTTP/1.1 201 Created
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Location: http://example.com/chunked.txt
+Content-Length: 0
+
diff --git a/cpan/HTTP-Tiny/t/cases/put-04.txt b/cpan/HTTP-Tiny/t/cases/put-04.txt
new file mode 100644
index 0000000000..eeec295c80
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/put-04.txt
@@ -0,0 +1,19 @@
+url
+ http://example.com/new.txt
+content
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+PUT /new.txt HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+Content-Type: application/octet-stream
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+HTTP/1.1 201 Created
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Location: http://example.com/new.txt
+Content-Length: 0
+
diff --git a/cpan/HTTP-Tiny/t/cases/put-05.txt b/cpan/HTTP-Tiny/t/cases/put-05.txt
new file mode 100644
index 0000000000..f4bcaf18e9
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/put-05.txt
@@ -0,0 +1,27 @@
+url
+ http://example.com/chunked.txt
+headers
+ Content-Type: text/plain
+content_cb
+ my @content = qq{abcdefghijklmnopqrstuvwxyz1234567890abcdef};
+ sub { shift @content }
+trailer_cb
+ sub { return { 'x-foo' => 'bar' } }
+----------
+PUT /chunked.txt HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+Content-Type: text/plain
+Transfer-Encoding: chunked
+
+2A
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+0
+X-Foo: bar
+----------
+HTTP/1.1 201 Created
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Location: http://example.com/chunked.txt
+Content-Length: 0
+
diff --git a/cpan/HTTP-Tiny/t/cases/redirect-01.txt b/cpan/HTTP-Tiny/t/cases/redirect-01.txt
new file mode 100644
index 0000000000..25e2ff2628
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/redirect-01.txt
@@ -0,0 +1,33 @@
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 302 Found
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index2.html
+
+<a href="http://example.com/index2.html">redirect</a>
+
+----------
+GET /index2.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
diff --git a/cpan/HTTP-Tiny/t/cases/redirect-02.txt b/cpan/HTTP-Tiny/t/cases/redirect-02.txt
new file mode 100644
index 0000000000..5035879ff4
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/redirect-02.txt
@@ -0,0 +1,50 @@
+new_args
+ max_redirect: 0
+url
+ http://example.com/index.html
+expected
+ <a href="http://example.com/index2.html">redirect</a>
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 302 Found
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index2.html
+
+<a href="http://example.com/index2.html">redirect</a>
+
+----------
+GET /index2.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 302 Found
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index3.html
+
+<a href="http://example.com/index3.html">redirect</a>
+
+----------
+GET /index3.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
diff --git a/cpan/HTTP-Tiny/t/cases/redirect-03.txt b/cpan/HTTP-Tiny/t/cases/redirect-03.txt
new file mode 100644
index 0000000000..0a7df722a7
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/redirect-03.txt
@@ -0,0 +1,50 @@
+new_args
+ max_redirect: 1
+url
+ http://example.com/index.html
+expected
+ <a href="http://example.com/index3.html">redirect</a>
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 302 Found
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index2.html
+
+<a href="http://example.com/index2.html">redirect</a>
+
+----------
+GET /index2.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 302 Found
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index3.html
+
+<a href="http://example.com/index3.html">redirect</a>
+
+----------
+GET /index3.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
diff --git a/cpan/HTTP-Tiny/t/cases/redirect-04.txt b/cpan/HTTP-Tiny/t/cases/redirect-04.txt
new file mode 100644
index 0000000000..c07412bdc3
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/redirect-04.txt
@@ -0,0 +1,50 @@
+new_args
+ max_redirect: 2
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 302 Found
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index2.html
+
+<a href="http://example.com/index2.html">redirect</a>
+
+----------
+GET /index2.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 302 Found
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index3.html
+
+<a href="http://example.com/index3.html">redirect</a>
+
+----------
+GET /index3.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
diff --git a/cpan/HTTP-Tiny/t/cases/redirect-05.txt b/cpan/HTTP-Tiny/t/cases/redirect-05.txt
new file mode 100644
index 0000000000..0691a80758
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/redirect-05.txt
@@ -0,0 +1,48 @@
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 302 Found
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index2.html
+
+<a href="http://example.com/index2.html">redirect</a>
+
+----------
+GET /index2.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 301 Found
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: /index3.html
+
+<a href="http://example.com/index3.html">redirect</a>
+
+----------
+GET /index3.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
diff --git a/cpan/HTTP-Tiny/t/cases/redirect-06.txt b/cpan/HTTP-Tiny/t/cases/redirect-06.txt
new file mode 100644
index 0000000000..b5a6a49762
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/redirect-06.txt
@@ -0,0 +1,33 @@
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 303 See Other
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index2.html
+
+<a href="http://example.com/index2.html">redirect</a>
+
+----------
+GET /index2.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
diff --git a/cpan/HTTP-Tiny/t/cases/redirect-07.txt b/cpan/HTTP-Tiny/t/cases/redirect-07.txt
new file mode 100644
index 0000000000..3320c6ce3c
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/redirect-07.txt
@@ -0,0 +1,33 @@
+url
+ http://example.com/index.html
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 307 Temporary Redirect
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index2.html
+
+<a href="http://example.com/index2.html">redirect</a>
+
+----------
+GET /index2.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
diff --git a/cpan/HTTP-Tiny/t/cases/redirect-08.txt b/cpan/HTTP-Tiny/t/cases/redirect-08.txt
new file mode 100644
index 0000000000..3f983b8402
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/redirect-08.txt
@@ -0,0 +1,19 @@
+url
+ http://example.com/index.html
+expected
+ <a href="http://example.com/index2.html">redirect</a>
+----------
+GET /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 305 Use Proxy
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index2.html
+
+<a href="http://example.com/index2.html">redirect</a>
+
diff --git a/cpan/HTTP-Tiny/t/cases/redirect-09.txt b/cpan/HTTP-Tiny/t/cases/redirect-09.txt
new file mode 100644
index 0000000000..02a75aa63b
--- /dev/null
+++ b/cpan/HTTP-Tiny/t/cases/redirect-09.txt
@@ -0,0 +1,35 @@
+url
+ http://example.com/index.html
+method
+ POST
+expected
+ abcdefghijklmnopqrstuvwxyz1234567890abcdef
+----------
+POST /index.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 303 See Other
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/html
+Content-Length: 53
+Location: http://example.com/index2.html
+
+<a href="http://example.com/index2.html">redirect</a>
+
+----------
+GET /index2.html HTTP/1.1
+Host: example.com
+Connection: close
+User-Agent: HTTP-Tiny/VERSION
+
+----------
+HTTP/1.1 200 OK
+Date: Thu, 03 Feb 1994 00:00:00 GMT
+Content-Type: text/plain
+Content-Length: 42
+
+abcdefghijklmnopqrstuvwxyz1234567890abcdef
+
diff --git a/lib/.gitignore b/lib/.gitignore
index a266b89924..cb1ea9c4ad 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -210,6 +210,7 @@
/IPC/Semaphore.pm
/IPC/SharedMem.pm
/IPC/SysV.pm
+/HTTP/Tiny.pm
/JSON/PP.pm
/JSON/PP/Boolean.pm
/Getopt/Long.pm
diff --git a/pod/perldelta.pod b/pod/perldelta.pod
index 8f861d80c9..d3b0fd16c2 100644
--- a/pod/perldelta.pod
+++ b/pod/perldelta.pod
@@ -109,6 +109,14 @@ generation task.
=item *
+L<HTTP::Tiny> 0.008 has been added as a dual-life module. It is a very
+small, simple HTTP/1.1 client designed for simple GET requests and file
+mirroring. It has has been added to enable CPAN.pm and CPANPLUS to
+"bootstrap" HTTP access to CPAN using pure Perl without relying on external
+binaries like F<curl> or F<wget>.
+
+=item *
+
L<Module::Metadata> 1.000003 has been added as a dual-life module. It gathers
package and POD information from Perl module files. It is a standalone module
based on Module::Build::ModuleInfo for use by other module installation