Ticket #2131: ticket2131.diff

File ticket2131.diff, 13.6 KB (added by milosu, 12 years ago)

updated patch compatible with Django-1.4b1 including test case

  • django/conf/global_settings.py

    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
    old new  
    280280# Example: "http://media.lawrence.com/static/"
    281281STATIC_URL = None
    282282
     283# Header to use in HttpResponseSendFile to inform the handler to serve the
     284# file with efficient handler-specific routines.
     285HTTPRESPONSE_SENDFILE_HEADER = 'X-Sendfile'
     286
    283287# List of upload handler classes to be applied in order.
    284288FILE_UPLOAD_HANDLERS = (
    285289    'django.core.files.uploadhandler.MemoryFileUploadHandler',
  • django/core/handlers/base.py

    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
    old new  
    6969        # as a flag for initialization being complete.
    7070        self._request_middleware = request_middleware
    7171
     72    def process_request(self, request_env):
     73        signals.request_started.send(sender=self.__class__)
     74        try:
     75            try:
     76                request = self.request_class(request_env)
     77            except UnicodeDecodeError:
     78                logger.warning('Bad Request (UnicodeDecodeError)',
     79                    exc_info=sys.exc_info(),
     80                    extra={
     81                        'status_code': 400,
     82                    }
     83                )
     84                response = http.HttpResponseBadRequest()
     85            else:
     86                response = self.get_response(request)
     87        finally:
     88            signals.request_finished.send(sender=self.__class__)
     89
     90        return response
     91
    7292    def get_response(self, request):
    7393        "Returns an HttpResponse object for the given HttpRequest"
    7494        from django.core import exceptions, urlresolvers
  • django/core/handlers/modpython.py

    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
    old new  
    33from warnings import warn
    44
    55from django import http
    6 from django.core import signals
    76from django.core.handlers.base import BaseHandler
    87from django.core.urlresolvers import set_script_prefix
    98from django.utils import datastructures
     
    142141            self.load_middleware()
    143142
    144143        set_script_prefix(req.get_options().get('django.root', ''))
    145         signals.request_started.send(sender=self.__class__)
    146         try:
    147             try:
    148                 request = self.request_class(req)
    149             except UnicodeDecodeError:
    150                 logger.warning('Bad Request (UnicodeDecodeError)',
    151                     exc_info=sys.exc_info(),
    152                     extra={
    153                         'status_code': 400,
    154                     }
    155                 )
    156                 response = http.HttpResponseBadRequest()
    157             else:
    158                 response = self.get_response(request)
    159         finally:
    160             signals.request_finished.send(sender=self.__class__)
     144
     145        response = self.process_request(req)
    161146
    162147        # Convert our custom HttpResponse object back into the mod_python req.
    163148        req.content_type = response['Content-Type']
     
    167152        for c in response.cookies.values():
    168153            req.headers_out.add('Set-Cookie', c.output(header=''))
    169154        req.status = response.status_code
    170         try:
    171             for chunk in response:
    172                 req.write(chunk)
    173         finally:
    174             response.close()
     155
     156        if isinstance(response, http.HttpResponseSendFile):
     157            req.sendfile(response.sendfile_filename)
     158        else:
     159            try:
     160                for chunk in response:
     161                    req.write(chunk)
     162            finally:
     163                response.close()
    175164
    176165        return 0 # mod_python.apache.OK
    177166
  • django/core/handlers/wsgi.py

    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
    old new  
    225225                self.initLock.release()
    226226
    227227        set_script_prefix(base.get_script_name(environ))
    228         signals.request_started.send(sender=self.__class__)
    229         try:
    230             try:
    231                 request = self.request_class(environ)
    232             except UnicodeDecodeError:
    233                 logger.warning('Bad Request (UnicodeDecodeError)',
    234                     exc_info=sys.exc_info(),
    235                     extra={
    236                         'status_code': 400,
    237                     }
    238                 )
    239                 response = http.HttpResponseBadRequest()
    240             else:
    241                 response = self.get_response(request)
    242         finally:
    243             signals.request_finished.send(sender=self.__class__)
     228        response = self.process_request(environ)
    244229
    245230        try:
    246231            status_text = STATUS_CODE_TEXT[response.status_code]
     
    251236        for c in response.cookies.values():
    252237            response_headers.append(('Set-Cookie', str(c.output(header=''))))
    253238        start_response(status, response_headers)
     239
     240        if isinstance(response, http.HttpResponseSendFile):
     241            filelike = open(response.sendfile_filename, 'rb')
     242            if 'wsgi.file_wrapper' in environ:
     243                return environ['wsgi.file_wrapper'](filelike,
     244                        response.block_size)
     245            else:
     246                # wraps close() as well
     247                from django.core.servers.basehttp import FileWrapper
     248                return FileWrapper(filelike, response.block_size)
     249
    254250        return response
  • django/http/__init__.py

    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
    old new  
    726726            raise Exception("This %s instance cannot tell its position" % self.__class__)
    727727        return sum([len(str(chunk)) for chunk in self._container])
    728728
     729class HttpResponseSendFile(HttpResponse):
     730    sendfile_fh = None
     731    def __init__(self, path_to_file, content_type=None, block_size=8192):
     732        if not content_type:
     733            from mimetypes import guess_type
     734            content_type = guess_type(path_to_file)[0]
     735            if content_type is None:
     736                content_type = "application/octet-stream"
     737        super(HttpResponseSendFile, self).__init__(None,
     738                content_type=content_type)
     739        self.sendfile_filename = path_to_file
     740        self.block_size = block_size
     741        self['Content-Length'] = os.path.getsize(path_to_file)
     742        self['Content-Disposition'] = ('attachment; filename=%s' %
     743                os.path.basename(path_to_file).encode('utf-8'))
     744        self[settings.HTTPRESPONSE_SENDFILE_HEADER] = path_to_file.encode('utf-8')
     745        self._empty_content = False
     746
     747    def set_empty_content(self):
     748       self._empty_content = True
     749
     750    def __iter__(self):
     751       if self._empty_content:
     752               return iter([''])
     753       from django.core.servers.basehttp import FileWrapper
     754       return FileWrapper(self.get_file_handler(), self.block_size)
     755
     756    def _get_content(self):
     757       return "".join(self.__iter__())
     758
     759    def _set_content(self, content):
     760       return
     761
     762    content = property(_get_content, _set_content)
     763
     764    def get_file_handler(self):
     765       if not self.sendfile_fh:
     766           self.sendfile_fh = open(self.sendfile_filename, 'rb')
     767       return self.sendfile_fh
     768
    729769class HttpResponseRedirect(HttpResponse):
    730770    status_code = 302
    731771
  • docs/ref/request-response.txt

    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
    old new  
    723723HttpResponse subclasses
    724724-----------------------
    725725
    726 Django includes a number of ``HttpResponse`` subclasses that handle different
    727 types of HTTP responses. Like ``HttpResponse``, these subclasses live in
    728 :mod:`django.http`.
     726Django includes a number of :class:`HttpResponse` subclasses that handle
     727different types of HTTP responses. Like :class:`HttpResponse`, these subclasses
     728live in :mod:`django.http`.
     729
     730.. class:: HttpResponseSendFile
     731
     732    .. versionadded:: 1.5
     733
     734    A special response class for efficient file serving. It informs the HTTP
     735    protocol handler to use platform-specific file serving mechanism (if
     736    available). The constructor takes three arguments -- the file path and,
     737    optionally, the file's content type and block size hint for handlers that
     738    need it.
     739
     740    Note that response middleware will be bypassed if you use
     741    :class:`HttpResponseSendFile`.
    729742
    730743.. class:: HttpResponseRedirect
    731744
  • tests/regressiontests/sendfile/__init__.py

    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
    old new  
     1
  • tests/regressiontests/sendfile/models.py

    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
    old new  
     1
     2
  • tests/regressiontests/sendfile/tests.py

    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
    old new  
     1import urllib, os
     2
     3from django.test import TestCase
     4from django.conf import settings
     5from django.core.files import temp as tempfile
     6
     7FILE_SIZE = 2 ** 10
     8CONTENT = 'a' * FILE_SIZE
     9
     10class SendFileTests(TestCase):
     11    def test_sendfile(self):
     12        tdir = tempfile.gettempdir()
     13
     14        file1 = tempfile.NamedTemporaryFile(suffix=".pdf", dir=tdir)
     15        file1.write(CONTENT)
     16        file1.seek(0)
     17
     18        response = self.client.get('/sendfile/serve_file/%s/' %
     19                urllib.quote(file1.name))
     20
     21        self.assertEqual(response.status_code, 200)
     22        self.assertEqual(response[settings.HTTPRESPONSE_SENDFILE_HEADER],
     23                file1.name)
     24        self.assertEqual(response['Content-Disposition'],
     25                'attachment; filename=%s' % os.path.basename(file1.name))
     26        self.assertEqual(response['Content-Length'], str(FILE_SIZE))
     27        self.assertEqual(response['Content-Type'], 'application/pdf')
     28
     29        # *if* the degraded case is to be supported, add this instead:
     30        self.assertEqual(response.content, CONTENT)
     31        #get_content = lambda: response.content
     32        #self.assertRaises(TypeError, get_content)
     33
     34        # TODO: test middleware bypass etc
     35
     36        file1.close()
  • tests/regressiontests/sendfile/urls.py

    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
    old new  
     1from django.conf.urls.defaults import patterns
     2
     3import views
     4
     5urlpatterns = patterns('',
     6    (r'^serve_file/(?P<filename>.*)/$', views.serve_file),
     7)
  • tests/regressiontests/sendfile/views.py

    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
    old new  
     1import urllib
     2
     3from django.http import HttpResponseSendFile
     4
     5def serve_file(request, filename):
     6    filename = urllib.unquote(filename)
     7    return HttpResponseSendFile(filename)
  • tests/urls.py

    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
    old new  
    3030    # admin scripts tests
    3131    (r'^admin_scripts/', include('regressiontests.admin_scripts.urls')),
    3232
     33    # HttpResponseSendfile tests
     34    (r'^sendfile/', include('regressiontests.sendfile.urls')),
     35
    3336)
Back to Top