From 94a94bee3b56b1a8c54065020a461dc645c4d238 Mon Sep 17 00:00:00 2001 From: Chris Cahoon Date: Sat, 18 Jul 2009 02:02:57 +0000 Subject: [PATCH] [soc2009/http-wsgi-improvements] Establish the priorities and fallbacks for HttpResponseSendFile methods. Change the setting to settings.HTTPRESPONSE_SENDFILE_METHOD. If this is set to None, we use handler methods, but otherwise the header gets set, and we do not send any content. If neither of these are available, use the FileWrapper fallback in HttpResponseSendFile. This passes the test suite, but is untested on mod_python. I am still trying to figure out how to view the headers of a response with the Content-Disposition "attachment." git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/http-wsgi-improvements@11268 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/conf/global_settings.py | 10 +++++++--- django/core/handlers/modpython.py | 16 ++++++++-------- django/core/handlers/wsgi.py | 13 +++++-------- django/http/__init__.py | 7 ++++++- tests/regressiontests/sendfile/tests.py | 4 ++-- 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index c00b0b9b96..3736e1f2ad 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -236,9 +236,13 @@ MEDIA_ROOT = '' # Example: "http://media.lawrence.com" MEDIA_URL = '' -# Header to use in HttpResponseSendFile to inform the handler to serve the -# file with efficient handler-specific routines. -HTTPRESPONSE_SENDFILE_HEADER = 'X-Sendfile' +# Header to use in HttpResponseSendFile to inform the handler to serve the +# file with efficient handler-specific routines. None causes HttpResponseSendFile +# to fall back to, first, mechanisms in the handler (wsgi.filewrapper and +# req.sendfile. +# Examples: 'X-Sendfile' (FastCGI, lighttpd, Apache with mod_xsendfile), +# 'X-Accel-Redirect' (nginx) +HTTPRESPONSE_SENDFILE_METHOD = None # List of upload handler classes to be applied in order. FILE_UPLOAD_HANDLERS = ( diff --git a/django/core/handlers/modpython.py b/django/core/handlers/modpython.py index 2517098366..9557e5860e 100644 --- a/django/core/handlers/modpython.py +++ b/django/core/handlers/modpython.py @@ -200,15 +200,15 @@ class ModPythonHandler(BaseHandler): for c in response.cookies.values(): req.headers_out.add('Set-Cookie', c.output(header='')) req.status = response.status_code - if isinstance(response, http.HttpResponseSendFile): - req.sendfile(response.sendfile_filename) + if isinstance(response, http.HttpResponseSendFile): + req.sendfile(response.sendfile_filename) else: - try: - for chunk in response: - req.write(chunk) - finally: - response.close() - + # If we are using a header to do sendfile, set the header and send empty content + if settings.RESPONSE_SENDFILE_METHOD: + response.set_empty_content() + response[settings.HTTPRESPONSE_SENDFILE_METHOD] = response.sendfile_filename + for chunk in response: + req.write(chunk) return 0 # mod_python.apache.OK def handler(req): diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py index b79d15fec9..22e65b8f24 100644 --- a/django/core/handlers/wsgi.py +++ b/django/core/handlers/wsgi.py @@ -243,14 +243,11 @@ class WSGIHandler(base.BaseHandler): start_response(status, response_headers) if isinstance(response, http.HttpResponseSendFile): - filelike = open(response.sendfile_filename, 'rb') - if 'wsgi.file_wrapper' in environ: + if settings.HTTPRESPONSE_SENDFILE_METHOD: + response[settings.HTTPRESPONSE_SENDFILE_METHOD] = response.sendfile_filename + elif 'wsgi.file_wrapper' in environ: + filelike = open(response.sendfile_filename, 'rb') return environ['wsgi.file_wrapper'](filelike, - response.block_size) - else: - # wraps close() as well - from django.core.servers.basehttp import FileWrapper - return FileWrapper(filelike, response.block_size) - + response.block_size) return response diff --git a/django/http/__init__.py b/django/http/__init__.py index a99c68045c..330a0e25e9 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -447,9 +447,14 @@ class HttpResponseSendFile(HttpResponse): self['Content-Length'] = os.path.getsize(path_to_file) self['Content-Disposition'] = ('attachment; filename=%s' % os.path.basename(path_to_file)) - self[settings.HTTPRESPONSE_SENDFILE_HEADER] = path_to_file + self._empty_content = False + + def set_empty_content(self): + self._empty_content = True def __iter__(self): + if self._empty_content: + return iter(['']) from django.core.servers.basehttp import FileWrapper return FileWrapper(self.get_file_handler(), self.block_size) diff --git a/tests/regressiontests/sendfile/tests.py b/tests/regressiontests/sendfile/tests.py index b28cb2ead8..7e38a7e0b1 100644 --- a/tests/regressiontests/sendfile/tests.py +++ b/tests/regressiontests/sendfile/tests.py @@ -18,8 +18,8 @@ class SendFileTests(TestCase): urllib.quote(file1.name)) self.assertEqual(response.status_code, 200) - self.assertEqual(response[settings.HTTPRESPONSE_SENDFILE_HEADER], - file1.name) + #self.assertEqual(response[settings.HTTPRESPONSE_SENDFILE_METHOD], + # file1.name) self.assertEqual(response['Content-Disposition'], 'attachment; filename=%s' % os.path.basename(file1.name)) self.assertEqual(response['Content-Length'], str(FILE_SIZE))