Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

gevent-socketio not using my @app.route endpoint for socketio

I am using Flask together with gevent-socketio:

$ cat requirements.txt 
Flask==0.10.1
Jinja2==2.7.1
MarkupSafe==0.18
Werkzeug==0.9.3
argparse==1.2.1
gevent==0.13.8
gevent-socketio==0.3.5-rc2
gevent-websocket==0.3.6
greenlet==0.4.1
itsdangerous==0.23
wsgiref==0.1.2

I'm using a pretty standard setup to start the server:

#Called from __main__
def run_dev_server():
    app.debug = True
    port = 5000
    dapp = werkzeug.debug.DebuggedApplication(app, evalex = True)
    SocketIOServer(('', port), dapp, resource="socket.io").serve_forever()

And a pretty standard hook for my SocketIO namespace:

@app.route('/socket.io/<path:rest>')
def push_stream(rest):
    print 'ws connect', rest
    try:
        socketio.socketio_manage(request.environ, {'/join_notification': JoinsNamespace}, request)
    except Exception as e:
        app.logger.error("Exception while handling socketio connection", exc_info=True)
    return flask.Response()

However, I'm having problems where 'connect' events aren't being fired on the client. After a little digging, I realized that even though I was getting 127.0.0.1 - - [2013-08-19 12:53:57] "GET /socket.io/1/websocket/170191232666 HTTP/1.1" 101 - - messages in the output, I wasn't getting the ws connect message (while other print statements in the code were working fine). I commented out that endpoint, and sure enough it's not even being called. That would explain why my namespace isn't being used. But why? Am I registering my namespace wrong?

print app.url_map yields:

Map([<Rule '/' (HEAD, OPTIONS, GET) -> root>,
 <Rule '/socket.io/<rest>' (HEAD, OPTIONS, GET) -> push_stream>,
 <Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>])

So nothing out of the ordinary.

Edit: The client code:

socket = io.connect('/join_notification')
console.log(socket)

socket.on('connect', function() {
    console.log('connected to websocket')
    socket.emit('login', {'name': data['name']})
})

socket.on('disconnect', function() {
    console.log('d/c\'d from websocket')
})

socket.on('join_error', function() {
    ...
})

socket.on('join_success', function(data){
    ...
})

socket.on('join', function(data) {
    ...
})
like image 510
vgel Avatar asked Aug 19 '13 17:08

vgel


People also ask

How do you run a Socket.IO in a Flask?

The socketio. run() function encapsulates the start up of the web server and replaces the app. run() standard Flask development server start up. When the application is in debug mode the Werkzeug development server is still used and configured properly inside socketio.

Can you use Socket.IO with Django?

Django with SocketIOCreate a socket app in Django project and add it to INSTALLED_APPS . Now inside socketio_app , edit views.py and add following code. Download views.py file from here. This code will create socket events for your applications.

What is Socket.IO in Flask?

Flask-SocketIO gives Flask applications access to low latency bi-directional communications between the clients and the server.

What is Socket.IO Python?

Socket.IO is a library that enables real-time, bidirectional and event-based communication between the browser and the server. It consists of: a Node. js server: Source | API. a Javascript client library for the browser (which can be also run from Node.


2 Answers

The weird behavior is because of this line:

dapp = werkzeug.debug.DebuggedApplication(app, evalex = True)

Socketio and the werkzeug debugger don't work together. There is already an open issue about this see: https://github.com/abourget/gevent-socketio/issues/114

But you can work around it by making a custom debugger class.

from werkzeug.debug import DebuggedApplication
class MyDebuggedApplication(DebuggedApplication):
    def __call__(self, environ, start_response):
        # check if websocket call
        if "wsgi.websocket" in environ and not environ["wsgi.websocket"] is None:
            # a websocket call, no debugger ;)
            return self.app(environ, start_response)
        # else go on with debugger
        return DebuggedApplication.__call__(self, environ, start_response)

# remember to call the overwritten debugger in your run_dev_server() function
dapp = MyDebuggedApplication(app, evalex = True)

The patch relies on the environment-key wsgi.websocket, which only seems to be present in websocket calls. Be careful, I haven't put much thought into that, there are might be other issues.

like image 57
m2k Avatar answered Nov 15 '22 22:11

m2k


Took me a while, but it looks like I've solved it, enjoy:

https://github.com/Aldanor/SocketIO-Flask-Debug

In a nutshell:

  • you need to explicitly extract the values from the generator returned by werkzeug.debug.DebuggedApplication whenever a socket.io request is spotted, this enables to establish the socket connection properly
  • socket.io namespace handlers will not be covered by werkzeug by default, so you need to insert your own try catch, save the traceback if an exception is caught and then reraise it somewhere within a request context
like image 33
aldanor Avatar answered Nov 15 '22 23:11

aldanor