Skip to content

Commit 1b0dfbf

Browse files
miss-islingtonjsbronderkumaraditya303
authored
[3.13] pythongh-135444: fix asyncio.DatagramTransport.sendto to account for datagram header size when data cannot be sent (pythonGH-135445) (python#137246)
pythongh-135444: fix `asyncio.DatagramTransport.sendto` to account for datagram header size when data cannot be sent (pythonGH-135445) (cherry picked from commit e3ea861) Co-authored-by: Justin Bronder <[email protected]> Co-authored-by: Kumar Aditya <[email protected]>
1 parent 3b28cb0 commit 1b0dfbf

File tree

5 files changed

+53
-5
lines changed

5 files changed

+53
-5
lines changed

Lib/asyncio/proactor_events.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,8 @@ def _pipe_closed(self, fut):
460460
class _ProactorDatagramTransport(_ProactorBasePipeTransport,
461461
transports.DatagramTransport):
462462
max_size = 256 * 1024
463+
_header_size = 8
464+
463465
def __init__(self, loop, sock, protocol, address=None,
464466
waiter=None, extra=None):
465467
self._address = address
@@ -499,7 +501,7 @@ def sendto(self, data, addr=None):
499501

500502
# Ensure that what we buffer is immutable.
501503
self._buffer.append((bytes(data), addr))
502-
self._buffer_size += len(data) + 8 # include header bytes
504+
self._buffer_size += len(data) + self._header_size
503505

504506
if self._write_fut is None:
505507
# No current write operations are active, kick one off
@@ -526,7 +528,7 @@ def _loop_writing(self, fut=None):
526528
return
527529

528530
data, addr = self._buffer.popleft()
529-
self._buffer_size -= len(data)
531+
self._buffer_size -= len(data) + self._header_size
530532
if self._address is not None:
531533
self._write_fut = self._loop._proactor.send(self._sock,
532534
data)

Lib/asyncio/selector_events.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1208,6 +1208,7 @@ def close(self):
12081208
class _SelectorDatagramTransport(_SelectorTransport, transports.DatagramTransport):
12091209

12101210
_buffer_factory = collections.deque
1211+
_header_size = 8
12111212

12121213
def __init__(self, loop, sock, protocol, address=None,
12131214
waiter=None, extra=None):
@@ -1281,21 +1282,21 @@ def sendto(self, data, addr=None):
12811282

12821283
# Ensure that what we buffer is immutable.
12831284
self._buffer.append((bytes(data), addr))
1284-
self._buffer_size += len(data) + 8 # include header bytes
1285+
self._buffer_size += len(data) + self._header_size
12851286
self._maybe_pause_protocol()
12861287

12871288
def _sendto_ready(self):
12881289
while self._buffer:
12891290
data, addr = self._buffer.popleft()
1290-
self._buffer_size -= len(data)
1291+
self._buffer_size -= len(data) + self._header_size
12911292
try:
12921293
if self._extra['peername']:
12931294
self._sock.send(data)
12941295
else:
12951296
self._sock.sendto(data, addr)
12961297
except (BlockingIOError, InterruptedError):
12971298
self._buffer.appendleft((data, addr)) # Try again later.
1298-
self._buffer_size += len(data)
1299+
self._buffer_size += len(data) + self._header_size
12991300
break
13001301
except OSError as exc:
13011302
self._protocol.error_received(exc)

Lib/test/test_asyncio/test_proactor_events.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,8 @@ def test_sendto(self):
566566
self.assertTrue(self.proactor.sendto.called)
567567
self.proactor.sendto.assert_called_with(
568568
self.sock, data, addr=('0.0.0.0', 1234))
569+
self.assertFalse(transport._buffer)
570+
self.assertEqual(0, transport._buffer_size)
569571

570572
def test_sendto_bytearray(self):
571573
data = bytearray(b'data')

Lib/test/test_asyncio/test_selector_events.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,47 @@ def test_sendto_closing(self):
14601460
transport.sendto(b'data', (1,))
14611461
self.assertEqual(transport._conn_lost, 2)
14621462

1463+
def test_sendto_sendto_ready(self):
1464+
data = b'data'
1465+
1466+
# First queue up the buffer by having the socket blocked
1467+
self.sock.sendto.side_effect = BlockingIOError
1468+
transport = self.datagram_transport()
1469+
transport.sendto(data, ('0.0.0.0', 12345))
1470+
self.loop.assert_writer(7, transport._sendto_ready)
1471+
self.assertEqual(1, len(transport._buffer))
1472+
self.assertEqual(transport._buffer_size, len(data) + transport._header_size)
1473+
1474+
# Now let the socket send the buffer
1475+
self.sock.sendto.side_effect = None
1476+
transport._sendto_ready()
1477+
self.assertTrue(self.sock.sendto.called)
1478+
self.assertEqual(
1479+
self.sock.sendto.call_args[0], (data, ('0.0.0.0', 12345)))
1480+
self.assertFalse(self.loop.writers)
1481+
self.assertFalse(transport._buffer)
1482+
self.assertEqual(transport._buffer_size, 0)
1483+
1484+
def test_sendto_sendto_ready_blocked(self):
1485+
data = b'data'
1486+
1487+
# First queue up the buffer by having the socket blocked
1488+
self.sock.sendto.side_effect = BlockingIOError
1489+
transport = self.datagram_transport()
1490+
transport.sendto(data, ('0.0.0.0', 12345))
1491+
self.loop.assert_writer(7, transport._sendto_ready)
1492+
self.assertEqual(1, len(transport._buffer))
1493+
self.assertEqual(transport._buffer_size, len(data) + transport._header_size)
1494+
1495+
# Now try to send the buffer, it will be added to buffer again if it fails
1496+
transport._sendto_ready()
1497+
self.assertTrue(self.sock.sendto.called)
1498+
self.assertEqual(
1499+
self.sock.sendto.call_args[0], (data, ('0.0.0.0', 12345)))
1500+
self.assertTrue(self.loop.writers)
1501+
self.assertEqual(1, len(transport._buffer))
1502+
self.assertEqual(transport._buffer_size, len(data) + transport._header_size)
1503+
14631504
def test_sendto_ready(self):
14641505
data = b'data'
14651506
self.sock.sendto.return_value = len(data)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :meth:`asyncio.DatagramTransport.sendto` to account for datagram header size when
2+
data cannot be sent.

0 commit comments

Comments
 (0)