Skip to content

Commit 1cdd242

Browse files
committed
asyncio: update StreamReader transport after StreamWriter.start_tls
Keep StreamReader in sync with the upgraded transport after StreamWriter.start_tls(), and add a regression test for the transport replacement.
1 parent 0a21a24 commit 1cdd242

2 files changed

Lines changed: 35 additions & 0 deletions

File tree

Lib/asyncio/streams.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ def _stream_reader(self):
216216
def _replace_transport(self, transport):
217217
self._transport = transport
218218
self._over_ssl = transport.get_extra_info('sslcontext') is not None
219+
reader = self._stream_reader
220+
if reader is not None:
221+
reader._replace_transport(transport)
219222

220223
def connection_made(self, transport):
221224
if self._reject_connection:
@@ -475,6 +478,10 @@ def set_transport(self, transport):
475478
assert self._transport is None, 'Transport already set'
476479
self._transport = transport
477480

481+
def _replace_transport(self, transport):
482+
assert self._transport is not None, 'Transport not set'
483+
self._transport = transport
484+
478485
def _maybe_resume_transport(self):
479486
if self._paused and len(self._buffer) <= self._limit:
480487
self._paused = False

Lib/test/test_asyncio/test_streams.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,34 @@ async def run_test():
861861
self.loop.run_until_complete(run_test())
862862
self.assertEqual(messages, [])
863863

864+
def test_streamwriter_start_tls_updates_reader_transport(self):
865+
reader = asyncio.StreamReader(loop=self.loop)
866+
protocol = asyncio.StreamReaderProtocol(reader, loop=self.loop)
867+
old_transport = mock.Mock()
868+
old_transport.get_extra_info.return_value = None
869+
old_transport.is_closing.return_value = False
870+
protocol.connection_made(old_transport)
871+
872+
writer = asyncio.StreamWriter(old_transport, protocol, reader, self.loop)
873+
874+
ssl_context = mock.sentinel.ssl_context
875+
new_transport = mock.Mock()
876+
new_transport.get_extra_info.return_value = ssl_context
877+
self.loop.start_tls = mock.AsyncMock(return_value=new_transport)
878+
879+
self.loop.run_until_complete(writer.start_tls(ssl_context))
880+
881+
self.loop.start_tls.assert_awaited_once_with(
882+
old_transport, protocol, ssl_context,
883+
server_side=False, server_hostname=None,
884+
ssl_handshake_timeout=None,
885+
ssl_shutdown_timeout=None,
886+
)
887+
self.assertIs(writer.transport, new_transport)
888+
self.assertIs(protocol._transport, new_transport)
889+
self.assertIs(reader._transport, new_transport)
890+
self.assertTrue(protocol._over_ssl)
891+
864892
def test_streamreader_constructor_without_loop(self):
865893
with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
866894
asyncio.StreamReader()

0 commit comments

Comments
 (0)