I've written the following flask server:
from flask import Flask, render_template, request
import os
app = Flask(__name__)
# home
#app.route('/')
def home():
return 'HOME PAGE'
#app.route('/add')
def add():
global a
a += 1
return str(a)
if __name__ == '__main__':
a = 0
HOST = '10.10.10.10'
PORT = 5555
app.run(HOST, PORT)
Considering there are two users (from different IP addresses) of my server: A and B. When user A requests by url 10.10.10.10:5555/add, he gets the result 1. After that, if user B requests by url 10.10.10.10:5555/add he will get 2. Because two users share the same variable a
However, I want my server to handle A and B separately which means a user A and B have a variable a in their own way. The requests of A shouldn't affect the result that B will get. For example, When user A requests, he gets 1. After that user B requests and he should get 1 as well.
How should I modify my code to achieve this?
Based on your question, I think you're confused about the definition of "global".
In Flask, you have a Flask server with multiple threads and potentially multiple processes handling requests. you had a global variable a, and you wanted to keep adding to it in every request and want a variable to be independent.This is totally possible in theory and practice. It's also a really bad idea. This case actually create Deadlocks
The problem is that you can't easily control which threads and processes "win"
You should keep the webserver itself as stateless as possible. Each request should be totally independent and not share any state in the server. Instead, use a database or caching layer which will handle the state for you. This seems more complicated but is actually simpler in practice. Check out SQLite for example ; it's pretty simple.
Thanks to #n00dl3 's suggestion, I've managed to achieve the goal of my example. Here is the code:
from flask import Flask, render_template, request, session
import os
from datetime import timedelta
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days = 7)
# login
#app.route('/<username>', methods=['GET', 'POST'])
def home(username):
if username in session:
print(session.keys())
return 'hello {}'.format(username)
else:
session[username] = username
# generate this user's variable
a[username] = 0
print(session.keys())
return 'login as {}'.format(username)
# logout
#app.route('/logout/<username>', methods=['GET', 'POST'])
def logout(username):
session.pop(username)
print(session.keys())
return '{} logout!'.format(username)
# call add function with specific username
#app.route('/add/<username>')
def add(username):
global a
a[username] += 1
return str(a[username])
if __name__ == '__main__':
a = {}
#HOST = environ.get('SERVER_HOST', 'localhost')
HOST = '10.10.50.23'
try:
PORT = int(os.environ.get('SERVER_PORT', '5555'))
except ValueError:
PORT = 5555
app.run(HOST, PORT, debug=True)
However, I'm not sure if my way is a decent solution. So still listen to any better answers.
use different WSGI server to deploy your project. see this link http://flask.pocoo.org/docs/1.0/deploying/
Related
how can I dynamically pass all of the GET parameters from one url to another via flask?
This is currently what I am doing:
import os
from flask import Flask,redirect
from flask import request
from flask import url_for
app = Flask(__name__)
#app.route('/')
def hello():
return redirect(url_for("https://myurl.com", **request.args))
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
I can't really do it statically. What I am trying to accomplish:
myflaskserver:5000/page?url=google.com&header=body&identity=flash -> https://myurl.com/page?url=google.com&header=body&identity=flash
myflaskserver:5000/dance?url=dance.com&function=dancer&move=quality-> https://myurl.com/dance?url=dance.com&function=dancer&move=quality-
myflaskserver:5000/quit?host=google.com&language=english&password=test1234-> https://myurl.com/quit?host=google.com&language=english&password=test1234
With minimal code, without procedurally having to use if statements, or doing it statically with GET parameters for each page.
Thank you.
Because of the way that the question is phrased, it seems that you are looking for a way to perpetuate the url parameters only. In other words, you're not asking how to perpetuate the url page path (or "routes" in flask terminology), perhaps because you already have a strategy for that in mind.
If that assumption is incorrect, see my note near the bottom of this answer.
It also seems that you prefer passing the parameters as url parameters (versus passing the same data in the request header or payload).
If both these assumptions are correct, then the following approach may work for you:
Use the query_string method of request. This method returns all the url parameters as a bytes object (meaning you probably will need to decode it to a string if you wish to join it to your new url string).
import os
from flask import Flask,redirect
from flask import request
app = Flask(__name__)
#app.route('/')
def hello():
request_parameters = request.query_string.decode('utf-8')
return redirect("https://myurl.com?{0}".format(request_parameters))
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
Given the first url in your example...
myflaskserver:5000/page?url=google.com&header=body&identity=flash
...the statement request.query_string.decode('utf-8') would return the string "url=google.com&header=body&identity=flash", which is then appended to your endpoint "https://myurl.com?".
Notice ? needs to be added to the endpoint before joining with request.query_string.
Passing page paths (i.e. routes)
This was not explicitly asked in the question, so I won't go into much detail. But if you need to parse the route from the request, you could use request.url_rule, which will return everything after the domain and before the url parameters.
import os
from flask import Flask,redirect
from flask import request
app = Flask(__name__)
#app.route('/hello')
def hello():
request_parameters = request.query_string.decode('utf-8')
route = request.url_rule
return redirect("https://myurl.com{0}?{1}".format(route,request_parameters))
if __name__ == '__main__':
# Bind to PORT if defined, otherwise default to 5000.
port = int(os.environ.get('PORT', 5000))
app.run(host='0.0.0.0', port=port)
Or you could look into flask.referrer, which will return the referring url from the request header if it is available, which it sometimes isn't, such as in cross-origin scenarios of local testing.
Sidebar
For the information of anyone passing by this post, it may be helpful to mention why the request as written in the question will fail:
When the endpoint "https://myurl.com" of url_for is called, it will fail because the method expects an endpoint (or route, in flask terminology) defined by your app (versus an external endpoint with schema and domain) when called from an active request like this.
I am creating a web application that when someone access it like that:
http://x.x.x.x:1337/bazinga
It will create a container with host's port 8083 maps to the container's port 80. Then I want to redirect the user to the web application (nginx) inside the container.
To do that I need to redirect the user to http://localhost:8083, which will redirect to the container nginx. The problem is that I don't want the user to see the port 8083. Because in the near future this web application should will 500 people and for each one I will create a container with different port, not only 8083 and I don't want them to be able to access someone else container by just trying different ports.
Therefore, I wanted to redirect the user to http://localhost:8083 but without showing it. The user should see some random URL, i.e: http://x.x.x.x/CS7YJVS7E8KTBD0AMDO3.
How can I do that?
This is my code:
from flask import Flask, session, redirect
import docker
import random
import string
app = Flask(__name__)
#app.route("/")
def index():
return "Congratulations, it's a web app!"
#app.route("/<random_id>")
def random_url(random_id):
if random_id not in global_routes:
return "URL doesn't exist"
return "My user!"
#app.route("/bazinga")
def bazinga():
randomString = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(20))
session["user"] = randomString
container = client.containers.run("nginx", remove=True, detach=True, ports={'80/tcp':'8083/tcp'})
global_routes[randomString] = True
return redirect("http://localhost:8083")
global_routes = {}
client = docker.from_env()
if __name__ == "__main__":
app.run(host="127.0.0.1", port=1337, debug=True)
I succeeded to do it.
I created a dynamic route like that:
#app.route("/<random_id>")
def random_url(random_id):
if random_id not in global_containers:
return "URL doesn't exist"
r = requests.get("http://localhost:" + str(global_containers[random_id].host_port))
return r.content
I am saving the containers and redirect to the container's id.
Inside the route function, I can use the ID to find the container and send get request to the container return it back.
I have tried a lot of things but nothing is working. It always gives me the "Incomplete response received from application" message in the navigator.
My code is:
import sys
from flask import Flask, request
app = Flask(__name__)
#app.route('/')
def application():
uri = request.args.get('url')
message = 'It works!\n'
response = '\n'.join([message, uri])
return response
The problem is or in the #app.route('/') line or in the uri = request.args.get('url').
I just want to call the with the navigator like http://example.com/script/?url=hello.
I tried changing #app.route('/') to #app.route('/script') and #app.route('/script/') but nothing is working... any ideas?
Thanks a lot!
For future readers: note that the original question has been edited in response to this suggestion.
First issue: You seem to be using some very low-level WSGI implementation when Flask does a lot of the sugar for you. Consider testing with a function that lets Flask do the work and then expand as needed.
import sys
from flask import Flask, request
app = Flask(__name__)
#app.route('/')
def test():
uri = request.args.get('url')
message = 'It works!\n'
version = 'Python %s\n' % sys.version.split()[0]
response = '\n'.join([message, version, uri])
return response
Next, keep in mind that Flask wants a string return type. If you want to pass a data structure back, consider jsonify.
I've got a Flask app in which I hope to accomplish the following things:
Have an endpoint that will run a series of queries
This endpoint needs to respond to the HTTP request within a limited number of seconds.
The queries can take up to several minutes to finish so I need them to run in a separate thread, with multiple clients polling the server every so often to see if they have fresh data to be returned to them
Hopefully hosted on Kubernetes with multiple instances of the pod running.
My below implementation has several issues:
The poll endpoint seems unnecesarily large, most of this is just dealing with the Queue of queries and making sure that each client gets their own results back, and not someone elses.
Not sure what is going on, but when I try to host more than one instance of this pod on Kubernetes, its like some poll requests from some users are being sent to instances in which their uuid does not exist.
I'm hoping for some understanding of what I'm doing wrong with threading and Queues because this seems like a hacky way of doing this. And also, how can I make the results of these queries available to all instances of Kubernetes running?
Thanks!
from flask import Flask, render_template, request, jsonify, g
from Queue import Queue
from threading import Thread
from time import sleep
app = Flask(__name__, template_folder='Templates')
#app.route('/')
def index():
return render_template('index.html')
#app.before_first_request
def before_first_request():
g.output = Queue()
g.data_results = {}
return ""
#app.route('/data')
def data():
"""
Endpoint hit to fire of a request for data from a given user (uuid)
"""
params = request.args.to_dict()
uuid = params['uuid']
# Create a list for this user, to store their results
g.data_results[uuid] = []
list_of_queries = ["SELECT * FROM tbl1;",
"SELECT * FROM tbl2;",
"SELECT * FROM tbl3;"]
for query in list_of_queries:
t = Thread(target=worker, args=(query, uuid, g.output))
t.daemon = True
t.start()
return jsonify({'msg':'Queries started'})
def worker(*args):
query, uuid, output = args
# Will actually be something like `result = run_query(query)`
result = {'uuid':uuid}
sleep(10)
output.put(result)
#app.route('/poll')
def poll():
"""
Endpoint hit ever x seconds from frontend
to see if the data is ready
"""
params = request.args.to_dict()
uuid_from_client = params['uuid']
# If client polls for result, but server has no record of this uuid
# This can happen in kubernetes with multiple instances running
if g.data_results.get(uuid_from_client) is None:
return jsonify({'msg':'pong', 'data':None, 'freshdata':None})
try:
output = g.output
# This line throws an error if there is nothing to get
results = output.get(False)
output.task_done()
# What is the uuid associated with the most recently returned data
# More than 1 chunk of data can be in here
uuid_from_data = results['uuid']
g.data_results[uuid_from_data].append(results)
except:
uuid_from_data = None
results = None
results_for_client_uuid = g.data_results[uuid_from_client]
if len(results_for_client_uuid) > 0:
res = results_for_client_uuid.pop(0)
else:
res = None
return jsonify({'msg':'pong', 'data':res})
if __name__ == "__main__":
with app.app_context():
app.run(host='0.0.0.0')
Setup your app architecture to use queuing softwares so that there is separation of concerns in terms of what job it does.
Here is a great article that can help you give some insight http://blog.gorgias.io/deploying-flask-celery-with-docker-and-kubernetes/
and one more https://endocode.com/blog/2015/03/24/using-googles-kubernetes-to-build-a-distributed-task-management-cluster/
I want to create a webapp that dynamically redirects to a URL, based on address that user typed. When a user visit my site by a address like this:
http://mydomain1.com/a1b2c3d4
I want redirect this user to URL:
http://mydomain2.com/register.php?id=a1b2c3d4&from=mydomain1.com
Yay, I love a good fight!
from pyramid.config import Configurator
from pyramid.httpexceptions import HTTPFound
from paste.httpserver import serve
config = Configurator()
config.add_route('redirect', '/{arg}')
def redirect_view(request):
dst = 'http://mydomain2.com/register.php?id={id}&from={host}'
args = {
'id': request.matchdict['arg'],
'host': request.host,
}
return HTTPFound(dst.format(**args))
config.add_view(redirect_view, route_name='redirect')
serve(config.make_wsgi_app(), host='0.0.0.0', port=80)
Here goes my attempt, I'm almost newbie in flask, so it should have room to improve
from flask import Flask, redirect, request
app = Flask(__name__)
host = 'domain2.org'
#app.route('/<path>')
def redirection(path):
return redirect('http://'+host+'/register.php?id='+path+'&from='+request.host)
if __name__ == '__main__':
app.run()
Edited to add the host to the from parameter
My solution was to use a Werkzeug rule using the path type :
host = 'domain2.org'
#app.route('/<path:path>')
def redirection(path):
return redirect('http://%s/%s' % (host, path), code=301)
This can be useful if you move a site and want another site instead with redirection on others pages.
There's a pyramid_rewrite extension (https://pypi.python.org/pypi/pyramid_rewrite/) that looks unmaintained, but seems to work. I had a use case it didn't handle, though: using Configure.include() with the route_prefix parameter.
It occurred to me that the usual approach is to do URL rewrites in the server, and I was using a WSGI server from the Python standard library. How hard could it be?
Make a custom request handler class:
from wsgiref.simple_server import make_server, WSGIRequestHandler
class MyReqHandler(WSGIRequestHandler):
def get_environ(self):
env = WSGIRequestHandler.get_environ(self)
if env['PATH_INFO'].startswith('/foo'):
env['PATH_INFO'] = env['PATH_INFO'].replace('foo', 'bar', 1)
return env
Pass it to make_server() when creating your server:
srvr = make_server('0.0.0.0', 6543, app, handler_class=MyReqHandler)
It works!
Straight-up substitution is all I needed for the problem at hand. Extending it to use regular expressions and exposing it via a nice API would be pretty straightforward.
I have another solution, that is straight-up pyramid, so it will work with some other wsgi server:
from pyramid.events import NewRequest, subscriber
#subscriber(NewRequest)
def mysubscriber(event):
req = event.request
if req.path_info.startswith('/~cfuller'):
req.path_info = req.path_info.replace('foo', 'bar', 1)
That's the declarative way, and it requires a config.scan(). Imperitively, you'd do something like
config.add_subscriber(mysubscriber, NewRequest)
See http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/events.html for the skinny on events.