Skip to content

Commit 950f72c

Browse files
Fix tarfile file-like objects used as data (#6747) (#8941)
(cherry picked from commit 7681235) Co-authored-by: Xavier Halloran <75104372+ReallyReivax@users.noreply.github.com>
1 parent 948ca8c commit 950f72c

4 files changed

Lines changed: 62 additions & 1 deletion

File tree

CHANGES/6732.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed handling of some file-like objects (e.g. ``tarfile.extractfile()``) which raise ``AttributeError`` instead of ``OSError`` when ``fileno`` fails for streaming payload data -- by :user:`ReallyReivax`.

CONTRIBUTORS.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ William Grzybowski
353353
William S.
354354
Wilson Ong
355355
wouter bolsterlee
356+
Xavier Halloran
356357
Xiang Li
357358
Yang Zhou
358359
Yannick Koechlin

aiohttp/payload.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,9 +401,11 @@ class BufferedReaderPayload(IOBasePayload):
401401
def size(self) -> Optional[int]:
402402
try:
403403
return os.fstat(self._value.fileno()).st_size - self._value.tell()
404-
except OSError:
404+
except (OSError, AttributeError):
405405
# data.fileno() is not supported, e.g.
406406
# io.BufferedReader(io.BytesIO(b'data'))
407+
# For some file-like objects (e.g. tarfile), the fileno() attribute may
408+
# not exist at all, and will instead raise an AttributeError.
407409
return None
408410

409411
def decode(self, encoding: str = "utf-8", errors: str = "strict") -> str:

tests/test_client_functional.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
import socket
1010
import ssl
1111
import sys
12+
import tarfile
1213
import time
14+
import zipfile
1315
from typing import Any, AsyncIterator, Type
1416
from unittest import mock
1517

@@ -511,6 +513,61 @@ async def handler(request):
511513
assert 200 == resp.status
512514

513515

516+
async def test_post_data_zipfile_filelike(aiohttp_client: AiohttpClient) -> None:
517+
data = b"This is a zip file payload text file."
518+
519+
async def handler(request: web.Request) -> web.Response:
520+
val = await request.read()
521+
assert data == val, "Transmitted zipfile member failed to match original data."
522+
return web.Response()
523+
524+
app = web.Application()
525+
app.router.add_route("POST", "/", handler)
526+
client = await aiohttp_client(app)
527+
528+
buf = io.BytesIO()
529+
with zipfile.ZipFile(file=buf, mode="w") as zf:
530+
with zf.open("payload1.txt", mode="w") as zip_filelike_writing:
531+
zip_filelike_writing.write(data)
532+
533+
buf.seek(0)
534+
zf = zipfile.ZipFile(file=buf, mode="r")
535+
resp = await client.post("/", data=zf.open("payload1.txt"))
536+
assert 200 == resp.status
537+
538+
539+
async def test_post_data_tarfile_filelike(aiohttp_client: AiohttpClient) -> None:
540+
data = b"This is a tar file payload text file."
541+
542+
async def handler(request: web.Request) -> web.Response:
543+
val = await request.read()
544+
assert data == val, "Transmitted tarfile member failed to match original data."
545+
return web.Response()
546+
547+
app = web.Application()
548+
app.router.add_route("POST", "/", handler)
549+
client = await aiohttp_client(app)
550+
551+
buf = io.BytesIO()
552+
with tarfile.open(fileobj=buf, mode="w") as tf:
553+
ti = tarfile.TarInfo(name="payload1.txt")
554+
ti.size = len(data)
555+
tf.addfile(tarinfo=ti, fileobj=io.BytesIO(data))
556+
557+
# Random-access tarfile.
558+
buf.seek(0)
559+
tf = tarfile.open(fileobj=buf, mode="r:")
560+
resp = await client.post("/", data=tf.extractfile("payload1.txt"))
561+
assert 200 == resp.status
562+
563+
# Streaming tarfile.
564+
buf.seek(0)
565+
tf = tarfile.open(fileobj=buf, mode="r|")
566+
for entry in tf:
567+
resp = await client.post("/", data=tf.extractfile(entry))
568+
assert 200 == resp.status
569+
570+
514571
async def test_ssl_client(
515572
aiohttp_server,
516573
ssl_ctx,

0 commit comments

Comments
 (0)