Giter Site home page Giter Site logo

flask-uwsgi-websocket's Introduction

Flask-uWSGI-WebSocket

High-performance WebSockets for your Flask apps powered by uWSGI. Low-level uWSGI WebSocket API access and flexible high-level abstractions for building complex WebSocket applications with Flask. Supports several different concurrency models including Gevent. Inspired by Flask-Sockets.

from flask import Flask
from flask_uwsgi_websocket import GeventWebSocket

app = Flask(__name__)
websocket = GeventWebSocket(app)

@websocket.route('/echo')
def echo(ws):
    while True:
        msg = ws.receive()
        ws.send(msg)

if __name__ == '__main__':
    app.run(gevent=100)

Installation

Preferred method of installation is via pip:

$ pip install Flask-uWSGI-WebSocket

Installing uWSGI

Of course you'll also need uWSGI (with SSL support, at minimum). It can also be installed with pip:

$ pip install uwsgi

If that fails or you need to enable the asyncio plugin, read on.

uWSGI on Mac OS X

On some versions of Mac OS X, OpenSSL headers are no longer included. If you use Homebrew, install OpenSSL and ensure they are available:

$ brew install openssl && brew link openssl --force

This should ensure pip can install uWSGI:

$ LDFLAGS="-L/usr/local/lib" pip install uwsgi --no-use-wheel

If you plan to use the asyncio plugin, you'll need to ensure that it's enabled when uWSGI is compiled. You can use UWSGI_PROFILE to do this. With Homebrew Python 3.5 installed:

$ LDFLAGS="-L/usr/local/lib" CFLAGS="-I/usr/local/include/python3.5m" UWSGI_PROFLILE="asyncio" pip3 install uwsgi --no-use-wheel

uWSGI on Linux

If your Linux distribution includes uWSGI with specific plugins, that is many times your best bet. If that fails or you'd prefer to compile uWSGI yourself, you'll need to ensure that the requisite build tools, OpenSSL headers, etc are installed:

$ apt-get install build-essential libssl-dev python3-dev python3-venv

According to the uWSGI asyncio docs, UWSGI_PROFILE and greenlet.h location should be specified.

If you are installing uWSGI into a virtualenv, the process is:

$ python3 -m venv pyvenv
$ . pyvenv/bin/activate
(pyvenv)$ pip install greenlet

Now, greenlet.h should be available at $VIRTUAL_ENV/include/site/python3.5. To build with pip:

$ mkdir -p $VIRTUAL_ENV/include/site/python3.5/greenlet
$ ln -s ../greenlet.h $VIRTUAL_ENV/include/site/python3.5/greenlet/
$ CFLAGS="-I$VIRTUAL_ENV/include/site/python3.5" UWSGI_PROFILE="asyncio" pip install uwsgi --no-use-wheel

Deployment

You can use uWSGI's built-in HTTP router to get up and running quickly:

$ uwsgi --master --http :8080 --http-websockets --wsgi echo:app

...which is what app.run does after wrapping your Flask app:

app.run(debug=True, host='localhost', port=8080, master=true, processes=8)

uWSGI supports several concurrency models, in particular it has nice support for Gevent. If you want to use Gevent, import flask_uwsgi_websocket.GeventWebSocket and configure uWSGI to use the gevent loop engine:

$ uwsgi --master --http :8080 --http-websockets --gevent 100 --wsgi echo:app

...or:

app.run(debug=True, gevent=100)

Note that you cannot use multiple threads with gevent loop engine.

To enable asyncio instead:

$ uwsgi --master --http :5000 --http-websockets --asyncio 100 --greenlet --wsgi chat:app

...or:

app.run(debug=True, asyncio=100, greenlet=True)

For production you'll probably want to run uWSGI behind Haproxy or Nginx, instead of using the built-int HTTP router. Explore the uWSGI documentation to learn more about the various concurrency and deployment options.

Development

It's possible to take advantage of Flask's interactive debugger by installing Werkzeug's DebuggedApplication middleware:

from werkzeug.debug import DebuggedApplication
app.wsgi_app = DebuggedApplication(app.wsgi_app, True)

...and running uWSGI with only a single worker:

$ uwsgi --master --http :8080 --http-websockets --wsgi-file --workers 1 --threads 8 app.py

If you use app.run(debug=True) or export FLASK_UWSGI_DEBUG, Flask-uWSGI-Websocket will do this automatically for you.

Examples

There are several examples available here.

API

WebSocket

Applies WebSocketMiddleware to your Flask App, allowing you to decorate routes with the route method, turning them into WebSocket handlers.

Additionally monkey-patches app.run, to run your app directly in uWSGI.

route(url)

run(debug, host, port, **kwargs) **kwargs are passed to uWSGI as command line arguments.

WebSocketMiddleware

WebSocket Middleware which automatically performs WebSocket handshake and passes WebSocketClient instances to your route.

WebSocketClient

Exposes the uWSGI WebSocket API.

recv() (alias WebSocket.receive())

recv_nb()

send(msg)

send_binary(msg)

recv_nb()

send_from_sharedarea(id, pos)

send_binary_from_sharedarea(id, pos)

GeventWebSocket

Fancier WebSocket abstraction that takes advantage of Gevent loop engine. Requires uWSGI to be run with --uwsgi option.

GeventWebSocketMiddleware

Automatically performs WebSocket handshake and passes a GeventWebSocketClient instance to your route.

GeventWebSocketClient

WebSocket client abstraction with fully non-blocking methods.

receive()

send(msg)

close()

connected

AsyncioWebSocket

Fancier WebSocket abstraction that takes advantage of Asyncio loop engine. Requires uWSGI to be run with --asyncio and --greenlet option.

AsyncioWebSocketMiddleware

Automatically performs WebSocket handshake and passes a AsyncioWebSocketClient instance to your route.

AsyncioWebSocketClient

WebSocket client abstraction with asyncio coroutines.

coroutine a_recv() (alias receive(), recv())

coroutine a_send(msg) (alias send())

recv_nb() (should be useless)

send_nb() (should be useless)

close()

connected

Advanced Usage

Normally websocket routes happen outside of the normal request context. You can get a request context in your websocket handler by using app.request_context:

app = Flask(__name__)
ws = GeventWebSocket(app)

@ws.route('/websocket')
def websocket(ws):
    with app.request_context(ws.environ):
        print request.args

flask-uwsgi-websocket's People

Contributors

amstocker avatar ecarrara avatar fizzadar avatar flippmoke avatar hitigon avatar ivoz avatar matthewowen avatar rayalan avatar rosensama avatar s3thi avatar stepheny avatar svenstaro avatar thiefmaster avatar timmartin19 avatar voidpp avatar white-gecko avatar zeekay avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flask-uwsgi-websocket's Issues

Strange empty string messages (not PING/PONGS) coming from .receive()

i have the following configuration on mac:

Flask (0.10.1)
Flask-uWSGI-WebSocket (0.4.4)
gevent (1.0.2)
greenlet (0.4.9)
uWSGI (2.0.11.2)

my app code is:

from flask import Flask
from flask.ext.uwsgi_websocket import GeventWebSocket

app = Flask(__name__)
websocket = GeventWebSocket(app)

@websocket.route('/echo')
def echo(ws):
    while True:
        msg = ws.recv()
        if msg is None:
            return
        print type(msg)
        print 'MESSAGE RECEIVED!!! ', msg
        ws.send(msg)

The ws.recv() call periodically receives some empty string '' messages from nowhere. Client doesn't send them.

When i've plugged off the flask and flask-uwsgi-websocket the following code worked fine:

import uwsgi


def app(env, start_response):
    # complete the handshake
    uwsgi.websocket_handshake(env['HTTP_SEC_WEBSOCKET_KEY'],
                              env.get('HTTP_ORIGIN', ''))
    while True:
        msg = uwsgi.websocket_recv()
        uwsgi.websocket_send(msg)

My uWSGI config is:

[uwsgi]
http-socket = 127.0.0.1:9090
wsgi-file = app.py
callable = app
gevent = 100
processes = 4

Incompatibility with Python 3.7.0

In python 3.7 async has become a keyword. So async.py will need to be renamed and imported under the new name.

  File "/path/to/venv/lib/python3.7/site-packages/flask_uwsgi_websocket/__init__.py", line 16
    from .async import *
              ^
SyntaxError: invalid syntax

Get the full request url or request args

Hello, I have a question. How can I access the full url or the request args inside a websocket route?

example:

websocket = GeventWebSocket(app)

@websocket.route("/ws")
def my_ws(ws):
    print request.args  # Does not work.

Uwsgi path incorrect since merging #24

Since merging #24, the path of uwsgi is wrong. #24 says that uwsgi is always in the same directory as a python interpreter, but it's not the case for me.
I use my distro's python, and uwsgi is installed using the user scheme, so python is in /usr/bin and uwsgi in ~/.local/bin (or rather site.USER_BASE).

Asyncio "no PONG received in 3 seconds" after 30 seconds

Hi,
I am wondering if the asyncio implementation sends the PINGs in the background because I receive no PONG received in 3 seconds every single time after 30 seconds.

My understanding is that the ping is sent every websocket.timeout in seconds and the websocket waits 3 seconds to receive PONG message, am I right?

AttributeError: 'GeventWebSocketClient' object has no attribute 'recv'

The GeventWebSocketClient does not expose the same interface as normal WebSocket, making it just a tad bit harder to move from WebSocket to GeventWebSocket. GeventWebSocket misses the recv method, while WebSocket does have it.

Please add recv as a alias of recieve on GeventWebSocket.

feature request: non blocking receive

Hi,

could you add a non blocking receive method ?
uwsgi.websocket_recv_nb()
like https://github.com/unbit/uwsgi/blob/master/tests/websockets.py
or https://github.com/unbit/uwsgi/blob/master/tests/websockets_chat_async.py

i am trying to create an ssh bridge but using you library in place of geventwebsocket

like there https://github.com/aluzzardi/wssh
he is using two greenlet to achieve that,
but with uwsgi we can not call the send ir recv from a greenlet

take a look to this
https://github.com/aluzzardi/wssh/blob/master/wssh/server.py
https://github.com/aluzzardi/wssh/blob/master/bin/wsshd

cant connect socket with lastest gevent & uwsgi

hi,
flask-uwsgi-websocket don't work with a fresh virtualenvironement.

pip freeze
Flask==0.10.1
Flask-uWSGI-WebSocket==0.5.2
Jinja2==2.8
MarkupSafe==0.23
Werkzeug==0.11.9
argparse==1.2.1
distribute==0.6.34
gevent==1.1.1
greenlet==0.4.9
itsdangerous==0.24
uWSGI==2.0.12
wsgiref==0.1.2


uwsgi --master --http 0.0.0.0:8081 --http-websockets --gevent 100 --wsgi echo:app

the html page work but the socket can not be initialised.
No error on the log.

i'm pretty sure my configuration is correct because i am able to run this https://github.com/unbit/uwsgi/blob/master/tests/websockets.py

uwsgi_response_write_body_do() erros

Sometimes I get the following errors:

uwsgi_response_write_body_do(): Broken pipe [core/writer.c line 419] during GET /output (127.0.0.1)

or

uwsgi_websockets_recv_pkt(): Connection reset by peer [core/websockets.c line 194] during GET /output (127.0.0.1)

or

uwsgi_response_write_body_do(): Protocol wrong type for socket [core/writer.c line 419] during GET /output (127.0.0.1)

The above listed errors seem to only occur for process that require a long time to compute. Weirdly, I get different errors when I simply re-run the same request.

This is the code (exemplified) that I'm running on server (the code itself runs without error):

from flask import Flask
from flask.ext.uwsgi_websocket import GeventWebSocket

app = Flask('myApp', static_folder=cfg.CLIENT_DIR_LOCATION, static_url_path='')
ws = GeventWebSocket(app)

@ws.route('/output')
def run_job(ws):
   while True:
        message = ws.receive()
        if message:
            send_data = json.loads(message)
            data = send_data['data']
            # Then I run some code in a loop and return the output
           for i in data:
               output = i
               ws.send(json.dumps({
                        'event': 'output',
                        'data': output
               }))

And I run the app as follows:

app.run(debug=True, port=8000, gevent=100)

How can I prevent this from happening?

How can I fix this AsyncioWebSocket "RuntimeError: Event loop is closed" issue?

I setup a Flask instance using AsyncioWebSocket websockets and implemented the echo example. It works completely fine, I can send and receive websocket messages just fine without any error messages on neither browser nor server side.

But if I leave it for a like 5-10 minutes and then load the echo example again I get these error messages below all of a sudden.

How can I fix this issue?

{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474548634, "msg":"Traceback (most recent call last):"}
{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474549313, "msg":"  File \"/var/www/testweb/venv/lib/python3.5/site-packages/flask/app.py\", line 1997, in __call__"}
{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474551626, "msg":"    return self.wsgi_app(environ, start_response)"}
{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474552147, "msg":"  File \"/var/www/testweb/venv/lib/python3.5/site-packages/flask_uwsgi_websocket/_asyncio.py\", line 122, in __call__"}
{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474552939, "msg":"    client = self.client(environ, uwsgi.connection_fd(), self.websocket.timeout, greenlet.getcurrent())"}
{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474553487, "msg":"  File \"/var/www/testweb/venv/lib/python3.5/site-packages/flask_uwsgi_websocket/_asyncio.py\", line 23, in __init__"}
{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474554201, "msg":"    self._loop.add_reader(self.fd, self._recv_ready)"}
{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474554706, "msg":"  File \"/usr/lib/python3.5/asyncio/selector_events.py\", line 231, in add_reader"}
{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474555640, "msg":"    self._check_closed()"}
{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474556135, "msg":"  File \"/usr/lib/python3.5/asyncio/base_events.py\", line 334, in _check_closed"}
{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474557029, "msg":"    raise RuntimeError('Event loop is closed')"}
{"datetime":"11/15/2017 15:54:34", "timestamp":1510732474557545, "msg":"RuntimeError: Event loop is closed"}

KeyError Exception raised from `threading` module on script shutdown

When closing dev server with Ctrl+C i get this error:
Exception KeyError: KeyError(139689878552080,) in <module 'threading' from '/usr/lib/python2.7/threading.pyc'> ignored

I think the issue is that somewhere in the code threading module is imported before monkey_patch is called (as described here: http://stackoverflow.com/questions/8774958/keyerror-in-module-threading-after-a-successful-py-test-run).

But I moved my imports to the top of the file like so:

from flask_uwsgi_websocket import GeventWebSocket
from flask import Flask, render_template, jsonify, request
# Create app
app = Flask(__name__)
ws = GeventWebSocket(app)

and still get the error.

However, if I comment last line ws = GeventWebSocket(app) the error disapears.

Any help please?

Random error on init

Hi, I'm trying to use this component, but, sometimes (really random - most of time, it works fine), when my app restarts, it gives me this error (gevent and asyncio, doesn't matter, tried both):

'AsyncioWebSocket' object has no attribute 'before_request_funcs' AttributeError("'AsyncioWebSocket' object has no attribute 'before_request_funcs'",)
  File "core/init/flask.py", line 74, in <module>
  File "/usr/lib64/python3.4/site-packages/flask/app.py", line 62, in wrapper_func
    return f(self, *args, **kwargs)
  File "/usr/lib64/python3.4/site-packages/flask_uwsgi_websocket/websocket.py", line 162, in register_blueprint
    blueprint.register(self, options, first_registration)
  File "/usr/lib64/python3.4/site-packages/flask/blueprints.py", line 153, in register
    deferred(state)
  File "/usr/lib64/python3.4/site-packages/flask/blueprints.py", line 128, in wrapper
    func(state)
  File "/usr/lib64/python3.4/site-packages/flask/blueprints.py", line 273, in <lambda>
    self.record_once(lambda s: s.app.before_request_funcs

The code I used to init websockets is:

WebSockets = None  # Just load websockets if really needed
if bp_websockets:
    try:
        from flask.ext.uwsgi_websocket import GeventWebSocket as Sockets
        WebSockets = Sockets(app)
        for bp, prefixo in bp_websockets:
            print(' Registrando o blueprint WebSocket', prefixo)
            # The error occurs in this line.
            WebSockets.register_blueprint(bp, url_prefix=prefixo)
    except exception as E:
        from traceback import format_tb
        from sys import exc_info
        print('*** Erro ao configurar websockets', E, repr(E))
        print(''.join(format_tb(exc_info()[2])))

Is there a way to disable HTTP to only accept HTTPS?

We try to only use HTTPS in our dev environments (mkcert is helpful here) to avoid any issues when running in prod. So we'd like to disable the default HTTP listener on port 5000 so devs can't accidentally use HTTP. In dev, we launch uwsgi as follows

ws_app = GeventWebSocket(app)
ssl_cert = path.join(path.dirname(__file__), 'ssl.cert')
ssl_key = path.join(path.dirname(__file__), 'ssl.key')
app.run(https='0.0.0.0:5001,{},{}'.format(ssl_cert,ssl_key), gevent=100)`

It works, but maybe I'm cheating as I see no mention of https options in the source.

Is there a way to disable HTTP:5000 or maybe to add --http-to-https to the call?

Thanks

Strange exception appearing on my logs

Traceback (most recent call last):
File "/var/www/api.xxx.com/venv/local/lib/python2.7/site-packages/flask/app.py", line 1836, in call
return self.wsgi_app(environ, start_response)
File "/var/www/api.xxx.com/venv/local/lib/python2.7/site-packages/flask_uwsgi_websocket/_gevent.py", line 90, in call
recv_queue.put(uwsgi.websocket_recv_nb())
File "/var/www/api.xxx.com/venv/local/lib/python2.7/site-packages/gevent/queue.py", line 155, in put
result = waiter.get()
File "/var/www/api.xxx.com/venv/local/lib/python2.7/site-packages/gevent/hub.py", line 568, in get
return self.hub.switch()
File "/var/www/api.xxx.com/venv/local/lib/python2.7/site-packages/gevent/hub.py", line 331, in switch
return greenlet.switch(self)
gevent.hub.LoopExit: This operation would block forever

Receiving 404 response code when opening websocket connection

I've been trying to get this module working, but every time I run into a 404 issue. This happens with each one of the gevent examples. (I haven't tried non-gevent.) Not sure what else to include except for packages and versions:

Flask==0.10.1
Flask-uWSGI-WebSocket==0.4.5
gevent==1.0.2
greenlet==0.4.9
itsdangerous==0.24
Jinja2==2.8
MarkupSafe==0.23
uWSGI==2.0.11.1
Werkzeug==0.10.4

Any help would be much appreciated!

Sending data as binary

Is sending data with opcode BINARY supported?
I see that send takes a parameter binary, and it also has send_binary method. Whatever method I call, it always sends the data as TEXT.
The code for send and send_binary seems to be doing the same thing!


    def send(self, msg, binary=True):
        if binary:
            return self.send_binary(msg)
        self.send_queue.put(msg)
        self.send_event.set()

    def send_binary(self, msg):
        self.send_queue.put(msg)
        self.send_event.set()

Am I missing something, or is binary opcode not supported?

Back-to-back event receiving with gevent

I'm seeing a problem with receiving multiple events in v0.5.2. I have a browser that sends multiple events. However, flask-uwsgi-websocket only reads 1 event every timeout seconds (30, in my case). I believe the problem is two-fold:

  1. _gevent.py, line 70: The max receive queue size shouldn't be limited to 1.
  2. The clause around recv_is_set() should make sure that it reads all the events, with a minimum of one event. This seems to work for me:
elif recv_event.is_set():
    try:
        incoming_event = None
        is_first = True
        while incoming_event or is_first:
            incoming_event = uwsgi.websocket_recv_nb()
            if is_first or incoming_event:
                recv_queue.put(incoming_event)
            is_first = False
        recv_event.clear()
        listening = spawn(listener, client)
    except IOError:
        client.connected = False

The above can be cleaned up quite a bit. I also suspect that it is safe to reset the clear() flag before we finish reading all the current events. However, I rearranged it for testing purposes and I haven't gone back and verified that theory.

How to run uwsgi-websocket behind Apache

Hello,
I am trying to use the GeventWebsocket in an apache + uwsgi + flask product, so I already have an uwsgi instance in my app, and I know it'll start another uwsgi instance in the method Websocket.init_app().
I 'd like to know how should I modify the uwsgi config to use the same uwsgi with websocket and http works, should I not invoke the method Websocket.init_app() in the code ?

here is my config of apache and uwsgi:
apache config:
<VirtualHost *:80>
ProxyPass / uwsgi://127.0.0.1:5001/
</VirtualHost>

uwsgi config:
[uwsgi]
#application's base info
base = /srv/www/myProject
project = myProject
socket = 127.0.0.1:5001
chmod-socket = 666
processes = 2
master = 1
buffer-size = 8192
pyhome = %(base)/venv
pythonpath = %(base)/%(project)/src

#python module to import
wsgi-file = %(base)/%(project)/src/web/manage.py
callable = app

#location of log files
logto = %(base)/log/uwsgi/%n.log

start uwsgi:
uwsgi --master --emperor /etc/uwsgi/vassals --die-on-term --uid root --gid root

Actually I'm not very good at uwsgi, thank you in advance.

Using uWSGI's asyncio engine & Redis for generic websocket communication

Following issue #27, i've been trying to implement a send/receive asyncio listener coroutine as follows:


websock = WebSocket(app)
@websock.route('/websocket')
def echo(ws):
    r = redis_conn.pubsub()
    r.subscribe("test_channel")
    asyncio.Task(listen(ws,r))

With the coroutine (called as a task, temporary: to edit to something sensible when bugs clear):

@asyncio.coroutine
def listen(ws,r):
    for msg in r.listen():
        ws.send(str(msg['data']))
    
    while True:
        msg=ws.receive()
        if msg is not None:
            print(msg)
        else:
            yield from asyncio.wait(1)

The server (namely, main flask app) should be able to send a message from anywhere using e.g.,redis_conn.publish('test_channel','hello @'+str(time.time()))

Using uwsgi (compiled with asyncio support) v2.0.10 and greenlet v0.4.7.

It seems uwsgi/websockets using uwsgi's asyncio event loop are is not supported in this configuration; asyncio is not in the supported concurrency models list (here

The error i'm getting when trying to connect is (browser to websocket route):

[BUG] current_wsgi_req NOT FOUND !!!
Task exception was never retrieved
future:  exception=SystemError('you can call uwsgi api function only from the main callable',)>
Traceback (most recent call last):
  File "/usr/lib/python3.4/asyncio/tasks.py", line 237, in _step
    result = next(coro)
  File "./api/__init__.py", line 80, in try_listen
    msg = ws.receive()
  File "./api/apivenv3/lib/python3.4/site-packages/flask_uwsgi_websocket/websocket.py", line 21, in receive
    return self.recv()
  File "./api/apivenv3/lib/python3.4/site-packages/flask_uwsgi_websocket/websocket.py", line 25, in recv
    return uwsgi.websocket_recv()
SystemError: you can call uwsgi api function only from the main callable

Any way around this error from within flask? [BUG] suggests uWSGI can't tell where the request is from (thread/process issue?)

flask-uwsgi-websocket responds with wrong connection header?

I could be misunderstanding something here, as I'm quite new to python/flask/websockets.

I have a simple socket application running, and am trying to connect to it using the python websockets library.

When websockets library tries to validate the headers received from my flask-uwsgi-websocket server, it crashes, saying that the headers are invalid. It appears that instead of responding with the requested 'connection':'upgrade', the flask-uwsgi-websocket library is responding with 'connection':'keep-alive'.

This results in the header validation failing on the client side.

I'm using 0.2.5, which as I understand, will automatically build the response based on the request headers.

I'm sure you'll want more information, I'm just not sure what would be most useful.

Heroku Question

Hello,

I'm currently using Flask-Sockets like you mention in your README. I am able to run this on Heroku using this as my Procfile:

web: gunicorn -k flask_sockets.worker app:app

I'm a bit confused on how I can get uWSGI up and running with your websockets on Heroku in a similar fashion... would that be a simple translation?

How to properly close a websocket

Closing the websocket with

ws.close()

appears to delay the close and the client receives an abnormal close code (1006)

websockets.exceptions.ConnectionClosed: WebSocket connection is closed: code = 1006 (connection closed abnormally [internal]), no reason

what is the proper way to close the websocket?

websocket in flask + https

Hi,

I want to use websocket via https (because h5 getUserMedia need the secure connect). Try the code use https in Flask as below.

app.run(gevent=100, host='192.168.2.217', debug=True, port=5000, ssl_context='adhoc')

It seems there is a conflict between websocket and https? Any body know how to solve this?

uwsgi: unrecognized option '--ssl_context' getopt_long() error

Do something before uwsgi starts handshaking

It would be nice if I could do something with the incoming HTTP request, before the actual uwsgi handshake is done (in https://github.com/zeekay/flask-uwsgi-websocket/blob/master/flask_uwsgi_websocket/websocket.py#L57).

This way I can include authentication HTTP headers and re-use the authentication system that I have set up for my REST API in my websocket connections. I would like to be able to do this before the handshake is done, so that I can deny the entire request before anything websocket-y is set up.

Using request context with other decorators

Under your Advanced Usage in the README docs it mentions how to expose the request context in a websocket handler using:

with app.request_context(ws.environ):

Is there a recommended way to do this so that the request context is available to other decorators? Say I have a @login_required decorator that invokes a check for a cookie or header? Should I roll my own decorator that combines the two? Do I need to unroll the @login_required decorator to occur as inline code after I've exposed the request context?

@ws.route('sockets/chat/<string:channel_name>')
@login_required
def websocket_chat(socket, channel_name):

Where @login_required expects to find the request context.

Timeouts On Connection When Using uwsgi/nginx

With Flask-uwsgi-websocket, are we supposed to manage ping/pong to keep the connection alive manually?

For me, the connection dies after a minute or so. I'm running nginx 1.6 in front of uwsgi 2.0 serving the Flask application. The uwsgi documentation on websockets makes it sound like uwsgi websockets take care of it for us in the background, but I'm not sure if that is the case. Also, I'm not sure if it makes a difference, but currently I'm using websockets only to allow clients to subscribe to messages - that is, the client only calls recv() and the server is only calling send(). It sounds like some of the ping/pong is built in to the recv_nb() call for native uwsgi websockets - I'm not sure how that translates to flask-uwsgi-websockets. I tried tossing in a ws.receive() on the server within the infinite loop (reading messages from redis, pushing out to clients over websockets), but that didn't seem to work either.

If not, is there a recommended way or example to handle this using flask-uwsgi-websocket?

--UPDATE--

It does seem like I've been able to resolve the dying connection by manually adding ping/pong into both server and client script. On the server, I check if it's been > 15 seconds since the last "heartbeat", and if so it sends a message and then calls ws.receive(), the client receives, interprets as heartbeat, and sends a message back. This does resolve the issue, but is that the best way to handle this? Ideally (in my mind) the client wouldn't have to code up this logic and can just receive messages as they become available, but perhaps there isn't an easier way.

Use in combination with flask-sqlalchemy

I am trying to use this in combination with flask-sqlalchemy, but I have noticed that after each request, a connection will stay open when I use the GeventWebSocket. This connection will not be closed for some reason.

Do you know the best way to manage connections when using flask-uwsgi-websocket in combination with flask-sqlalchemy+pymysql?

I do gevent.monkey.patch_all() at the beginning of my websocket app, and I tried remove the database connection after each request using db.session.close() and db.engine.dispose(). But this did not seem to have any impact whatsoever.

Can I catch client disconnects on server-side?

On the client-side it is easy to use the onclose function to detect when the server drops off.

But how about on the server-side, how can the server detect when a client has disconnected?

I ran the pubsub-asyncio example and I can see the redis_subscribe function exits when a message is sent to a disconnected client. But how can I detect the client disconnect event in itself?

I don’t want to rely on sending ping like messages just to detect disconnected clients. Websockets already have a ping/pong mechanism, I’d just be reinventing the wheel adding more complexity.

Problem when the app is in a package

I've placed my app in a package like that:

- package/
  |- __init__.py
  -- echo.py

The code of echo.py is

from flask import Flask
from flask_uwsgi_websocket import GeventWebSocket

app = Flask(__name__.split('.')[0])

print("name is: {}".format(__name__.split('.')[0]))
print(app.name)
print(app.import_name)

websocket = GeventWebSocket(app)

@websocket.route('/echo')
def echo(ws):
    while True:
        msg = ws.receive()
        ws.send(msg)

@app.route('/hello')
def hallo():
    return 'Hello'

if __name__ == '__main__':
    app.run(gevent=100)

As recommended in the flask docu http://flask.pocoo.org/docs/1.0/api/ I use app = Flask(__name__.split('.')[0]) for the name.
But flask_uwsgi_websocket will generate the following uwsgi call:

Running: /home/me/.virtualenvs/env/bin/uwsgi --http localhost:5000 --http-websockets --gevent 100 --master  --virtualenv /home/me/.virtualenvs/env --wsgi echo:app

which results in ModuleNotFoundError: No module named 'echo'

Instead of:

Running: /home/me/.virtualenvs/env/bin/uwsgi --http localhost:5000 --http-websockets --gevent 100 --master  --virtualenv /home/me/.virtualenvs/env --wsgi package.echo:app

Starting uwsgi directly with this command works.

Uninstalling Flask-uWSGI-WebSocket makes Flask return 404 on all routes

I created a small but real app with Flask. I tested a couple of routes and they worked fine. I installed Flask-uWSGI-WebSocket and did some work with websockets and everything worked well.

I then uninstalled Flask-uWSGI-WebSocket so I could debug other parts of the app without having to restart uwsgi manually and commented all references to web sockets but suddenly Flask started returning 404 on all previously existing routes.

Reinstalling Flask-uWSGI-WebSocket solved my problem.

Syntax error in version 0.5.1

The new version seems to have a syntax error:

  File "/Users/mdh/Envs/tmaps/lib/python2.7/site-packages/flask/exthook.py", line 62, in load_module
    __import__(realname)
  File "/Users/mdh/Envs/tmaps/lib/python2.7/site-packages/flask_uwsgi_websocket/__init__.py", line 30, in <module>
    from ._asyncio import *
  File "/Users/mdh/Envs/tmaps/lib/python2.7/site-packages/flask_uwsgi_websocket/_asyncio.py", line 36
    msg = yield from self.send_queue.get()
                   ^
SyntaxError: invalid syntax

Why gevent?

I was looking at websocket support by uWSGI and wondered why did you add gevent to the mix. What does it do that gevent threads won't?

Thanks!

Uwsgi and nginx

Hello,

I have some issues to deploy my flask project in nginx and uwsgi.
When I try to connect the websocket the server return a 502 bad gateway response.
All the other routes are ok..

Have you some example of uwsgi and nginx config to deploy a flask project with this app?

Thanks
Valentin.

fix python version classifier

Hi,

I wanted to use pypi2deb to easily convert this package into a deb package for debian and debian derivates. The problem is that pypi2deb relies on correct Python version classifiers to tell which package is compatible with what Python version and the default is Python 2.

I’m not really sure if this package supports Python 2.x. In case it does the version classifiers probably should be

Programming Language :: Python :: 2.7
Programming Language :: Python :: 3

In case Python 2.x is not supported it should just be

Programming Language :: Python :: 3 :: Only

I’d be very happy if you could fix the classifiers and publish a new release, so pypi2deb works correctly.

Thank you for your time and your work on this project!

How can I send something to a specific websocket in my application from another function?

Imagine this:

@websocket.route("/websocket")
def my_websocket(ws):
    ws.send("foo")

@app.route("/somewhere")
def somewhere():
    #ws.send("bar")

Is this way of working at all possible with websockets? Do I instead need some kind of internal message spooler in my_websocket that I send stuff to which in return sends it to the client? Or can I somehow do this more conveniently as I outlined in my example?

`Advanced Usage` block in README refers to WebSocket.environ, which doesn't exist.

The Advanced Usage section in the README says that ws.environ should be used where ws = <flavor>WebSocket(app). However, WebSocket and descendants don't have an environ member.

It looks like a reference to the environ is only retained in the WebSocketClient instance which is not readily accessible from the WebSocket, so perhaps this wasn't implemented. #77 is a strong hint supporting that.

Can anyone recommend a way to access to the request context in a websocket route?

ws connection failed

I don't know the reason why the connection failed

chrome:

shell:

code:

from flask import Flask, render_template
from flask_uwsgi_websocket import WebSocket

app = Flask(__name__)
ws = WebSocket(app)


@app.route('/')
def index():
    return render_template('test2.html')


@ws.route('/websocket')
def echo(ws):
    while True:
        msg = ws.receive()
        if msg is not None:
            ws.send(msg)
        else:
            return


if __name__ == '__main__':
    app.run(debug=True, threads=16, port=8080)

Typo in command in README.rst causes silent failure

README.rst says to enable asyncio support with the environment variable:

UWSGI_PROFLILE=asyncio ...

but "PROFLILE" is misspelled, which causes the build to silently ignore the option and fail to use the asyncio profile.

Number of CLOSE_WAIT connections when using with nginx

We are trying to use this flask plugin (version=0.4.4) to setup a notification server using redis / uwsgi / gevent and nginx(1.4.7). However we are noticing that on client disconnection many of the connections between nginx and the uwsgi flask application are left in CLOSE_WAIT state meaning that application never sends a FIN to nginx and leaves the connection hanging forever. This gets pretty bad eventually filling up the gevent async queue rendering the server unusable. Please see code below

    # spawn a gevent queue to communicate between redis connection and generator
    q = Queue()
    # subscribe to channel and listen for messages.
    # Spawn listener in an asynchronous greenlet
    redis_connection = redis.Redis(connection_pool=redis_pool)
    sub = gevent.spawn(subscribe_and_listen, channel, q, redis_connection, serialize_websocket)

    try:
        while True:
            # wait on queue and yield message to web socket
            message = q.get()
            ws.send(message)
            ack = ws.receive()
            if ack is None:
                # If no ack is received, the connection
                # is broken. Close gracefully on server side
                ws.close()
                break
    except Exception as e:
        logger.error(str(e))
    finally:
        # Kill the greenlet handling the redis subscription
        sub.kill()
        logger.info("Closing client connection")

I looked into the code further and uwsgi's implementation of websocket close and it seems like the close method is a noop. Please advise if we are doing something wrong here and if this can be fixed. Thanks!

Add a changelog to the project

It would be great if you could add a changelog to the project. Making it easier to understand what changed between different version :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.