diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 7a6837546d930f..e2a15e2be2ed77 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -99,65 +99,6 @@ def _set_reuseport(sock): 'SO_REUSEPORT defined but not implemented.') -def _ipaddr_info(host, port, family, type, proto, flowinfo=0, scopeid=0): - # Try to skip getaddrinfo if "host" is already an IP. Users might have - # handled name resolution in their own code and pass in resolved IPs. - if not hasattr(socket, 'inet_pton'): - return - - if proto not in {0, socket.IPPROTO_TCP, socket.IPPROTO_UDP} or \ - host is None: - return None - - if type == socket.SOCK_STREAM: - proto = socket.IPPROTO_TCP - elif type == socket.SOCK_DGRAM: - proto = socket.IPPROTO_UDP - else: - return None - - if port is None: - port = 0 - elif isinstance(port, bytes) and port == b'': - port = 0 - elif isinstance(port, str) and port == '': - port = 0 - else: - # If port's a service name like "http", don't skip getaddrinfo. - try: - port = int(port) - except (TypeError, ValueError): - return None - - if family == socket.AF_UNSPEC: - afs = [socket.AF_INET] - if _HAS_IPv6: - afs.append(socket.AF_INET6) - else: - afs = [family] - - if isinstance(host, bytes): - host = host.decode('idna') - if '%' in host: - # Linux's inet_pton doesn't accept an IPv6 zone index after host, - # like '::1%lo0'. - return None - - for af in afs: - try: - socket.inet_pton(af, host) - # The host has already been resolved. - if _HAS_IPv6 and af == socket.AF_INET6: - return af, type, proto, '', (host, port, flowinfo, scopeid) - else: - return af, type, proto, '', (host, port) - except OSError: - pass - - # "host" is not an IP address. - return None - - def _interleave_addrinfos(addrinfos, first_address_family_count=1): """Interleave list of addrinfo tuples by family.""" # Group addresses by family @@ -938,6 +879,15 @@ async def getaddrinfo(self, host, port, *, else: getaddr_func = socket.getaddrinfo + try: + return getaddr_func( + host, port, family, type, proto, + flags | socket.AI_NUMERICHOST | socket.AI_NUMERICSERV, + ) + except socket.gaierror as e: + if e.errno != socket.EAI_NONAME: + raise + return await self.run_in_executor( None, getaddr_func, host, port, family, type, proto, flags) @@ -1514,14 +1464,17 @@ async def create_datagram_endpoint(self, protocol_factory, async def _ensure_resolved(self, address, *, family=0, type=socket.SOCK_STREAM, proto=0, flags=0, loop): - host, port = address[:2] - info = _ipaddr_info(host, port, family, type, proto, *address[2:]) - if info is not None: - # "host" is already a resolved IP. - return [info] - else: - return await loop.getaddrinfo(host, port, family=family, type=type, - proto=proto, flags=flags) + host, port, *rest = address + if not rest: + return await loop.getaddrinfo( + host, port, family=family, type=type, proto=proto, flags=flags, + ) + return [ + (*first, (host, port, *rest)) for *first, (host, port, *_) in + await loop.getaddrinfo( + host, port, family=family, type=type, proto=proto, flags=flags, + ) + ] async def _create_server_getaddrinfo(self, host, port, family, flags): infos = await self._ensure_resolved((host, port), family=family, diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index e59bc25668b4cb..ea44a5bcbb5305 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -36,7 +36,8 @@ def mock_socket_module(): m_socket = mock.MagicMock(spec=socket) for name in ( 'AF_INET', 'AF_INET6', 'AF_UNSPEC', 'IPPROTO_TCP', 'IPPROTO_UDP', - 'SOCK_STREAM', 'SOCK_DGRAM', 'SOL_SOCKET', 'SO_REUSEADDR', 'inet_pton' + 'SOCK_STREAM', 'SOCK_DGRAM', 'SOL_SOCKET', 'SO_REUSEADDR', 'inet_pton', + 'gaierror', 'AI_NUMERICHOST', 'AI_NUMERICSERV', 'EAI_NONAME', ): if hasattr(socket, name): setattr(m_socket, name, getattr(socket, name)) @@ -2059,31 +2060,6 @@ def test_create_datagram_endpoint_nosoreuseport(self, m_socket): self.assertRaises(ValueError, self.loop.run_until_complete, coro) - @patch_socket - def test_create_datagram_endpoint_ip_addr(self, m_socket): - def getaddrinfo(*args, **kw): - self.fail('should not have called getaddrinfo') - - m_socket.getaddrinfo = getaddrinfo - m_socket.socket.return_value.bind = bind = mock.Mock() - self.loop._add_reader = mock.Mock() - - reuseport_supported = hasattr(socket, 'SO_REUSEPORT') - coro = self.loop.create_datagram_endpoint( - lambda: MyDatagramProto(loop=self.loop), - local_addr=('1.2.3.4', 0), - reuse_port=reuseport_supported) - - t, p = self.loop.run_until_complete(coro) - try: - bind.assert_called_with(('1.2.3.4', 0)) - m_socket.socket.assert_called_with(family=m_socket.AF_INET, - proto=m_socket.IPPROTO_UDP, - type=m_socket.SOCK_DGRAM) - finally: - t.close() - test_utils.run_briefly(self.loop) # allow transport to close - def test_accept_connection_retry(self): sock = mock.Mock() sock.accept.side_effect = BlockingIOError() diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index 4bb5d4fb816a9a..4587d19a0cb262 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -172,7 +172,9 @@ def test_sock_connect_resolve_using_socket_params(self, m_gai): con = self.loop.create_task(self.loop.sock_connect(sock, addr)) self.loop.run_until_complete(con) m_gai.assert_called_with( - addr[0], addr[1], sock.family, sock.type, sock.proto, 0) + addr[0], addr[1], sock.family, sock.type, sock.proto, + mock.ANY, + ) self.loop.run_until_complete(con) sock.connect.assert_called_with(('127.0.0.1', 0)) diff --git a/Misc/NEWS.d/next/Library/2022-02-22-10-22-44.bpo-46824.IflDPF.rst b/Misc/NEWS.d/next/Library/2022-02-22-10-22-44.bpo-46824.IflDPF.rst new file mode 100644 index 00000000000000..05ccd84333d847 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-02-22-10-22-44.bpo-46824.IflDPF.rst @@ -0,0 +1 @@ +skip getaddrinfo thread if host is already resolved, using socket.AI_NUMERIC