Handle plain OSError when waiting for the writer to close#362
Open
joanfabregat wants to merge 1 commit into
Open
Handle plain OSError when waiting for the writer to close#362joanfabregat wants to merge 1 commit into
joanfabregat wants to merge 1 commit into
Conversation
A peer that becomes unreachable mid-connection (e.g. EHOSTUNREACH when the client host vanishes) raises a plain OSError from wait_closed(), which the except tuple in TCPServer._close() missed - only ConnectionError subclasses were caught. The exception then escaped the client_connected_cb task via run()'s finally block, or propagated into the ASGI application's send path via protocol_send(Closed). Catch OSError instead: BrokenPipeError, ConnectionAbortedError and ConnectionResetError are all OSError subclasses, so the tuple collapses to (OSError, RuntimeError, asyncio.CancelledError) - consistent with run()'s existing `except OSError`. Fixes pgjones#361 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #361.
When a peer becomes unreachable mid-connection (e.g.
EHOSTUNREACH— observed on Kubernetes when a client pod with open keep-alive connections is force-deleted),await self.writer.wait_closed()inTCPServer._close()raises a plainOSError, which the except tuple missed — CPython only mapsECONNRESET/EPIPE/ESHUTDOWN/ECONNABORTED/ECONNREFUSEDtoConnectionErrorsubclasses. The exception then either escapes theclient_connected_cbtask viarun()'sfinally:block ("Unhandled exception in client_connected_cb"), or propagates into the ASGI application's send path viaprotocol_send(Closed)— full tracebacks for both manifestations in #361.Change
Catch
OSErrorin_close(): sinceBrokenPipeError,ConnectionAbortedErrorandConnectionResetErrorare allOSErrorsubclasses, the tuple collapses to(OSError, RuntimeError, asyncio.CancelledError)— consistent with theexcept OSError: passthatrun()already applies to the connection body. This is the same pattern as #134 (ConnectionAbortedError) and #172 (CancelledError).A test is included: a
MemoryWriterwhosewait_closed()raisesOSError(EHOSTUNREACH)— it fails on currentmainwith the exact production traceback and passes with this change.Verification
main(raisesOSErrorout ofrun()), passes with the fixblack --check,isort --check,flake8cleanNotes
TimeoutErrorplus atransport.abort()at the same call site (since Python 3.11TimeoutErroris itself anOSErrorsubclass, so this change also stops that error from escaping — but without the abort Handle TimeoutError when waiting for the writer to close #342 adds). Whichever lands second needs a trivial rebase.RawDatabranch ofprotocol_send()(except (ConnectionError, RuntimeError)arounddrain()) likely has the sameEHOSTUNREACHblind spot, but it changes write-failure handling semantics so it's deliberately left out of this PR.