Skip to content

Commit a8ec729

Browse files
committed
Improved PrefixStreamIO.read() to handle partial chunk reads and added tests for large payloads and partial chunks.
1 parent abdb46b commit a8ec729

File tree

2 files changed

+33
-4
lines changed

2 files changed

+33
-4
lines changed

lf_toolkit/io/stream_io.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,11 @@ async def read(self, size: int) -> bytes:
6363
if content_length == 0:
6464
raise ValueError("Content-Length header not found or is zero")
6565

66-
if content_length > size:
67-
raise ValueError("Content-Length is larger than the read size")
68-
69-
return await self.base.read(content_length)
66+
data = b""
67+
while len(data) < content_length:
68+
chunk = await self.base.read(content_length - len(data))
69+
data += chunk
70+
return data
7071

7172
async def write(self, data: bytes):
7273
response_headers_str = f"Content-Length: {len(data)}\r\n\r\n"

tests/io/stream_io_test.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,31 @@ async def test_raises_on_missing_content_length(self, stream):
175175

176176
with pytest.raises(ValueError, match="Content-Length"):
177177
await prefix_io.read(4096)
178+
179+
@pytest.mark.anyio
180+
async def test_large_payload_does_not_raise(self, stream):
181+
"""Payloads larger than 4096 bytes must be read without raising."""
182+
prefix_io = PrefixStreamIO(stream)
183+
184+
payload = b"x" * 8192
185+
header = f"Content-Length: {len(payload)}\r\n\r\n".encode()
186+
stream.feed(header + payload)
187+
188+
result = await prefix_io.read(4096)
189+
assert result == payload
190+
191+
@pytest.mark.anyio
192+
async def test_exact_read_of_partial_chunks(self, stream):
193+
"""All bytes are read even when the underlying stream delivers chunks smaller than content_length."""
194+
prefix_io = PrefixStreamIO(stream)
195+
196+
payload = b"a" * 100
197+
header = f"Content-Length: {len(payload)}\r\n\r\n".encode()
198+
# Feed header and payload as separate tiny chunks (10 bytes each)
199+
full = header + payload
200+
for i in range(0, len(full), 10):
201+
stream.feed(full[i:i + 10])
202+
203+
result = await prefix_io.read(4096)
204+
assert result == payload
205+
assert len(result) == 100

0 commit comments

Comments
 (0)