Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Flask + nginx fcgi - output large response?

I'm using Python Flask + nginx with FCGI.

On some requests, I have to output large responses. Usually those responses are fetched from a socket. Currently I'm doing the response like this:

response = []
while True:
    recv = s.recv(1024)
    if not recv: break
    response.append(recv)
s.close()
response = ''.join(response)

return flask.make_response(response, 200, {
                                           'Content-type': 'binary/octet-stream',
                                           'Content-length': len(response),
                                           'Content-transfer-encoding': 'binary',
                                           })

The problem is I actually do not need the data. I also have a way to determine the exact response length to be fetched from the socket. So I need a good way to send the HTTP headers, then start outputing directly from the socket, instead of collecting it in memory and then supplying to nginx (probably by some sort of a stream).

I was unable to find the solution to this seemingly common issue. How would that be achieved?

Thank you!

like image 348
ddinchev Avatar asked May 18 '12 10:05

ddinchev


1 Answers

if response in flask.make_response is an iterable, it will be iterated over to produce the response, and each string is written to the output stream on it's own.

what this means is that you can also return a generator which will yield the output when iterated over. if you know the content length, then you can (and should) pass it as header.

a simple example:

from flask import Flask
app = Flask(__name__)
import sys
import time
import flask

@app.route('/')
def generated_response_example():
    n = 20
    def response_generator():
        for i in range(n):
            print >>sys.stderr, i
            yield "%03d\n" % i
            time.sleep(.2)

    print >>sys.stderr, "returning generator..."
    gen = response_generator()

    # the call to flask.make_response is not really needed as it happens imlicitly 
    # if you return a tuple.
    return flask.make_response(gen ,"200 OK", {'Content-length': 4*n})

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

if you run this and try it in a browser, you should see a nice incemental count...

(the content type is not set because it seems if i do that my browser waits until the whole content has been streamed before rendering the page. wget -qO - localhost:5000 doesn't have this problems.

like image 114
mata Avatar answered Oct 20 '22 11:10

mata