flask-sock
flask-sock copied to clipboard
invalid frame header
I am trying to upgrade ,an application i am working on from flask-socket to flask-sock a while ago flask and werkzeug were upgraded from very old versions to 2.2.2 for both and caused the websocket to didn't work.
Following the gevent example works on an isolated environment, while at my app, during the connection handshake, the underlaying simple-websocket sends an AcceptConnection which resulted with error (1002 protocol error).
The original request:
Request(host='localhost:5001', target='/', extensions=['permessage-deflate; client_max_window_bits'], extra_headers=[(b'connection', b'Upgrade'), (b'pragma', b'no-cache'), (b'cache-control', b'no-cache'), (b'user-agent', b'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'), (b'upgrade', b'websocket'), (b'origin', b'https://www.piesocket.com'), (b'sec-websocket-version', b'13'), (b'accept-encoding', b'gzip, deflate, br'), (b'accept-language', b'en-US,en;q=0.9'), (b'sec-websocket-key', b'Jj7EU6WXADUUFno6ByyM8Q==')], subprotocols=[])
The response:
InformationalResponse(headers=<Headers([(b'upgrade', b'WebSocket'), (b'connection', b'Upgrade'), (b'sec-websocket-accept', b'zAzLyNKKjek9I3n8P/okYVfJPVU='), (b'sec-websocket-extensions', b'permessage-deflate; client_max_window_bits=15')])>, http_version=b'1.1', reason=b'', status_code=101)
At this point the client (tried multiple ones) sees the following error:
Invalid frame header
While the flask server returns
File "/Users/user/workspace/Management/server/src/management/modules/websocket/module.py", line 268, in _read_from_websocket
s=websocket_connection.receive(),
File "/Users/user/.local/share/virtualenvs/management_web_api_server-gHGDlQHu/lib/python3.7/site-packages/simple_websocket/ws.py", line 112, in receive
raise ConnectionClosed(self.close_reason, self.close_message)
simple_websocket.ws.ConnectionClosed: Connection closed: 1002 WebSocket Protocol Error
I tried to debug both raw example and my app, both send the exact same AcceptConnection response (only difference was the token's value). however my application closes the connection during the handshake. I couldn't find evidence for a middleware which messes with the headers/buffer.
I did find this comment.
My question is if there is a support for flask and werkzeug 2.2.2 with this library and if there is a reason for the flask route to reject a message sent during the handshake
Can you please create a short and simple example application that demonstrates the issue? The latest Flask and Werkzeug versions should be supported.
yes, i didn't manage to reproduce it outside of the legacy application, i am still trying to reproduce it via a simple app which will also help me isolate the issue. I also try to upgrade flask to latest which may take some time
The difference in the flows is after the accept connection (which is the same for both success and failure applications), the server sends 'b'\x88\x1a\x03\xeaWebSocket Protocol Error'' to the client
I think it is going to be very difficult to debug this just by looking at the bytes. What the server sends is a standard protocol error. What matters is what made the server send this error, which must have happened before.
is there a focal point to find what happens after the 'accept connection' phase?
The WebSocket logic is not in this repository, you will need to look at the wsproto package, where the low-level protocol handshakes are implemented.
Hi, in continue to asafm-DI issue I propose a websocket app which doesn't work, this (hopefully) imitates our way of work and perhaps would be easier for you to understand.
import geventwebsocket.handler
from flask import Flask, render_template
from flask_sock import Sock
from gevent.pywsgi import WSGIServer
app = Flask(__name__)
sock = Sock(app)
@app.route('/')
def index():
return render_template('index.html')
def _register_websocket_endpoints():
@sock.route('/echo')
def echo(ws):
_handle_websocket_connection(ws)
def _handle_websocket_connection(websocket_connection):
while True:
websocket_connection.receive()
websocket_connection.send("bar")
if __name__ == "__main__":
_register_websocket_endpoints()
WSGIServer(('0.0.0.0', 11206), app, handler_class=geventwebsocket.handler.WebSocketHandler,
).serve_forever()
We can see the error here - it says the connection was closed but we are not sure why, we assume it is because we are not proficient at using the new flask-sock and we would use help.
Thank you
@barv-di There is a gevent example in this repo. You are not doing this correctly. Two problems, a) you need to monkey patch the standard library for Gevent compatibility and b) you should not use gevent-websocket with this package. Your fixed example would be:
from gevent import monkey
monkey.patch_all()
from flask import Flask, render_template
from flask_sock import Sock
from gevent.pywsgi import WSGIServer
app = Flask(__name__)
app.config['SOCK_SERVER_OPTIONS'] = {'ping_interval': 25}
sock = Sock(app)
@app.route('/')
def index():
return render_template('index.html')
def _register_websocket_endpoints():
@sock.route('/echo')
def echo(ws):
_handle_websocket_connection(ws)
def _handle_websocket_connection(websocket_connection):
while True:
websocket_connection.receive()
websocket_connection.send("bar")
if __name__ == "__main__":
_register_websocket_endpoints()
WSGIServer(('0.0.0.0', 11206), app).serve_forever()
from gevent import monkey
monkey.patch_all()
from flask import Flask, render_template
from flask_sock import Sock
from gevent.pywsgi import WSGIServer
app = Flask(__name__)
app.config['SOCK_SERVER_OPTIONS'] = {'ping_interval': 25}
sock = Sock(app)
@app.route('/')
def index():
return render_template('index.html')
def _register_websocket_endpoints():
@sock.route('/echo')
def echo(ws):
_handle_websocket_connection(ws)
def _handle_websocket_connection(websocket_connection):
while True:
websocket_connection.receive()
websocket_connection.send("bar")
if __name__ == "__main__":
_register_websocket_endpoints()
WSGIServer(('0.0.0.0', 11206), app).serve_forever()
Running this still not works (closing state) the same error.
Running it with - gunicorn -b 0.0.0.0:11206 --workers 4 --threads 100 --worker-class gevent app:app
See the documentation for how to run with gunicorn: https://flask-sock.readthedocs.io/en/latest/web_servers.html#id3
Running the code in previous post along with gunicorn -b 0.0.0.0:11206 --worker-class gevent app:app
does not work. the socket is in closed state (same error as before when sending a message in the index.html popup)
I copied the code you had written and the command from ur documentation.
I attach the pip3 list output (maybe useful?)
`Package Version
---------------- -------
bidict 0.22.1
blinker 1.6.3
click 8.1.7
dnspython 2.4.2
eventlet 0.33.3
Flask 2.3.2
flask-sock 0.7.0
Flask-SocketIO 5.3.6
gevent 23.9.1
greenlet 3.0.0
gunicorn 21.2.0
h11 0.14.0
itsdangerous 2.1.2
Jinja2 3.1.2
MarkupSafe 2.1.3
packaging 23.2
pip 23.2.1
python-engineio 4.7.1
python-socketio 5.9.0
setuptools 68.2.2
simple-websocket 1.0.0
six 1.16.0
Werkzeug 3.0.0
wheel 0.41.2
wsproto 1.2.0
zope.event 5.0
zope.interface 6.1
`
The code that I shown above is written to be used with the gevent web server, not gunicorn. I based it on your own code. Does it work that way?
Because you are registering your endpoints inside the if condition at the bottom. That code does not run when Gunicorn imports your app.
@miguelgrinberg I'm joining this conversation, there is random behaviour with error:
WebSocketApi.ts:3 WebSocket connection to 'ws://localhost:5000/ws' failed: Invalid frame header
Error is shown after first send (from client) and receiving message (from server).
Browser javascript code:
export class WebSocketApi {
static SOCKET: WebSocket | any = new WebSocket("ws://localhost:5000/ws")
static ONCONNECT = new Map<string, any>()
static ONMESSAGE = new Map<string, Map<string, any>>()
static Init() {
WebSocketApi.SOCKET.onopen = WebSocketApi._Connected
WebSocketApi.SOCKET.onmessage = WebSocketApi._OnMessage
WebSocketApi.SOCKET.onerror = (e) => {
console.error(e)
}
static SendMessage(message: string) {
WebSocketApi.SOCKET.send(message)
}
}
}
flask code
from flask_cors import CORS from flask_sock import Sock from flask import Flask
app = Flask("Exear", static_url_path='') app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{os.path.join(vfs.GetWorkingDirectory(), 'vts_db.db')}" app.config['SECRET_KEY'] = 'secretkeytopsecret'
websocket = Sock(app)
@websocket.route("/ws") def _WsReturn(ws: wsServer):
print("---------")
print(ws.receive())
ws.send(f"Respose {random.randint(0, 10)}")
app.run(debug=True, host="0.0.0.0")
@erikpa1 Try with a production web server. You are using the Flask development web server for this and on top of that you are using development mode in your app. Also your websocket handler exits after receiving and sending. Is this intended? The connection will close if you let the function end.
@miguelgrinberg yeah sorry, I came to write, it was because I was missing while True
, sorry :D my mistake :D