From fc2d503b4aa2dbda9180046fef63e9e8e7d04516 Mon Sep 17 00:00:00 2001 From: Treadgold Date: Sun, 1 Feb 2026 09:45:47 +1300 Subject: [PATCH] IteratorByteStream: split large iterator chunks into 64KB pieces --- httpx/_content.py | 1 + tests/test_content.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/httpx/_content.py b/httpx/_content.py index dc4442ca..f9d2c2e8 100644 --- a/httpx/_content.py +++ b/httpx/_content.py @@ -68,6 +68,7 @@ class IteratorByteStream(SyncByteStream): yield part[offset : offset + chunk_size] offset += chunk_size + class AsyncIteratorByteStream(AsyncByteStream): CHUNK_SIZE = 65_536 diff --git a/tests/test_content.py b/tests/test_content.py index 9bfe9837..07b686c7 100644 --- a/tests/test_content.py +++ b/tests/test_content.py @@ -516,3 +516,28 @@ def test_allow_nan_false(): ValueError, match="Out of range float values are not JSON compliant" ): httpx.Response(200, json=data_with_inf) + + +@pytest.mark.anyio +async def test_iterator_content_splits_large_chunks(): + # Generator yielding a single large chunk (100 KB) + large_chunk = b"a" * 102_400 # 100 KB + + def gen() -> typing.Iterator[bytes]: + yield large_chunk + + # Pass generator to Request (internally uses IteratorByteStream) + request = httpx.Request(method, url, content=gen()) + + # Cast to Iterable[bytes] to make mypy happy + sync_stream: typing.Iterable[bytes] = request.stream # type: ignore + + # Collect chunks + chunks = list(sync_stream) + + # Each chunk must be <= 64 KB + for chunk in chunks: + assert len(chunk) <= 64 * 1024 + + # Total content matches original + assert b"".join(chunks) == large_chunk