aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Mehrdad Afshari <mmx@google.com>2017-12-04 14:49:15 -0800
committerGravatar Mehrdad Afshari <mmx@google.com>2017-12-04 15:21:49 -0800
commitb030ccae05068dce61512f803749c8c389d2df70 (patch)
treed436e59e4d496b47f7db3ab199fd26e910295246 /src
parent562544151d75be743273e547308183c3b869643f (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/python/grpcio/grpc/_server.py8
-rw-r--r--src/python/grpcio_tests/tests/unit/_invocation_defects_test.py22
2 files changed, 29 insertions, 1 deletions
diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py
index cd59b07c04..04f943c079 100644
--- a/src/python/grpcio/grpc/_server.py
+++ b/src/python/grpcio/grpc/_server.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
diff --git a/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py b/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py
index 0a1e50c94c..2a1a49ce74 100644
--- a/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py
+++ b/src/python/grpcio_tests/tests/unit/_invocation_defects_test.py
@@ -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)