Sebasthian Ogalde
Sebasthian Ogalde

Reputation: 517

Flask and Flask SocketIO: receiving an event from inside of an external module

I am developing an application to read some information from scientific instruments and displaying it using a remote web client. I am achieving this with Flask and Flask-SocketIO. I have a main application.py file which contains a Flask instance called app. I use this together with Flask-SocketIO. The HTML page deployed by Flask contains a Javascript Socketio instance which communicates with the server application (in python).

Besides, I have an external module which represents my scientific instrument. I want to receive events from the website (in Javascript) to this module-class. I tried passing the app and socketio python objects to the constructor in order to request the context from the Flask app. This works very well only for emitting events from the module.

In short, application.py has this basic structure:

from gevent import monkey
monkey.patch_all()
from flask import Flask, Response, render_template, session, request, g
from flask_socketio import SocketIO, emit, disconnect, Namespace
from Instrument import *

app = Flask(__name__)
app.debug = True
socketio = SocketIO(app)

# some stuff here with @app.routes
@app.route('/instrument')
def inst(arg):
    t = Instrument(arg, app, socketio)

if __name__ == '__main__':
    socketio.run(app, port=8000, log_output=True)

For the external module Instrument.py I tried to use the decorator syntax unsuccessfully (kind of @self.socketio.on, I am not so acquainted with them). So I tried with the following code:

class Instrument():
    def __init__(self, arg, app, socketio):
        self.app = app
        self.socketio = socketio
        self.socketio.on_event('someCoolEvent', self.someCoolMethod, namespace='/coolapp') # THIS GIVES ME ERRORS

    def someCoolMethod():
        with self.app.test_request_context('/coolapp'):
            self.socketio.emit('emittedEvent', namespace='/telescope') # THIS WORKS FINE

The error I am getting is the following (EDITED: added two lines that can be useful):

[2017-03-03 06:31:31,456][INFO] - _handle_event: received event "my event" from a40724e9e60e4a61ace9e19e59ceabda [/telescope]
[2017-03-03 06:31:31,604][INFO] - handle_get_request: a40724e9e60e4a61ace9e19e59ceabda: Received request to upgrade to websocket
[2017-03-03 06:31:33 +0000] [6333] [ERROR] Error handling request /socket.io/?EIO=3&transport=websocket&sid=24e7d134ea4c4840888c28e0e3ff1f6d
Traceback (most recent call last):
  File "/home/user/instr_gui/venv/local/lib/python2.7/site-packages/gunicorn/workers/async.py", line 52, in handle
    self.handle_request(listener_name, req, client, addr)
  File "/home/user/instr_gui/venv/local/lib/python2.7/site-packages/gunicorn/workers/ggevent.py", line 152, in handle_request
    super(GeventWorker, self).handle_request(*args)
  File "/home/user/instr_gui/venv/local/lib/python2.7/site-packages/gunicorn/workers/async.py", line 103, in handle_request
    respiter = self.wsgi(environ, resp.start_response)
  File "/home/user/instr_gui/venv/local/lib/python2.7/site-packages/flask/app.py", line 1994, in __call__
    return self.wsgi_app(environ, start_response)
  File "/home/user/instr_gui/venv/local/lib/python2.7/site-packages/flask_socketio/__init__.py", line 42, in __call__
    start_response)
  File "/home/user/instr_gui/venv/local/lib/python2.7/site-packages/engineio/middleware.py", line 47, in __call__
    return self.engineio_app.handle_request(environ, start_response)
  File "/home/user/instr_gui/venv/local/lib/python2.7/site-packages/socketio/server.py", line 353, in handle_request
    return self.eio.handle_request(environ, start_response)
  File "/home/user/instr_gui/venv/local/lib/python2.7/site-packages/engineio/server.py", line 260, in handle_request
    environ, start_response)
  File "/home/user/instr_gui/venv/local/lib/python2.7/site-packages/engineio/socket.py", line 86, in handle_get_request
    start_response)
  File "/home/user/instr_gui/venv/local/lib/python2.7/site-packages/engineio/socket.py", line 127, in _upgrade_websocket
    return ws(environ, start_response)
  File "/home/user/instr_gui/venv/local/lib/python2.7/site-packages/engineio/async_gevent.py", line 34, in __call__
    raise RuntimeError('You need to use the gevent-websocket server. '
RuntimeError: You need to use the gevent-websocket server. See the Deployment section of the documentation for more information.

When the app is initialised, the log says:

2017-03-04 02:52:04 [23530] [INFO] Starting gunicorn 18.0
2017-03-04 02:52:04 [23530] [INFO] Listening at: http://127.0.0.1:8000 (23530)
2017-03-04 02:52:04 [23530] [INFO] Using worker: gevent
2017-03-04 02:52:04 [23534] [INFO] Booting worker with pid: 23534

So I have tried configuring the file /etc/init/myapp.conf with both gunicorn -k gevent -w 1 module:app and gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 module:app but I'm getting the same error.

At the browser I see another error, that appears at the same time as the 'main' one:

WebSocket connection to 'ws://somewebpage.com:2080/socket.io/?
EIO=3&transport=websocket&sid=572923d1b8fd402795bba50823941520' 
failed: Error during WebSocket handshake: Unexpected response code: 500

How can I receive events correctly from the external module? Maybe there is a better way to achieve the result I want. I would be very grateful if someone gives me a hand with this.

Upvotes: 2

Views: 2622

Answers (3)

Sebasthian Ogalde
Sebasthian Ogalde

Reputation: 517

I answer myself. I found the following post, which explains that the error:

WebSocket connection to 'ws://somewebpage.com:2080/socket.io/?
EIO=3&transport=websocket&sid=572923d1b8fd402795bba50823941520' 
failed: Error during WebSocket handshake: Unexpected response code: 500

is caused due to a misconfiguration in the nginx configuration file. It should be like this:

...
location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_redirect off;

    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";

    proxy_set_header Host $http_host;
}
...

Now works like a charm: emitting and receiving events, from the imported module and from the main python script.

Upvotes: 1

Miguel Grinberg
Miguel Grinberg

Reputation: 67547

The error that you are getting is unrelated to your problem, I think. It's saying that you are using the standard gunicorn worker for gevent, but you need to use the gevent-websocket worker for websocket to work. See the last example in this section of the documentation.

Upvotes: 0

Siva Sakthi Velan
Siva Sakthi Velan

Reputation: 90

can you try add policy_server=False in socketio.run(app, port=8000, log_output=True,policy_server=False)

Upvotes: 0

Related Questions