<div dir="ltr"><div>Hi,</div><div>I'm one of the developers of the OpenConnect multi-protocol VPN client. We've been using Flask to emulate the HTTP-based authentication front-ends of VPN servers [1], and it's been working really well for this purpose.<br></div><div><br></div><div>I recently discovered that the Flask development server appears to be unable to correctly handle HTTP/1.1 connection reuse [2]. When a connection to the development server is reused for multiple requests, the *body* of one request will be misinterpreted as a new HTTP/1.1 request line.</div><div><br></div><div>Here's a complete example,  tested against Python 3.6.9, Werkzeug (2.0.1), and Flask (2.0.1):</div><div><br></div><div>    from flask import Flask, abort<br>    from werkzeug.serving import WSGIRequestHandler<br>    WSGIRequestHandler.protocol_version = "HTTP/1.1"<br><br>    # Create Flask app<br>    app = Flask(__name__)<br>    app.config.update(DEBUG=True, PORT=int(port))<br><br>    # Add a route that accepts POST<br>    @app.route('/', methods=('POST',))<br>    def post_slash():</div><div>        assert 'foo' in request.form<br></div><div>        return '', 200<br><br>    # Run it<br>    app.run(host='localhost', port='8080', debug=True)</div><div><br></div><div>Demonstration of incorrect parsing of multiple requests by this server, with curl connection reuse:<br>     curl -v -X POST <a href="http://localhost:8080">http://localhost:8080</a>  -d foo=bar -: -v -X POST <a href="http://localhost:8080">http://localhost:8080</a> -d foo=baz</div><div><br></div><div>Server log shows:<br></div><div>127.0.0.1 - - [24/May/2021 12:28:22] "POST / HTTP/1.1" 200 -<br>127.0.0.1 - - [24/May/2021 12:28:22] "foo=barPOST / HTTP/1.1" 405 -<br>127.0.0.1 - - [24/May/2021 12:28:22] code 400, message Bad request syntax ('foo=baz')<br>127.0.0.1 - - [24/May/2021 12:28:22] "None / HTTP/0.9" HTTPStatus.BAD_REQUEST -</div><div><br></div><div>As mentioned above, the server is evidently misinterpreting the body of the first request (b"foo=bar" on the wire) as the beginning of the second request. That's *despite* the fact that the server has clearly *read* the body and parsed it into the request.form object (otherwise the assertion would have failed). It appears that the server is not correctly advancing its position in the connection stream after reading the body, and thus restarting at the wrong position when awaiting subsequent requests. I also tested with HTTPS (by setting a valid ssl_context in app.run) and got the same results.<br></div><div><br></div><div>If the connection is not reused by the client (e.g. two separate curl commands) then the server handles the second request correctly. It also succeeds if protocol_version = "HTTP/1.1" is removed from the server configuration, thus forcing the client *not* to try to reuse connections, because cause the server identifies itself as HTTP/1.0, which doesn't allow connection reuse.<br></div><div><br></div><div>Is this a known issue in Flask or Werkzeug? For the time being, it appears the only workaround is not to reuse HTTP/1.1 connections at all… any easy fix/patch?<br></div><div><br></div><div>Thanks,</div><div>Dan Lenski<br></div><div><br></div><div>[1] See fake-*-server.py in <a href="https://gitlab.com/openconnect/openconnect/tree/master/tests">https://gitlab.com/openconnect/openconnect/tree/master/tests</a>)</div><div>[2] <a href="https://en.wikipedia.org/wiki/HTTP_persistent_connection#HTTP_1.1">https://en.wikipedia.org/wiki/HTTP_persistent_connection#HTTP_1.1</a></div></div>