Skip to content

Commit 7f7d681

Browse files
[PR #10656/06db052e backport][3.11] Revert: Close the socket if there's a failure in start_connection() #10464 (#10657)
**This is a backport of PR #10656 as merged into master (06db052).** Reverts #10464 While this change improved the situation for uvloop users, it caused a regression with `SelectorEventLoop` (issue #10617) The alternative fix is MagicStack/uvloop#646 (not merged at the time of this PR) issue #10617 appears to be very similar to python/cpython@d5aeccf If someone can come up with a working reproducer for #10617 we can revisit this. cc @top-oai Minimal implementation that shows on cancellation the socket is cleaned up without the explicit `close` #10617 (comment) so this should be unneeded unless I've missed something (very possible with all the moving parts here) ## Related issue number fixes #10617 Co-authored-by: J. Nick Koston <[email protected]>
1 parent b93993d commit 7f7d681

File tree

5 files changed

+6
-65
lines changed

5 files changed

+6
-65
lines changed

CHANGES/10464.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10656.bugfix.rst

CHANGES/10617.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10656.bugfix.rst

CHANGES/10656.bugfix.rst

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Reverted explicitly closing sockets if an exception is raised during ``create_connection`` -- by :user:`bdraco`.
2+
3+
This change originally appeared in aiohttp 3.11.13

aiohttp/connector.py

+1-15
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,6 @@ async def _wrap_create_connection(
11081108
client_error: Type[Exception] = ClientConnectorError,
11091109
**kwargs: Any,
11101110
) -> Tuple[asyncio.Transport, ResponseHandler]:
1111-
sock: Union[socket.socket, None] = None
11121111
try:
11131112
async with ceil_timeout(
11141113
timeout.sock_connect, ceil_threshold=timeout.ceil_threshold
@@ -1120,11 +1119,7 @@ async def _wrap_create_connection(
11201119
interleave=self._interleave,
11211120
loop=self._loop,
11221121
)
1123-
connection = await self._loop.create_connection(
1124-
*args, **kwargs, sock=sock
1125-
)
1126-
sock = None
1127-
return connection
1122+
return await self._loop.create_connection(*args, **kwargs, sock=sock)
11281123
except cert_errors as exc:
11291124
raise ClientConnectorCertificateError(req.connection_key, exc) from exc
11301125
except ssl_errors as exc:
@@ -1133,15 +1128,6 @@ async def _wrap_create_connection(
11331128
if exc.errno is None and isinstance(exc, asyncio.TimeoutError):
11341129
raise
11351130
raise client_error(req.connection_key, exc) from exc
1136-
finally:
1137-
if sock is not None:
1138-
# Will be hit if an exception is thrown before the event loop takes the socket.
1139-
# In that case, proactively close the socket to guard against event loop leaks.
1140-
# For example, see https://github.com/MagicStack/uvloop/issues/653.
1141-
try:
1142-
sock.close()
1143-
except OSError as exc:
1144-
raise client_error(req.connection_key, exc) from exc
11451131

11461132
async def _wrap_existing_connection(
11471133
self,

tests/test_connector.py

-50
Original file line numberDiff line numberDiff line change
@@ -617,56 +617,6 @@ async def certificate_error(*args, **kwargs):
617617
await conn.close()
618618

619619

620-
async def test_tcp_connector_closes_socket_on_error(
621-
loop: asyncio.AbstractEventLoop, start_connection: mock.AsyncMock
622-
) -> None:
623-
req = ClientRequest("GET", URL("https://127.0.0.1:443"), loop=loop)
624-
625-
conn = aiohttp.TCPConnector()
626-
with (
627-
mock.patch.object(
628-
conn._loop,
629-
"create_connection",
630-
autospec=True,
631-
spec_set=True,
632-
side_effect=ValueError,
633-
),
634-
pytest.raises(ValueError),
635-
):
636-
await conn.connect(req, [], ClientTimeout())
637-
638-
assert start_connection.return_value.close.called
639-
640-
await conn.close()
641-
642-
643-
async def test_tcp_connector_closes_socket_on_error_results_in_another_error(
644-
loop: asyncio.AbstractEventLoop, start_connection: mock.AsyncMock
645-
) -> None:
646-
"""Test that when error occurs while closing the socket."""
647-
req = ClientRequest("GET", URL("https://127.0.0.1:443"), loop=loop)
648-
start_connection.return_value.close.side_effect = OSError(
649-
1, "error from closing socket"
650-
)
651-
652-
conn = aiohttp.TCPConnector()
653-
with (
654-
mock.patch.object(
655-
conn._loop,
656-
"create_connection",
657-
autospec=True,
658-
spec_set=True,
659-
side_effect=ValueError,
660-
),
661-
pytest.raises(aiohttp.ClientConnectionError, match="error from closing socket"),
662-
):
663-
await conn.connect(req, [], ClientTimeout())
664-
665-
assert start_connection.return_value.close.called
666-
667-
await conn.close()
668-
669-
670620
async def test_tcp_connector_server_hostname_default(
671621
loop: Any, start_connection: mock.AsyncMock
672622
) -> None:

0 commit comments

Comments
 (0)