summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2015-01-14 15:56:20 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2015-01-14 15:56:20 (GMT)
commit177e9f0855ca398bf208a7466ed288e2ae22f6d5 (patch)
tree27c994b28ebad69a451db7937c7fed06e37ff37a
parentf651a604075c2dc9a2d7f3d3bd74da374ff8a696 (diff)
downloadcpython-177e9f0855ca398bf208a7466ed288e2ae22f6d5.zip
cpython-177e9f0855ca398bf208a7466ed288e2ae22f6d5.tar.gz
cpython-177e9f0855ca398bf208a7466ed288e2ae22f6d5.tar.bz2
Issue #23197, asyncio: On SSL handshake failure, check if the waiter is
cancelled before setting its exception. * Add unit tests for this case. * Cleanup also sslproto.py
-rw-r--r--Lib/asyncio/selector_events.py2
-rw-r--r--Lib/asyncio/sslproto.py5
-rw-r--r--Lib/test/test_asyncio/test_selector_events.py20
-rw-r--r--Lib/test/test_asyncio/test_sslproto.py45
4 files changed, 65 insertions, 7 deletions
diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py
index b2f29c7..ca86264 100644
--- a/Lib/asyncio/selector_events.py
+++ b/Lib/asyncio/selector_events.py
@@ -750,7 +750,7 @@ class _SelectorSslTransport(_SelectorTransport):
self._loop.remove_reader(self._sock_fd)
self._loop.remove_writer(self._sock_fd)
self._sock.close()
- if self._waiter is not None:
+ if self._waiter is not None and not self._waiter.cancelled():
self._waiter.set_exception(exc)
if isinstance(exc, Exception):
return
diff --git a/Lib/asyncio/sslproto.py b/Lib/asyncio/sslproto.py
index 987c158..541e252 100644
--- a/Lib/asyncio/sslproto.py
+++ b/Lib/asyncio/sslproto.py
@@ -530,10 +530,11 @@ class SSLProtocol(protocols.Protocol):
self._in_handshake = False
sslobj = self._sslpipe.ssl_object
- peercert = None if handshake_exc else sslobj.getpeercert()
try:
if handshake_exc is not None:
raise handshake_exc
+
+ peercert = sslobj.getpeercert()
if not hasattr(self._sslcontext, 'check_hostname'):
# Verify hostname if requested, Python 3.4+ uses check_hostname
# and checks the hostname in do_handshake()
@@ -551,7 +552,7 @@ class SSLProtocol(protocols.Protocol):
self, exc_info=True)
self._transport.close()
if isinstance(exc, Exception):
- if self._waiter is not None:
+ if self._waiter is not None and not self._waiter.cancelled():
self._waiter.set_exception(exc)
return
else:
diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py
index 360327a..64c2e65 100644
--- a/Lib/test/test_asyncio/test_selector_events.py
+++ b/Lib/test/test_asyncio/test_selector_events.py
@@ -1148,16 +1148,28 @@ class SelectorSslTransportTests(test_utils.TestCase):
self.assertTrue(self.sslsock.close.called)
def test_on_handshake_base_exc(self):
+ waiter = asyncio.Future(loop=self.loop)
transport = _SelectorSslTransport(
- self.loop, self.sock, self.protocol, self.sslcontext)
- transport._waiter = asyncio.Future(loop=self.loop)
+ self.loop, self.sock, self.protocol, self.sslcontext, waiter)
exc = BaseException()
self.sslsock.do_handshake.side_effect = exc
with test_utils.disable_logger():
self.assertRaises(BaseException, transport._on_handshake, 0)
self.assertTrue(self.sslsock.close.called)
- self.assertTrue(transport._waiter.done())
- self.assertIs(exc, transport._waiter.exception())
+ self.assertTrue(waiter.done())
+ self.assertIs(exc, waiter.exception())
+
+ def test_cancel_handshake(self):
+ # Python issue #23197: cancelling an handshake must not raise an
+ # exception or log an error, even if the handshake failed
+ waiter = asyncio.Future(loop=self.loop)
+ transport = _SelectorSslTransport(
+ self.loop, self.sock, self.protocol, self.sslcontext, waiter)
+ waiter.cancel()
+ exc = ValueError()
+ self.sslsock.do_handshake.side_effect = exc
+ with test_utils.disable_logger():
+ transport._on_handshake(0)
def test_pause_resume_reading(self):
tr = self._make_one()
diff --git a/Lib/test/test_asyncio/test_sslproto.py b/Lib/test/test_asyncio/test_sslproto.py
new file mode 100644
index 0000000..053fefe
--- /dev/null
+++ b/Lib/test/test_asyncio/test_sslproto.py
@@ -0,0 +1,45 @@
+"""Tests for asyncio/sslproto.py."""
+
+import unittest
+from unittest import mock
+
+import asyncio
+from asyncio import sslproto
+from asyncio import test_utils
+
+
+class SslProtoHandshakeTests(test_utils.TestCase):
+
+ def setUp(self):
+ self.loop = asyncio.new_event_loop()
+ self.set_event_loop(self.loop)
+
+ def test_cancel_handshake(self):
+ # Python issue #23197: cancelling an handshake must not raise an
+ # exception or log an error, even if the handshake failed
+ sslcontext = test_utils.dummy_ssl_context()
+ app_proto = asyncio.Protocol()
+ waiter = asyncio.Future(loop=self.loop)
+ ssl_proto = sslproto.SSLProtocol(self.loop, app_proto, sslcontext,
+ waiter)
+ handshake_fut = asyncio.Future(loop=self.loop)
+
+ def do_handshake(callback):
+ exc = Exception()
+ callback(exc)
+ handshake_fut.set_result(None)
+ return []
+
+ waiter.cancel()
+ transport = mock.Mock()
+ sslpipe = mock.Mock()
+ sslpipe.do_handshake.side_effect = do_handshake
+ with mock.patch('asyncio.sslproto._SSLPipe', return_value=sslpipe):
+ ssl_proto.connection_made(transport)
+
+ with test_utils.disable_logger():
+ self.loop.run_until_complete(handshake_fut)
+
+
+if __name__ == '__main__':
+ unittest.main()