From dc97e544127153aff51b1c77ca6c09a12dc6eb21 Mon Sep 17 00:00:00 2001 From: benoitc Date: Tue, 27 Aug 2013 11:45:32 +0200 Subject: [PATCH] handle io.BytesIO object with file_wrapper fix #595 --- examples/frameworks/flask_sendfile.py | 14 ++++++++++++++ gunicorn/http/wsgi.py | 11 +++++++---- gunicorn/util.py | 13 +++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 examples/frameworks/flask_sendfile.py diff --git a/examples/frameworks/flask_sendfile.py b/examples/frameworks/flask_sendfile.py new file mode 100644 index 00000000..fe3d243a --- /dev/null +++ b/examples/frameworks/flask_sendfile.py @@ -0,0 +1,14 @@ +import io + +from flask import Flask, send_file + +app = Flask(__name__) + +@app.route('/') +def index(): + buf = io.BytesIO() + buf.write('hello world') + buf.seek(0) + return send_file(buf, + attachment_filename="testing.txt", + as_attachment=True) diff --git a/gunicorn/http/wsgi.py b/gunicorn/http/wsgi.py index e50c49fc..b8476818 100644 --- a/gunicorn/http/wsgi.py +++ b/gunicorn/http/wsgi.py @@ -10,6 +10,7 @@ import sys from gunicorn.six import unquote_to_wsgi_str, string_types, binary_type, reraise from gunicorn import SERVER_SOFTWARE +import gunicorn.six as six import gunicorn.util as util try: @@ -330,11 +331,13 @@ class Response(object): sent += sendfile(sockno, fileno, offset + sent, nbytes - sent) def write_file(self, respiter): - if sendfile is not None and \ - hasattr(respiter.filelike, 'fileno') and \ - hasattr(respiter.filelike, 'tell'): + if sendfile is not None and util.is_fileobject(respiter.filelike): + # sometimes the fileno isn't a callable + if six.callable(respiter.filelike.fileno): + fileno = respiter.filelike.fileno() + else: + fileno = respiter.filelike.fileno - fileno = respiter.filelike.fileno() fd_offset = os.lseek(fileno, 0, os.SEEK_CUR) fo_offset = respiter.filelike.tell() nbytes = max(os.fstat(fileno).st_size - fo_offset, 0) diff --git a/gunicorn/util.py b/gunicorn/util.py index 9b7529fa..e4cf0d31 100644 --- a/gunicorn/util.py +++ b/gunicorn/util.py @@ -5,6 +5,7 @@ import fcntl +import io import os import pkg_resources import random @@ -507,3 +508,15 @@ def to_bytestring(value): return value assert isinstance(value, text_type) return value.encode("utf-8") + +def is_fileobject(obj): + if not hasattr(obj, "tell") or not hasattr(obj, "fileno"): + return False + + # check BytesIO case and maybe others + try: + obj.fileno() + except io.UnsupportedOperation: + return False + + return True