I’m trying to implement simple SSE with Django Channel’s AsyncHttpConsumer.
My minimal example:
class BaseSSEConsumer(AsyncHttpConsumer, ABC):
    cors_headers = [
        (b"Access-Control-Allow-Origin", b"*"),
        (b"Access-Control-Allow-Methods", b"GET, OPTIONS"),
        (
            b"Access-Control-Allow-Headers",
            b"Content-Type, Cache-Control, Authorization",
        ),
    ]
    default_headers = [
        (b"Cache-Control", b"no-cache"),
        (b"Content-Type", b"text/event-stream"),
        (b"Transfer-Encoding", b"chunked"),
        *cors_headers,
    ]
    async def handle(self, body):
        await self.send_headers(status=200, headers=self.default_headers)
        params = self._parse_query_params()
        message = {
            "type": "sse",
            "data": {
                "timestamp": datetime.now().isoformat(),
                "params": params,
            },
        }
        event = f"data: {json.dumps(message)}\n\n"
        while True:
            print(f"Sending SSE message")
            await self.send_body(body=event.encode(), more_body=True)
            await asyncio.sleep(1)
    async def disconnect(self, close_code=None):
        print("Disconnecting BaseSSEConsumer")
    def _parse_query_params(self) -> dict[str, list]:
        query_string = self.scope["query_string"].decode()
        return parse_qs(query_string)
I’m successfully sending events. When i close a browser’s tab the disconnect method doesn’t get called, instead after waiting for a few seconds i get following exception:
Application instance <Task pending name='Task-1' coro=<ASGIStaticFilesHandler.__call__() running at /usr/local/lib/python3.13/site-packages/django/contrib/staticfiles/handlers.py:101> wait_for=<Future pending cb=[Task.task_wakeup()]>> for connection <WebRequest at 0x7f5495d35d30 method=GET uri=/api/sse/?symbols=AAPL%2CGOOG%2CMSFT clientproto=HTTP/1.1> took too long to shut down and was killed.
I’m running the runserver with daphne.
Things i tried without success:
- changing the server to uvicorn
- offloading request processing to another thread
Is disconnecting from client supported by Channels? Should disconnect method be called after closing browser’s tab? Thanks!