Return UNKNOWN on GenericRpcHandler failure

A GenericRpcHandler registered on a gRPC Server is not supposed to raise
an exception and if it does so it is considered a programming defect.
However, gRPC is supposed to respond to the client with an UNKNOWN
status code.  Previously, this situation was left unhandled and the
client ended up receiving a response with CANCELLED status code.

This commit fixes the issue https://github.com/grpc/grpc/issues/13629.
pull/13633/head
Mehrdad Afshari 7 years ago
parent 562544151d
commit b030ccae05
  1. 8
      src/python/grpcio/grpc/_server.py
  2. 22
      src/python/grpcio_tests/tests/unit/_invocation_defects_test.py

@ -591,7 +591,13 @@ def _handle_call(rpc_event, generic_handlers, thread_pool,
if not rpc_event.success:
return None, None
if rpc_event.request_call_details.method is not None:
method_handler = _find_method_handler(rpc_event, generic_handlers)
try:
method_handler = _find_method_handler(rpc_event, generic_handlers)
except Exception as e: # pylint: disable=broad-except
details = 'Exception servicing handler: {}'.format(e)
logging.exception(details)
return _reject_rpc(rpc_event, cygrpc.StatusCode.unknown,
b'Error in service handler!'), None
if method_handler is None:
return _reject_rpc(rpc_event, cygrpc.StatusCode.unimplemented,
b'Method not found!'), None

@ -32,6 +32,7 @@ _UNARY_UNARY = '/test/UnaryUnary'
_UNARY_STREAM = '/test/UnaryStream'
_STREAM_UNARY = '/test/StreamUnary'
_STREAM_STREAM = '/test/StreamStream'
_DEFECTIVE_GENERIC_RPC_HANDLER = '/test/DefectiveGenericRpcHandler'
class _Callback(object):
@ -95,6 +96,9 @@ class _Handler(object):
yield request
self._control.control()
def defective_generic_rpc_handler(self):
raise test_control.Defect()
class _MethodHandler(grpc.RpcMethodHandler):
@ -132,6 +136,8 @@ class _GenericHandler(grpc.GenericRpcHandler):
elif handler_call_details.method == _STREAM_STREAM:
return _MethodHandler(True, True, None, None, None, None, None,
self._handler.handle_stream_stream)
elif handler_call_details.method == _DEFECTIVE_GENERIC_RPC_HANDLER:
return self._handler.defective_generic_rpc_handler()
else:
return None
@ -176,6 +182,10 @@ def _stream_stream_multi_callable(channel):
return channel.stream_stream(_STREAM_STREAM)
def _defective_handler_multi_callable(channel):
return channel.unary_unary(_DEFECTIVE_GENERIC_RPC_HANDLER)
class InvocationDefectsTest(unittest.TestCase):
def setUp(self):
@ -235,6 +245,18 @@ class InvocationDefectsTest(unittest.TestCase):
for _ in range(test_constants.STREAM_LENGTH // 2 + 1):
next(response_iterator)
def testDefectiveGenericRpcHandlerUnaryResponse(self):
request = b'\x07\x08'
multi_callable = _defective_handler_multi_callable(self._channel)
with self.assertRaises(grpc.RpcError) as exception_context:
response = multi_callable(
request,
metadata=(('test', 'DefectiveGenericRpcHandlerUnary'),))
self.assertIs(grpc.StatusCode.UNKNOWN,
exception_context.exception.code())
if __name__ == '__main__':
unittest.main(verbosity=2)

Loading…
Cancel
Save