Skip to content

Commit 0153d2e

Browse files
committed
skip getaddrinfo thread if host is already resolved, using socket.AI_NUMERIC...
1 parent 288af84 commit 0153d2e

File tree

3 files changed

+14
-190
lines changed

3 files changed

+14
-190
lines changed

Lib/asyncio/base_events.py

Lines changed: 11 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -96,65 +96,6 @@ def _set_reuseport(sock):
9696
'SO_REUSEPORT defined but not implemented.')
9797

9898

99-
def _ipaddr_info(host, port, family, type, proto, flowinfo=0, scopeid=0):
100-
# Try to skip getaddrinfo if "host" is already an IP. Users might have
101-
# handled name resolution in their own code and pass in resolved IPs.
102-
if not hasattr(socket, 'inet_pton'):
103-
return
104-
105-
if proto not in {0, socket.IPPROTO_TCP, socket.IPPROTO_UDP} or \
106-
host is None:
107-
return None
108-
109-
if type == socket.SOCK_STREAM:
110-
proto = socket.IPPROTO_TCP
111-
elif type == socket.SOCK_DGRAM:
112-
proto = socket.IPPROTO_UDP
113-
else:
114-
return None
115-
116-
if port is None:
117-
port = 0
118-
elif isinstance(port, bytes) and port == b'':
119-
port = 0
120-
elif isinstance(port, str) and port == '':
121-
port = 0
122-
else:
123-
# If port's a service name like "http", don't skip getaddrinfo.
124-
try:
125-
port = int(port)
126-
except (TypeError, ValueError):
127-
return None
128-
129-
if family == socket.AF_UNSPEC:
130-
afs = [socket.AF_INET]
131-
if _HAS_IPv6:
132-
afs.append(socket.AF_INET6)
133-
else:
134-
afs = [family]
135-
136-
if isinstance(host, bytes):
137-
host = host.decode('idna')
138-
if '%' in host:
139-
# Linux's inet_pton doesn't accept an IPv6 zone index after host,
140-
# like '::1%lo0'.
141-
return None
142-
143-
for af in afs:
144-
try:
145-
socket.inet_pton(af, host)
146-
# The host has already been resolved.
147-
if _HAS_IPv6 and af == socket.AF_INET6:
148-
return af, type, proto, '', (host, port, flowinfo, scopeid)
149-
else:
150-
return af, type, proto, '', (host, port)
151-
except OSError:
152-
pass
153-
154-
# "host" is not an IP address.
155-
return None
156-
157-
15899
def _interleave_addrinfos(addrinfos, first_address_family_count=1):
159100
"""Interleave list of addrinfo tuples by family."""
160101
# Group addresses by family
@@ -856,6 +797,15 @@ async def getaddrinfo(self, host, port, *,
856797
else:
857798
getaddr_func = socket.getaddrinfo
858799

800+
try:
801+
return getaddr_func(
802+
host, port, family, type, proto,
803+
flags | socket.AI_NUMERICHOST | socket.AI_NUMERICSERV,
804+
)
805+
except socket.gaierror as e:
806+
if e.errno != socket.EAI_NONAME:
807+
raise
808+
859809
return await self.run_in_executor(
860810
None, getaddr_func, host, port, family, type, proto, flags)
861811

@@ -1392,13 +1342,8 @@ async def _ensure_resolved(self, address, *,
13921342
family=0, type=socket.SOCK_STREAM,
13931343
proto=0, flags=0, loop):
13941344
host, port = address[:2]
1395-
info = _ipaddr_info(host, port, family, type, proto, *address[2:])
1396-
if info is not None:
1397-
# "host" is already a resolved IP.
1398-
return [info]
1399-
else:
1400-
return await loop.getaddrinfo(host, port, family=family, type=type,
1401-
proto=proto, flags=flags)
1345+
return await loop.getaddrinfo(host, port, family=family, type=type,
1346+
proto=proto, flags=flags)
14021347

14031348
async def _create_server_getaddrinfo(self, host, port, family, flags):
14041349
infos = await self._ensure_resolved((host, port), family=family,

Lib/test/test_asyncio/test_base_events.py

Lines changed: 2 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ def mock_socket_module():
3131
m_socket = mock.MagicMock(spec=socket)
3232
for name in (
3333
'AF_INET', 'AF_INET6', 'AF_UNSPEC', 'IPPROTO_TCP', 'IPPROTO_UDP',
34-
'SOCK_STREAM', 'SOCK_DGRAM', 'SOL_SOCKET', 'SO_REUSEADDR', 'inet_pton'
34+
'SOCK_STREAM', 'SOCK_DGRAM', 'SOL_SOCKET', 'SO_REUSEADDR', 'inet_pton',
35+
'gaierror', 'AI_NUMERICHOST', 'AI_NUMERICSERV', 'EAI_NONAME',
3536
):
3637
if hasattr(socket, name):
3738
setattr(m_socket, name, getattr(socket, name))
@@ -50,103 +51,6 @@ def patch_socket(f):
5051
new_callable=mock_socket_module)(f)
5152

5253

53-
class BaseEventTests(test_utils.TestCase):
54-
55-
def test_ipaddr_info(self):
56-
UNSPEC = socket.AF_UNSPEC
57-
INET = socket.AF_INET
58-
INET6 = socket.AF_INET6
59-
STREAM = socket.SOCK_STREAM
60-
DGRAM = socket.SOCK_DGRAM
61-
TCP = socket.IPPROTO_TCP
62-
UDP = socket.IPPROTO_UDP
63-
64-
self.assertEqual(
65-
(INET, STREAM, TCP, '', ('1.2.3.4', 1)),
66-
base_events._ipaddr_info('1.2.3.4', 1, INET, STREAM, TCP))
67-
68-
self.assertEqual(
69-
(INET, STREAM, TCP, '', ('1.2.3.4', 1)),
70-
base_events._ipaddr_info(b'1.2.3.4', 1, INET, STREAM, TCP))
71-
72-
self.assertEqual(
73-
(INET, STREAM, TCP, '', ('1.2.3.4', 1)),
74-
base_events._ipaddr_info('1.2.3.4', 1, UNSPEC, STREAM, TCP))
75-
76-
self.assertEqual(
77-
(INET, DGRAM, UDP, '', ('1.2.3.4', 1)),
78-
base_events._ipaddr_info('1.2.3.4', 1, UNSPEC, DGRAM, UDP))
79-
80-
# Socket type STREAM implies TCP protocol.
81-
self.assertEqual(
82-
(INET, STREAM, TCP, '', ('1.2.3.4', 1)),
83-
base_events._ipaddr_info('1.2.3.4', 1, UNSPEC, STREAM, 0))
84-
85-
# Socket type DGRAM implies UDP protocol.
86-
self.assertEqual(
87-
(INET, DGRAM, UDP, '', ('1.2.3.4', 1)),
88-
base_events._ipaddr_info('1.2.3.4', 1, UNSPEC, DGRAM, 0))
89-
90-
# No socket type.
91-
self.assertIsNone(
92-
base_events._ipaddr_info('1.2.3.4', 1, UNSPEC, 0, 0))
93-
94-
if socket_helper.IPV6_ENABLED:
95-
# IPv4 address with family IPv6.
96-
self.assertIsNone(
97-
base_events._ipaddr_info('1.2.3.4', 1, INET6, STREAM, TCP))
98-
99-
self.assertEqual(
100-
(INET6, STREAM, TCP, '', ('::3', 1, 0, 0)),
101-
base_events._ipaddr_info('::3', 1, INET6, STREAM, TCP))
102-
103-
self.assertEqual(
104-
(INET6, STREAM, TCP, '', ('::3', 1, 0, 0)),
105-
base_events._ipaddr_info('::3', 1, UNSPEC, STREAM, TCP))
106-
107-
# IPv6 address with family IPv4.
108-
self.assertIsNone(
109-
base_events._ipaddr_info('::3', 1, INET, STREAM, TCP))
110-
111-
# IPv6 address with zone index.
112-
self.assertIsNone(
113-
base_events._ipaddr_info('::3%lo0', 1, INET6, STREAM, TCP))
114-
115-
def test_port_parameter_types(self):
116-
# Test obscure kinds of arguments for "port".
117-
INET = socket.AF_INET
118-
STREAM = socket.SOCK_STREAM
119-
TCP = socket.IPPROTO_TCP
120-
121-
self.assertEqual(
122-
(INET, STREAM, TCP, '', ('1.2.3.4', 0)),
123-
base_events._ipaddr_info('1.2.3.4', None, INET, STREAM, TCP))
124-
125-
self.assertEqual(
126-
(INET, STREAM, TCP, '', ('1.2.3.4', 0)),
127-
base_events._ipaddr_info('1.2.3.4', b'', INET, STREAM, TCP))
128-
129-
self.assertEqual(
130-
(INET, STREAM, TCP, '', ('1.2.3.4', 0)),
131-
base_events._ipaddr_info('1.2.3.4', '', INET, STREAM, TCP))
132-
133-
self.assertEqual(
134-
(INET, STREAM, TCP, '', ('1.2.3.4', 1)),
135-
base_events._ipaddr_info('1.2.3.4', '1', INET, STREAM, TCP))
136-
137-
self.assertEqual(
138-
(INET, STREAM, TCP, '', ('1.2.3.4', 1)),
139-
base_events._ipaddr_info('1.2.3.4', b'1', INET, STREAM, TCP))
140-
141-
@patch_socket
142-
def test_ipaddr_info_no_inet_pton(self, m_socket):
143-
del m_socket.inet_pton
144-
self.assertIsNone(base_events._ipaddr_info('1.2.3.4', 1,
145-
socket.AF_INET,
146-
socket.SOCK_STREAM,
147-
socket.IPPROTO_TCP))
148-
149-
15054
class BaseEventLoopTests(test_utils.TestCase):
15155

15256
def setUp(self):
@@ -1827,32 +1731,6 @@ def test_create_datagram_endpoint_nosoreuseport(self, m_socket):
18271731

18281732
self.assertRaises(ValueError, self.loop.run_until_complete, coro)
18291733

1830-
@patch_socket
1831-
def test_create_datagram_endpoint_ip_addr(self, m_socket):
1832-
def getaddrinfo(*args, **kw):
1833-
self.fail('should not have called getaddrinfo')
1834-
1835-
m_socket.getaddrinfo = getaddrinfo
1836-
m_socket.socket.return_value.bind = bind = mock.Mock()
1837-
self.loop._add_reader = mock.Mock()
1838-
self.loop._add_reader._is_coroutine = False
1839-
1840-
reuseport_supported = hasattr(socket, 'SO_REUSEPORT')
1841-
coro = self.loop.create_datagram_endpoint(
1842-
lambda: MyDatagramProto(loop=self.loop),
1843-
local_addr=('1.2.3.4', 0),
1844-
reuse_port=reuseport_supported)
1845-
1846-
t, p = self.loop.run_until_complete(coro)
1847-
try:
1848-
bind.assert_called_with(('1.2.3.4', 0))
1849-
m_socket.socket.assert_called_with(family=m_socket.AF_INET,
1850-
proto=m_socket.IPPROTO_UDP,
1851-
type=m_socket.SOCK_DGRAM)
1852-
finally:
1853-
t.close()
1854-
test_utils.run_briefly(self.loop) # allow transport to close
1855-
18561734
def test_accept_connection_retry(self):
18571735
sock = mock.Mock()
18581736
sock.accept.side_effect = BlockingIOError()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
skip getaddrinfo thread if host is already resolved, using socket.AI_NUMERIC

0 commit comments

Comments
 (0)