1# -*- coding: utf-8 -*-
2"""
3test_invalid_content_lengths.py
4~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6This module contains tests that use invalid content lengths, and validates that
7they fail appropriately.
8"""
9import pytest
10
11import h2.config
12import h2.connection
13import h2.errors
14import h2.events
15import h2.exceptions
16
17
18class TestInvalidContentLengths(object):
19    """
20    Hyper-h2 raises Protocol Errors when the content-length sent by a remote
21    peer is not valid.
22    """
23    example_request_headers = [
24        (':authority', 'example.com'),
25        (':path', '/'),
26        (':scheme', 'https'),
27        (':method', 'POST'),
28        ('content-length', '15'),
29    ]
30    example_response_headers = [
31        (':status', '200'),
32        ('server', 'fake-serv/0.1.0')
33    ]
34    server_config = h2.config.H2Configuration(client_side=False)
35
36    def test_too_much_data(self, frame_factory):
37        """
38        Remote peers sending data in excess of content-length causes Protocol
39        Errors.
40        """
41        c = h2.connection.H2Connection(config=self.server_config)
42        c.initiate_connection()
43        c.receive_data(frame_factory.preamble())
44
45        headers = frame_factory.build_headers_frame(
46            headers=self.example_request_headers
47        )
48        first_data = frame_factory.build_data_frame(data=b'\x01'*15)
49        c.receive_data(headers.serialize() + first_data.serialize())
50        c.clear_outbound_data_buffer()
51
52        second_data = frame_factory.build_data_frame(data=b'\x01')
53        with pytest.raises(h2.exceptions.InvalidBodyLengthError) as exp:
54            c.receive_data(second_data.serialize())
55
56        assert exp.value.expected_length == 15
57        assert exp.value.actual_length == 16
58        assert str(exp.value) == (
59            "InvalidBodyLengthError: Expected 15 bytes, received 16"
60        )
61
62        expected_frame = frame_factory.build_goaway_frame(
63            last_stream_id=1,
64            error_code=h2.errors.ErrorCodes.PROTOCOL_ERROR,
65        )
66        assert c.data_to_send() == expected_frame.serialize()
67
68    def test_insufficient_data(self, frame_factory):
69        """
70        Remote peers sending less data than content-length causes Protocol
71        Errors.
72        """
73        c = h2.connection.H2Connection(config=self.server_config)
74        c.initiate_connection()
75        c.receive_data(frame_factory.preamble())
76
77        headers = frame_factory.build_headers_frame(
78            headers=self.example_request_headers
79        )
80        first_data = frame_factory.build_data_frame(data=b'\x01'*13)
81        c.receive_data(headers.serialize() + first_data.serialize())
82        c.clear_outbound_data_buffer()
83
84        second_data = frame_factory.build_data_frame(
85            data=b'\x01',
86            flags=['END_STREAM'],
87        )
88        with pytest.raises(h2.exceptions.InvalidBodyLengthError) as exp:
89            c.receive_data(second_data.serialize())
90
91        assert exp.value.expected_length == 15
92        assert exp.value.actual_length == 14
93        assert str(exp.value) == (
94            "InvalidBodyLengthError: Expected 15 bytes, received 14"
95        )
96
97        expected_frame = frame_factory.build_goaway_frame(
98            last_stream_id=1,
99            error_code=h2.errors.ErrorCodes.PROTOCOL_ERROR,
100        )
101        assert c.data_to_send() == expected_frame.serialize()
102
103    def test_insufficient_data_empty_frame(self, frame_factory):
104        """
105        Remote peers sending less data than content-length where the last data
106        frame is empty causes Protocol Errors.
107        """
108        c = h2.connection.H2Connection(config=self.server_config)
109        c.initiate_connection()
110        c.receive_data(frame_factory.preamble())
111
112        headers = frame_factory.build_headers_frame(
113            headers=self.example_request_headers
114        )
115        first_data = frame_factory.build_data_frame(data=b'\x01'*14)
116        c.receive_data(headers.serialize() + first_data.serialize())
117        c.clear_outbound_data_buffer()
118
119        second_data = frame_factory.build_data_frame(
120            data=b'',
121            flags=['END_STREAM'],
122        )
123        with pytest.raises(h2.exceptions.InvalidBodyLengthError) as exp:
124            c.receive_data(second_data.serialize())
125
126        assert exp.value.expected_length == 15
127        assert exp.value.actual_length == 14
128        assert str(exp.value) == (
129            "InvalidBodyLengthError: Expected 15 bytes, received 14"
130        )
131
132        expected_frame = frame_factory.build_goaway_frame(
133            last_stream_id=1,
134            error_code=h2.errors.ErrorCodes.PROTOCOL_ERROR,
135        )
136        assert c.data_to_send() == expected_frame.serialize()
137