Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Lib/asyncio/proactor_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,11 @@ def loop(f=None):
logger.debug("%r got a new connection from %r: %r",
server, addr, conn)
protocol = protocol_factory()
if server is not None and not server.is_serving():
# The server was closed after the connection was
# accepted.
conn.close()
return
if sslcontext is not None:
self._make_ssl_transport(
conn, protocol, sslcontext, server_side=True,
Expand Down
4 changes: 4 additions & 0 deletions Lib/asyncio/selector_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ async def _accept_connection2(
transport = None
try:
protocol = protocol_factory()
if server is not None and not server.is_serving():
# The server was closed after the connection was accepted.
conn.close()
return
waiter = self.create_future()
if sslcontext:
transport = self._make_ssl_transport(
Expand Down
32 changes: 32 additions & 0 deletions Lib/test/test_asyncio/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import threading
import unittest

from test import support
from test.support import socket_helper
from test.test_asyncio import utils as test_utils
from test.test_asyncio import functional as func_tests
Expand Down Expand Up @@ -187,6 +188,37 @@ async def serve(rd, wr):
loop.call_soon(wr.close)
await srv.wait_closed()

async def test_close_before_transport_created(self):
# gh-109564: Closing the server after a connection was accepted,
# but before its transport was created, must drop the connection
# quietly.
connected = False

class Proto(asyncio.Protocol):
def connection_made(self, transport):
nonlocal connected
connected = True

def factory():
srv.close()
return Proto()

errors = []
loop = asyncio.get_running_loop()
loop.set_exception_handler(lambda loop, ctx: errors.append(ctx))
srv = await loop.create_server(factory, socket_helper.HOSTv4, 0)
self.addCleanup(srv.close)

addr = srv.sockets[0].getsockname()
(rd, wr) = await asyncio.open_connection(addr[0], addr[1])
self.addCleanup(wr.close)
async with asyncio.timeout(support.SHORT_TIMEOUT):
self.assertEqual(await rd.read(), b'')
await srv.wait_closed()

self.assertFalse(connected)
self.assertEqual(errors, [])

async def test_close_clients(self):
async def serve(rd, wr):
try:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix a race in :mod:`asyncio` where closing a server after it accepted a
connection, but before the connection's transport was created, reported an
:exc:`AssertionError` to the exception handler. Such connections are now
closed quietly.
Loading