logger.debug("%r got a new connection from %r: %r",
server, addr, conn)
conn.setblocking(False)
- except (BlockingIOError, InterruptedError, ConnectionAbortedError):
- # Early exit because the socket accept buffer is empty.
- return None
+ except ConnectionAbortedError:
+ # Discard connections that were aborted before accept().
+ continue
+ except (BlockingIOError, InterruptedError):
+ # Early exit because of a signal or
+ # the socket accept buffer is empty.
+ return
except OSError as exc:
# There's nowhere to send the error, so just log it.
if exc.errno in (errno.EMFILE, errno.ENFILE,
self.loop.run_until_complete(asyncio.sleep(0))
self.assertEqual(sock.accept.call_count, backlog)
+ def test_accept_connection_skip_connectionabortederror(self):
+ sock = mock.Mock()
+
+ def mock_sock_accept():
+ # mock accept(2) returning -ECONNABORTED every-other
+ # time that it's called. This applies most to OpenBSD
+ # whose sockets generate this errno more reproducibly than
+ # Linux and other OS.
+ if sock.accept.call_count % 2 == 0:
+ raise ConnectionAbortedError
+ return (mock.Mock(), mock.Mock())
+
+ sock.accept.side_effect = mock_sock_accept
+ backlog = 100
+ # test that _accept_connection's loop calls sock.accept
+ # all 100 times, continuing past ConnectionAbortedError
+ # instead of unnecessarily returning early
+ mock_obj = mock.patch.object
+ with mock_obj(self.loop, '_accept_connection2') as accept2_mock:
+ self.loop._accept_connection(
+ mock.Mock(), sock, backlog=backlog)
+ # as in test_accept_connection_multiple avoid task pending
+ # warnings by using asyncio.sleep(0)
+ self.loop.run_until_complete(asyncio.sleep(0))
+ self.assertEqual(sock.accept.call_count, backlog)
class SelectorTransportTests(test_utils.TestCase):