]> Sergey Matveev's repositories - public-inbox.git/commitdiff
qspawn: retry sysread when parsing headers, too
authorEric Wong <e@80x24.org>
Thu, 4 Jul 2019 10:02:06 +0000 (10:02 +0000)
committerEric Wong <e@80x24.org>
Thu, 4 Jul 2019 10:16:41 +0000 (10:16 +0000)
We need to ensure the BIN_DETECT (8000 byte) check in
ViewVCS can be handled properly when sending giant
files.  Otherwise, EPOLLET won't notify us, again,
and responses can get stuck.

While we're at it, bump up the read-size up to 4096
bytes so we make fewer trips to the kernel.

lib/PublicInbox/Qspawn.pm
t/httpd-corner.psgi
t/httpd-corner.t

index 8f0b9fe240ebc378a81ce86a7207f73d71ec4f63..fb48585c270609390a777e9976a3db8034e95d10 100644 (file)
@@ -195,9 +195,19 @@ sub psgi_return {
 
        my $buf = '';
        my $rd_hdr = sub {
-               my $r = sysread($rpipe, $buf, 1024, length($buf));
-               return if !defined($r) && $! == EAGAIN || $! == EINTR;
-               $parse_hdr->($r, \$buf);
+               # we must loop until EAGAIN for EPOLLET in HTTPD/Async.pm
+               # We also need to check EINTR for generic PSGI servers.
+               my $ret;
+               my $n = 0;
+               do {
+                       my $r = sysread($rpipe, $buf, 4096, length($buf));
+                       return if !defined($r) && $! == EAGAIN || $! == EINTR;
+
+                       # $r may be undef, here:
+                       $n += $r if $r;
+                       $ret = $parse_hdr->($r ? $n : $r, \$buf);
+               } until (defined $ret);
+               $ret;
        };
 
        my $wcb = delete $env->{'qspawn.wcb'};
index f8396907f2b25da4211d17396bde3cccae0ba712..9728aa05a0aa081f6c4b4bf7f23d7bcb27b25656 100644 (file)
@@ -72,6 +72,19 @@ my $app = sub {
                        getline => sub { undef },
                        close => sub { die 'CLOSE FAIL' },
                );
+       } elsif ($path eq '/async-big') {
+               require PublicInbox::Qspawn;
+               open my $null, '>', '/dev/null' or die;
+               my $rdr = { 2 => fileno($null) };
+               my $cmd = [qw(dd if=/dev/zero count=30 bs=1024k)];
+               my $qsp = PublicInbox::Qspawn->new($cmd, undef, $rdr);
+               return $qsp->psgi_return($env, undef, sub {
+                       my ($r, $bref) = @_;
+                       # make $rd_hdr retry sysread + $parse_hdr in Qspawn:
+                       return until length($$bref) > 8000;
+                       close $null;
+                       [ 200, [ qw(Content-Type application/octet-stream) ]];
+               });
        }
 
        [ $code, $h, $body ]
index 5efb9d14e80a50f45b5b3326ee90d391241a4e2e..35318b501f6dfd3f237f796d7e5f5ec031dc020f 100644 (file)
@@ -251,9 +251,10 @@ SKIP: {
                $have_curl = 1;
                last;
        }
-       my $ntest = 2;
+       my $ntest = 4;
        $have_curl or skip('curl(1) missing', $ntest);
-       my $url = 'http://' . $sock->sockhost . ':' . $sock->sockport . '/sha1';
+       my $base = 'http://' . $sock->sockhost . ':' . $sock->sockport;
+       my $url = "$base/sha1";
        my ($r, $w);
        pipe($r, $w) or die "pipe: $!";
        my $cmd = [qw(curl --tcp-nodelay --no-buffer -T- -HExpect: -sS), $url];
@@ -270,6 +271,17 @@ SKIP: {
        is($?, 0, 'curl exited successfully');
        is($err, '', 'no errors from curl');
        is($out, sha1_hex($str), 'read expected body');
+
+       open my $fh, '-|', qw(curl -sS), "$base/async-big" or die $!;
+       my $n = 0;
+       my $non_zero = 0;
+       while (1) {
+               my $r = sysread($fh, my $buf, 4096) or last;
+               $n += $r;
+               $buf =~ /\A\0+\z/ or $non_zero++;
+       }
+       is($n, 30 * 1024 * 1024, 'got expected output from curl');
+       is($non_zero, 0, 'read all zeros');
 }
 
 {