1# -*- coding: utf-8 -*- 2""" 3test_h2_upgrade.py 4~~~~~~~~~~~~~~~~~~ 5 6This module contains tests that exercise the HTTP Upgrade functionality of 7hyper-h2, ensuring that clients and servers can upgrade their plaintext 8HTTP/1.1 connections to HTTP/2. 9""" 10import base64 11 12import pytest 13 14import h2.config 15import h2.connection 16import h2.errors 17import h2.events 18import h2.exceptions 19 20 21class TestClientUpgrade(object): 22 """ 23 Tests of the client-side of the HTTP/2 upgrade dance. 24 """ 25 example_request_headers = [ 26 (b':authority', b'example.com'), 27 (b':path', b'/'), 28 (b':scheme', b'https'), 29 (b':method', b'GET'), 30 ] 31 example_response_headers = [ 32 (b':status', b'200'), 33 (b'server', b'fake-serv/0.1.0') 34 ] 35 36 def test_returns_http2_settings(self, frame_factory): 37 """ 38 Calling initiate_upgrade_connection returns a base64url encoded 39 Settings frame with the settings used by the connection. 40 """ 41 conn = h2.connection.H2Connection() 42 data = conn.initiate_upgrade_connection() 43 44 # The base64 encoding must not be padded. 45 assert not data.endswith(b'=') 46 47 # However, SETTINGS frames should never need to be padded. 48 decoded_frame = base64.urlsafe_b64decode(data) 49 expected_frame = frame_factory.build_settings_frame( 50 settings=conn.local_settings 51 ) 52 assert decoded_frame == expected_frame.serialize_body() 53 54 def test_emits_preamble(self, frame_factory): 55 """ 56 Calling initiate_upgrade_connection emits the connection preamble. 57 """ 58 conn = h2.connection.H2Connection() 59 conn.initiate_upgrade_connection() 60 61 data = conn.data_to_send() 62 assert data.startswith(frame_factory.preamble()) 63 64 data = data[len(frame_factory.preamble()):] 65 expected_frame = frame_factory.build_settings_frame( 66 settings=conn.local_settings 67 ) 68 assert data == expected_frame.serialize() 69 70 def test_can_receive_response(self, frame_factory): 71 """ 72 After upgrading, we can safely receive a response. 73 """ 74 c = h2.connection.H2Connection() 75 c.initiate_upgrade_connection() 76 c.clear_outbound_data_buffer() 77 78 f1 = frame_factory.build_headers_frame( 79 stream_id=1, 80 headers=self.example_response_headers, 81 ) 82 f2 = frame_factory.build_data_frame( 83 stream_id=1, 84 data=b'some data', 85 flags=['END_STREAM'] 86 ) 87 events = c.receive_data(f1.serialize() + f2.serialize()) 88 assert len(events) == 3 89 90 assert isinstance(events[0], h2.events.ResponseReceived) 91 assert isinstance(events[1], h2.events.DataReceived) 92 assert isinstance(events[2], h2.events.StreamEnded) 93 94 assert events[0].headers == self.example_response_headers 95 assert events[1].data == b'some data' 96 assert all(e.stream_id == 1 for e in events) 97 98 assert not c.data_to_send() 99 100 def test_can_receive_pushed_stream(self, frame_factory): 101 """ 102 After upgrading, we can safely receive a pushed stream. 103 """ 104 c = h2.connection.H2Connection() 105 c.initiate_upgrade_connection() 106 c.clear_outbound_data_buffer() 107 108 f = frame_factory.build_push_promise_frame( 109 stream_id=1, 110 promised_stream_id=2, 111 headers=self.example_request_headers, 112 ) 113 events = c.receive_data(f.serialize()) 114 assert len(events) == 1 115 116 assert isinstance(events[0], h2.events.PushedStreamReceived) 117 assert events[0].headers == self.example_request_headers 118 assert events[0].parent_stream_id == 1 119 assert events[0].pushed_stream_id == 2 120 121 def test_cannot_send_headers_stream_1(self, frame_factory): 122 """ 123 After upgrading, we cannot send headers on stream 1. 124 """ 125 c = h2.connection.H2Connection() 126 c.initiate_upgrade_connection() 127 c.clear_outbound_data_buffer() 128 129 with pytest.raises(h2.exceptions.ProtocolError): 130 c.send_headers(stream_id=1, headers=self.example_request_headers) 131 132 def test_cannot_send_data_stream_1(self, frame_factory): 133 """ 134 After upgrading, we cannot send data on stream 1. 135 """ 136 c = h2.connection.H2Connection() 137 c.initiate_upgrade_connection() 138 c.clear_outbound_data_buffer() 139 140 with pytest.raises(h2.exceptions.ProtocolError): 141 c.send_data(stream_id=1, data=b'some data') 142 143 144class TestServerUpgrade(object): 145 """ 146 Tests of the server-side of the HTTP/2 upgrade dance. 147 """ 148 example_request_headers = [ 149 (b':authority', b'example.com'), 150 (b':path', b'/'), 151 (b':scheme', b'https'), 152 (b':method', b'GET'), 153 ] 154 example_response_headers = [ 155 (b':status', b'200'), 156 (b'server', b'fake-serv/0.1.0') 157 ] 158 server_config = h2.config.H2Configuration(client_side=False) 159 160 def test_returns_nothing(self, frame_factory): 161 """ 162 Calling initiate_upgrade_connection returns nothing. 163 """ 164 conn = h2.connection.H2Connection(config=self.server_config) 165 curl_header = b"AAMAAABkAAQAAP__" 166 data = conn.initiate_upgrade_connection(curl_header) 167 assert data is None 168 169 def test_emits_preamble(self, frame_factory): 170 """ 171 Calling initiate_upgrade_connection emits the connection preamble. 172 """ 173 conn = h2.connection.H2Connection(config=self.server_config) 174 conn.initiate_upgrade_connection() 175 176 data = conn.data_to_send() 177 expected_frame = frame_factory.build_settings_frame( 178 settings=conn.local_settings 179 ) 180 assert data == expected_frame.serialize() 181 182 def test_can_send_response(self, frame_factory): 183 """ 184 After upgrading, we can safely send a response. 185 """ 186 c = h2.connection.H2Connection(config=self.server_config) 187 c.initiate_upgrade_connection() 188 c.clear_outbound_data_buffer() 189 190 c.send_headers(stream_id=1, headers=self.example_response_headers) 191 c.send_data(stream_id=1, data=b'some data', end_stream=True) 192 193 f1 = frame_factory.build_headers_frame( 194 stream_id=1, 195 headers=self.example_response_headers, 196 ) 197 f2 = frame_factory.build_data_frame( 198 stream_id=1, 199 data=b'some data', 200 flags=['END_STREAM'] 201 ) 202 203 expected_data = f1.serialize() + f2.serialize() 204 assert c.data_to_send() == expected_data 205 206 def test_can_push_stream(self, frame_factory): 207 """ 208 After upgrading, we can safely push a stream. 209 """ 210 c = h2.connection.H2Connection(config=self.server_config) 211 c.initiate_upgrade_connection() 212 c.clear_outbound_data_buffer() 213 214 c.push_stream( 215 stream_id=1, 216 promised_stream_id=2, 217 request_headers=self.example_request_headers 218 ) 219 220 f = frame_factory.build_push_promise_frame( 221 stream_id=1, 222 promised_stream_id=2, 223 headers=self.example_request_headers, 224 ) 225 assert c.data_to_send() == f.serialize() 226 227 def test_cannot_receive_headers_stream_1(self, frame_factory): 228 """ 229 After upgrading, we cannot receive headers on stream 1. 230 """ 231 c = h2.connection.H2Connection(config=self.server_config) 232 c.initiate_upgrade_connection() 233 c.receive_data(frame_factory.preamble()) 234 c.clear_outbound_data_buffer() 235 236 f = frame_factory.build_headers_frame( 237 stream_id=1, 238 headers=self.example_request_headers, 239 ) 240 c.receive_data(f.serialize()) 241 242 expected_frame = frame_factory.build_rst_stream_frame( 243 stream_id=1, 244 error_code=h2.errors.ErrorCodes.STREAM_CLOSED, 245 ) 246 assert c.data_to_send() == expected_frame.serialize() 247 248 def test_cannot_receive_data_stream_1(self, frame_factory): 249 """ 250 After upgrading, we cannot receive data on stream 1. 251 """ 252 c = h2.connection.H2Connection(config=self.server_config) 253 c.initiate_upgrade_connection() 254 c.receive_data(frame_factory.preamble()) 255 c.clear_outbound_data_buffer() 256 257 f = frame_factory.build_data_frame( 258 stream_id=1, 259 data=b'some data', 260 ) 261 c.receive_data(f.serialize()) 262 263 expected = frame_factory.build_rst_stream_frame( 264 stream_id=1, 265 error_code=h2.errors.ErrorCodes.STREAM_CLOSED, 266 ).serialize() 267 assert c.data_to_send() == expected 268 269 def test_client_settings_are_applied(self, frame_factory): 270 """ 271 The settings provided by the client are applied and immediately 272 ACK'ed. 273 """ 274 server = h2.connection.H2Connection(config=self.server_config) 275 client = h2.connection.H2Connection() 276 277 # As a precaution, let's confirm that the server and client, at the 278 # start of the connection, do not agree on their initial settings 279 # state. 280 assert ( 281 client.local_settings != server.remote_settings 282 ) 283 284 # Get the client header data and pass it to the server. 285 header_data = client.initiate_upgrade_connection() 286 server.initiate_upgrade_connection(header_data) 287 288 # This gets complex, but here we go. 289 # RFC 7540 § 3.2.1 says that "explicit acknowledgement" of the settings 290 # in the header is "not necessary". That's annoyingly vague, but we 291 # interpret that to mean "should not be sent". So to test that this 292 # worked we need to test that the server has only sent the preamble, 293 # and has not sent a SETTINGS ack, and also that the server has the 294 # correct settings. 295 expected_frame = frame_factory.build_settings_frame( 296 server.local_settings 297 ) 298 assert server.data_to_send() == expected_frame.serialize() 299 300 assert ( 301 client.local_settings == server.remote_settings 302 ) 303