aio-libs / aiohttp

Asynchronous HTTP client/server framework for asyncio and Python
https://docs.aiohttp.org
Other
15.15k stars 2.02k forks source link

Client: client payload slowness produces ServerTimeoutError #4855

Closed hashbrowncipher closed 1 month ago

hashbrowncipher commented 4 years ago

🐞 Describe the bug

When the client is slow to produce a payload, a ServerTimeoutError is reported. The documentation defines the Timeout.sock_read tunable as A timeout for reading a portion of data from a peer. We were not reading from the peer when the timeout was raised.

πŸ’‘ To Reproduce

import asyncio

import aiohttp

class WaitingPayload(aiohttp.Payload):
    def __init__(self, value):
        super().__init__(value)

    async def write(self, writer):
        await asyncio.sleep(10)
        await writer.write(b"Echo!")

async def print_text():
    timeout = aiohttp.ClientTimeout(sock_read=1)
    async with aiohttp.ClientSession(timeout=timeout) as session:
        async with session.put('http://httpbin.org/put', data=WaitingPayload(10)) as resp:
            print(resp.status)
            print(await resp.text())

asyncio.run(print_text())

πŸ’‘ Expected behavior

No error should be raised. For users wanting to monitor the progress of Payload transmission, a separate tunable can be provided for this purpose.

$ python --version
Python 3.7.6
$ pip freeze
aiohttp==3.6.2
async-timeout==3.0.1
attrs==19.3.0
chardet==3.0.4
idna==2.10
multidict==4.7.6
yarl==1.4.2
hashbrowncipher commented 4 years ago

I have been able to work around the issue with this patch:

diff --git a/aiohttp/client_proto.py b/aiohttp/client_proto.py
index db22406c..34335b81 100644
--- a/aiohttp/client_proto.py
+++ b/aiohttp/client_proto.py
@@ -148,7 +148,8 @@ class ResponseHandler(BaseProtocol,
         self._skip_payload = skip_payload

         self._read_timeout = read_timeout
-        self._reschedule_timeout()
+        if self._read_timeout_handle is not None:
+            self._reschedule_timeout()

         self._parser = HttpResponseParser(
             self, self._loop, timer=timer,
Dreamsorcerer commented 2 months ago

Is this still an issue for you? The reproducer seems to work fine for me today.