[3.6] bpo-29406: asyncio SSL contexts leak sockets after calling close with certain servers (GH-409) (#2062)
* bpo-29406: asyncio SSL contexts leak sockets after calling close with certain servers (#409) (cherry picked from commita608d2d5a7
) * [3.6] bpo-29406: asyncio SSL contexts leak sockets after calling close with certain servers (GH-409) * asyncio SSL contexts leak sockets after calling close with certain servers * cleanup _shutdown_timeout_handle on _fatal_error. (cherry picked from commita608d2d5a7
)
This commit is contained in:
parent
911068e250
commit
6e14fd2a14
|
@ -7,6 +7,7 @@ except ImportError: # pragma: no cover
|
||||||
|
|
||||||
from . import base_events
|
from . import base_events
|
||||||
from . import compat
|
from . import compat
|
||||||
|
from . import futures
|
||||||
from . import protocols
|
from . import protocols
|
||||||
from . import transports
|
from . import transports
|
||||||
from .log import logger
|
from .log import logger
|
||||||
|
@ -412,7 +413,7 @@ class SSLProtocol(protocols.Protocol):
|
||||||
|
|
||||||
def __init__(self, loop, app_protocol, sslcontext, waiter,
|
def __init__(self, loop, app_protocol, sslcontext, waiter,
|
||||||
server_side=False, server_hostname=None,
|
server_side=False, server_hostname=None,
|
||||||
call_connection_made=True):
|
call_connection_made=True, shutdown_timeout=5.0):
|
||||||
if ssl is None:
|
if ssl is None:
|
||||||
raise RuntimeError('stdlib ssl module not available')
|
raise RuntimeError('stdlib ssl module not available')
|
||||||
|
|
||||||
|
@ -443,6 +444,8 @@ class SSLProtocol(protocols.Protocol):
|
||||||
self._session_established = False
|
self._session_established = False
|
||||||
self._in_handshake = False
|
self._in_handshake = False
|
||||||
self._in_shutdown = False
|
self._in_shutdown = False
|
||||||
|
self._shutdown_timeout = shutdown_timeout
|
||||||
|
self._shutdown_timeout_handle = None
|
||||||
# transport, ex: SelectorSocketTransport
|
# transport, ex: SelectorSocketTransport
|
||||||
self._transport = None
|
self._transport = None
|
||||||
self._call_connection_made = call_connection_made
|
self._call_connection_made = call_connection_made
|
||||||
|
@ -557,6 +560,15 @@ class SSLProtocol(protocols.Protocol):
|
||||||
self._in_shutdown = True
|
self._in_shutdown = True
|
||||||
self._write_appdata(b'')
|
self._write_appdata(b'')
|
||||||
|
|
||||||
|
if self._shutdown_timeout is not None:
|
||||||
|
self._shutdown_timeout_handle = self._loop.call_later(
|
||||||
|
self._shutdown_timeout, self._on_shutdown_timeout)
|
||||||
|
|
||||||
|
def _on_shutdown_timeout(self):
|
||||||
|
if self._transport is not None:
|
||||||
|
self._fatal_error(
|
||||||
|
futures.TimeoutError(), 'Can not complete shitdown operation')
|
||||||
|
|
||||||
def _write_appdata(self, data):
|
def _write_appdata(self, data):
|
||||||
self._write_backlog.append((data, 0))
|
self._write_backlog.append((data, 0))
|
||||||
self._write_buffer_size += len(data)
|
self._write_buffer_size += len(data)
|
||||||
|
@ -684,12 +696,22 @@ class SSLProtocol(protocols.Protocol):
|
||||||
})
|
})
|
||||||
if self._transport:
|
if self._transport:
|
||||||
self._transport._force_close(exc)
|
self._transport._force_close(exc)
|
||||||
|
self._transport = None
|
||||||
|
|
||||||
|
if self._shutdown_timeout_handle is not None:
|
||||||
|
self._shutdown_timeout_handle.cancel()
|
||||||
|
self._shutdown_timeout_handle = None
|
||||||
|
|
||||||
def _finalize(self):
|
def _finalize(self):
|
||||||
self._sslpipe = None
|
self._sslpipe = None
|
||||||
|
|
||||||
if self._transport is not None:
|
if self._transport is not None:
|
||||||
self._transport.close()
|
self._transport.close()
|
||||||
|
self._transport = None
|
||||||
|
|
||||||
|
if self._shutdown_timeout_handle is not None:
|
||||||
|
self._shutdown_timeout_handle.cancel()
|
||||||
|
self._shutdown_timeout_handle = None
|
||||||
|
|
||||||
def _abort(self):
|
def _abort(self):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -96,6 +96,40 @@ class SslProtoHandshakeTests(test_utils.TestCase):
|
||||||
test_utils.run_briefly(self.loop)
|
test_utils.run_briefly(self.loop)
|
||||||
self.assertIsInstance(waiter.exception(), ConnectionAbortedError)
|
self.assertIsInstance(waiter.exception(), ConnectionAbortedError)
|
||||||
|
|
||||||
|
def test_close_abort(self):
|
||||||
|
# From issue #bpo-29406
|
||||||
|
# abort connection if server does not complete shutdown procedure
|
||||||
|
ssl_proto = self.ssl_protocol()
|
||||||
|
transport = self.connection_made(ssl_proto)
|
||||||
|
ssl_proto._on_handshake_complete(None)
|
||||||
|
ssl_proto._start_shutdown()
|
||||||
|
self.assertIsNotNone(ssl_proto._shutdown_timeout_handle)
|
||||||
|
|
||||||
|
exc_handler = mock.Mock()
|
||||||
|
self.loop.set_exception_handler(exc_handler)
|
||||||
|
ssl_proto._shutdown_timeout_handle._run()
|
||||||
|
|
||||||
|
exc_handler.assert_called_with(
|
||||||
|
self.loop, {'message': 'Can not complete shitdown operation',
|
||||||
|
'exception': mock.ANY,
|
||||||
|
'transport': transport,
|
||||||
|
'protocol': ssl_proto}
|
||||||
|
)
|
||||||
|
self.assertIsNone(ssl_proto._shutdown_timeout_handle)
|
||||||
|
|
||||||
|
def test_close(self):
|
||||||
|
# From issue #bpo-29406
|
||||||
|
# abort connection if server does not complete shutdown procedure
|
||||||
|
ssl_proto = self.ssl_protocol()
|
||||||
|
transport = self.connection_made(ssl_proto)
|
||||||
|
ssl_proto._on_handshake_complete(None)
|
||||||
|
ssl_proto._start_shutdown()
|
||||||
|
self.assertIsNotNone(ssl_proto._shutdown_timeout_handle)
|
||||||
|
|
||||||
|
ssl_proto._finalize()
|
||||||
|
self.assertIsNone(ssl_proto._transport)
|
||||||
|
self.assertIsNone(ssl_proto._shutdown_timeout_handle)
|
||||||
|
|
||||||
def test_close_during_handshake(self):
|
def test_close_during_handshake(self):
|
||||||
# bpo-29743 Closing transport during handshake process leaks socket
|
# bpo-29743 Closing transport during handshake process leaks socket
|
||||||
waiter = asyncio.Future(loop=self.loop)
|
waiter = asyncio.Future(loop=self.loop)
|
||||||
|
|
|
@ -49,6 +49,10 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- bpo-29406: asyncio SSL contexts leak sockets after calling close with
|
||||||
|
certain servers.
|
||||||
|
Patch by Nikolay Kim
|
||||||
|
|
||||||
- bpo-29870: Fix ssl sockets leaks when connection is aborted in asyncio/ssl
|
- bpo-29870: Fix ssl sockets leaks when connection is aborted in asyncio/ssl
|
||||||
implementation. Patch by Michaël Sghaïer.
|
implementation. Patch by Michaël Sghaïer.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue