test_rfc7838.py (15036B)
1 # -*- coding: utf-8 -*- 2 """ 3 test_rfc7838 4 ~~~~~~~~~~~~ 5 6 Test the RFC 7838 ALTSVC support. 7 """ 8 import pytest 9 10 import h2.config 11 import h2.connection 12 import h2.events 13 import h2.exceptions 14 15 16 class TestRFC7838Client(object): 17 """ 18 Tests that the client supports receiving the RFC 7838 AltSvc frame. 19 """ 20 example_request_headers = [ 21 (':authority', 'example.com'), 22 (':path', '/'), 23 (':scheme', 'https'), 24 (':method', 'GET'), 25 ] 26 example_response_headers = [ 27 (u':status', u'200'), 28 (u'server', u'fake-serv/0.1.0') 29 ] 30 31 def test_receiving_altsvc_stream_zero(self, frame_factory): 32 """ 33 An ALTSVC frame received on stream zero correctly transposes all the 34 fields from the frames. 35 """ 36 c = h2.connection.H2Connection() 37 c.initiate_connection() 38 c.clear_outbound_data_buffer() 39 40 f = frame_factory.build_alt_svc_frame( 41 stream_id=0, origin=b"example.com", field=b'h2=":8000"; ma=60' 42 ) 43 events = c.receive_data(f.serialize()) 44 45 assert len(events) == 1 46 event = events[0] 47 48 assert isinstance(event, h2.events.AlternativeServiceAvailable) 49 assert event.origin == b"example.com" 50 assert event.field_value == b'h2=":8000"; ma=60' 51 52 # No data gets sent. 53 assert not c.data_to_send() 54 55 def test_receiving_altsvc_stream_zero_no_origin(self, frame_factory): 56 """ 57 An ALTSVC frame received on stream zero without an origin field is 58 ignored. 59 """ 60 c = h2.connection.H2Connection() 61 c.initiate_connection() 62 c.clear_outbound_data_buffer() 63 64 f = frame_factory.build_alt_svc_frame( 65 stream_id=0, origin=b"", field=b'h2=":8000"; ma=60' 66 ) 67 events = c.receive_data(f.serialize()) 68 69 assert not events 70 assert not c.data_to_send() 71 72 def test_receiving_altsvc_on_stream(self, frame_factory): 73 """ 74 An ALTSVC frame received on a stream correctly transposes all the 75 fields from the frame and attaches the expected origin. 76 """ 77 c = h2.connection.H2Connection() 78 c.initiate_connection() 79 c.send_headers(stream_id=1, headers=self.example_request_headers) 80 c.clear_outbound_data_buffer() 81 82 f = frame_factory.build_alt_svc_frame( 83 stream_id=1, origin=b"", field=b'h2=":8000"; ma=60' 84 ) 85 events = c.receive_data(f.serialize()) 86 87 assert len(events) == 1 88 event = events[0] 89 90 assert isinstance(event, h2.events.AlternativeServiceAvailable) 91 assert event.origin == b"example.com" 92 assert event.field_value == b'h2=":8000"; ma=60' 93 94 # No data gets sent. 95 assert not c.data_to_send() 96 97 def test_receiving_altsvc_on_stream_with_origin(self, frame_factory): 98 """ 99 An ALTSVC frame received on a stream with an origin field present gets 100 ignored. 101 """ 102 c = h2.connection.H2Connection() 103 c.initiate_connection() 104 c.send_headers(stream_id=1, headers=self.example_request_headers) 105 c.clear_outbound_data_buffer() 106 107 f = frame_factory.build_alt_svc_frame( 108 stream_id=1, origin=b"example.com", field=b'h2=":8000"; ma=60' 109 ) 110 events = c.receive_data(f.serialize()) 111 112 assert len(events) == 0 113 assert not c.data_to_send() 114 115 def test_receiving_altsvc_on_stream_not_yet_opened(self, frame_factory): 116 """ 117 When an ALTSVC frame is received on a stream the client hasn't yet 118 opened, the frame is ignored. 119 """ 120 c = h2.connection.H2Connection() 121 c.initiate_connection() 122 c.clear_outbound_data_buffer() 123 124 # We'll test this twice, once on a client-initiated stream ID and once 125 # on a server initiated one. 126 f1 = frame_factory.build_alt_svc_frame( 127 stream_id=1, origin=b"", field=b'h2=":8000"; ma=60' 128 ) 129 f2 = frame_factory.build_alt_svc_frame( 130 stream_id=2, origin=b"", field=b'h2=":8000"; ma=60' 131 ) 132 events = c.receive_data(f1.serialize() + f2.serialize()) 133 134 assert len(events) == 0 135 assert not c.data_to_send() 136 137 def test_receiving_altsvc_before_sending_headers(self, frame_factory): 138 """ 139 When an ALTSVC frame is received but the client hasn't sent headers yet 140 it gets ignored. 141 """ 142 c = h2.connection.H2Connection() 143 c.initiate_connection() 144 145 # We need to create the idle stream. We have to do it by calling 146 # a private API. While this can't naturally happen in hyper-h2 (we 147 # don't currently have a mechanism by which this could occur), it could 148 # happen in the future and we defend against it. 149 c._begin_new_stream( 150 stream_id=1, allowed_ids=h2.connection.AllowedStreamIDs.ODD 151 ) 152 c.clear_outbound_data_buffer() 153 154 f = frame_factory.build_alt_svc_frame( 155 stream_id=1, origin=b"", field=b'h2=":8000"; ma=60' 156 ) 157 events = c.receive_data(f.serialize()) 158 159 assert len(events) == 0 160 assert not c.data_to_send() 161 162 def test_receiving_altsvc_after_receiving_headers(self, frame_factory): 163 """ 164 When an ALTSVC frame is received but the server has already sent 165 headers it gets ignored. 166 """ 167 c = h2.connection.H2Connection() 168 c.initiate_connection() 169 c.send_headers(stream_id=1, headers=self.example_request_headers) 170 171 f = frame_factory.build_headers_frame( 172 headers=self.example_response_headers 173 ) 174 c.receive_data(f.serialize()) 175 c.clear_outbound_data_buffer() 176 177 f = frame_factory.build_alt_svc_frame( 178 stream_id=1, origin=b"", field=b'h2=":8000"; ma=60' 179 ) 180 events = c.receive_data(f.serialize()) 181 182 assert len(events) == 0 183 assert not c.data_to_send() 184 185 def test_receiving_altsvc_on_closed_stream(self, frame_factory): 186 """ 187 When an ALTSVC frame is received on a closed stream, we ignore it. 188 """ 189 c = h2.connection.H2Connection() 190 c.initiate_connection() 191 c.send_headers( 192 stream_id=1, headers=self.example_request_headers, end_stream=True 193 ) 194 195 f = frame_factory.build_headers_frame( 196 headers=self.example_response_headers, 197 flags=['END_STREAM'], 198 ) 199 c.receive_data(f.serialize()) 200 c.clear_outbound_data_buffer() 201 202 f = frame_factory.build_alt_svc_frame( 203 stream_id=1, origin=b"", field=b'h2=":8000"; ma=60' 204 ) 205 events = c.receive_data(f.serialize()) 206 207 assert len(events) == 0 208 assert not c.data_to_send() 209 210 def test_receiving_altsvc_on_pushed_stream(self, frame_factory): 211 """ 212 When an ALTSVC frame is received on a stream that the server pushed, 213 the frame is accepted. 214 """ 215 c = h2.connection.H2Connection() 216 c.initiate_connection() 217 c.send_headers(stream_id=1, headers=self.example_request_headers) 218 219 f = frame_factory.build_push_promise_frame( 220 stream_id=1, 221 promised_stream_id=2, 222 headers=self.example_request_headers 223 ) 224 c.receive_data(f.serialize()) 225 c.clear_outbound_data_buffer() 226 227 f = frame_factory.build_alt_svc_frame( 228 stream_id=2, origin=b"", field=b'h2=":8000"; ma=60' 229 ) 230 events = c.receive_data(f.serialize()) 231 232 assert len(events) == 1 233 event = events[0] 234 235 assert isinstance(event, h2.events.AlternativeServiceAvailable) 236 assert event.origin == b"example.com" 237 assert event.field_value == b'h2=":8000"; ma=60' 238 239 # No data gets sent. 240 assert not c.data_to_send() 241 242 def test_cannot_send_explicit_alternative_service(self, frame_factory): 243 """ 244 A client cannot send an explicit alternative service. 245 """ 246 c = h2.connection.H2Connection() 247 c.initiate_connection() 248 c.send_headers(stream_id=1, headers=self.example_request_headers) 249 c.clear_outbound_data_buffer() 250 251 with pytest.raises(h2.exceptions.ProtocolError): 252 c.advertise_alternative_service( 253 field_value=b'h2=":8000"; ma=60', 254 origin=b"example.com", 255 ) 256 257 def test_cannot_send_implicit_alternative_service(self, frame_factory): 258 """ 259 A client cannot send an implicit alternative service. 260 """ 261 c = h2.connection.H2Connection() 262 c.initiate_connection() 263 c.send_headers(stream_id=1, headers=self.example_request_headers) 264 c.clear_outbound_data_buffer() 265 266 with pytest.raises(h2.exceptions.ProtocolError): 267 c.advertise_alternative_service( 268 field_value=b'h2=":8000"; ma=60', 269 stream_id=1, 270 ) 271 272 273 class TestRFC7838Server(object): 274 """ 275 Tests that the server supports sending the RFC 7838 AltSvc frame. 276 """ 277 example_request_headers = [ 278 (':authority', 'example.com'), 279 (':path', '/'), 280 (':scheme', 'https'), 281 (':method', 'GET'), 282 ] 283 example_response_headers = [ 284 (u':status', u'200'), 285 (u'server', u'fake-serv/0.1.0') 286 ] 287 288 server_config = h2.config.H2Configuration(client_side=False) 289 290 def test_receiving_altsvc_as_server_stream_zero(self, frame_factory): 291 """ 292 When an ALTSVC frame is received on stream zero and we are a server, 293 we ignore it. 294 """ 295 c = h2.connection.H2Connection(config=self.server_config) 296 c.initiate_connection() 297 c.receive_data(frame_factory.preamble()) 298 c.clear_outbound_data_buffer() 299 300 f = frame_factory.build_alt_svc_frame( 301 stream_id=0, origin=b"example.com", field=b'h2=":8000"; ma=60' 302 ) 303 events = c.receive_data(f.serialize()) 304 305 assert len(events) == 0 306 assert not c.data_to_send() 307 308 def test_receiving_altsvc_as_server_on_stream(self, frame_factory): 309 """ 310 When an ALTSVC frame is received on a stream and we are a server, we 311 ignore it. 312 """ 313 c = h2.connection.H2Connection(config=self.server_config) 314 c.initiate_connection() 315 c.receive_data(frame_factory.preamble()) 316 317 f = frame_factory.build_headers_frame( 318 headers=self.example_request_headers 319 ) 320 c.receive_data(f.serialize()) 321 c.clear_outbound_data_buffer() 322 323 f = frame_factory.build_alt_svc_frame( 324 stream_id=1, origin=b"", field=b'h2=":8000"; ma=60' 325 ) 326 events = c.receive_data(f.serialize()) 327 328 assert len(events) == 0 329 assert not c.data_to_send() 330 331 def test_sending_explicit_alternative_service(self, frame_factory): 332 """ 333 A server can send an explicit alternative service. 334 """ 335 c = h2.connection.H2Connection(config=self.server_config) 336 c.initiate_connection() 337 c.receive_data(frame_factory.preamble()) 338 c.clear_outbound_data_buffer() 339 340 c.advertise_alternative_service( 341 field_value=b'h2=":8000"; ma=60', 342 origin=b"example.com", 343 ) 344 345 f = frame_factory.build_alt_svc_frame( 346 stream_id=0, origin=b"example.com", field=b'h2=":8000"; ma=60' 347 ) 348 assert c.data_to_send() == f.serialize() 349 350 def test_sending_implicit_alternative_service(self, frame_factory): 351 """ 352 A server can send an implicit alternative service. 353 """ 354 c = h2.connection.H2Connection(config=self.server_config) 355 c.initiate_connection() 356 c.receive_data(frame_factory.preamble()) 357 358 f = frame_factory.build_headers_frame( 359 headers=self.example_request_headers 360 ) 361 c.receive_data(f.serialize()) 362 c.clear_outbound_data_buffer() 363 364 c.advertise_alternative_service( 365 field_value=b'h2=":8000"; ma=60', 366 stream_id=1, 367 ) 368 369 f = frame_factory.build_alt_svc_frame( 370 stream_id=1, origin=b"", field=b'h2=":8000"; ma=60' 371 ) 372 assert c.data_to_send() == f.serialize() 373 374 def test_no_implicit_alternative_service_before_headers(self, 375 frame_factory): 376 """ 377 If headers haven't been received yet, the server forbids sending an 378 implicit alternative service. 379 """ 380 c = h2.connection.H2Connection(config=self.server_config) 381 c.initiate_connection() 382 c.receive_data(frame_factory.preamble()) 383 c.clear_outbound_data_buffer() 384 385 with pytest.raises(h2.exceptions.ProtocolError): 386 c.advertise_alternative_service( 387 field_value=b'h2=":8000"; ma=60', 388 stream_id=1, 389 ) 390 391 def test_no_implicit_alternative_service_after_response(self, 392 frame_factory): 393 """ 394 If the server has sent response headers, hyper-h2 forbids sending an 395 implicit alternative service. 396 """ 397 c = h2.connection.H2Connection(config=self.server_config) 398 c.initiate_connection() 399 c.receive_data(frame_factory.preamble()) 400 401 f = frame_factory.build_headers_frame( 402 headers=self.example_request_headers 403 ) 404 c.receive_data(f.serialize()) 405 c.send_headers(stream_id=1, headers=self.example_response_headers) 406 c.clear_outbound_data_buffer() 407 408 with pytest.raises(h2.exceptions.ProtocolError): 409 c.advertise_alternative_service( 410 field_value=b'h2=":8000"; ma=60', 411 stream_id=1, 412 ) 413 414 def test_cannot_provide_origin_and_stream_id(self, frame_factory): 415 """ 416 The user cannot provide both the origin and stream_id arguments when 417 advertising alternative services. 418 """ 419 c = h2.connection.H2Connection(config=self.server_config) 420 c.initiate_connection() 421 c.receive_data(frame_factory.preamble()) 422 f = frame_factory.build_headers_frame( 423 headers=self.example_request_headers 424 ) 425 c.receive_data(f.serialize()) 426 427 with pytest.raises(ValueError): 428 c.advertise_alternative_service( 429 field_value=b'h2=":8000"; ma=60', 430 origin=b"example.com", 431 stream_id=1, 432 ) 433 434 def test_cannot_provide_unicode_altsvc_field(self, frame_factory): 435 """ 436 The user cannot provide the field value for alternative services as a 437 unicode string. 438 """ 439 c = h2.connection.H2Connection(config=self.server_config) 440 c.initiate_connection() 441 c.receive_data(frame_factory.preamble()) 442 443 with pytest.raises(ValueError): 444 c.advertise_alternative_service( 445 field_value=u'h2=":8000"; ma=60', 446 origin=b"example.com", 447 )