Skip to content

Commit 0105a8f

Browse files
committed
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.
1 parent d3dbdf8 commit 0105a8f

File tree

4 files changed

+40
-18
lines changed

4 files changed

+40
-18
lines changed

asyncio/base_events.py

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -75,30 +75,46 @@ class _StopError(BaseException):
7575
def _check_resolved_address(sock, address):
7676
# Ensure that the address is already resolved to avoid the trap of hanging
7777
# the entire event loop when the address requires doing a DNS lookup.
78+
#
79+
# getaddrinfo() is slow (around 10 us per call): this function should only
80+
# be called in debug mode
7881
family = sock.family
82+
7983
if family == socket.AF_INET:
8084
host, port = address
8185
elif family == socket.AF_INET6:
8286
host, port = address[:2]
8387
else:
8488
return
8589

86-
type_mask = 0
87-
if hasattr(socket, 'SOCK_NONBLOCK'):
88-
type_mask |= socket.SOCK_NONBLOCK
89-
if hasattr(socket, 'SOCK_CLOEXEC'):
90-
type_mask |= socket.SOCK_CLOEXEC
91-
# Use getaddrinfo(flags=AI_NUMERICHOST) to ensure that the address is
92-
# already resolved.
93-
try:
94-
socket.getaddrinfo(host, port,
95-
family=family,
96-
type=(sock.type & ~type_mask),
97-
proto=sock.proto,
98-
flags=socket.AI_NUMERICHOST)
99-
except socket.gaierror as err:
100-
raise ValueError("address must be resolved (IP address), got %r: %s"
101-
% (address, err))
90+
# On Windows, socket.inet_pton() is only available since Python 3.4
91+
if hasattr(socket, 'inet_pton'):
92+
# getaddrinfo() is slow and has known issue: prefer inet_pton()
93+
# if available
94+
try:
95+
socket.inet_pton(family, host)
96+
except OSError as exc:
97+
raise ValueError("address must be resolved (IP address), "
98+
"got host %r: %s"
99+
% (host, exc))
100+
else:
101+
# Use getaddrinfo(flags=AI_NUMERICHOST) to ensure that the address is
102+
# already resolved.
103+
type_mask = 0
104+
if hasattr(socket, 'SOCK_NONBLOCK'):
105+
type_mask |= socket.SOCK_NONBLOCK
106+
if hasattr(socket, 'SOCK_CLOEXEC'):
107+
type_mask |= socket.SOCK_CLOEXEC
108+
try:
109+
socket.getaddrinfo(host, port,
110+
family=family,
111+
type=(sock.type & ~type_mask),
112+
proto=sock.proto,
113+
flags=socket.AI_NUMERICHOST)
114+
except socket.gaierror as err:
115+
raise ValueError("address must be resolved (IP address), "
116+
"got host %r: %s"
117+
% (host, err))
102118

103119
def _raise_stop_error(*args):
104120
raise _StopError

asyncio/proactor_events.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,8 @@ def sock_sendall(self, sock, data):
437437

438438
def sock_connect(self, sock, address):
439439
try:
440-
base_events._check_resolved_address(sock, address)
440+
if self._debug:
441+
base_events._check_resolved_address(sock, address)
441442
except ValueError as err:
442443
fut = futures.Future(loop=self)
443444
fut.set_exception(err)

asyncio/selector_events.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,8 @@ def sock_connect(self, sock, address):
397397
raise ValueError("the socket must be non-blocking")
398398
fut = futures.Future(loop=self)
399399
try:
400-
base_events._check_resolved_address(sock, address)
400+
if self._debug:
401+
base_events._check_resolved_address(sock, address)
401402
except ValueError as err:
402403
fut.set_exception(err)
403404
else:

tests/test_events.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1437,6 +1437,10 @@ def wait():
14371437
'selector': self.loop._selector.__class__.__name__})
14381438

14391439
def test_sock_connect_address(self):
1440+
# In debug mode, sock_connect() must ensure that the address is already
1441+
# resolved (call _check_resolved_address())
1442+
self.loop.set_debug(True)
1443+
14401444
addresses = [(socket.AF_INET, ('www.python.org', 80))]
14411445
if support.IPV6_ENABLED:
14421446
addresses.extend((

0 commit comments

Comments
 (0)