From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on dcvr.yhbt.net X-Spam-Level: X-Spam-ASN: X-Spam-Status: No, score=-4.0 required=3.0 tests=ALL_TRUSTED,BAYES_00 shortcircuit=no autolearn=ham autolearn_force=no version=3.4.2 Received: from localhost (dcvr.yhbt.net [127.0.0.1]) by dcvr.yhbt.net (Postfix) with ESMTP id 1C11C1F751 for ; Sat, 11 Apr 2020 10:53:31 +0000 (UTC) From: Eric Wong To: meta@public-inbox.org Subject: [PATCH 4/4] t/httpd-corner.t: relax read-after-failed-write handling Date: Sat, 11 Apr 2020 10:53:30 +0000 Message-Id: <20200411105330.19544-5-e@yhbt.net> In-Reply-To: <20200411105330.19544-1-e@yhbt.net> References: <20200411105330.19544-1-e@yhbt.net> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit List-Id: I've observed FreeBSD 11.2 read(2) having one of three behaviors after a failed write(2) on a socket: 1) returning number of bytes read 2) failing with ECONNRESET 3) returning with EOF 1) is the most common, and I've only seen 1) on Linux. It may be possible to use SO_LINGER or shutdown(2) to ensure 1) always happens, but SO_LINGER behavior seems inconsistent across OSes, especially with non-blocking sockets. Since these tests are corner-cases where we're dealing with broken/malicious clients, lets continue spending the least amount of syscalls protecting ourselves in the daemon and instead make the client-side test code tolerate more socket implementations. --- t/httpd-corner.t | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/t/httpd-corner.t b/t/httpd-corner.t index 89f2866b..21b5c560 100644 --- a/t/httpd-corner.t +++ b/t/httpd-corner.t @@ -122,36 +122,48 @@ if ('test worker death') { is(scalar(grep(/CLOSE FAIL/, @$after)), 1, 'body->close not called'); } -SKIP: { +sub check_400 { + my ($conn) = @_; + my $r = $conn->read(my $buf, 8192); + # ECONNRESET and $r==0 are both observed on FreeBSD 11.2 + if (!defined($r)) { + ok($!{ECONNRESET}, 'ECONNRESET on read (BSD sometimes)'); + } elsif ($r > 0) { + like($buf, qr!\AHTTP/1\.\d 400 !, 'got 400 response'); + } else { + is($r, 0, 'got EOF (BSD sometimes)'); + } + close($conn); # ensure we don't get SIGPIPE later +} + +{ + local $SIG{PIPE} = 'IGNORE'; my $conn = conn_for($sock, 'excessive header'); - $SIG{PIPE} = 'IGNORE'; $conn->write("GET /callback HTTP/1.0\r\n"); foreach my $i (1..500000) { last unless $conn->write("X-xxxxxJunk-$i: omg\r\n"); } ok(!$conn->write("\r\n"), 'broken request'); - ok($conn->read(my $buf, 8192), 'read response'); - my ($head, $body) = split(/\r\n\r\n/, $buf); - like($head, qr/\b400\b/, 'got 400 response'); + check_400($conn); } { my $conn = conn_for($sock, 'excessive body Content-Length'); - $SIG{PIPE} = 'IGNORE'; my $n = (10 * 1024 * 1024) + 1; $conn->write("PUT /sha1 HTTP/1.0\r\nContent-Length: $n\r\n\r\n"); - ok($conn->read(my $buf, 8192), 'read response'); + my $r = $conn->read(my $buf, 8192); + ok($r > 0, 'read response'); my ($head, $body) = split(/\r\n\r\n/, $buf); like($head, qr/\b413\b/, 'got 413 response'); } { my $conn = conn_for($sock, 'excessive body chunked'); - $SIG{PIPE} = 'IGNORE'; my $n = (10 * 1024 * 1024) + 1; $conn->write("PUT /sha1 HTTP/1.1\r\nTransfer-Encoding: chunked\r\n"); $conn->write("\r\n".sprintf("%x\r\n", $n)); - ok($conn->read(my $buf, 8192), 'read response'); + my $r = $conn->read(my $buf, 8192); + ok($r > 0, 'read response'); my ($head, $body) = split(/\r\n\r\n/, $buf); like($head, qr/\b413\b/, 'got 413 response'); } @@ -410,10 +422,7 @@ SKIP: { ok($!, 'got error set in $!'); is($w, undef, 'write error happened'); ok($n > 0, 'was able to write'); - my $r = $conn->read(my $buf, 66666); - ok($r > 0, 'got non-empty response'); - like($buf, qr!HTTP/1\.\d 400 !, 'got 400 response'); - + check_400($conn); $conn = conn_for($sock, '1.1 chunk trailer excessive'); $conn->write("PUT /sha1 HTTP/1.1\r\nTransfer-Encoding:chunked\r\n\r\n"); is($conn->syswrite("1\r\na"), 4, 'wrote first header + chunk'); @@ -424,9 +433,7 @@ SKIP: { } ok($!, 'got error set in $!'); ok($n > 0, 'wrote part of chunk end (\r)'); - $r = $conn->read($buf, 66666); - ok($r > 0, 'got non-empty response'); - like($buf, qr!HTTP/1\.\d 400 !, 'got 400 response'); + check_400($conn); } {