Source code

Revision control

Copy as Markdown

Other Tools

# -*- coding: utf-8 -*-
"""
test_informational_responses
~~~~~~~~~~~~~~~~~~~~~~~~~~
Tests that validate that hyper-h2 correctly handles informational (1XX)
responses in its state machine.
"""
import pytest
import h2.config
import h2.connection
import h2.events
import h2.exceptions
class TestReceivingInformationalResponses(object):
"""
Tests for receiving informational responses.
"""
example_request_headers = [
(b':authority', b'example.com'),
(b':path', b'/'),
(b':scheme', b'https'),
(b':method', b'GET'),
(b'expect', b'100-continue'),
]
example_informational_headers = [
(b':status', b'100'),
(b'server', b'fake-serv/0.1.0')
]
example_response_headers = [
(b':status', b'200'),
(b'server', b'fake-serv/0.1.0')
]
example_trailers = [
(b'trailer', b'you-bet'),
]
@pytest.mark.parametrize('end_stream', (True, False))
def test_single_informational_response(self, frame_factory, end_stream):
"""
When receiving a informational response, the appropriate event is
signaled.
"""
c = h2.connection.H2Connection()
c.initiate_connection()
c.send_headers(
stream_id=1,
headers=self.example_request_headers,
end_stream=end_stream
)
f = frame_factory.build_headers_frame(
headers=self.example_informational_headers,
stream_id=1,
)
events = c.receive_data(f.serialize())
assert len(events) == 1
event = events[0]
assert isinstance(event, h2.events.InformationalResponseReceived)
assert event.headers == self.example_informational_headers
assert event.stream_id == 1
@pytest.mark.parametrize('end_stream', (True, False))
def test_receiving_multiple_header_blocks(self, frame_factory, end_stream):
"""
At least three header blocks can be received: informational, headers,
trailers.
"""
c = h2.connection.H2Connection()
c.initiate_connection()
c.send_headers(
stream_id=1,
headers=self.example_request_headers,
end_stream=end_stream
)
f1 = frame_factory.build_headers_frame(
headers=self.example_informational_headers,
stream_id=1,
)
f2 = frame_factory.build_headers_frame(
headers=self.example_response_headers,
stream_id=1,
)
f3 = frame_factory.build_headers_frame(
headers=self.example_trailers,
stream_id=1,
flags=['END_STREAM'],
)
events = c.receive_data(
f1.serialize() + f2.serialize() + f3.serialize()
)
assert len(events) == 4
assert isinstance(events[0], h2.events.InformationalResponseReceived)
assert events[0].headers == self.example_informational_headers
assert events[0].stream_id == 1
assert isinstance(events[1], h2.events.ResponseReceived)
assert events[1].headers == self.example_response_headers
assert events[1].stream_id == 1
assert isinstance(events[2], h2.events.TrailersReceived)
assert events[2].headers == self.example_trailers
assert events[2].stream_id == 1
@pytest.mark.parametrize('end_stream', (True, False))
def test_receiving_multiple_informational_responses(self,
frame_factory,
end_stream):
"""
More than one informational response is allowed.
"""
c = h2.connection.H2Connection()
c.initiate_connection()
c.send_headers(
stream_id=1,
headers=self.example_request_headers,
end_stream=end_stream
)
f1 = frame_factory.build_headers_frame(
headers=self.example_informational_headers,
stream_id=1,
)
f2 = frame_factory.build_headers_frame(
headers=[(':status', '101')],
stream_id=1,
)
events = c.receive_data(f1.serialize() + f2.serialize())
assert len(events) == 2
assert isinstance(events[0], h2.events.InformationalResponseReceived)
assert events[0].headers == self.example_informational_headers
assert events[0].stream_id == 1
assert isinstance(events[1], h2.events.InformationalResponseReceived)
assert events[1].headers == [(b':status', b'101')]
assert events[1].stream_id == 1
@pytest.mark.parametrize('end_stream', (True, False))
def test_receive_provisional_response_with_end_stream(self,
frame_factory,
end_stream):
"""
Receiving provisional responses with END_STREAM set causes
ProtocolErrors.
"""
c = h2.connection.H2Connection()
c.initiate_connection()
c.send_headers(
stream_id=1,
headers=self.example_request_headers,
end_stream=end_stream
)
c.clear_outbound_data_buffer()
f = frame_factory.build_headers_frame(
headers=self.example_informational_headers,
stream_id=1,
flags=['END_STREAM']
)
with pytest.raises(h2.exceptions.ProtocolError):
c.receive_data(f.serialize())
expected = frame_factory.build_goaway_frame(
last_stream_id=0,
error_code=1,
)
assert c.data_to_send() == expected.serialize()
@pytest.mark.parametrize('end_stream', (True, False))
def test_receiving_out_of_order_headers(self, frame_factory, end_stream):
"""
When receiving a informational response after the actual response
headers we consider it a ProtocolError and raise it.
"""
c = h2.connection.H2Connection()
c.initiate_connection()
c.send_headers(
stream_id=1,
headers=self.example_request_headers,
end_stream=end_stream
)
f1 = frame_factory.build_headers_frame(
headers=self.example_response_headers,
stream_id=1,
)
f2 = frame_factory.build_headers_frame(
headers=self.example_informational_headers,
stream_id=1,
)
c.receive_data(f1.serialize())
c.clear_outbound_data_buffer()
with pytest.raises(h2.exceptions.ProtocolError):
c.receive_data(f2.serialize())
expected = frame_factory.build_goaway_frame(
last_stream_id=0,
error_code=1,
)
assert c.data_to_send() == expected.serialize()
class TestSendingInformationalResponses(object):
"""
Tests for sending informational responses.
"""
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')
]
bytes_informational_headers = [
(b':status', b'100'),
(b'server', b'fake-serv/0.1.0')
]
example_response_headers = [
(b':status', b'200'),
(b'server', b'fake-serv/0.1.0')
]
example_trailers = [
(b'trailer', b'you-bet'),
]
server_config = h2.config.H2Configuration(client_side=False)
@pytest.mark.parametrize(
'hdrs', (unicode_informational_headers, bytes_informational_headers),
)
@pytest.mark.parametrize('end_stream', (True, False))
def test_single_informational_response(self,
frame_factory,
hdrs,
end_stream):
"""
When sending a informational response, the appropriate frames are
emitted.
"""
c = h2.connection.H2Connection(config=self.server_config)
c.initiate_connection()
c.receive_data(frame_factory.preamble())
flags = ['END_STREAM'] if end_stream else []
f = frame_factory.build_headers_frame(
headers=self.example_request_headers,
stream_id=1,
flags=flags,
)
c.receive_data(f.serialize())
c.clear_outbound_data_buffer()
frame_factory.refresh_encoder()
c.send_headers(
stream_id=1,
headers=hdrs
)
f = frame_factory.build_headers_frame(
headers=hdrs,
stream_id=1,
)
assert c.data_to_send() == f.serialize()
@pytest.mark.parametrize(
'hdrs', (unicode_informational_headers, bytes_informational_headers),
)
@pytest.mark.parametrize('end_stream', (True, False))
def test_sending_multiple_header_blocks(self,
frame_factory,
hdrs,
end_stream):
"""
At least three header blocks can be sent: informational, headers,
trailers.
"""
c = h2.connection.H2Connection(config=self.server_config)
c.initiate_connection()
c.receive_data(frame_factory.preamble())
flags = ['END_STREAM'] if end_stream else []
f = frame_factory.build_headers_frame(
headers=self.example_request_headers,
stream_id=1,
flags=flags,
)
c.receive_data(f.serialize())
c.clear_outbound_data_buffer()
frame_factory.refresh_encoder()
# Send the three header blocks.
c.send_headers(
stream_id=1,
headers=hdrs
)
c.send_headers(
stream_id=1,
headers=self.example_response_headers
)
c.send_headers(
stream_id=1,
headers=self.example_trailers,
end_stream=True
)
# Check that we sent them properly.
f1 = frame_factory.build_headers_frame(
headers=hdrs,
stream_id=1,
)
f2 = frame_factory.build_headers_frame(
headers=self.example_response_headers,
stream_id=1,
)
f3 = frame_factory.build_headers_frame(
headers=self.example_trailers,
stream_id=1,
flags=['END_STREAM']
)
assert (
c.data_to_send() ==
f1.serialize() + f2.serialize() + f3.serialize()
)
@pytest.mark.parametrize(
'hdrs', (unicode_informational_headers, bytes_informational_headers),
)
@pytest.mark.parametrize('end_stream', (True, False))
def test_sending_multiple_informational_responses(self,
frame_factory,
hdrs,
end_stream):
"""
More than one informational response is allowed.
"""
c = h2.connection.H2Connection(config=self.server_config)
c.initiate_connection()
c.receive_data(frame_factory.preamble())
flags = ['END_STREAM'] if end_stream else []
f = frame_factory.build_headers_frame(
headers=self.example_request_headers,
stream_id=1,
flags=flags,
)
c.receive_data(f.serialize())
c.clear_outbound_data_buffer()
frame_factory.refresh_encoder()
# Send two informational responses.
c.send_headers(
stream_id=1,
headers=hdrs,
)
c.send_headers(
stream_id=1,
headers=[(':status', '101')]
)
# Check we sent them both.
f1 = frame_factory.build_headers_frame(
headers=hdrs,
stream_id=1,
)
f2 = frame_factory.build_headers_frame(
headers=[(':status', '101')],
stream_id=1,
)
assert c.data_to_send() == f1.serialize() + f2.serialize()
@pytest.mark.parametrize(
'hdrs', (unicode_informational_headers, bytes_informational_headers),
)
@pytest.mark.parametrize('end_stream', (True, False))
def test_send_provisional_response_with_end_stream(self,
frame_factory,
hdrs,
end_stream):
"""
Sending provisional responses with END_STREAM set causes
ProtocolErrors.
"""
c = h2.connection.H2Connection(config=self.server_config)
c.initiate_connection()
c.receive_data(frame_factory.preamble())
flags = ['END_STREAM'] if end_stream else []
f = frame_factory.build_headers_frame(
headers=self.example_request_headers,
stream_id=1,
flags=flags,
)
c.receive_data(f.serialize())
with pytest.raises(h2.exceptions.ProtocolError):
c.send_headers(
stream_id=1,
headers=hdrs,
end_stream=True,
)
@pytest.mark.parametrize(
'hdrs', (unicode_informational_headers, bytes_informational_headers),
)
@pytest.mark.parametrize('end_stream', (True, False))
def test_reject_sending_out_of_order_headers(self,
frame_factory,
hdrs,
end_stream):
"""
When sending an informational response after the actual response
headers we consider it a ProtocolError and raise it.
"""
c = h2.connection.H2Connection(config=self.server_config)
c.initiate_connection()
c.receive_data(frame_factory.preamble())
flags = ['END_STREAM'] if end_stream else []
f = frame_factory.build_headers_frame(
headers=self.example_request_headers,
stream_id=1,
flags=flags,
)
c.receive_data(f.serialize())
c.send_headers(
stream_id=1,
headers=self.example_response_headers
)
with pytest.raises(h2.exceptions.ProtocolError):
c.send_headers(
stream_id=1,
headers=hdrs
)