From a08f49b2c4867786c366922f1123fbb83bb419a8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 24 Sep 2025 09:12:10 -0500 Subject: [PATCH 1/2] Workaround uvloop losing track of sockets when passing a socket to create_connection related issue #10506 --- aiohttp/connector.py | 56 ++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/aiohttp/connector.py b/aiohttp/connector.py index e1288424bff..75c2fa0eed6 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -1215,26 +1215,43 @@ async def _wrap_create_connection( client_error: Type[Exception] = ClientConnectorError, **kwargs: Any, ) -> Tuple[asyncio.Transport, ResponseHandler]: + # Add ssl_shutdown_timeout for Python 3.11+ when SSL is used + if ( + kwargs.get("ssl") + and self._ssl_shutdown_timeout + and sys.version_info >= (3, 11) + ): + kwargs["ssl_shutdown_timeout"] = self._ssl_shutdown_timeout try: async with ceil_timeout( timeout.sock_connect, ceil_threshold=timeout.ceil_threshold ): - sock = await aiohappyeyeballs.start_connection( - addr_infos=addr_infos, - local_addr_infos=self._local_addr_infos, - happy_eyeballs_delay=self._happy_eyeballs_delay, - interleave=self._interleave, - loop=self._loop, - socket_factory=self._socket_factory, - ) - # Add ssl_shutdown_timeout for Python 3.11+ when SSL is used - if ( - kwargs.get("ssl") - and self._ssl_shutdown_timeout - and sys.version_info >= (3, 11) - ): - kwargs["ssl_shutdown_timeout"] = self._ssl_shutdown_timeout - return await self._loop.create_connection(*args, **kwargs, sock=sock) + if self._happy_eyeballs_delay is None: + # If happyeyeballs is disabled, connect in sequence + # this avoids a bug in uvloop where it can lose track + # of sockets passed between aiohappyeyeballs.start_connect + # and create_connection and try to reuse the same fd. + # https://github.com/aio-libs/aiohttp/issues/10506 + # https://github.com/MagicStack/uvloop/issues/645 + first_addr_infos = addr_infos[0] + address_tuple = first_addr_infos[4] + host: str = address_tuple[0] + port: int = address_tuple[1] + return await self._loop.create_connection( + *args, host=host, port=port, **kwargs + ) + else: + sock = await aiohappyeyeballs.start_connection( + addr_infos=addr_infos, + local_addr_infos=self._local_addr_infos, + happy_eyeballs_delay=self._happy_eyeballs_delay, + interleave=self._interleave, + loop=self._loop, + socket_factory=self._socket_factory, + ) + return await self._loop.create_connection( + *args, **kwargs, sock=sock + ) except cert_errors as exc: raise ClientConnectorCertificateError(req.connection_key, exc) from exc except ssl_errors as exc: @@ -1447,7 +1464,12 @@ async def _create_direct_connection( ) except (ClientConnectorError, asyncio.TimeoutError) as exc: last_exc = exc - aiohappyeyeballs.pop_addr_infos_interleave(addr_infos, self._interleave) + if self._happy_eyeballs_delay is None: + addr_infos.pop(0) + else: + aiohappyeyeballs.pop_addr_infos_interleave( + addr_infos, self._interleave + ) continue if req.is_ssl() and fingerprint: From decb65c4ebe867dec60c4ba7f3b7274a6f68871e Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 24 Sep 2025 09:13:56 -0500 Subject: [PATCH 2/2] Workaround uvloop losing track of sockets when passing a socket to create_connection related issue #10506 --- aiohttp/connector.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/aiohttp/connector.py b/aiohttp/connector.py index 75c2fa0eed6..523419cc771 100644 --- a/aiohttp/connector.py +++ b/aiohttp/connector.py @@ -1229,8 +1229,9 @@ async def _wrap_create_connection( if self._happy_eyeballs_delay is None: # If happyeyeballs is disabled, connect in sequence # this avoids a bug in uvloop where it can lose track - # of sockets passed between aiohappyeyeballs.start_connect - # and create_connection and try to reuse the same fd. + # of sockets passed directly to create_connection and + # try to reuse the same fd. The problem does not + # happen when passing host/port to create_connection. # https://github.com/aio-libs/aiohttp/issues/10506 # https://github.com/MagicStack/uvloop/issues/645 first_addr_infos = addr_infos[0]