A length-prefixed stream needs a maximum body size and an explicit short-read path.
Python asyncio byte frames: reject an oversized length before reading a body
Operation contract
The parser consumes a two-byte unsigned length, rejects a declared body over its eight-byte limit, and then reads exactly that many bytes. One owned frame completes; another ends after five of its seven promised bytes. The partial payload is treated as a failed record, never as a shorter accepted record.
Failure boundary
StreamReader.feed_data is used only to supply controlled bytes without opening a socket. A real peer can pause forever between bytes, so a caller also needs a deadline and a connection cleanup policy. The length check bounds one returned body; it does not cap the total bytes a transport may have buffered before parsing begins.
Working program
import asyncio
async def read_receipt_frame(reader, maximum_body):
header = await reader.readexactly(2)
body_size = int.from_bytes(header, "big")
if body_size > maximum_body:
raise ValueError("receipt body too large")
return await reader.readexactly(body_size)
async def inspect_frames():
complete = asyncio.StreamReader()
complete.feed_data((7).to_bytes(2, "big") + b"R47paid")
complete.feed_eof()
print("accepted", (await read_receipt_frame(complete, 8)).decode("ascii"))
oversized = asyncio.StreamReader()
oversized.feed_data((73).to_bytes(2, "big"))
oversized.feed_eof()
try:
await read_receipt_frame(oversized, 8)
except ValueError:
print("oversized_rejected", True)
shortened = asyncio.StreamReader()
shortened.feed_data((7).to_bytes(2, "big") + b"R73pa")
shortened.feed_eof()
try:
await read_receipt_frame(shortened, 8)
except asyncio.IncompleteReadError as failure:
print("partial_rejected", failure.partial == b"R73pa")
asyncio.run(inspect_frames())Output
accepted R47paid
oversized_rejected True
partial_rejected TrueCosts and limits
Header parsing is constant work; copying a valid body costs time and memory proportional to its declared size. A maximum body of eight bytes is a fixture policy, not a network-wide memory budget.
Common Mistakes
- read(n) may return fewer than n bytes without EOF; use readexactly for a fixed frame.
- Reject the declared length before allocating or awaiting its body.
- An incomplete read is a failed frame even when partial bytes exist.
