about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--lib/PublicInbox/Daemon.pm26
-rw-r--r--t/httpd-corner.t19
-rw-r--r--t/httpd.t8
-rw-r--r--t/nntpd-tls.t11
4 files changed, 59 insertions, 5 deletions
diff --git a/lib/PublicInbox/Daemon.pm b/lib/PublicInbox/Daemon.pm
index c4481555..8b59b65f 100644
--- a/lib/PublicInbox/Daemon.pm
+++ b/lib/PublicInbox/Daemon.pm
@@ -8,6 +8,7 @@ use warnings;
 use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
 use IO::Handle;
 use IO::Socket;
+use Socket qw(IPPROTO_TCP);
 use Cwd qw/abs_path/;
 STDOUT->autoflush(1);
 STDERR->autoflush(1);
@@ -552,6 +553,18 @@ sub tls_start_cb ($$) {
         }
 }
 
+sub defer_accept ($) {
+        if ($^O eq 'linux') {
+                my ($s) = @_;
+                my $x = getsockopt($s, IPPROTO_TCP, Socket::TCP_DEFER_ACCEPT());
+                return unless defined $x; # may be Unix socket
+                my $sec = unpack('i', $x);
+                return if $sec > 0; # systemd users may set a higher value
+                setsockopt($s, IPPROTO_TCP, Socket::TCP_DEFER_ACCEPT(), 1);
+        }
+        # TODO FreeBSD accf_http / accf_data
+}
+
 sub daemon_loop ($$$) {
         my ($refresh, $post_accept, $nntpd) = @_;
         PublicInbox::EvCleanup::enable(); # early for $refresh
@@ -581,10 +594,15 @@ sub daemon_loop ($$$) {
         $SIG{HUP} = $refresh;
         $SIG{CHLD} = 'DEFAULT';
         $SIG{$_} = 'IGNORE' for qw(USR2 TTIN TTOU WINCH);
-        # this calls epoll_create:
-        @listeners = map {
-                PublicInbox::Listener->new($_,
-                                $post_accept{sockname($_)} || $post_accept)
+        @listeners = map {;
+                my $tls_cb = $post_accept{sockname($_)};
+
+                # NNTPS, HTTPS, HTTP, and POP3S are client-first traffic
+                # NNTP and POP3 are server-first
+                defer_accept($_) if $tls_cb || !$nntpd;
+
+                # this calls epoll_create:
+                PublicInbox::Listener->new($_, $tls_cb || $post_accept)
         } @listeners;
         PublicInbox::DS->EventLoop;
         $parent_pipe = undef;
diff --git a/t/httpd-corner.t b/t/httpd-corner.t
index c1dc77db..13befcf1 100644
--- a/t/httpd-corner.t
+++ b/t/httpd-corner.t
@@ -36,6 +36,17 @@ my %opts = (
         Listen => 1024,
 );
 my $sock = IO::Socket::INET->new(%opts);
+my $defer_accept_val;
+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";
+        }
+}
+
 my $upath = "$tmpdir/s";
 my $unix = IO::Socket::UNIX->new(
         Listen => 1024,
@@ -497,6 +508,14 @@ 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');
+};
+
 done_testing();
 
 sub capture {
diff --git a/t/httpd.t b/t/httpd.t
index c061031c..8c2a3173 100644
--- a/t/httpd.t
+++ b/t/httpd.t
@@ -10,6 +10,7 @@ foreach my $mod (qw(Plack::Util Plack::Builder HTTP::Date HTTP::Status)) {
 }
 use File::Temp qw/tempdir/;
 use IO::Socket::INET;
+use Socket qw(IPPROTO_TCP);
 require './t/common.perl';
 
 # FIXME: too much setup
@@ -99,6 +100,13 @@ EOF
                 'fsck on cloned directory successful');
 }
 
+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;
+        ok(unpack('i', $x) > 0, 'TCP_DEFER_ACCEPT set');
+};
+
 done_testing();
 
 1;
diff --git a/t/nntpd-tls.t b/t/nntpd-tls.t
index e8fb63b4..ef683cab 100644
--- a/t/nntpd-tls.t
+++ b/t/nntpd-tls.t
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 use Test::More;
 use File::Temp qw(tempdir);
-use Socket qw(SOCK_STREAM);
+use Socket qw(SOCK_STREAM IPPROTO_TCP);
 # IO::Poll and Net::NNTP are part of the standard library, but
 # distros may split them off...
 foreach my $mod (qw(DBD::SQLite IO::Socket::SSL Net::NNTP IO::Poll)) {
@@ -182,6 +182,15 @@ for my $args (
         is(sysread($slow, my $eof, 4096), 0, 'got EOF');
         $slow = undef;
 
+        SKIP: {
+                skip 'TCP_DEFER_ACCEPT is Linux-only', 2 if $^O ne 'linux';
+                my $var = Socket::TCP_DEFER_ACCEPT();
+                defined(my $x = getsockopt($nntps, IPPROTO_TCP, $var)) or die;
+                ok(unpack('i', $x) > 0, 'TCP_DEFER_ACCEPT set on NNTPS');
+                defined($x = getsockopt($starttls, IPPROTO_TCP, $var)) or die;
+                is(unpack('i', $x), 0, 'TCP_DEFER_ACCEPT is 0 on plain NNTP');
+        };
+
         $c = undef;
         kill('TERM', $pid);
         is($pid, waitpid($pid, 0), 'nntpd exited successfully');