Replies: 3 comments 20 replies
-
I think |
Beta Was this translation helpful? Give feedback.
-
This is not SSE specific. This is because Uvicorn fails to shutdown while the client is still connected. Here is a simple app that is slow to send the body to the client: import asyncio
async def app(scope, receive, send):
assert scope['type'] == 'http'
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
(b'content-type', b'text/plain'),
],
})
await asyncio.sleep(1e9) # can be forever
await send({
'type': 'http.response.body',
'body': b'Hello, world!',
}) A second Ctrl-C would do a "force-quit", but that doesn't work. But the worst thing is that there is no timeout on the handler. It's a DoS vulnerability. |
Beta Was this translation helpful? Give feedback.
-
Granian maintainer here.
SSE just exposes this behaviour, but from my experience forcefully close every connection on ctrl+c/signals is worse. So that's why every server waits for existing connection to shut down and avoid to abruptly close them. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I noticed that ASGI servers running an app with an active SSE connection will not shutdown when a
ctrl+c
is emitted.This can easily reproduced using this fastapi+htmx app:
Simply run
uvicorn app:app
and browse tohttp://127.0.0.1:8000/
, thenctrl+c
the server and note that it hangs with:Note that
uvicorn
is not the only ASGI server that behaves this way, I've tried Daphne, Hypercorn and Granian and they all behave the same. I also noticed that thesse-starlette
had to patch the uvicornServer.handle_exit
during import to work around this problem. This breaks the separation between the ASGI app and server, which makes this solution not ideal. However, it's hard to think of a better solution since the ASGI specification does not provide any event for when the shutdown is required: thelifespan.shutdown
event only occurs when all active connections have been closed.In comparison, ASGI servers do close active websocket connections when an exit signal is received. Here's the
WebSocketProtocol.shutdown
method in uvicorn:uvicorn/uvicorn/protocols/websockets/websockets_impl.py
Lines 147 to 153 in 66b9b58
The same thing could be implemented for SSE, discriminating for the
text/event-stream
content type specifically. For instance, this rough patch seems to do the job:I imagine that the difference between SSE and websockets is that websockets can be seen as a different protocol with its own rules, while SSE is merely a thin layer on top of long running HTTP response, which we might generally want to run to completion when the server shutdowns. In that sense, SSE would be an exception. Also, notice that the ASGI specification does mention websockets specifically, but doesn't mention SSE at all.
It seems like this is a situation where it's unclear whether it's the responsibility of the ASGI servers, frameworks or specification to take this problem into account. However I think it would be very valuable for the community to tackle this issue since SSE are still a popular solution for unidirectional event-based communication.
Beta Was this translation helpful? Give feedback.
All reactions