Flask hangs when sending a post request to itself - python

I'm trying to send a post request to my Flask app from one of its own views, but it hangs until I kill the server. If I do the request in JavaScript, it works fine. Why does it not work from the Python code?
from flask import Blueprint, render_template, abort, request, Response, session, url_for
from jinja2 import TemplateNotFound
from flask.ext.wtf import Form
from wtforms import BooleanField, TextField, PasswordField
import requests
login = Blueprint('login', __name__, template_folder='templates')
class LoginForm(Form):
email = TextField('Email')
password = PasswordField('Password')
#login.route('/login', methods=['GET', 'POST'])
def _login():
form = LoginForm(request.form, csrf_enabled=False)
if form.validate_on_submit():
return requests.post(request.url_root + '/api/login', data={"test": True})
return render_template('login.html', form=form)

Prior to 1.0, Flask's development server was single-threaded by default. In that mode, it can only handle one request at a time. Making a request blocks until it receives the response. Your Flask code makes a request in the one thread, and then waits. There are no other threads to handle this second request. So the request never completes, and the original request waits forever.
Enable threads in the dev server to avoid the deadlock and fix the immediate problem.
app.run(threaded=True)
However, making a full HTTP request to the app from within the app should never be necessary and indicates a deeper design issue. For example, observe that the internal request will not have access to the session on the client's browser. Extract the common code and call it internally, rather than making a new request.
def common_login(data):
...
#app.route("/login")
def login():
...
common_login(data)
...
#app.route("/api/login")
def api_login():
...
common_login(data)
...

I'm not familiar with Flask. However this bit of code:
if form.validate_on_submit():
return requests.post(request.url_root + '/api/login', data={"test": True})
Seems like you're accepting a posted form, validating it, and then posting it again. Over and over.

Related

Previously-set request cookies returns None in a Flask application

I'm trying to store JWT Tokens in cookies for a Flask application to restrict some endpoints. An endpoint, "/authorize" , is responsible for setting the cookies then redirect the page to the root endpoint, "/".
from flask import Flask, request, make_response, redirect
#app.route("/authorize", methods=["GET"])
def authorize():
token = request.args.get('token')
expires = request.args.get('expires')
# some code to validate the token
resp_output = make_response(redirect("/"))
resp_output.set_cookie("token", token, expires=expires)
return resp_output
#app.route("/", methods=["GET"])
def index():
token = request.cookies.get("token)
# do something with the token
However, when I tried to deploy this, I ran into some problems with the redirecting and therefore have to change redirect("/") to redirect("https://someaddress.com/)" where https://someaddress.com/ is the address of the flask application. Now when I try to retrieve the token cookies in the root endpoint, it returns None. I suspect it is because the redirection has turnt from an internal one to an external one.
Please help me find a workaround for this. Or if you think I should resolve the problems that lead to the change from internal to external redirection so I can go back to what works. (If anyone can point me to some resources explaining exactly how redirection, or more specifically Flask's redirection, works, I'd really appreciate it.)
Using url_for function from flask should work in your case, as it will look for the link within the app context:
from flask import Flask, request, make_response, redirect, url_for
#app.route("/authorize", methods=["GET"])
def authorize():
token = request.args.get('token')
expires = request.args.get('expires')
# some code to validate the token
resp_output = make_response(redirect(url_for('index')))
resp_output.set_cookie("token", token, expires=expires)
return resp_output
#app.route("/", methods=["GET"])
def index():
token = request.cookies.get("token)
# do something with the token
Btw, I would recommend you pass your authorization logic to a decorator, have a look on authorization decorators using flask.
In case this don't work in production, that can be some setting related to your reverse proxy - like nginx conf file. Let me know if it is the case
on Nginx file on sites-enabled folder etc/nginx/sites-enabled/<project-name>, comment or remove the following line:
proxy_set_header Host $host;
Hope it suits you well!

How to import a file in Flask uWSGI Nginx?

I checked every SO question about it, but the answers are mainly on import errors while I do not have such a problem. Mainly I followed this article followed by this one to have a functioning registration.
Instead of using Flask-SQLalchemy I wanted to create my own database (for fun), but when I try to access the database (DButils.py) functions it occurs an internal server error.
The flask code at the top is:
from flask import Flask, render_template, flash, redirect, url_for, session,
from wtforms import Form, StringField, TextAreaField, PasswordField, validators
from functools import wraps
from DButils import *
My folder follows the same order of the git, with DButils.py in the same folder as app.py.
I did not encounter the error when I import the module, but only when I try to call its functions. In DButils.py I have only a signup function:
def signup(nick, email, password):
return True
And when I try to call it in the app.py code like:
#app.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm(request.form)
if request.method == 'POST' and form.validate():
email = form.email.data
nick = form.nick.data
password = form.password.data
signup(nick,email,password) #WHEN COMMENTED NO ERROR OCCURS
return redirect(url_for('login'))
return render_template('register.html', form=form)
I get the message "Internal Server Error" with no other clue about it. What can it be? How can I call a function in an external module in Flask?
Thanks for your help!
I found the answer by an trial-error approach. Apparently using pkill --signal SIGHUP uwsgi in combination with sudo systemctl restart nginx.

Flask forgetting Routes on POST

I am trying to run a very simple flask-application on a (shared) WSGI server. The code works fine when I run it with the build-in server, but if I try to POST to the URL on the production WSGI server, I receive a 404, The requested URL was not found on the server error.
This only occurs for POST requests, GET and PUT are processed as expected.
By removing the placeholder tid, flask can be convinced to properly process the request, but this is obviously not a proper solution.
The server is running Phusion Passenger, the flask version is 1.0.2.
As it is a shared server, I have no further access to the server configuration.
What can cause flask to seemingly forget routes on a WSGI-server?
A minimal example that reproduces to behaviour (on the server only, of course) can be seen below:
from flask import Flask
from flask.views import MethodView
app = Flask(__name__)
class API(MethodView):
def get(self, tid=0):
return "Test"
def put(self, tid=0):
return "Test"
def post(self, tid=0):
return "Test"
app.add_url_rule("/test/<int:tid>", view_func=API.as_view('api'))
You have to specify the methods you use in add_url_rule:
app.add_url_rule("/test/<int:tid>", view_func=API.as_view('api'), methods=['GET', 'PUT', 'POST'])

Flask return JWT token when redirecting

I have been following this Flask pyjwt guide, however my web app is somewhat similar to Miguel's microblog example that uses render_template() and redirect(url_for(...)) for navigation.
I have implemented an encoding and decoding service in my application, however I do not know how to properly return the encoded JWT token to the user using redirect()
My code is as follows:
#app.route('/', methods=['GET', 'POST'])
def login():
login_form = LoginForm()
username = login_form.username.data
password = login_form.password.data
if is_user_valid(username, password):
return redirect(url_for('home'), auth_service.encode_token(username))
render_template('login.html', form=login_form)
My problem is placing the auth token inside the redirect method causes a page to appear saying "Redirecting... you should be redirected, if not click here", which I do not want. I do not particularly wish to change my redirect methods to something similar to make_response(jsonify(...)) as I would then need to handle these responses in the front end when this is a simple login page.
How should I be returning the auth token to the client correctly?
Typically you attach it to response headers which is what your redirect method returns.
response = redirect(url_for('home'))
response.headers['X-JWT-TOKEN'] = auth_service.encode_token(username)
return response
However, I'm not sure if this is gonna be very useful in your setup when using render_template, because you can not easily access these headers on the frontend.
You have two alternatives here:
Make your login endpoint an API. This way you can return the token for the client-side code to handle and store it somehow.
Drop the JWT and stick to using sessions. I know that this is not what you expected, but outside of building APIs JWT-based auth is not very helpful.

API and APP Flask [duplicate]

I'm trying to send a post request to my Flask app from one of its own views, but it hangs until I kill the server. If I do the request in JavaScript, it works fine. Why does it not work from the Python code?
from flask import Blueprint, render_template, abort, request, Response, session, url_for
from jinja2 import TemplateNotFound
from flask.ext.wtf import Form
from wtforms import BooleanField, TextField, PasswordField
import requests
login = Blueprint('login', __name__, template_folder='templates')
class LoginForm(Form):
email = TextField('Email')
password = PasswordField('Password')
#login.route('/login', methods=['GET', 'POST'])
def _login():
form = LoginForm(request.form, csrf_enabled=False)
if form.validate_on_submit():
return requests.post(request.url_root + '/api/login', data={"test": True})
return render_template('login.html', form=form)
Prior to 1.0, Flask's development server was single-threaded by default. In that mode, it can only handle one request at a time. Making a request blocks until it receives the response. Your Flask code makes a request in the one thread, and then waits. There are no other threads to handle this second request. So the request never completes, and the original request waits forever.
Enable threads in the dev server to avoid the deadlock and fix the immediate problem.
app.run(threaded=True)
However, making a full HTTP request to the app from within the app should never be necessary and indicates a deeper design issue. For example, observe that the internal request will not have access to the session on the client's browser. Extract the common code and call it internally, rather than making a new request.
def common_login(data):
...
#app.route("/login")
def login():
...
common_login(data)
...
#app.route("/api/login")
def api_login():
...
common_login(data)
...
I'm not familiar with Flask. However this bit of code:
if form.validate_on_submit():
return requests.post(request.url_root + '/api/login', data={"test": True})
Seems like you're accepting a posted form, validating it, and then posting it again. Over and over.

Categories

Resources