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!
Related
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.
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.
WHAT WORKS
I created a simple Web Application in Flask that takes care of operating a simple return render_template("index.html") when the root node is accessed by a Web Browser.
# app.py
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def show_index():
return render_template("index.html")
if __name__ == "__main__":
app.run(port=80)
The index.html is a simple page that uses tracking.js in order to get the user webcam and track his/her face in the live video stream.
Opening cmd and typing python app.py results in Running on http://127.0.0.1:80/
Accessing the above mentioned URL results in the correct display of the page, that asks me for permission to use the camera, opens it and correctly tracks my face in the live video feed. So it's all working fine till here.
WHAT DOES NOT WORKS
The problem I'm experiencing arises when I dockerize my application using Docker. docker-machine ip is 192.168.99.100
Opening cmd and typing: docker run -p 4000:80 my_face_track_app results in: Running on http://0.0.0.0:80/
Accessing 192.168.99.100:4000 results in the correct display of index.html but I am not asked anymore for permission on the camera and inspecting the JS console I read the following exception:
getUserMedia() no longer works on insecure origins
Here the full error log:
I know the error is telling me I'm not serving the page in HTTPS.
Has anyone else encountered this problem?
What would be the proper solution to the issue or a possible walkaround?
Any help will be highly appreciated, thank you a lot in advance
WHAT I HAVE TRIED TO DO IN ORDER TO SOLVE THE PROBLEM
Since an HTTPS serving of the page is needed in order for JS to execute the function getUserMedia() I tought about serving my Flask application with an SSL certificate by modifying app.py like this:
# app.py
from flask import Flask, render_template
import OpenSSL
app = Flask(__name__)
#app.route("/")
def show_index():
return render_template("index.html")
if __name__ == "__main__":
app.run(port=80, ssl_context="adhoc")
I then dockerized the app building a new image. Typing:
docker run -p 443:80 facetrackapphttps
Results in
Running on https://127.0.0.1:80
So yeah, here HTTPS is ON: the problem is that the port 80 of the HTTPS Flask App is mapped to the port 443 of the docker-machine ip 192.168.99.100.
Trying to access 192.168.99.100:443 does not work and nothing is shown.
Does anybody have an idea about how to do this?
If your application is bound to 127.0.0.1 inside the container, you're not going to be able to access it from your host. According to the flask docs, flask will bind to 127.0.0.1 by default.
You'll need to modify your service so that it binds to 0.0.0.0 inside the container:
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80, ssl_context="adhoc")
I'm trying to figure out how to run a web service on heroku using flask and JSONRPC.
I would like to get to a point where, from my desktop I can do:
from flask_jsonrpc.proxy import ServiceProxy
service = ServiceProxy('http://<myapp>.heroku.com/api')
result = service.App.index()
print result
looking at heroku logs I can see :
2014-07-05T13:18:42.910030+00:00 app[web.1]: 2014-07-05 13:18:42 [2] [INFO] Listening at: http://0.0.0.0:21040 (2)
and trying using that port with :
service = ServiceProxy('http://<myapp>.heroku.com:21020/api')
still doesn't make it work (it seems hanging)
But when I run this through foreman, though, I can happy access it and seems working fine.
but when I try with the deployed application I get:
ValueError: No JSON object could be decoded
This is the application (not much I know , but is just to see how heroku works)
import os
from flask import Flask
from flask_jsonrpc import JSONRPC
app = Flask(__name__)
jsonrpc = JSONRPC(app, '/api', enable_web_browsable_api=True)
#jsonrpc.method('App.index')
def index():
return u'Welcome to Flask JSON-RPC'
if __name__ == '__main__':
port = int(os.environ.get("PORT", 5000))
app.run(host='0.0.0.0', debug=True, port=port)
this is the content of my Procfile:
web: gunicorn run:app -p $PORT
Am I missing something obvious here ?
Cheers.
L.
p.S
accessing http://.heroku.com/api/browse
from within the through foreman and the deployed app, it seems working fine.
[edit]
solved :
yes I was missing something.... looking better a the log I noticed the host which was :
host=<myapp>.herokuapp.com
instead of
.heroku.com
Changing the address to the correct one, it all seems working fine.
http://.herokuapp.com/api
All sorted , see original post.
Sorry for the noise.
I'm trying to set up python and flask on the arduino yun. I've managed to run python files via the /etc/config/uhttpd configuration file:
...
list interpreter ".py=/usr/bin/python"
...
The default path for the website's root is: /www in which I've placed a soft link (apps) to the sd card. So now I can run python programs: http://[ip arduino]/apps/helloworld.py
And when I make my first helloflask.py program and run that via python helloflask.py I can see the result at: http://[ip arduino]:5000
But now I want to configure the uhttpd mini webserver (which is capable to exchange information via CGI) to use the flask setup. The URI: http://flask.pocoo.org/docs/deploying/cgi/#server-setup shows some instructions... but I just don't get it. I've made a directory ../apps/uno in which I've placed a __init__.py file with the following content:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "He Flask!"
In the apps dir I've put a file: cgi.py with this content:
from wsgiref.handlers import CGIHandler
from uno import app
CGIHandler().run(app)
Now I when I browse: http://[ip arduino]/cgi.py get a server error occured, contact the administrator (I think this is the CGI interface from uhttpd).
I just don't grasp the CGI configuration for Flask/uhttpd
I looked into this too and got a little further, I was able to setup a simple hello world but once I tried to do something non-trivial I ran into a big issue that uhttpd doesn't support URL rewriting/aliasing. This means your flask app can only be served at the URL of its .py file instead of at a root like http:// (arduino IP) /flaskapp/. None of the routes inside the app will be visible and makes the whole thing unusable.
However, instead of trying to force flask into uhttpd I had great success running the built in server that flask provides. Take a look at this guide I wrote up that uses flask to serve data from a Yun: https://learn.adafruit.com/smart-measuring-cup/overview
The thing to do is add a call to app.run when the script is run, for example make your flask app look like:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello Flask!"
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, threaded=True)
Then log in to the Yun and run the script using python. Flask's built in server should start serving the app on http:// (arduino IP) :5000/. Make sure to include the host='0.0.0.0' as it's required to listen on the Yun's external network interface. You probably also want debug=True so there are better error messages (and live reloading of the server when the code changes), and I found threaded=True helps because the default server only handles one connection at a time. The Yun is a relatively slow processor so don't expect to service a lot of concurrent requests, however it's quite capable for providing a simple REST API or web application for a few users.
If you want this server to always run on bootup, edit the /etc/rc.local file to include a call to python and your script.