The protocol's connection_lost() method will (eventually) be
called with None as its argument.
"""
- self._closed = True
- if self._ssl_protocol is not None:
- self._ssl_protocol._abort()
+ self._force_close(None)
def _force_close(self, exc):
self._closed = True
- self._ssl_protocol._abort(exc)
+ if self._ssl_protocol is not None:
+ self._ssl_protocol._abort(exc)
def _test__append_write_backlog(self, data):
# for test only
if self._app_transport is not None:
self._app_transport._closed = True
if self._state == SSLProtocolState.DO_HANDSHAKE:
- self._abort()
+ self._abort(None)
else:
self._set_state(SSLProtocolState.FLUSHING)
self._shutdown_timeout_handle = self._loop.call_later(
else:
self._loop.call_soon(self._transport.close)
- def _abort(self):
+ def _abort(self, exc):
self._set_state(SSLProtocolState.UNWRAPPED)
if self._transport is not None:
- self._transport.abort()
+ self._transport._force_close(exc)
# Outgoing flow
sslobj = mock.Mock()
# emulate reading decompressed data
sslobj.read.side_effect = ssl.SSLWantReadError
+ sslobj.write.side_effect = ssl.SSLWantReadError
if do_handshake is not None:
sslobj.do_handshake = do_handshake
ssl_proto._sslobj = sslobj
test_utils.run_briefly(self.loop)
ssl_proto._app_transport.close()
- self.assertTrue(transport.abort.called)
+ self.assertTrue(transport._force_close.called)
+
+ def test_close_during_ssl_over_ssl(self):
+ # gh-113214: passing exceptions from the inner wrapped SSL protocol to the
+ # shim transport provided by the outer SSL protocol should not raise
+ # attribute errors
+ outer = self.ssl_protocol(proto=self.ssl_protocol())
+ self.connection_made(outer)
+ # Closing the outer app transport should not raise an exception
+ messages = []
+ self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx))
+ outer._app_transport.close()
+ self.assertEqual(messages, [])
def test_get_extra_info_on_closed_connection(self):
waiter = self.loop.create_future()