mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-135836: Fix IndexError
in asyncio.create_connection()
(#135875)
This commit is contained in:
parent
135ba86212
commit
9084b15156
3 changed files with 67 additions and 30 deletions
|
@ -1016,38 +1016,43 @@ class BaseEventLoop(events.AbstractEventLoop):
|
||||||
family, type_, proto, _, address = addr_info
|
family, type_, proto, _, address = addr_info
|
||||||
sock = None
|
sock = None
|
||||||
try:
|
try:
|
||||||
sock = socket.socket(family=family, type=type_, proto=proto)
|
try:
|
||||||
sock.setblocking(False)
|
sock = socket.socket(family=family, type=type_, proto=proto)
|
||||||
if local_addr_infos is not None:
|
sock.setblocking(False)
|
||||||
for lfamily, _, _, _, laddr in local_addr_infos:
|
if local_addr_infos is not None:
|
||||||
# skip local addresses of different family
|
for lfamily, _, _, _, laddr in local_addr_infos:
|
||||||
if lfamily != family:
|
# skip local addresses of different family
|
||||||
continue
|
if lfamily != family:
|
||||||
try:
|
continue
|
||||||
sock.bind(laddr)
|
try:
|
||||||
break
|
sock.bind(laddr)
|
||||||
except OSError as exc:
|
break
|
||||||
msg = (
|
except OSError as exc:
|
||||||
f'error while attempting to bind on '
|
msg = (
|
||||||
f'address {laddr!r}: {str(exc).lower()}'
|
f'error while attempting to bind on '
|
||||||
)
|
f'address {laddr!r}: {str(exc).lower()}'
|
||||||
exc = OSError(exc.errno, msg)
|
)
|
||||||
my_exceptions.append(exc)
|
exc = OSError(exc.errno, msg)
|
||||||
else: # all bind attempts failed
|
my_exceptions.append(exc)
|
||||||
if my_exceptions:
|
else: # all bind attempts failed
|
||||||
raise my_exceptions.pop()
|
if my_exceptions:
|
||||||
else:
|
raise my_exceptions.pop()
|
||||||
raise OSError(f"no matching local address with {family=} found")
|
else:
|
||||||
await self.sock_connect(sock, address)
|
raise OSError(f"no matching local address with {family=} found")
|
||||||
return sock
|
await self.sock_connect(sock, address)
|
||||||
except OSError as exc:
|
return sock
|
||||||
my_exceptions.append(exc)
|
except OSError as exc:
|
||||||
if sock is not None:
|
my_exceptions.append(exc)
|
||||||
sock.close()
|
raise
|
||||||
raise
|
|
||||||
except:
|
except:
|
||||||
if sock is not None:
|
if sock is not None:
|
||||||
sock.close()
|
try:
|
||||||
|
sock.close()
|
||||||
|
except OSError:
|
||||||
|
# An error when closing a newly created socket is
|
||||||
|
# not important, but it can overwrite more important
|
||||||
|
# non-OSError error. So ignore it.
|
||||||
|
pass
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
exceptions = my_exceptions = None
|
exceptions = my_exceptions = None
|
||||||
|
|
|
@ -24,6 +24,10 @@ import warnings
|
||||||
MOCK_ANY = mock.ANY
|
MOCK_ANY = mock.ANY
|
||||||
|
|
||||||
|
|
||||||
|
class CustomError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def tearDownModule():
|
def tearDownModule():
|
||||||
asyncio._set_event_loop_policy(None)
|
asyncio._set_event_loop_policy(None)
|
||||||
|
|
||||||
|
@ -1326,6 +1330,31 @@ class BaseEventLoopWithSelectorTests(test_utils.TestCase):
|
||||||
self.assertEqual(len(cm.exception.exceptions), 1)
|
self.assertEqual(len(cm.exception.exceptions), 1)
|
||||||
self.assertIsInstance(cm.exception.exceptions[0], OSError)
|
self.assertIsInstance(cm.exception.exceptions[0], OSError)
|
||||||
|
|
||||||
|
@patch_socket
|
||||||
|
def test_create_connection_connect_non_os_err_close_err(self, m_socket):
|
||||||
|
# Test the case when sock_connect() raises non-OSError exception
|
||||||
|
# and sock.close() raises OSError.
|
||||||
|
async def getaddrinfo(*args, **kw):
|
||||||
|
return [(2, 1, 6, '', ('107.6.106.82', 80))]
|
||||||
|
|
||||||
|
def getaddrinfo_task(*args, **kwds):
|
||||||
|
return self.loop.create_task(getaddrinfo(*args, **kwds))
|
||||||
|
|
||||||
|
self.loop.getaddrinfo = getaddrinfo_task
|
||||||
|
self.loop.sock_connect = mock.Mock()
|
||||||
|
self.loop.sock_connect.side_effect = CustomError
|
||||||
|
sock = mock.Mock()
|
||||||
|
m_socket.socket.return_value = sock
|
||||||
|
sock.close.side_effect = OSError
|
||||||
|
|
||||||
|
coro = self.loop.create_connection(MyProto, 'example.com', 80)
|
||||||
|
self.assertRaises(
|
||||||
|
CustomError, self.loop.run_until_complete, coro)
|
||||||
|
|
||||||
|
coro = self.loop.create_connection(MyProto, 'example.com', 80, all_errors=True)
|
||||||
|
self.assertRaises(
|
||||||
|
CustomError, self.loop.run_until_complete, coro)
|
||||||
|
|
||||||
def test_create_connection_multiple(self):
|
def test_create_connection_multiple(self):
|
||||||
async def getaddrinfo(*args, **kw):
|
async def getaddrinfo(*args, **kw):
|
||||||
return [(2, 1, 6, '', ('0.0.0.1', 80)),
|
return [(2, 1, 6, '', ('0.0.0.1', 80)),
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Fix :exc:`IndexError` in :meth:`asyncio.loop.create_connection` that could
|
||||||
|
occur when non-\ :exc:`OSError` exception is raised during connection and
|
||||||
|
socket's ``close()`` raises :exc:`!OSError`.
|
Loading…
Add table
Add a link
Reference in a new issue