]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-142352: Fix `asyncio` `start_tls()` to transfer buffered data from StreamReader...
authorMaksym Kasimov <39828623+kasimov-maxim@users.noreply.github.com>
Sat, 28 Feb 2026 17:49:04 +0000 (19:49 +0200)
committerGitHub <noreply@github.com>
Sat, 28 Feb 2026 17:49:04 +0000 (23:19 +0530)
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
Lib/asyncio/base_events.py
Lib/test/test_asyncio/test_streams.py
Misc/NEWS.d/next/Library/2025-12-06-16-14-18.gh-issue-142352.pW5HLX88.rst [new file with mode: 0644]

index 6619c87bcf5b93fff2b849ae142d8087993be863..b565b1d8a9e2263d1f5469a0d0ea62dca78b165a 100644 (file)
@@ -1345,6 +1345,17 @@ class BaseEventLoop(events.AbstractEventLoop):
         # have a chance to get called before "ssl_protocol.connection_made()".
         transport.pause_reading()
 
+        # gh-142352: move buffered StreamReader data to SSLProtocol
+        if server_side:
+            from .streams import StreamReaderProtocol
+            if isinstance(protocol, StreamReaderProtocol):
+                stream_reader = getattr(protocol, '_stream_reader', None)
+                if stream_reader is not None:
+                    buffer = stream_reader._buffer
+                    if buffer:
+                        ssl_protocol._incoming.write(buffer)
+                        buffer.clear()
+
         transport.set_protocol(ssl_protocol)
         conmade_cb = self.call_soon(ssl_protocol.connection_made, transport)
         resume_cb = self.call_soon(transport.resume_reading)
index f93ee54abc6469f4d9612fa3a3788138005805a7..cae8c7c6f7c94c97aa8688c61dd35e68ba7d45cc 100644 (file)
@@ -819,6 +819,48 @@ class StreamTests(test_utils.TestCase):
         self.assertEqual(msg1, b"hello world 1!\n")
         self.assertEqual(msg2, b"hello world 2!\n")
 
+    @unittest.skipIf(ssl is None, 'No ssl module')
+    def test_start_tls_buffered_data(self):
+        # gh-142352: test start_tls() with buffered data
+
+        async def server_handler(client_reader, client_writer):
+            # Wait for TLS ClientHello to be buffered before start_tls().
+            await client_reader._wait_for_data('test_start_tls_buffered_data'),
+            self.assertTrue(client_reader._buffer)
+            await client_writer.start_tls(test_utils.simple_server_sslcontext())
+
+            line = await client_reader.readline()
+            self.assertEqual(line, b"ping\n")
+            client_writer.write(b"pong\n")
+            await client_writer.drain()
+            client_writer.close()
+            await client_writer.wait_closed()
+
+        async def client(addr):
+            reader, writer = await asyncio.open_connection(*addr)
+            await writer.start_tls(test_utils.simple_client_sslcontext())
+
+            writer.write(b"ping\n")
+            await writer.drain()
+            line = await reader.readline()
+            self.assertEqual(line, b"pong\n")
+            writer.close()
+            await writer.wait_closed()
+
+        async def run_test():
+            server = await asyncio.start_server(
+                server_handler, socket_helper.HOSTv4, 0)
+            server_addr = server.sockets[0].getsockname()
+
+            await client(server_addr)
+            server.close()
+            await server.wait_closed()
+
+        messages = []
+        self.loop.set_exception_handler(lambda loop, ctx: messages.append(ctx))
+        self.loop.run_until_complete(run_test())
+        self.assertEqual(messages, [])
+
     def test_streamreader_constructor_without_loop(self):
         with self.assertRaisesRegex(RuntimeError, 'no current event loop'):
             asyncio.StreamReader()
diff --git a/Misc/NEWS.d/next/Library/2025-12-06-16-14-18.gh-issue-142352.pW5HLX88.rst b/Misc/NEWS.d/next/Library/2025-12-06-16-14-18.gh-issue-142352.pW5HLX88.rst
new file mode 100644 (file)
index 0000000..13e38b1
--- /dev/null
@@ -0,0 +1,4 @@
+Fix :meth:`asyncio.StreamWriter.start_tls` to transfer buffered data from
+:class:`~asyncio.StreamReader` to the SSL layer, preventing data loss when
+upgrading a connection to TLS mid-stream (e.g., when implementing PROXY
+protocol support).