aio-libs / aiohttp

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

WebSocketResponse requires closing/closed checks before sending something #3391

Open achimnol opened 5 years ago

achimnol commented 5 years ago

Long story short

In the server-side websocket handlers written with aiohttp, WebSocketResponse's send_xxx() methods "ignores" closing/closed state of the connection.

So in my code, I had to add if ws.closed: break everywhere that calls send_xxx().

Maybe this issue is related to #2025.

Expected behaviour

The send_xxx() methods should raise an explicit exception so that a server-side task can notice if the connection is closed.

Actual behaviour

The send_xxx() methods just emit an warning to the logger and continue. This behavior leads to a memory leak as the unsent messages accumulates in the buffer. I get a stream of log messages like:

socket.send() raised exception.
websocket connection is closing.
socket.send() raised exception.
websocket connection is closing.
socket.send() raised exception.
websocket connection is closing.
socket.send() raised exception.
websocket connection is closing.
socket.send() raised exception.
websocket connection is closing.
socket.send() raised exception.
websocket connection is closing.
socket.send() raised exception.
...

until my server-side task finishes (which runs without noticing the connection is closed!).

Steps to reproduce

  1. Write a simple websocket handler like:
async def handler(request):
  ws = web.WebSocketResponse()
  await ws.prepare(request)
  for _ in range(10):
    await asyncio.sleep(1)
    await ws.send_str('test')
  return ws
  1. Open this handler in a web browser using Javascript that reads only one message and closes the connection actively. (NOTE: closing the page abruptly in the browser makes aiohttp to raise asyncio.CancelledError)

Your environment

Linux & macOS.

aio-libs-bot commented 5 years ago

GitMate.io thinks the contributor most likely able to help you is @asvetlov.

Possibly related issues are https://github.com/aio-libs/aiohttp/issues/370 (Server closes connection before sending data), https://github.com/aio-libs/aiohttp/issues/3363 (How send "WebSocket Connection Close[FIN]"???), https://github.com/aio-libs/aiohttp/issues/2202 (Send explicit RuntimeError on response operations after session closing), https://github.com/aio-libs/aiohttp/issues/2309 (WebSocketResponse does not throw/close when connection is abruptly cut), and https://github.com/aio-libs/aiohttp/issues/172 (Question about ServerHttpProtocol.closing()).

asvetlov commented 5 years ago

I like the proposal but it is a backward incompatible change: a server code can get an unexpected exception on await ws.send_str('data').

Let's postpone the fix to aiohttp 4.0 (next version after 3.5 release)

redradist commented 1 year ago

I like the proposal but it is a backward incompatible change: a server code can get an unexpected exception on await ws.send_str('data').

Let's postpone the fix to aiohttp 4.0 (next version after 3.5 release)

@asvetlov Is there any updates for this issue ? It is very annoying to have this workaround for socket.send() raised exception., because of silent affect