heroku-python / flask-sockets

[DEPRECATED] Alternative: https://github.com/miguelgrinberg/flask-sock
MIT License
1.74k stars 167 forks source link

WebSocket dies with handler return #60

Closed hoehermann closed 6 years ago

hoehermann commented 6 years ago

Hi

I am using flask to run a long-running task in the background. I now want to use flask-sockets to send progress updates to the user. Consider this example where queue is filled with progress updates as they become available:

@sockets.route('/progress')
def progress(ws):
    while not ws.closed:
        ws.send(queue.get())

In principle, this code is running fine, but the flask application is blocked until the handler returns. I see how this is consistent with flask's single process architecture, but I expect websockets to work asynchronously, too. I tried introducing an explicit thread like this:

@sockets.route('/progress')
def progress(ws):
    def target(ws,queue):
        while not ws.closed:
            ws.send(queue.get())
    threading.Thread(target=target, args=(ws, queue)).start()

Unfortunately, the thread dies with the exception WebSocketError: Socket is dead. It looks like the socket is closed by the server as soon as progress returns and is then considered dead, although it is still available in target's context.

Is sending from background threads unsupported or am I missing something? In case it is depending on the server used, I am using the pywsgi.WSGIServer as shown in the example code.

Kind regards

hoehermann commented 6 years ago

After digging further, I noticed #15 containing information regarding gunicorn, gevent and the chat example. It looks like a flask-sockets handler is already threaded implicitly using gevent, but I need to prevent premature handler return and allow switching to another thread explicitly by invoking gevent.sleep() like this:

@sockets.route('/progress')
def progress(ws):
    def target(ws,queue):
        while not ws.closed:
            ws.send(queue.get())
    threading.Thread(target=target, args=(ws, queue)).start()
    while not ws.closed:
        gevent.sleep(10)

It looks a bit backwards. Ideally, I'd like to block the execution until the socket is closed, but that would require to override the socket's on_close handler as mentioned here.