Subject: | [PATCH] NB: set http_bytes if read_entity_body hits EAGAIN on first read |
Date: | Fri, 6 Jul 2012 01:25:39 +0000 |
To: | bug-Net-HTTP [...] rt.cpan.org |
From: | Eric Wong <normalperson [...] yhbt.net> |
If read_entity_body is called before a response body is readable,
http_bytes is never set. This causes future calls of read_entity_body
to never return zero (EOF).
I encountered this issue on 6.01 and 6.03. The new test case
illustrates the issue this patch fixes.
note: commit 88ecea4531de4178603a6453fc73e2cf2b53a6e5 breaks all usage
of Net::HTTP::NB in which the user expects EAGAIN. I needed to revert
88ecea4531de4178603a6453fc73e2cf2b53a6e5 before testing this patch.
---
lib/Net/HTTP/Methods.pm | 3 +--
t/http-nb.t | 52 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 53 insertions(+), 2 deletions(-)
create mode 100644 t/http-nb.t
diff --git a/lib/Net/HTTP/Methods.pm b/lib/Net/HTTP/Methods.pm
index cd5e857..e4e41cd 100644
--- a/lib/Net/HTTP/Methods.pm
+++ b/lib/Net/HTTP/Methods.pm
@@ -565,8 +565,7 @@ sub read_entity_body {
my $n = $bytes;
$n = $size if $size && $size < $n;
$n = my_read($self, $$buf_ref, $n);
- return undef unless defined $n;
- ${*$self}{'http_bytes'} = $bytes - $n;
+ ${*$self}{'http_bytes'} = defined $n ? $bytes - $n : $bytes;
return $n;
}
else {
diff --git a/t/http-nb.t b/t/http-nb.t
new file mode 100644
index 0000000..b3dafe0
--- /dev/null
+++ b/t/http-nb.t
@@ -0,0 +1,52 @@
+#!perl -w
+
+use strict;
+use Test;
+
+plan tests => 14;
+
+require Net::HTTP::NB;
+use IO::Socket::INET;
+use Data::Dumper;
+use IO::Select;
+use Socket qw(TCP_NODELAY);
+my $buf;
+
+# bind a random TCP port for testing
+my %lopts = (
+ LocalAddr => "127.0.0.1",
+ LocalPort => 0,
+ Proto => "tcp",
+ ReuseAddr => 1,
+ Listen => 1024
+);
+
+my $srv = IO::Socket::INET->new(%lopts);
+ok(ref($srv), "IO::Socket::INET");
+my $host = $srv->sockhost . ':' . $srv->sockport;
+my $nb = Net::HTTP::NB->new(Host => $host, Blocking => 0);
+ok(ref($nb), "Net::HTTP::NB");
+ok(IO::Select->new($nb)->can_write(3), 1);
+
+ok($nb->write_request("GET", "/"));
+my $acc = $srv->accept;
+ok(ref($acc), "IO::Socket::INET");
+$acc->sockopt(TCP_NODELAY, 1);
+ok($acc->sysread($buf, 4096));
+ok($acc->syswrite("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n"));
+
+ok(1, IO::Select->new($nb)->can_read(3));
+my @r = $nb->read_response_headers;
+ok($r[0], 200);
+
+# calling read_entity_body before response body is readable causes
+# EOF to never happen eventually
+ok(!defined($nb->read_entity_body($buf, 4096)) && $!{EAGAIN});
+
+ok($acc->syswrite("hello"), 5, "server wrote response body");
+
+ok(IO::Select->new($nb)->can_read(3), 1, "client body is readable");
+ok($nb->read_entity_body($buf, 4096), 5, "client gets 5 bytes");
+
+# this fails if we got EAGAIN from the first read_entity_body call:
+ok($nb->read_entity_body($buf, 4096), 0, "client gets EOF");
--
Eric Wong