X-Git-Url: http://www.git.stargrave.org/?a=blobdiff_plain;f=t%2Fhttpd-corner.t;h=4077a6d1a44bdb308dbacdaef2b5c7f7c2fcbef9;hb=1bbfab375438f149dcff9047dd0c5ed3a08eed53;hp=c1dc77dba9a3a5b89fa8420e9c66af962fa19e82;hpb=74dc166d18b5586689f21f6032196279e6db4db3;p=public-inbox.git diff --git a/t/httpd-corner.t b/t/httpd-corner.t index c1dc77db..4077a6d1 100644 --- a/t/httpd-corner.t +++ b/t/httpd-corner.t @@ -6,6 +6,7 @@ use strict; use warnings; use Test::More; use Time::HiRes qw(gettimeofday tv_interval); +use PublicInbox::Spawn qw(which); foreach my $mod (qw(Plack::Util Plack::Builder HTTP::Date HTTP::Status IPC::Run)) { @@ -18,7 +19,7 @@ use File::Temp qw/tempdir/; use IO::Socket; use IO::Socket::UNIX; use Fcntl qw(:seek); -use Socket qw(IPPROTO_TCP TCP_NODELAY); +use Socket qw(IPPROTO_TCP TCP_NODELAY SOL_SOCKET); use POSIX qw(mkfifo); require './t/common.perl'; my $tmpdir = tempdir('httpd-corner-XXXXXX', TMPDIR => 1, CLEANUP => 1); @@ -28,20 +29,31 @@ my $err = "$tmpdir/stderr.log"; my $out = "$tmpdir/stdout.log"; my $httpd = 'blib/script/public-inbox-httpd'; my $psgi = "./t/httpd-corner.psgi"; -my %opts = ( - LocalAddr => '127.0.0.1', - ReuseAddr => 1, - Proto => 'tcp', - Type => SOCK_STREAM, - Listen => 1024, -); -my $sock = IO::Socket::INET->new(%opts); +my $sock = tcp_server(); + +# make sure stdin is not a pipe for lsof test to check for leaking pipes +open(STDIN, '<', '/dev/null') or die 'no /dev/null: $!'; + +# Make sure we don't clobber socket options set by systemd or similar +# using socket activation: +my ($defer_accept_val, $accf_arg); +if ($^O eq 'linux') { + setsockopt($sock, IPPROTO_TCP, Socket::TCP_DEFER_ACCEPT(), 5) or die; + my $x = getsockopt($sock, IPPROTO_TCP, Socket::TCP_DEFER_ACCEPT()); + defined $x or die "getsockopt: $!"; + $defer_accept_val = unpack('i', $x); + if ($defer_accept_val <= 0) { + die "unexpected TCP_DEFER_ACCEPT value: $defer_accept_val"; + } +} elsif ($^O eq 'freebsd' && system('kldstat -m accf_data >/dev/null') == 0) { + require PublicInbox::Daemon; + my $var = PublicInbox::Daemon::SO_ACCEPTFILTER(); + $accf_arg = pack('a16a240', 'dataready', ''); + setsockopt($sock, SOL_SOCKET, $var, $accf_arg) or die "setsockopt: $!"; +} + my $upath = "$tmpdir/s"; -my $unix = IO::Socket::UNIX->new( - Listen => 1024, - Type => SOCK_STREAM, - Local => $upath -); +my $unix = unix_server($upath); ok($unix, 'UNIX socket created'); my $pid; END { kill 'TERM', $pid if defined $pid }; @@ -89,7 +101,7 @@ my $spawn_httpd = sub { is(scalar(grep(/CLOSE FAIL/, @$after)), 1, 'body->close not called'); } -{ +SKIP: { my $conn = conn_for($sock, 'excessive header'); $SIG{PIPE} = 'IGNORE'; $conn->write("GET /callback HTTP/1.0\r\n"); @@ -149,14 +161,9 @@ my $spawn_httpd = sub { } sub conn_for { - my ($sock, $msg) = @_; - my $conn = IO::Socket::INET->new( - PeerAddr => $sock->sockhost, - PeerPort => $sock->sockport, - Proto => 'tcp', - Type => SOCK_STREAM); + my ($dest, $msg) = @_; + my $conn = tcp_connect($dest); ok($conn, "connected for $msg"); - $conn->autoflush(1); setsockopt($conn, IPPROTO_TCP, TCP_NODELAY, 1); return $conn; } @@ -237,15 +244,9 @@ my $check_self = sub { }; SKIP: { - my $have_curl = 0; - foreach my $p (split(':', $ENV{PATH})) { - -x "$p/curl" or next; - $have_curl = 1; - last; - } - my $ntest = 2; - $have_curl or skip('curl(1) missing', $ntest); - my $url = 'http://' . $sock->sockhost . ':' . $sock->sockport . '/sha1'; + which('curl') or skip('curl(1) missing', 4); + 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]; @@ -262,6 +263,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'); } { @@ -497,6 +509,28 @@ SKIP: { is($body, sha1_hex(''), 'read expected body #2'); } +SKIP: { + skip 'TCP_DEFER_ACCEPT is Linux-only', 1 if $^O ne 'linux'; + my $var = Socket::TCP_DEFER_ACCEPT(); + defined(my $x = getsockopt($sock, IPPROTO_TCP, $var)) or die; + is(unpack('i', $x), $defer_accept_val, + 'TCP_DEFER_ACCEPT unchanged if previously set'); +}; +SKIP: { + skip 'SO_ACCEPTFILTER is FreeBSD-only', 1 if $^O ne 'freebsd'; + skip 'accf_data not loaded: kldload accf_data' if !defined $accf_arg; + my $var = PublicInbox::Daemon::SO_ACCEPTFILTER(); + defined(my $x = getsockopt($sock, SOL_SOCKET, $var)) or die; + is($x, $accf_arg, 'SO_ACCEPTFILTER unchanged if previously set'); +}; +SKIP: { + skip 'only testing lsof(8) output on Linux', 1 if $^O ne 'linux'; + skip 'no lsof in PATH', 1 unless which('lsof'); + my @lsof = `lsof -p $pid`; + is_deeply([grep(/\bdeleted\b/, @lsof)], [], 'no lingering deleted inputs'); + is_deeply([grep(/\bpipe\b/, @lsof)], [], 'no extra pipes with -W0'); +}; + done_testing(); sub capture {