Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Flask, how to detect a SSE client disconnect from front end Javascript

Tags:

Maybe this is a problem in Flask, there is no way to handle disconnection event on the server side.

In Response class, there is a method named "call_on_close", where we can add a function without argument, e.g. on_close(), it will be fired when the response object's close method called, but this doesn't happen when I call EventSource.close() from the client side in Javascript.

code on server side:

from flask import Response r = Response(stream(), ...) r.call_on_close(on_close) return r   def on_close():   print "response is closed!"  def stream():   ...  # subscribe to redis   for message in pubsub.listen():     ....     yield 'data: %s\n\n' % message 

on client side: add unload handler to page with SSE

$(window).unload(   function() {     sse.close();   } } 

Anything is wrong?

Any suggestions or solution with code is appreciated!

Thanks in advance!

like image 345
bwlee Avatar asked Aug 22 '13 14:08

bwlee


2 Answers

The generator receives a GeneratorExit exception, and that's when you know it will exit. For example:

def stream():     try:         i = 0         while True:             yield 'Hello {}!'.format(i)             i += 1             time.sleep(1)     finally:         # Called after a GeneratorExit, cleanup here         i = 0   @app.route('/messages') def messages():     return Response(stream(), content_type='text/event-stream') 

Will yield a infinite stream of "Hello!", and you will know when it's done, where you can run cleanup code. If your generator blocks the thread, it will need to be unblocked in some way (maybe pushing a dummy item) so that the generator can be closed.

like image 181
Lonami Avatar answered Sep 25 '22 09:09

Lonami


I've had a similar problem with Rails Live Controllers. The problem is that the framework doesn't seem to detect that the connection is closed until it attempts to send an Event to the client.

One approach is to send periodic "heartbeat" events to the client. I'm presently using this successfully on my Rails project with an interval of 60 seconds. I have a separate thread that "emits" these heartbeats into Redis, that my controller has subscribed to.

An alternative to the threaded approach, is to wrap the Redis pubsub block with a timeout (again, say 60 seconds). And then send the heartbeat event to the client - followed by another pubsub call. The downside to this approach, is you may miss an event while you're not subscribed.

There's more on the threading approach here: Redis + ActionController::Live threads not dying

like image 38
Nigel Ramsay Avatar answered Sep 24 '22 09:09

Nigel Ramsay