<p dir="auto">Flask will send 400 "Bad Request Version" if Flask send 401 or 403 response to Client without closing the http connection.</p>
<ol dir="auto">
<li>Client POST text/xml message without "Basic Authorization"</li>
<li>Server response 401, but don't send "HTTP: close"</li>
<li>Client POST text/xml message again with "Basic Authorization"</li>
<li>Server response 400 "Bad Request Version"</li>
</ol>
<p dir="auto">I wish Flask can handle the second POST correctly, And I have tested using another server, it works well.<br>
Please the capture for details. And the partial code is as following(full code link:<a href="https://github.com/ballzb/pyacs">https://github.com/ballzb/pyacs</a> )</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="@basic_auth.verify_password
def verify_password(username, password):
    #app.logger.error(f&quot;username={username}, password={password}&quot;)
    if username==config['local']['username'] and password==config['local']['password']:
        return username
    else:
        g.current_user = username
        return False #username or password error

@basic_auth.error_handler
def auth_error(status_code):
    #app.logger.error(f&quot;status_code={status_code}, username={g.current_user}&quot;)
    if status_code == 401:
        if g.current_user:
            return cwmp.make_403_response()
        else:
            return cwmp.make_401_response(config['local']['authentication'])
    # return &quot;Access Denied&quot;, status_code


@app.route('/acs', methods=['GET', 'POST'])
@multi_auth.login_required
def acs():
    &quot;&quot;&quot; main tr069/acs entry point &quot;&quot;&quot;
    if request.method == 'GET':
        return DESCRIPTION

    if request.method != 'POST':
        return 'There is nothing to show'

    #app.logger.error(request.headers)


    # POST requests
    if request.content_type.find('text/xml')==-1:
        app.logger.error(f&quot;request.content_type={request.content_type}&quot;)
        return 'Wrong content type'

    result = cwmp.handle_request(request)
    if result:
        return result
    else:
        return DESCRIPTION"><pre class="notranslate"><span class="pl-en">@<span class="pl-s1">basic_auth</span>.<span class="pl-s1">verify_password</span></span>
<span class="pl-k">def</span> <span class="pl-en">verify_password</span>(<span class="pl-s1">username</span>, <span class="pl-s1">password</span>):
    <span class="pl-c">#app.logger.error(f"username={username}, password={password}")</span>
    <span class="pl-k">if</span> <span class="pl-s1">username</span><span class="pl-c1">==</span><span class="pl-s1">config</span>[<span class="pl-s">'local'</span>][<span class="pl-s">'username'</span>] <span class="pl-c1">and</span> <span class="pl-s1">password</span><span class="pl-c1">==</span><span class="pl-s1">config</span>[<span class="pl-s">'local'</span>][<span class="pl-s">'password'</span>]:
        <span class="pl-k">return</span> <span class="pl-s1">username</span>
    <span class="pl-k">else</span>:
        <span class="pl-s1">g</span>.<span class="pl-s1">current_user</span> <span class="pl-c1">=</span> <span class="pl-s1">username</span>
        <span class="pl-k">return</span> <span class="pl-c1">False</span> <span class="pl-c">#username or password error</span>

<span class="pl-en">@<span class="pl-s1">basic_auth</span>.<span class="pl-s1">error_handler</span></span>
<span class="pl-k">def</span> <span class="pl-en">auth_error</span>(<span class="pl-s1">status_code</span>):
    <span class="pl-c">#app.logger.error(f"status_code={status_code}, username={g.current_user}")</span>
    <span class="pl-k">if</span> <span class="pl-s1">status_code</span> <span class="pl-c1">==</span> <span class="pl-c1">401</span>:
        <span class="pl-k">if</span> <span class="pl-s1">g</span>.<span class="pl-s1">current_user</span>:
            <span class="pl-k">return</span> <span class="pl-s1">cwmp</span>.<span class="pl-en">make_403_response</span>()
        <span class="pl-k">else</span>:
            <span class="pl-k">return</span> <span class="pl-s1">cwmp</span>.<span class="pl-en">make_401_response</span>(<span class="pl-s1">config</span>[<span class="pl-s">'local'</span>][<span class="pl-s">'authentication'</span>])
    <span class="pl-c"># return "Access Denied", status_code</span>


<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">route</span>(<span class="pl-s">'/acs'</span>, <span class="pl-s1">methods</span><span class="pl-c1">=</span>[<span class="pl-s">'GET'</span>, <span class="pl-s">'POST'</span>])</span>
<span class="pl-en">@<span class="pl-s1">multi_auth</span>.<span class="pl-s1">login_required</span></span>
<span class="pl-k">def</span> <span class="pl-en">acs</span>():
    <span class="pl-s">""" main tr069/acs entry point """</span>
    <span class="pl-k">if</span> <span class="pl-s1">request</span>.<span class="pl-s1">method</span> <span class="pl-c1">==</span> <span class="pl-s">'GET'</span>:
        <span class="pl-k">return</span> <span class="pl-v">DESCRIPTION</span>

    <span class="pl-k">if</span> <span class="pl-s1">request</span>.<span class="pl-s1">method</span> <span class="pl-c1">!=</span> <span class="pl-s">'POST'</span>:
        <span class="pl-k">return</span> <span class="pl-s">'There is nothing to show'</span>

    <span class="pl-c">#app.logger.error(request.headers)</span>


    <span class="pl-c"># POST requests</span>
    <span class="pl-k">if</span> <span class="pl-s1">request</span>.<span class="pl-s1">content_type</span>.<span class="pl-en">find</span>(<span class="pl-s">'text/xml'</span>)<span class="pl-c1">==</span><span class="pl-c1">-</span><span class="pl-c1">1</span>:
        <span class="pl-s1">app</span>.<span class="pl-s1">logger</span>.<span class="pl-en">error</span>(<span class="pl-s">f"request.content_type=<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">request</span>.<span class="pl-s1">content_type</span><span class="pl-kos">}</span></span>"</span>)
        <span class="pl-k">return</span> <span class="pl-s">'Wrong content type'</span>

    <span class="pl-s1">result</span> <span class="pl-c1">=</span> <span class="pl-s1">cwmp</span>.<span class="pl-en">handle_request</span>(<span class="pl-s1">request</span>)
    <span class="pl-k">if</span> <span class="pl-s1">result</span>:
        <span class="pl-k">return</span> <span class="pl-s1">result</span>
    <span class="pl-k">else</span>:
        <span class="pl-k">return</span> <span class="pl-v">DESCRIPTION</span></pre></div>
<p dir="auto">Environment:</p>
<ul dir="auto">
<li>Python version: 3.10</li>
<li>Flask version: 2.1.1<br>
<a href="https://github.com/pallets/flask/files/8499045/400.zip">400.zip</a></li>
</ul>
<p dir="auto">With the following example:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="from flask import Flask

app = Flask(__name__)


@app.route('/', methods=['POST'])
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run()"><pre class="notranslate"><span class="pl-k">from</span> <span class="pl-s1">flask</span> <span class="pl-k">import</span> <span class="pl-v">Flask</span>

<span class="pl-s1">app</span> <span class="pl-c1">=</span> <span class="pl-v">Flask</span>(<span class="pl-s1">__name__</span>)


<span class="pl-en">@<span class="pl-s1">app</span>.<span class="pl-en">route</span>(<span class="pl-s">'/'</span>, <span class="pl-s1">methods</span><span class="pl-c1">=</span>[<span class="pl-s">'POST'</span>])</span>
<span class="pl-k">def</span> <span class="pl-en">hello_world</span>():
    <span class="pl-k">return</span> <span class="pl-s">'Hello World!'</span>


<span class="pl-k">if</span> <span class="pl-s1">__name__</span> <span class="pl-c1">==</span> <span class="pl-s">'__main__'</span>:
    <span class="pl-s1">app</span>.<span class="pl-en">run</span>()</pre></div>
<p dir="auto">When you set the request body to <code class="notranslate">{}</code> with Postman or any HTTP clients, the first request will return 200, while the second request will return a 405 error response. The log shows the request method is <code class="notranslate">{}POST</code>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="&quot;{}POST / HTTP/1.1&quot; 405"><pre class="notranslate"><code class="notranslate">"{}POST / HTTP/1.1" 405
</code></pre></div>
<p dir="auto">Notice the request body became the part of the request method.</p>