Why are requests with same url processed synchronously? - python

I thought it was a quirk of the framework I was using, so I tested with another framework, but the result is the same: sending a burst of requests will process them asynchronously as expected when the urls are different, but will queue the requests with the same url and process them synchronously.
Why are some requests processed synchronously and some asynchronously?
Below the code to test with both Flask and CherryPy. Visiting localhost:5000 with Flask and localhost:8080 with CherryPy will load a page with JavaScript that will send a burst of 16 requests. The requests have an unused parameter in the query string which can have 4 different values, so the server will receive 4 different requests, each 4 times, for a total of 16 requests.
The server starts processing the first 4 requests for each parameter value, after finishing them will start processing the next 4 requests, etc.
Why aren't all 16 requests processed at the same time?
Flask
import flask
import time
import threading
import datetime
app = flask.Flask(__name__)
def log(txt):
print(' {} {:6d} {}'.format(datetime.datetime.now().strftime('%H:%M:%S.%f'),
threading.current_thread().ident,
txt))
#app.route('/')
def index():
log('index')
return """<!DOCTYPE HTML>
<html>
<head>
<script>
setTimeout(function(){{
for(i=0; i<16; i++) {{
xhttp = new XMLHttpRequest();
xhttp.open("GET", "test/" + i%4, true);
xhttp.send();
}}
}}, 2000);
</script>
</head>
<body>
hello
</body>
</html>"""
#app.route('/test/<x>')
def test(x):
log('test{}'.format(x))
time.sleep(3)
log(' test{}'.format(x))
return 'OK'
app.run(threaded=True)
CherryPy
import cherrypy
import time
import datetime
import threading
def log(txt):
print(' {} {:6d} {}'.format(datetime.datetime.now().strftime('%H:%M:%S.%f'),
threading.current_thread().ident,
txt))
class Root:
#cherrypy.expose
def index(self):
log('index')
return """<!DOCTYPE HTML>
<html>
<head>
<script>
setTimeout(function(){{
for(i=0; i<16; i++) {{
xhttp = new XMLHttpRequest();
xhttp.open("GET", "test?x=" + i%4, true);
xhttp.send();
}}
}}, 2000);
</script>
</head>
<body>
hello
</body>
</html>"""
#cherrypy.expose
def test(self, x):
log('test{}'.format(x))
time.sleep(3)
log(' test{}'.format(x))
return 'OK'
if __name__ == '__main__':
cherrypy.quickstart(Root())

Related

Python flask realtime in render html

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

How can I send data though socket-io without the client requesting first with python and flask

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"

Simple example how to redirect logging messages with flask? [duplicate]

I have a view that generates data and streams it in real time. I can't figure out how to send this data to a variable that I can use in my HTML template. My current solution just outputs the data to a blank page as it arrives, which works, but I want to include it in a larger page with formatting. How do I update, format, and display the data as it is streamed to the page?
import flask
import time, math
app = flask.Flask(__name__)
#app.route('/')
def index():
def inner():
# simulate a long process to watch
for i in range(500):
j = math.sqrt(i)
time.sleep(1)
# this value should be inserted into an HTML template
yield str(i) + '<br/>\n'
return flask.Response(inner(), mimetype='text/html')
app.run(debug=True)
You can stream data in a response, but you can't dynamically update a template the way you describe. The template is rendered once on the server side, then sent to the client.
One solution is to use JavaScript to read the streamed response and output the data on the client side. Use XMLHttpRequest to make a request to the endpoint that will stream the data. Then periodically read from the stream until it's done.
This introduces complexity, but allows updating the page directly and gives complete control over what the output looks like. The following example demonstrates that by displaying both the current value and the log of all values.
This example assumes a very simple message format: a single line of data, followed by a newline. This can be as complex as needed, as long as there's a way to identify each message. For example, each loop could return a JSON object which the client decodes.
from math import sqrt
from time import sleep
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def index():
return render_template("index.html")
#app.route("/stream")
def stream():
def generate():
for i in range(500):
yield "{}\n".format(sqrt(i))
sleep(1)
return app.response_class(generate(), mimetype="text/plain")
<p>This is the latest output: <span id="latest"></span></p>
<p>This is all the output:</p>
<ul id="output"></ul>
<script>
var latest = document.getElementById('latest');
var output = document.getElementById('output');
var xhr = new XMLHttpRequest();
xhr.open('GET', '{{ url_for('stream') }}');
xhr.send();
var position = 0;
function handleNewData() {
// the response text include the entire response so far
// split the messages, then take the messages that haven't been handled yet
// position tracks how many messages have been handled
// messages end with a newline, so split will always show one extra empty message at the end
var messages = xhr.responseText.split('\n');
messages.slice(position, -1).forEach(function(value) {
latest.textContent = value; // update the latest value in place
// build and append a new item to a list to log all output
var item = document.createElement('li');
item.textContent = value;
output.appendChild(item);
});
position = messages.length - 1;
}
var timer;
timer = setInterval(function() {
// check the response for new data
handleNewData();
// stop checking once the response has ended
if (xhr.readyState == XMLHttpRequest.DONE) {
clearInterval(timer);
latest.textContent = 'Done';
}
}, 1000);
</script>
An <iframe> can be used to display streamed HTML output, but it has some downsides. The frame is a separate document, which increases resource usage. Since it's only displaying the streamed data, it might not be easy to style it like the rest of the page. It can only append data, so long output will render below the visible scroll area. It can't modify other parts of the page in response to each event.
index.html renders the page with a frame pointed at the stream endpoint. The frame has fairly small default dimensions, so you may want to to style it further. Use render_template_string, which knows to escape variables, to render the HTML for each item (or use render_template with a more complex template file). An initial line can be yielded to load CSS in the frame first.
from flask import render_template_string, stream_with_context
#app.route("/stream")
def stream():
#stream_with_context
def generate():
yield render_template_string('<link rel=stylesheet href="{{ url_for("static", filename="stream.css") }}">')
for i in range(500):
yield render_template_string("<p>{{ i }}: {{ s }}</p>\n", i=i, s=sqrt(i))
sleep(1)
return app.response_class(generate())
<p>This is all the output:</p>
<iframe src="{{ url_for("stream") }}"></iframe>
5 years late, but this actually can be done the way you were initially trying to do it, javascript is totally unnecessary (Edit: the author of the accepted answer added the iframe section after I wrote this). You just have to include embed the output as an <iframe>:
from flask import Flask, render_template, Response
import time, math
app = Flask(__name__)
#app.route('/content')
def content():
"""
Render the content a url different from index
"""
def inner():
# simulate a long process to watch
for i in range(500):
j = math.sqrt(i)
time.sleep(1)
# this value should be inserted into an HTML template
yield str(i) + '<br/>\n'
return Response(inner(), mimetype='text/html')
#app.route('/')
def index():
"""
Render a template at the index. The content will be embedded in this template
"""
return render_template('index.html.jinja')
app.run(debug=True)
Then the 'index.html.jinja' file will include an <iframe> with the content url as the src, which would something like:
<!doctype html>
<head>
<title>Title</title>
</head>
<body>
<div>
<iframe frameborder="0"
onresize="noresize"
style='background: transparent; width: 100%; height:100%;'
src="{{ url_for('content')}}">
</iframe>
</div>
</body>
When rendering user-provided data render_template_string() should be used to render the content to avoid injection attacks. However, I left this out of the example because it adds additional complexity, is outside the scope of the question, isn't relevant to the OP since he isn't streaming user-provided data, and won't be relevant for the vast majority of people seeing this post since streaming user-provided data is a far edge case that few if any people will ever have to do.
Originally I had a similar problem to the one posted here where a model is being trained and the update should be stationary and formatted in Html. The following answer is for future reference or people trying to solve the same problem and need inspiration.
A good solution to achieve this is to use an EventSource in Javascript, as described here. This listener can be started using a context variable, such as from a form or other source. The listener is stopped by sending a stop command. A sleep command is used for visualization without doing any real work in this example. Lastly, Html formatting can be achieved using Javascript DOM-Manipulation.
Flask Application
import flask
import time
app = flask.Flask(__name__)
#app.route('/learn')
def learn():
def update():
yield 'data: Prepare for learning\n\n'
# Preapre model
time.sleep(1.0)
for i in range(1, 101):
# Perform update
time.sleep(0.1)
yield f'data: {i}%\n\n'
yield 'data: close\n\n'
return flask.Response(update(), mimetype='text/event-stream')
#app.route('/', methods=['GET', 'POST'])
def index():
train_model = False
if flask.request.method == 'POST':
if 'train_model' in list(flask.request.form):
train_model = True
return flask.render_template('index.html', train_model=train_model)
app.run(threaded=True)
HTML Template
<form action="/" method="post">
<input name="train_model" type="submit" value="Train Model" />
</form>
<p id="learn_output"></p>
{% if train_model %}
<script>
var target_output = document.getElementById("learn_output");
var learn_update = new EventSource("/learn");
learn_update.onmessage = function (e) {
if (e.data == "close") {
learn_update.close();
} else {
target_output.innerHTML = "Status: " + e.data;
}
};
</script>
{% endif %}

How to escape HTML characters in Flask-SocketIO?

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:

socket.io upgraded from 0.9.6 to 1.3.5 sends wrong requests

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?

Categories

Resources