"Flask-SocketIO is Running under Werkzeug" despite using socketio.run() - python

I'm having trouble running Flask & SocketIO with Eventlet despite using socketio.run(), any suggestions are appreciated. I'm currently on Python 3.9 and I've tried multiple different versions of each of these modules with no avail.
[2021-04-04 06:39:05,709] WARNING in __init__: Flask-SocketIO is Running under Werkzeug, WebSocket is not available.
"GET /socket.io/?EIO=4&transport=websocket HTTP/1.1" 400 -
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SAR</title>
<script src="https://cdn.socket.io/3.1.3/socket.io.min.js" integrity="sha384-cPwlPLvBTa3sKAgddT6krw0cJat7egBga3DJepJyrLl4Q9/5WLra3rrnMcyTyOnh" crossorigin="anonymous"></script>
</head>
<body>
<button id="ping" onclick="send()">ping</button>
<script>
var socket = io.connect("http://" + document.domain + ":" + location.port, {transports: ['websocket']});
socket.on("connect", function(){
socket.emit("ping", "Established a connection, pinging!");
});
socket.on("pong", function(response){
console.log(response)
});
function send(){
socket.emit("ping", "ping_data");
}
</script>
</body>
</html>
app.py
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
import eventlet
app = Flask(__name__)
socketio = SocketIO(app, logger=True)
#app.route( '/' )
def index():
return render_template( 'index.html')
def receivedCallback():
print('Pong received by user!')
#socketio.on( 'ping' )
def handle_ping(data):
print("received", data)
socketio.emit('pong', "pong_data", callback=receivedCallback)
if __name__ == '__main__':
socketio.run(app)

It seems like running my main file through the terminal resolves this issue. If anyone knows why this is please do share, thanks. :)
python app.py

To give a loose idea of why it runs with python app.py is that the if __name__ == '__main__' is executed only when it is run through the terminal, much like how the main function is called first in many other programming languages like Java or C when run from the terminal directly.
When a Flask app is run through Werkzeug, the flask app instance is imported and sort of wrapped into a module by Werkzeug in which the requests are routed into. (That is why the if __name__ == __main__ part is never executed – much like when you import another module in your code, the main function of that module is never called.) So, as far as my understanding goes, when you run your Flask app through Werkzeug, the requests are received and routed over HTTP by default and not over the WebSocket protocol that Flask-SocketIO uses when you run socketio.run(). The WebSocket protocol requires a socket to be always open, enabling asynchronous I/O which cannot work in HTTP as it is a client-server protocol.
I, however, do not have an answer to how to solve the problem and work with Flask-SocketIO through Werkzeug, but I hope the above explanation throws some light into your problem and drives you in the correct direction to look at.

Related

Having trouble getting flask socketio to properly work using heroku

I am building a website that continuously streams data from an eeg headset and updates a graph on the homepage. When I run my code on a local server using "flask run," the code works perfectly -- all graphs function as intended, in real time. When running the code on heroku, however, it takes an extremely long time for the socket to establish a connection, and even once it does it continues to display the error:
Failed to load resource: the server responded with a status of 400 (BAD REQUEST)
Here is code I use in my main script, app.py:
from flask_socketio import SocketIO
socketio = SocketIO(app, cors_allowed_origins="*", async_mode=None, logger=True, engineio_logger=True)
...
if __name__ == '__main__':
socketio.run(app)
I use the following tag in my home.html file:
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
And the corresponding code in my application.js:
var socket = io.connect('https://' + document.domain + ':' + location.port);
These are all the packages I have in my requirements.txt:
flask==2.0.2
gunicorn==20.1.0
flask-socketio==4.3.1
python-engineio==3.13.2
python-socketio==4.6.0
numpy==1.22.3
scipy==1.8.0
firebase_admin==5.2.0
tensorflow-cpu==2.8.0
connexion==2.6.0
markupsafe==2.0.1
brainflow==4.9.2
pylsl==1.15.0
requests==2.27.1
werkzeug==2.0.1
Finally, here is my Procfile:
web: gunicorn app:app
I have tried pretty much anything, including setting CORS, using combinations of eventlet/gevent-websocket. Nothing I've tried yet seems to work.
If more clarification helps, here is a link to the site, as well as the GitHub repository. Thanks so much, and please let me know if I ought to provide any more details!

Push message to all online users using Flask, WITHOUT creating threads for each users

Suppose ther is a chat group.
Is there a way to append new messages sent by any member of a particular group to the html page of all the online users in that group using FLASK.
WITHOUT: creating a thread for each users and monitoring for latest updates in the chats.
This is an example, with a very minimal code. If you are interested then you can explore more and do the experiments.
Firstly, you need the Flask-SocketIO module, run the below command to install it.
pip install flask-socketio
With this example, the project directory will be something like below:
ProjectDirectory
|
|-> templates
| |
| |-> index.html
|
|-> app.py
app.py
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'randomSecretKEy#123'
socket = SocketIO(app)
#app.route('/')
def index():
return render_template('index.html')
#socket.on('message')
def message(data):
print(data)
emit('receive_message', data, broadcast=True)
if __name__ == '__main__':
socket.run(app)
index.html
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I=" crossorigin="anonymous"></script>
<script src="//code.jquery.com/jquery-1.12.4.min.js" integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ=" crossorigin="anonymous"></script>
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('connect', function() {
console.log('Connected to server');
});
socket.on('receive_message', function(msg) {
console.log('Received message: ', msg)
})
function sendMessage() {
msg = $('#message').val()
socket.emit('message', msg)
}
</script>
<input type="text" id="message"><button onclick="sendMessage()">Send</button>
Run the Flask app, open two browser tabs, with web developer tools opened side by side. AS soon as you open, you will see that Connected to server message has been logged. Now, type some message and hit the send button, in one tab, and you will see that, the message has been received in another tab's console.
I hope you get a direction with this and it is helpful for you.
Note: Again, I am telling this is a very basic example. Off course, you can add more logic and functionality, and make it more like a chatting app, but it will be out of the scope of this question. So, I will leave it to you and your imagination, to which extent you can learn and take this ahead. You can read about the module's documentation here.

Usage custom namespace Flask Socket io gives an errors

I've used perfect Flask-SocketIO library with Python 3 for couple of months. Everything worked as expected until the last couple of days.
All works fine, if namespace for connection to websocket server stay default /socket.io. But I'm geting an error now if I trying to change namespace for connection to python flask-socketio backend.
My app.py:
from flask import Flask, session, request, render_template, copy_current_request_context
from flask_cors import CORS, cross_origin
import flask_socketio as socketio
import ujson
async_mode = 'threading'
namespace = '/mynamespace'
app = Flask(__name__)
CORS(app)
app.config['SECRET_KEY'] = 'secret!'
sio = socketio.SocketIO(app, async_mode=async_mode)
#sio.on('connect', namespace=namespace)
def connect():
logging.info('Connected')
#sio.on('disconnect', namespace=namespace)
def disconnect():
logging.info('Disconnected')
#app.route("/home/index")
def home():
return render_template('index.html',
async_mode=sio.async_mode)
I'm using ./main.py to run the server, main.py contains:
from app import app, sio
if __name__ == "__main__":
sio.run(app, debug=False)
My template/index.html contains ton of code js, but I think most valuable I loading the socketio from cdn in a head:
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>
... and I using connect with custom namespace path, as in docs:
namespace = '/mynamespace/socket.io';
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port,
{path: namespace});
socket.on('connect', function() {
socket.emit('Connected to server');
})
As I understand, By default socketio library trying to connect to backend with emitting connect message to namespace. During loading 'index.html' template on '/home/index' route, logging the errors to console:
Flask server also gives and 404 error:
My best guess: at this moment it looks like something changed in client-side JS library or in chrome browser itself(few days ago I updated Chrome).
Maybe I just understood wrong one small detail. I really appreciate some help with this problem.
Stack versions:
Python 3.7.2,
Flask 1.0.2,
Flask-SocketIO 3.3.1,
socketio.min.js
1.3.5,
Google Chrome 77.0.3865.90 (64 bit)
You are confusing namespace with path, which are completely different things. The path is the endpoint URL where the Socket.IO server is listening. The namespace is a protocol feature of Socket.IO that allows multiplexing of multiple logical connections into a single physical connection.

Passing foreign characters via Socket.IO

I am doing a very simple thing, just sending a message to my Flask app via Socket.IO . It works like a charm with English, but some other languages break somewhere in the process.
Minimal working example follows.
testapp.py:
from flask import Flask
from flask_socketio import SocketIO
app = Flask('example')
socketio = SocketIO(app)
#socketio.on('test')
def on_connect(data):
print(data)
if __name__ == '__main__':
socketio.run(app)
index.html:
<!doctype html>
<html>
<body>
<script type="text/javascript" src="js/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost:5000');
socket.on('connect', function() {
socket.emit('test', 'ASCII text');
socket.emit('test', 'Český text');
});
</script>
</body>
</html>
Instead of expected Český text, I am getting ÄŚeskĂ˝ text on the console.
I am using the newest versions of both the server packages (Flask-SocketIO 3.0.2, python-socketio 2.0.0, python-engineio 2.2.0) and the client (socket.io.js 2.1.1) and also checked that both of my files are UTF-8 encoded.
Some bug reports and questions mention a breaking change that happenned between 1.x and 2.x versions, so i tried using some older versions of the client (1.4.8, 1.7.4) instead of the newest one. The result was not much better: ÃÅeskÄË text.
This is beginners' stuff, so there must be a popular SO question covering it already. I probably just can't find it. So... what did I miss?

Can't modify the value of the session within event handlers for websockets events

I'm building a little flask&pyhon-based app and my main feature is based on websockets. I discovered that I can't modify the value of the sesssion within event handlers for websockets events(I'm using flask-socketio) because flask stores its session on the client side. So, as the author of the extension recomanded I installed flask-kvsession to store the session on the server-side in a redis-based backend.
I followed the instructions presented http://pythonhosted.org/Flask-KVSession/, but the problem persists. So I created a little program to show you what I'm talking about.
# main.py
from flask import Flask, session, render_template
from flask.ext.socketio import SocketIO
from pprint import pprint
import redis
from flask_kvsession import KVSessionExtension
from simplekv.memory.redisstore import RedisStore
store = RedisStore(redis.StrictRedis())
app = Flask(__name__)
app.debug = True
app.secret_key = 'secret!'
KVSessionExtension(store, app)
socketio = SocketIO(app)
#app.route('/')
def index():
pprint(session)
return render_template("client.html")
#socketio.on('connect')
def handle_connect(message):
session['debug'] = 'debug'
pprint(session)
if __name__ == "__main__":
socketio.run(app)
<!-- templates/client.html -->
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/
socket.io/0.9.16/socket.io.min.js"></script>
<script type="text/javascript">
var sock = io.connect('http:localhost:5000');
sock.emit('connect', {debug: 'debug'});
</script>
</body>
</html>
Here is the output of the werkzeug debuging server:
* Running on http://127.0.0.1:5000/
* Restarting with reloader
<KVSession {}>
127.0.0.1 - - [2014-07-04 21:25:51] "GET / HTTP/1.1" 200 442 0.004452
<KVSession {'debug': 'debug'}>
<KVSession {}>
127.0.0.1 - - [2014-07-04 21:26:02] "GET / HTTP/1.1" 200 442 0.000923
<KVSession {'debug': 'debug'}>
I'd expect that the second time when I will access that page the contents of the session to be 'debug': 'debug' but it is not.
Here is what happened on the redis server while I was running this app:
127.0.0.1:6379> MONITOR
OK
1404498351.888321 [0 127.0.0.1:38129] "GET" "136931c509f674e3_53b6e25b"
1404498352.073011 [0 127.0.0.1:38129] "GET" "136931c509f674e3_53b6e25b"
1404498362.455320 [0 127.0.0.1:38129] "GET" "136931c509f674e3_53b6e25b"
1404498362.612346 [0 127.0.0.1:38129] "GET" "136931c509f674e3_53b6e25b"
As you can see, the value of the session is accessed 4 times, but is never modfied.
So, what should I do to fix this bug?
As explained in the documentation, Flask-SocketIO does not write sessions back to the session store, it just keeps them in memory and accessible to socket handlers.
If you want changes to the server-side session to persist and be available to regular HTTP clients you have to save the session yourself after you make those changes. I haven't tested this myself, but I think this will do:
session.modified = True
app.save_session(session, make_response('dummy'))
Note that save_session() gets a dummy response. This will only work if the session was already established in a regular HTTP call before this. Obviously a new session will not be created with the code above, since the response will be discarded and never reach the client.

Categories