h2: Guard against KeyError for data received on a closed stream#365
Closed
gonzageraci wants to merge 1 commit into
Closed
h2: Guard against KeyError for data received on a closed stream#365gonzageraci wants to merge 1 commit into
gonzageraci wants to merge 1 commit into
Conversation
When an application sends its response and the stream is closed before the client finishes sending the request body, h2 can still emit DataReceived events for that stream. The DataReceived branch indexed self.streams without guarding a missing key, raising KeyError and crashing the connection. Mirror the existing StreamEnded guard and ignore data for an already-closed stream.
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.
What
The HTTP/2 protocol crashes the connection with a
KeyErrorwhen aDataReceivedevent arrives for a stream that is no longer tracked inself.streams.Why
When an application sends its complete response and the stream is closed (removed from
self.streamsvia_close_stream) before the client has finished sending the request body, the h2 state machine can still emitDataReceivedevents for that stream. TheDataReceivedbranch in_handle_eventsindexesself.streams[event.stream_id]without guarding for a missing key, so the lookup raisesKeyErrorand the connection task dies withUnhandled exception in client_connected_cb.This is observed in production behind an HTTP/2 load balancer:
The adjacent
StreamEndedbranch already guards the same lookup with atry/except KeyErrorfor exactly this "response sent before full request received" case; theDataReceivedbranch was missing the equivalent guard.Change
Wrap the
DataReceivedstream dispatch intry/except KeyError, mirroring the existingStreamEndedhandling. Connection-level flow control is still acknowledged viaacknowledge_received_data, since the stream remains valid at the h2 layer.Added a regression test that reproduces the race (stream closed, then body data received) and asserts the connection no longer raises. Without the fix the test fails with
KeyError: 1; with it the fulltests/protocol/test_h2.pysuite passes, andblack,isort,flake8andmypyare clean.