diff --git a/test/test_h2_upgrade.py b/test/test_h2_upgrade.py index d63d44f3..1fa851e9 100644 --- a/test/test_h2_upgrade.py +++ b/test/test_h2_upgrade.py @@ -9,6 +9,7 @@ """ import base64 +from h2.utilities import utf8_encode_headers import pytest import h2.config @@ -18,16 +19,23 @@ import h2.exceptions +EXAMPLE_REQUEST_HEADERS = [ + (':authority', 'example.com'), + (':path', '/'), + (':scheme', 'https'), + (':method', 'GET'), +] +EXAMPLE_REQUEST_HEADERS_BYTES = [ + (b':authority', b'example.com'), + (b':path', b'/'), + (b':scheme', b'https'), + (b':method', b'GET'), +] + class TestClientUpgrade(object): """ Tests of the client-side of the HTTP/2 upgrade dance. """ - example_request_headers = [ - (b':authority', b'example.com'), - (b':path', b'/'), - (b':scheme', b'https'), - (b':method', b'GET'), - ] example_response_headers = [ (b':status', b'200'), (b'server', b'fake-serv/0.1.0') @@ -97,7 +105,8 @@ def test_can_receive_response(self, frame_factory): assert not c.data_to_send() - def test_can_receive_pushed_stream(self, frame_factory): + @pytest.mark.parametrize("headers", [EXAMPLE_REQUEST_HEADERS, EXAMPLE_REQUEST_HEADERS_BYTES]) + def test_can_receive_pushed_stream(self, frame_factory, headers): """ After upgrading, we can safely receive a pushed stream. """ @@ -108,17 +117,18 @@ def test_can_receive_pushed_stream(self, frame_factory): f = frame_factory.build_push_promise_frame( stream_id=1, promised_stream_id=2, - headers=self.example_request_headers, + headers=headers ) events = c.receive_data(f.serialize()) assert len(events) == 1 assert isinstance(events[0], h2.events.PushedStreamReceived) - assert events[0].headers == self.example_request_headers + assert events[0].headers == utf8_encode_headers(headers) assert events[0].parent_stream_id == 1 assert events[0].pushed_stream_id == 2 - def test_cannot_send_headers_stream_1(self, frame_factory): + @pytest.mark.parametrize("headers", [EXAMPLE_REQUEST_HEADERS, EXAMPLE_REQUEST_HEADERS_BYTES]) + def test_cannot_send_headers_stream_1(self, frame_factory, headers): """ After upgrading, we cannot send headers on stream 1. """ @@ -127,7 +137,7 @@ def test_cannot_send_headers_stream_1(self, frame_factory): c.clear_outbound_data_buffer() with pytest.raises(h2.exceptions.ProtocolError): - c.send_headers(stream_id=1, headers=self.example_request_headers) + c.send_headers(stream_id=1, headers=headers) def test_cannot_send_data_stream_1(self, frame_factory): """ @@ -145,12 +155,6 @@ class TestServerUpgrade(object): """ Tests of the server-side of the HTTP/2 upgrade dance. """ - example_request_headers = [ - (b':authority', b'example.com'), - (b':path', b'/'), - (b':scheme', b'https'), - (b':method', b'GET'), - ] example_response_headers = [ (b':status', b'200'), (b'server', b'fake-serv/0.1.0') @@ -203,7 +207,8 @@ def test_can_send_response(self, frame_factory): expected_data = f1.serialize() + f2.serialize() assert c.data_to_send() == expected_data - def test_can_push_stream(self, frame_factory): + @pytest.mark.parametrize("headers", [EXAMPLE_REQUEST_HEADERS, EXAMPLE_REQUEST_HEADERS_BYTES]) + def test_can_push_stream(self, frame_factory, headers): """ After upgrading, we can safely push a stream. """ @@ -214,17 +219,18 @@ def test_can_push_stream(self, frame_factory): c.push_stream( stream_id=1, promised_stream_id=2, - request_headers=self.example_request_headers + request_headers=headers ) f = frame_factory.build_push_promise_frame( stream_id=1, promised_stream_id=2, - headers=self.example_request_headers, + headers=headers, ) assert c.data_to_send() == f.serialize() - def test_cannot_receive_headers_stream_1(self, frame_factory): + @pytest.mark.parametrize("headers", [EXAMPLE_REQUEST_HEADERS, EXAMPLE_REQUEST_HEADERS_BYTES]) + def test_cannot_receive_headers_stream_1(self, frame_factory, headers): """ After upgrading, we cannot receive headers on stream 1. """ @@ -235,7 +241,7 @@ def test_cannot_receive_headers_stream_1(self, frame_factory): f = frame_factory.build_headers_frame( stream_id=1, - headers=self.example_request_headers, + headers=headers, ) c.receive_data(f.serialize()) diff --git a/test/test_head_request.py b/test/test_head_request.py index ef730072..a804f90a 100644 --- a/test/test_head_request.py +++ b/test/test_head_request.py @@ -7,25 +7,32 @@ import pytest -class TestHeadRequest(object): - example_request_headers = [ - (b':authority', b'example.com'), - (b':path', b'/'), - (b':scheme', b'https'), - (b':method', b'HEAD'), - ] +EXAMPLE_REQUEST_HEADERS_BYTES = [ + (b':authority', b'example.com'), + (b':path', b'/'), + (b':scheme', b'https'), + (b':method', b'HEAD'), +] + +EXAMPLE_REQUEST_HEADERS = [ + (':authority', 'example.com'), + (':path', '/'), + (':scheme', 'https'), + (':method', 'HEAD'), +] +class TestHeadRequest(object): example_response_headers = [ (b':status', b'200'), (b'server', b'fake-serv/0.1.0'), (b'content_length', b'1'), ] - def test_non_zero_content_and_no_body(self, frame_factory): - + @pytest.mark.parametrize('headers', [EXAMPLE_REQUEST_HEADERS, EXAMPLE_REQUEST_HEADERS_BYTES]) + def test_non_zero_content_and_no_body(self, frame_factory, headers): c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(1, self.example_request_headers, end_stream=True) + c.send_headers(1, headers, end_stream=True) f = frame_factory.build_headers_frame( self.example_response_headers, @@ -40,10 +47,11 @@ def test_non_zero_content_and_no_body(self, frame_factory): assert event.stream_id == 1 assert event.headers == self.example_response_headers - def test_reject_non_zero_content_and_body(self, frame_factory): + @pytest.mark.parametrize('headers', [EXAMPLE_REQUEST_HEADERS, EXAMPLE_REQUEST_HEADERS_BYTES]) + def test_reject_non_zero_content_and_body(self, frame_factory, headers): c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(1, self.example_request_headers) + c.send_headers(1, headers) headers = frame_factory.build_headers_frame( self.example_response_headers diff --git a/test/test_header_indexing.py b/test/test_header_indexing.py index 23fd06f1..122db330 100644 --- a/test/test_header_indexing.py +++ b/test/test_header_indexing.py @@ -32,10 +32,10 @@ class TestHeaderIndexing(object): the appropriate hpack data structures. """ example_request_headers = [ - HeaderTuple(u':authority', u'example.com'), - HeaderTuple(u':path', u'/'), - HeaderTuple(u':scheme', u'https'), - HeaderTuple(u':method', u'GET'), + HeaderTuple(':authority', 'example.com'), + HeaderTuple(':path', '/'), + HeaderTuple(':scheme', 'https'), + HeaderTuple(':method', 'GET'), ] bytes_example_request_headers = [ HeaderTuple(b':authority', b'example.com'), @@ -45,11 +45,11 @@ class TestHeaderIndexing(object): ] extended_request_headers = [ - HeaderTuple(u':authority', u'example.com'), - HeaderTuple(u':path', u'/'), - HeaderTuple(u':scheme', u'https'), - HeaderTuple(u':method', u'GET'), - NeverIndexedHeaderTuple(u'authorization', u'realpassword'), + HeaderTuple(':authority', 'example.com'), + HeaderTuple(':path', '/'), + HeaderTuple(':scheme', 'https'), + HeaderTuple(':method', 'GET'), + NeverIndexedHeaderTuple('authorization', 'realpassword'), ] bytes_extended_request_headers = [ HeaderTuple(b':authority', b'example.com'), @@ -60,8 +60,8 @@ class TestHeaderIndexing(object): ] example_response_headers = [ - HeaderTuple(u':status', u'200'), - HeaderTuple(u'server', u'fake-serv/0.1.0') + HeaderTuple(':status', '200'), + HeaderTuple('server', 'fake-serv/0.1.0') ] bytes_example_response_headers = [ HeaderTuple(b':status', b'200'), @@ -69,9 +69,9 @@ class TestHeaderIndexing(object): ] extended_response_headers = [ - HeaderTuple(u':status', u'200'), - HeaderTuple(u'server', u'fake-serv/0.1.0'), - NeverIndexedHeaderTuple(u'secure', u'you-bet'), + HeaderTuple(':status', '200'), + HeaderTuple('server', 'fake-serv/0.1.0'), + NeverIndexedHeaderTuple('secure', 'you-bet'), ] bytes_extended_response_headers = [ HeaderTuple(b':status', b'200'), @@ -228,7 +228,7 @@ def test_header_tuples_are_decoded_info_response(self, # to avoid breaking the example headers. headers = headers[:] if encoding: - headers[0] = HeaderTuple(u':status', u'100') + headers[0] = HeaderTuple(':status', '100') else: headers[0] = HeaderTuple(b':status', b'100') @@ -334,10 +334,10 @@ class TestSecureHeaders(object): Certain headers should always be transformed to their never-indexed form. """ example_request_headers = [ - (u':authority', u'example.com'), - (u':path', u'/'), - (u':scheme', u'https'), - (u':method', u'GET'), + (':authority', 'example.com'), + (':path', '/'), + (':scheme', 'https'), + (':method', 'GET'), ] bytes_example_request_headers = [ (b':authority', b'example.com'), @@ -346,15 +346,15 @@ class TestSecureHeaders(object): (b':method', b'GET'), ] possible_auth_headers = [ - (u'authorization', u'test'), - (u'Authorization', u'test'), - (u'authorization', u'really long test'), - HeaderTuple(u'authorization', u'test'), - HeaderTuple(u'Authorization', u'test'), - HeaderTuple(u'authorization', u'really long test'), - NeverIndexedHeaderTuple(u'authorization', u'test'), - NeverIndexedHeaderTuple(u'Authorization', u'test'), - NeverIndexedHeaderTuple(u'authorization', u'really long test'), + ('authorization', 'test'), + ('Authorization', 'test'), + ('authorization', 'really long test'), + HeaderTuple('authorization', 'test'), + HeaderTuple('Authorization', 'test'), + HeaderTuple('authorization', 'really long test'), + NeverIndexedHeaderTuple('authorization', 'test'), + NeverIndexedHeaderTuple('Authorization', 'test'), + NeverIndexedHeaderTuple('authorization', 'really long test'), (b'authorization', b'test'), (b'Authorization', b'test'), (b'authorization', b'really long test'), @@ -364,15 +364,15 @@ class TestSecureHeaders(object): NeverIndexedHeaderTuple(b'authorization', b'test'), NeverIndexedHeaderTuple(b'Authorization', b'test'), NeverIndexedHeaderTuple(b'authorization', b'really long test'), - (u'proxy-authorization', u'test'), - (u'Proxy-Authorization', u'test'), - (u'proxy-authorization', u'really long test'), - HeaderTuple(u'proxy-authorization', u'test'), - HeaderTuple(u'Proxy-Authorization', u'test'), - HeaderTuple(u'proxy-authorization', u'really long test'), - NeverIndexedHeaderTuple(u'proxy-authorization', u'test'), - NeverIndexedHeaderTuple(u'Proxy-Authorization', u'test'), - NeverIndexedHeaderTuple(u'proxy-authorization', u'really long test'), + ('proxy-authorization', 'test'), + ('Proxy-Authorization', 'test'), + ('proxy-authorization', 'really long test'), + HeaderTuple('proxy-authorization', 'test'), + HeaderTuple('Proxy-Authorization', 'test'), + HeaderTuple('proxy-authorization', 'really long test'), + NeverIndexedHeaderTuple('proxy-authorization', 'test'), + NeverIndexedHeaderTuple('Proxy-Authorization', 'test'), + NeverIndexedHeaderTuple('proxy-authorization', 'really long test'), (b'proxy-authorization', b'test'), (b'Proxy-Authorization', b'test'), (b'proxy-authorization', b'really long test'), @@ -384,16 +384,16 @@ class TestSecureHeaders(object): NeverIndexedHeaderTuple(b'proxy-authorization', b'really long test'), ] secured_cookie_headers = [ - (u'cookie', u'short'), - (u'Cookie', u'short'), - (u'cookie', u'nineteen byte cooki'), - HeaderTuple(u'cookie', u'short'), - HeaderTuple(u'Cookie', u'short'), - HeaderTuple(u'cookie', u'nineteen byte cooki'), - NeverIndexedHeaderTuple(u'cookie', u'short'), - NeverIndexedHeaderTuple(u'Cookie', u'short'), - NeverIndexedHeaderTuple(u'cookie', u'nineteen byte cooki'), - NeverIndexedHeaderTuple(u'cookie', u'longer manually secured cookie'), + ('cookie', 'short'), + ('Cookie', 'short'), + ('cookie', 'nineteen byte cooki'), + HeaderTuple('cookie', 'short'), + HeaderTuple('Cookie', 'short'), + HeaderTuple('cookie', 'nineteen byte cooki'), + NeverIndexedHeaderTuple('cookie', 'short'), + NeverIndexedHeaderTuple('Cookie', 'short'), + NeverIndexedHeaderTuple('cookie', 'nineteen byte cooki'), + NeverIndexedHeaderTuple('cookie', 'longer manually secured cookie'), (b'cookie', b'short'), (b'Cookie', b'short'), (b'cookie', b'nineteen byte cooki'), @@ -406,12 +406,12 @@ class TestSecureHeaders(object): NeverIndexedHeaderTuple(b'cookie', b'longer manually secured cookie'), ] unsecured_cookie_headers = [ - (u'cookie', u'twenty byte cookie!!'), - (u'Cookie', u'twenty byte cookie!!'), - (u'cookie', u'substantially longer than 20 byte cookie'), - HeaderTuple(u'cookie', u'twenty byte cookie!!'), - HeaderTuple(u'cookie', u'twenty byte cookie!!'), - HeaderTuple(u'Cookie', u'twenty byte cookie!!'), + ('cookie', 'twenty byte cookie!!'), + ('Cookie', 'twenty byte cookie!!'), + ('cookie', 'substantially longer than 20 byte cookie'), + HeaderTuple('cookie', 'twenty byte cookie!!'), + HeaderTuple('cookie', 'twenty byte cookie!!'), + HeaderTuple('Cookie', 'twenty byte cookie!!'), (b'cookie', b'twenty byte cookie!!'), (b'Cookie', b'twenty byte cookie!!'), (b'cookie', b'substantially longer than 20 byte cookie'), diff --git a/test/test_informational_responses.py b/test/test_informational_responses.py index e18c44bc..64d3a6e7 100644 --- a/test/test_informational_responses.py +++ b/test/test_informational_responses.py @@ -216,15 +216,22 @@ class TestSendingInformationalResponses(object): Tests for sending informational responses. """ example_request_headers = [ + (':authority', 'example.com'), + (':path', '/'), + (':scheme', 'https'), + (':method', 'GET'), + ('expect', '100-continue'), + ] + bytes_example_request_headers = [ (b':authority', b'example.com'), (b':path', b'/'), (b':scheme', b'https'), (b':method', b'GET'), (b'expect', b'100-continue'), ] - unicode_informational_headers = [ - (u':status', u'100'), - (u'server', u'fake-serv/0.1.0') + informational_headers = [ + (':status', '100'), + ('server', 'fake-serv/0.1.0') ] bytes_informational_headers = [ (b':status', b'100'), @@ -240,12 +247,16 @@ class TestSendingInformationalResponses(object): server_config = h2.config.H2Configuration(client_side=False) @pytest.mark.parametrize( - 'hdrs', (unicode_informational_headers, bytes_informational_headers), + 'hdrs', (informational_headers, bytes_informational_headers), + ) + @pytest.mark.parametrize( + 'request_headers', (example_request_headers, bytes_example_request_headers), ) @pytest.mark.parametrize('end_stream', (True, False)) def test_single_informational_response(self, frame_factory, hdrs, + request_headers, end_stream): """ When sending a informational response, the appropriate frames are @@ -256,7 +267,7 @@ def test_single_informational_response(self, c.receive_data(frame_factory.preamble()) flags = ['END_STREAM'] if end_stream else [] f = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, stream_id=1, flags=flags, ) @@ -276,12 +287,16 @@ def test_single_informational_response(self, assert c.data_to_send() == f.serialize() @pytest.mark.parametrize( - 'hdrs', (unicode_informational_headers, bytes_informational_headers), + 'hdrs', (informational_headers, bytes_informational_headers), + ) + @pytest.mark.parametrize( + 'request_headers', (example_request_headers, bytes_example_request_headers), ) @pytest.mark.parametrize('end_stream', (True, False)) def test_sending_multiple_header_blocks(self, frame_factory, hdrs, + request_headers, end_stream): """ At least three header blocks can be sent: informational, headers, @@ -292,7 +307,7 @@ def test_sending_multiple_header_blocks(self, c.receive_data(frame_factory.preamble()) flags = ['END_STREAM'] if end_stream else [] f = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, stream_id=1, flags=flags, ) @@ -335,12 +350,16 @@ def test_sending_multiple_header_blocks(self, ) @pytest.mark.parametrize( - 'hdrs', (unicode_informational_headers, bytes_informational_headers), + 'hdrs', (informational_headers, bytes_informational_headers), + ) + @pytest.mark.parametrize( + 'request_headers', (example_request_headers, bytes_example_request_headers), ) @pytest.mark.parametrize('end_stream', (True, False)) def test_sending_multiple_informational_responses(self, frame_factory, hdrs, + request_headers, end_stream): """ More than one informational response is allowed. @@ -350,7 +369,7 @@ def test_sending_multiple_informational_responses(self, c.receive_data(frame_factory.preamble()) flags = ['END_STREAM'] if end_stream else [] f = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, stream_id=1, flags=flags, ) @@ -365,7 +384,7 @@ def test_sending_multiple_informational_responses(self, ) c.send_headers( stream_id=1, - headers=[(':status', '101')] + headers=[(b':status', b'101')] ) # Check we sent them both. @@ -380,12 +399,16 @@ def test_sending_multiple_informational_responses(self, assert c.data_to_send() == f1.serialize() + f2.serialize() @pytest.mark.parametrize( - 'hdrs', (unicode_informational_headers, bytes_informational_headers), + 'hdrs', (informational_headers, bytes_informational_headers), + ) + @pytest.mark.parametrize( + 'request_headers', (example_request_headers, bytes_example_request_headers), ) @pytest.mark.parametrize('end_stream', (True, False)) def test_send_provisional_response_with_end_stream(self, frame_factory, hdrs, + request_headers, end_stream): """ Sending provisional responses with END_STREAM set causes @@ -396,7 +419,7 @@ def test_send_provisional_response_with_end_stream(self, c.receive_data(frame_factory.preamble()) flags = ['END_STREAM'] if end_stream else [] f = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, stream_id=1, flags=flags, ) @@ -410,12 +433,16 @@ def test_send_provisional_response_with_end_stream(self, ) @pytest.mark.parametrize( - 'hdrs', (unicode_informational_headers, bytes_informational_headers), + 'hdrs', (informational_headers, bytes_informational_headers), + ) + @pytest.mark.parametrize( + 'request_headers', (example_request_headers, bytes_example_request_headers), ) @pytest.mark.parametrize('end_stream', (True, False)) def test_reject_sending_out_of_order_headers(self, frame_factory, hdrs, + request_headers, end_stream): """ When sending an informational response after the actual response @@ -426,7 +453,7 @@ def test_reject_sending_out_of_order_headers(self, c.receive_data(frame_factory.preamble()) flags = ['END_STREAM'] if end_stream else [] f = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, stream_id=1, flags=flags, ) diff --git a/test/test_interacting_stacks.py b/test/test_interacting_stacks.py index 32c96732..70f2235b 100644 --- a/test/test_interacting_stacks.py +++ b/test/test_interacting_stacks.py @@ -20,6 +20,8 @@ """ from . import coroutine_tests +import pytest + import h2.config import h2.connection import h2.events @@ -32,23 +34,33 @@ class TestCommunication(coroutine_tests.CoroutineTestCase): """ server_config = h2.config.H2Configuration(client_side=False) - def test_basic_request_response(self): + request_headers = [ + (':method', 'GET'), + (':path', '/'), + (':authority', 'example.com'), + (':scheme', 'https'), + ('user-agent', 'test-client/0.1.0'), + ] + + request_headers_bytes = [ + (b':method', b'GET'), + (b':path', b'/'), + (b':authority', b'example.com'), + (b':scheme', b'https'), + (b'user-agent', b'test-client/0.1.0'), + ] + + response_headers = [ + (b':status', b'204'), + (b'server', b'test-server/0.1.0'), + (b'content-length', b'0'), + ] + + @pytest.mark.parametrize('request_headers', [request_headers, request_headers_bytes]) + def test_basic_request_response(self, request_headers): """ A request issued by hyper-h2 can be responded to by hyper-h2. """ - request_headers = [ - (b':method', b'GET'), - (b':path', b'/'), - (b':authority', b'example.com'), - (b':scheme', b'https'), - (b'user-agent', b'test-client/0.1.0'), - ] - response_headers = [ - (b':status', b'204'), - (b'server', b'test-server/0.1.0'), - (b'content-length', b'0'), - ] - def client(): c = h2.connection.H2Connection() @@ -78,7 +90,7 @@ def client(): assert len(events) == 2 assert isinstance(events[0], h2.events.ResponseReceived) assert events[0].stream_id == 1 - assert events[0].headers == response_headers + assert events[0].headers == self.response_headers assert isinstance(events[1], h2.events.StreamEnded) assert events[1].stream_id == 1 @@ -108,12 +120,12 @@ def server(): assert isinstance(events[0], h2.events.SettingsAcknowledged) assert isinstance(events[1], h2.events.RequestReceived) assert events[1].stream_id == 1 - assert events[1].headers == request_headers + assert events[1].headers == self.request_headers_bytes assert isinstance(events[2], h2.events.StreamEnded) assert events[2].stream_id == 1 # Send our response. - events = c.send_headers(1, response_headers, end_stream=True) + events = c.send_headers(1, self.response_headers, end_stream=True) assert not events yield c.data_to_send() diff --git a/test/test_invalid_content_lengths.py b/test/test_invalid_content_lengths.py index fe682fcc..b33e9c6a 100644 --- a/test/test_invalid_content_lengths.py +++ b/test/test_invalid_content_lengths.py @@ -27,13 +27,21 @@ class TestInvalidContentLengths(object): (':method', 'POST'), ('content-length', '15'), ] + example_request_headers_bytes = [ + (b':authority', b'example.com'), + (b':path', b'/'), + (b':scheme', b'https'), + (b':method', b'POST'), + (b'content-length', b'15'), + ] example_response_headers = [ (':status', '200'), ('server', 'fake-serv/0.1.0') ] server_config = h2.config.H2Configuration(client_side=False) - def test_too_much_data(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_too_much_data(self, frame_factory, request_headers): """ Remote peers sending data in excess of content-length causes Protocol Errors. @@ -43,7 +51,7 @@ def test_too_much_data(self, frame_factory): c.receive_data(frame_factory.preamble()) headers = frame_factory.build_headers_frame( - headers=self.example_request_headers + headers=request_headers ) first_data = frame_factory.build_data_frame(data=b'\x01'*15) c.receive_data(headers.serialize() + first_data.serialize()) @@ -65,7 +73,8 @@ def test_too_much_data(self, frame_factory): ) assert c.data_to_send() == expected_frame.serialize() - def test_insufficient_data(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_insufficient_data(self, frame_factory, request_headers): """ Remote peers sending less data than content-length causes Protocol Errors. @@ -75,7 +84,7 @@ def test_insufficient_data(self, frame_factory): c.receive_data(frame_factory.preamble()) headers = frame_factory.build_headers_frame( - headers=self.example_request_headers + headers=request_headers ) first_data = frame_factory.build_data_frame(data=b'\x01'*13) c.receive_data(headers.serialize() + first_data.serialize()) @@ -100,7 +109,8 @@ def test_insufficient_data(self, frame_factory): ) assert c.data_to_send() == expected_frame.serialize() - def test_insufficient_data_empty_frame(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_insufficient_data_empty_frame(self, frame_factory, request_headers): """ Remote peers sending less data than content-length where the last data frame is empty causes Protocol Errors. @@ -110,7 +120,7 @@ def test_insufficient_data_empty_frame(self, frame_factory): c.receive_data(frame_factory.preamble()) headers = frame_factory.build_headers_frame( - headers=self.example_request_headers + headers=request_headers ) first_data = frame_factory.build_data_frame(data=b'\x01'*14) c.receive_data(headers.serialize() + first_data.serialize()) diff --git a/test/test_invalid_frame_sequences.py b/test/test_invalid_frame_sequences.py index 05832cbb..81bf07c4 100644 --- a/test/test_invalid_frame_sequences.py +++ b/test/test_invalid_frame_sequences.py @@ -26,6 +26,12 @@ class TestInvalidFrameSequences(object): (':scheme', 'https'), (':method', 'GET'), ] + example_request_headers_bytes = [ + (b':authority', b'example.com'), + (b':path', b'/'), + (b':scheme', b'https'), + (b':method', b'GET'), + ] example_response_headers = [ (':status', '200'), ('server', 'fake-serv/0.1.0') @@ -33,13 +39,14 @@ class TestInvalidFrameSequences(object): server_config = h2.config.H2Configuration(client_side=False) client_config = h2.config.H2Configuration(client_side=True) - def test_cannot_send_on_closed_stream(self): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_cannot_send_on_closed_stream(self, request_headers): """ When we've closed a stream locally, we cannot send further data. """ c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(1, self.example_request_headers, end_stream=True) + c.send_headers(1, request_headers, end_stream=True) with pytest.raises(h2.exceptions.ProtocolError): c.send_data(1, b'some data') @@ -57,7 +64,8 @@ def test_missing_preamble_errors(self): with pytest.raises(h2.exceptions.ProtocolError): c.receive_data(encoded_headers_frame) - def test_server_connections_reject_even_streams(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_server_connections_reject_even_streams(self, frame_factory, request_headers): """ Servers do not allow clients to initiate even-numbered streams. """ @@ -66,30 +74,32 @@ def test_server_connections_reject_even_streams(self, frame_factory): c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - self.example_request_headers, stream_id=2 + request_headers, stream_id=2 ) with pytest.raises(h2.exceptions.ProtocolError): c.receive_data(f.serialize()) - def test_clients_reject_odd_stream_pushes(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_clients_reject_odd_stream_pushes(self, frame_factory, request_headers): """ Clients do not allow servers to push odd numbered streams. """ c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(1, self.example_request_headers, end_stream=True) + c.send_headers(1, request_headers, end_stream=True) f = frame_factory.build_push_promise_frame( stream_id=1, - headers=self.example_request_headers, + headers=request_headers, promised_stream_id=3 ) with pytest.raises(h2.exceptions.ProtocolError): c.receive_data(f.serialize()) - def test_can_handle_frames_with_invalid_padding(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_can_handle_frames_with_invalid_padding(self, frame_factory, request_headers): """ Frames with invalid padding cause connection teardown. """ @@ -97,7 +107,7 @@ def test_can_handle_frames_with_invalid_padding(self, frame_factory): c.initiate_connection() c.receive_data(frame_factory.preamble()) - f = frame_factory.build_headers_frame(self.example_request_headers) + f = frame_factory.build_headers_frame(request_headers) c.receive_data(f.serialize()) c.clear_outbound_data_buffer() @@ -134,7 +144,8 @@ def test_receiving_frames_with_insufficent_size(self, frame_factory): ) assert c.data_to_send() == expected_frame.serialize() - def test_reject_data_on_closed_streams(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_reject_data_on_closed_streams(self, frame_factory, request_headers): """ When a stream is not open to the remote peer, we reject receiving data frames from them. @@ -144,7 +155,7 @@ def test_reject_data_on_closed_streams(self, frame_factory): c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - self.example_request_headers, + request_headers, flags=['END_STREAM'] ) c.receive_data(f.serialize()) @@ -161,7 +172,8 @@ def test_reject_data_on_closed_streams(self, frame_factory): ).serialize() assert c.data_to_send() == expected - def test_unexpected_continuation_on_closed_stream(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_unexpected_continuation_on_closed_stream(self, frame_factory, request_headers): """ CONTINUATION frames received on closed streams cause connection errors of type PROTOCOL_ERROR. @@ -171,7 +183,7 @@ def test_unexpected_continuation_on_closed_stream(self, frame_factory): c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - self.example_request_headers, + request_headers, flags=['END_STREAM'] ) c.receive_data(f.serialize()) @@ -190,7 +202,8 @@ def test_unexpected_continuation_on_closed_stream(self, frame_factory): ) assert c.data_to_send() == expected_frame.serialize() - def test_prevent_continuation_dos(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_prevent_continuation_dos(self, frame_factory, request_headers): """ Receiving too many CONTINUATION frames in one block causes a protocol error. @@ -200,7 +213,7 @@ def test_prevent_continuation_dos(self, frame_factory): c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - self.example_request_headers, + request_headers, ) f.flags = {'END_STREAM'} c.receive_data(f.serialize()) @@ -255,7 +268,8 @@ def test_reject_invalid_settings_values(self, frame_factory, settings): h2.errors.ErrorCodes.PROTOCOL_ERROR ) - def test_invalid_frame_headers_are_protocol_errors(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_invalid_frame_headers_are_protocol_errors(self, frame_factory, request_headers): """ When invalid frame headers are received they cause ProtocolErrors to be raised. @@ -265,7 +279,7 @@ def test_invalid_frame_headers_are_protocol_errors(self, frame_factory): c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - headers=self.example_request_headers + headers=request_headers ) # Do some annoying bit twiddling here: the stream ID is currently set @@ -280,7 +294,8 @@ def test_invalid_frame_headers_are_protocol_errors(self, frame_factory): assert "Received frame with invalid header" in str(e.value) - def test_data_before_headers(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_data_before_headers(self, frame_factory, request_headers): """ When data frames are received before headers they cause ProtocolErrors to be raised. @@ -288,7 +303,7 @@ def test_data_before_headers(self, frame_factory): c = h2.connection.H2Connection(config=self.client_config) c.initiate_connection() # transition stream into OPEN - c.send_headers(1, self.example_request_headers) + c.send_headers(1, request_headers) f = frame_factory.build_data_frame(b"hello") @@ -297,7 +312,8 @@ def test_data_before_headers(self, frame_factory): assert "cannot receive data before headers" in str(e.value) - def test_get_stream_reset_event_on_auto_reset(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_get_stream_reset_event_on_auto_reset(self, frame_factory, request_headers): """ When hyper-h2 resets a stream automatically, a StreamReset event fires. """ @@ -306,7 +322,7 @@ def test_get_stream_reset_event_on_auto_reset(self, frame_factory): c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - self.example_request_headers, + request_headers, flags=['END_STREAM'] ) c.receive_data(f.serialize()) @@ -330,7 +346,8 @@ def test_get_stream_reset_event_on_auto_reset(self, frame_factory): assert event.error_code == h2.errors.ErrorCodes.STREAM_CLOSED assert not event.remote_reset - def test_one_one_stream_reset(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_one_one_stream_reset(self, frame_factory, request_headers): """ When hyper-h2 resets a stream automatically, a StreamReset event fires, but only for the first reset: the others are silent. @@ -340,7 +357,7 @@ def test_one_one_stream_reset(self, frame_factory): c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - self.example_request_headers, + request_headers, flags=['END_STREAM'] ) c.receive_data(f.serialize()) @@ -365,8 +382,9 @@ def test_one_one_stream_reset(self, frame_factory): assert event.error_code == h2.errors.ErrorCodes.STREAM_CLOSED assert not event.remote_reset + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) @pytest.mark.parametrize('value', ['', 'twelve']) - def test_error_on_invalid_content_length(self, frame_factory, value): + def test_error_on_invalid_content_length(self, frame_factory, value, request_headers): """ When an invalid content-length is received, a ProtocolError is thrown. """ @@ -377,7 +395,7 @@ def test_error_on_invalid_content_length(self, frame_factory, value): f = frame_factory.build_headers_frame( stream_id=1, - headers=self.example_request_headers + [('content-length', value)] + headers=request_headers + [('content-length', value)] ) with pytest.raises(h2.exceptions.ProtocolError): c.receive_data(f.serialize()) @@ -388,7 +406,8 @@ def test_error_on_invalid_content_length(self, frame_factory, value): ) assert c.data_to_send() == expected_frame.serialize() - def test_invalid_header_data_protocol_error(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_invalid_header_data_protocol_error(self, frame_factory, request_headers): """ If an invalid header block is received, we raise a ProtocolError. """ @@ -399,7 +418,7 @@ def test_invalid_header_data_protocol_error(self, frame_factory): f = frame_factory.build_headers_frame( stream_id=1, - headers=self.example_request_headers + headers=request_headers ) f.data = b'\x00\x00\x00\x00' @@ -412,20 +431,21 @@ def test_invalid_header_data_protocol_error(self, frame_factory): ) assert c.data_to_send() == expected_frame.serialize() - def test_invalid_push_promise_data_protocol_error(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_invalid_push_promise_data_protocol_error(self, frame_factory, request_headers): """ If an invalid header block is received on a PUSH_PROMISE, we raise a ProtocolError. """ c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(stream_id=1, headers=self.example_request_headers) + c.send_headers(stream_id=1, headers=request_headers) c.clear_outbound_data_buffer() f = frame_factory.build_push_promise_frame( stream_id=1, promised_stream_id=2, - headers=self.example_request_headers + headers=request_headers ) f.data = b'\x00\x00\x00\x00' @@ -438,7 +458,8 @@ def test_invalid_push_promise_data_protocol_error(self, frame_factory): ) assert c.data_to_send() == expected_frame.serialize() - def test_cannot_receive_push_on_pushed_stream(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_cannot_receive_push_on_pushed_stream(self, frame_factory, request_headers): """ If a PUSH_PROMISE frame is received with the parent stream ID being a pushed stream, this is rejected with a PROTOCOL_ERROR. @@ -447,14 +468,14 @@ def test_cannot_receive_push_on_pushed_stream(self, frame_factory): c.initiate_connection() c.send_headers( stream_id=1, - headers=self.example_request_headers, + headers=request_headers, end_stream=True ) f1 = frame_factory.build_push_promise_frame( stream_id=1, promised_stream_id=2, - headers=self.example_request_headers, + headers=request_headers, ) f2 = frame_factory.build_headers_frame( stream_id=2, @@ -466,7 +487,7 @@ def test_cannot_receive_push_on_pushed_stream(self, frame_factory): f = frame_factory.build_push_promise_frame( stream_id=2, promised_stream_id=4, - headers=self.example_request_headers, + headers=request_headers, ) with pytest.raises(h2.exceptions.ProtocolError): @@ -478,7 +499,8 @@ def test_cannot_receive_push_on_pushed_stream(self, frame_factory): ) assert c.data_to_send() == expected_frame.serialize() - def test_cannot_send_push_on_pushed_stream(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_cannot_send_push_on_pushed_stream(self, frame_factory, request_headers): """ If a user tries to send a PUSH_PROMISE frame with the parent stream ID being a pushed stream, this is rejected with a PROTOCOL_ERROR. @@ -487,14 +509,14 @@ def test_cannot_send_push_on_pushed_stream(self, frame_factory): c.initiate_connection() c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - stream_id=1, headers=self.example_request_headers + stream_id=1, headers=request_headers ) c.receive_data(f.serialize()) c.push_stream( stream_id=1, promised_stream_id=2, - request_headers=self.example_request_headers + request_headers=request_headers ) c.send_headers(stream_id=2, headers=self.example_response_headers) @@ -502,5 +524,5 @@ def test_cannot_send_push_on_pushed_stream(self, frame_factory): c.push_stream( stream_id=2, promised_stream_id=4, - request_headers=self.example_request_headers + request_headers=request_headers ) diff --git a/test/test_invalid_headers.py b/test/test_invalid_headers.py index 2690d314..996e48aa 100644 --- a/test/test_invalid_headers.py +++ b/test/test_invalid_headers.py @@ -37,7 +37,7 @@ class TestInvalidFrameSequences(object): (':method', 'GET'), ('user-agent', 'someua/0.0.1'), ] - invalid_header_blocks = [ + base_invalid_header_blocks = [ base_request_headers + [('Uppercase', 'name')], base_request_headers + [(':late', 'pseudo-header')], [(':path', 'duplicate-pseudo-header')] + base_request_headers, @@ -56,6 +56,9 @@ class TestInvalidFrameSequences(object): if header[0] != ':authority'], [(':protocol', 'websocket')] + base_request_headers, ] + invalid_header_blocks = base_invalid_header_blocks + [ + h2.utilities.utf8_encode_headers(headers) for headers in base_invalid_header_blocks + ] server_config = h2.config.H2Configuration( client_side=False, header_encoding='utf-8' ) @@ -117,7 +120,6 @@ def test_push_promise_skipping_validation(self, frame_factory, headers): config = h2.config.H2Configuration( client_side=True, validate_inbound_headers=False, - header_encoding='utf-8' ) c = h2.connection.H2Connection(config=config) @@ -137,7 +139,7 @@ def test_push_promise_skipping_validation(self, frame_factory, headers): events = c.receive_data(data) assert len(events) == 1 pp_event = events[0] - assert pp_event.headers == headers + assert pp_event.headers == h2.utilities.utf8_encode_headers(headers) @pytest.mark.parametrize('headers', invalid_header_blocks) def test_headers_event_skipping_validation(self, frame_factory, headers): @@ -148,7 +150,6 @@ def test_headers_event_skipping_validation(self, frame_factory, headers): config = h2.config.H2Configuration( client_side=False, validate_inbound_headers=False, - header_encoding='utf-8' ) c = h2.connection.H2Connection(config=config) @@ -160,7 +161,7 @@ def test_headers_event_skipping_validation(self, frame_factory, headers): events = c.receive_data(data) assert len(events) == 1 request_event = events[0] - assert request_event.headers == headers + assert request_event.headers == h2.utilities.utf8_encode_headers(headers) def test_te_trailers_is_valid(self, frame_factory): """ diff --git a/test/test_priority.py b/test/test_priority.py index cbc73322..086cf68f 100644 --- a/test/test_priority.py +++ b/test/test_priority.py @@ -25,6 +25,12 @@ class TestPriority(object): (':scheme', 'https'), (':method', 'GET'), ] + example_request_headers_bytes = [ + (b':authority', b'example.com'), + (b':path', b'/'), + (b':scheme', b'https'), + (b':method', b'GET'), + ] example_response_headers = [ (':status', '200'), ('server', 'pytest-h2'), @@ -56,7 +62,8 @@ def test_receiving_priority_emits_priority_update(self, frame_factory): assert event.weight == 256 assert event.exclusive is False - def test_headers_with_priority_info(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_headers_with_priority_info(self, frame_factory, request_headers): """ Receiving a HEADERS frame with priority information on it emits a PriorityUpdated event. @@ -67,7 +74,7 @@ def test_headers_with_priority_info(self, frame_factory): c.clear_outbound_data_buffer() f = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, stream_id=3, flags=['PRIORITY'], stream_weight=15, @@ -86,7 +93,8 @@ def test_headers_with_priority_info(self, frame_factory): assert event.weight == 16 assert event.exclusive is True - def test_streams_may_not_depend_on_themselves(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_streams_may_not_depend_on_themselves(self, frame_factory, request_headers): """ A stream adjusted to depend on itself causes a Protocol Error. """ @@ -96,7 +104,7 @@ def test_streams_may_not_depend_on_themselves(self, frame_factory): c.clear_outbound_data_buffer() f = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, stream_id=3, flags=['PRIORITY'], stream_weight=15, @@ -120,6 +128,7 @@ def test_streams_may_not_depend_on_themselves(self, frame_factory): ) assert c.data_to_send() == expected_frame.serialize() + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) @pytest.mark.parametrize( 'depends_on,weight,exclusive', [ @@ -129,15 +138,15 @@ def test_streams_may_not_depend_on_themselves(self, frame_factory): ] ) def test_can_prioritize_stream(self, depends_on, weight, exclusive, - frame_factory): + frame_factory, request_headers): """ 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.send_headers(headers=request_headers, stream_id=1) + c.send_headers(headers=request_headers, stream_id=3) c.clear_outbound_data_buffer() c.prioritize( @@ -155,6 +164,7 @@ def test_can_prioritize_stream(self, depends_on, weight, exclusive, ) assert c.data_to_send() == f.serialize() + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) @pytest.mark.parametrize( 'depends_on,weight,exclusive', [ @@ -164,7 +174,7 @@ def test_can_prioritize_stream(self, depends_on, weight, exclusive, ] ) def test_emit_headers_with_priority_info(self, depends_on, weight, - exclusive, frame_factory): + exclusive, frame_factory, request_headers): """ It is possible to send a headers frame with priority information on it. @@ -174,7 +184,7 @@ def test_emit_headers_with_priority_info(self, depends_on, weight, c.clear_outbound_data_buffer() c.send_headers( - headers=self.example_request_headers, + headers=request_headers, stream_id=3, priority_weight=weight, priority_depends_on=depends_on, @@ -182,7 +192,7 @@ def test_emit_headers_with_priority_info(self, depends_on, weight, ) f = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, stream_id=3, flags=['PRIORITY'], stream_weight=weight - 1, @@ -191,7 +201,8 @@ def test_emit_headers_with_priority_info(self, depends_on, weight, ) assert c.data_to_send() == f.serialize() - def test_may_not_prioritize_stream_to_depend_on_self(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_may_not_prioritize_stream_to_depend_on_self(self, frame_factory, request_headers): """ A stream adjusted to depend on itself causes a Protocol Error. """ @@ -199,7 +210,7 @@ def test_may_not_prioritize_stream_to_depend_on_self(self, frame_factory): c.initiate_connection() c.receive_data(frame_factory.preamble()) c.send_headers( - headers=self.example_request_headers, + headers=request_headers, stream_id=3, priority_weight=255, priority_depends_on=0, @@ -215,7 +226,8 @@ def test_may_not_prioritize_stream_to_depend_on_self(self, frame_factory): assert not c.data_to_send() - def test_may_not_initially_set_stream_depend_on_self(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_may_not_initially_set_stream_depend_on_self(self, frame_factory, request_headers): """ A stream that starts by depending on itself causes a Protocol Error. """ @@ -226,7 +238,7 @@ def test_may_not_initially_set_stream_depend_on_self(self, frame_factory): with pytest.raises(h2.exceptions.ProtocolError): c.send_headers( - headers=self.example_request_headers, + headers=request_headers, stream_id=3, priority_depends_on=3, ) @@ -247,8 +259,9 @@ def test_prioritize_requires_valid_weight(self, weight): assert not c.data_to_send() + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) @pytest.mark.parametrize('weight', [0, -15, 257]) - def test_send_headers_requires_valid_weight(self, weight): + def test_send_headers_requires_valid_weight(self, weight, request_headers): """ A call to send_headers with an invalid weight causes a ProtocolError. """ @@ -259,7 +272,7 @@ def test_send_headers_requires_valid_weight(self, weight): with pytest.raises(h2.exceptions.ProtocolError): c.send_headers( stream_id=1, - headers=self.example_request_headers, + headers=request_headers, priority_weight=weight ) @@ -284,6 +297,7 @@ def test_prioritize_defaults(self, frame_factory): ) assert c.data_to_send() == f.serialize() + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) @pytest.mark.parametrize( 'priority_kwargs', [ @@ -292,7 +306,7 @@ def test_prioritize_defaults(self, frame_factory): {'priority_exclusive': False}, ] ) - def test_send_headers_defaults(self, priority_kwargs, frame_factory): + def test_send_headers_defaults(self, priority_kwargs, frame_factory, request_headers): """ When send_headers() is called with only one explicit argument, it emits default values for everything else. @@ -303,12 +317,12 @@ def test_send_headers_defaults(self, priority_kwargs, frame_factory): c.send_headers( stream_id=1, - headers=self.example_request_headers, + headers=request_headers, **priority_kwargs ) f = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, stream_id=1, flags=['PRIORITY'], stream_weight=15, @@ -317,7 +331,8 @@ def test_send_headers_defaults(self, priority_kwargs, frame_factory): ) assert c.data_to_send() == f.serialize() - def test_servers_cannot_prioritize(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_servers_cannot_prioritize(self, frame_factory, request_headers): """ Server stacks are not allowed to call ``prioritize()``. """ @@ -328,14 +343,15 @@ def test_servers_cannot_prioritize(self, frame_factory): f = frame_factory.build_headers_frame( stream_id=1, - headers=self.example_request_headers, + headers=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): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_servers_cannot_prioritize_with_headers(self, frame_factory, request_headers): """ Server stacks are not allowed to prioritize on headers either. """ @@ -346,7 +362,7 @@ def test_servers_cannot_prioritize_with_headers(self, frame_factory): f = frame_factory.build_headers_frame( stream_id=1, - headers=self.example_request_headers, + headers=request_headers, ) c.receive_data(f.serialize()) diff --git a/test/test_related_events.py b/test/test_related_events.py index eb6b8789..19334408 100644 --- a/test/test_related_events.py +++ b/test/test_related_events.py @@ -6,6 +6,8 @@ Specific tests to validate the "related events" logic used by certain events inside hyper-h2. """ +import pytest + import h2.config import h2.connection import h2.events @@ -22,6 +24,13 @@ class TestRelatedEvents(object): (':method', 'GET'), ] + example_request_headers_bytes = [ + (b':authority', b'example.com'), + (b':path', b'/'), + (b':scheme', b'https'), + (b':method', b'GET'), + ] + example_response_headers = [ (':status', '200'), ('server', 'fake-serv/0.1.0') @@ -38,7 +47,8 @@ class TestRelatedEvents(object): server_config = h2.config.H2Configuration(client_side=False) - def test_request_received_related_all(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_request_received_related_all(self, frame_factory, request_headers): """ RequestReceived has two possible related events: PriorityUpdated and StreamEnded, all fired when a single HEADERS frame is received. @@ -48,7 +58,7 @@ def test_request_received_related_all(self, frame_factory): c.receive_data(frame_factory.preamble()) input_frame = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, flags=['END_STREAM', 'PRIORITY'], stream_weight=15, depends_on=0, @@ -67,7 +77,8 @@ def test_request_received_related_all(self, frame_factory): base_event.priority_updated, h2.events.PriorityUpdated ) - def test_request_received_related_priority(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_request_received_related_priority(self, frame_factory, request_headers): """ RequestReceived can be related to PriorityUpdated. """ @@ -76,7 +87,7 @@ def test_request_received_related_priority(self, frame_factory): c.receive_data(frame_factory.preamble()) input_frame = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, flags=['PRIORITY'], stream_weight=15, depends_on=0, @@ -94,7 +105,8 @@ def test_request_received_related_priority(self, frame_factory): base_event.priority_updated, h2.events.PriorityUpdated ) - def test_request_received_related_stream_ended(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_request_received_related_stream_ended(self, frame_factory, request_headers): """ RequestReceived can be related to StreamEnded. """ @@ -103,7 +115,7 @@ def test_request_received_related_stream_ended(self, frame_factory): c.receive_data(frame_factory.preamble()) input_frame = frame_factory.build_headers_frame( - headers=self.example_request_headers, + headers=request_headers, flags=['END_STREAM'], ) events = c.receive_data(input_frame.serialize()) @@ -116,13 +128,14 @@ def test_request_received_related_stream_ended(self, frame_factory): 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): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_response_received_related_nothing(self, frame_factory, request_headers): """ 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) + c.send_headers(stream_id=1, headers=request_headers) input_frame = frame_factory.build_headers_frame( headers=self.example_response_headers, @@ -135,14 +148,15 @@ def test_response_received_related_nothing(self, frame_factory): assert base_event.stream_ended is None assert base_event.priority_updated is None - def test_response_received_related_all(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_response_received_related_all(self, frame_factory, request_headers): """ 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) + c.send_headers(stream_id=1, headers=request_headers) input_frame = frame_factory.build_headers_frame( headers=self.example_response_headers, @@ -164,13 +178,14 @@ def test_response_received_related_all(self, frame_factory): base_event.priority_updated, h2.events.PriorityUpdated ) - def test_response_received_related_priority(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_response_received_related_priority(self, frame_factory, request_headers): """ ResponseReceived can be related to PriorityUpdated. """ c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(stream_id=1, headers=self.example_request_headers) + c.send_headers(stream_id=1, headers=request_headers) input_frame = frame_factory.build_headers_frame( headers=self.example_response_headers, @@ -191,13 +206,14 @@ def test_response_received_related_priority(self, frame_factory): base_event.priority_updated, h2.events.PriorityUpdated ) - def test_response_received_related_stream_ended(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_response_received_related_stream_ended(self, frame_factory, request_headers): """ ResponseReceived can be related to StreamEnded. """ c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(stream_id=1, headers=self.example_request_headers) + c.send_headers(stream_id=1, headers=request_headers) input_frame = frame_factory.build_headers_frame( headers=self.example_response_headers, @@ -213,14 +229,15 @@ def test_response_received_related_stream_ended(self, frame_factory): 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): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_trailers_received_related_all(self, frame_factory, request_headers): """ 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) + c.send_headers(stream_id=1, headers=request_headers) f = frame_factory.build_headers_frame( headers=self.example_response_headers, @@ -247,13 +264,14 @@ def test_trailers_received_related_all(self, frame_factory): base_event.priority_updated, h2.events.PriorityUpdated ) - def test_trailers_received_related_stream_ended(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_trailers_received_related_stream_ended(self, frame_factory, request_headers): """ 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) + c.send_headers(stream_id=1, headers=request_headers) f = frame_factory.build_headers_frame( headers=self.example_response_headers, @@ -274,14 +292,15 @@ def test_trailers_received_related_stream_ended(self, frame_factory): 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): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_informational_response_related_nothing(self, frame_factory, request_headers): """ 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) + c.send_headers(stream_id=1, headers=request_headers) input_frame = frame_factory.build_headers_frame( headers=self.informational_response_headers, @@ -293,14 +312,15 @@ def test_informational_response_related_nothing(self, frame_factory): assert base_event.priority_updated is None - def test_informational_response_received_related_all(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_informational_response_received_related_all(self, frame_factory, request_headers): """ 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) + c.send_headers(stream_id=1, headers=request_headers) input_frame = frame_factory.build_headers_frame( headers=self.informational_response_headers, @@ -320,13 +340,14 @@ def test_informational_response_received_related_all(self, frame_factory): base_event.priority_updated, h2.events.PriorityUpdated ) - def test_data_received_normally_relates_to_nothing(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_data_received_normally_relates_to_nothing(self, frame_factory, request_headers): """ 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) + c.send_headers(stream_id=1, headers=request_headers) f = frame_factory.build_headers_frame( headers=self.example_response_headers, @@ -343,13 +364,14 @@ def test_data_received_normally_relates_to_nothing(self, frame_factory): assert base_event.stream_ended is None - def test_data_received_related_stream_ended(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_data_received_related_stream_ended(self, frame_factory, request_headers): """ 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) + c.send_headers(stream_id=1, headers=request_headers) f = frame_factory.build_headers_frame( headers=self.example_response_headers, diff --git a/test/test_rfc7838.py b/test/test_rfc7838.py index d7704e23..2396a3f3 100644 --- a/test/test_rfc7838.py +++ b/test/test_rfc7838.py @@ -23,9 +23,15 @@ class TestRFC7838Client(object): (':scheme', 'https'), (':method', 'GET'), ] + example_request_headers_bytes = [ + (b':authority', b'example.com'), + (b':path', b'/'), + (b':scheme', b'https'), + (b':method', b'GET'), + ] example_response_headers = [ - (u':status', u'200'), - (u'server', u'fake-serv/0.1.0') + (':status', '200'), + ('server', 'fake-serv/0.1.0') ] def test_receiving_altsvc_stream_zero(self, frame_factory): @@ -69,14 +75,15 @@ def test_receiving_altsvc_stream_zero_no_origin(self, frame_factory): assert not events assert not c.data_to_send() - def test_receiving_altsvc_on_stream(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_receiving_altsvc_on_stream(self, frame_factory, request_headers): """ An ALTSVC frame received on a stream correctly transposes all the fields from the frame and attaches the expected origin. """ c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(stream_id=1, headers=self.example_request_headers) + c.send_headers(stream_id=1, headers=request_headers) c.clear_outbound_data_buffer() f = frame_factory.build_alt_svc_frame( @@ -94,14 +101,15 @@ def test_receiving_altsvc_on_stream(self, frame_factory): # No data gets sent. assert not c.data_to_send() - def test_receiving_altsvc_on_stream_with_origin(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_receiving_altsvc_on_stream_with_origin(self, frame_factory, request_headers): """ An ALTSVC frame received on a stream with an origin field present gets ignored. """ c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(stream_id=1, headers=self.example_request_headers) + c.send_headers(stream_id=1, headers=request_headers) c.clear_outbound_data_buffer() f = frame_factory.build_alt_svc_frame( @@ -159,14 +167,15 @@ def test_receiving_altsvc_before_sending_headers(self, frame_factory): assert len(events) == 0 assert not c.data_to_send() - def test_receiving_altsvc_after_receiving_headers(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_receiving_altsvc_after_receiving_headers(self, frame_factory, request_headers): """ When an ALTSVC frame is received but the server has already sent headers it gets ignored. """ c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(stream_id=1, headers=self.example_request_headers) + c.send_headers(stream_id=1, headers=request_headers) f = frame_factory.build_headers_frame( headers=self.example_response_headers @@ -182,14 +191,15 @@ def test_receiving_altsvc_after_receiving_headers(self, frame_factory): assert len(events) == 0 assert not c.data_to_send() - def test_receiving_altsvc_on_closed_stream(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_receiving_altsvc_on_closed_stream(self, frame_factory, request_headers): """ When an ALTSVC frame is received on a closed stream, we ignore it. """ c = h2.connection.H2Connection() c.initiate_connection() c.send_headers( - stream_id=1, headers=self.example_request_headers, end_stream=True + stream_id=1, headers=request_headers, end_stream=True ) f = frame_factory.build_headers_frame( @@ -207,19 +217,20 @@ def test_receiving_altsvc_on_closed_stream(self, frame_factory): assert len(events) == 0 assert not c.data_to_send() - def test_receiving_altsvc_on_pushed_stream(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_receiving_altsvc_on_pushed_stream(self, frame_factory, request_headers): """ When an ALTSVC frame is received on a stream that the server pushed, the frame is accepted. """ c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(stream_id=1, headers=self.example_request_headers) + c.send_headers(stream_id=1, headers=request_headers) f = frame_factory.build_push_promise_frame( stream_id=1, promised_stream_id=2, - headers=self.example_request_headers + headers=request_headers ) c.receive_data(f.serialize()) c.clear_outbound_data_buffer() @@ -239,13 +250,14 @@ def test_receiving_altsvc_on_pushed_stream(self, frame_factory): # No data gets sent. assert not c.data_to_send() - def test_cannot_send_explicit_alternative_service(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_cannot_send_explicit_alternative_service(self, frame_factory, request_headers): """ A client cannot send an explicit alternative service. """ c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(stream_id=1, headers=self.example_request_headers) + c.send_headers(stream_id=1, headers=request_headers) c.clear_outbound_data_buffer() with pytest.raises(h2.exceptions.ProtocolError): @@ -254,13 +266,14 @@ def test_cannot_send_explicit_alternative_service(self, frame_factory): origin=b"example.com", ) - def test_cannot_send_implicit_alternative_service(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_cannot_send_implicit_alternative_service(self, frame_factory, request_headers): """ A client cannot send an implicit alternative service. """ c = h2.connection.H2Connection() c.initiate_connection() - c.send_headers(stream_id=1, headers=self.example_request_headers) + c.send_headers(stream_id=1, headers=request_headers) c.clear_outbound_data_buffer() with pytest.raises(h2.exceptions.ProtocolError): @@ -280,6 +293,12 @@ class TestRFC7838Server(object): (':scheme', 'https'), (':method', 'GET'), ] + example_request_headers_bytes = [ + (b':authority', b'example.com'), + (b':path', b'/'), + (b':scheme', b'https'), + (b':method', b'GET'), + ] example_response_headers = [ (u':status', u'200'), (u'server', u'fake-serv/0.1.0') @@ -305,7 +324,8 @@ def test_receiving_altsvc_as_server_stream_zero(self, frame_factory): assert len(events) == 0 assert not c.data_to_send() - def test_receiving_altsvc_as_server_on_stream(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_receiving_altsvc_as_server_on_stream(self, frame_factory, request_headers): """ When an ALTSVC frame is received on a stream and we are a server, we ignore it. @@ -315,7 +335,7 @@ def test_receiving_altsvc_as_server_on_stream(self, frame_factory): c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - headers=self.example_request_headers + headers=request_headers ) c.receive_data(f.serialize()) c.clear_outbound_data_buffer() @@ -347,7 +367,8 @@ def test_sending_explicit_alternative_service(self, frame_factory): ) assert c.data_to_send() == f.serialize() - def test_sending_implicit_alternative_service(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_sending_implicit_alternative_service(self, frame_factory, request_headers): """ A server can send an implicit alternative service. """ @@ -356,7 +377,7 @@ def test_sending_implicit_alternative_service(self, frame_factory): c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - headers=self.example_request_headers + headers=request_headers ) c.receive_data(f.serialize()) c.clear_outbound_data_buffer() @@ -388,8 +409,10 @@ def test_no_implicit_alternative_service_before_headers(self, stream_id=1, ) + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) def test_no_implicit_alternative_service_after_response(self, - frame_factory): + frame_factory, + request_headers): """ If the server has sent response headers, hyper-h2 forbids sending an implicit alternative service. @@ -399,7 +422,7 @@ def test_no_implicit_alternative_service_after_response(self, c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - headers=self.example_request_headers + headers=request_headers ) c.receive_data(f.serialize()) c.send_headers(stream_id=1, headers=self.example_response_headers) @@ -411,7 +434,8 @@ def test_no_implicit_alternative_service_after_response(self, stream_id=1, ) - def test_cannot_provide_origin_and_stream_id(self, frame_factory): + @pytest.mark.parametrize("request_headers", [example_request_headers, example_request_headers_bytes]) + def test_cannot_provide_origin_and_stream_id(self, frame_factory, request_headers): """ The user cannot provide both the origin and stream_id arguments when advertising alternative services. @@ -420,7 +444,7 @@ def test_cannot_provide_origin_and_stream_id(self, frame_factory): c.initiate_connection() c.receive_data(frame_factory.preamble()) f = frame_factory.build_headers_frame( - headers=self.example_request_headers + headers=request_headers ) c.receive_data(f.serialize()) diff --git a/test/test_rfc8441.py b/test/test_rfc8441.py index b2bf881f..d3bbde40 100644 --- a/test/test_rfc8441.py +++ b/test/test_rfc8441.py @@ -5,6 +5,9 @@ Test the RFC 8441 extended connect request support. """ +from h2.utilities import utf8_encode_headers +import pytest + import h2.config import h2.connection import h2.events @@ -16,16 +19,26 @@ class TestRFC8441(object): and the server supports receiving it. """ - def test_can_send_headers(self, frame_factory): - headers = [ - (b':authority', b'example.com'), - (b':path', b'/'), - (b':scheme', b'https'), - (b':method', b'CONNECT'), - (b':protocol', b'websocket'), - (b'user-agent', b'someua/0.0.1'), - ] + headers = [ + (':authority', 'example.com'), + (':path', '/'), + (':scheme', 'https'), + (':method', 'CONNECT'), + (':protocol', 'websocket'), + ('user-agent', 'someua/0.0.1'), + ] + + headers_bytes = [ + (b':authority', b'example.com'), + (b':path', b'/'), + (b':scheme', b'https'), + (b':method', b'CONNECT'), + (b':protocol', b'websocket'), + (b'user-agent', b'someua/0.0.1'), + ] + @pytest.mark.parametrize("headers", [headers, headers_bytes]) + def test_can_send_headers(self, frame_factory, headers): client = h2.connection.H2Connection() client.initiate_connection() client.send_headers(stream_id=1, headers=headers) @@ -37,4 +50,4 @@ def test_can_send_headers(self, frame_factory): event = events[1] assert isinstance(event, h2.events.RequestReceived) assert event.stream_id == 1 - assert event.headers == headers + assert event.headers == utf8_encode_headers(headers)