Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support for HTTP/1.1 Transfer-Encoding: chunked #367

Closed
kislyuk opened this issue Dec 22, 2011 · 23 comments
Closed

support for HTTP/1.1 Transfer-Encoding: chunked #367

kislyuk opened this issue Dec 22, 2011 · 23 comments

Comments

@kislyuk
Copy link
Contributor

kislyuk commented Dec 22, 2011

Flask seems to choke on chunked encoded requests (I've only tried with Content-Type: application/json). This results in an error when parsing the request body (and being able to deal with chunked encoding is a requirement of HTTP 1.1).

@kislyuk
Copy link
Contributor Author

kislyuk commented Dec 23, 2011

By the way, cherryPy seems to have chunked support (haven't tested it yet), which may give guidance with the implementation.

@mitsuhiko
Copy link
Contributor

WSGI does not support chunked request encodings. There is not much I can do about that.

@kislyuk
Copy link
Contributor Author

kislyuk commented Dec 23, 2011

Then the consumer should reject the request, instead of trying to parse the body and failing, no?

Also, this changelog: http://code.google.com/p/modwsgi/wiki/ChangesInVersion0300 indicates that mod_wsgi, for one, is capable of parsing chunked requests (even though it doesn't support actual buffering of them).

@mitsuhiko
Copy link
Contributor

Then the consumer should reject the request, instead of trying to parse the body and failing, no?

Werkzeug does not generate responses by itself, that's up for the developer. The question is if we should automatically reject chunked requests in Flask and there I don't know yet. I was always hoping that WSGI servers would advertise their ability to handle chunked requests but it does not seem like there is a consistent way they do that.

Also, this changelog: http://code.google.com/p/modwsgi/wiki/ChangesInVersion0300 indicates that mod_wsgi, for one, is capable of parsing chunked requests (even though it doesn't support actual buffering of them).

Yes. But from the view of Werkzeug I cannot find out if a WSGI server extends the WSGI spec to support chunked requests. And if I assume it does I can cause a timeout on the client.

The problem is that WSGI specifies that one should read up to Content-Length. This header is missing if chunked requests are being handled. But then there are two situations. Either the WSGI server unpacks the chunked requests for us and we're good to read from the stream until the stream stops by itself or the WSGI server does not and if we try to do we request too much data from the client and the connection stalls waiting for more bytes that never get transmitted.

@kislyuk
Copy link
Contributor Author

kislyuk commented Dec 23, 2011

I see, thanks for the explanation.

Not happy with WSGI right now :(

@mitsuhiko
Copy link
Contributor

What I could do would be changing werkzeug to listen to a 'werkzeug.support_chunked' environment variable. In that case if the application is hosted on mod_wsgi you can flip that flag with a middleware and it magically starts working.

@kislyuk
Copy link
Contributor Author

kislyuk commented Dec 23, 2011

That would be great, however I'm using flask on its own as a simple REST API endpoint, putting it on mod_wsgi would surely be overkill.

@mitsuhiko
Copy link
Contributor

Well, CherryPy as a server instead of the builtin one would do the trick as well I guess. Will check that after Christmas.

@kislyuk
Copy link
Contributor Author

kislyuk commented Dec 23, 2011

Thanks!

Flask itself is a joy to work with.

@a13xb
Copy link

a13xb commented Jun 6, 2012

Let's revisit this.

Either the WSGI server unpacks the chunked requests for us and we're good to read from the stream until the stream stops by itself or the WSGI server does not and if we try to do we request too much data from the client and the connection stalls waiting for more bytes that never get transmitted.

PEP 333/3333 state

WSGI servers must handle any supported inbound "hop-by-hop" headers on their own, such as by decoding any inbound Transfer-Encoding, including chunked encoding if applicable.

This guarantees that the application will always see a flat decoded stream in "wsgi.input", not chunks.

@zroadhouse-rmn
Copy link

Is there an update on this issue? I'm trying to use stream_with_context and was hoping to see the resulting output as chunks instead of as a HTTP 1.0 response without a content-length.

@alanhamlett
Copy link
Contributor

@zroadhouse-wsm You're following this snippet?
http://flask.pocoo.org/snippets/118/

I think that snippet (chunked responses) works but this issue is for reading chunked requests from clients.

@zroadhouse-rmn
Copy link

Yes - very similar. In my case, I'm waiting for a long running query. Flask is executing behind AWS ELB -> nginx -> uwsgi -> flask. The ELB enforces a 60s timeout and I was hoping to use stream_with_context to transmit content using chunks to keep the connection open while my query executes. My test results calling at each of the layers:

  • ELB - times out after 60s
  • Nginx - appears to buffer all the content and then return the response
  • Flask (directly using the werkzeug server) - Returns an HTTP 1.0 header (see after the fragment) and then transmits content until complete

Here's the code snippet I am using for experimenting:

from flask import Flask, Response, stream_with_context
import time

app = Flask(__name__)

@app.route('/streamit', methods=['GET'])
def streamit():
    def generator():
        for x in xrange(70):
            time.sleep(1.0)
            yield ' '
        yield '<html><body>done</body></html>'
    return Response(stream_with_context(generator()))

if __name__ == '__main__':
    app.run()

# Run this script and then testing using:
#   curl -v -N http://localhost:5000/streamit

Curl output:

$ curl -v -N http://localhost:5000/streamit
* About to connect() to localhost port 5000 (#0)
*   Trying ::1...
* Adding handle: conn: 0x7f96d180aa00
* Adding handle: send: 0
* Adding handle: recv: 0
* Curl_addHandleToPipeline: length: 1
* - Conn 0 (0x7f96d180aa00) send_pipe: 1, recv_pipe: 0
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 5000 (#0)
> GET /streamit HTTP/1.1
> User-Agent: curl/7.33.0
> Host: localhost:5000
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Content-Type: text/html; charset=utf-8
< Connection: close
< Server: Werkzeug/0.9.3 Python/2.7.5
< Date: Mon, 27 Jan 2014 22:48:30 GMT
< 
                                                                      <html><body>done</body></html>* Closing connection 0

@ThiefMaster
Copy link
Member

Uh... the embedded flask/werkzeug server is not suitable for production.

@zroadhouse-rmn
Copy link

@ThiefMaster - If you read my post, you'll see I'm using AWS ELB -> nginx -> uwsgi -> flask -- the example above is part of my debugging exercise.

@zroadhouse-rmn
Copy link

Looks like my issue is different from the OP - commented here since this was the only issue mentioning chunked encoding. If anyone can provide a pointer for getting response chunked encoding working, I'd appreciate it.

@DasIch
Copy link
Contributor

DasIch commented Jan 28, 2014

The original point mentioned by @mitsuhiko still stands: WSGI by itself does not allow chunked encoding. So there is no pointer to give.

@jedwards1211
Copy link

jedwards1211 commented Sep 21, 2017

@mitsuhiko @DasIch I think you guys missed @a13xb's comment...PEP 333 and PEP 3333 explicitly state:

WSGI servers must handle any supported inbound "hop-by-hop" headers on their own, such as by decoding any inbound Transfer-Encoding, including chunked encoding if applicable.

Am I misunderstanding this or does it contradict what you guys have said?

@mitsuhiko
Copy link
Contributor

The WSGI spec unfortunately cannot be implemented with regards to chunked. We have hidden support to dealing with severs can decode it on the fly (wsgi.input_terminated).

@jedwards1211
Copy link

You mean something about the design makes it technically impossible to implement official support for chunked encoding?

@jedwards1211
Copy link

jedwards1211 commented Sep 21, 2017

I see other people saying things like

It is a limitation of the WSGI specification itself in as much as the WSGI specification prohibits use of chunked request content by requiring a CONTENT_LENGTH value for request.

Does this mean the WSGI spec contradicts itself?

@mitsuhiko
Copy link
Contributor

I tried to fix this problem a few years back and I introduced a hint called wsgi.input_terminated to make the content length optional. If a WSGI servers supports chunked and sets that key to True then Flask/Werkzeug will support chunked as well.

@jedwards1211
Copy link

I see, strange. Thanks for the tip!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants