From 51d546ae4d563fde608e23c9c337fefd7e95c93f Mon Sep 17 00:00:00 2001 From: Andrew Svetlov Date: Sat, 18 Nov 2017 18:54:05 +0200 Subject: [PATCH] bpo-32069: Drop legacy SSL transport (#4451) * Drop legacy SSL transport * Drop unused import * Fix Windows tests * Drop never executed on Python 3.4+ code --- Lib/asyncio/proactor_events.py | 5 - Lib/asyncio/selector_events.py | 248 ------------ Lib/asyncio/sslproto.py | 20 +- Lib/asyncio/test_utils.py | 5 - Lib/test/test_asyncio/test_events.py | 68 ---- Lib/test/test_asyncio/test_selector_events.py | 363 ------------------ .../2017-11-18-17-09-01.bpo-32069.S0wyy4.rst | 1 + 7 files changed, 5 insertions(+), 705 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2017-11-18-17-09-01.bpo-32069.S0wyy4.rst diff --git a/Lib/asyncio/proactor_events.py b/Lib/asyncio/proactor_events.py index 5e7a39727cc..e35d05b7bf0 100644 --- a/Lib/asyncio/proactor_events.py +++ b/Lib/asyncio/proactor_events.py @@ -392,11 +392,6 @@ class BaseProactorEventLoop(base_events.BaseEventLoop): def _make_ssl_transport(self, rawsock, protocol, sslcontext, waiter=None, *, server_side=False, server_hostname=None, extra=None, server=None): - if not sslproto._is_sslproto_available(): - raise NotImplementedError("Proactor event loop requires Python 3.5" - " or newer (ssl.MemoryBIO) to support " - "SSL") - ssl_protocol = sslproto.SSLProtocol(self, protocol, sslcontext, waiter, server_side, server_hostname) _ProactorSocketTransport(self, rawsock, ssl_protocol, diff --git a/Lib/asyncio/selector_events.py b/Lib/asyncio/selector_events.py index f3b278c9ea7..4baad48ce39 100644 --- a/Lib/asyncio/selector_events.py +++ b/Lib/asyncio/selector_events.py @@ -74,28 +74,12 @@ class BaseSelectorEventLoop(base_events.BaseEventLoop): def _make_ssl_transport(self, rawsock, protocol, sslcontext, waiter=None, *, server_side=False, server_hostname=None, extra=None, server=None): - if not sslproto._is_sslproto_available(): - return self._make_legacy_ssl_transport( - rawsock, protocol, sslcontext, waiter, - server_side=server_side, server_hostname=server_hostname, - extra=extra, server=server) - ssl_protocol = sslproto.SSLProtocol(self, protocol, sslcontext, waiter, server_side, server_hostname) _SelectorSocketTransport(self, rawsock, ssl_protocol, extra=extra, server=server) return ssl_protocol._app_transport - def _make_legacy_ssl_transport(self, rawsock, protocol, sslcontext, - waiter, *, - server_side=False, server_hostname=None, - extra=None, server=None): - # Use the legacy API: SSL_write, SSL_read, etc. The legacy API is used - # on Python 3.4 and older, when ssl.MemoryBIO is not available. - return _SelectorSslTransport( - self, rawsock, protocol, sslcontext, waiter, - server_side, server_hostname, extra, server) - def _make_datagram_transport(self, sock, protocol, address=None, waiter=None, extra=None): return _SelectorDatagramTransport(self, sock, protocol, @@ -848,238 +832,6 @@ class _SelectorSocketTransport(_SelectorTransport): return True -class _SelectorSslTransport(_SelectorTransport): - - _buffer_factory = bytearray - - def __init__(self, loop, rawsock, protocol, sslcontext, waiter=None, - server_side=False, server_hostname=None, - extra=None, server=None): - if ssl is None: - raise RuntimeError('stdlib ssl module not available') - - if not sslcontext: - sslcontext = sslproto._create_transport_context(server_side, server_hostname) - - wrap_kwargs = { - 'server_side': server_side, - 'do_handshake_on_connect': False, - } - if server_hostname and not server_side: - wrap_kwargs['server_hostname'] = server_hostname - sslsock = sslcontext.wrap_socket(rawsock, **wrap_kwargs) - - super().__init__(loop, sslsock, protocol, extra, server) - # the protocol connection is only made after the SSL handshake - self._protocol_connected = False - - self._server_hostname = server_hostname - self._waiter = waiter - self._sslcontext = sslcontext - self._paused = False - - # SSL-specific extra info. (peercert is set later) - self._extra.update(sslcontext=sslcontext) - - if self._loop.get_debug(): - logger.debug("%r starts SSL handshake", self) - start_time = self._loop.time() - else: - start_time = None - self._on_handshake(start_time) - - def _wakeup_waiter(self, exc=None): - if self._waiter is None: - return - if not self._waiter.cancelled(): - if exc is not None: - self._waiter.set_exception(exc) - else: - self._waiter.set_result(None) - self._waiter = None - - def _on_handshake(self, start_time): - try: - self._sock.do_handshake() - except ssl.SSLWantReadError: - self._loop._add_reader(self._sock_fd, - self._on_handshake, start_time) - return - except ssl.SSLWantWriteError: - self._loop._add_writer(self._sock_fd, - self._on_handshake, start_time) - return - except BaseException as exc: - if self._loop.get_debug(): - logger.warning("%r: SSL handshake failed", - self, exc_info=True) - self._loop._remove_reader(self._sock_fd) - self._loop._remove_writer(self._sock_fd) - self._sock.close() - self._wakeup_waiter(exc) - if isinstance(exc, Exception): - return - else: - raise - - self._loop._remove_reader(self._sock_fd) - self._loop._remove_writer(self._sock_fd) - - peercert = self._sock.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() - if (self._server_hostname and - self._sslcontext.verify_mode != ssl.CERT_NONE): - try: - ssl.match_hostname(peercert, self._server_hostname) - except Exception as exc: - if self._loop.get_debug(): - logger.warning("%r: SSL handshake failed " - "on matching the hostname", - self, exc_info=True) - self._sock.close() - self._wakeup_waiter(exc) - return - - # Add extra info that becomes available after handshake. - self._extra.update(peercert=peercert, - cipher=self._sock.cipher(), - compression=self._sock.compression(), - ssl_object=self._sock, - ) - - self._read_wants_write = False - self._write_wants_read = False - self._loop._add_reader(self._sock_fd, self._read_ready) - self._protocol_connected = True - self._loop.call_soon(self._protocol.connection_made, self) - # only wake up the waiter when connection_made() has been called - self._loop.call_soon(self._wakeup_waiter) - - if self._loop.get_debug(): - dt = self._loop.time() - start_time - logger.debug("%r: SSL handshake took %.1f ms", self, dt * 1e3) - - def pause_reading(self): - # XXX This is a bit icky, given the comment at the top of - # _read_ready(). Is it possible to evoke a deadlock? I don't - # know, although it doesn't look like it; write() will still - # accept more data for the buffer and eventually the app will - # call resume_reading() again, and things will flow again. - - if self._closing: - raise RuntimeError('Cannot pause_reading() when closing') - if self._paused: - raise RuntimeError('Already paused') - self._paused = True - self._loop._remove_reader(self._sock_fd) - if self._loop.get_debug(): - logger.debug("%r pauses reading", self) - - def resume_reading(self): - if not self._paused: - raise RuntimeError('Not paused') - self._paused = False - if self._closing: - return - self._loop._add_reader(self._sock_fd, self._read_ready) - if self._loop.get_debug(): - logger.debug("%r resumes reading", self) - - def _read_ready(self): - if self._conn_lost: - return - if self._write_wants_read: - self._write_wants_read = False - self._write_ready() - - if self._buffer: - self._loop._add_writer(self._sock_fd, self._write_ready) - - try: - data = self._sock.recv(self.max_size) - except (BlockingIOError, InterruptedError, ssl.SSLWantReadError): - pass - except ssl.SSLWantWriteError: - self._read_wants_write = True - self._loop._remove_reader(self._sock_fd) - self._loop._add_writer(self._sock_fd, self._write_ready) - except Exception as exc: - self._fatal_error(exc, 'Fatal read error on SSL transport') - else: - if data: - self._protocol.data_received(data) - else: - try: - if self._loop.get_debug(): - logger.debug("%r received EOF", self) - keep_open = self._protocol.eof_received() - if keep_open: - logger.warning('returning true from eof_received() ' - 'has no effect when using ssl') - finally: - self.close() - - def _write_ready(self): - if self._conn_lost: - return - if self._read_wants_write: - self._read_wants_write = False - self._read_ready() - - if not (self._paused or self._closing): - self._loop._add_reader(self._sock_fd, self._read_ready) - - if self._buffer: - try: - n = self._sock.send(self._buffer) - except (BlockingIOError, InterruptedError, ssl.SSLWantWriteError): - n = 0 - except ssl.SSLWantReadError: - n = 0 - self._loop._remove_writer(self._sock_fd) - self._write_wants_read = True - except Exception as exc: - self._loop._remove_writer(self._sock_fd) - self._buffer.clear() - self._fatal_error(exc, 'Fatal write error on SSL transport') - return - - if n: - del self._buffer[:n] - - self._maybe_resume_protocol() # May append to buffer. - - if not self._buffer: - self._loop._remove_writer(self._sock_fd) - if self._closing: - self._call_connection_lost(None) - - def write(self, data): - if not isinstance(data, (bytes, bytearray, memoryview)): - raise TypeError('data argument must be a bytes-like object, ' - 'not %r' % type(data).__name__) - if not data: - return - - if self._conn_lost: - if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES: - logger.warning('socket.send() raised exception.') - self._conn_lost += 1 - return - - if not self._buffer: - self._loop._add_writer(self._sock_fd, self._write_ready) - - # Add it to the buffer. - self._buffer.extend(data) - self._maybe_pause_protocol() - - def can_write_eof(self): - return False - - class _SelectorDatagramTransport(_SelectorTransport): _buffer_factory = collections.deque diff --git a/Lib/asyncio/sslproto.py b/Lib/asyncio/sslproto.py index 53e6d2b667b..c231eb58ee5 100644 --- a/Lib/asyncio/sslproto.py +++ b/Lib/asyncio/sslproto.py @@ -18,25 +18,13 @@ def _create_transport_context(server_side, server_hostname): # Client side may pass ssl=True to use a default # context; in that case the sslcontext passed is None. # The default is secure for client connections. - if hasattr(ssl, 'create_default_context'): - # Python 3.4+: use up-to-date strong settings. - sslcontext = ssl.create_default_context() - if not server_hostname: - sslcontext.check_hostname = False - else: - # Fallback for Python 3.3. - sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - sslcontext.options |= ssl.OP_NO_SSLv2 - sslcontext.options |= ssl.OP_NO_SSLv3 - sslcontext.set_default_verify_paths() - sslcontext.verify_mode = ssl.CERT_REQUIRED + # Python 3.4+: use up-to-date strong settings. + sslcontext = ssl.create_default_context() + if not server_hostname: + sslcontext.check_hostname = False return sslcontext -def _is_sslproto_available(): - return hasattr(ssl, "MemoryBIO") - - # States of an _SSLPipe. _UNWRAPPED = "UNWRAPPED" _DO_HANDSHAKE = "DO_HANDSHAKE" diff --git a/Lib/asyncio/test_utils.py b/Lib/asyncio/test_utils.py index c9b19825f20..c3ddfe37563 100644 --- a/Lib/asyncio/test_utils.py +++ b/Lib/asyncio/test_utils.py @@ -508,8 +508,3 @@ def mock_nonblocking_socket(proto=socket.IPPROTO_TCP, type=socket.SOCK_STREAM, sock.family = family sock.gettimeout.return_value = 0.0 return sock - - -def force_legacy_ssl_support(): - return mock.patch('asyncio.sslproto._is_sslproto_available', - return_value=False) diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 5394ddfce24..1a8bc134296 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -736,10 +736,6 @@ class EventLoopTestsMixin: self._test_create_ssl_connection(httpd, create_connection, peername=httpd.address) - def test_legacy_create_ssl_connection(self): - with test_utils.force_legacy_ssl_support(): - self.test_create_ssl_connection() - @unittest.skipIf(ssl is None, 'No ssl module') @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets') def test_create_ssl_unix_connection(self): @@ -757,10 +753,6 @@ class EventLoopTestsMixin: check_sockname, peername=httpd.address) - def test_legacy_create_ssl_unix_connection(self): - with test_utils.force_legacy_ssl_support(): - self.test_create_ssl_unix_connection() - def test_create_connection_local_addr(self): with test_utils.run_test_server() as httpd: port = support.find_unused_port() @@ -1061,10 +1053,6 @@ class EventLoopTestsMixin: # stop serving server.close() - def test_legacy_create_server_ssl(self): - with test_utils.force_legacy_ssl_support(): - self.test_create_server_ssl() - @unittest.skipIf(ssl is None, 'No ssl module') @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets') def test_create_unix_server_ssl(self): @@ -1096,10 +1084,6 @@ class EventLoopTestsMixin: # stop serving server.close() - def test_legacy_create_unix_server_ssl(self): - with test_utils.force_legacy_ssl_support(): - self.test_create_unix_server_ssl() - @unittest.skipIf(ssl is None, 'No ssl module') def test_create_server_ssl_verify_failed(self): proto = MyProto(loop=self.loop) @@ -1129,10 +1113,6 @@ class EventLoopTestsMixin: self.assertIsNone(proto.transport) server.close() - def test_legacy_create_server_ssl_verify_failed(self): - with test_utils.force_legacy_ssl_support(): - self.test_create_server_ssl_verify_failed() - @unittest.skipIf(ssl is None, 'No ssl module') @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets') def test_create_unix_server_ssl_verify_failed(self): @@ -1163,11 +1143,6 @@ class EventLoopTestsMixin: self.assertIsNone(proto.transport) server.close() - - def test_legacy_create_unix_server_ssl_verify_failed(self): - with test_utils.force_legacy_ssl_support(): - self.test_create_unix_server_ssl_verify_failed() - @unittest.skipIf(ssl is None, 'No ssl module') def test_create_server_ssl_match_failed(self): proto = MyProto(loop=self.loop) @@ -1196,10 +1171,6 @@ class EventLoopTestsMixin: proto.transport.close() server.close() - def test_legacy_create_server_ssl_match_failed(self): - with test_utils.force_legacy_ssl_support(): - self.test_create_server_ssl_match_failed() - @unittest.skipIf(ssl is None, 'No ssl module') @unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets') def test_create_unix_server_ssl_verified(self): @@ -1226,10 +1197,6 @@ class EventLoopTestsMixin: server.close() self.loop.run_until_complete(proto.done) - def test_legacy_create_unix_server_ssl_verified(self): - with test_utils.force_legacy_ssl_support(): - self.test_create_unix_server_ssl_verified() - @unittest.skipIf(ssl is None, 'No ssl module') def test_create_server_ssl_verified(self): proto = MyProto(loop=self.loop) @@ -1259,10 +1226,6 @@ class EventLoopTestsMixin: server.close() self.loop.run_until_complete(proto.done) - def test_legacy_create_server_ssl_verified(self): - with test_utils.force_legacy_ssl_support(): - self.test_create_server_ssl_verified() - def test_create_server_sock(self): proto = asyncio.Future(loop=self.loop) @@ -2159,37 +2122,6 @@ if sys.platform == 'win32': def create_event_loop(self): return asyncio.ProactorEventLoop() - if not sslproto._is_sslproto_available(): - def test_create_ssl_connection(self): - raise unittest.SkipTest("need python 3.5 (ssl.MemoryBIO)") - - def test_create_server_ssl(self): - raise unittest.SkipTest("need python 3.5 (ssl.MemoryBIO)") - - def test_create_server_ssl_verify_failed(self): - raise unittest.SkipTest("need python 3.5 (ssl.MemoryBIO)") - - def test_create_server_ssl_match_failed(self): - raise unittest.SkipTest("need python 3.5 (ssl.MemoryBIO)") - - def test_create_server_ssl_verified(self): - raise unittest.SkipTest("need python 3.5 (ssl.MemoryBIO)") - - def test_legacy_create_ssl_connection(self): - raise unittest.SkipTest("IocpEventLoop incompatible with legacy SSL") - - def test_legacy_create_server_ssl(self): - raise unittest.SkipTest("IocpEventLoop incompatible with legacy SSL") - - def test_legacy_create_server_ssl_verify_failed(self): - raise unittest.SkipTest("IocpEventLoop incompatible with legacy SSL") - - def test_legacy_create_server_ssl_match_failed(self): - raise unittest.SkipTest("IocpEventLoop incompatible with legacy SSL") - - def test_legacy_create_server_ssl_verified(self): - raise unittest.SkipTest("IocpEventLoop incompatible with legacy SSL") - def test_reader_callback(self): raise unittest.SkipTest("IocpEventLoop does not have add_reader()") diff --git a/Lib/test/test_asyncio/test_selector_events.py b/Lib/test/test_asyncio/test_selector_events.py index a3d118e1881..e67afcc7204 100644 --- a/Lib/test/test_asyncio/test_selector_events.py +++ b/Lib/test/test_asyncio/test_selector_events.py @@ -14,7 +14,6 @@ from asyncio import selectors from asyncio import test_utils from asyncio.selector_events import BaseSelectorEventLoop from asyncio.selector_events import _SelectorTransport -from asyncio.selector_events import _SelectorSslTransport from asyncio.selector_events import _SelectorSocketTransport from asyncio.selector_events import _SelectorDatagramTransport @@ -1178,368 +1177,6 @@ class SelectorSocketTransportTests(test_utils.TestCase): remove_writer.assert_called_with(self.sock_fd) -@unittest.skipIf(ssl is None, 'No ssl module') -class SelectorSslTransportTests(test_utils.TestCase): - - def setUp(self): - super().setUp() - self.loop = self.new_test_loop() - self.protocol = test_utils.make_test_protocol(asyncio.Protocol) - self.sock = mock.Mock(socket.socket) - self.sock.fileno.return_value = 7 - self.sslsock = mock.Mock() - self.sslsock.fileno.return_value = 1 - self.sslcontext = mock.Mock() - self.sslcontext.wrap_socket.return_value = self.sslsock - - def ssl_transport(self, waiter=None, server_hostname=None): - transport = _SelectorSslTransport(self.loop, self.sock, self.protocol, - self.sslcontext, waiter=waiter, - server_hostname=server_hostname) - self.addCleanup(close_transport, transport) - return transport - - def _make_one(self, create_waiter=None): - transport = self.ssl_transport() - self.sock.reset_mock() - self.sslsock.reset_mock() - self.sslcontext.reset_mock() - self.loop.reset_counters() - return transport - - def test_on_handshake(self): - waiter = asyncio.Future(loop=self.loop) - tr = self.ssl_transport(waiter=waiter) - self.assertTrue(self.sslsock.do_handshake.called) - self.loop.assert_reader(1, tr._read_ready) - test_utils.run_briefly(self.loop) - self.assertIsNone(waiter.result()) - - def test_on_handshake_reader_retry(self): - self.loop.set_debug(False) - self.sslsock.do_handshake.side_effect = ssl.SSLWantReadError - transport = self.ssl_transport() - self.loop.assert_reader(1, transport._on_handshake, None) - - def test_on_handshake_writer_retry(self): - self.loop.set_debug(False) - self.sslsock.do_handshake.side_effect = ssl.SSLWantWriteError - transport = self.ssl_transport() - self.loop.assert_writer(1, transport._on_handshake, None) - - def test_on_handshake_exc(self): - exc = ValueError() - self.sslsock.do_handshake.side_effect = exc - with test_utils.disable_logger(): - waiter = asyncio.Future(loop=self.loop) - self.ssl_transport(waiter=waiter) - self.assertTrue(waiter.done()) - self.assertIs(exc, waiter.exception()) - self.assertTrue(self.sslsock.close.called) - - def test_on_handshake_base_exc(self): - waiter = asyncio.Future(loop=self.loop) - transport = self.ssl_transport(waiter=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(waiter.done()) - self.assertIs(exc, waiter.exception()) - - def test_cancel_handshake(self): - # Python issue #23197: cancelling a handshake must not raise an - # exception or log an error, even if the handshake failed - waiter = asyncio.Future(loop=self.loop) - transport = self.ssl_transport(waiter=waiter) - waiter.cancel() - exc = ValueError() - self.sslsock.do_handshake.side_effect = exc - with test_utils.disable_logger(): - transport._on_handshake(0) - transport.close() - test_utils.run_briefly(self.loop) - - def test_pause_resume_reading(self): - tr = self._make_one() - self.assertFalse(tr._paused) - self.loop.assert_reader(1, tr._read_ready) - tr.pause_reading() - self.assertTrue(tr._paused) - self.assertFalse(1 in self.loop.readers) - tr.resume_reading() - self.assertFalse(tr._paused) - self.loop.assert_reader(1, tr._read_ready) - with self.assertRaises(RuntimeError): - tr.resume_reading() - - def test_write(self): - transport = self._make_one() - transport.write(b'data') - self.assertEqual(list_to_buffer([b'data']), transport._buffer) - - def test_write_bytearray(self): - transport = self._make_one() - data = bytearray(b'data') - transport.write(data) - self.assertEqual(list_to_buffer([b'data']), transport._buffer) - self.assertEqual(data, bytearray(b'data')) # Hasn't been mutated. - self.assertIsNot(data, transport._buffer) # Hasn't been incorporated. - - def test_write_memoryview(self): - transport = self._make_one() - data = memoryview(b'data') - transport.write(data) - self.assertEqual(list_to_buffer([b'data']), transport._buffer) - - def test_write_no_data(self): - transport = self._make_one() - transport._buffer.extend(b'data') - transport.write(b'') - self.assertEqual(list_to_buffer([b'data']), transport._buffer) - - def test_write_str(self): - transport = self._make_one() - self.assertRaises(TypeError, transport.write, 'str') - - def test_write_closing(self): - transport = self._make_one() - transport.close() - self.assertEqual(transport._conn_lost, 1) - transport.write(b'data') - self.assertEqual(transport._conn_lost, 2) - - @mock.patch('asyncio.selector_events.logger') - def test_write_exception(self, m_log): - transport = self._make_one() - transport._conn_lost = 1 - transport.write(b'data') - self.assertEqual(transport._buffer, list_to_buffer()) - transport.write(b'data') - transport.write(b'data') - transport.write(b'data') - transport.write(b'data') - m_log.warning.assert_called_with('socket.send() raised exception.') - - def test_read_ready_recv(self): - self.sslsock.recv.return_value = b'data' - transport = self._make_one() - transport._read_ready() - self.assertTrue(self.sslsock.recv.called) - self.assertEqual((b'data',), self.protocol.data_received.call_args[0]) - - def test_read_ready_write_wants_read(self): - self.loop._add_writer = mock.Mock() - self.sslsock.recv.side_effect = BlockingIOError - transport = self._make_one() - transport._write_wants_read = True - transport._write_ready = mock.Mock() - transport._buffer.extend(b'data') - transport._read_ready() - - self.assertFalse(transport._write_wants_read) - transport._write_ready.assert_called_with() - self.loop._add_writer.assert_called_with( - transport._sock_fd, transport._write_ready) - - def test_read_ready_recv_eof(self): - self.sslsock.recv.return_value = b'' - transport = self._make_one() - transport.close = mock.Mock() - transport._read_ready() - transport.close.assert_called_with() - self.protocol.eof_received.assert_called_with() - - def test_read_ready_recv_conn_reset(self): - err = self.sslsock.recv.side_effect = ConnectionResetError() - transport = self._make_one() - transport._force_close = mock.Mock() - with test_utils.disable_logger(): - transport._read_ready() - transport._force_close.assert_called_with(err) - - def test_read_ready_recv_retry(self): - self.sslsock.recv.side_effect = ssl.SSLWantReadError - transport = self._make_one() - transport._read_ready() - self.assertTrue(self.sslsock.recv.called) - self.assertFalse(self.protocol.data_received.called) - - self.sslsock.recv.side_effect = BlockingIOError - transport._read_ready() - self.assertFalse(self.protocol.data_received.called) - - self.sslsock.recv.side_effect = InterruptedError - transport._read_ready() - self.assertFalse(self.protocol.data_received.called) - - def test_read_ready_recv_write(self): - self.loop._remove_reader = mock.Mock() - self.loop._add_writer = mock.Mock() - self.sslsock.recv.side_effect = ssl.SSLWantWriteError - transport = self._make_one() - transport._read_ready() - self.assertFalse(self.protocol.data_received.called) - self.assertTrue(transport._read_wants_write) - - self.loop._remove_reader.assert_called_with(transport._sock_fd) - self.loop._add_writer.assert_called_with( - transport._sock_fd, transport._write_ready) - - def test_read_ready_recv_exc(self): - err = self.sslsock.recv.side_effect = OSError() - transport = self._make_one() - transport._fatal_error = mock.Mock() - transport._read_ready() - transport._fatal_error.assert_called_with( - err, - 'Fatal read error on SSL transport') - - def test_write_ready_send(self): - self.sslsock.send.return_value = 4 - transport = self._make_one() - transport._buffer = list_to_buffer([b'data']) - transport._write_ready() - self.assertEqual(list_to_buffer(), transport._buffer) - self.assertTrue(self.sslsock.send.called) - - def test_write_ready_send_none(self): - self.sslsock.send.return_value = 0 - transport = self._make_one() - transport._buffer = list_to_buffer([b'data1', b'data2']) - transport._write_ready() - self.assertTrue(self.sslsock.send.called) - self.assertEqual(list_to_buffer([b'data1data2']), transport._buffer) - - def test_write_ready_send_partial(self): - self.sslsock.send.return_value = 2 - transport = self._make_one() - transport._buffer = list_to_buffer([b'data1', b'data2']) - transport._write_ready() - self.assertTrue(self.sslsock.send.called) - self.assertEqual(list_to_buffer([b'ta1data2']), transport._buffer) - - def test_write_ready_send_closing_partial(self): - self.sslsock.send.return_value = 2 - transport = self._make_one() - transport._buffer = list_to_buffer([b'data1', b'data2']) - transport._write_ready() - self.assertTrue(self.sslsock.send.called) - self.assertFalse(self.sslsock.close.called) - - def test_write_ready_send_closing(self): - self.sslsock.send.return_value = 4 - transport = self._make_one() - transport._buffer = list_to_buffer([b'data']) - transport.close() - transport._write_ready() - self.protocol.connection_lost.assert_called_with(None) - - def test_write_ready_send_closing_empty_buffer(self): - self.sslsock.send.return_value = 4 - call_soon = self.loop.call_soon = mock.Mock() - transport = self._make_one() - transport._buffer = list_to_buffer() - transport.close() - transport._write_ready() - call_soon.assert_called_with(transport._call_connection_lost, None) - - def test_write_ready_send_retry(self): - transport = self._make_one() - transport._buffer = list_to_buffer([b'data']) - - self.sslsock.send.side_effect = ssl.SSLWantWriteError - transport._write_ready() - self.assertEqual(list_to_buffer([b'data']), transport._buffer) - - self.sslsock.send.side_effect = BlockingIOError() - transport._write_ready() - self.assertEqual(list_to_buffer([b'data']), transport._buffer) - - def test_write_ready_send_read(self): - transport = self._make_one() - transport._buffer = list_to_buffer([b'data']) - - self.loop._remove_writer = mock.Mock() - self.sslsock.send.side_effect = ssl.SSLWantReadError - transport._write_ready() - self.assertFalse(self.protocol.data_received.called) - self.assertTrue(transport._write_wants_read) - self.loop._remove_writer.assert_called_with(transport._sock_fd) - - def test_write_ready_send_exc(self): - err = self.sslsock.send.side_effect = OSError() - - transport = self._make_one() - transport._buffer = list_to_buffer([b'data']) - transport._fatal_error = mock.Mock() - transport._write_ready() - transport._fatal_error.assert_called_with( - err, - 'Fatal write error on SSL transport') - self.assertEqual(list_to_buffer(), transport._buffer) - - def test_write_ready_read_wants_write(self): - self.loop._add_reader = mock.Mock() - self.sslsock.send.side_effect = BlockingIOError - transport = self._make_one() - transport._read_wants_write = True - transport._read_ready = mock.Mock() - transport._write_ready() - - self.assertFalse(transport._read_wants_write) - transport._read_ready.assert_called_with() - self.loop._add_reader.assert_called_with( - transport._sock_fd, transport._read_ready) - - def test_write_eof(self): - tr = self._make_one() - self.assertFalse(tr.can_write_eof()) - self.assertRaises(NotImplementedError, tr.write_eof) - - def check_close(self): - tr = self._make_one() - tr.close() - - self.assertTrue(tr.is_closing()) - self.assertEqual(1, self.loop.remove_reader_count[1]) - self.assertEqual(tr._conn_lost, 1) - - tr.close() - self.assertEqual(tr._conn_lost, 1) - self.assertEqual(1, self.loop.remove_reader_count[1]) - - test_utils.run_briefly(self.loop) - - def test_close(self): - self.check_close() - self.assertTrue(self.protocol.connection_made.called) - self.assertTrue(self.protocol.connection_lost.called) - - def test_close_not_connected(self): - self.sslsock.do_handshake.side_effect = ssl.SSLWantReadError - self.check_close() - self.assertFalse(self.protocol.connection_made.called) - self.assertFalse(self.protocol.connection_lost.called) - - @unittest.skipIf(ssl is None, 'No SSL support') - def test_server_hostname(self): - self.ssl_transport(server_hostname='localhost') - self.sslcontext.wrap_socket.assert_called_with( - self.sock, do_handshake_on_connect=False, server_side=False, - server_hostname='localhost') - - -class SelectorSslWithoutSslTransportTests(unittest.TestCase): - - @mock.patch('asyncio.selector_events.ssl', None) - def test_ssl_transport_requires_ssl_module(self): - Mock = mock.Mock - with self.assertRaises(RuntimeError): - _SelectorSslTransport(Mock(), Mock(), Mock(), Mock()) - - class SelectorDatagramTransportTests(test_utils.TestCase): def setUp(self): diff --git a/Misc/NEWS.d/next/Library/2017-11-18-17-09-01.bpo-32069.S0wyy4.rst b/Misc/NEWS.d/next/Library/2017-11-18-17-09-01.bpo-32069.S0wyy4.rst new file mode 100644 index 00000000000..2d695391e20 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2017-11-18-17-09-01.bpo-32069.S0wyy4.rst @@ -0,0 +1 @@ +Drop legacy SSL transport from asyncio, ssl.MemoryBIO is always used anyway.