Skip to content

Commit 30850ba

Browse files
authored
Fix aio-libs#7306 - Set ClientWebSocketResponse.close_code correctly in concurrent closing scenario (aio-libs#7680)
1 parent c0ba7e5 commit 30850ba

File tree

3 files changed

+31
-2
lines changed

3 files changed

+31
-2
lines changed

CHANGES/7306.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed ``ClientWebSocketResponse.close_code`` being erroneously set to ``None`` when there are concurrent async tasks receiving data and closing the connection.

aiohttp/client_ws.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ async def send_json(
191191
async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bool:
192192
# we need to break `receive()` cycle first,
193193
# `close()` may be called from different task
194-
if self._waiting is not None and not self._closed:
194+
if self._waiting is not None and not self._closing:
195+
self._closing = True
195196
self._reader.feed_data(WS_CLOSING_MESSAGE, 0)
196197
await self._waiting
197198

@@ -210,7 +211,7 @@ async def close(self, *, code: int = WSCloseCode.OK, message: bytes = b"") -> bo
210211
self._response.close()
211212
return True
212213

213-
if self._closing:
214+
if self._close_code:
214215
self._response.close()
215216
return True
216217

tests/test_client_ws_functional.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,33 @@ async def handler(request):
233233
assert msg.type == aiohttp.WSMsgType.CLOSED
234234

235235

236+
async def test_concurrent_task_close(aiohttp_client: Any) -> None:
237+
async def handler(request):
238+
ws = web.WebSocketResponse()
239+
await ws.prepare(request)
240+
await ws.receive()
241+
return ws
242+
243+
app = web.Application()
244+
app.router.add_route("GET", "/", handler)
245+
246+
client = await aiohttp_client(app)
247+
async with client.ws_connect("/") as resp:
248+
# wait for the message in a separate task
249+
task = asyncio.create_task(resp.receive())
250+
251+
# Make sure we start to wait on receiving message before closing the connection
252+
await asyncio.sleep(0.1)
253+
254+
closed = await resp.close()
255+
256+
await task
257+
258+
assert closed
259+
assert resp.closed
260+
assert resp.close_code == 1000
261+
262+
236263
async def test_concurrent_close(aiohttp_client: Any) -> None:
237264
client_ws = None
238265

0 commit comments

Comments
 (0)