6

I'm having problems with a Flask view that should return a response with content-type "application/json" in response to a POST request. Specifically, if I do:

curl -v -d 'foo=bar' http://example.org/jsonpost

to this view:

@app.route('/jsonpost', methods=['GET', 'POST'])
def json_post():
    resp = make_response('{"test": "ok"}')
    resp.headers['Content-Type'] = "application/json"
    return resp

I get some sort of connection reset:

* About to connect() to example.org port 80 (#0)
*   Trying xxx.xxx.xxx.xxx... connected
* Connected to example.org (xxx.xxx.xxx.xxx) port 80 (#0)
> POST /routing/jsonpost HTTP/1.1
> User-Agent: curl/7.19.7 (i486-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15
> Host: example.org
> Accept: */*
> Content-Length: 7
> Content-Type: application/x-www-form-urlencoded
>
< HTTP/1.1 200 OK
< Server: nginx/1.2.4
< Date: Thu, 27 Dec 2012 14:07:59 GMT
< Content-Type: application/json
< Content-Length: 14
< Connection: keep-alive
< Set-Cookie: session="..."; Path=/; HttpOnly
< Cache-Control: public
<
* transfer closed with 14 bytes remaining to read
* Closing connection #0
curl: (18) transfer closed with 14 bytes remaining to read

If instead I do:

curl -d 'foo=bar' http://example.org/htmlpost

to:

@app.route('/htmlpost', methods=['GET', 'POST'])
def html_post():
    resp = make_response('{"test": "ok"}')
    resp.headers['Content-Type'] = "text/html"
    return resp

I get the expected the full response (200-ok)

{"test": "ok"}

By the way, if I send a GET request to the same JSON route:

curl http://example.org/jsonpost

I also get the expected response.. Any ideas?

Paco
  • 4,520
  • 3
  • 29
  • 53
Marcoq
  • 404
  • 1
  • 6
  • 14
  • Try adding `curl -v ...` to see what's happening when your request is made. – Audrius Kažukauskas Dec 27 '12 at 13:05
  • Thank you Audrius, I've edited the question. In fact it appears to be some kind of connection reset. I still don't know what it may be. – Marcoq Dec 27 '12 at 14:21
  • According to http://stackoverflow.com/a/1761718/1870151 this happens because curl expects 14 bytes due to `Content-Length: 14` set (the length of your JSON-encoded response body), but for some reason the server closes the connection earlier. Perhaps it's a nginx related problem? You should also tell us what WSGI server you're using. – Audrius Kažukauskas Dec 27 '12 at 14:41
  • I'm using `nginx/1.2.4` and `uWSGI 0.9.9.3`. I get the same content lenght calling `htmlpost`. – Marcoq Dec 27 '12 at 15:16
  • That is very old version of uWSGI. It's possible that you're hitting some bug in its code. I'd advise to try out the latest version, which is 1.4.3 at the moment of writing this. – Audrius Kažukauskas Dec 27 '12 at 15:24

1 Answers1

14

Thanks to Audrius's comments I tracked a possible source of the problem to the interaction between uWSGI and nginx: apparently, if you receive POST data in a request you must read it before returning a response.

This, for example, fixes my issue.

@app.route('/jsonpost', methods=['GET', 'POST'])
def json_post():
    if request.method == 'POST':
        dummy = request.form
    resp = make_response('{"test": "ok"}')
    resp.headers['Content-Type'] = "application/json"
    return resp

A different solution involves passing --post-buffering 1 to uWSGI as described by uWSGI's author Roberto De Ioris.

I still don't understand why the problem does not present itself with Content-Type set to "text/html"

Community
  • 1
  • 1
Marcoq
  • 404
  • 1
  • 6
  • 14
  • 1
    "if you POST, you must read the data". you my friend are a legend. apologies for posting this, but it has cost me several hours!!!!! +1 thanks bah humbug!! – Gordon Dec 30 '15 at 21:32