1 # Copyright 2019 The gRPC authors.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
20 _DEFAULT_SOCK_OPTIONS = (socket.SO_REUSEADDR,
21 socket.SO_REUSEPORT) if os.name != 'nt' else (
23 _UNRECOVERABLE_ERRNOS = (errno.EADDRINUSE, errno.ENOSR)
26 def get_socket(bind_address='localhost',
29 sock_options=_DEFAULT_SOCK_OPTIONS):
32 Useful for reserving a port for a system-under-test.
35 bind_address: The host to which to bind.
36 port: The port to which to bind.
37 listen: A boolean value indicating whether or not to listen on the socket.
38 sock_options: A sequence of socket options to apply to the socket.
42 - the address to which the socket is bound
43 - the port to which the socket is bound
44 - the socket object itself
46 _sock_options = sock_options if sock_options else []
48 address_families = (socket.AF_INET6, socket.AF_INET)
50 address_families = (socket.AF_INET)
51 for address_family in address_families:
53 sock = socket.socket(address_family, socket.SOCK_STREAM)
54 for sock_option in _sock_options:
55 sock.setsockopt(socket.SOL_SOCKET, sock_option, 1)
56 sock.bind((bind_address, port))
59 return bind_address, sock.getsockname()[1], sock
60 except OSError as os_error:
62 if os_error.errno in _UNRECOVERABLE_ERRNOS:
66 # For PY2, socket.error is a child class of IOError; for PY3, it is
67 # pointing to OSError. We need this catch to make it 2/3 agnostic.
68 except socket.error: # pylint: disable=duplicate-except
71 raise RuntimeError("Failed to bind to {} with sock_options {}".format(
72 bind_address, sock_options))
75 @contextlib.contextmanager
76 def bound_socket(bind_address='localhost',
79 sock_options=_DEFAULT_SOCK_OPTIONS):
80 """Opens a socket bound to an arbitrary port.
82 Useful for reserving a port for a system-under-test.
85 bind_address: The host to which to bind.
86 port: The port to which to bind.
87 listen: A boolean value indicating whether or not to listen on the socket.
88 sock_options: A sequence of socket options to apply to the socket.
92 - the address to which the socket is bound
93 - the port to which the socket is bound
95 host, port, sock = get_socket(bind_address=bind_address,
98 sock_options=sock_options)