Flask, FlaskSocketIO - RuntimeError: Cannot obtain socket from WSGI environment - python

When I try to use the functionality that uses websockets in my application, I get this error in the console:
File "/Users/user/venv/lib/python3.7/site-packages/simple_websocket/ws.py", line 138, in __init__
raise RuntimeError('Cannot obtain socket from WSGI environment.')
RuntimeError: Cannot obtain socket from WSGI environment.
I also get this error in the browser console:
WebSocket connection to 'ws://localhost:5000/socket.io/?EIO=4&transport=websocket&sid=40NYzDgGYStMR0CEAAAJ' failed:
I tried using gevent, gevent-websocket, and eventlet, but this created other issues, and I'm not sure that I need to use gevent or eventlet for this use case.
Here's the rest of the relevant code:
__ init __.py
from flask_socketio import SocketIO
...
socketio = SocketIO()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
socketio.init_app(app, cors_allowed_origins='*')
...
return app
app.py
from app import create_app, socketio
app = create_app()
if __name__ == '__main__':
socketio.run(app)
routes.py
This only accepts POST requests because I send data to this route from a Celery task
from app import socketio
...
#main_bp.route('/send_message', methods=['POST'])
def send_message():
...
socketio.emit('test_message', {'msg':'Test'}, namespace='/test')
return 'Results Sent'
index.html
var socket = io.connect('http://localhost:5000/test');
socket.on('connect', function(){
console.log('Connected to socket')
});
socket.on('test_message', function(message){
console.log('Received test message');
}
)
Note that in the browser console I'll see "Connected to socket", but not "Received test message"

You are using the simple-websocket package. This package has a list of supported web servers. The error indicates that you are using a web server that is not in the supported list.
Supported web servers are:
The Flask dev server (for development purposes only, of course)
Gunicorn
Eventlet
Gevent
From this list, it seems your only choice for a production web server is Gunicorn, since you say that eventlet/gevent won't work for your needs.
The use of Gunicorn is covered in the documentation. In that section, simple-websocket is the third option mentioned. Here is the example start up command:
gunicorn -w 1 --threads 100 module:app

Maybe you can turn on the logger and the engine.io logger. that qould give you an idea what is the issue.
don't use eventlet and gevent together. use anyone and uninstall the other.

Related

i am trying to make a simple chat app with flask_socketio and it doesn't work properly

I am trying to create a simple chat app by using flask and flask_socketio and it works but the WebSocket upgrade doesn't happen and it gives me this error properly
The WebSocket transport is not available, you must install a WebSocket server that is compatible with your async mode to enable it. See the documentation for details. (further occurrences of this error will be logged with level INFO)
and I saw many tutorials and ended with installing (gunicorn, gevent, eventlet) and updating the packages and the library but when I run the app it works and the message sent from the client to the server and broadcasting it again to all the client but the connection is not websocket it's pooling
the server-side
from flask import Flask, app, render_template, redirect
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'test'
socketio = SocketIO(app)
#app.route('/')
def main():
return render_template('chat.html')
#socketio.on('message')
def message_func(data):
socketio.send(data)
if __name__ == '__main__':
socketio.run(app)
the client
var socket = io('http://' + document.domain + ':' + location.port);
socket.on('connect', function() {
console.log('connected');
})
socket.on('message', function(data) {
const p = document.createElement('p');
const br = document.createElement('br');
p.innerHTML = data;
document.querySelector('#panel').append(p);
})
document.querySelector('#send').onclick = () => {
socket.send(document.querySelector('#userin').value)
}
In the "Deployment: Gunicorn Web Server" documentation it says
In the documentation of When using gunicorn with the gevent worker
and the WebSocket support provided by gevent-websocket, the command
that starts the server must be changed to select a custom gevent web
server that supports the WebSocket protocol. The modified command is:
gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 module:app
For that to work you additionally need to install gevent-websocket.

Using Gevent in flask: API is not asynchronous

Earlier I was using Waitress. Now I'm using Gevent to run my Flask app that has only one API
from flask import Flask, request, jsonify
import documentUtil
from gevent.pywsgi import WSGIServer
app = Flask(__name__)
#app.route('/post-document-string', methods=['POST'])
def parse_data():
req_data = request.get_json(force=True)
text = req_data['text']
result = documentUtil.parse(text)
return jsonify(keywords = result)
if __name__=='__main__':
http_server = WSGIServer(('127.0.0.1', 8000), app)
http_server.serve_forever()
This works fine. But the API is not asynchronous. If from front-end, I fire the same API twice at the same time, the second call waits for the first one to give response first.
What is wrong here ? How can I make it asynchronous ?
We use Gunicorn to run Flask in multiple processes. You get more juice out of python that way + auto restarts and stuff. Sample config file:
import multiprocessing
bind = "0.0.0.0:80"
workers = (multiprocessing.cpu_count() * 2) + 1
# ... additional config
Then run with something like
gunicorn --config /path/to/file application.app
"""index.py"""
from flask import Flask
from flask import jsonify
app = Flask(__name__)
#app.route('/')
def index():
"""Main page"""
doc = {
'site': 'stackoverflow',
'page_id': 6347182,
'title': 'Using Gevent in flask'
}
return jsonify(doc)
# To start application
gunicorn -k gevent --bind 0.0.0.0 index:app
k : worker_class
--bind : bind address
# See https://docs.gunicorn.org/en/latest/settings.html
Not sure, however I think adding thread param in server object can solve the problem.
http_server = WSGIServer(('127.0.0.1', 8000), app, numthreads=50)
source: https://f.gallai.re/wsgiserver
I found the Chrome browser was the culprit, after learning based on this answer:
https://stackoverflow.com/a/62912019/253127
basically Chrome is trying to cache the result of the first request, and then serve that to the additional tabs.
You might get around this by disabling AJAX caching, assuming you're using jQuery the code is:
$.post(
{url: '/', cache: false},
{'text':'my data'}
).then(function(data){
console.log(`server return data was: ${data}`);
});

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.

Trying to connect Flask backend to React frontend using SSL

My backend is an AWS EC2 server that runs on flask, I'm trying to port over my code from http to https. I've used letsencrypt and certbot to create valid certificates. My flask backend accesses the certs and then calls the host. I tried adding SSLify after looking through other posts ( and seeing that it can be used with Flask, but it didn't change anything.
from flask_cors import CORS
from flask_sslify import SSLify
from OpenSSL import SSL
app = Flask(__name__)
CORS(app)
sslify = SSLify(app)
sslify
if __name__ == "__main__":
context = ("../../../../etc/letsencrypt/live/app.mydomain.name/fullchain.pem","../../../../etc/letsencrypt/live/app.mydomain.name/p$
app.run(host='127.0.0.1', port=5000, debug=True, ssl_context=context)
On the React frontend side, I'm using axios
axios.post('https://127.0.0.1:5000/run_query', postData, axiosConfig)
.then(function (response) {
console.log("Successful connection");
console.log(response.data);
influencerList = response.data.query_results;
console.log(influencerList);
currentComponent.setState({IL: influencerList});
})
.catch(function (error) {
console.log(error);
});
using app.run options I listed above on the backend returns
xhr.js:178 OPTIONS https://127.0.0.1:5000/run_query net::ERR_CONNECTION_REFUSED in the Google Chrome Browser Console.
I'm runnning nginx on Linux AMI. The certificates are for RehlOS/CentOS generated through letsencrypt and certbot. My Nginx error.log doesn't return anything, running the backend python file doesn't log anything anymore, like it did before going from HTTP. It just displays this:
* Running on https://ec2-34-209-86-220.us-west-2.compute.amazonaws.com:5000/ (Press CTRL+C to quit)
* Restarting with stat
I also tried changing the post URL to https://ec2-34-209-86-220.us-west-2.compute.amazonaws.com:5000/run-query instead of https://127.0.0.1:5000/run_query but got the exact same error.
I hope it's something simple, any help would be great. Thanks!

How to launch a Bottle application over a CherryPy standalone web server?

I have a python web app developed using the bottle framework. My bottle app is web API that provide methods that return JSon data, so no static content is needed. I am trying to deploy it to production using a CherryPy server which is supposed to be robust for production applications.
My web_api.py file (my bottle app) looks something like this:
from bottle import Bottle, request
app = Bottle()
#app.get('/stuff')
def do_stuff():
'''
Method that does stuff.
'''
stuff = {'data': 'some data'}
# Return the environment info as Json data
return stuff
I have a server.py file to launch the Bottle app over the CherryPy server that looks like this:
from my_package.web_api import app
from cherrypy.wsgiserver import CherryPyWSGIServer
server = CherryPyWSGIServer(
('0.0.0.0', 80),
app,
server_name='My_App',
numthreads=30)
server.start()
so when I run my server using this command:
python server.py
My server is successfully started and start listening in port 80 as expected. However once I start my web server I cannot stop it any more. I have tried Ctrl + C which works with the development server but has no effect here. Am I starting the server the right way? How do I stop it once it is running? Is this the correct way to launch a Bottle app over CherryPy?
BTW, I am running python 2.7 in Windows 8.
Your code is correct, you just need to add a try/catch statement:
from my_package.web_api import app
from cherrypy.wsgiserver import CherryPyWSGIServer
server = CherryPyWSGIServer(
('0.0.0.0', 80),
app,
server_name='My_App',
numthreads=30)
try:
server.start()
except KeyboardInterrupt:
server.stop()
You might wanna also consider to do some logging with wsgi-request-logger or something similar.
This are three alternative ways on hosting a WSGI application within cherrypy:
import cherrypy as cp
from cherrypy.wsgiserver import CherryPyWSGIServer
from cherrypy.process.servers import ServerAdapter
from bottle import Bottle
app = Bottle()
#app.get('/stuff')
def do_stuff():
'''
Method that does stuff.
'''
stuff = {'data': 'some dataX'}
return stuff
def run_decoupled(app, host='0.0.0.0', port=8080, **config):
server = CherryPyWSGIServer((host, port), app, **config)
try:
server.start()
except KeyboardInterrupt:
server.stop()
def run_in_cp_tree(app, host='0.0.0.0', port=8080, **config):
cp.tree.graft(app, '/')
cp.config.update(config)
cp.config.update({
'server.socket_port': port,
'server.socket_host': host
})
cp.engine.signals.subscribe() # optional
cp.engine.start()
cp.engine.block()
def run_with_adapter(app, host='0.0.0.0', port=8080, config=None, **kwargs):
cp.server.unsubscribe()
bind_addr = (host, port)
cp.server = ServerAdapter(cp.engine,
CherryPyWSGIServer(bind_addr, app, **kwargs),
bind_addr).subscribe()
if config:
cp.config.update(config)
cp.engine.signals.subscribe() # optional
cp.engine.start()
cp.engine.block()
The run_in_cp_tree and run_with_adapter functions are using the cherrypy engine, which enables the use of plugins to have off-the-shelf auto-reload, pidfile, daemonization, signal management and some more goodies, along with the possibility to create one of your own.
Notice that you can also use the WSGIPathInfoDispatcher to attach multiple wsgi applications on the CherryPyWSGIServer.
Trying to connect any WSGI server to my BottlePy app here in 2019 turned out to be rather tricky(to a noobie like me).
I tried connecting several ones, spent most off my time with CherryPy, which has changed his syntax.
The simpliest to me turned out to be waitress https://waitress.readthedocs.io/en/latest/usage.html
After i figured out how to use it on waitress i got it in cherrypy also. So:
CherryPy http://docs.cherrypy.org/en/latest/advanced.html?highlight=WSGi#host-a-foreign-wsgi-application-in-cherrypy
1)add after imports
import cherrypy as cp
app = bottle.Bottle()
2) change in routes "#bottle" to "#app"
3)add this as main function
cp.tree.graft(app, '/')
cp.server.start()
Waitress
1)add after imports
import waitress
app = bottle.Bottle()
2)add this as main function
waitress.serve(app, listen='*:44100')
3) change in routes "#bottle" to "#app"

Categories