diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py index 9613ac2a114..de9fa4f4f7f 100644 --- a/Lib/asyncio/base_events.py +++ b/Lib/asyncio/base_events.py @@ -102,7 +102,7 @@ def _set_reuseport(sock): 'SO_REUSEPORT defined but not implemented.') -def _ipaddr_info(host, port, family, type, proto): +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'): @@ -151,7 +151,7 @@ def _ipaddr_info(host, port, family, type, proto): 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, 0, 0) + return af, type, proto, '', (host, port, flowinfo, scopeid) else: return af, type, proto, '', (host, port) except OSError: @@ -1348,7 +1348,7 @@ class BaseEventLoop(events.AbstractEventLoop): family=0, type=socket.SOCK_STREAM, proto=0, flags=0, loop): host, port = address[:2] - info = _ipaddr_info(host, port, family, type, proto) + info = _ipaddr_info(host, port, family, type, proto, *address[2:]) if info is not None: # "host" is already a resolved IP. return [info] diff --git a/Lib/test/test_asyncio/test_base_events.py b/Lib/test/test_asyncio/test_base_events.py index 27e21b33d36..f068fc781f5 100644 --- a/Lib/test/test_asyncio/test_base_events.py +++ b/Lib/test/test_asyncio/test_base_events.py @@ -1299,6 +1299,28 @@ class BaseEventLoopWithSelectorTests(test_utils.TestCase): t.close() test_utils.run_briefly(self.loop) # allow transport to close + @patch_socket + def test_create_connection_ipv6_scope(self, m_socket): + m_socket.getaddrinfo = socket.getaddrinfo + sock = m_socket.socket.return_value + sock.family = socket.AF_INET6 + + self.loop._add_reader = mock.Mock() + self.loop._add_reader._is_coroutine = False + self.loop._add_writer = mock.Mock() + self.loop._add_writer._is_coroutine = False + + coro = self.loop.create_connection(asyncio.Protocol, 'fe80::1%1', 80) + t, p = self.loop.run_until_complete(coro) + try: + sock.connect.assert_called_with(('fe80::1', 80, 0, 1)) + _, kwargs = m_socket.socket.call_args + self.assertEqual(kwargs['family'], m_socket.AF_INET6) + self.assertEqual(kwargs['type'], m_socket.SOCK_STREAM) + finally: + t.close() + test_utils.run_briefly(self.loop) # allow transport to close + @patch_socket def test_create_connection_ip_addr(self, m_socket): self._test_create_connection_ip_addr(m_socket, True) diff --git a/Misc/NEWS.d/next/Library/2019-05-11-16-21-29.bpo-35545.FcvJvP.rst b/Misc/NEWS.d/next/Library/2019-05-11-16-21-29.bpo-35545.FcvJvP.rst new file mode 100644 index 00000000000..f4349afefcd --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-05-11-16-21-29.bpo-35545.FcvJvP.rst @@ -0,0 +1,2 @@ +Fix asyncio discarding IPv6 scopes when ensuring hostname resolutions +internally