|
Packit |
03f954 |
use strict;
|
|
Packit |
03f954 |
use warnings;
|
|
Packit |
03f954 |
use Test::More;
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
plan tests => 37;
|
|
Packit |
03f954 |
#use Data::Dump ();
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
my $CRLF = "\015\012";
|
|
Packit |
03f954 |
my $LF = "\012";
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
{
|
|
Packit |
03f954 |
package HTTP;
|
|
Packit |
03f954 |
use base 'Net::HTTP::Methods';
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
my %servers = (
|
|
Packit |
03f954 |
a => { "/" => "HTTP/1.0 200 OK${CRLF}Content-Type: text/plain${CRLF}Content-Length: 6${CRLF}${CRLF}Hello\n",
|
|
Packit |
03f954 |
"/bad1" => "HTTP/1.0 200 OK${LF}Server: foo${LF}HTTP/1.0 200 OK${LF}Content-type: text/foo${LF}${LF}abc\n",
|
|
Packit |
03f954 |
"/09" => "Hello${CRLF}World!${CRLF}",
|
|
Packit |
03f954 |
"/chunked" => "HTTP/1.1 200 OK${CRLF}Transfer-Encoding: chunked${CRLF}${CRLF}0002; foo=3; bar${CRLF}He${CRLF}1${CRLF}l${CRLF}2${CRLF}lo${CRLF}0000${CRLF}Content-MD5: xxx${CRLF}${CRLF}",
|
|
Packit |
03f954 |
"/chunked,chunked" => "HTTP/1.1 200 OK${CRLF}Transfer-Encoding: chunked${CRLF}Transfer-Encoding: chunked${CRLF}${CRLF}0002; foo=3; bar${CRLF}He${CRLF}1${CRLF}l${CRLF}2${CRLF}lo${CRLF}0000${CRLF}Content-MD5: xxx${CRLF}${CRLF}",
|
|
Packit |
03f954 |
"/head" => "HTTP/1.1 200 OK${CRLF}Content-Length: 16${CRLF}Content-Type: text/plain${CRLF}${CRLF}",
|
|
Packit |
03f954 |
"/colon-header" => "HTTP/1.1 200 OK${CRLF}Content-Type: text/plain${CRLF}Content-Length: 6${CRLF}Bad-Header: :foo${CRLF}${CRLF}Hello\n",
|
|
Packit |
03f954 |
},
|
|
Packit |
03f954 |
);
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
sub http_connect {
|
|
Packit |
03f954 |
my($self, $cnf) = @_;
|
|
Packit |
03f954 |
my $server = $servers{$cnf->{PeerAddr}} || return undef;
|
|
Packit |
03f954 |
${*$self}{server} = $server;
|
|
Packit |
03f954 |
${*$self}{read_chunk_size} = $cnf->{ReadChunkSize};
|
|
Packit |
03f954 |
return $self;
|
|
Packit |
03f954 |
}
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
sub print {
|
|
Packit |
03f954 |
my $self = shift;
|
|
Packit |
03f954 |
#Data::Dump::dump("PRINT", @_);
|
|
Packit |
03f954 |
my $in = shift;
|
|
Packit |
03f954 |
my($method, $uri) = split(' ', $in);
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
my $out;
|
|
Packit |
03f954 |
if ($method eq "TRACE") {
|
|
Packit |
03f954 |
my $len = length($in);
|
|
Packit |
03f954 |
$out = "HTTP/1.0 200 OK${CRLF}Content-Length: $len${CRLF}" .
|
|
Packit |
03f954 |
"Content-Type: message/http${CRLF}${CRLF}" .
|
|
Packit |
03f954 |
$in;
|
|
Packit |
03f954 |
}
|
|
Packit |
03f954 |
else {
|
|
Packit |
03f954 |
$out = ${*$self}{server}{$uri};
|
|
Packit |
03f954 |
$out = "HTTP/1.0 404 Not found${CRLF}${CRLF}" unless defined $out;
|
|
Packit |
03f954 |
}
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
${*$self}{out} .= $out;
|
|
Packit |
03f954 |
return 1;
|
|
Packit |
03f954 |
}
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
sub sysread {
|
|
Packit |
03f954 |
my $self = shift;
|
|
Packit |
03f954 |
#Data::Dump::dump("SYSREAD", @_);
|
|
Packit |
03f954 |
my $length = $_[1];
|
|
Packit |
03f954 |
my $offset = $_[2] || 0;
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
if (my $read_chunk_size = ${*$self}{read_chunk_size}) {
|
|
Packit |
03f954 |
$length = $read_chunk_size if $read_chunk_size < $length;
|
|
Packit |
03f954 |
}
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
my $data = substr(${*$self}{out}, 0, $length, "");
|
|
Packit |
03f954 |
return 0 unless length($data);
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
$_[0] = "" unless defined $_[0];
|
|
Packit |
03f954 |
substr($_[0], $offset) = $data;
|
|
Packit |
03f954 |
return length($data);
|
|
Packit |
03f954 |
}
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
# ----------------
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
sub request {
|
|
Packit |
03f954 |
my($self, $method, $uri, $headers, $opt) = @_;
|
|
Packit |
03f954 |
$headers ||= [];
|
|
Packit |
03f954 |
$opt ||= {};
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
my($code, $message, @h);
|
|
Packit |
03f954 |
my $buf = "";
|
|
Packit |
03f954 |
eval {
|
|
Packit |
03f954 |
$self->write_request($method, $uri, @$headers) || die "Can't write request";
|
|
Packit |
03f954 |
($code, $message, @h) = $self->read_response_headers(%$opt);
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
my $tmp;
|
|
Packit |
03f954 |
my $n;
|
|
Packit |
03f954 |
while ($n = $self->read_entity_body($tmp, 32)) {
|
|
Packit |
03f954 |
#Data::Dump::dump($tmp, $n);
|
|
Packit |
03f954 |
$buf .= $tmp;
|
|
Packit |
03f954 |
}
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
push(@h, $self->get_trailers);
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
};
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
my %res = ( code => $code,
|
|
Packit |
03f954 |
message => $message,
|
|
Packit |
03f954 |
headers => \@h,
|
|
Packit |
03f954 |
content => $buf,
|
|
Packit |
03f954 |
);
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
if ($@) {
|
|
Packit |
03f954 |
$res{error} = $@;
|
|
Packit |
03f954 |
}
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
return \%res;
|
|
Packit |
03f954 |
}
|
|
Packit |
03f954 |
}
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
# Start testing
|
|
Packit |
03f954 |
my $h;
|
|
Packit |
03f954 |
my $res;
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
$h = HTTP->new(Host => "a", KeepAlive => 1) || die;
|
|
Packit |
03f954 |
$res = $h->request(GET => "/");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
#Data::Dump::dump($res);
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
is($res->{code}, 200);
|
|
Packit |
03f954 |
is($res->{content}, "Hello\n");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
$res = $h->request(GET => "/404");
|
|
Packit |
03f954 |
is($res->{code}, 404);
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
$res = $h->request(TRACE => "/foo");
|
|
Packit |
03f954 |
is($res->{code}, 200);
|
|
Packit |
03f954 |
is($res->{content}, "TRACE /foo HTTP/1.1${CRLF}Keep-Alive: 300${CRLF}Connection: Keep-Alive${CRLF}Host: a${CRLF}${CRLF}");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
# try to turn off keep alive
|
|
Packit |
03f954 |
$h->keep_alive(0);
|
|
Packit |
03f954 |
$res = $h->request(TRACE => "/foo");
|
|
Packit |
03f954 |
is($res->{code}, "200");
|
|
Packit |
03f954 |
is($res->{content}, "TRACE /foo HTTP/1.1${CRLF}Connection: close${CRLF}Host: a${CRLF}${CRLF}");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
# try a bad one
|
|
Packit |
03f954 |
# It's bad because 2nd 'HTTP/1.0 200' is illegal. But passes anyway if laxed => 1.
|
|
Packit |
03f954 |
$res = $h->request(GET => "/bad1", [], {laxed => 1});
|
|
Packit |
03f954 |
is($res->{code}, "200");
|
|
Packit |
03f954 |
is($res->{message}, "OK");
|
|
Packit |
03f954 |
is("@{$res->{headers}}", "Server foo Content-type text/foo");
|
|
Packit |
03f954 |
is($res->{content}, "abc\n");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
$res = $h->request(GET => "/bad1");
|
|
Packit |
03f954 |
like($res->{error}, qr/Bad header/);
|
|
Packit |
03f954 |
ok(!$res->{code});
|
|
Packit |
03f954 |
$h = undef; # it is in a bad state now
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
$h = HTTP->new("a") || die; # reconnect
|
|
Packit |
03f954 |
$res = $h->request(GET => "/09", [], {laxed => 1});
|
|
Packit |
03f954 |
is($res->{code}, "200");
|
|
Packit |
03f954 |
is($res->{message}, "Assumed OK");
|
|
Packit |
03f954 |
is($res->{content}, "Hello${CRLF}World!${CRLF}");
|
|
Packit |
03f954 |
is($h->peer_http_version, "0.9");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
$res = $h->request(GET => "/09");
|
|
Packit |
03f954 |
like($res->{error}, qr/^Bad response status line: 'Hello'/);
|
|
Packit |
03f954 |
$h = undef; # it's in a bad state again
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
$h = HTTP->new(Host => "a", KeepAlive => 1, ReadChunkSize => 1) || die; # reconnect
|
|
Packit |
03f954 |
$res = $h->request(GET => "/chunked");
|
|
Packit |
03f954 |
is($res->{code}, 200);
|
|
Packit |
03f954 |
is($res->{content}, "Hello");
|
|
Packit |
03f954 |
is("@{$res->{headers}}", "Transfer-Encoding chunked Content-MD5 xxx");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
# once more
|
|
Packit |
03f954 |
$res = $h->request(GET => "/chunked");
|
|
Packit |
03f954 |
is($res->{code}, "200");
|
|
Packit |
03f954 |
is($res->{content}, "Hello");
|
|
Packit |
03f954 |
is("@{$res->{headers}}", "Transfer-Encoding chunked Content-MD5 xxx");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
# Test bogus headers. Chunked appearing twice is illegal, but happens anyway sometimes. [RT#77240]
|
|
Packit |
03f954 |
$res = $h->request(GET => "/chunked,chunked");
|
|
Packit |
03f954 |
is($res->{code}, "200");
|
|
Packit |
03f954 |
is($res->{content}, "Hello");
|
|
Packit |
03f954 |
is("@{$res->{headers}}", "Transfer-Encoding chunked Transfer-Encoding chunked Content-MD5 xxx");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
# test head
|
|
Packit |
03f954 |
$res = $h->request(HEAD => "/head");
|
|
Packit |
03f954 |
is($res->{code}, "200");
|
|
Packit |
03f954 |
is($res->{content}, "");
|
|
Packit |
03f954 |
is("@{$res->{headers}}", "Content-Length 16 Content-Type text/plain");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
$res = $h->request(GET => "/");
|
|
Packit |
03f954 |
is($res->{code}, "200");
|
|
Packit |
03f954 |
is($res->{content}, "Hello\n");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
$h = HTTP->new(Host => undef, PeerAddr => "a", );
|
|
Packit |
03f954 |
$h->http_version("1.0");
|
|
Packit |
03f954 |
ok(!defined $h->host);
|
|
Packit |
03f954 |
$res = $h->request(TRACE => "/");
|
|
Packit |
03f954 |
is($res->{code}, "200");
|
|
Packit |
03f954 |
is($res->{content}, "TRACE / HTTP/1.0\r\n\r\n");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
# check that headers with colons at the start of values don't break
|
|
Packit |
03f954 |
$res = $h->request(GET => '/colon-header');
|
|
Packit |
03f954 |
is("@{$res->{headers}}", "Content-Type text/plain Content-Length 6 Bad-Header :foo");
|
|
Packit |
03f954 |
|
|
Packit |
03f954 |
require Net::HTTP;
|
|
Packit |
03f954 |
eval {
|
|
Packit |
03f954 |
$h = Net::HTTP->new;
|
|
Packit |
03f954 |
};
|
|
Packit |
03f954 |
print "# $@";
|
|
Packit |
03f954 |
ok($@);
|
|
Packit |
03f954 |
|