# -*- coding: utf-8 -*- """ test_related_events.py ~~~~~~~~~~~~~~~~~~~~~~ Specific tests to validate the "related events" logic used by certain events inside hyper-h2. """ import h2.config import h2.connection import h2.events class TestRelatedEvents(object): """ Related events correlate all those events that happen on a single frame. """ example_request_headers = [ (':authority', 'example.com'), (':path', '/'), (':scheme', 'https'), (':method', 'GET'), ] example_response_headers = [ (':status', '200'), ('server', 'fake-serv/0.1.0') ] informational_response_headers = [ (':status', '100'), ('server', 'fake-serv/0.1.0') ] example_trailers = [ ('another', 'field'), ] server_config = h2.config.H2Configuration(client_side=False) def test_request_received_related_all(self, frame_factory): """ RequestReceived has two possible related events: PriorityUpdated and StreamEnded, all fired when a single HEADERS frame is received. """ c = h2.connection.H2Connection(config=self.server_config) c.initiate_connection() c.receive_data(frame_factory.preamble()) input_frame = frame_factory.build_headers_frame( headers=self.example_request_headers, flags=['END_STREAM', 'PRIORITY'], stream_weight=15, depends_on=0, exclusive=False, ) events = c.receive_data(input_frame.serialize()) assert len(events) == 3 base_event = events[0] other_events = events[1:] assert base_event.stream_ended in other_events assert isinstance(base_event.stream_ended, h2.events.StreamEnded) assert base_event.priority_updated in other_events assert isinstance( base_event.priority_updated, h2.events.PriorityUpdated ) def test_request_received_related_priority(self, frame_factory): """ RequestReceived can be related to PriorityUpdated. """ c = h2.connection.H2Connection(config=self.server_config) c.initiate_connection() c.receive_data(frame_factory.preamble()) input_frame = frame_factory.build_headers_frame( headers=self.example_request_headers, flags=['PRIORITY'], stream_weight=15, depends_on=0, exclusive=False, ) events = c.receive_data(input_frame.serialize()) assert len(events) == 2 base_event = events[0] priority_updated_event = events[1] assert base_event.priority_updated is priority_updated_event assert base_event.stream_ended is None assert isinstance( base_event.priority_updated, h2.events.PriorityUpdated ) def test_request_received_related_stream_ended(self, frame_factory): """ RequestReceived can be related to StreamEnded. """ c = h2.connection.H2Connection(config=self.server_config) c.initiate_connection() c.receive_data(frame_factory.preamble()) input_frame = frame_factory.build_headers_frame( headers=self.example_request_headers, flags=['END_STREAM'], ) events = c.receive_data(input_frame.serialize()) assert len(events) == 2 base_event = events[0] stream_ended_event = events[1] assert base_event.stream_ended is stream_ended_event assert base_event.priority_updated is None assert isinstance(base_event.stream_ended, h2.events.StreamEnded) def test_response_received_related_nothing(self, frame_factory): """ ResponseReceived is ordinarily related to no events. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers(stream_id=1, headers=self.example_request_headers) input_frame = frame_factory.build_headers_frame( headers=self.example_response_headers, ) events = c.receive_data(input_frame.serialize()) assert len(events) == 1 base_event = events[0] assert base_event.stream_ended is None assert base_event.priority_updated is None def test_response_received_related_all(self, frame_factory): """ ResponseReceived has two possible related events: PriorityUpdated and StreamEnded, all fired when a single HEADERS frame is received. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers(stream_id=1, headers=self.example_request_headers) input_frame = frame_factory.build_headers_frame( headers=self.example_response_headers, flags=['END_STREAM', 'PRIORITY'], stream_weight=15, depends_on=0, exclusive=False, ) events = c.receive_data(input_frame.serialize()) assert len(events) == 3 base_event = events[0] other_events = events[1:] assert base_event.stream_ended in other_events assert isinstance(base_event.stream_ended, h2.events.StreamEnded) assert base_event.priority_updated in other_events assert isinstance( base_event.priority_updated, h2.events.PriorityUpdated ) def test_response_received_related_priority(self, frame_factory): """ ResponseReceived can be related to PriorityUpdated. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers(stream_id=1, headers=self.example_request_headers) input_frame = frame_factory.build_headers_frame( headers=self.example_response_headers, flags=['PRIORITY'], stream_weight=15, depends_on=0, exclusive=False, ) events = c.receive_data(input_frame.serialize()) assert len(events) == 2 base_event = events[0] priority_updated_event = events[1] assert base_event.priority_updated is priority_updated_event assert base_event.stream_ended is None assert isinstance( base_event.priority_updated, h2.events.PriorityUpdated ) def test_response_received_related_stream_ended(self, frame_factory): """ ResponseReceived can be related to StreamEnded. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers(stream_id=1, headers=self.example_request_headers) input_frame = frame_factory.build_headers_frame( headers=self.example_response_headers, flags=['END_STREAM'], ) events = c.receive_data(input_frame.serialize()) assert len(events) == 2 base_event = events[0] stream_ended_event = events[1] assert base_event.stream_ended is stream_ended_event assert base_event.priority_updated is None assert isinstance(base_event.stream_ended, h2.events.StreamEnded) def test_trailers_received_related_all(self, frame_factory): """ TrailersReceived has two possible related events: PriorityUpdated and StreamEnded, all fired when a single HEADERS frame is received. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers(stream_id=1, headers=self.example_request_headers) f = frame_factory.build_headers_frame( headers=self.example_response_headers, ) c.receive_data(f.serialize()) input_frame = frame_factory.build_headers_frame( headers=self.example_trailers, flags=['END_STREAM', 'PRIORITY'], stream_weight=15, depends_on=0, exclusive=False, ) events = c.receive_data(input_frame.serialize()) assert len(events) == 3 base_event = events[0] other_events = events[1:] assert base_event.stream_ended in other_events assert isinstance(base_event.stream_ended, h2.events.StreamEnded) assert base_event.priority_updated in other_events assert isinstance( base_event.priority_updated, h2.events.PriorityUpdated ) def test_trailers_received_related_stream_ended(self, frame_factory): """ TrailersReceived can be related to StreamEnded by itself. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers(stream_id=1, headers=self.example_request_headers) f = frame_factory.build_headers_frame( headers=self.example_response_headers, ) c.receive_data(f.serialize()) input_frame = frame_factory.build_headers_frame( headers=self.example_trailers, flags=['END_STREAM'], ) events = c.receive_data(input_frame.serialize()) assert len(events) == 2 base_event = events[0] stream_ended_event = events[1] assert base_event.stream_ended is stream_ended_event assert base_event.priority_updated is None assert isinstance(base_event.stream_ended, h2.events.StreamEnded) def test_informational_response_related_nothing(self, frame_factory): """ InformationalResponseReceived in the standard case is related to nothing. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers(stream_id=1, headers=self.example_request_headers) input_frame = frame_factory.build_headers_frame( headers=self.informational_response_headers, ) events = c.receive_data(input_frame.serialize()) assert len(events) == 1 base_event = events[0] assert base_event.priority_updated is None def test_informational_response_received_related_all(self, frame_factory): """ InformationalResponseReceived has one possible related event: PriorityUpdated, fired when a single HEADERS frame is received. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers(stream_id=1, headers=self.example_request_headers) input_frame = frame_factory.build_headers_frame( headers=self.informational_response_headers, flags=['PRIORITY'], stream_weight=15, depends_on=0, exclusive=False, ) events = c.receive_data(input_frame.serialize()) assert len(events) == 2 base_event = events[0] priority_updated_event = events[1] assert base_event.priority_updated is priority_updated_event assert isinstance( base_event.priority_updated, h2.events.PriorityUpdated ) def test_data_received_normally_relates_to_nothing(self, frame_factory): """ A plain DATA frame leads to DataReceieved with no related events. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers(stream_id=1, headers=self.example_request_headers) f = frame_factory.build_headers_frame( headers=self.example_response_headers, ) c.receive_data(f.serialize()) input_frame = frame_factory.build_data_frame( data=b'some data', ) events = c.receive_data(input_frame.serialize()) assert len(events) == 1 base_event = events[0] assert base_event.stream_ended is None def test_data_received_related_stream_ended(self, frame_factory): """ DataReceived can be related to StreamEnded by itself. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers(stream_id=1, headers=self.example_request_headers) f = frame_factory.build_headers_frame( headers=self.example_response_headers, ) c.receive_data(f.serialize()) input_frame = frame_factory.build_data_frame( data=b'some data', flags=['END_STREAM'], ) events = c.receive_data(input_frame.serialize()) assert len(events) == 2 base_event = events[0] stream_ended_event = events[1] assert base_event.stream_ended is stream_ended_event assert isinstance(base_event.stream_ended, h2.events.StreamEnded)