use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
use IO::Handle;
use IO::Socket;
-use Socket qw(IPPROTO_TCP);
+use Socket qw(IPPROTO_TCP SOL_SOCKET);
+sub SO_ACCEPTFILTER () { 0x1000 }
use Cwd qw/abs_path/;
STDOUT->autoflush(1);
STDERR->autoflush(1);
}
}
-sub defer_accept ($) {
+sub defer_accept ($$) {
+ my ($s, $af_name) = @_;
+ return unless defined $af_name;
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);
+ } elsif ($^O eq 'freebsd') {
+ my $x = getsockopt($s, SOL_SOCKET, SO_ACCEPTFILTER);
+ return if defined $x; # don't change if set
+ my $accf_arg = pack('a16a240', $af_name, '');
+ setsockopt($s, SOL_SOCKET, SO_ACCEPTFILTER, $accf_arg);
}
- # TODO FreeBSD accf_http / accf_data
}
-sub daemon_loop ($$$) {
- my ($refresh, $post_accept, $nntpd) = @_;
+sub daemon_loop ($$$$) {
+ my ($refresh, $post_accept, $nntpd, $af_default) = @_;
PublicInbox::EvCleanup::enable(); # early for $refresh
my %post_accept;
while (my ($k, $v) = each %tls_opt) {
# NNTPS, HTTPS, HTTP, and POP3S are client-first traffic
# NNTP and POP3 are server-first
- defer_accept($_) if $tls_cb || !$nntpd;
+ defer_accept($_, $tls_cb ? 'dataready' : $af_default);
# this calls epoll_create:
PublicInbox::Listener->new($_, $tls_cb || $post_accept)
sub run ($$$;$) {
my ($default, $refresh, $post_accept, $nntpd) = @_;
daemon_prepare($default);
+ my $af_default = $default =~ /:8080\z/ ? 'httpready' : undef;
daemonize();
- daemon_loop($refresh, $post_accept, $nntpd);
+ daemon_loop($refresh, $post_accept, $nntpd, $af_default);
}
sub do_chown ($) {
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);
Listen => 1024,
);
my $sock = IO::Socket::INET->new(%opts);
-my $defer_accept_val;
+
+# 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());
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";
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");
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');
+};
done_testing();
defined(my $x = getsockopt($sock, IPPROTO_TCP, $var)) or die;
ok(unpack('i', $x) > 0, 'TCP_DEFER_ACCEPT set');
};
+SKIP: {
+ skip 'SO_ACCEPTFILTER is FreeBSD-only', 1 if $^O ne 'freebsd';
+ if (system('kldstat -m accf_http >/dev/null') != 0) {
+ skip 'accf_http not loaded: kldload accf_http', 1;
+ }
+ require PublicInbox::Daemon;
+ my $var = PublicInbox::Daemon::SO_ACCEPTFILTER();
+ my $x = getsockopt($sock, SOL_SOCKET, $var);
+ like($x, qr/\Ahttpready\0+\z/, 'got httpready accf for HTTP');
+};
done_testing();
use warnings;
use Test::More;
use File::Temp qw(tempdir);
-use Socket qw(SOCK_STREAM IPPROTO_TCP);
+use Socket qw(SOCK_STREAM IPPROTO_TCP SOL_SOCKET);
# 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)) {
defined($x = getsockopt($starttls, IPPROTO_TCP, $var)) or die;
is(unpack('i', $x), 0, 'TCP_DEFER_ACCEPT is 0 on plain NNTP');
};
+ SKIP: {
+ skip 'SO_ACCEPTFILTER is FreeBSD-only', 2 if $^O ne 'freebsd';
+ if (system('kldstat -m accf_data >/dev/null')) {
+ skip 'accf_data not loaded? kldload accf_data', 2;
+ }
+ require PublicInbox::Daemon;
+ my $var = PublicInbox::Daemon::SO_ACCEPTFILTER();
+ my $x = getsockopt($nntps, SOL_SOCKET, $var);
+ like($x, qr/\Adataready\0+\z/, 'got dataready accf for NNTPS');
+ $x = getsockopt($starttls, IPPROTO_TCP, $var);
+ is($x, undef, 'no BSD accept filter for plain NNTP');
+ };
$c = undef;
kill('TERM', $pid);