diff --git a/h2/connection.py b/h2/connection.py index 9974942b9..0d701bcf5 100644 --- a/h2/connection.py +++ b/h2/connection.py @@ -1296,7 +1296,16 @@ def receive_data(self, data): try: for frame in self.incoming_buffer: - events.extend(self._receive_frame(frame)) + frame_events = self._receive_frame(frame) + + # If more than one event popped out here, we want to process + # them as "related events". + if len(frame_events) > 1: + event_set = frozenset(frame_events) + for event in frame_events: + event.related_events = event_set + + events.extend(frame_events) except InvalidPaddingError: self._terminate_connection(PROTOCOL_ERROR) raise ProtocolError("Received frame with invalid padding.") diff --git a/h2/events.py b/h2/events.py index e749101b2..06b1e336c 100644 --- a/h2/events.py +++ b/h2/events.py @@ -23,6 +23,9 @@ class RequestReceived(object): .. versionchanged:: 2.3.0 Changed the type of ``headers`` to :class:`HeaderTuple `. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``related_events`` property. """ def __init__(self): #: The Stream ID for the stream this request was made on. @@ -31,6 +34,13 @@ def __init__(self): #: The request headers. self.headers = None + #: Any events "related" to this one: that is, any events that occurred + #: at the exact same time as this one because their data was carried on + #: the same frame. All of these events need to be processed at once. + #: + #: .. versionadded:: 2.4.0 + self.related_events = frozenset() + def __repr__(self): return "" % ( self.stream_id, self.headers @@ -46,6 +56,9 @@ class ResponseReceived(object): .. versionchanged:: 2.3.0 Changed the type of ``headers`` to :class:`HeaderTuple `. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``related_events`` property. """ def __init__(self): #: The Stream ID for the stream this response was made on. @@ -54,6 +67,13 @@ def __init__(self): #: The response headers. self.headers = None + #: Any events "related" to this one: that is, any events that occurred + #: at the exact same time as this one because their data was carried on + #: the same frame. All of these events need to be processed at once. + #: + #: .. versionadded:: 2.4.0 + self.related_events = frozenset() + def __repr__(self): return "" % ( self.stream_id, self.headers @@ -72,6 +92,9 @@ class TrailersReceived(object): .. versionchanged:: 2.3.0 Changed the type of ``headers`` to :class:`HeaderTuple `. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``related_events`` property. """ def __init__(self): #: The Stream ID for the stream on which these trailers were received. @@ -80,6 +103,13 @@ def __init__(self): #: The trailers themselves. self.headers = None + #: Any events "related" to this one: that is, any events that occurred + #: at the exact same time as this one because their data was carried on + #: the same frame. All of these events need to be processed at once. + #: + #: .. versionadded:: 2.4.0 + self.related_events = frozenset() + def __repr__(self): return "" % ( self.stream_id, self.headers @@ -104,6 +134,9 @@ class InformationalResponseReceived(object): .. versionchanged:: 2.3.0 Changed the type of ``headers`` to :class:`HeaderTuple `. This has no effect on current users. + + .. versionchanged:: 2.4.0 + Added ``related_events`` property. """ def __init__(self): #: The Stream ID for the stream this informational response was made @@ -113,6 +146,13 @@ def __init__(self): #: The headers for this informational response. self.headers = None + #: Any events "related" to this one: that is, any events that occurred + #: at the exact same time as this one because their data was carried on + #: the same frame. All of these events need to be processed at once. + #: + #: .. versionadded:: 2.4.0 + self.related_events = frozenset() + def __repr__(self): return "" % ( self.stream_id, self.headers @@ -124,6 +164,9 @@ class DataReceived(object): The DataReceived event is fired whenever data is received on a stream from the remote peer. The event carries the data itself, and the stream ID on which the data was received. + + .. versionchanged:: 2.4.0 + Added ``related_events`` property. """ def __init__(self): #: The Stream ID for the stream this data was received on. @@ -138,6 +181,13 @@ def __init__(self): #: than ``len(data)``. self.flow_controlled_length = None + #: Any events "related" to this one: that is, any events that occurred + #: at the exact same time as this one because their data was carried on + #: the same frame. All of these events need to be processed at once. + #: + #: .. versionadded:: 2.4.0 + self.related_events = frozenset() + def __repr__(self): return ( "" % self.stream_id @@ -333,6 +393,9 @@ class PriorityUpdated(object): This event is purely advisory, and does not need to be acted on. .. versionadded:: 2.0.0 + + .. versionchanged:: 2.4.0 + Added ``related_events`` property. """ def __init__(self): #: The ID of the stream whose priority information is being updated. @@ -350,6 +413,13 @@ def __init__(self): #: parent. self.exclusive = None + #: Any events "related" to this one: that is, any events that occurred + #: at the exact same time as this one because their data was carried on + #: the same frame. All of these events need to be processed at once. + #: + #: .. versionadded:: 2.4.0 + self.related_events = frozenset() + def __repr__(self): return ( "