Skip to content
Advertisement

(flask + socket.IO) Result of emit callback is the response of my REST endpoint

Just to give a context here, I’m a node.JS developer, but I’m on a project that I need to work with Python using Flask framework.

The problem is, when a client request to an endpoint of my rest flask app, I need to emit an event using socket.IO, and get some data from the socket server, then this data is the response of the endpoint. But I didn’t figured out how to send this, because flask needs a “return” statement saying what is the response, and my callback is in another context.

Sample of what I’m trying to do: (There’s some comments explaining)

import socketio
import eventlet
from flask import Flask, request

sio = socketio.Server()
app = Flask(__name__)


@app.route('/test/<param>')
def get(param):
    def ack(data):
        print (data) #Should be the response
    sio.emit('event', param, callback=ack) # Socket server call my ack function
    #Without a return statement, the endpoint return 500

if __name__ == '__main__':
    app = socketio.Middleware(sio, app)
    eventlet.wsgi.server(eventlet.listen(('', 8000)), app)

Maybe, the right question here is: Is this possible?

Advertisement

Answer

I’m going to give you one way to implement what you want specifically, but I believe you have an important design flaw in this, as I explain in a comment above. In the way you have this coded, your socketio.Server() object will broadcast to all your clients, so will not be able to get a callback. If you want to emit to one client (hopefully not the same one that sent the HTTP request), then you need to add a room=client_sid argument to the emit. Or, if you are contacting a Socket.IO server, then you need to use a Socket.IO client here, not a server.

In any case, to block your HTTP route until the callback function is invoked, you can use an Event object. Something like this:

from threading import Event
from flask import jsonify

@app.route('/test/<param>')
def get(param):
    ev = threading.Event()
    result = None

    def ack(data):
        nonlocal result
        nonlocal ev

        result = {'data': data}
        ev.set()  # unblock HTTP route

    sio.emit('event', param, room=some_client_sid, callback=ack)
    ev.wait()  # blocks until ev.set() is called
    return jsonify(result)

Advertisement