fix(http): serve a body the transport already read through the bounded reader

This commit is contained in:
mateo-berri 2026-09-25 20:33:35 -07:00
parent 1e8672ded1
commit d6d2473668
2 changed files with 52 additions and 0 deletions

View file

@ -630,6 +630,17 @@ async def _wire_bounded(response: httpx.Response, limit: int) -> AsyncGenerator[
yield chunk
def _already_read_within(response: httpx.Response, max_bytes: int) -> httpx.Response:
if len(response.content) > max_bytes:
raise HTTPResponseLimitError("Response exceeds the configured size limit")
return httpx.Response(
response.status_code,
headers=_headers_of_the_decoded_body(response.headers),
content=response.content,
request=response.request,
)
async def _decoded_within(response: httpx.Response, wire: AsyncGenerator[bytes, None], max_bytes: int) -> bytes:
decoding: Final = httpx.Response(
response.status_code, headers=response.headers, content=wire, request=response.request
@ -817,6 +828,8 @@ class AsyncHTTPHandler:
)
if response.is_redirect or response.is_error:
return httpx.Response(response.status_code, headers=response.headers, request=response.request)
if response.is_stream_consumed:
return _already_read_within(response, max_bytes)
wire_limit: Final = _wire_byte_limit(response.headers, max_bytes)
if int(response.headers.get("content-length", "0")) > wire_limit:
raise HTTPResponseLimitError("Response exceeds the configured size limit")

View file

@ -1839,6 +1839,45 @@ async def test_bounded_get_caps_the_wire_bytes_of_a_compressed_body_that_decodes
await handler.close()
@pytest.mark.asyncio
async def test_bounded_get_serves_a_body_the_transport_already_read(monkeypatch):
monkeypatch.setenv("DISABLE_AIOHTTP_TRANSPORT", "True")
card: Final = {"data": [{"id": "served", "max_model_len": 8192}]}
def respond(request: httpx.Request) -> httpx.Response:
return httpx.Response(200, json=card)
handler = AsyncHTTPHandler()
handler.client = httpx.AsyncClient(transport=httpx.MockTransport(respond))
try:
response = await handler.get("https://one.test/v1/models", max_response_bytes=2 * 1024 * 1024)
finally:
await handler.close()
assert response.json() == card
assert response.headers["content-length"] == str(len(response.content))
@pytest.mark.asyncio
async def test_bounded_get_bounds_an_already_read_body_on_its_decoded_length(monkeypatch):
monkeypatch.setenv("DISABLE_AIOHTTP_TRANSPORT", "True")
document: Final = b"0" * 4096
def respond(request: httpx.Request) -> httpx.Response:
return httpx.Response(200, content=gzip.compress(document), headers={"content-encoding": "gzip"})
handler = AsyncHTTPHandler()
handler.client = httpx.AsyncClient(transport=httpx.MockTransport(respond))
try:
served = await handler.get("https://cdn.example/notes.txt", max_response_bytes=len(document))
assert served.content == document
assert "content-encoding" not in served.headers
assert served.headers["content-length"] == str(len(document))
with pytest.raises(HTTPResponseLimitError, match="size limit"):
await handler.get("https://cdn.example/notes.txt", max_response_bytes=len(document) - 1)
finally:
await handler.close()
@pytest.mark.asyncio
async def test_bounded_get_rejects_a_declared_compressed_length_over_the_wire_limit(respx_mock, monkeypatch):
monkeypatch.setenv("DISABLE_AIOHTTP_TRANSPORT", "True")