RuntimeError: working outside of request context

user1601716 picture user1601716 · Dec 1, 2014 · Viewed 7.4k times · Source

I am trying to create a 'keepalive' websocket thread to send an emit every 10 seconds to the browser once someone connects to the page, but I'm getting an error and am not sure how to get around it.

Any ideas on how to make this work?

And how would I kill this thread once a 'disconnect' is sent?

Thanks!

@socketio.on('connect', namespace='/endpoint')
def test_connect():
    emit('my response', {'data': '<br>Client thinks i\'m connected'})

    def background_thread():
        """Example of how to send server generated events to clients."""
        count = 0
        while True:
            time.sleep(10)
            count += 1
            emit('my response', {'data': 'websocket is keeping alive'}, namespace='/endpoint')

    global thread
    if thread is None:
        thread = Thread(target=background_thread)
        thread.start()

Answer

Miguel picture Miguel · Dec 1, 2014

You wrote your background thread in a way that requires it to know who's the client, since you are sending a direct message to it. For that reason the background thread needs to have access to the request context. In Flask you can install a copy of the current request context in a thread using the copy_current_request_context decorator:

@copy_current_request_context
def background_thread():
    """Example of how to send server generated events to clients."""
    count = 0
    while True:
        time.sleep(10)
        count += 1
        emit('my response', {'data': 'websocket is keeping alive'}, namespace='/endpoint')

Couple of notes:

  • It is not necessary to set the namespace when you are sending back to the client, by default the emit call will be on the same namespace used by the client. The namespace needs to be specified when you broadcast or send messages outside of a request context.
  • Keep in mind your design will require a separate thread for each client that connects. It would be more efficient to have a single background thread that broadcasts to all clients. See the example application that I have on the Github repository for an example: https://github.com/miguelgrinberg/Flask-SocketIO/tree/master/example

To stop the thread when the client disconnects you can use any multi-threading mechanism to let the thread know it needs to exit. This can be, for example, a global variable that you set on the disconnect event. A not so great alternative that is easy to implement is to wait for the emit to raise an exception when the client went away and use that to exit the thread.