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)
def test_simple(tctx): playbook, cff = start_h2_client(tctx) flow = Placeholder(HTTPFlow) server = Placeholder(Server) initial = Placeholder(bytes) assert (playbook >> DataReceived( tctx.client, cff.build_headers_frame(example_request_headers, flags=[ "END_STREAM" ]).serialize()) << http.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(flow) >> reply() << OpenConnection(server) >> reply(None, side_effect=make_h2) << SendData(server, initial)) frames = decode_frames(initial()) assert [type(x) for x in frames] == [ hyperframe.frame.SettingsFrame, hyperframe.frame.HeadersFrame, ] sff = FrameFactory() assert ( playbook # a conforming h2 server would send settings first, we disregard this for now. >> DataReceived( server, sff.build_headers_frame(example_response_headers).serialize()) << http.HttpResponseHeadersHook(flow) >> reply() >> DataReceived( server, sff.build_data_frame(b"Hello, World!", flags=["END_STREAM" ]).serialize()) << http.HttpResponseHook(flow) >> reply() << SendData( tctx.client, cff.build_headers_frame(example_response_headers).serialize() + cff.build_data_frame(b"Hello, World!").serialize() + cff.build_data_frame(b"", flags=["END_STREAM"]).serialize())) assert flow().request.url == "http://example.com/" assert flow().response.text == "Hello, World!"
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_response_trailers(tctx: Context, open_h2_server_conn: Server, stream): playbook, cff = start_h2_client(tctx) tctx.server = open_h2_server_conn sff = FrameFactory() def enable_streaming(flow: HTTPFlow): flow.response.stream = bool(stream) flow = Placeholder(HTTPFlow) ( playbook >> DataReceived(tctx.client, cff.build_headers_frame(example_request_headers, flags=["END_STREAM"]).serialize()) << http.HttpRequestHeadersHook(flow) >> reply() << http.HttpRequestHook(flow) >> reply() << SendData(tctx.server, Placeholder(bytes)) # a conforming h2 server would send settings first, we disregard this for now. >> DataReceived(tctx.server, sff.build_headers_frame(example_response_headers).serialize() + sff.build_data_frame(b"Hello, World!").serialize()) << http.HttpResponseHeadersHook(flow) >> reply(side_effect=enable_streaming) ) if stream: playbook << SendData( tctx.client, cff.build_headers_frame(example_response_headers).serialize() + cff.build_data_frame(b"Hello, World!").serialize() ) assert ( playbook >> DataReceived(tctx.server, sff.build_headers_frame(example_response_trailers, flags=["END_STREAM"]).serialize()) << http.HttpResponseHook(flow) ) assert flow().response.trailers del flow().response.trailers["resp-trailer-a"] if stream: assert ( playbook >> reply() << SendData(tctx.client, cff.build_headers_frame(example_response_trailers[1:], flags=["END_STREAM"]).serialize()) ) else: assert ( playbook >> reply() << SendData(tctx.client, cff.build_headers_frame(example_response_headers).serialize() + cff.build_data_frame(b"Hello, World!").serialize() + cff.build_headers_frame(example_response_trailers[1:], flags=["END_STREAM"]).serialize()))