This question already has an answer here:
Flask: A RESTful API and SocketIO Server
(1 answer)
Closed 7 years ago.
This seems like a very simple problem but it's got me confused nonetheless, I have a Flask application that serves up a webpage and communicates with that page via Socket.io. The Flask application looks like this:
app = Flask(__name__)
socketio = SocketIO(app)
#socketio.on_error()
def error_handler(e):
print e
#this fires
#socketio.on("connect")
def connect():
print "connected"
#this does not
#socketio.on('test')
def test_handler(message):
print "TEST WORKS"
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0')
socketio.run(app)
My page itself is very simple:
<!doctype html>
<html>
<head>
<title>Flask Socket.IO test</title>
<style>
body { font: 13px Helvetica, Arial; }
</style>
<script src="socket.io-1.4.3.js"></script>
<script src="jquery-2.2.0.min.js"></script>
<script>
var socket = io("192.168.42.1:5000");
socket.on('connect', function() {
console.log(" connected ");
});
$(document).ready( function() {
$( '.startBtn' ).click(function() {
console.log("ok");
socket.emit('test', {data:"start!"});
});
});
</script>
</head>
<body>
<div class="startBtn">
<h1>START</h1>
</div>
</body>
</html>
I see on both sides that they connect (i.e. the connect event is triggered on both sides) but nothing that I send from the page to the server is received. I'm guessing somehow I have things misconfigured but the connection being established makes me think otherwise.
So the problem seems to be in how I was setting up the Flask application and socketio. Changing it to this:
app = Flask(__name__)
socketio = SocketIO(app, async_mode='eventlet')
#app.route('/')
def index():
return render_template('index.html')
#socketio.on('test')
def josh_test(message):
print "test"
if __name__ == '__main__':
socketio.run(app, debug=True)
it now all works fantastically with no changes to the HTML file. My previous version had:
if __name__ == "__main__":
app.run(debug=True, host='0.0.0.0')
socketio.run(app)
and that was what was causing the problems.
Related
I have a flask app that is running selenium on a VM server. I am running a long looping function, and I want to alert the user on the client side if the driver have reached certain points while scrawling (because the function runs in a very long loop, it's not possible afaik to use flask flash without returning redirect or render_template)
This why I think socketio is my only option.
What I have currently is:
Snippet from routes.py (trying to connect a driver)
#app.route("/", methods=["GET", "POST"])
#login_required
def index():
if request.method == "POST":
try:
globals()["driver_" + str(user_id)] = uc.Chrome(options=chrome_options, use_subprocess=True)
What I want to achieve:
try:
globals()["driver_" + str(user_id)] = uc.Chrome(options=chrome_options, use_subprocess=True)
message = "Driver connected succssefully"
socket.emit('send-message', message)
I have the following in layout.html:
<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() {
console.log('initSocketIO');
});
socket.on('send-message', (message) => {
console.log(message);
});
</script>
I can see in the console initSocketIO but how do I set it up that I will also see send-message data ("Driver connected succssefully") on the console?
How can i see data from realtime on my page? When I use these codes outside of flask, the listener waits for the data to come and prints it to the screen.
My python code:
import itertools
import time
from flask import Flask, Response, redirect, request, url_for
from TikTokLive import TikTokLiveClient
from TikTokLive.types.events import CommentEvent, ConnectEvent, GiftEvent
# Instantiate the client with the user's username
client: TikTokLiveClient = TikTokLiveClient(unique_id="#username")
app = Flask(__name__)
#client.on("connect")
async def on_connect(_: ConnectEvent):
print("Connected to Room ID:", client.room_id)
#client.on("gift")
async def on_gift(event: GiftEvent):
# If it's type 1 and the streak is over
if event.gift.gift_type == 1:
f"{event.user.uniqueId} sent {event.gift.repeat_count}x \"{event.gift.extended_gift.name}\" {event.gift.extended_gift.name}"
#app.route('/')
def index():
if request.headers.get('accept') == 'text/event-stream':
return Response(on_gift(GiftEvent), content_type='text/event-stream')
return redirect(url_for('static', filename='index.html'))
if __name__ == "__main__":
app.run(debug=True)
client.run()
My index.html page
<!doctype html>
<title>Server Send Events Demo</title>
<style>
#data {
text-align: center;
}
</style>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
if (!!window.EventSource) {
var source = new EventSource('/');
source.onmessage = function(e) {
$("#data").text(e.data);
}
}
</script>
<div id="data">nothing received yet</div>
My error
My goal is for my Flask server to send the client data either every three seconds, or when a function is called. To do this I am using SocketIO. However based on some example code I am working with, it seems that I can only send data after a client requests something. I don't want the client to have to 'poll' to find if there is new data, so I want the server to push it when it is ready.
Here is what I tried so far. (some of the code is unnecessary since it is based off an example) This should use socketio to push the time to the client every few seconds.
HTML
<!DOCTYPE HTML>
<html>
<head>
<title>Socket-Test</title>
<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
namespace = '/test';
var socket = io(namespace);
socket.on('my_response', function(msg, cb) {
$('#log').text( $('<div/>').text(msg.data).html());
if (cb)
cb();
});
});
</script>
</head>
<body style="background-color:white;">
<h1 style="background-color:white;">Socket</h1>
<div id="time" ></div>
</body>
</html>
Python
import threading
from flask import Flask, render_template, session, copy_current_request_context,request
from flask_socketio import SocketIO, emit, disconnect
from threading import Lock
import time
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()
clients = []
def update():
time.sleep(1)
emit('my_response',
{'data': time.time},
room=clients[0])
t=threading.Thread(target=update)
#socket_.on('connect')
def handle_connect():
print('Client connected')
clients.append(request.sid)
t.start()
#app.route('/')
def index():
return render_template('index.html', async_mode=socket_.async_mode)
#socket_.on('my_event', namespace='/test')
def test_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': message['data'], 'count': session['receive_count']})
#socket_.on('my_broadcast_event', namespace='/test')
def test_broadcast_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': time.time},
broadcast=True)
socket_.run(app,port=8050)
I try to run it but it gives me the error RuntimeError: Working outside of request context.
I fixed my code by following this tutorial: https://www.shanelynn.ie/asynchronous-updates-to-a-webpage-with-flask-and-socket-io/
import threading
from flask import Flask, render_template, session, copy_current_request_context,request
from flask_socketio import SocketIO, emit, disconnect
from threading import Lock
import time
async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socket_ = SocketIO(app, async_mode=async_mode)
thread = None
thread_lock = Lock()
def update():
time.sleep(1)
socket_.emit('my_response',
{'data': time.time()},
namespace='/test')
print("emitted")
update()
t=threading.Thread(target=update)
#socket_.on('connect', namespace='/test')
def handle_connect():
print('Client connected')
if not t.isAlive():
t.start()
#app.route('/')
def index():
return render_template('index.html', async_mode=socket_.async_mode)
socket_.run(app,port=8070)
HTML
<!DOCTYPE HTML>
<html>
<head>
<title>Socket-Test</title>
<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
namespace = '/test';
var socket = io(namespace);
console.log(("test"));
socket.on('my_response', function(msg) {
$('#time').text( $('<div/>').text(msg.data).html());
console.log(msg);
});
});
</script>
</head>
<body style="background-color:white;">
<h1 style="background-color:white;">Socket</h1>
<div id="time" ></div>
</body>
</html>
I would like to point out that using recursion in this case is not the best choice.
you call the update function inside the update and do not have the completion of this process.
the best option would be to use a loop(as done in the link you attached)
def update():
while True:
time.sleep(1)
socket_.emit('my_response', {'data': time.time()}, namespace='/test')
print("emitted")
t=threading.Thread(target=update)
also, it would be better to write "while is_work_var" instead of "while True"
Based on the example on GitHub, this is my Python script:
from flask import Flask, render_template, session, request
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, async_mode='eventlet')
#app.route('/')
def index():
return render_template('index.html')
#socketio.on('my event', namespace='/test')
def test_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my response',
{'data': message['data'], 'count': session['receive_count']})
if __name__ == '__main__':
socketio.run(app, debug=True)
This is the HTML template:
<!DOCTYPE HTML>
<html>
<head>
<script src="//code.jquery.com/jquery-1.4.2.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io.min.js"></script>
<script charset="utf-8">
$(document).ready(function(){
namespace = '/test'; // change to an empty string to use the global namespace
// the socket.io documentation recommends sending an explicit package upon connection
// this is specially important when using the global namespace
var socket = io.connect('http://' + document.domain + ':' + location.port + namespace);
// event handler for server sent data
// the data is displayed in the "Received" section of the page
socket.on('my response', function(msg) {
$('#log').append('<br>Received #' + msg.count + ': ' + msg.data);
});
// handlers for the different forms in the page
// these send data to the server in a variety of ways
$('form#emit').submit(function(event) {
socket.emit('my event', {data: $('#emit_data').val()});
return false;
});
});
</script>
</head>
<body>
<form id="emit" method="POST" action='#'>
<input type="text" name="emit_data" id="emit_data" placeholder="Message">
<input type="submit" value="Echo">
</form>
<h2>Receive:</h2>
<div id="log"></div>
</body>
</html>
Everything works fine. But the problem is user can use any HTML tags in the messages.
For example:
I think it's little dangerous. Because any users can also run some JavaScript code and broadcast it. Then every clients will run it.
Is there's anyway can use Jinja auto escape the output, or there's any other ways?
Flask-SocketIO author here.
The example application was meant as a quick example of how to send and receive messages, I did not consider it to be an example of how to deal with user input safely.
But the point is well taken, I have updated the example app to properly handle user input now. How you do this is dependent on the application. For this example, I've chosen to do the escaping on the client side using jQuery:
$('#log').append('<br>' + $('<div/>').text('Received #' + msg.count + ': ' + msg.data).html());
However, the way I found is, we can escape the HTML characters in the script like:
import jinja2
# other code here
#socketio.on('my event', namespace='/test')
def test_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my response',
{'data': jinja2.escape(message['data']), 'count': session['receive_count']})
# ^^^^^^^^^^^^^^ use jinja2 escape the output before send it to the clients.
Demo output:
I had a small sandbox server on gevent-socketio.
The backend was
from flask import Flask, render_template, request
from socketio import socketio_manage
from socketio.namespace import BaseNamespace
from socketio.server import SocketIOServer
class FirstNamespace(BaseNamespace):
def on_make_upper_hook(self, msg):
response = {'response from make_upper':msg.upper()}
self.emit('response_channel', response)
return True
app = Flask(__name__)
app.config['DEBUG'] = True
#app.route('/')
def index():
return render_template('index.html')
#app.route('/socket.io/')
#app.route('/socket.io/<path:remaining>')
def socketio_endpoint(remaining=None):
print('remaining path: {}'.format(remaining))
socketio_manage(request.environ,
{'/chat': FirstNamespace})
return 'ok'
if __name__ == '__main__':
SocketIOServer(('0.0.0.0', 8080), app,
resource="socket.io").serve_forever()
And the frontend (with socket.io 0.9.6) was
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="static/socket.io.js"></script>
<script>
var socket = io.connect('/chat');
socket.on('connect', function () {
console.log('socket connected');
});
</script>
</head>
<body>
</body>
</html>
In the browser console I saw that connection went fine. The server was also fine:
127.0.0.1 - - [2015-07-05 10:30:55] "GET / HTTP/1.1" 200 420 0.006791
remaining path: 1/websocket/683868734428
But!
When I tried to upgrade socket.io like so:
<script src="https://cdn.socket.io/socket.io-1.3.5.js"></script>
<script>
var socket = io('http://localhost:8080/chat');
socket.on('connect', function () {
console.log('socket connected');
});
</script>
I started to get client errors
GET http://localhost:8080/socket.io/?EIO=3&transport=polling&t=1436081649609-0 500 (Internal Server Error)
and server errors:
File "/Users/1111/.virtualenvs/stuff/lib/python2.7/site-packages/socketio/__init__.py",
line 67, in socketio_manage
socket = environ['socketio']
KeyError: 'socketio'
So somehow my precious socketio key disappeared from request.environ and I don't know how to get it back. How to fix this?