summaryrefslogtreecommitdiffstats
path: root/Lib/asyncio
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2015-10-05 16:15:28 (GMT)
committerGuido van Rossum <guido@python.org>2015-10-05 16:15:28 (GMT)
commitb9bf913ab32d27d221fb765fd90d64d07e926000 (patch)
tree375ff9c2e3ae0287a6b4ae8b00630f04015ba674 /Lib/asyncio
parentd17e9785de023fc425142e0d348f368677b90011 (diff)
downloadcpython-b9bf913ab32d27d221fb765fd90d64d07e926000.zip
cpython-b9bf913ab32d27d221fb765fd90d64d07e926000.tar.gz
cpython-b9bf913ab32d27d221fb765fd90d64d07e926000.tar.bz2
Issue #23972: updates to asyncio datagram API. By Chris Laws.
Diffstat (limited to 'Lib/asyncio')
-rw-r--r--Lib/asyncio/base_events.py174
-rw-r--r--Lib/asyncio/events.py40
2 files changed, 146 insertions, 68 deletions
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index a50e005..af9c881 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -700,75 +700,109 @@ class BaseEventLoop(events.AbstractEventLoop):
@coroutine
def create_datagram_endpoint(self, protocol_factory,
local_addr=None, remote_addr=None, *,
- family=0, proto=0, flags=0):
+ family=0, proto=0, flags=0,
+ reuse_address=None, reuse_port=None,
+ allow_broadcast=None, sock=None):
"""Create datagram connection."""
- if not (local_addr or remote_addr):
- if family == 0:
- raise ValueError('unexpected address family')
- addr_pairs_info = (((family, proto), (None, None)),)
- else:
- # join address by (family, protocol)
- addr_infos = collections.OrderedDict()
- for idx, addr in ((0, local_addr), (1, remote_addr)):
- if addr is not None:
- assert isinstance(addr, tuple) and len(addr) == 2, (
- '2-tuple is expected')
-
- infos = yield from self.getaddrinfo(
- *addr, family=family, type=socket.SOCK_DGRAM,
- proto=proto, flags=flags)
- if not infos:
- raise OSError('getaddrinfo() returned empty list')
-
- for fam, _, pro, _, address in infos:
- key = (fam, pro)
- if key not in addr_infos:
- addr_infos[key] = [None, None]
- addr_infos[key][idx] = address
-
- # each addr has to have info for each (family, proto) pair
- addr_pairs_info = [
- (key, addr_pair) for key, addr_pair in addr_infos.items()
- if not ((local_addr and addr_pair[0] is None) or
- (remote_addr and addr_pair[1] is None))]
-
- if not addr_pairs_info:
- raise ValueError('can not get address information')
-
- exceptions = []
-
- for ((family, proto),
- (local_address, remote_address)) in addr_pairs_info:
- sock = None
+ if sock is not None:
+ if (local_addr or remote_addr or
+ family or proto or flags or
+ reuse_address or reuse_port or allow_broadcast):
+ # show the problematic kwargs in exception msg
+ opts = dict(local_addr=local_addr, remote_addr=remote_addr,
+ family=family, proto=proto, flags=flags,
+ reuse_address=reuse_address, reuse_port=reuse_port,
+ allow_broadcast=allow_broadcast)
+ problems = ', '.join(
+ '{}={}'.format(k, v) for k, v in opts.items() if v)
+ raise ValueError(
+ 'socket modifier keyword arguments can not be used '
+ 'when sock is specified. ({})'.format(problems))
+ sock.setblocking(False)
r_addr = None
- try:
- sock = socket.socket(
- family=family, type=socket.SOCK_DGRAM, proto=proto)
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
- sock.setblocking(False)
-
- if local_addr:
- sock.bind(local_address)
- if remote_addr:
- yield from self.sock_connect(sock, remote_address)
- r_addr = remote_address
- except OSError as exc:
- if sock is not None:
- sock.close()
- exceptions.append(exc)
- except:
- if sock is not None:
- sock.close()
- raise
- else:
- break
else:
- raise exceptions[0]
+ if not (local_addr or remote_addr):
+ if family == 0:
+ raise ValueError('unexpected address family')
+ addr_pairs_info = (((family, proto), (None, None)),)
+ else:
+ # join address by (family, protocol)
+ addr_infos = collections.OrderedDict()
+ for idx, addr in ((0, local_addr), (1, remote_addr)):
+ if addr is not None:
+ assert isinstance(addr, tuple) and len(addr) == 2, (
+ '2-tuple is expected')
+
+ infos = yield from self.getaddrinfo(
+ *addr, family=family, type=socket.SOCK_DGRAM,
+ proto=proto, flags=flags)
+ if not infos:
+ raise OSError('getaddrinfo() returned empty list')
+
+ for fam, _, pro, _, address in infos:
+ key = (fam, pro)
+ if key not in addr_infos:
+ addr_infos[key] = [None, None]
+ addr_infos[key][idx] = address
+
+ # each addr has to have info for each (family, proto) pair
+ addr_pairs_info = [
+ (key, addr_pair) for key, addr_pair in addr_infos.items()
+ if not ((local_addr and addr_pair[0] is None) or
+ (remote_addr and addr_pair[1] is None))]
+
+ if not addr_pairs_info:
+ raise ValueError('can not get address information')
+
+ exceptions = []
+
+ if reuse_address is None:
+ reuse_address = os.name == 'posix' and sys.platform != 'cygwin'
+
+ for ((family, proto),
+ (local_address, remote_address)) in addr_pairs_info:
+ sock = None
+ r_addr = None
+ try:
+ sock = socket.socket(
+ family=family, type=socket.SOCK_DGRAM, proto=proto)
+ if reuse_address:
+ sock.setsockopt(
+ socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ if reuse_port:
+ if not hasattr(socket, 'SO_REUSEPORT'):
+ raise ValueError(
+ 'reuse_port not supported by socket module')
+ else:
+ sock.setsockopt(
+ socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
+ if allow_broadcast:
+ sock.setsockopt(
+ socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
+ sock.setblocking(False)
+
+ if local_addr:
+ sock.bind(local_address)
+ if remote_addr:
+ yield from self.sock_connect(sock, remote_address)
+ r_addr = remote_address
+ except OSError as exc:
+ if sock is not None:
+ sock.close()
+ exceptions.append(exc)
+ except:
+ if sock is not None:
+ sock.close()
+ raise
+ else:
+ break
+ else:
+ raise exceptions[0]
protocol = protocol_factory()
waiter = futures.Future(loop=self)
- transport = self._make_datagram_transport(sock, protocol, r_addr,
- waiter)
+ transport = self._make_datagram_transport(
+ sock, protocol, r_addr, waiter)
if self._debug:
if local_addr:
logger.info("Datagram endpoint local_addr=%r remote_addr=%r "
@@ -804,7 +838,8 @@ class BaseEventLoop(events.AbstractEventLoop):
sock=None,
backlog=100,
ssl=None,
- reuse_address=None):
+ reuse_address=None,
+ reuse_port=None):
"""Create a TCP server.
The host parameter can be a string, in that case the TCP server is bound
@@ -857,8 +892,15 @@ class BaseEventLoop(events.AbstractEventLoop):
continue
sockets.append(sock)
if reuse_address:
- sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
- True)
+ sock.setsockopt(
+ socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
+ if reuse_port:
+ if not hasattr(socket, 'SO_REUSEPORT'):
+ raise ValueError(
+ 'reuse_port not supported by socket module')
+ else:
+ sock.setsockopt(
+ socket.SOL_SOCKET, socket.SO_REUSEPORT, True)
# Disable IPv4/IPv6 dual stack support (enabled by
# default on Linux) which makes a single socket
# listen on both address families.
diff --git a/Lib/asyncio/events.py b/Lib/asyncio/events.py
index 1e42ddd..176a846 100644
--- a/Lib/asyncio/events.py
+++ b/Lib/asyncio/events.py
@@ -297,7 +297,8 @@ class AbstractEventLoop:
def create_server(self, protocol_factory, host=None, port=None, *,
family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE,
- sock=None, backlog=100, ssl=None, reuse_address=None):
+ sock=None, backlog=100, ssl=None, reuse_address=None,
+ reuse_port=None):
"""A coroutine which creates a TCP server bound to host and port.
The return value is a Server object which can be used to stop
@@ -327,6 +328,11 @@ class AbstractEventLoop:
TIME_WAIT state, without waiting for its natural timeout to
expire. If not specified will automatically be set to True on
UNIX.
+
+ reuse_port tells the kernel to allow this endpoint to be bound to
+ the same port as other existing endpoints are bound to, so long as
+ they all set this flag when being created. This option is not
+ supported on Windows.
"""
raise NotImplementedError
@@ -358,7 +364,37 @@ class AbstractEventLoop:
def create_datagram_endpoint(self, protocol_factory,
local_addr=None, remote_addr=None, *,
- family=0, proto=0, flags=0):
+ family=0, proto=0, flags=0,
+ reuse_address=None, reuse_port=None,
+ allow_broadcast=None, sock=None):
+ """A coroutine which creates a datagram endpoint.
+
+ This method will try to establish the endpoint in the background.
+ When successful, the coroutine returns a (transport, protocol) pair.
+
+ protocol_factory must be a callable returning a protocol instance.
+
+ socket family AF_INET or socket.AF_INET6 depending on host (or
+ family if specified), socket type SOCK_DGRAM.
+
+ reuse_address tells the kernel to reuse a local socket in
+ TIME_WAIT state, without waiting for its natural timeout to
+ expire. If not specified it will automatically be set to True on
+ UNIX.
+
+ reuse_port tells the kernel to allow this endpoint to be bound to
+ the same port as other existing endpoints are bound to, so long as
+ they all set this flag when being created. This option is not
+ supported on Windows and some UNIX's. If the
+ :py:data:`~socket.SO_REUSEPORT` constant is not defined then this
+ capability is unsupported.
+
+ allow_broadcast tells the kernel to allow this endpoint to send
+ messages to the broadcast address.
+
+ sock can optionally be specified in order to use a preexisting
+ socket object.
+ """
raise NotImplementedError
# Pipes and subprocesses.