Search code examples
pyramidserver-sent-events

Server Sent Events with Pyramid - How to detect if the connection to the client has been lost


I have a pyramid application that send SSE messages. It works basically like these:

def message_generator():
    for i in range(100):
        print("Sending message:" + str(i))
        yield "data: %s\n\n" % json.dumps({'message': str(i)})
        time.sleep(random.randint(1, 10))

@view_config(route_name='events')
def events(request):
    headers = [('Content-Type', 'text/event-stream'),
               ('Cache-Control', 'no-cache')]
    response = Response(headerlist=headers)
    response.app_iter = message_generator()
    return response

When I browse to /events I get the events. When I move to another page the events stop, when I close the browser the events stop.

The problem happens for example if I am in /events and I switch off the computer. The server does not know that the client got lost and message_generator keeps sending messages to the void.

In this page: A Look at Server-Sent Events mention this:

...the server should detect this (when the client stops) and stop sending further events as the client is no longer listening for them. If the server does not do this, then it will essentially be sending events out into a void.

Is there a way to detect this with Pyramid? I tried with

request.add_finished_callback()

but this callback seems to be called with

return response

I use Gunicorn with gevent to start the server.

Any idea is highly appreciated


Solution

  • From PEP 3333:

    Applications returning a generator or other custom iterator should not assume the entire iterator will be consumed, as it may be closed early by the server.

    Basically a WSGI server "should" invoke the close() method on the app_iter when a client disconnects (all generators, such as in your example, support this automatically). However, a server is not required to do it, and it seems many WSGI servers do not. For example, you mentioned gunicorn (which I haven't independently verified), but I did verify that waitress also does not. I opened [1] on waitress as a result, and have been working on a fix. Streaming responses in WSGI environments is shaky at best and usually depends on the server. For example, on waitress, you need to set send_bytes=0 to avoid it buffering the response data.

    [1] https://github.com/Pylons/waitress/issues/236