1# -*- coding: utf-8 -*- 2""" 3test_stream_reset 4~~~~~~~~~~~~~~~~~ 5 6More complex tests that exercise stream resetting functionality to validate 7that connection state is appropriately maintained. 8 9Specifically, these tests validate that streams that have been reset accurately 10keep track of connection-level state. 11""" 12import pytest 13 14import h2.connection 15import h2.errors 16import h2.events 17 18 19class TestStreamReset(object): 20 """ 21 Tests for resetting streams. 22 """ 23 example_request_headers = [ 24 (b':authority', b'example.com'), 25 (b':path', b'/'), 26 (b':scheme', b'https'), 27 (b':method', b'GET'), 28 ] 29 example_response_headers = [ 30 (b':status', b'200'), 31 (b'server', b'fake-serv/0.1.0'), 32 (b'content-length', b'0') 33 ] 34 35 def test_reset_stream_keeps_header_state_correct(self, frame_factory): 36 """ 37 A stream that has been reset still affects the header decoder. 38 """ 39 c = h2.connection.H2Connection() 40 c.initiate_connection() 41 c.send_headers(stream_id=1, headers=self.example_request_headers) 42 c.reset_stream(stream_id=1) 43 c.send_headers(stream_id=3, headers=self.example_request_headers) 44 c.clear_outbound_data_buffer() 45 46 f = frame_factory.build_headers_frame( 47 headers=self.example_response_headers, stream_id=1 48 ) 49 rst_frame = frame_factory.build_rst_stream_frame( 50 1, h2.errors.ErrorCodes.STREAM_CLOSED 51 ) 52 events = c.receive_data(f.serialize()) 53 assert not events 54 assert c.data_to_send() == rst_frame.serialize() 55 56 # This works because the header state should be intact from the headers 57 # frame that was send on stream 1, so they should decode cleanly. 58 f = frame_factory.build_headers_frame( 59 headers=self.example_response_headers, stream_id=3 60 ) 61 event = c.receive_data(f.serialize())[0] 62 63 assert isinstance(event, h2.events.ResponseReceived) 64 assert event.stream_id == 3 65 assert event.headers == self.example_response_headers 66 67 @pytest.mark.parametrize('close_id,other_id', [(1, 3), (3, 1)]) 68 def test_reset_stream_keeps_flow_control_correct(self, 69 close_id, 70 other_id, 71 frame_factory): 72 """ 73 A stream that has been reset does not affect the connection flow 74 control window. 75 """ 76 c = h2.connection.H2Connection() 77 c.initiate_connection() 78 c.send_headers(stream_id=1, headers=self.example_request_headers) 79 c.send_headers(stream_id=3, headers=self.example_request_headers) 80 81 # Record the initial window size. 82 initial_window = c.remote_flow_control_window(stream_id=other_id) 83 84 f = frame_factory.build_headers_frame( 85 headers=self.example_response_headers, stream_id=close_id 86 ) 87 c.receive_data(f.serialize()) 88 c.reset_stream(stream_id=close_id) 89 c.clear_outbound_data_buffer() 90 91 f = frame_factory.build_data_frame( 92 data=b'some data', 93 stream_id=close_id 94 ) 95 c.receive_data(f.serialize()) 96 97 expected = frame_factory.build_rst_stream_frame( 98 stream_id=close_id, 99 error_code=h2.errors.ErrorCodes.STREAM_CLOSED, 100 ).serialize() 101 assert c.data_to_send() == expected 102 103 new_window = c.remote_flow_control_window(stream_id=other_id) 104 assert initial_window - len(b'some data') == new_window 105 106 @pytest.mark.parametrize('clear_streams', [True, False]) 107 def test_reset_stream_automatically_resets_pushed_streams(self, 108 frame_factory, 109 clear_streams): 110 """ 111 Resetting a stream causes RST_STREAM frames to be automatically emitted 112 to close any streams pushed after the reset. 113 """ 114 c = h2.connection.H2Connection() 115 c.initiate_connection() 116 c.send_headers(stream_id=1, headers=self.example_request_headers) 117 c.reset_stream(stream_id=1) 118 c.clear_outbound_data_buffer() 119 120 if clear_streams: 121 # Call open_outbound_streams to force the connection to clean 122 # closed streams. 123 c.open_outbound_streams 124 125 f = frame_factory.build_push_promise_frame( 126 stream_id=1, 127 promised_stream_id=2, 128 headers=self.example_request_headers, 129 ) 130 events = c.receive_data(f.serialize()) 131 assert not events 132 133 f = frame_factory.build_rst_stream_frame( 134 stream_id=2, 135 error_code=h2.errors.ErrorCodes.REFUSED_STREAM, 136 ) 137 assert c.data_to_send() == f.serialize() 138