Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handling CTRL+C for websockets [linux] #333

Open
tf198 opened this issue Apr 10, 2024 · 6 comments
Open

Handling CTRL+C for websockets [linux] #333

tf198 opened this issue Apr 10, 2024 · 6 comments

Comments

@tf198
Copy link

tf198 commented Apr 10, 2024

Using the minimal example websocket code the quart server hangs on CTRL+C until all clients have closed their connections.
This also applies to SSE examples.

app.py

from quart import Quart, websocket
app = Quart(__name__)

@app.websocket('/ws')
async def ws():
    while True:
        data = await websocket.receive()
        await websocket.send(data)

client.py

import websocket, time

ws = websocket.WebSocket()
ws.connect(f'ws://localhost:5000/ws')
i = 0
while True:
    ws.send(str(i))
    data = ws.recv()
    print("GOT", data)
    time.sleep(1)
    i+=1
$ quart run
 * Serving Quart app 'app'
 * Debug mode: False
 * Please use an ASGI server (e.g. Hypercorn) directly in production
 * Running on http://127.0.0.1:5000 (CTRL + C to quit)
[2024-04-11 09:17:11 +1000] [596071] [INFO] Running on http://127.0.0.1:5000 (CTRL + C to quit)
[2024-04-11 09:17:18 +1000] [596071] [INFO] 127.0.0.1:48344 GET /ws 1.1 101 - 2055
^C
**hangs until client disconnects**

The server should be closing all open websockets on SIGINT and allow the errors to propagate for cleanup.

I've been unable to find a nice solution to this. Currently manually adding a SIGINT handler to the current asyncio loop and calling loop.close() but that doesn't allow for nice cleanup.

Environment:

  • Python version: 3.12.2
  • Quart version: 0.19.4
@tf198
Copy link
Author

tf198 commented Apr 11, 2024

Managed to get something that shuts down cleanly, including raising asicio.CancelledError on active connections
tf198@a6a9ec1
Not sure if it the most elegant solution but async messes with my head!

@denaillc
Copy link

Also looking for a solution to this in the main branch. When dockerized, my app will not stop running if a socketio conneciton is open until I refresh the client to make the connection go away. Then it terminates.

@zakx
Copy link

zakx commented Sep 24, 2024

I have the same issue and have not found a good way to handle this yet.

@npt
Copy link

npt commented Sep 26, 2024

I have the same issue with HTTP streaming as opposed to websockets.

Edit: This seems to really be an issue in Hypercorn — the blocking happens in hypercorn.asyncio.run.worker_serve() when it calls await server.wait_closed(). I managed to handle it, at the cost of some verbosity, by

  • passing my own asyncio.Event as shutdown_trigger when calling hypercorn.asyncio.serve()
  • before calling serve(), setting up a signal handler to trigger that event:
    for sig in (signal.SIGINT, signal.SIGTERM):
        asyncio.get_running_loop().add_signal_handler(sig, aborting.set)
  • in the streaming generator (/ websocket loop, presumably), checking that event and returning if it's set

@Erraen
Copy link

Erraen commented Oct 18, 2024

I have the same problem and it started showing up after switching to python 3.12. After rolling back to 3.11 everything works fine.

@zakx
Copy link

zakx commented Oct 18, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants