Skip to content

Commit

Permalink
[PR #10464/182198fc backport][3.12] Close the socket if there's a fai…
Browse files Browse the repository at this point in the history
…lure in start_connection() (#10478)

Co-authored-by: Andrew Top <[email protected]>
  • Loading branch information
patchback[bot] and top-oai authored Feb 20, 2025
1 parent 9f933bf commit a9c8841
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGES/10464.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Changed connection creation to explicitly close sockets if an exception is raised in the event loop's ``create_connection`` method -- by :user:`top-oai`.
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Andrej Antonov
Andrew Leech
Andrew Lytvyn
Andrew Svetlov
Andrew Top
Andrew Zhou
Andrii Soldatenko
Anes Abismail
Expand Down
13 changes: 12 additions & 1 deletion aiohttp/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,7 @@ async def _wrap_create_connection(
client_error: Type[Exception] = ClientConnectorError,
**kwargs: Any,
) -> Tuple[asyncio.Transport, ResponseHandler]:
sock: Union[socket.socket, None] = None
try:
async with ceil_timeout(
timeout.sock_connect, ceil_threshold=timeout.ceil_threshold
Expand All @@ -1119,7 +1120,11 @@ async def _wrap_create_connection(
interleave=self._interleave,
loop=self._loop,
)
return await self._loop.create_connection(*args, **kwargs, sock=sock)
connection = await self._loop.create_connection(
*args, **kwargs, sock=sock
)
sock = None
return connection
except cert_errors as exc:
raise ClientConnectorCertificateError(req.connection_key, exc) from exc
except ssl_errors as exc:
Expand All @@ -1128,6 +1133,12 @@ async def _wrap_create_connection(
if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
raise
raise client_error(req.connection_key, exc) from exc
finally:
if sock is not None:
# Will be hit if an exception is thrown before the event loop takes the socket.
# In that case, proactively close the socket to guard against event loop leaks.
# For example, see https://github.com/MagicStack/uvloop/issues/653.
sock.close()

async def _wrap_existing_connection(
self,
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ def start_connection():
"aiohttp.connector.aiohappyeyeballs.start_connection",
autospec=True,
spec_set=True,
return_value=mock.create_autospec(socket.socket, spec_set=True, instance=True),
) as start_connection_mock:
yield start_connection_mock

Expand Down
23 changes: 23 additions & 0 deletions tests/test_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,29 @@ async def certificate_error(*args, **kwargs):
await conn.close()


async def test_tcp_connector_closes_socket_on_error(
loop: asyncio.AbstractEventLoop, start_connection: mock.AsyncMock
) -> None:
req = ClientRequest("GET", URL("https://127.0.0.1:443"), loop=loop)

conn = aiohttp.TCPConnector()
with (
mock.patch.object(
conn._loop,
"create_connection",
autospec=True,
spec_set=True,
side_effect=ValueError,
),
pytest.raises(ValueError),
):
await conn.connect(req, [], ClientTimeout())

assert start_connection.return_value.close.called

await conn.close()


async def test_tcp_connector_server_hostname_default(
loop: Any, start_connection: mock.AsyncMock
) -> None:
Expand Down
1 change: 1 addition & 0 deletions tests/test_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ async def make_conn():
"aiohttp.connector.aiohappyeyeballs.start_connection",
autospec=True,
spec_set=True,
return_value=mock.create_autospec(socket.socket, spec_set=True, instance=True),
)
def test_proxy_connection_error(self, start_connection: Any) -> None:
async def make_conn():
Expand Down

0 comments on commit a9c8841

Please sign in to comment.