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()))
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)
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_no_normalization(tctx, normalize): """Test that we don't normalize headers when we just pass them through.""" tctx.options.normalize_outbound_headers = normalize tctx.options.validate_inbound_headers = False server = Placeholder(Server) flow = Placeholder(HTTPFlow) playbook, cff = start_h2_client(tctx) request_headers = list(example_request_headers) + [(b"Should-Not-Be-Capitalized! ", b" :) ")] request_headers_lower = [(k.lower(), v) for (k, v) in request_headers] response_headers = list(example_response_headers) + [(b"Same", b"Here")] response_headers_lower = [(k.lower(), v) for (k, v) in response_headers] initial = Placeholder(bytes) assert ( playbook >> DataReceived(tctx.client, cff.build_headers_frame(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, ] assert hpack.hpack.Decoder().decode(frames[1].data, True) == request_headers_lower if normalize else request_headers sff = FrameFactory() ( playbook >> DataReceived(server, sff.build_headers_frame(response_headers, flags=["END_STREAM"]).serialize()) << http.HttpResponseHeadersHook(flow) >> reply() << http.HttpResponseHook(flow) >> reply() ) if normalize: playbook << Log("Lowercased 'Same' header as uppercase is not allowed with HTTP/2.") hdrs = response_headers_lower if normalize else response_headers assert playbook << SendData(tctx.client, cff.build_headers_frame(hdrs, flags=["END_STREAM"]).serialize()) assert flow().request.headers.fields == ((b"Should-Not-Be-Capitalized! ", b" :) "),) assert flow().response.headers.fields == ((b"Same", b"Here"),)
def test_no_normalization(tctx): """Test that we don't normalize headers when we just pass them through.""" server = Placeholder(Server) flow = Placeholder(HTTPFlow) playbook, cff = start_h2_client(tctx) request_headers = example_request_headers + ( (b"Should-Not-Be-Capitalized! ", b" :) "), ) response_headers = example_response_headers + ( (b"Same", b"Here"), ) initial = Placeholder(bytes) assert ( playbook >> DataReceived(tctx.client, cff.build_headers_frame(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, ] assert hpack.hpack.Decoder().decode(frames[1].data, True) == list(request_headers) sff = FrameFactory() assert ( playbook >> DataReceived(server, sff.build_headers_frame(response_headers, flags=["END_STREAM"]).serialize()) << http.HttpResponseHeadersHook(flow) >> reply() << http.HttpResponseHook(flow) >> reply() << SendData(tctx.client, cff.build_headers_frame(response_headers).serialize() + cff.build_data_frame(b"", flags=["END_STREAM"]).serialize()) ) assert flow().request.headers.fields == ((b"Should-Not-Be-Capitalized! ", b" :) "),) assert flow().response.headers.fields == ((b"Same", b"Here"),)