async def test_http1_websocket(nursery: trio._core._run.Nursery) -> None: client_stream, server_stream = trio.testing.memory_stream_pair() server_stream.socket = MockSocket() server = TCPServer(sanity_framework, Config(), server_stream) nursery.start_soon(server.run) client = wsproto.WSConnection(wsproto.ConnectionType.CLIENT) await client_stream.send_all( client.send(wsproto.events.Request(host="hypercorn", target="/"))) client.receive_data(await client_stream.receive_some(1024)) assert list(client.events()) == [ wsproto.events.AcceptConnection(extra_headers=[ (b"date", b"Thu, 01 Jan 1970 01:23:20 GMT"), (b"server", b"hypercorn-h11"), ]) ] await client_stream.send_all( client.send(wsproto.events.BytesMessage(data=SANITY_BODY))) client.receive_data(await client_stream.receive_some(1024)) assert list(client.events()) == [ wsproto.events.TextMessage(data="Hello & Goodbye") ] await client_stream.send_all( client.send(wsproto.events.CloseConnection(code=1000))) client.receive_data(await client_stream.receive_some(1024)) assert list(client.events()) == [ wsproto.events.CloseConnection(code=1000, reason="") ]
async def test_startup_timeout_error(nursery: trio._core._run.Nursery) -> None: config = Config() config.startup_timeout = 0.01 lifespan = Lifespan(SlowLifespanFramework(0.02, trio.sleep), config) # type: ignore nursery.start_soon(lifespan.handle_lifespan) with pytest.raises(LifespanTimeout) as exc_info: await lifespan.wait_for_startup() assert str(exc_info.value).startswith("Timeout whilst awaiting startup")
async def test_http2_request(nursery: trio._core._run.Nursery) -> None: client_stream, server_stream = trio.testing.memory_stream_pair() server_stream.transport_stream = Mock(return_value=PropertyMock( return_value=MockSocket())) server_stream.do_handshake = AsyncMock() server_stream.selected_alpn_protocol = Mock(return_value="h2") server = TCPServer(sanity_framework, Config(), WorkerContext(), server_stream) nursery.start_soon(server.run) client = h2.connection.H2Connection() client.initiate_connection() await client_stream.send_all(client.data_to_send()) stream_id = client.get_next_available_stream_id() client.send_headers( stream_id, [ (":method", "GET"), (":path", "/"), (":authority", "hypercorn"), (":scheme", "https"), ("content-length", "%d" % len(SANITY_BODY)), ], ) client.send_data(stream_id, SANITY_BODY) client.end_stream(stream_id) await client_stream.send_all(client.data_to_send()) events = [] open_ = True while open_: # bytes cast is key otherwise b"" is lost data = bytes(await client_stream.receive_some(1024)) if data == b"": open_ = False h2_events = client.receive_data(data) for event in h2_events: if isinstance(event, h2.events.DataReceived): client.acknowledge_received_data(event.flow_controlled_length, event.stream_id) elif isinstance( event, (h2.events.ConnectionTerminated, h2.events.StreamEnded, h2.events.StreamReset), ): open_ = False break else: events.append(event) await client_stream.send_all(client.data_to_send()) assert isinstance(events[2], h2.events.ResponseReceived) assert events[2].headers == [ (b":status", b"200"), (b"content-length", b"15"), (b"date", b"Thu, 01 Jan 1970 01:23:20 GMT"), (b"server", b"hypercorn-h2"), ]
def _client_stream( nursery: trio._core._run.Nursery, ) -> Generator[trio.testing._memory_streams.MemorySendStream, None, None]: config = Config() config.keep_alive_timeout = KEEP_ALIVE_TIMEOUT client_stream, server_stream = trio.testing.memory_stream_pair() server_stream.socket = MockSocket() server = TCPServer(slow_framework, config, WorkerContext(), server_stream) nursery.start_soon(server.run) yield client_stream
async def spawn_app( nursery: trio._core._run.Nursery, app: ASGIFramework, config: Config, scope: dict, send: Callable[[dict], Awaitable[None]], ) -> Callable[[dict], Awaitable[None]]: app_send_channel, app_receive_channel = trio.open_memory_channel(config.max_app_queue_size) nursery.start_soon(_handle, app, config, scope, app_receive_channel.receive, send) return app_send_channel.send
async def test_http2_websocket(nursery: trio._core._run.Nursery) -> None: client_stream, server_stream = trio.testing.memory_stream_pair() server_stream.transport_stream = Mock(return_value=PropertyMock( return_value=MockSocket())) server_stream.do_handshake = AsyncMock() server_stream.selected_alpn_protocol = Mock(return_value="h2") server = TCPServer(sanity_framework, Config(), WorkerContext(), server_stream) nursery.start_soon(server.run) h2_client = h2.connection.H2Connection() h2_client.initiate_connection() await client_stream.send_all(h2_client.data_to_send()) stream_id = h2_client.get_next_available_stream_id() h2_client.send_headers( stream_id, [ (":method", "CONNECT"), (":path", "/"), (":authority", "hypercorn"), (":scheme", "https"), ("sec-websocket-version", "13"), ], ) await client_stream.send_all(h2_client.data_to_send()) events = h2_client.receive_data(await client_stream.receive_some(1024)) await client_stream.send_all(h2_client.data_to_send()) events = h2_client.receive_data(await client_stream.receive_some(1024)) if not isinstance(events[-1], h2.events.ResponseReceived): events = h2_client.receive_data(await client_stream.receive_some(1024)) assert events[-1].headers == [ (b":status", b"200"), (b"date", b"Thu, 01 Jan 1970 01:23:20 GMT"), (b"server", b"hypercorn-h2"), ] client = wsproto.connection.Connection(wsproto.ConnectionType.CLIENT) h2_client.send_data( stream_id, client.send(wsproto.events.BytesMessage(data=SANITY_BODY))) await client_stream.send_all(h2_client.data_to_send()) events = h2_client.receive_data(await client_stream.receive_some(1024)) client.receive_data(events[0].data) assert list(client.events()) == [ wsproto.events.TextMessage(data="Hello & Goodbye") ] h2_client.send_data(stream_id, client.send(wsproto.events.CloseConnection(code=1000))) await client_stream.send_all(h2_client.data_to_send()) events = h2_client.receive_data(await client_stream.receive_some(1024)) client.receive_data(events[0].data) assert list(client.events()) == [ wsproto.events.CloseConnection(code=1000, reason="") ] await client_stream.send_all(b"")
async def test_h2_flow_control(nursery: trio._core._run.Nursery) -> None: connection = MockConnection() nursery.start_soon(connection.server.handle_connection) stream_id = await connection.send_request( BASIC_HEADERS + [(":method", "GET"), (":path", "/")], {h2.settings.SettingCodes.INITIAL_WINDOW_SIZE: FLOW_WINDOW_SIZE}, ) await connection.end_stream(stream_id) async for event in connection.get_events(): if isinstance(event, h2.events.DataReceived): assert len(event.data) <= FLOW_WINDOW_SIZE elif isinstance(event, h2.events.StreamEnded): await connection.close()
async def test_server_sends_chunked(nursery: trio._core._run.Nursery) -> None: connection = MockConnection(framework=chunked_response_framework) nursery.start_soon(connection.server.handle_connection) stream_id = await connection.send_request( BASIC_HEADERS + [(":method", "GET"), (":path", "/")], {}) await connection.end_stream(stream_id) response_data = b"" async for event in connection.get_events(): if isinstance(event, h2.events.DataReceived): response_data += event.data elif isinstance(event, h2.events.StreamEnded): await connection.close() assert response_data == b"chunked data"
async def test_h2server_upgrade(nursery: trio._core._run.Nursery) -> None: upgrade_request = h11.Request(method="GET", target="/", headers=[("Host", "hypercorn")]) connection = MockConnection(upgrade_request=upgrade_request) nursery.start_soon(connection.server.handle_connection) response_data = b"" async for event in connection.get_events(): if isinstance(event, h2.events.ResponseReceived): assert (b":status", b"200") in event.headers assert (b"server", b"hypercorn-h2") in event.headers assert b"date" in (header[0] for header in event.headers) elif isinstance(event, h2.events.DataReceived): response_data += event.data elif isinstance(event, h2.events.StreamEnded): await connection.close()
async def test_post_response_keep_alive_timeout( nursery: trio._core._run.Nursery) -> None: config = Config() config.keep_alive_timeout = 0.01 connection = MockConnection(config=config) nursery.start_soon(connection.server.handle_connection) stream_id = await connection.send_request( BASIC_HEADERS + [(":method", "GET"), (":path", "/1")], {}) await connection.end_stream(stream_id) async for event in connection.get_events(): if isinstance(event, h2.events.StreamEnded): break # Request ended with trio.move_on_after(2 * config.keep_alive_timeout): pass events = [event async for event in connection.get_events()] assert isinstance(events[-1], h2.events.ConnectionTerminated)
async def test_http1_request(nursery: trio._core._run.Nursery) -> None: client_stream, server_stream = trio.testing.memory_stream_pair() server_stream.socket = MockSocket() server = TCPServer(sanity_framework, Config(), WorkerContext(), server_stream) nursery.start_soon(server.run) client = h11.Connection(h11.CLIENT) await client_stream.send_all( client.send( h11.Request( method="POST", target="/", headers=[ (b"host", b"hypercorn"), (b"connection", b"close"), (b"content-length", b"%d" % len(SANITY_BODY)), ], ))) await client_stream.send_all(client.send(h11.Data(data=SANITY_BODY))) await client_stream.send_all(client.send(h11.EndOfMessage())) events = [] while True: event = client.next_event() if event == h11.NEED_DATA: # bytes cast is key otherwise b"" is lost data = bytes(await client_stream.receive_some(1024)) client.receive_data(data) elif isinstance(event, h11.ConnectionClosed): break else: events.append(event) assert events == [ h11.Response( status_code=200, headers=[ (b"content-length", b"15"), (b"date", b"Thu, 01 Jan 1970 01:23:20 GMT"), (b"server", b"hypercorn-h11"), (b"connection", b"close"), ], http_version=b"1.1", reason=b"", ), h11.Data(data=b"Hello & Goodbye"), h11.EndOfMessage(headers=[]), # type: ignore ]
async def test_pipelining(nursery: trio._core._run.Nursery) -> None: connection = MockConnection() nursery.start_soon(connection.server.handle_connection) streams = [ await connection.send_request( BASIC_HEADERS + [(":method", "GET"), (":path", "/1")], {}), await connection.send_request( BASIC_HEADERS + [(":method", "GET"), (":path", "/1")], {}), ] for stream_id in streams: await connection.end_stream(stream_id) responses = 0 async for event in connection.get_events(): if isinstance(event, h2.events.ResponseReceived): responses += 1 elif isinstance(event, h2.events.StreamEnded) and responses == 2: await connection.close() assert responses == len(streams)
async def test_h2_push(nursery: trio._core._run.Nursery) -> None: connection = MockConnection(framework=push_framework) nursery.start_soon(connection.server.handle_connection) stream_id = await connection.send_request( BASIC_HEADERS + [(":method", "GET"), (":path", "/")], {}) await connection.end_stream(stream_id) push_received = False streams_received = 0 async for event in connection.get_events(): if isinstance(event, h2.events.PushedStreamReceived): assert (b":path", b"/") in event.headers assert (b":method", b"GET") in event.headers assert (b":scheme", b"http") in event.headers assert (b":authority", b"hypercorn") in event.headers push_received = True elif isinstance(event, h2.events.StreamEnded): streams_received += 1 if streams_received == 2: await connection.close() assert push_received
async def test_request(headers: list, body: str, nursery: trio._core._run.Nursery) -> None: connection = MockConnection() nursery.start_soon(connection.server.handle_connection) stream_id = await connection.send_request(headers, {}) if body != "": await connection.send_data(stream_id, body.encode()) await connection.end_stream(stream_id) response_data = b"" async for event in connection.get_events(): if isinstance(event, h2.events.ResponseReceived): assert (b":status", b"200") in event.headers assert (b"server", b"hypercorn-h2") in event.headers assert b"date" in (header[0] for header in event.headers) elif isinstance(event, h2.events.DataReceived): response_data += event.data elif isinstance(event, h2.events.StreamEnded): await connection.close() data = json.loads(response_data.decode()) assert data["request_body"] == body # type: ignore