From 9ac495af4451ece0424f8053bce1b141fe4d8d8f Mon Sep 17 00:00:00 2001 From: Chris Cahoon Date: Thu, 23 Jul 2009 20:17:30 +0000 Subject: [PATCH] [soc2009/http-wsgi-improvements] Changes for ``HttpResponseSendFile`` support in FastCGI. Refs #2131. git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/http-wsgi-improvements@11319 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/conf/global_settings.py | 4 ++-- django/core/handlers/modpython.py | 4 ++-- django/core/handlers/wsgi.py | 17 ++++++++++++----- django/http/__init__.py | 1 - docs/ref/request-response.txt | 10 ++++++++++ tests/regressiontests/sendfile/tests.py | 2 -- 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 3736e1f2ad..22b2b6e10a 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -240,9 +240,9 @@ MEDIA_URL = '' # 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), +# Examples: 'X-Sendfile' (lighttpd & Cherokee with FastCGI/SCGI, Apache with mod_xsendfile), # 'X-Accel-Redirect' (nginx) -HTTPRESPONSE_SENDFILE_METHOD = None +HTTPRESPONSE_SENDFILE_HEADER = 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 9557e5860e..338eb3abb1 100644 --- a/django/core/handlers/modpython.py +++ b/django/core/handlers/modpython.py @@ -204,9 +204,9 @@ class ModPythonHandler(BaseHandler): req.sendfile(response.sendfile_filename) else: # If we are using a header to do sendfile, set the header and send empty content - if settings.RESPONSE_SENDFILE_METHOD: + if settings.RESPONSE_SENDFILE_HEADER: response.set_empty_content() - response[settings.HTTPRESPONSE_SENDFILE_METHOD] = response.sendfile_filename + response[settings.HTTPRESPONSE_SENDFILE_HEADER] = response.sendfile_filename for chunk in response: req.write(chunk) return 0 # mod_python.apache.OK diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py index ebf45a6368..e96ef44550 100644 --- a/django/core/handlers/wsgi.py +++ b/django/core/handlers/wsgi.py @@ -242,14 +242,21 @@ class WSGIHandler(base.BaseHandler): response_headers.append(('Set-Cookie', str(c.output(header='')))) if isinstance(response, http.HttpResponseSendFile): - if settings.HTTPRESPONSE_SENDFILE_METHOD: - response_headers.append((settings.HTTPRESPONSE_SENDFILE_METHOD, - response.sendfile_filename)) + filename = response.sendfile_filename + if settings.HTTPRESPONSE_SENDFILE_HEADER: + response.set_empty_content() + response_headers.append((settings.HTTPRESPONSE_SENDFILE_HEADER, + filename)) elif 'wsgi.file_wrapper' in environ: - filelike = open(response.sendfile_filename, 'rb') + filelike = open(filename, 'rb') return environ['wsgi.file_wrapper'](filelike, response.block_size) - + else: + import os.path + if not os.path.exists(filename): + raise Exception("Filename provided to HttpResponseSendFile does not exist.") + response_headers.append(('Content-Length', + str(os.path.getsize(filename)))) start_response(status, response_headers) return response diff --git a/django/http/__init__.py b/django/http/__init__.py index 330a0e25e9..90048e87a4 100644 --- a/django/http/__init__.py +++ b/django/http/__init__.py @@ -444,7 +444,6 @@ class HttpResponseSendFile(HttpResponse): super(HttpResponseSendFile, self).__init__('', content_type=content_type) self.sendfile_filename = path_to_file self.block_size = block_size - self['Content-Length'] = os.path.getsize(path_to_file) self['Content-Disposition'] = ('attachment; filename=%s' % os.path.basename(path_to_file)) self._empty_content = False diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index 65e97bf758..3db6a680eb 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -585,6 +585,16 @@ live in :mod:`django.http`. optionally, the file's content type and block size hint for handlers that need it. + If the setting ``HTTPRESPONSE_SENDFILE_HEADER`` is overridden (default None), + HttpResponseSendFile will return that response header set as the file name given. + If the file is unavailable, no content will be returned. Since certain servers + do not allow direct access to the file system, it is not feasible to verify + the file's existence beforehand. + + Be very careful with this method. It provides access to the filesystem that + must be controlled, and performs no verification of a file's existence in most + cases. + Note that response middleware will be bypassed if you use :class:`HttpResponseSendFile`. diff --git a/tests/regressiontests/sendfile/tests.py b/tests/regressiontests/sendfile/tests.py index 7e38a7e0b1..5d3f782294 100644 --- a/tests/regressiontests/sendfile/tests.py +++ b/tests/regressiontests/sendfile/tests.py @@ -18,8 +18,6 @@ class SendFileTests(TestCase): urllib.quote(file1.name)) self.assertEqual(response.status_code, 200) - #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))