mirror of
https://github.com/python/cpython.git
synced 2024-12-03 14:55:56 +08:00
asyncio: Only call _check_resolved_address() in debug mode
* _check_resolved_address() is implemented with getaddrinfo() which is slow * If available, use socket.inet_pton() instead of socket.getaddrinfo(), because it is much faster Microbenchmark (timeit) on Fedora 21 (Python 3.4, Linux 3.17, glibc 2.20) to validate the IPV4 address "127.0.0.1" or the IPv6 address "::1": * getaddrinfo() 10.4 usec per loop * inet_pton(): 0.285 usec per loop On glibc older than 2.14, getaddrinfo() always requests the list of all local IP addresses to the kernel (using a NETLINK socket). getaddrinfo() has other known issues, it's better to avoid it when it is possible.
This commit is contained in:
parent
aa41b9b22b
commit
2fc2313038
@ -75,7 +75,11 @@ class _StopError(BaseException):
|
||||
def _check_resolved_address(sock, address):
|
||||
# Ensure that the address is already resolved to avoid the trap of hanging
|
||||
# the entire event loop when the address requires doing a DNS lookup.
|
||||
#
|
||||
# getaddrinfo() is slow (around 10 us per call): this function should only
|
||||
# be called in debug mode
|
||||
family = sock.family
|
||||
|
||||
if family == socket.AF_INET:
|
||||
host, port = address
|
||||
elif family == socket.AF_INET6:
|
||||
@ -83,22 +87,34 @@ def _check_resolved_address(sock, address):
|
||||
else:
|
||||
return
|
||||
|
||||
type_mask = 0
|
||||
if hasattr(socket, 'SOCK_NONBLOCK'):
|
||||
type_mask |= socket.SOCK_NONBLOCK
|
||||
if hasattr(socket, 'SOCK_CLOEXEC'):
|
||||
type_mask |= socket.SOCK_CLOEXEC
|
||||
# Use getaddrinfo(flags=AI_NUMERICHOST) to ensure that the address is
|
||||
# already resolved.
|
||||
try:
|
||||
socket.getaddrinfo(host, port,
|
||||
family=family,
|
||||
type=(sock.type & ~type_mask),
|
||||
proto=sock.proto,
|
||||
flags=socket.AI_NUMERICHOST)
|
||||
except socket.gaierror as err:
|
||||
raise ValueError("address must be resolved (IP address), got %r: %s"
|
||||
% (address, err))
|
||||
# On Windows, socket.inet_pton() is only available since Python 3.4
|
||||
if hasattr(socket, 'inet_pton'):
|
||||
# getaddrinfo() is slow and has known issue: prefer inet_pton()
|
||||
# if available
|
||||
try:
|
||||
socket.inet_pton(family, host)
|
||||
except OSError as exc:
|
||||
raise ValueError("address must be resolved (IP address), "
|
||||
"got host %r: %s"
|
||||
% (host, exc))
|
||||
else:
|
||||
# Use getaddrinfo(flags=AI_NUMERICHOST) to ensure that the address is
|
||||
# already resolved.
|
||||
type_mask = 0
|
||||
if hasattr(socket, 'SOCK_NONBLOCK'):
|
||||
type_mask |= socket.SOCK_NONBLOCK
|
||||
if hasattr(socket, 'SOCK_CLOEXEC'):
|
||||
type_mask |= socket.SOCK_CLOEXEC
|
||||
try:
|
||||
socket.getaddrinfo(host, port,
|
||||
family=family,
|
||||
type=(sock.type & ~type_mask),
|
||||
proto=sock.proto,
|
||||
flags=socket.AI_NUMERICHOST)
|
||||
except socket.gaierror as err:
|
||||
raise ValueError("address must be resolved (IP address), "
|
||||
"got host %r: %s"
|
||||
% (host, err))
|
||||
|
||||
def _raise_stop_error(*args):
|
||||
raise _StopError
|
||||
|
@ -437,7 +437,8 @@ class BaseProactorEventLoop(base_events.BaseEventLoop):
|
||||
|
||||
def sock_connect(self, sock, address):
|
||||
try:
|
||||
base_events._check_resolved_address(sock, address)
|
||||
if self._debug:
|
||||
base_events._check_resolved_address(sock, address)
|
||||
except ValueError as err:
|
||||
fut = futures.Future(loop=self)
|
||||
fut.set_exception(err)
|
||||
|
@ -397,7 +397,8 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop):
|
||||
raise ValueError("the socket must be non-blocking")
|
||||
fut = futures.Future(loop=self)
|
||||
try:
|
||||
base_events._check_resolved_address(sock, address)
|
||||
if self._debug:
|
||||
base_events._check_resolved_address(sock, address)
|
||||
except ValueError as err:
|
||||
fut.set_exception(err)
|
||||
else:
|
||||
|
@ -1437,6 +1437,10 @@ class EventLoopTestsMixin:
|
||||
'selector': self.loop._selector.__class__.__name__})
|
||||
|
||||
def test_sock_connect_address(self):
|
||||
# In debug mode, sock_connect() must ensure that the address is already
|
||||
# resolved (call _check_resolved_address())
|
||||
self.loop.set_debug(True)
|
||||
|
||||
addresses = [(socket.AF_INET, ('www.python.org', 80))]
|
||||
if support.IPV6_ENABLED:
|
||||
addresses.extend((
|
||||
|
Loading…
Reference in New Issue
Block a user