async def test_exit_and_connection_was_broken(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) server_conn = Connection(server_h2c, to_client_transport, loop=loop) server_proc = EventsProcessor(DummyHandler(), server_conn) client_proc = EventsProcessor(DummyHandler(), client_conn) request = Request('POST', 'http', '/', authority='test.com') client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(request.to_headers(), _processor=client_proc) request = SavoysRequest(kyler='cloth') await send_message(client_h2_stream, request, SavoysRequest, end=True) to_server_transport.process(server_proc) server_h2_stream = server_proc.handler.stream request_metadata = Metadata.from_headers(server_proc.handler.headers) with pytest.raises(OSError): async with Stream(server_h2_stream, Cardinality.UNARY_UNARY, SavoysRequest, SavoysReply, metadata=request_metadata) as server_stream: await server_stream.recv_message() # simulate broken connection to_client_transport.__raise_on_write__(OSError)
async def test_send_headers_into_closed_stream(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) server_conn = Connection(server_h2c, to_client_transport, loop=loop) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) client_processor = EventsProcessor(DummyHandler(), client_conn) client_stream = client_conn.create_stream() server_processor = EventsProcessor(DummyHandler(), server_conn) request = Request(method='POST', scheme='http', path='/', content_type='application/grpc+proto', authority='test.com') await client_stream.send_request(request.to_headers(), _processor=client_processor) to_server_transport.process(server_processor) server_stream, = server_processor.streams.values() server_stream._h2_connection.streams.pop(server_stream.id) with pytest.raises(StreamClosedError): await server_stream.send_headers([(':status', '200')])
async def test_send_trailing_metadata_on_closed_stream(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) server_conn = Connection(server_h2c, to_client_transport, loop=loop) server_proc = EventsProcessor(DummyHandler(), server_conn) client_proc = EventsProcessor(DummyHandler(), client_conn) request = Request('POST', 'http', '/', content_type='application/grpc+proto', authority='test.com') client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(request.to_headers(), _processor=client_proc) request = DummyRequest(value='ping') await send_message(client_h2_stream, ProtoCodec(), request, DummyRequest, end=True) to_server_transport.process(server_proc) server_h2_stream = server_proc.handler.stream request_metadata = Metadata.from_headers(server_proc.handler.headers) send_trailing_metadata_done = False async with Stream(server_h2_stream, Cardinality.UNARY_UNARY, ProtoCodec(), DummyRequest, DummyReply, metadata=request_metadata) as server_stream: await server_stream.send_trailing_metadata(status=Status.UNKNOWN) send_trailing_metadata_done = True assert send_trailing_metadata_done
async def test_exit_and_connection_was_closed(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) server_conn = Connection(server_h2c, to_client_transport, loop=loop) server_proc = EventsProcessor(DummyHandler(), server_conn) client_proc = EventsProcessor(DummyHandler(), client_conn) request = Request('POST', 'http', '/', content_type='application/grpc+proto', authority='test.com') client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(request.to_headers(), _processor=client_proc) request = DummyRequest(value='ping') await send_message(client_h2_stream, ProtoCodec(), request, DummyRequest, end=True) to_server_transport.process(server_proc) server_h2_stream = server_proc.handler.stream request_metadata = Metadata.from_headers(server_proc.handler.headers) async with Stream(server_h2_stream, Cardinality.UNARY_UNARY, ProtoCodec(), DummyRequest, DummyReply, metadata=request_metadata) as server_stream: await server_stream.recv_message() client_h2c.close_connection() to_server_transport.process(server_proc) raise ServerError() # should be suppressed
async def test_exit_and_connection_was_broken(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) server_conn = Connection(server_h2c, to_client_transport, loop=loop) server_proc = EventsProcessor(DummyHandler(), server_conn) client_proc = EventsProcessor(DummyHandler(), client_conn) request = Request(method='POST', scheme='http', path='/', content_type='application/grpc+proto', authority='test.com') client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(request.to_headers(), _processor=client_proc) request = DummyRequest(value='ping') await send_message(client_h2_stream, ProtoCodec(), request, DummyRequest, end=True) to_server_transport.process(server_proc) server_h2_stream = server_proc.handler.stream request_metadata = decode_metadata(server_proc.handler.headers) with pytest.raises(WriteError): async with Stream(server_h2_stream, Cardinality.UNARY_UNARY, ProtoCodec(), DummyRequest, DummyReply, metadata=request_metadata) as server_stream: await server_stream.recv_message() # simulate broken connection to_client_transport.__raise_on_write__(WriteError)
async def test_recv_data_larger_than_window_size(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) server_conn = Connection(server_h2c, to_client_transport, loop=loop) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) client_processor = EventsProcessor(DummyHandler(), client_conn) client_stream = client_conn.create_stream() request = Request(method='POST', scheme='http', path='/', content_type='application/grpc+proto', authority='test.com') await client_stream.send_request(request.to_headers(), _processor=client_processor) initial_window = server_h2c.local_settings.initial_window_size assert (client_h2c.local_flow_control_window( client_stream.id) == initial_window) # data should be bigger than window size data = b'0' * (initial_window + 1) size = len(data) # sending less than a full message await client_stream.send_data(data[:initial_window - 1]) # let server process it's events server_processor = EventsProcessor(DummyHandler(), server_conn) for event in to_server_transport.events(): server_processor.process(event) # checking window size was decreased assert client_h2c.local_flow_control_window(client_stream.id) == 1 # simulate that server is waiting for the size of a message and should # acknowledge that size as soon as it will be received server_stream, = server_processor.streams.values() recv_task = loop.create_task(server_stream.recv_data(size)) await asyncio.wait([recv_task], timeout=.01, loop=loop) assert server_stream.__buffer__._read_size == size assert server_stream.__buffer__._size == initial_window - 1 # check that server acknowledged received partial data assert client_h2c.local_flow_control_window(client_stream.id) > 1 # sending remaining data and recv_task should finish await client_stream.send_data(data[initial_window - 1:]) for event in to_server_transport.events(): server_processor.process(event) await asyncio.wait_for(recv_task, 0.01, loop=loop) assert server_stream.__buffer__._size == 0
async def test_stream_release(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) server_conn = Connection(server_h2c, to_client_transport, loop=loop) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) client_processor = EventsProcessor(DummyHandler(), client_conn) client_stream = client_conn.create_stream() server_processor = EventsProcessor(DummyHandler(), server_conn) request = Request(method='POST', scheme='http', path='/', content_type='application/grpc+proto', authority='test.com') assert not client_processor.streams client_release_stream = await client_stream.send_request( request.to_headers(), _processor=client_processor, ) assert client_release_stream and client_processor.streams # sending data and closing stream on the client-side msg = b'message' await client_stream.send_data(msg, end_stream=True) events1 = to_server_transport.process(server_processor) assert any(isinstance(e, StreamEnded) for e in events1), events1 # intentionally sending some stream-specific frame after stream was # half-closed client_h2c.increment_flow_control_window(10, stream_id=client_stream.id) client_conn.flush() events2 = to_server_transport.process(server_processor) assert any(isinstance(e, WindowUpdated) for e in events2), events2 server_stream, = server_processor.streams.values() await server_stream.recv_data(len(msg)) await server_stream.end() events3 = to_client_transport.process(client_processor) assert any(isinstance(e, StreamEnded) for e in events3), events3 # simulating request handler exit by releasing server-side stream server_processor.handler.release_stream() assert not server_processor.streams # simulating call exit by releasing client-side stream assert client_processor.streams client_release_stream() assert not client_processor.streams
async def test_send_data_larger_than_frame_size(loop): client_h2c, server_h2c = create_connections() transport = TransportStub(server_h2c) conn = Connection(client_h2c, transport, loop=loop) stream = conn.create_stream() request = Request('POST', 'http', '/', authority='test.com') processor = EventsProcessor(DummyHandler(), conn) await stream.send_request(request.to_headers(), _processor=processor) await stream.send_data(b'0' * (client_h2c.max_outbound_frame_size + 1))
async def test_initial_window_size_update(loop): client_h2c, server_h2c = create_connections() to_client_transport = TransportStub(client_h2c) server_conn = Connection(server_h2c, to_client_transport, loop=loop) to_server_transport = TransportStub(server_h2c) client_conn = Connection(client_h2c, to_server_transport, loop=loop) client_processor = EventsProcessor(DummyHandler(), client_conn) client_stream = client_conn.create_stream() request = Request(method='POST', scheme='http', path='/', content_type='application/grpc+proto', authority='test.com') await client_stream.send_request(request.to_headers(), _processor=client_processor) # data should be bigger than window size initial_window = server_h2c.local_settings.initial_window_size data = b'0' * (initial_window + 1) assert (client_h2c.local_flow_control_window( client_stream.id) == initial_window) # send_data should wait until settings/window updated send_task = loop.create_task(client_stream.send_data(data)) await asyncio.wait([send_task], timeout=0.01) assert client_h2c.local_flow_control_window(client_stream.id) == 0 # updating settings and window, this should increase stream window size server_h2c.update_settings( {SettingCodes.INITIAL_WINDOW_SIZE: initial_window + 1}) server_h2c.increment_flow_control_window(1, stream_id=None) server_conn.flush() to_client_transport.process(client_processor) assert client_h2c.local_flow_control_window(client_stream.id) == 1 await asyncio.wait([send_task], timeout=0.01) assert send_task.done()