Skip to content

Commit 060eef2

Browse files
[3.13] pythongh-136234: Fix SelectorSocketTransport.writelines to be robust to connection loss (pythonGH-136743) (pythonGH-138702) (python#139710)
[3.14] pythongh-136234: Fix `SelectorSocketTransport.writelines` to be robust to connection loss (pythonGH-136743) (pythonGH-138702) (cherry picked from commit 5cd6cfe) Co-authored-by: Kumar Aditya <[email protected]>
1 parent 333d4a6 commit 060eef2

File tree

3 files changed

+25
-0
lines changed

3 files changed

+25
-0
lines changed

Lib/asyncio/selector_events.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,13 @@ def writelines(self, list_of_data):
11701170
raise RuntimeError('unable to writelines; sendfile is in progress')
11711171
if not list_of_data:
11721172
return
1173+
1174+
if self._conn_lost:
1175+
if self._conn_lost >= constants.LOG_THRESHOLD_FOR_CONNLOST_WRITES:
1176+
logger.warning('socket.send() raised exception.')
1177+
self._conn_lost += 1
1178+
return
1179+
11731180
self._buffer.extend([memoryview(data) for data in list_of_data])
11741181
self._write_ready()
11751182
# If the entire buffer couldn't be written, register a write handler

Lib/test/test_asyncio/test_selector_events.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,22 @@ def test_writelines_pauses_protocol(self):
817817
self.assertTrue(self.sock.send.called)
818818
self.assertTrue(self.loop.writers)
819819

820+
def test_writelines_after_connection_lost(self):
821+
# GH-136234
822+
transport = self.socket_transport()
823+
self.sock.send = mock.Mock()
824+
self.sock.send.side_effect = ConnectionResetError
825+
transport.write(b'data1') # Will fail immediately, causing connection lost
826+
827+
transport.writelines([b'data2'])
828+
self.assertFalse(transport._buffer)
829+
self.assertFalse(self.loop.writers)
830+
831+
test_utils.run_briefly(self.loop) # Allow _call_connection_lost to run
832+
transport.writelines([b'data2'])
833+
self.assertFalse(transport._buffer)
834+
self.assertFalse(self.loop.writers)
835+
820836
@unittest.skipUnless(selector_events._HAS_SENDMSG, 'no sendmsg')
821837
def test_write_sendmsg_full(self):
822838
data = memoryview(b'data')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :meth:`asyncio.WriteTransport.writelines` to be robust to connection
2+
failure, by using the same behavior as :meth:`~asyncio.WriteTransport.write`.

0 commit comments

Comments
 (0)