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_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(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) 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_deadline(loop): stream = H2StreamStub(loop=loop) headers = [ (':method', 'POST'), (':path', '/package.Service/Method'), ('te', 'trailers'), ('content-type', 'application/grpc'), ('grpc-timeout', '10m'), ] async def _method(stream_): await asyncio.sleep(1) methods = {'/package.Service/Method': Handler( _method, Cardinality.UNARY_UNARY, DummyRequest, DummyReply, )} task = loop.create_task( request_handler(methods, stream, headers, ProtoCodec(), release_stream) ) await asyncio.wait_for(task, 0.1, loop=loop) assert stream.__events__ == [ SendHeaders(headers=[ (':status', '200'), ('grpc-status', '4'), # DEADLINE_EXCEEDED ], end_stream=True), Reset(ErrorCodes.NO_ERROR), ]
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) client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(create_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) async with mk_stream(server_h2_stream, 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) client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(create_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 mk_stream(server_h2_stream, request_metadata) as server_stream: server_stream.metadata = request_metadata await server_stream.recv_message() # simulate broken connection to_client_transport.__raise_on_write__(WriteError)
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) client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(create_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) send_trailing_metadata_done = False async with mk_stream(server_h2_stream, request_metadata) as server_stream: await server_stream.send_trailing_metadata(status=Status.UNKNOWN) send_trailing_metadata_done = True assert send_trailing_metadata_done
def _stream_streaming(stub): stream = Stream(stub, '/svc/Method', Cardinality.UNARY_STREAM, DummyRequest, DummyReply, codec=ProtoCodec(), status_details_codec=None, dispatch=_DispatchServerEvents()) stream.metadata = MultiDict() return stream
async def test_exit_and_stream_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) client_h2_stream = client_conn.create_stream() await client_h2_stream.send_request(create_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) async with mk_stream(server_h2_stream, request_metadata) as server_stream: await server_stream.recv_message() # simulating client closing stream await client_h2_stream.reset() to_server_transport.process(server_proc) # we should fail here on this attempt to send something await server_stream.send_message(DummyReply(value='pong'))
def mk_stream(h2_stream, metadata): stream = Stream(h2_stream, '/svc/Method', Cardinality.UNARY_UNARY, DummyRequest, DummyReply, codec=ProtoCodec(), status_details_codec=None, dispatch=_DispatchServerEvents()) stream.metadata = metadata return stream
def __init__(self, *, loop, server_conn=None, recv_type=None, send_type=None, path='/foo/bar', content_type='application/grpc+proto', codec=ProtoCodec(), deadline=None, metadata=None): self.server_conn = server_conn or ServerConn(loop=loop) self.stream_id = (self.server_conn.client_h2c .get_next_available_stream_id()) self.server_conn.client_h2c.send_headers(self.stream_id, [ (':method', 'POST'), (':scheme', 'http'), (':path', path), (':authority', 'test.com'), ('te', 'trailers'), ('content-type', content_type), ]) self.server_conn.client_flush() self.server_h2s = self.server_conn.server_proto.processor.handler.stream assert self.server_h2s self.server_stream = server.Stream( self.server_h2s, Cardinality.UNARY_UNARY, codec, recv_type, send_type, metadata=metadata or {}, deadline=deadline, )
def __init__(self, *, loop, client_conn=None, send_type=None, recv_type=None, path='/foo/bar', codec=ProtoCodec(), cardinality=Cardinality.UNARY_UNARY, connect_time=None, timeout=None, deadline=None, metadata=None): self.client_conn = client_conn or ClientConn(loop=loop) channel = client.Channel(port=-1, loop=loop, codec=codec) self.client_stream = channel.request( path, cardinality, send_type, recv_type, timeout=timeout, deadline=deadline, metadata=metadata, ) self.client_stream._channel = ChannelStub( self.client_conn.client_proto, connect_time=connect_time)
async def test_invalid_method(loop): stream = H2StreamStub(loop=loop) headers = [(':method', 'GET')] await request_handler({}, stream, headers, ProtoCodec(), release_stream) assert stream.__events__ == [ SendHeaders(headers=[(':status', '405')], end_stream=True), Reset(ErrorCodes.NO_ERROR), ]
def mk_stream(h2_stream, metadata): stream = Stream(h2_stream, Cardinality.UNARY_UNARY, DummyRequest, DummyReply, codec=ProtoCodec(), dispatch=_DispatchServerEvents()) stream.metadata = metadata return stream
def _stream_streaming(stub): stream = Stream(stub, Cardinality.UNARY_STREAM, DummyRequest, DummyReply, codec=ProtoCodec(), dispatch=_DispatchServerEvents()) stream.metadata = MultiDict() return stream
def _stream(stub): stream = Stream(stub, '/svc/Method', Cardinality.UNARY_UNARY, DummyRequest, DummyReply, codec=ProtoCodec(), dispatch=_DispatchServerEvents()) stream.metadata = MultiDict() return stream
async def test_connection_error(): class BrokenChannel: def __connect__(self): raise IOError('Intentionally broken connection') stream = Stream(BrokenChannel(), '/foo/bar', MultiDict(), Cardinality.UNARY_UNARY, DummyRequest, DummyReply, codec=ProtoCodec(), dispatch=_DispatchChannelEvents()) with pytest.raises(IOError) as err: async with stream: await stream.send_request() err.match('Intentionally broken connection')
async def test_send_trailing_metadata(loop, svc_type): async with ChannelFor( [svc_type()], codec=ProtoCodec(), status_details_codec=ProtoStatusDetailsCodec(), ) as channel: stub = DummyServiceStub(channel) with pytest.raises(GRPCError) as error: await stub.UnaryUnary(DummyRequest(value='ping')) assert error.value.status is Status.DATA_LOSS assert error.value.message == 'Some data loss occurred' assert error.value.details == [ Help(links=[Help.Link(url='https://example.com')]), ]
async def test_missing_content_type(loop): stream = H2StreamStub(loop=loop) headers = [ (':method', 'POST'), ] await request_handler({}, stream, headers, ProtoCodec(), release_stream) assert stream.__events__ == [ SendHeaders(headers=[ (':status', '415'), ('grpc-status', '2'), # UNKNOWN ('grpc-message', 'Missing content-type header'), ], end_stream=True), Reset(ErrorCodes.NO_ERROR), ]
async def test_missing_te_header(loop): stream = H2StreamStub(loop=loop) headers = [ (':method', 'POST'), ('content-type', 'application/grpc'), ] await request_handler({}, stream, headers, ProtoCodec(), release_stream) assert stream.__events__ == [ SendHeaders(headers=[ (':status', '400'), ('grpc-status', '2'), # UNKNOWN ('grpc-message', 'Required "te: trailers" header is missing'), ], end_stream=True), Reset(ErrorCodes.NO_ERROR), ]
async def test_connection_error(): request = Request('POST', 'http', '/foo/bar', content_type='application/grpc+proto', authority='test.com') class BrokenChannel: def __connect__(self): raise IOError('Intentionally broken connection') stream = Stream(BrokenChannel(), request, ProtoCodec(), DummyRequest, DummyReply) with pytest.raises(IOError) as err: async with stream: await stream.send_request() err.match('Intentionally broken connection')
async def test_missing_method(loop): stream = H2StreamStub(loop=loop) headers = [ (':method', 'POST'), (':path', '/missing.Service/MissingMethod'), ('te', 'trailers'), ('content-type', 'application/grpc'), ] await request_handler({}, stream, headers, ProtoCodec(), release_stream) assert stream.__events__ == [ SendHeaders(headers=[ (':status', '200'), ('grpc-status', '12'), # UNIMPLEMENTED ('grpc-message', 'Method not found'), ], end_stream=True), Reset(ErrorCodes.NO_ERROR), ]
async def test_invalid_grpc_timeout(loop): stream = H2StreamStub(loop=loop) headers = [ (':method', 'POST'), (':path', '/package.Service/Method'), ('te', 'trailers'), ('content-type', 'application/grpc'), ('grpc-timeout', 'invalid'), ] methods = {'/package.Service/Method': object()} await request_handler(methods, stream, headers, ProtoCodec(), release_stream) assert stream.__events__ == [ SendHeaders(headers=[ (':status', '200'), ('grpc-status', '2'), # UNKNOWN ('grpc-message', 'Invalid grpc-timeout header'), ], end_stream=True), Reset(ErrorCodes.NO_ERROR), ]
def grpc_decode(message_bin, message_type=None, codec=ProtoCodec()): message_len = struct.unpack('>I', message_bin[1:5])[0] assert len(message_bin) == message_len + 5 message = codec.decode(message_bin[5:], message_type) return message
def grpc_encode(message, message_type=None, codec=ProtoCodec()): message_bin = codec.encode(message, message_type) header = struct.pack('?', False) + struct.pack('>I', len(message_bin)) return header + message_bin
def test_proto_invalid_type(): codec = ProtoCodec() assert codec.encode(DummyRequest(value='42'), DummyRequest) == \ DummyRequest(value='42').SerializeToString() with pytest.raises(TypeError, match='Message must be of type'): codec.encode(1, DummyRequest)
def _stream_streaming(stub): return Stream(stub, Cardinality.UNARY_STREAM, ProtoCodec(), DummyRequest, DummyReply, metadata=Metadata([]))
async def call_handler(mapping, stream, headers): await request_handler(mapping, stream, headers, ProtoCodec(), None, _DispatchServerEvents(), release_stream)