How do I detect a client disconnect from an async StreamingHttpResponse?

Hello Django fans!

I’m writing a Django app that involves sending notifications to users via long-polling and Server-Sent Events (SSE). My actual app does some moderately complicated stuff involving redis pub/sub channels, but this simplified code illustrates the basic technique that I’m using:

async def date_view(request: HttpRequest) -> StreamingHttpResponse:
    """Send the current date to the client every 1s."""
    return StreamingHttpResponse(
        date_stream(),
        content_type="text/event-stream",
        headers={
            "X-Accel-Buffering": "no",
            "Cache-Control": "no-cache",
        },
    )

async def date_stream() -> AsyncIterator[bytes]:
    """Generates SSE messages containing the date."""
    while True:
        await asyncio.sleep(1)
        yield utils.sse_message(
            data=timezone.now().isoformat().encode("utf8"),
            event="date"
        )

When I run this with Daphne, I get lots of error messages like this (I inserted some line breaks for readability):

WARNING 2023-07-16 00:33:58,168 server 815074 140525947258432
Application instance
<Task pending name='Task-1'
  coro=<ASGIStaticFilesHandler.__call__() running at
    /path/to/python3.10/site-packages/django/contrib/staticfiles/handlers.py:101>
  wait_for=<Future pending cb=[Task.task_wakeup()]>>
for connection
<WebRequest at 0x7fcec0f96ad0 method=GET
 uri=/notifications clientproto=HTTP/1.1>
took too long to shut down and was killed.

I assume this is because the while True loop runs forever, with nothing to stop it once the client disconnects.

My question: is there a way to gracefully detect disconnects? Something like Starlette’s request.is_disconnected. It seems like various parts of the stack do have this info, but I haven’t been able to find an elegant way of accessing it from my view.

Disconnect handling will be available from Django 5.0
See Asynchronous support | Django documentation | Django

Thanks, that is elegant! Guess I’ll have to come up with some hack in the mean time though.

If I needed this I’d either backport the single commit that added it or pick a commit on the main branch to deploy. (Either way I’d make sure my test suits was reliable.) Good luck.

1 Like