def test_no_data_on_closed_stream(self, tctx): frame_factory = FrameFactory() req = Request.make("GET", "http://example.com/") resp = {":status": 200} assert ( Playbook(Http2Client(tctx)) << SendData( tctx.server, Placeholder(bytes)) # preamble + initial settings frame >> DataReceived( tctx.server, frame_factory.build_settings_frame({}, ack=True).serialize()) >> http.RequestHeaders(1, req, end_stream=True) << SendData( tctx.server, b"\x00\x00\x06\x01\x05\x00\x00\x00\x01\x82\x86\x84\\\x81\x07") >> http.RequestEndOfMessage(1) >> DataReceived( tctx.server, frame_factory.build_headers_frame(resp).serialize()) << http.ReceiveHttp(Placeholder( http.ResponseHeaders)) >> http.RequestProtocolError( 1, "cancelled", code=status_codes.CLIENT_CLOSED_REQUEST) << SendData( tctx.server, frame_factory.build_rst_stream_frame( 1, ErrorCodes.CANCEL).serialize()) >> DataReceived( tctx.server, frame_factory.build_data_frame(b"foo").serialize()) << SendData( tctx.server, frame_factory.build_rst_stream_frame( 1, ErrorCodes.STREAM_CLOSED).serialize()) ) # important: no ResponseData event here!
def test_early_server_data(tctx): playbook, cff = start_h2_client(tctx) sff = FrameFactory() tctx.server.address = ("example.com", 80) tctx.server.state = ConnectionState.OPEN tctx.server.alpn = b"h2" flow = Placeholder(HTTPFlow) server1 = Placeholder(bytes) server2 = Placeholder(bytes) assert ( playbook >> DataReceived(tctx.client, cff.build_headers_frame(example_request_headers, flags=["END_STREAM"]).serialize()) << http.HttpRequestHeadersHook(flow) >> reply() << (h := http.HttpRequestHook(flow)) # Surprise! We get data from the server before the request hook finishes. >> DataReceived(tctx.server, sff.build_settings_frame({}).serialize()) << SendData(tctx.server, server1) # Request hook finishes... >> reply(to=h) << SendData(tctx.server, server2) ) assert [type(x) for x in decode_frames(server1())] == [ hyperframe.frame.SettingsFrame, hyperframe.frame.SettingsFrame, ] assert [type(x) for x in decode_frames(server2())] == [ hyperframe.frame.HeadersFrame, ]
def start_h2_client(tctx: Context) -> Tuple[Playbook, FrameFactory]: tctx.client.alpn = b"h2" frame_factory = FrameFactory() playbook = Playbook(http.HttpLayer(tctx, HTTPMode.regular)) assert ( playbook << SendData(tctx.client, Placeholder()) # initial settings frame >> DataReceived(tctx.client, frame_factory.preamble()) >> DataReceived(tctx.client, frame_factory.build_settings_frame({}, ack=True).serialize()) ) return playbook, frame_factory
def test_max_concurrency(tctx): playbook, cff = start_h2_client(tctx) server = Placeholder(Server) req1_bytes = Placeholder(bytes) settings_ack_bytes = Placeholder(bytes) req2_bytes = Placeholder(bytes) playbook.hooks = False sff = FrameFactory() assert (playbook >> DataReceived( tctx.client, cff.build_headers_frame(example_request_headers, flags=["END_STREAM"], stream_id=1).serialize()) << OpenConnection(server) >> reply(None, side_effect=make_h2) << SendData(server, req1_bytes) >> DataReceived( server, sff.build_settings_frame({ h2.settings.SettingCodes.MAX_CONCURRENT_STREAMS: 1 }).serialize()) << SendData( server, settings_ack_bytes) >> DataReceived( tctx.client, cff.build_headers_frame(example_request_headers, flags=["END_STREAM"], stream_id=3).serialize()) # Can't send it upstream yet, all streams in use! >> DataReceived( server, sff.build_headers_frame(example_response_headers, flags=["END_STREAM"], stream_id=1).serialize()) # But now we can! << SendData(server, req2_bytes) << SendData(tctx.client, Placeholder(bytes)) >> DataReceived( server, sff.build_headers_frame(example_response_headers, flags=["END_STREAM"], stream_id=3).serialize()) << SendData( tctx.client, Placeholder(bytes))) settings, req1 = decode_frames(req1_bytes()) settings_ack, = decode_frames(settings_ack_bytes()) req2, = decode_frames(req2_bytes()) assert type(settings) == hyperframe.frame.SettingsFrame assert type(req1) == hyperframe.frame.HeadersFrame assert type(settings_ack) == hyperframe.frame.SettingsFrame assert type(req2) == hyperframe.frame.HeadersFrame assert req1.stream_id == 1 assert req2.stream_id == 3
def h2_frames(draw): ff = FrameFactory() headers1 = ff.build_headers_frame(headers=draw(h2_headers())) headers1.flags.clear() headers1.flags |= draw(h2_flags) headers2 = ff.build_headers_frame(headers=draw(h2_headers()), depends_on=draw(h2_stream_ids), stream_weight=draw(integers(0, 255)), exclusive=draw(booleans())) headers2.flags.clear() headers2.flags |= draw(h2_flags) settings = ff.build_settings_frame(settings=draw( dictionaries( keys=sampled_from(SettingCodes), values=integers(0, 2**32 - 1), max_size=5, )), ack=draw(booleans())) continuation = ff.build_continuation_frame(header_block=ff.encoder.encode( draw(h2_headers())), flags=draw(h2_flags)) goaway = ff.build_goaway_frame(draw(h2_stream_ids)) push_promise = ff.build_push_promise_frame( stream_id=draw(h2_stream_ids_nonzero), promised_stream_id=draw(h2_stream_ids), headers=draw(h2_headers())) rst = ff.build_rst_stream_frame(draw(h2_stream_ids_nonzero)) prio = ff.build_priority_frame( stream_id=draw(h2_stream_ids_nonzero), weight=draw(integers(0, 255)), depends_on=draw(h2_stream_ids), exclusive=draw(booleans()), ) data1 = ff.build_data_frame(draw(binary()), draw(h2_flags)) data2 = ff.build_data_frame(draw(binary()), draw(h2_flags), stream_id=draw(h2_stream_ids_nonzero)) window_update = ff.build_window_update_frame(draw(h2_stream_ids), draw(integers(0, 2**32 - 1))) frames = draw( lists(sampled_from([ headers1, headers2, settings, continuation, goaway, push_promise, rst, prio, data1, data2, window_update ]), min_size=1, max_size=11)) return b"".join(x.serialize() for x in frames)