Skip to content

Commit 9777f29

Browse files
committed
gh-156275: Fix asyncio losing received TLS data for a bytearray buffer
1 parent 3a5aa68 commit 9777f29

3 files changed

Lines changed: 42 additions & 1 deletion

File tree

Lib/asyncio/sslproto.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -773,8 +773,10 @@ def _do_read__buffered(self):
773773

774774
if count > 0:
775775
offset = count
776+
# gh-156275: a bytearray slice is a copy, slice a view instead
777+
view = memoryview(buf)
776778
while offset < wants:
777-
count = self._sslobj.read(wants - offset, buf[offset:])
779+
count = self._sslobj.read(wants - offset, view[offset:])
778780
if count > 0:
779781
offset += count
780782
else:

Lib/test/test_asyncio/test_ssl.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,43 @@ async def client(addr):
894894
asyncio.wait_for(client(srv.addr),
895895
timeout=self.TIMEOUT))
896896

897+
def test_buffered_proto_bytearray(self):
898+
# gh-156275: decrypt into the caller's buffer, not a copy of a slice
899+
CHUNKS = [b'A' * 30, b'B' * 30, b'C' * 30]
900+
901+
class ClientProto(asyncio.BufferedProtocol):
902+
def __init__(self, done):
903+
self.done = done
904+
self.buf = bytearray(100)
905+
self.data = b''
906+
907+
def get_buffer(self, sizehint):
908+
return self.buf
909+
910+
def buffer_updated(self, nbytes):
911+
self.data += self.buf[:nbytes]
912+
913+
def connection_lost(self, exc):
914+
self.done.set_result(self.data)
915+
916+
async def serve(reader, writer):
917+
for chunk in CHUNKS:
918+
writer.write(chunk)
919+
writer.close()
920+
921+
async def run():
922+
server = await asyncio.start_server(
923+
serve, '127.0.0.1', 0, ssl=test_utils.simple_server_sslcontext())
924+
self.addCleanup(server.close)
925+
done = self.loop.create_future()
926+
await self.loop.create_connection(
927+
lambda: ClientProto(done),
928+
*server.sockets[0].getsockname()[:2],
929+
ssl=test_utils.simple_client_sslcontext())
930+
self.assertEqual(await done, b''.join(CHUNKS))
931+
932+
self.loop.run_until_complete(asyncio.wait_for(run(), timeout=self.TIMEOUT))
933+
897934
def test_start_tls_slow_client_cancel(self):
898935
HELLO_MSG = b'1' * self.PAYLOAD_SIZE
899936

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :mod:`asyncio` losing received TLS data when
2+
:meth:`~asyncio.BufferedProtocol.get_buffer` returns a :class:`bytearray`.

0 commit comments

Comments
 (0)