miguelgrinberg / Flask-SocketIO

Socket.IO integration for Flask applications.
MIT License
5.31k stars 888 forks source link

Multiple SocketIO instances connected to the same Flask server #2022

Closed Fjf closed 8 months ago

Fjf commented 8 months ago

Describe the bug I have an application which attempts to have two separate SocketIO instances over different paths (e.g., localhost:80/socket.io and localhost:80/socket.io.2). This seems to be not supported, as the last socketio.init_app(app) will take precedence over what events the test_client will receive. However, I do seem to get the events when running it 'deployed' over gunicorn+eventlet.

To Reproduce Initialize two socketio instances with the init_app construction

sio = SocketIO(path="socket1")
sio2 = SocketIO2(path="socket2")
def create_app():
    app = Flask(
        __name__,
        template_folder='../client/public',
        static_folder='../client/public/static'
    )
    sio.init_app(app)
    sio2.init_app(app)
    return app

Then have a test looking like this:

def test_socketio_endpoint_fail(self):
    # Create test client and connect
    socketio_client = sio.test_client(self.app, flask_test_client=self.client)
    self.assertTrue(socketio_client.is_connected())

    # Test failing event
    data = {"dummy": "data"}
    socketio_client.emit("text_request", data)
    # Validate response received
    responses = socketio_client.get_received()
    error = responses[0]
    self.assertEquals(error["name"], "error")
    self.assertEquals(error["args"][0]["code"], 404)
    socketio_client.disconnect()

Expected behavior Regardless of initialization order, both socketio instances should be valid for testing.

Logs

When initializing in the order (sio -> sio2)

Server initialized for eventlet.
Server initialized for eventlet.
received event "text_request" from UxTbdPkQOIAYTXJUAAAA [/]
INFO:socketio.server:received event "text_request" from UxTbdPkQOIAYTXJUAAAA [/]
emitting event "error" to UxTbdPkQOIAYTXJUAAAA [/]
INFO:socketio.server:emitting event "error" to UxTbdPkQOIAYTXJUAAAA [/]

Error
Traceback (most recent call last):
  File "tests/test_socketio.py", line 68, in test_socketio_endpoint_fail
    error = responses[0]
IndexError: list index out of range

When initializing in the order (sio2 -> sio)

Server initialized for eventlet.
Server initialized for eventlet.
received event "text_request" from vcIKtkTIX2jFfIp2AAAA [/]
INFO:socketio.server:received event "text_request" from vcIKtkTIX2jFfIp2AAAA [/]
emitting event "error" to vcIKtkTIX2jFfIp2AAAA [/]
INFO:socketio.server:emitting event "error" to vcIKtkTIX2jFfIp2AAAA [/]
tests/test_socketio.py:69: DeprecationWarning: Please use assertEqual instead.
  self.assertEquals(error["name"], "error")

Ran 1 test in 2.030s
OK
miguelgrinberg commented 8 months ago

This is not an issue. The way Flask extensions attach to a Flask application instance makes it impossible to have two active instances of an extension attached to one application. What you need to do is create two Flask applications, then attach a Flask-SocketIO instance to each. Finally use a dispatcher middleware to route traffic between the Flask applications.