Using eventlet to manage socketio in Flask

zeck picture zeck · Jan 12, 2016 · Viewed 15.9k times · Source

I am trying to set up a small server to handle HTTP and socketio requests -- I don't have much experience setting up servers, but right now apache2 serves the http just fine. The socketio transactions, however, keep failing with error code 400 (bad request), and I see some strange errors in the server logs. Sometimes I see an engineio error and the server responds w/ a 'bad request' and code 400, but always it tells me the eventlet server needs to be started:

[Mon Jan 11 19:02:54.068282 2016] [:error] [pid 4908:tid 140274923673344] [client 100.96.180.39:53473]     return ws(environ, start_response)
[Mon Jan 11 19:02:54.068305 2016] [:error] [pid 4908:tid 140274923673344] [client 100.96.180.39:53473]   File "/var/www/projectENV/lib/python2.7/site-packages/engineio/async_eventlet.py", line 10, in __call__
[Mon Jan 11 19:02:54.068342 2016] [:error] [pid 4908:tid 140274923673344] [client 100.96.180.39:53473]     raise RuntimeError('You need to use the eventlet server.')
[Mon Jan 11 19:02:54.068380 2016] [:error] [pid 4908:tid 140274923673344] [client 100.96.180.39:53473] RuntimeError: You need to use the eventlet server. See the Deployment section of the documentation for more information.
[Mon Jan 11 19:02:54.253124 2016] [:error] [pid 4909:tid 140274940458752] WARNING:engineio:Invalid session cde3f9aadbee4794bf9d7bb98d0b396e

My server code is pretty basic:

 from flask import Flask
 import flaskext.couchdb
 from flask.ext.socketio import SocketIO

 # for socketio
 import eventlet
 eventlet.monkey_patch()

 # creation of server & db objects
 app = Flask(__name__)

 # socketio initialization
 socketio =  SocketIO(app, async_mode='eventlet')

 # import views once site properties are set
 from app import views

 if __name__== "__main__":
     socketio.run(app, debug=True)

And my client code, written in python, uses the socketio-client library straight from the docs:

from socketIO_client import SocketIO, LoggingNamespace
with SocketIO(SERVER_URL, 80, LoggingNamespace) as socketIO:
    socketIO.emit('aaa')
    socketIO.wait(seconds=1)

Isn't the socketio.run(app) supposed to start the eventlet server for me? Why is the server spitting back bad request (sometimes)?

Answer

Miguel picture Miguel · Jan 12, 2016

To make a WSGI application available online you need to expose it through a web server. When your application uses Flask-SocketIO, a plain WSGI web server isn't sufficient, because WSGI does not support WebSocket, the WSGI protocol needs unofficial extensions to support this protocol.

Flask-SocketIO supports a variety of web servers that support WebSocket. It appears you have eventlet installed in your virtual environment, so that is why you receive the error that you have to use the eventlet web server.

What you don't seem to realize, is that you are using Apache's web server (I'm guessing mod_wsgi?). This web server is a normal, forking web server, it is not an eventlet compatible web server.

Isn't the socketio.run(app) supposed to start the eventlet server for me?

Yes, if you were to run your application via socketio.run(app) you would get a fully enabled eventlet web server. But you are not doing that, you are running it on apache. Eventlet has a web server, and apache has a web server, they are two separate web servers, both able to run a WSGI application. But the apache one does not support WebSocket.

The Flask-SocketIO documentation describes a few deployment scenarios that are valid.