From 204ec92bb191f6f31a8f93bdea54a4d812a3c17d Mon Sep 17 00:00:00 2001 From: baizhu <806927537@qq.com> Date: Wed, 31 Dec 2025 11:37:47 +0800 Subject: [PATCH 1/3] Fix zombie connection when TLS handshake fails in HTTP proxy When using an HTTP proxy to connect to HTTPS servers, if the TLS handshake fails after CONNECT succeeds, the connection remains in ACTIVE state and never gets cleaned up, occupying the connection pool forever. This fix ensures the connection is properly closed when TLS fails, preventing Pool timeout issues. --- httpcore/_async/http_proxy.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/httpcore/_async/http_proxy.py b/httpcore/_async/http_proxy.py index cc9d9206..f83b12a6 100644 --- a/httpcore/_async/http_proxy.py +++ b/httpcore/_async/http_proxy.py @@ -312,9 +312,15 @@ async def handle_async_request(self, request: Request) -> Response: "server_hostname": self._remote_origin.host.decode("ascii"), "timeout": timeout, } - async with Trace("start_tls", logger, request, kwargs) as trace: - stream = await stream.start_tls(**kwargs) - trace.return_value = stream + try: + async with Trace("start_tls", logger, request, kwargs) as trace: + stream = await stream.start_tls(**kwargs) + trace.return_value = stream + except Exception: + # Close the underlying connection when TLS handshake fails to avoid + # zombie connections occupying the connection pool + await self._connection.aclose() + raise # Determine if we should be using HTTP/1.1 or HTTP/2 ssl_object = stream.get_extra_info("ssl_object") From 30789fc7ea51969c7a52426fece83236c9c988f2 Mon Sep 17 00:00:00 2001 From: baizhu <806927537@qq.com> Date: Wed, 31 Dec 2025 12:42:08 +0800 Subject: [PATCH 2/3] Fix sync version: Add try-except for TLS handshake failure Apply the same fix to the synchronous version to keep async/sync in sync. --- httpcore/_sync/http_proxy.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/httpcore/_sync/http_proxy.py b/httpcore/_sync/http_proxy.py index ecca88f7..9b3a4a36 100644 --- a/httpcore/_sync/http_proxy.py +++ b/httpcore/_sync/http_proxy.py @@ -307,14 +307,15 @@ def handle_request(self, request: Request) -> Response: alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] ssl_context.set_alpn_protocols(alpn_protocols) - kwargs = { - "ssl_context": ssl_context, - "server_hostname": self._remote_origin.host.decode("ascii"), - "timeout": timeout, - } - with Trace("start_tls", logger, request, kwargs) as trace: - stream = stream.start_tls(**kwargs) - trace.return_value = stream + try: + with Trace("start_tls", logger, request, kwargs) as trace: + stream = stream.start_tls(**kwargs) + trace.return_value = stream + except Exception: + # Close the underlying connection when TLS handshake fails to avoid + # zombie connections occupying the connection pool + self._connection.close() + raise # Determine if we should be using HTTP/1.1 or HTTP/2 ssl_object = stream.get_extra_info("ssl_object") From 00b33c3aedb78132bcac8935af3fd8014ffb5f47 Mon Sep 17 00:00:00 2001 From: baizhu <806927537@qq.com> Date: Wed, 31 Dec 2025 12:58:01 +0800 Subject: [PATCH 3/3] Fix kwargs definition in sync version --- httpcore/_sync/http_proxy.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/httpcore/_sync/http_proxy.py b/httpcore/_sync/http_proxy.py index 9b3a4a36..69310ea5 100644 --- a/httpcore/_sync/http_proxy.py +++ b/httpcore/_sync/http_proxy.py @@ -307,6 +307,11 @@ def handle_request(self, request: Request) -> Response: alpn_protocols = ["http/1.1", "h2"] if self._http2 else ["http/1.1"] ssl_context.set_alpn_protocols(alpn_protocols) + kwargs = { + "ssl_context": ssl_context, + "server_hostname": self._remote_origin.host.decode("ascii"), + "timeout": timeout, + } try: with Trace("start_tls", logger, request, kwargs) as trace: stream = stream.start_tls(**kwargs)