diff --git a/src/python/grpcio/grpc/_server.py b/src/python/grpcio/grpc/_server.py index d48c4a2a717..3b5a7e69f6c 100644 --- a/src/python/grpcio/grpc/_server.py +++ b/src/python/grpcio/grpc/_server.py @@ -958,11 +958,27 @@ class _Server(grpc.Server): _add_generic_handlers(self._state, generic_rpc_handlers) def add_insecure_port(self, address): - return _add_insecure_port(self._state, _common.encode(address)) + port = _add_insecure_port(self._state, _common.encode(address)) + if port == 0: + # The Core API doesn't return a failure message. The best we can do + # is raising an exception to prevent further confusion. + raise RuntimeError('Failed to bind to address %s; set ' + 'GRPC_VERBOSITY=debug env to see detailed error ' + 'message.' % address) + else: + return port def add_secure_port(self, address, server_credentials): - return _add_secure_port(self._state, _common.encode(address), + port = _add_secure_port(self._state, _common.encode(address), server_credentials) + if port == 0: + # The Core API doesn't return a failure message. The best we can do + # is raising an exception to prevent further confusion. + raise RuntimeError('Failed to bind to address %s; set ' + 'GRPC_VERBOSITY=debug env to see detailed error ' + 'message.' % address) + else: + return port def start(self): _start(self._state) diff --git a/src/python/grpcio/grpc/experimental/aio/_server.py b/src/python/grpcio/grpc/experimental/aio/_server.py index 00c53490c5d..78eadfa3074 100644 --- a/src/python/grpcio/grpc/experimental/aio/_server.py +++ b/src/python/grpcio/grpc/experimental/aio/_server.py @@ -80,7 +80,15 @@ class Server(_base_server.Server): Returns: An integer port on which the server will accept RPC requests. """ - return self._server.add_insecure_port(_common.encode(address)) + port = self._server.add_insecure_port(_common.encode(address)) + if port == 0: + # The Core API doesn't return a failure message. The best we can do + # is raising an exception to prevent further confusion. + raise RuntimeError('Failed to bind to address %s; set ' + 'GRPC_VERBOSITY=debug env to see detailed error ' + 'message.' % address) + else: + return port def add_secure_port(self, address: str, server_credentials: grpc.ServerCredentials) -> int: @@ -97,8 +105,16 @@ class Server(_base_server.Server): Returns: An integer port on which the server will accept RPC requests. """ - return self._server.add_secure_port(_common.encode(address), + port = self._server.add_secure_port(_common.encode(address), server_credentials) + if port == 0: + # The Core API doesn't return a failure message. The best we can do + # is raising an exception to prevent further confusion. + raise RuntimeError('Failed to bind to address %s; set ' + 'GRPC_VERBOSITY=debug env to see detailed error ' + 'message.' % address) + else: + return port async def start(self) -> None: """Starts this Server. diff --git a/src/python/grpcio_tests/tests/unit/_server_test.py b/src/python/grpcio_tests/tests/unit/_server_test.py index bf00158ecdf..0ade8877b13 100644 --- a/src/python/grpcio_tests/tests/unit/_server_test.py +++ b/src/python/grpcio_tests/tests/unit/_server_test.py @@ -18,6 +18,8 @@ import logging import grpc +from tests.unit.framework.common import get_socket + class _ActualGenericRpcHandler(grpc.GenericRpcHandler): @@ -47,6 +49,17 @@ class ServerTest(unittest.TestCase): self.assertIn('grpc.GenericRpcHandler', str(exception_context.exception)) + def test_failed_port_binding_exception(self): + address, _, __ = get_socket() + server = grpc.server(None) + + with self.assertRaises(RuntimeError): + server.add_insecure_port(address) + + with self.assertRaises(RuntimeError): + server.add_secure_port(address, + grpc.ssl_server_credentials(((b'', b''),))) + if __name__ == '__main__': logging.basicConfig() diff --git a/src/python/grpcio_tests/tests_aio/unit/server_test.py b/src/python/grpcio_tests/tests_aio/unit/server_test.py index 43c90dc823e..73c28ef2557 100644 --- a/src/python/grpcio_tests/tests_aio/unit/server_test.py +++ b/src/python/grpcio_tests/tests_aio/unit/server_test.py @@ -21,7 +21,7 @@ import unittest import grpc from grpc.experimental import aio -from tests.unit.framework.common import test_constants +from tests.unit.framework.common import get_socket, test_constants from tests_aio.unit._test_base import AioTestBase _SIMPLE_UNARY_UNARY = '/test/SimpleUnaryUnary' @@ -464,6 +464,17 @@ class TestServer(AioTestBase): self.assertEqual(grpc.StatusCode.INTERNAL, await call.code()) + async def test_port_binding_exception(self): + address, _, __ = get_socket() + server = aio.server() + + with self.assertRaises(RuntimeError): + server.add_insecure_port(address) + + with self.assertRaises(RuntimeError): + server.add_secure_port(address, + grpc.ssl_server_credentials(((b'', b''),))) + if __name__ == '__main__': logging.basicConfig(level=logging.DEBUG)