Flask-SocketIO running app but not firing events - python

So I'm building a simple Flask social media application that uses Flask's session ids. I want to remove a user's session id whenever they leave the page so that they have to re-login. I did a little research and found out that Flask-SocketIO allows you to do that through the 'connect' and 'disconnect' events. However, when I run the app, and I open up the site/log on to the page, nothing happens. I don't get any output from the connect event nor the disconnect event when I close the tab. Here's the full code.
# Starting/Initialisation of App
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = SECRET_KEY
socket_io = SocketIO(app=app, logger=True, engineio_logger=True)
db = SQLAlchemy(app)
# Socket IO Events
#socket_io.on('connect')
def on_connect():
print("Connected!")
#socket_io.on('disconnect')
def on_disconnect():
# TODO Fix this socket io disconnect event. It's not working for some reason. Just figure things out with socketio
print("Client Disconnected")
print("YOOOOOOOOOO")
session.pop('id')
#socket_io.on('message')
def on_message():
print("Exit message")
# App Routes
# A page where a user can login to an account
#app.route('/', methods=['GET', 'POST'])
def index():
# If user submits form
if request.method == "POST":
# Get response and user's Account object from db
# if query does not return None aka user is in database
# Check if response's password is the same as the user's password
# If it is, redirect to homepage
# Else if not, return an "invalid password" error
form = request.form # User Response
user = Account.query.filter_by(email=str(request.form['email'])).first() # Query for user
if user is not None: # Validating query
if str(form['password']) == user.password: # Checking password
session['id'] = user.id
return redirect('/home')
else:
return "Invalid password"
else:
return "Invalid email or password"
else:
return render_template('index.html')
# Run App
if __name__ == '__main__':
socket_io.run(app)
I'm don't receive any errors or whatsoever so I'm really confused.
Below is my pip freeze
bidict==0.21.2
click==8.0.1
colorama==0.4.4
Flask==2.0.1
Flask-SocketIO==5.1.0
Flask-SQLAlchemy==2.5.1
greenlet==1.1.0
h11==0.12.0
itsdangerous==2.0.1
Jinja2==3.0.1
MarkupSafe==2.0.1
python-engineio==4.2.0
python-socketio==5.3.0
simple-websocket==0.2.0
SQLAlchemy==1.4.22
Werkzeug==2.0.1
wsproto==1.0.0
Would really appreciate it if anyone has any solutions to this. Thanks in advance.

Few things to consider here:
print statements to stdout won't necessarily show in your output with Flask, I believe stderr does, so you might want to try either that or the logger, e.g.:
print('Connected!', file=sys.stderr)
app.logger.info("Connected!")
Are you sure you have the right version of the client? I don't believe the client is included with Flask-SocketIO, and Flask-SocketIO isn't compatible with the latest client version (4). You should be using client version 3.1.3 (https://socket.io/docs/v3/). This is the version of the SocketIO library you include (this example truncated from the aforementioned SocketIO docs and updated to show an alert box once connected so you can be sure it's worked):
<html>
<body>
...
<script src="https://cdn.socket.io/3.1.3/socket.io.min.js" integrity="sha384-cPwlPLvBTa3sKAg ddT6krw0cJat7egBga3DJepJyrLl4Q9/5WLra3rrnMcyTyOnh" crossorigin="anonymous"></script>
<script type='text/javascript'>
const socket = io("ws://localhost:5000");
socket.on("connect", () => {
// either with send()
alert("You're connected!");
socket.send("Hello!");
// or with emit() and custom event names
socket.emit("salutations", "Hello!", { "mr": "john" }, Uint8Array.from([1, 2, 3, 4]));
});
</script>
</body>
</html>
does your web console in your browser show any errors? Are you sure the client side code is correct and you're connected?
Viewing the Javascript console varies from browser to browser, but will show you any errors encountered in your Javascript. In Chrome for example, you click the 3 dots to the far right of your address bar, then More Tools -> Developer Tools. This is cruicial in debugging Javascript issues.

Are you sure you have included socket.io in client side means in html. you can include this script in your head in html to include it.
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous" defer></script>
Then you need to define socket in your javascript file or script tag.
<script type="text/javascript">
socket = io.connect(document.href );
socket.on('connect', function() {
// do something when user connects
});
socket.emit("message",data) // data is sent to flask
</script>
for more information, refer to the documentation.
https://flask-socketio.readthedocs.io/en/latest/

Related

Problem loading a web app using flask, python, HTML and PostgreSQL; not able to connect python and html scripts plus Internal Server Error message

Recently, I have started working on a new project : a web app which will take a name as an input from a user and as result outputs the database rows related to the user input. The database is created using PostgreSQL and in order to complete the task I am using Python as a programming language, followed by Flask (I am new to it) and HTML. I have created 2 source codes, 1 in Python as below :
import os
import psycopg2 as pg
import pandas as pd
import flask
app = flask.Flask(__name__)
#app.route('/')
def home():
return "<a href='/search'>Input a query</a>"
#app.route('/search')
def search():
term = flask.request.args.get('query')
db = pg.connect(
host="***",
database="***",
user ="***",
password="***")
db_cursor = db.cursor()
q = ('SELECT * FROM table1')
possibilities = [i for [i] in db_cursor.execute(q) if term.lower() in i.lower()]
return flask.jsonify({'html':'<p>No results found</p>' if not possibilities else '<ul>\n{}</ul>'.format('\n'.join('<li>{}</li>'.format(i) for i in possibilities))})
if __name__ == '__main__':
app.run()
and HTML code :
<html>
<head>
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<input type='text' name ='query' id='query'>
<button type='button' id='search'>Search</button>
<div id='results'></div>
</body>
<script>
$(document).ready(function(){
$('#search').click(function(){
var text = $('#query').val();
$.ajax({
url: "/search",
type: "get",
data: {query: text},
success: function(response) {
$("#results").html(response.html);
},
error: function(xhr) {
//Do Something to handle error
}
});
});
});
</script>
</html>
For these scripts I read the discussion here.
These scripts are giving me troubles and I have two main questions :
First : How are these two source codes connected to each other? whenever I run the python script or the html, they look completly disconnected and are not functioning.Moreover, when I run the Python script it gives me this error message on the webpage :
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
and this message on terminal :
Serving Flask app 'userInferface' (lazy loading)
Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
Debug mode: off
Running on....
Can someone please help me by showing how can these 2 scripts connect and why am I getting such errors. Thank you.
You need to use render_template to connect Flask and your HTML code. For example:
from flask import render_template
#app.route("/", methods=['GET'])
def index():
return render_template('index.html')

Flask-SocketIO not receiving events on server

I'm currently learning Flask and have recently found out about Flask-SocketIO. I've learned, that the module is based on events so that the client side can communicate with the server side, so I tried doing that. But for some reason, the events I send from the client side, don't make it to the server. I've tried fixing it for a few hours but I don't understand what is wrong with my code. Thanks for helping me!
main.py
from flask import Flask, render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#app.route("/", methods=["GET", "POST"])
def home():
return render_template("index.html")
#socketio.on('my event')
def handle_my_custom_event(json):
print('received json: ' + str(json))
if __name__ == "__main__":
socketio.run(app, debug=True)
/templates/index.html
<!DOCTYPE html>
<html lang="en">
<h1> Chat room</h1>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('connect', function() {
socket.emit('my event', {data: 'I\'m connected!'}); #Creating event when connected
});
</script>
</html>
Any time things don't go the way you think they should go, you have to look for clues left in the logs. In this case that means looking at the output of the Flask process, and the browser console.
The Flask process does not show any errors, but it does show that there was no Socket.IO connection attempted. So really this isn't a problem about events not being received, but connections don't being made.
The browser console shows this:
Uncaught SyntaxError: Private field '#Creating' must be declared in an enclosing class
You see the problem? You are using # to start a comment. That should have been a // in JavaScript.

JavaScript EventSource triggers onerror after every received message

I have a web app utilizing Python Flask where I am trying to use Server Sent Events (SSEs) to push messages to web pages without having to poll from the client side or request that data. I'm using Redis to listen for new data which will then be sent to the web page. To start and make sure that I can use SSEs correctly, I've used a template similar to an example like this (How to implement server push in Flask framework?).
The problem I'm running into is that every time the client receives a message, the EventSource onmessage() method is called and delivers the message properly, but then the .onerror() method immediately gets triggered, causing the client to try to reconnect. This results in the '/listen' endpoint being called over and over and over, leading to the creation of many redis pubsub objects that are redundant and subscribe to the same channels.
The python code that runs the flask app is as follows
import flask
from flask_bootstrap import Bootstrap
from redis import Redis
from flask_wtf import FlaskForm
app = flask.Flask(__name__)
bootstrap = Bootstrap(app)
red = Redis(host='localhost', port=6379, db=0)
#app.route('/listen')
def listen():
pubsub = red.pubsub()
pubsub.subscribe('chat')
def stream():
for message in pubsub.listen():
if message['type'] == 'message':
msg = f"data: {message['data'].decode('utf-8')}\n\n"
yield msg
for msg in stream():
return Response(msg, mimetype='text/event-stream')
#app.route('/sse_page', methods=['GET', 'POST'])
def sse_page():
form = FlaskForm()
return render_template('sse_page.html', title='Server Push Testing', form=form)
if __name__ == "__main__":
app.run(port=8000, threaded=True, debug=True)
The corresponding section of sse_page.html where I try to open the EventSource and listen for the events stream is
<body>
<div id="target_div">Watch this space...</div>
</body>
<script>
var source = new EventSource("/listen");
source.onmessage = function (event) {
console.log('data: ', event)
$("#target_div").text(event.data)
};
source.onerror = function (event) {
console.log('error ', event)
};
source.onopen = function (event) {
console.log('open', event)
};
</script>
Using the redis-cli to send messages like those seen
here (and transcribed below)
127.0.0.1:6379> publish chat a
(integer) 1
127.0.0.1:6379> publish chat b
(integer) 2
Result in the console logging messages from Eventsource.onopen(), Eventsource.onmessage(), and Eventsource.onerror() for every single message, as seen
here.
I cannot figure out why the eventsource has an error after every single message that is received or how to prevent that from happening.
The answer to this question is NOT a problem in the code itself.
What this ended up being was an issue with the anti-virus security that was being used on the machine. Using Sophos AV Endpoint was causing each SSE to be treated as a download, and so any of the text data was unable to be streamed until the 'download was complete'.
This is (apparently) a known issue (see link https://community.sophos.com/on-premise-endpoint/f/sophos-endpoint-software/74878/server-sent-events-blocked-by-download-scanner) and there are a couple of ways to deal with it. You can either disable web scanning by Sophos (which does not work if you do not have administrator permissions) or run the flask app securely over HTTPS (https://blog.miguelgrinberg.com/post/running-your-flask-application-over-https has a great tutorial).
Credit too should go to this post (JavaScript EventSource SSE not firing in browser), which is how I was able to find that the AV software was what ended up causing my issues.

How to refresh front page after back-end task completed

I'm design a web application with python flask, the processing flow is like:
User choose a specified a URL, and ask server site to do some time-consuming task;
The time-consuming task is then running in a new thread;
After #2 is done, server will update some info regarding the URL, I need to refresh the page in #1
I don't know how to notify the "completion" status to front-end so as to refresh the page.
Anyone can help me out?
Thanks
Using flask-socketio would probably work for what you're asking. It establishes a connection between the front-end and back-end that lets the backend emit a message and the front-end registers a callback that fires when it receives the event.
$ pip install flask-socketio
app.py
import json
import time
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#app.route('/')
def hello(name=None):
return render_template('index.html')
#socketio.on('long-running-event')
def handle_my_custom_event(input_json):
time.sleep(5)
emit('processing-finished', json.dumps({'data': 'finished processing!'}))
if __name__ == '__main__':
socketio.run(app)
templates/index.html
<html>
<body>
<button id="long-running-operation">Process data</button>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
var socket = io.connect('http://' + document.domain + ':' + location.port);
let button = document.getElementById('long-running-operation');
button.addEventListener('click', () => {
socket.emit('long-running-event', {data: 'process data'});
});
socket.on('processing-finished', function (data) {
alert(JSON.parse(data).data)
});
</script>
</body>
</html>
Then run the app with python app.py. Visit the app in the browser at http://127.0.0.1:5000/ and click the button. The button will send the 'long-running-event' request to flask and flask processes it by sleeping for 5 seconds to simulate a long running process. Then flask emits the 'processing-finished message and javascript fires the callback registered to listen for that event by alerting the message that flask sent.

Why does my generator block Flask from sending SSE responses?

I am trying to use Flask to serve an SSE request, but my client only receives the events after my generator function has stopped / the connection is closed.
Here is the simplest reproduction I have been able to produce to demonstrate this:
#!/usr/bin/env python
from flask import Flask, Response
from time import sleep
def stream():
n = 10
while n > 0:
yield "data: hi\n\n"
sleep(0.5)
n = n - 1
app = Flask(__name__)
#app.route("/events")
def streamSessionEvents():
return Response(
stream(),
mimetype="text/event-stream"
)
app.run(debug=True, threaded=True)
Here is my test client:
<!doctype html>
<html>
<head>
<script>
var source = new EventSource(
"/events"
);
source.onmessage = function(event)
{
console.log(event);
};
</script>
</head>
<body>
</body>
</html>
The stream() generator will produce ten events and then return (I've deliberately done this to demonstrate the problem, ideally the generator would keep going forever), at which point the connection is dropped. The client page logs nothing until this point, then it spits out all ten events (if I dont have the counter variable in stream() then the page never gets any events).
I havent used Python or Flask a great deal and this has me very stuck, I cant see what I'm doing differently to other examples around the net. Any help very much appreciated.
Two things might interfere:
You have debug set to True, which installs middleware (specifically the Werkzeug debugger) that may break streaming.
From the Flask streaming patterns documentation:
Note though that some WSGI middlewares might break streaming, so be careful there in debug environments with profilers and other things you might have enabled.
However, using either curl or Chrome on your test code with Flask 0.10.1 and Werkzeug 0.9.4 I see the data: hi responses come streaming through properly, regardless of the debug flag setting. In other words, your code is working correctly with the most recent versions of the Flask stack.
EventSource streams are subject to same-origin policy limits. If you are not loading the HTML page from the same host and port, the request to your Flask server will be denied.
Adding the test page source in the Flask server at a separate route works for me:
#app.route('/')
def index():
return '''\
<!doctype html>
<html>
<head>
<script>
var source = new EventSource(
"/events"
);
source.onmessage = function(event)
{
console.log(event);
};
</script>
</head>
<body>
</body>
</html>
'''

Categories

Resources