Fix the ClientWebSocketResponse.receive stuck for a long time if network is suddenly unavaiable. - #5860
Fix the ClientWebSocketResponse.receive stuck for a long time if network is suddenly unavaiable.#5860Hanaasagi wants to merge 1 commit into
ClientWebSocketResponse.receive stuck for a long time if network is suddenly unavaiable.#5860Conversation
|
Please fill out the PR template with the details on what this is supposed to do. |
receive stuck for long time even if heartbeat is failed.receive stuck for long time even if heartbeat is failed.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5860 +/- ##
==========================================
+ Coverage 96.75% 96.80% +0.05%
==========================================
Files 44 44
Lines 9851 9863 +12
Branches 1591 1594 +3
==========================================
+ Hits 9531 9548 +17
+ Misses 182 178 -4
+ Partials 138 137 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
…if network is suddenly interrupted. Related to aio-libs#2309; If we call `receive()` with a unlimited timeout, it will get stuck for long time, because `_pong_not_received` has no way to awake a block coroutine `receive()`. This PR add a `asyncio.Event` to awake this coroutine.
receive stuck for long time even if heartbeat is failed.receive stuck for long time even if heartbeat is failed.
receive stuck for long time even if heartbeat is failed.ClientWebSocketResponse.receive stuck for a long time if network is suddenly unavaiable.
| elif is_heartbeat_failed_task in done: | ||
| read_task.cancel() | ||
| assert self._exception is not None | ||
| raise self._exception |
There was a problem hiding this comment.
I'm not sure that raise TimeoutError or just return a WSMessage(WSMsgType.ERROR, ..) message which is better.
|
any update? |
| ) | ||
| is_heartbeat_failed_task: asyncio.Task[bool] = asyncio.create_task( | ||
| self._is_heartbeat_failed.wait() | ||
| ) |
There was a problem hiding this comment.
Do we really need two additional tasks in order to solve this bug? Is it at all necessary to check heartbeat status, or is it sufficient to check transport.is_closing()?
|
The linked issue was closed, would be good if @bdraco or @Hanaasagi could confirm if this PR is no longer relevant. |
|
The receive resolves immediately when we know the connection is dropped now |
|
Very likely this issue is fixed. Please let us know if you still experience this behavior on 3.10.5 or later and we can reopen. |
What do these changes do?
This PR fix the websocket client will get stuck when a the internect is unavaiable.
How to reproduce it
Run following code, and turn off the internet after seeing the
Connectedtext in the console.In my local machine, it will block ablout 8 min and get a
CLOSEDmessage.Explain Why
receivemethod has atimeoutargument, and the default value isNone. It means no timeout for reading from a connection. The websocket pong frame and user data frame uses the same timeout. So ifaiohttpsend a ping frame successfully, then we turn off the network. Thereceivecoroutine will be block at themsg = await self._reader.read(). Because there are no data comes from the server.aiohttp/aiohttp/client_ws.py
Lines 213 to 238 in 184274d
Even if we call
_pong_not_received, it doesn't cancel thereceivecoroutine.aiohttp/aiohttp/client_ws.py
Lines 101 to 106 in 184274d
The
receivecoroutine finished until aEofStreamerror from the TCP Stack.What did this PR do
Add a simple flag(
asyncio.Event)_is_heartbeat_failedinClientWebSocketResponse. And when callClientWebSocketResponse.receive, it both awaitself._reader.read()and this flag byasyntio.wait. So if the heartbeat is failed, theClientWebSocketResponse.receivewill be awake.Are there changes in behavior for the user?
Related issue number
Fix #2309
Checklist
CONTRIBUTORS.txtCHANGESfolder<issue_id>.<type>for example (588.bugfix)issue_idchange it to the pr id after creating the pr.feature: Signifying a new feature..bugfix: Signifying a bug fix..doc: Signifying a documentation improvement..removal: Signifying a deprecation or removal of public API..misc: A ticket has been closed, but it is not of interest to users.