# -*- coding: utf-8 -*- """ test_priority ~~~~~~~~~~~~~ Test the priority logic of Hyper-h2. """ import pytest import h2.config import h2.connection import h2.errors import h2.events import h2.exceptions import h2.stream class TestPriority(object): """ Basic priority tests. """ example_request_headers = [ (':authority', 'example.com'), (':path', '/'), (':scheme', 'https'), (':method', 'GET'), ] example_response_headers = [ (':status', '200'), ('server', 'pytest-h2'), ] server_config = h2.config.H2Configuration(client_side=False) def test_receiving_priority_emits_priority_update(self, frame_factory): """ Receiving a priority frame emits a PriorityUpdated event. """ c = h2.connection.H2Connection(config=self.server_config) c.initiate_connection() c.receive_data(frame_factory.preamble()) c.clear_outbound_data_buffer() f = frame_factory.build_priority_frame( stream_id=1, weight=255, ) events = c.receive_data(f.serialize()) assert len(events) == 1 assert not c.data_to_send() event = events[0] assert isinstance(event, h2.events.PriorityUpdated) assert event.stream_id == 1 assert event.depends_on == 0 assert event.weight == 256 assert event.exclusive is False def test_headers_with_priority_info(self, frame_factory): """ Receiving a HEADERS frame with priority information on it emits a PriorityUpdated event. """ c = h2.connection.H2Connection(config=self.server_config) c.initiate_connection() c.receive_data(frame_factory.preamble()) c.clear_outbound_data_buffer() f = frame_factory.build_headers_frame( headers=self.example_request_headers, stream_id=3, flags=['PRIORITY'], stream_weight=15, depends_on=1, exclusive=True, ) events = c.receive_data(f.serialize()) assert len(events) == 2 assert not c.data_to_send() event = events[1] assert isinstance(event, h2.events.PriorityUpdated) assert event.stream_id == 3 assert event.depends_on == 1 assert event.weight == 16 assert event.exclusive is True def test_streams_may_not_depend_on_themselves(self, frame_factory): """ A stream adjusted to depend on itself causes a Protocol Error. """ c = h2.connection.H2Connection(config=self.server_config) c.initiate_connection() c.receive_data(frame_factory.preamble()) c.clear_outbound_data_buffer() f = frame_factory.build_headers_frame( headers=self.example_request_headers, stream_id=3, flags=['PRIORITY'], stream_weight=15, depends_on=1, exclusive=True, ) c.receive_data(f.serialize()) c.clear_outbound_data_buffer() f = frame_factory.build_priority_frame( stream_id=3, depends_on=3, weight=15 ) with pytest.raises(h2.exceptions.ProtocolError): c.receive_data(f.serialize()) expected_frame = frame_factory.build_goaway_frame( last_stream_id=3, error_code=h2.errors.ErrorCodes.PROTOCOL_ERROR, ) assert c.data_to_send() == expected_frame.serialize() @pytest.mark.parametrize( 'depends_on,weight,exclusive', [ (0, 256, False), (3, 128, False), (3, 128, True), ] ) def test_can_prioritize_stream(self, depends_on, weight, exclusive, frame_factory): """ hyper-h2 can emit priority frames. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers(headers=self.example_request_headers, stream_id=1) c.send_headers(headers=self.example_request_headers, stream_id=3) c.clear_outbound_data_buffer() c.prioritize( stream_id=1, depends_on=depends_on, weight=weight, exclusive=exclusive ) f = frame_factory.build_priority_frame( stream_id=1, weight=weight - 1, depends_on=depends_on, exclusive=exclusive, ) assert c.data_to_send() == f.serialize() @pytest.mark.parametrize( 'depends_on,weight,exclusive', [ (0, 256, False), (1, 128, False), (1, 128, True), ] ) def test_emit_headers_with_priority_info(self, depends_on, weight, exclusive, frame_factory): """ It is possible to send a headers frame with priority information on it. """ c = h2.connection.H2Connection() c.initiate_connection() c.clear_outbound_data_buffer() c.send_headers( headers=self.example_request_headers, stream_id=3, priority_weight=weight, priority_depends_on=depends_on, priority_exclusive=exclusive, ) f = frame_factory.build_headers_frame( headers=self.example_request_headers, stream_id=3, flags=['PRIORITY'], stream_weight=weight - 1, depends_on=depends_on, exclusive=exclusive, ) assert c.data_to_send() == f.serialize() def test_may_not_prioritize_stream_to_depend_on_self(self, frame_factory): """ A stream adjusted to depend on itself causes a Protocol Error. """ c = h2.connection.H2Connection() c.initiate_connection() c.receive_data(frame_factory.preamble()) c.send_headers( headers=self.example_request_headers, stream_id=3, priority_weight=255, priority_depends_on=0, priority_exclusive=False, ) c.clear_outbound_data_buffer() with pytest.raises(h2.exceptions.ProtocolError): c.prioritize( stream_id=3, depends_on=3, ) assert not c.data_to_send() def test_may_not_initially_set_stream_depend_on_self(self, frame_factory): """ A stream that starts by depending on itself causes a Protocol Error. """ c = h2.connection.H2Connection() c.initiate_connection() c.receive_data(frame_factory.preamble()) c.clear_outbound_data_buffer() with pytest.raises(h2.exceptions.ProtocolError): c.send_headers( headers=self.example_request_headers, stream_id=3, priority_depends_on=3, ) assert not c.data_to_send() @pytest.mark.parametrize('weight', [0, -15, 257]) def test_prioritize_requires_valid_weight(self, weight): """ A call to prioritize with an invalid weight causes a ProtocolError. """ c = h2.connection.H2Connection() c.initiate_connection() c.clear_outbound_data_buffer() with pytest.raises(h2.exceptions.ProtocolError): c.prioritize(stream_id=1, weight=weight) assert not c.data_to_send() @pytest.mark.parametrize('weight', [0, -15, 257]) def test_send_headers_requires_valid_weight(self, weight): """ A call to send_headers with an invalid weight causes a ProtocolError. """ c = h2.connection.H2Connection() c.initiate_connection() c.clear_outbound_data_buffer() with pytest.raises(h2.exceptions.ProtocolError): c.send_headers( stream_id=1, headers=self.example_request_headers, priority_weight=weight ) assert not c.data_to_send() def test_prioritize_defaults(self, frame_factory): """ When prioritize() is called with no explicit arguments, it emits a weight of 16, depending on stream zero non-exclusively. """ c = h2.connection.H2Connection() c.initiate_connection() c.clear_outbound_data_buffer() c.prioritize(stream_id=1) f = frame_factory.build_priority_frame( stream_id=1, weight=15, depends_on=0, exclusive=False, ) assert c.data_to_send() == f.serialize() @pytest.mark.parametrize( 'priority_kwargs', [ {'priority_weight': 16}, {'priority_depends_on': 0}, {'priority_exclusive': False}, ] ) def test_send_headers_defaults(self, priority_kwargs, frame_factory): """ When send_headers() is called with only one explicit argument, it emits default values for everything else. """ c = h2.connection.H2Connection() c.initiate_connection() c.clear_outbound_data_buffer() c.send_headers( stream_id=1, headers=self.example_request_headers, **priority_kwargs ) f = frame_factory.build_headers_frame( headers=self.example_request_headers, stream_id=1, flags=['PRIORITY'], stream_weight=15, depends_on=0, exclusive=False, ) assert c.data_to_send() == f.serialize() def test_servers_cannot_prioritize(self, frame_factory): """ Server stacks are not allowed to call ``prioritize()``. """ c = h2.connection.H2Connection(config=self.server_config) c.initiate_connection() c.receive_data(frame_factory.preamble()) c.clear_outbound_data_buffer() f = frame_factory.build_headers_frame( stream_id=1, headers=self.example_request_headers, ) c.receive_data(f.serialize()) with pytest.raises(h2.exceptions.RFC1122Error): c.prioritize(stream_id=1) def test_servers_cannot_prioritize_with_headers(self, frame_factory): """ Server stacks are not allowed to prioritize on headers either. """ c = h2.connection.H2Connection(config=self.server_config) c.initiate_connection() c.receive_data(frame_factory.preamble()) c.clear_outbound_data_buffer() f = frame_factory.build_headers_frame( stream_id=1, headers=self.example_request_headers, ) c.receive_data(f.serialize()) with pytest.raises(h2.exceptions.RFC1122Error): c.send_headers( stream_id=1, headers=self.example_response_headers, priority_weight=16, )