mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-01 02:02:20 +00:00
fix(http): serve a body the transport already read through the bounded reader
This commit is contained in:
parent
1e8672ded1
commit
d6d2473668
2 changed files with 52 additions and 0 deletions
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue