Django

Code

Ticket #7581: streaming_response.diff

File streaming_response.diff, 6.8 kB (added by mrmachine, 2 years ago)

add HttpResponse.content_generator, consume generators only once, set Content-Length in WSGIHander.

  • django/http/__init__.py

    old new  
    268268        if not content_type: 
    269269            content_type = "%s; charset=%s" % (settings.DEFAULT_CONTENT_TYPE, 
    270270                    settings.DEFAULT_CHARSET) 
    271         if not isinstance(content, basestring) and hasattr(content, '__iter__'): 
    272             self._container = content 
    273             self._is_string = False 
    274         else: 
    275             self._container = [content] 
    276             self._is_string = True 
     271        self.content = content 
    277272        self.cookies = SimpleCookie() 
    278273        if status: 
    279274            self.status_code = status 
     
    345340                        expires='Thu, 01-Jan-1970 00:00:00 GMT') 
    346341 
    347342    def _get_content(self): 
     343        if not self._is_string: 
     344            self._container = [''.join(self._container)] 
     345            self._is_string = True 
    348346        if self.has_header('Content-Encoding'): 
    349347            return ''.join(self._container) 
    350348        return smart_str(''.join(self._container), self._charset) 
    351349 
    352350    def _set_content(self, value): 
    353         self._container = [value] 
    354         self._is_string = True 
     351        if not isinstance(value, basestring) and hasattr(value, '__iter__'): 
     352            self._container = value 
     353            self._is_string = False 
     354        else: 
     355            self._container = [value] 
     356            self._is_string = True 
    355357 
    356358    content = property(_get_content, _set_content) 
    357359 
     360    def _get_content_generator(self): 
     361        if not self._is_string: 
     362            return self._container 
     363 
     364    content_generator = property(_get_content_generator) 
     365 
    358366    def __iter__(self): 
    359367        self._iterator = iter(self._container) 
    360368        return self 
  • django/http/utils.py

    old new  
    3131    if request.method == 'HEAD': 
    3232        response.content = '' 
    3333    return response 
     34 
     35def set_content_length(request, response): 
     36    """ 
     37    Ensures that we always have a Content-Length header, required by some 
     38    handlers (WSGI), which don't permit chunked transfer encoding. 
     39    """ 
     40    if not 'Content-Length' in response: 
     41        response['Content-Length'] = str(len(response.content)) 
     42    return response 
  • django/core/handlers/wsgi.py

    old new  
    182182class WSGIHandler(BaseHandler): 
    183183    initLock = Lock() 
    184184    request_class = WSGIRequest 
     185    response_fixes = [http.fix_location_header, 
     186                      http.conditional_content_removal, 
     187                      http.set_content_length] 
    185188 
    186189    def __call__(self, environ, start_response): 
    187190        from django.conf import settings 
  • django/utils/text.py

    old new  
    175175    zfile.close() 
    176176    return zbuf.getvalue() 
    177177 
     178def compress_sequence(sequence): 
     179    import cStringIO, gzip 
     180    zbuf = cStringIO.StringIO() 
     181    zfile = gzip.GzipFile(mode='wb', compresslevel=6, fileobj=zbuf) 
     182    yield zbuf.getvalue() 
     183    for item in sequence: 
     184        position = zbuf.tell() 
     185        zfile.write(item) 
     186        zfile.flush() 
     187        zbuf.seek(position) 
     188        yield zbuf.read() 
     189 
    178190ustring_re = re.compile(u"([\u0080-\uffff])") 
    179191 
    180192def javascript_quote(s, quote_double_quotes=False): 
  • django/middleware/common.py

    old new  
    107107        if settings.USE_ETAGS: 
    108108            if response.has_header('ETag'): 
    109109                etag = response['ETag'] 
    110             else
     110            elif not response.content_generator
    111111                etag = '"%s"' % md5.new(response.content).hexdigest() 
    112             if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag: 
    113                 cookies = response.cookies 
    114                 response = http.HttpResponseNotModified() 
    115                 response.cookies = cookies 
    116             else: 
    117                 response['ETag'] = etag 
     112            try: 
     113                if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag: 
     114                    cookies = response.cookies 
     115                    response = http.HttpResponseNotModified() 
     116                    response.cookies = cookies 
     117                else: 
     118                    response['ETag'] = etag 
     119            except NameError: 
     120                pass 
    118121 
    119122        return response 
    120123 
  • django/middleware/gzip.py

    old new  
    11import re 
    22 
    3 from django.utils.text import compress_string 
     3from django.utils.text import compress_sequence, compress_string 
    44from django.utils.cache import patch_vary_headers 
    55 
    66re_accepts_gzip = re.compile(r'\bgzip\b') 
     
    1313    """ 
    1414    def process_response(self, request, response): 
    1515        # It's not worth compressing non-OK or really short responses. 
    16         if response.status_code != 200 or len(response.content) < 200
     16        if response.status_code != 200 or (not response.content_generator and len(response.content) < 200)
    1717            return response 
    1818 
    1919        patch_vary_headers(response, ('Accept-Encoding',)) 
     
    3333        if not re_accepts_gzip.search(ae): 
    3434            return response 
    3535 
    36         response.content = compress_string(response.content) 
     36        if response.content_generator: 
     37            response.content = compress_sequence(response.content_generator) 
     38            del response['Content-Length'] 
     39        else: 
     40            response.content = compress_string(response.content) 
     41            response['Content-Length'] = str(len(response.content)) 
    3742        response['Content-Encoding'] = 'gzip' 
    38         response['Content-Length'] = str(len(response.content)) 
    3943        return response 
  • django/middleware/http.py

    old new  
    1010    """ 
    1111    def process_response(self, request, response): 
    1212        response['Date'] = http_date() 
    13         if not response.has_header('Content-Length')
     13        if not response.has_header('Content-Length') and not response.content_generator
    1414            response['Content-Length'] = str(len(response.content)) 
    1515 
    1616        if response.has_header('ETag'):