Repository navigation
AnyIOStream.start_tls leaves the TCP socket open when the TLS handshake is cancelled
#1128
Replies: 1 comment
|
I ran into this too and confirmed it on httpcore 1.0.9 with both the asyncio and trio backends: after the cancel the socket stays open, and on asyncio it is still registered with the event loop. Changing The change with a test is on my fork: Zhassulan1@e520b33 The test fails on master on both backends and passes with the change. The full test suite passes with 100% coverage on Python 3.8 to 3.14. Pull request creation is limited to collaborators at the moment, so I could not open a PR. Happy to open one if that becomes possible, or feel free to take the commit as is. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
If a request is cancelled during the TLS handshake, httpcore does not close the TCP socket. The socket stays open until the server closes it. This is not the proxy-tunnel case in #905 and #921, but a direct connection.
Versions
httpcore 1.0.9 (the code is the same on
master), anyio 4.14.2 and 4.15.1, Python 3.14.6, asyncio.Reproduction
A server that accepts the TCP connection but never answers the TLS handshake, and a request cancelled during the handshake:
Expected
closedand1: the socket is closed once the cancelled request returns.Actual
openand2: the socket is still open and still watched by the event loop, so garbage collection does not close it either.Cause
AnyIOStream.start_tls(httpcore/_backends/anyio.py) closes the stream only onException:A cancellation raises
CancelledError, which is aBaseException, so this branch does not run.TrioStream.start_tls(httpcore/_backends/trio.py) has the sameexcept Exception.Suggested fix
In both backends, catch
BaseExceptionand close withaclose_forcefully, as anyio's ownconnect_tcpdoes for its TLS branch (except BaseException: await aclose_forcefully(connected_stream)).aclose_forcefullycompletes even while the task is being cancelled. With this change in the anyio backend, the reproduction printsclosedand1.All reactions