Skip to content

Commit ac90d8b

Browse files
committed
Make sure we always close transports if connection waiter has failed
1 parent 874555c commit ac90d8b

File tree

3 files changed

+91
-28
lines changed

3 files changed

+91
-28
lines changed

tests/test_tcp.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1255,9 +1255,59 @@ async def client(addr):
12551255
max_clients=1,
12561256
backlog=1) as srv:
12571257

1258-
with self.assertRaises(ssl.SSLCertVerificationError):
1258+
exc_type = ssl.SSLError
1259+
if self.PY37:
1260+
exc_type = ssl.SSLCertVerificationError
1261+
with self.assertRaises(exc_type):
12591262
self.loop.run_until_complete(client(srv.addr))
12601263

1264+
def test_ssl_handshake_timeout(self):
1265+
if self.implementation == 'asyncio':
1266+
raise unittest.SkipTest()
1267+
1268+
# bpo-29970: Check that a connection is aborted if handshake is not
1269+
# completed in timeout period, instead of remaining open indefinitely
1270+
client_sslctx = self._create_client_ssl_context()
1271+
1272+
# silence error logger
1273+
messages = []
1274+
self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx))
1275+
1276+
server_side_aborted = False
1277+
1278+
def server(sock):
1279+
nonlocal server_side_aborted
1280+
try:
1281+
sock.recv_all(1024 * 1024)
1282+
except ConnectionAbortedError:
1283+
server_side_aborted = True
1284+
finally:
1285+
sock.close()
1286+
1287+
async def client(addr):
1288+
await asyncio.wait_for(
1289+
self.loop.create_connection(
1290+
asyncio.Protocol,
1291+
*addr,
1292+
ssl=client_sslctx,
1293+
server_hostname='',
1294+
ssl_handshake_timeout=10.0),
1295+
0.5,
1296+
loop=self.loop)
1297+
1298+
with self.tcp_server(server,
1299+
max_clients=1,
1300+
backlog=1) as srv:
1301+
1302+
with self.assertRaises(asyncio.TimeoutError):
1303+
self.loop.run_until_complete(client(srv.addr))
1304+
1305+
self.assertTrue(server_side_aborted)
1306+
1307+
# Python issue #23197: cancelling a handshake must not raise an
1308+
# exception or log an error, even if the handshake failed
1309+
self.assertEqual(messages, [])
1310+
12611311
def test_ssl_connect_accepted_socket(self):
12621312
server_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
12631313
server_context.load_cert_chain(self.ONLYCERT, self.ONLYKEY)

uvloop/loop.pyx

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,7 +1087,7 @@ cdef class Loop:
10871087
raise OSError(err.errno, 'error while attempting '
10881088
'to bind on address %r: %s'
10891089
% (pyaddr, err.strerror.lower()))
1090-
except:
1090+
except Exception:
10911091
tcp._close()
10921092
raise
10931093

@@ -1625,7 +1625,7 @@ cdef class Loop:
16251625
try:
16261626
tcp._open(sock.fileno())
16271627
tcp.listen(backlog)
1628-
except:
1628+
except Exception:
16291629
tcp._close()
16301630
raise
16311631

@@ -1794,7 +1794,7 @@ cdef class Loop:
17941794
tr._close()
17951795
tr = None
17961796
exceptions.append(exc)
1797-
except:
1797+
except Exception:
17981798
if tr is not None:
17991799
tr._close()
18001800
tr = None
@@ -1832,7 +1832,7 @@ cdef class Loop:
18321832
tr._open(sock.fileno())
18331833
tr._init_protocol()
18341834
await waiter
1835-
except:
1835+
except Exception:
18361836
# It's OK to call `_close()` here, as opposed to
18371837
# `_force_close()` or `close()` as we want to terminate the
18381838
# transport immediately. The `waiter` can only be waken
@@ -1844,7 +1844,11 @@ cdef class Loop:
18441844
tr._attach_fileobj(sock)
18451845

18461846
if ssl:
1847-
await ssl_waiter
1847+
try:
1848+
await ssl_waiter
1849+
except Exception:
1850+
tr._close()
1851+
raise
18481852
return protocol._app_transport, app_protocol
18491853
else:
18501854
return tr, protocol
@@ -1934,7 +1938,7 @@ cdef class Loop:
19341938
raise OSError(errno.EADDRINUSE, msg) from None
19351939
else:
19361940
raise
1937-
except:
1941+
except Exception:
19381942
sock.close()
19391943
raise
19401944

@@ -1957,14 +1961,14 @@ cdef class Loop:
19571961

19581962
try:
19591963
pipe._open(sock.fileno())
1960-
except:
1964+
except Exception:
19611965
pipe._close()
19621966
sock.close()
19631967
raise
19641968

19651969
try:
19661970
pipe.listen(backlog)
1967-
except:
1971+
except Exception:
19681972
pipe._close()
19691973
raise
19701974

@@ -2026,7 +2030,7 @@ cdef class Loop:
20262030
tr.connect(path)
20272031
try:
20282032
await waiter
2029-
except:
2033+
except Exception:
20302034
tr._close()
20312035
raise
20322036

@@ -2049,14 +2053,18 @@ cdef class Loop:
20492053
tr._open(sock.fileno())
20502054
tr._init_protocol()
20512055
await waiter
2052-
except:
2056+
except Exception:
20532057
tr._close()
20542058
raise
20552059

20562060
tr._attach_fileobj(sock)
20572061

20582062
if ssl:
2059-
await ssl_waiter
2063+
try:
2064+
await ssl_waiter
2065+
except Exception:
2066+
tr._close()
2067+
raise
20602068
return protocol._app_transport, app_protocol
20612069
else:
20622070
return tr, protocol
@@ -2408,7 +2416,11 @@ cdef class Loop:
24082416
transport._init_protocol()
24092417
transport._attach_fileobj(sock)
24102418

2411-
await waiter
2419+
try:
2420+
await waiter
2421+
except Exception:
2422+
transport.close()
2423+
raise
24122424

24132425
if ssl:
24142426
return protocol._app_transport, protocol
@@ -2488,7 +2500,7 @@ cdef class Loop:
24882500

24892501
try:
24902502
await waiter
2491-
except:
2503+
except Exception:
24922504
proc.close()
24932505
raise
24942506

@@ -2540,7 +2552,7 @@ cdef class Loop:
25402552
transp._open(pipe.fileno())
25412553
transp._init_protocol()
25422554
await waiter
2543-
except:
2555+
except Exception:
25442556
transp.close()
25452557
raise
25462558
transp._attach_fileobj(pipe)
@@ -2565,7 +2577,7 @@ cdef class Loop:
25652577
transp._open(pipe.fileno())
25662578
transp._init_protocol()
25672579
await waiter
2568-
except:
2580+
except Exception:
25692581
transp.close()
25702582
raise
25712583
transp._attach_fileobj(pipe)
@@ -2803,7 +2815,7 @@ cdef class Loop:
28032815
if sock is not None:
28042816
sock.close()
28052817
exceptions.append(exc)
2806-
except:
2818+
except Exception:
28072819
if sock is not None:
28082820
sock.close()
28092821
raise
@@ -2821,7 +2833,12 @@ cdef class Loop:
28212833
udp._set_waiter(waiter)
28222834
udp._init_protocol()
28232835

2824-
await waiter
2836+
try:
2837+
await waiter
2838+
except Exception:
2839+
udp.close()
2840+
raise
2841+
28252842
return udp, protocol
28262843

28272844
def _asyncgen_finalizer_hook(self, agen):

uvloop/sslproto.pyx

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ class SSLProtocol(object):
424424
self._waiter = waiter
425425
self._loop = loop
426426
self._app_protocol = app_protocol
427-
self._app_transport = _SSLProtocolTransport(self._loop, self)
427+
self._app_transport = None
428428
# _SSLPipe instance (None until the connection is made)
429429
self._sslpipe = None
430430
self._session_established = False
@@ -466,12 +466,6 @@ class SSLProtocol(object):
466466
if self._session_established:
467467
self._session_established = False
468468
self._loop.call_soon(self._app_protocol.connection_lost, exc)
469-
else:
470-
# Most likely an exception occurred while in SSL handshake.
471-
# Just mark the app transport as closed so that its __del__
472-
# doesn't complain.
473-
if self._app_transport is not None:
474-
self._app_transport._closed = True
475469

476470
self._transport = None
477471
self._app_transport = None
@@ -617,8 +611,10 @@ class SSLProtocol(object):
617611
self._extra.update(peercert=peercert,
618612
cipher=sslobj.cipher(),
619613
compression=sslobj.compression(),
620-
ssl_object=sslobj,
621-
)
614+
ssl_object=sslobj)
615+
616+
self._app_transport = _SSLProtocolTransport(self._loop, self)
617+
622618
if self._call_connection_made:
623619
self._app_protocol.connection_made(self._app_transport)
624620
self._wakeup_waiter()
@@ -684,7 +680,7 @@ class SSLProtocol(object):
684680
ConnectionAbortedError)):
685681
if self._loop.get_debug():
686682
aio_logger.debug("%r: %s", self, message, exc_info=True)
687-
else:
683+
elif not isinstance(exc, aio_CancelledError):
688684
self._loop.call_exception_handler({
689685
'message': message,
690686
'exception': exc,

0 commit comments

Comments
 (0)