Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flask not processing other HTTP requests after Chrome browser accesses the web-site

Problem statement: my web server on Flask is not processing HTTP requests after I try to access non existing file from Chrome browser. When Chrome is shut down or accessing page that does exists, the backlog HTTP requests are processed at once.

Impact: web server availability is poor.

Question: why does it happen and how to fix it without running Flask in threaded mode?

The closest post I found online is this: github.com/pallets/flask/issues/2188 but could not find exactly the same problem and solution. Looking forward to your thoughts - thanks so much for your help!

Primary Hypothesis: Chrome does not read all content of the 404 response, and Flask is waiting all the content to be read

Details:

Steps to reproduce the problem:

1) run minimal Flask application (http://flask.pocoo.org/docs/0.12/quickstart/):

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

app.run()    
  • Running on хттп://127.0.0.1:5000/ (Press CTRL+C to quit)

2) verify you get 'Hello world' response in browser or curl:

curl -v localhost:5000/

3) in Chrome go to localhost:5000/pagethatdoesnotexists observe Not Found error in the browser

4) repeat curl -v localhost:5000/ command

observe that connection is established but response is not received e.g. :

curl -v localhost:5000/ * Trying ::1... * Connection failed * connect to ::1 port 5000 failed: Connection refused * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 5000 (#0)

GET / HTTP/1.1 Host: localhost:5000 User-Agent: curl/7.49.0 Accept: /

5) in Chrome go to the page that exists or shut down Chrome

observe immediate response to curl:

  • HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Content-Type: text/html; charset=utf-8 < Content-Length: 13 < Server: Werkzeug/0.11.10 Python/3.5.1 < Date: Tue, 28 Feb 2017 21:44:20 GMT <
  • Closing connection 0 Hello, World!

it may take more than one attempt to reproduce the problem. Typically happens >8 times out of 10

Other pieces of information:

1) instead of curl I can use Safari or telnet or a python script - same problem

2) Safari does not create the problem, which Chrome does

3) tried to mimic Chrome by sending exactly the same http request as Chrome does - but cannot reproduce the problem. Chrome probably does something else.

4) when I run Flask in threaded mode (handling each request with a different thread), the problem goes away.

5) versions:

Chrome Version 56.0.2924.87 (64-bit)

Python 3.5.2 |Anaconda 4.1.1 (64-bit)| (default, Jul 2 2016, 17:53:06) [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux

flask.version '0.11.1'

6) issue is also reproduced on AWS Ubuntu production server machine

7) tried sending custom headers in 404 http response with no luck

@app.errorhandler(404)
def page_not_found(e):
    # return render_template('404.html'), 404
    resp = make_response(render_template('404.html'), 404)
    # resp.headers['Connection'] = 'close'
    resp.headers['Cache-Control'] = 'no-cache, no-store'
    return resp

UPDATE

I was able to reproduce the problem without 404 error, just with normal http requests from Chrome. There are no errors in Flask log observed.

Here is the video with the problem demonstration

Another interesting thing - if using incognito window in Chrome browser, the problem is not observed. Yet, clearing cache in normal mode Chrome does not solve the problem.

like image 923
DrSasha Avatar asked Feb 28 '17 22:02

DrSasha


People also ask

Can flask handle HTTP requests?

Flask has different decorators to handle http requests. Http protocol is the basis for data communication in the World Wide Web. Used to send HTML form data to the server. The data received by the POST method is not cached by the server.

What is debug mode in flask?

Flask Debug mode allows developers to locate any possible error and as well the location of the error, by logging a traceback of the error. The Flask debug mode also enables developers to interactively run arbitrary python codes, so that one can resolve and get to the root cause on why an error happened.

How do I access requests in flask?

To access the incoming data in Flask, you have to use the request object. The request object holds all incoming data from the request, which includes the mimetype, referrer, IP address, raw data, HTTP method, and headers, among other things.

What is the default IP address for flask?

--host – the IP address of the web server to run your Flask application on. The default value is '127.0. 0.1'.


2 Answers

Enable threading.

app.run(host='0.0.0.0', port=80, debug=True, threaded=True)

TL;DR

The problem is still valid. It seems that Chrome does not close the connection when page prefetch is enabled, and it blocks the execution of the server, hence the processing of subsequent requests.

In my case, the problem even worst since Android-based phones also use this prefetch feature with the same results, and I can not change the settings every client.

My solution/workaround is to enable the threading option in the underlying werkzeug server (https://werkzeug.palletsprojects.com/en/0.16.x/serving/#werkzeug.serving.run_simple). Of course, it is more resources heavy on the server-side, but it allows us to separate the ill behaving requests/clients in a separate thread without blocking other requests.

if __name__ == '__main__':
    logger.info('starting web server life cycle')
    app.run(host='0.0.0.0', port=80, debug=True, threaded=True)

I also checked that the request processing is finished correctly, and it does, at least in the Flask side. So the problem must be either in Chrome / other clients or in the underlying werkzeug server.

@app.before_request
def filter_prefetch():
    logger.debug("before request")
    logger.debug(request.headers)
# uncomment these to filter Chrome specific prefetch requests.
#    if 'Purpose' in request.headers and request.headers.get('Purpose') == 'prefetch':
#        logger.debug("prefetch requests are not allowed")
#        return '', status.HTTP_403_FORBIDDEN


@app.after_request
def debug_after(response):
    logger.debug("after request")
    response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
    response.headers["Pragma"] = "no-cache"
    response.headers["Expires"] = "0"
    response.headers['Cache-Control'] = 'public, max-age=0'
    response.headers['Connection'] = 'close'
    return response

like image 149
geryxyz Avatar answered Oct 17 '22 21:10

geryxyz


I've ran into the same problem twice.

The same environment: pure Flask (no reverse proxy), the simplest application.

After you've open URL with Chrome/Chromium -- Flask will hang and won't respond to other clients (curl, postman, firefox, python-request, ..).

Workaround for Chrome

Disable URL-prediction services in Chrome/Chromium (Actual names of options are on the screenshot)

chromium-settings

Real solution (for Flask)

Comming soon — contributions are welcome! (most probably I will never resolve this).

like image 20
maxkoryukov Avatar answered Oct 17 '22 21:10

maxkoryukov