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'};
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 ]
$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];
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');
}
{