]> Sergey Matveev's repositories - public-inbox.git/blob - lib/PublicInbox/Listener.pm
09f1f2e5fabde020d464995552e896729981d4c6
[public-inbox.git] / lib / PublicInbox / Listener.pm
1 # Copyright (C) 2015-2021 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3 #
4 # Used by -nntpd for listen sockets
5 package PublicInbox::Listener;
6 use strict;
7 use parent 'PublicInbox::DS';
8 use Socket qw(SOL_SOCKET SO_KEEPALIVE IPPROTO_TCP TCP_NODELAY);
9 use IO::Handle;
10 use PublicInbox::Syscall qw(EPOLLIN EPOLLEXCLUSIVE);
11 use Errno qw(EAGAIN ECONNABORTED EPERM);
12
13 # Warn on transient errors, mostly resource limitations.
14 # EINTR would indicate the failure to set NonBlocking in systemd or similar
15 my %ERR_WARN = map {;
16         eval("Errno::$_()") => $_
17 } qw(EMFILE ENFILE ENOBUFS ENOMEM EINTR);
18
19 sub new ($$$) {
20         my ($class, $s, $cb) = @_;
21         setsockopt($s, SOL_SOCKET, SO_KEEPALIVE, 1);
22         setsockopt($s, IPPROTO_TCP, TCP_NODELAY, 1); # ignore errors on non-TCP
23         listen($s, 2**31 - 1); # kernel will clamp
24         my $self = bless { post_accept => $cb }, $class;
25         $self->SUPER::new($s, EPOLLIN|EPOLLEXCLUSIVE);
26 }
27
28 sub event_step {
29         my ($self) = @_;
30         my $sock = $self->{sock} or return;
31
32         # no loop here, we want to fairly distribute clients
33         # between multiple processes sharing the same socket
34         # XXX our event loop needs better granularity for
35         # a single accept() here to be, umm..., acceptable
36         # on high-traffic sites.
37         if (my $addr = accept(my $c, $sock)) {
38                 IO::Handle::blocking($c, 0); # no accept4 :<
39                 eval { $self->{post_accept}->($c, $addr, $sock) };
40                 warn "E: $@\n" if $@;
41         } elsif ($! == EAGAIN || $! == ECONNABORTED || $! == EPERM) {
42                 # EAGAIN is common and likely
43                 # ECONNABORTED is common with bad connections
44                 # EPERM happens if firewall rules prevent a connection
45                 # on Linux (and everything that emulates Linux).
46                 # Firewall rules are sometimes intentional, so we don't
47                 # warn on EPERM to avoid being too noisy...
48                 return;
49         } elsif (my $sym = $ERR_WARN{int($!)}) {
50                 warn "W: accept(): $! ($sym)\n";
51         } else {
52                 warn "BUG?: accept(): $!\n";
53         }
54 }
55
56 1;