From bb231059b14277c34a8a0331e51406d5abe4f424 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Fri, 5 Apr 2024 03:58:05 +0000 Subject: [PATCH] [PR #8283/54e13b0a backport][3.9] Fix blocking I/O in the event loop while processing files in a post request (#8293) Co-authored-by: J. Nick Koston --- CHANGES/8283.bugfix.rst | 2 ++ aiohttp/test_utils.py | 11 +++++++++-- aiohttp/web_request.py | 10 ++++++---- 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 CHANGES/8283.bugfix.rst diff --git a/CHANGES/8283.bugfix.rst b/CHANGES/8283.bugfix.rst new file mode 100644 index 00000000000..d456d59ba8e --- /dev/null +++ b/CHANGES/8283.bugfix.rst @@ -0,0 +1,2 @@ +Fixed blocking I/O in the event loop while processing files in a POST request +-- by :user:`bdraco`. diff --git a/aiohttp/test_utils.py b/aiohttp/test_utils.py index b5821a7fb84..a36e8599689 100644 --- a/aiohttp/test_utils.py +++ b/aiohttp/test_utils.py @@ -594,8 +594,15 @@ def make_mocked_request( """ task = mock.Mock() if loop is ...: - loop = mock.Mock() - loop.create_future.return_value = () + # no loop passed, try to get the current one if + # its is running as we need a real loop to create + # executor jobs to be able to do testing + # with a real executor + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = mock.Mock() + loop.create_future.return_value = () if version < HttpVersion(1, 1): closing = True diff --git a/aiohttp/web_request.py b/aiohttp/web_request.py index 781713e5985..4bc670a798c 100644 --- a/aiohttp/web_request.py +++ b/aiohttp/web_request.py @@ -726,19 +726,21 @@ async def post(self) -> "MultiDictProxy[Union[str, bytes, FileField]]": # https://tools.ietf.org/html/rfc7578#section-4.4 if field.filename: # store file in temp file - tmp = tempfile.TemporaryFile() + tmp = await self._loop.run_in_executor( + None, tempfile.TemporaryFile + ) chunk = await field.read_chunk(size=2**16) while chunk: chunk = field.decode(chunk) - tmp.write(chunk) + await self._loop.run_in_executor(None, tmp.write, chunk) size += len(chunk) if 0 < max_size < size: - tmp.close() + await self._loop.run_in_executor(None, tmp.close) raise HTTPRequestEntityTooLarge( max_size=max_size, actual_size=size ) chunk = await field.read_chunk(size=2**16) - tmp.seek(0) + await self._loop.run_in_executor(None, tmp.seek, 0) if field_ct is None: field_ct = "application/octet-stream"