I am using Eventlet to do some background emit processes, but now ever since starting to use Eventlet, under every handler under #socketio.on, the request variable does not work as intended. For example, under an ordinary #APP.route('/abc/') I can use request.cookies.get('csrf'), etc. But under #socketio.on, request simply isn't the same. request.cookies.get('...') returns None every time.
import json
import os
import time
import eventlet
import gamble
import eng_puzzle
import authentication
import gcaptchacheck
from flask import Flask, redirect, url_for, request, render_template, make_response
from flask_compress import Compress
from flask_socketio import SocketIO, send, emit
from flask_sslify import SSLify
from store import REDISDB
eventlet.monkey_patch()
These are the imports.
def bg_emit():
"""Background emit"""
currentpotinfo = json.loads(REDISDB.get('potinfo').decode('UTF-8'))
pot_info = {
'id': currentpotinfo['id'],
'endtime': currentpotinfo['starttime'] + 30 - int(time.time()),
'hashwin': currentpotinfo['hashwin'],
'message': 'Entries closing in: ',
'winner': 'Not yet chosen'
}
socketio.emit('infoupdate', {'info': pot_info}, broadcast=True)
Background emit function (above)
def testemit():
"""test"""
while True:
bg_emit()
eventlet.sleep(1)
How it is called (above)
APP = Flask(__name__)
Compress(APP)
socketio = SocketIO(APP)
Defining APP and socketio (above)
if __name__ == '__main__':
print('RUN')
eventlet.spawn(testemit)
try:
socketio.run(APP, port=80, debug=True)
except PermissionError:
HPORT = int(os.environ.get('PORT'))
socketio.run(APP, port=HPORT, debug=False)
How the server is run. The except is just for deployment. (above)
#socketio.on('pot')
def handle_message(message):
"""Handle messages"""
print(message)
user = authentication.checksession(request)
print('user', user)
print(request.cookies.get('csrf'))
if user and request.cookies.get('csrf') == message.get('csrf'):
One of the places that are having trouble. authentication is a custom script for authentication site wide. There are debug messages and they prove that None is returned every time a cookie is called. How can I get around this issue? Is there a solve to the issue itself or do I have to make my own way around it?
Related
I am trying to send Keyboard typed data to a webpage using Flask SSE following this tutorial. I could send the data. But the problem is that I could send the data only when the cursor is focused on the terminal (I am using input() method in an infinite loop to capture data).
Since I want to capture data even though the cursor is not focused in the terminal I tried using the keyboard module (record() method). Though I could successfully run and capture in a separate program, I couldnt run it in Flask code. I am running the following code.
import gevent
from gevent.pywsgi import WSGIServer
from gevent import monkey
monkey.patch_all()
from numpy import random
from flask import Flask, json, Response, render_template
import keyboard
import sys
app = Flask(__name__)
def event():
while True:
print("Inside")
recorded_events = keyboard.read_key(suppress=True);
#app.route('/')
def index():
return render_template('index.html')
#app.route('/stream/', methods=['GET', 'POST'])
def stream():
return Response(event(), mimetype="text/event-stream")
if __name__ == "__main__":
WSGIServer(('', 5001), app).serve_forever()
Aren't you supposed to yield the values?
def event():
while True:
yield keyboard.read_key(suppress=True)
I know how to receive data from POST request in main thread:
from flask import Flask, render_template, request
app = Flask(__name__)
#app.route("/", methods=['POST'])
def parse_request():
my_value = request.form.get('value_key')
print(my_value)
return render_template("index.html")
But can I do that in background thread to avoid blocking UI (rendering index.html)?
I'm assuming you want the html rendering and processing of your request to run concurrently. So, you can try threading in Python https://realpython.com/intro-to-python-threading/.
Let say you have a function that performs some processing on the request value, You can try this:
from threading import Thread
from flask import Flask, render_template, request
def process_value(val):
output = val * 10
return output
app = Flask(__name__)
#app.route("/", methods=['POST'])
def parse_request():
my_value = request.form.get('value_key')
req_thread = Thread(target=process_value(my_value))
req_thread.start()
print(my_value)
return render_template("index.html")
Thread will allow process_value to run in the background
I need an endless loop to run server-side and emit some information per iteration.
Also, I need to make sure it is started only once - preferably on server start-up - and not induced by various users using the app.
Then I would like to avoid additional stack (threading, background jobs etc.) if possible.
Here is the task:
#app.route('/refresh_games_list_periodically')
def refresh_games_list_periodically():
while True:
response = {
'games_list': games_manager.get_nonexpired_games(),
}
socketio.emit('games_list_refreshed', response)
time.sleep(1)
It starts whenever 'index.html' is rendered:
#app.route('/')
def index():
return render_template('index.html')
index.html:
fetch("{{url_for('refresh_games_list_periodically') }}")
Full code:
import time
from flask import (
Flask,
render_template,
jsonify,
request,
)
from flask_socketio import SocketIO, emit
from minesweeper import Game, GamesManager
games_manager = GamesManager()
app = Flask(__name__)
socketio = SocketIO(app)
#app.route('/refresh_games_list_periodically')
def refresh_games_list_periodically():
while True:
response = {
'games_list': games_manager.get_nonexpired_games(),
}
socketio.emit('games_list_refreshed', response)
time.sleep(1)
#app.route('/')
def index():
return render_template('index.html')
The problem currently is that whenever a new user enters 'index.html', another iteration of the loop will start running besides the old one.
I have tried using some boolean flags to mark the view as already called, but could not get it right in Flask context.
Then I thought I could make refresh_games_list_periodically a normal function, not a view, and just call it in my Flask app before views. But then the while loop will be blocking and no page will be rendered:
import time
from flask import (
Flask,
render_template,
jsonify,
request,
)
from flask_socketio import SocketIO, emit
from minesweeper import Game, GamesManager
games_manager = GamesManager()
app = Flask(__name__)
socketio = SocketIO(app)
refresh_games_list_periodically()
def refresh_games_list_periodically():
while True: # WILL BLOCK
response = {
'games_list': games_manager.get_nonexpired_games(),
}
socketio.emit('games_list_refreshed', response)
time.sleep(1)
#app.route('/')
def index():
return render_template('index.html')
How else can I do this?
thread = threading.Thread(target=refresh_games_list_periodically)
thread.start()
I guess ... I imagine this is not a great idea however and you are probably better off running your socket loop in one process and your flask app seperately
Is there a way to determine what the path was requested to the server, including if it included a question mark? The application
from gevent import monkey
monkey.patch_all()
import gevent
from gevent.pywsgi import WSGIServer
from flask import Flask, Response, request
def root():
return Response(
f'full_path:{request.full_path} '
f'path:{request.path} '
f'query_string:{request.query_string} '
f'url:{request.url}'
)
app = Flask('app')
app.add_url_rule('/', view_func=root)
server = WSGIServer(('0.0.0.0', 8081), app)
server.serve_forever()
always results in
full_path:/? path:/ query_string:b'' url:http://localhost:8081/
if requesting either
http://localhost:8081/?
or
http://localhost:8081/
This may seem unimportant in a lot of cases, but I'm making an authentication flow with multiple redirections, where the user is meant to end up at the exact same URL as they started. At the moment, I can't see a way to ensure this happens with Flask + gevent WSGIServer.
This is a similar question to Flask request: determine exact path, including if there is a question mark, but the answer doesn't seem to be applicable when the WSGIServer from gevent.pywsgi is used, since request.environ has neither of the keys RAW_URI nor REQUEST_URI
There is a way by defining a custom handler_class / WSGIHandler and adding self.path to request.environ
from gevent import monkey
monkey.patch_all()
import gevent
from gevent.pywsgi import WSGIHandler, WSGIServer
from flask import Flask, Response, request
def root():
return Response(
f'request_line_path:{request.environ["REQUEST_LINE_PATH"]}'
)
class RequestLinePathHandler(WSGIHandler):
def get_environ(self):
return {
**super().get_environ(),
'REQUEST_LINE_PATH': self.path,
}
app = Flask('app')
app.add_url_rule('/', view_func=root)
server = WSGIServer(('0.0.0.0', 8081), app, handler_class=RequestLinePathHandler)
server.serve_forever()
so a request to http://localhost:8081/ outputs
request_line_path:/
and a request to http://localhost:8081/? outputs
request_line_path:/?
for reasons I want to trigger the reboot of an raspberry pi using a REST api.
My REST api is coded in python flask like this:
from flask import Flask
from flask import jsonify
import subprocess
app = Flask(__name__)
#app.route('/api/reboot')
def reboot():
subprocess.call("/sbin/reboot")
return jsonify(triggered='reboot')
if __name__ == '__main__':
app.run(debug=True,host="0.0.0.0")
the code is working perfectly fine. But due to its a reboot the return will not be send (because the system is rebooting obviously).
Is there a way to trigger the reboot some kind of async with a delay of a couple milliseconds, that allows to return some value (in my case just an custom 'ack') prior the actual reboot?
Try threading.Timer:
For example:
from flask import Flask
from flask import jsonify
import subprocess
import threading
app = Flask(__name__)
def _reboot():
subprocess.call("/sbin/reboot")
#app.route('/api/reboot')
def reboot():
t = threading.Timer(1, _reboot)
t.start()
return jsonify(triggered='reboot')
if __name__ == '__main__':
app.run(debug=True,host="0.0.0.0")