Python websocket/socketio load user session failed - python

Good day to you all.
I have a project and need to use websocket to get the json data in realtime.
That external json with HTTPBasicAuth protected.
I want to use the current login id and password to do the authentication.
But got error.
Anyone can help?
Thank you.
Error Message :
RuntimeError: Working outside of request context.
This typically means that you attempted to use functionality that needed
an active HTTP request. Consult the documentation on testing for
information about how to avoid this problem.
from flask_socketio import SocketIO, emit
from flask import Flask, render_template, redirect, request, session,url_for, copy_current_request_context
from flask_session import Session
from flask import request
from random import random
from time import sleep
from threading import Thread, Event
import urllib.request, json
import json
import asyncio
import websockets
import requests
from requests.auth import HTTPBasicAuth
from flask_mysqldb import MySQL
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
app.config['DEBUG'] = True
#turn the flask app into a socketio app
socketio = SocketIO(app, async_mode=None, logger=True, engineio_logger=True)
#random number Generator Thread
thread = Thread()
thread_stop_event = Event()
def randomNumberGenerator():
user=session['email']
pass=session['password']
res = requests.get('https://json.source.com', verify=False, auth=HTTPBasicAuth(user, password))
socketio.emit('newnumber', {'data': res.json()}, namespace='/test')
socketio.sleep(1)
#app.route("/")
def index():
# check if the users exist or not
if not session.get("email"):
# if not there in the session then redirect to the login page
return redirect("/login")
return render_template('index.html', email=session['email'])
#app.route("/login", methods=["POST", "GET"])
def login():
# if form is submited
if request.method == "POST":
# record the user name
session["email"] = request.form.get("email")
session["password"] = request.form.get("password")
# redirect to the main page
return redirect("/")
return render_template("login.html")
#app.route("/logout")
def logout():
session["email"] = None
return redirect("/")
#socketio.on('connect', namespace='/test')
def test_connect():
# need visibility of the global thread object
global thread
print('Client connected')
#Start the random number generator thread only if the thread has not been started before.
if not thread.is_alive():
print("Starting Thread")
thread = socketio.start_background_task(randomNumberGenerator)
#socketio.on('disconnect', namespace='/test')
def test_disconnect():
print('Client disconnected')
if __name__ == '__main__':
socketio.run(app)

Related

Multiple suscribers to SSE being blocked via pubsub with Flask

I'm creating a live data table page using Flask. The data shown is coming from a PostgreSQL database.
I am passing the new data being inserted into the database using the pubsub mechanism, and the operation is then transmitted using SSE to the front end Flask application.
Everything is working fine when I am opening one window but when I'm opening another window (to simulate multiple users), only the most recently loaded window receives the SSE. What am I doing wrong?
I tried multiple solutions, from using Flask in threaded mode to using gevent or gunicorn
Here is my code:
main.py
from gevent import monkey
monkey.patch_all()
import psycopg2, pgpubsub
from flask import Flask, render_template, Response
from gevent.pywsgi import WSGIServer
import time
app = Flask(__name__)
pubsub = pgpubsub.connect(database="postgres"....)
def get_connection():
try:
return psycopg2.connect(database="postgres.....")
except Exception as e:
return f"Error connecting to DB: {e}"
#app.route('/')
def home():
return render_template('index.html')
#app.route('/events')
def events():
def update_pusher():
print('Started listening')
pubsub.listen('data_changed')
while True:
for event in pubsub.events(yield_timeouts=True):
if event is None:
pass
else:
yield f"data: {event.payload}\nevent: online\n\n"
time.sleep(0.01)
return Response(
response=update_pusher(),
mimetype='text/event-stream'
)
if __name__ == "__main__":
http_server = WSGIServer(("localhost", 5003), app)
http_server.serve_forever()
index.html
<p>This list is populated by server side events.</p>
<ul id="list"></ul>
<script>
var eventSource = new EventSource("/events")
eventSource.addEventListener("online", function(e) {
// console.log(e.data.color)
data = JSON.parse(e.data)
const li = document.createElement('li')
li.innerText = data
list.appendChild(li)
}, false)

Return continuous terminal log through flask client - Python

Is there a way to return multiple responses to 1 get request?
I have a basic flask app where I am trying to make it run another python app and send the terminal logs to the client side.
I can return a json value but I can't return any text or the output of the terminal.
Here is the server side:
from flask import Flask, stream_with_context, request, Response
from flask_restful import Api, Resource
from flask_socketio import SocketIO
import intermedia_choose_action_flask
import subprocess
app = Flask(__name__)
api = Api(app)
socketio = SocketIO(app)
class spamblacklistsend(Resource):
def get(self):
imapp = subprocess.Popen(["python3", "/home/tech/scripts/Intermedia_automate/intermedia_choose_action.py", "--addblockeveryone", "--ed", "test#fakeu.com"], bufsize=10, errors='replace')
imapp.app_context().push()
# p = subprocess.Popen(["python3", "/home/tech/scripts/Intermedia_automate/intermedia_choose_action.py", "--addblockeveryone", "--ed", "test#fakeu.com"], bufsize=10, errors='replace')
return imapp.app_context().push()
api.add_resource(spamblacklistsend, "/spamblacklistsend")
if __name__ == "__main__":
app.run(debug=True)
Here is the client side:
from flask import json
import requests
BASE = "http://127.0.0.1:5000/"
response = requests.get(BASE + "spamblacklistsend")
print(imapp.app_context().push())
I know that return stops the function. Is there anyway to return and continue?

Passing data received from a json request to another function in python flask socketio

I have a python flask app which receives data from json. I have also used socketio and threading in order to process data realtime.
In my program I need to send the data, that I receive from json requests, to another python function.
Below is the code that I wrote to do this: -
from flask_socketio import SocketIO, emit
from flask import Flask, render_template, request, url_for, copy_current_request_context
from random import random
from time import sleep
from pygeodesy.ellipsoidalVincenty import LatLon
from threading import Thread, Event
__author__ = 'shark'
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
app.config['DEBUG'] = True
# turn the flask app into a socketio app
socketio = SocketIO(app, async_mode=None, logger=True, engineio_logger=True)
# random number Generator Thread
thread = Thread()
thread_stop_event = Event()
#app.route('/platform-data', methods=['POST'])
def platformData():
"""
Generate a random number every 1 second and emit to a socketio instance (broadcast)
Ideally to be run in a separate thread?
"""
# infinite loop of magical random numbers
print("Receiving platform data")
while not thread_stop_event.isSet():
req_data = request.get_json()
id = req_data['id']
latitude = req_data['coordinates'][1]
longitude = req_data['coordinates'][0]
speed = req_data['speed']
angle = req_data['angle']
length = req_data['dimensions'][0]
width = req_data['dimensions'][1]
laneW = req_data['lane_width']
spdLmt = req_data['speed_limit']
return testProcess(speed)
def testProcess(speed):
if speed>30:
print("slow down")
socketio.emit('speed', {'speed': speed}, namespace='/test')
socketio.sleep(.5)
#app.route('/')
def index():
# only by sending this page first will the client be connected to the socketio instance
return render_template('index.html')
#socketio.on('connect', namespace='/test')
def test_connect():
# need visibility of the global thread object
global thread
print('Client connected')
# Start the random number generator thread only if the thread has not been started before.
if not thread.isAlive():
print("Starting Thread")
thread = socketio.start_background_task(platformData)
#socketio.on('disconnect', namespace='/test')
def test_disconnect():
print('Client disconnected')
if __name__ == '__main__':
socketio.run(app)
However, when I run the app and POST data from Postman, I get the below error in my console: -
TypeError: The view function did not return a valid response. The
return type must be a string, dict, tuple, Response instance, or WSGI
callable, but it was a int.
127.0.0.1 - - [05/Mar/2020 17:06:25] "POST /platform-data HTTP/1.1" 500 15625 0.008975
I know the reason for this is that I have declared return testProcess(speed).
Therefore, I need to know the correct way to pass speed variable to 'testProcess' function.
platformData() must return a a string, dict, tuple, Response instance, or WSGI callable but you make return the return of testProcess which is not defined
try:
def testProcess(speed):
if speed>30:
return "slow down"
else:
return "ok"
Or just call testProcess(speed) without return
and then return something else

Flask socket.io redirect to template after socket.io event finishes

I am receiving some socket.io event. After the event is handled I would like to redirect the user to some page. However, the redirection doesn't work. I don't know what is wrong. Below is my code:
from flask import Flask, render_template, send_from_directory, redirect, url_for
from flask_socketio import SocketIO, emit
import base64
import os
import random, string
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#app.route('/')
def index():
try:
image_names = os.listdir('./images')
print image_names
return render_template("gallery.html", image_names=image_names)
except Exception as ex:
print ex
#socketio.on('takephoto')
def takePhoto(*args):
try:
decoded = base64.b64decode(args[0])
filename = ''.join(random.choice(string.lowercase) for x in range(6)) + '.jpg'
with open("./images/" + filename, "wb") as fh:
fh.write(args[0].decode('base64'))
except Exception, ex:
print ex
redirect(url_for('index')) #This doesnt work
#how can i go to index from this point?
if __name__ == '__main__':
from gevent import pywsgi
from geventwebsocket.handler import WebSocketHandler
app.debug = True
server = pywsgi.WSGIServer(('', 5001), app, handler_class=WebSocketHandler)
server.serve_forever()
I think you are missing the return statement.
return redirect(url_for('index'))
A little late to the party, but I just ran into this. Emitting a 'redirect' event and sending url_for('index') all via sockets is your best bet.
Server side:
emit('redirect', url_for('auth.login'), namespace=request.namespace, room=[request.sid])
Client side:
socket.on('redirect', (dest) => {
window.location.href = dest;
});

Long polling chat server

I need some help regarding some minor problems in this chat server I've written. As you can see, I am using a global variable 'chat' to store newly submitted messages. This is because I can't find a working pubsub module for MongoDB (or I just don't know how to use one). Because of this, when 2 people send message almost at the exact same time, only the newer message is being outputted. Here is my current code:
#!/usr/bin/env python
import gevent
import gevent.monkey; gevent.monkey.patch_all()
from gevent.event import Event
from gevent.pywsgi import WSGIServer
from flask import Flask, request, Response, render_template, jsonify
import time
from pymongo import MongoClient
app = Flask(__name__)
app.config['DEBUG'] = True
a = Event()
chat = {}
def logchat(data):
client = MongoClient()
db = client.chatlog
db.bences.insert(data)
#app.route('/output')
def sse_request():
a.wait()
return jsonify(chat)
#app.route('/processchat', methods=['POST'])
def processchat():
global chat
ms = int(round(time.time() * 1000))
chat = {'name': request.form['chatname'], 'msg': request.form['chatmsg'], 'ts': ms}
a.set(); a.clear()
logchat(chat)
return jsonify(success=True)
#app.route('/')
def page():
return render_template('chat.html')
if __name__ == '__main__':
http_server = WSGIServer(('0.0.0.0', 80), app)
http_server.serve_forever()
What can you suggest for me to do? Thanks a lot!

Categories