Merge pull request #23632 from lidizheng/raise-when-bind-failed

Raises an exception when port binding failed
pull/23601/head^2
Lidi Zheng 5 years ago committed by GitHub
commit 70f001565b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      src/python/grpcio/grpc/_common.py
  2. 9
      src/python/grpcio/grpc/_server.py
  3. 9
      src/python/grpcio/grpc/experimental/aio/_server.py
  4. 5
      src/python/grpcio_tests/commands.py
  5. 16
      src/python/grpcio_tests/tests/unit/_server_test.py
  6. 16
      src/python/grpcio_tests/tests_aio/unit/server_test.py

@ -61,6 +61,9 @@ STATUS_CODE_TO_CYGRPC_STATUS_CODE = {
MAXIMUM_WAIT_TIMEOUT = 0.1
_ERROR_MESSAGE_PORT_BINDING_FAILED = 'Failed to bind to address %s; set ' \
'GRPC_VERBOSITY=debug environment variable to see detailed error message.'
def encode(s):
if isinstance(s, bytes):
@ -144,3 +147,22 @@ def wait(wait_fn, wait_complete_fn, timeout=None, spin_cb=None):
return True
_wait_once(wait_fn, remaining, spin_cb)
return False
def validate_port_binding_result(address, port):
"""Validates if the port binding succeed.
If the port returned by Core is 0, the binding is failed. However, in that
case, the Core API doesn't return a detailed failing reason. The best we
can do is raising an exception to prevent further confusion.
Args:
address: The address string to be bound.
port: An int returned by core
"""
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(_ERROR_MESSAGE_PORT_BINDING_FAILED % address)
else:
return port

@ -958,11 +958,14 @@ 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))
return _common.validate_port_binding_result(
address, _add_insecure_port(self._state, _common.encode(address)))
def add_secure_port(self, address, server_credentials):
return _add_secure_port(self._state, _common.encode(address),
server_credentials)
return _common.validate_port_binding_result(
address,
_add_secure_port(self._state, _common.encode(address),
server_credentials))
def start(self):
_start(self._state)

@ -80,7 +80,8 @@ 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))
return _common.validate_port_binding_result(
address, self._server.add_insecure_port(_common.encode(address)))
def add_secure_port(self, address: str,
server_credentials: grpc.ServerCredentials) -> int:
@ -97,8 +98,10 @@ 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),
server_credentials)
return _common.validate_port_binding_result(
address,
self._server.add_secure_port(_common.encode(address),
server_credentials))
async def start(self) -> None:
"""Starts this Server.

@ -227,7 +227,10 @@ class TestGevent(setuptools.Command):
)
BANNED_WINDOWS_TESTS = (
# TODO(https://github.com/grpc/grpc/pull/15411) enable this test
'unit._dns_resolver_test.DNSResolverTest.test_connect_loopback',)
'unit._dns_resolver_test.DNSResolverTest.test_connect_loopback',
# TODO(https://github.com/grpc/grpc/pull/15411) enable this test
'unit._server_test.ServerTest.test_failed_port_binding_exception',
)
description = 'run tests with gevent. Assumes grpc/gevent are installed'
user_options = []

@ -18,6 +18,8 @@ import logging
import grpc
from tests.unit import resources
class _ActualGenericRpcHandler(grpc.GenericRpcHandler):
@ -47,6 +49,20 @@ class ServerTest(unittest.TestCase):
self.assertIn('grpc.GenericRpcHandler',
str(exception_context.exception))
def test_failed_port_binding_exception(self):
server = grpc.server(None, options=(('grpc.so_reuseport', 0),))
port = server.add_insecure_port('localhost:0')
bind_address = "localhost:%d" % port
with self.assertRaises(RuntimeError):
server.add_insecure_port(bind_address)
server_credentials = grpc.ssl_server_credentials([
(resources.private_key(), resources.certificate_chain())
])
with self.assertRaises(RuntimeError):
server.add_secure_port(bind_address, server_credentials)
if __name__ == '__main__':
logging.basicConfig()

@ -15,12 +15,14 @@
import asyncio
import gc
import logging
import socket
import time
import unittest
import grpc
from grpc.experimental import aio
from tests.unit import resources
from tests.unit.framework.common import test_constants
from tests_aio.unit._test_base import AioTestBase
@ -464,6 +466,20 @@ class TestServer(AioTestBase):
self.assertEqual(grpc.StatusCode.INTERNAL, await call.code())
async def test_port_binding_exception(self):
server = aio.server(options=(('grpc.so_reuseport', 0),))
port = server.add_insecure_port('localhost:0')
bind_address = "localhost:%d" % port
with self.assertRaises(RuntimeError):
server.add_insecure_port(bind_address)
server_credentials = grpc.ssl_server_credentials([
(resources.private_key(), resources.certificate_chain())
])
with self.assertRaises(RuntimeError):
server.add_secure_port(bind_address, server_credentials)
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)

Loading…
Cancel
Save