diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//django/conf/global_settings.py ../Django-1.4b1//django/conf/global_settings.py
--- ../Django-1.4b1.orig//django/conf/global_settings.py	2012-02-16 05:03:40.000000000 +0100
+++ ../Django-1.4b1//django/conf/global_settings.py	2012-03-05 22:57:38.414932600 +0100
@@ -280,6 +280,10 @@
 # Example: "http://media.lawrence.com/static/"
 STATIC_URL = None
 
+# Header to use in HttpResponseSendFile to inform the handler to serve the
+# file with efficient handler-specific routines.
+HTTPRESPONSE_SENDFILE_HEADER = 'X-Sendfile'
+
 # List of upload handler classes to be applied in order.
 FILE_UPLOAD_HANDLERS = (
     'django.core.files.uploadhandler.MemoryFileUploadHandler',
diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//django/core/handlers/base.py ../Django-1.4b1//django/core/handlers/base.py
--- ../Django-1.4b1.orig//django/core/handlers/base.py	2012-02-16 05:03:44.000000000 +0100
+++ ../Django-1.4b1//django/core/handlers/base.py	2012-03-05 22:57:38.424933600 +0100
@@ -69,6 +69,26 @@
         # as a flag for initialization being complete.
         self._request_middleware = request_middleware
 
+    def process_request(self, request_env):
+        signals.request_started.send(sender=self.__class__)
+        try:
+            try:
+                request = self.request_class(request_env)
+            except UnicodeDecodeError:
+                logger.warning('Bad Request (UnicodeDecodeError)',
+                    exc_info=sys.exc_info(),
+                    extra={
+                        'status_code': 400,
+                    }
+                )
+                response = http.HttpResponseBadRequest()
+            else:
+                response = self.get_response(request)
+        finally:
+            signals.request_finished.send(sender=self.__class__)
+
+        return response
+
     def get_response(self, request):
         "Returns an HttpResponse object for the given HttpRequest"
         from django.core import exceptions, urlresolvers
diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//django/core/handlers/modpython.py ../Django-1.4b1//django/core/handlers/modpython.py
--- ../Django-1.4b1.orig//django/core/handlers/modpython.py	2012-02-16 05:03:44.000000000 +0100
+++ ../Django-1.4b1//django/core/handlers/modpython.py	2012-03-05 22:57:38.428934000 +0100
@@ -3,7 +3,6 @@
 from warnings import warn
 
 from django import http
-from django.core import signals
 from django.core.handlers.base import BaseHandler
 from django.core.urlresolvers import set_script_prefix
 from django.utils import datastructures
@@ -142,22 +141,8 @@
             self.load_middleware()
 
         set_script_prefix(req.get_options().get('django.root', ''))
-        signals.request_started.send(sender=self.__class__)
-        try:
-            try:
-                request = self.request_class(req)
-            except UnicodeDecodeError:
-                logger.warning('Bad Request (UnicodeDecodeError)',
-                    exc_info=sys.exc_info(),
-                    extra={
-                        'status_code': 400,
-                    }
-                )
-                response = http.HttpResponseBadRequest()
-            else:
-                response = self.get_response(request)
-        finally:
-            signals.request_finished.send(sender=self.__class__)
+
+        response = self.process_request(req)
 
         # Convert our custom HttpResponse object back into the mod_python req.
         req.content_type = response['Content-Type']
@@ -167,11 +152,15 @@
         for c in response.cookies.values():
             req.headers_out.add('Set-Cookie', c.output(header=''))
         req.status = response.status_code
-        try:
-            for chunk in response:
-                req.write(chunk)
-        finally:
-            response.close()
+
+        if isinstance(response, http.HttpResponseSendFile):
+            req.sendfile(response.sendfile_filename)
+        else:
+            try:
+                for chunk in response:
+            	    req.write(chunk)
+            finally:
+	        response.close()
 
         return 0 # mod_python.apache.OK
 
diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//django/core/handlers/wsgi.py ../Django-1.4b1//django/core/handlers/wsgi.py
--- ../Django-1.4b1.orig//django/core/handlers/wsgi.py	2012-02-16 05:03:44.000000000 +0100
+++ ../Django-1.4b1//django/core/handlers/wsgi.py	2012-03-05 22:57:38.437934900 +0100
@@ -225,22 +225,7 @@
                 self.initLock.release()
 
         set_script_prefix(base.get_script_name(environ))
-        signals.request_started.send(sender=self.__class__)
-        try:
-            try:
-                request = self.request_class(environ)
-            except UnicodeDecodeError:
-                logger.warning('Bad Request (UnicodeDecodeError)',
-                    exc_info=sys.exc_info(),
-                    extra={
-                        'status_code': 400,
-                    }
-                )
-                response = http.HttpResponseBadRequest()
-            else:
-                response = self.get_response(request)
-        finally:
-            signals.request_finished.send(sender=self.__class__)
+        response = self.process_request(environ)
 
         try:
             status_text = STATUS_CODE_TEXT[response.status_code]
@@ -251,4 +236,15 @@
         for c in response.cookies.values():
             response_headers.append(('Set-Cookie', str(c.output(header=''))))
         start_response(status, response_headers)
+
+        if isinstance(response, http.HttpResponseSendFile):
+            filelike = open(response.sendfile_filename, 'rb')
+            if 'wsgi.file_wrapper' in environ:
+                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)
+
         return response
diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//django/http/__init__.py ../Django-1.4b1//django/http/__init__.py
--- ../Django-1.4b1.orig//django/http/__init__.py	2012-02-16 05:03:40.000000000 +0100
+++ ../Django-1.4b1//django/http/__init__.py	2012-03-06 10:13:39.229084200 +0100
@@ -726,6 +726,46 @@
             raise Exception("This %s instance cannot tell its position" % self.__class__)
         return sum([len(str(chunk)) for chunk in self._container])
 
+class HttpResponseSendFile(HttpResponse):
+    sendfile_fh = None
+    def __init__(self, path_to_file, content_type=None, block_size=8192):
+        if not content_type:
+            from mimetypes import guess_type
+            content_type = guess_type(path_to_file)[0]
+            if content_type is None:
+                content_type = "application/octet-stream"
+        super(HttpResponseSendFile, self).__init__(None,
+                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).encode('utf-8'))
+        self[settings.HTTPRESPONSE_SENDFILE_HEADER] = path_to_file.encode('utf-8')
+        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)
+
+    def _get_content(self):
+       return "".join(self.__iter__())
+
+    def _set_content(self, content):
+       return 
+
+    content = property(_get_content, _set_content)
+
+    def get_file_handler(self):
+       if not self.sendfile_fh:
+           self.sendfile_fh = open(self.sendfile_filename, 'rb')
+       return self.sendfile_fh
+
 class HttpResponseRedirect(HttpResponse):
     status_code = 302
 
diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//docs/ref/request-response.txt ../Django-1.4b1//docs/ref/request-response.txt
--- ../Django-1.4b1.orig//docs/ref/request-response.txt	2012-02-16 05:03:40.000000000 +0100
+++ ../Django-1.4b1//docs/ref/request-response.txt	2012-03-05 22:57:38.454936600 +0100
@@ -723,9 +723,22 @@
 HttpResponse subclasses
 -----------------------
 
-Django includes a number of ``HttpResponse`` subclasses that handle different
-types of HTTP responses. Like ``HttpResponse``, these subclasses live in
-:mod:`django.http`.
+Django includes a number of :class:`HttpResponse` subclasses that handle
+different types of HTTP responses. Like :class:`HttpResponse`, these subclasses
+live in :mod:`django.http`.
+
+.. class:: HttpResponseSendFile
+
+    .. versionadded:: 1.5
+
+    A special response class for efficient file serving. It informs the HTTP
+    protocol handler to use platform-specific file serving mechanism (if
+    available). The constructor takes three arguments -- the file path and,
+    optionally, the file's content type and block size hint for handlers that
+    need it.
+
+    Note that response middleware will be bypassed if you use
+    :class:`HttpResponseSendFile`.
 
 .. class:: HttpResponseRedirect
 
diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//tests/regressiontests/sendfile/__init__.py ../Django-1.4b1//tests/regressiontests/sendfile/__init__.py
--- ../Django-1.4b1.orig//tests/regressiontests/sendfile/__init__.py	1970-01-01 01:00:00.000000000 +0100
+++ ../Django-1.4b1//tests/regressiontests/sendfile/__init__.py	2012-03-05 23:51:36.738742000 +0100
@@ -0,0 +1 @@
+
diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//tests/regressiontests/sendfile/models.py ../Django-1.4b1//tests/regressiontests/sendfile/models.py
--- ../Django-1.4b1.orig//tests/regressiontests/sendfile/models.py	1970-01-01 01:00:00.000000000 +0100
+++ ../Django-1.4b1//tests/regressiontests/sendfile/models.py	2012-03-05 23:51:31.332201400 +0100
@@ -0,0 +1,2 @@
+
+
diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//tests/regressiontests/sendfile/tests.py ../Django-1.4b1//tests/regressiontests/sendfile/tests.py
--- ../Django-1.4b1.orig//tests/regressiontests/sendfile/tests.py	1970-01-01 01:00:00.000000000 +0100
+++ ../Django-1.4b1//tests/regressiontests/sendfile/tests.py	2012-03-05 22:57:38.460937200 +0100
@@ -0,0 +1,36 @@
+import urllib, os
+
+from django.test import TestCase
+from django.conf import settings
+from django.core.files import temp as tempfile
+
+FILE_SIZE = 2 ** 10
+CONTENT = 'a' * FILE_SIZE
+
+class SendFileTests(TestCase):
+    def test_sendfile(self):
+        tdir = tempfile.gettempdir()
+
+        file1 = tempfile.NamedTemporaryFile(suffix=".pdf", dir=tdir)
+        file1.write(CONTENT)
+        file1.seek(0)
+
+        response = self.client.get('/sendfile/serve_file/%s/' %
+                urllib.quote(file1.name))
+
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response[settings.HTTPRESPONSE_SENDFILE_HEADER],
+                file1.name)
+        self.assertEqual(response['Content-Disposition'],
+                'attachment; filename=%s' % os.path.basename(file1.name))
+        self.assertEqual(response['Content-Length'], str(FILE_SIZE))
+        self.assertEqual(response['Content-Type'], 'application/pdf')
+
+        # *if* the degraded case is to be supported, add this instead:
+        self.assertEqual(response.content, CONTENT)
+        #get_content = lambda: response.content
+        #self.assertRaises(TypeError, get_content)
+
+        # TODO: test middleware bypass etc
+
+        file1.close()
diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//tests/regressiontests/sendfile/urls.py ../Django-1.4b1//tests/regressiontests/sendfile/urls.py
--- ../Django-1.4b1.orig//tests/regressiontests/sendfile/urls.py	1970-01-01 01:00:00.000000000 +0100
+++ ../Django-1.4b1//tests/regressiontests/sendfile/urls.py	2012-03-05 22:57:38.465937700 +0100
@@ -0,0 +1,7 @@
+from django.conf.urls.defaults import patterns
+
+import views
+
+urlpatterns = patterns('',
+    (r'^serve_file/(?P<filename>.*)/$', views.serve_file),
+)
diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//tests/regressiontests/sendfile/views.py ../Django-1.4b1//tests/regressiontests/sendfile/views.py
--- ../Django-1.4b1.orig//tests/regressiontests/sendfile/views.py	1970-01-01 01:00:00.000000000 +0100
+++ ../Django-1.4b1//tests/regressiontests/sendfile/views.py	2012-03-05 22:57:38.468938000 +0100
@@ -0,0 +1,7 @@
+import urllib
+
+from django.http import HttpResponseSendFile
+
+def serve_file(request, filename):
+    filename = urllib.unquote(filename)
+    return HttpResponseSendFile(filename)
diff -ru --new-file -x settings.py -x '*.pyc' -x '*.stackdump' -x '*.orig' -x '*.rej' ../Django-1.4b1.orig//tests/urls.py ../Django-1.4b1//tests/urls.py
--- ../Django-1.4b1.orig//tests/urls.py	2012-02-16 05:03:37.000000000 +0100
+++ ../Django-1.4b1//tests/urls.py	2012-03-05 22:57:38.471938300 +0100
@@ -30,4 +30,7 @@
     # admin scripts tests
     (r'^admin_scripts/', include('regressiontests.admin_scripts.urls')),
 
+    # HttpResponseSendfile tests 
+    (r'^sendfile/', include('regressiontests.sendfile.urls')),
+
 )
