Skip to content

Commit

Permalink
Fix FileResponse fallback code
Browse files Browse the repository at this point in the history
In case the FileResponse is using _sendfile_fallback and the requested
range is smaller then the chunk size, we need to only read and send
count bytes.
  • Loading branch information
mdellweg committed Jan 7, 2025
1 parent 237d467 commit 0ac82e1
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGES/6726.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed a bug in ``FileResponse`` fallback code when the requested range is smaller than the chunk size.
1 change: 1 addition & 0 deletions CONTRIBUTORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ Martin Sucha
Mathias Fröjdman
Mathieu Dugré
Matt VanEseltine
Matthias Dellweg
Matthias Marquardt
Matthieu Hauglustaine
Matthieu Rigal
Expand Down
2 changes: 1 addition & 1 deletion aiohttp/web_fileresponse.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ async def _sendfile_fallback(
chunk_size = self._chunk_size
loop = asyncio.get_event_loop()
chunk = await loop.run_in_executor(
None, self._seek_and_read, fobj, offset, chunk_size
None, self._seek_and_read, fobj, offset, min(chunk_size, count)
)
while chunk:
await writer.write(chunk)
Expand Down
27 changes: 27 additions & 0 deletions tests/test_web_sendfile_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -1164,3 +1164,30 @@ async def handler(request: web.Request) -> web.FileResponse:

resp.release()
await client.close()


@pytest.mark.parametrize("chunk_size", (15, 31))
async def test_a_ranged_request_serves_no_additional_bytes(
aiohttp_client: AiohttpClient, sender: _Sender, chunk_size: int
) -> None:
filepath = pathlib.Path(__file__).parent / "aiohttp.jpg"

async def handler(request: web.Request) -> web.FileResponse:
return sender(filepath, chunk_size=chunk_size)

app = web.Application()
app.router.add_get("/", handler)
client = await aiohttp_client(app)

resp = await client.get("/", headers={"Range": "bytes=20-40"})
assert resp.status == 206
body = await resp.read()

# 21 bytes, because http range headers are including both fenceposts.
assert len(body) == 21

# Additionally verify that we are served the right bytes from that file.
with filepath.open("rb") as f:
f.seek(20)
content = f.read(21)
assert content == body

0 comments on commit 0ac82e1

Please sign in to comment.