Code

Ticket #18796: 18796.patch

File 18796.patch, 4.8 KB (added by aaugustin, 18 months ago)
  • django/http/response.py

    commit be117094f7d66a716e6ca61809441bbe6d0cefbe
    Author: Aymeric Augustin <aymeric.augustin@m4x.org>
    Date:   Wed Oct 24 23:41:45 2012 +0200
    
        Fixed #18796 -- Refactored conversion to bytes in HttpResponse
    
    diff --git a/django/http/response.py b/django/http/response.py
    index 6bbadd7..68c3ca9 100644
    a b from django.http.cookie import SimpleCookie 
    1616from django.utils import six, timezone 
    1717from django.utils.encoding import force_bytes, iri_to_uri 
    1818from django.utils.http import cookie_date 
     19from django.utils.six.moves import map 
    1920 
    2021 
    2122class BadHeaderError(ValueError): 
    class HttpResponseBase(object): 
    191192 
    192193    def make_bytes(self, value): 
    193194        """Turn a value into a bytestring encoded in the output charset.""" 
    194         # For backwards compatibility, this method supports values that are 
    195         # unlikely to occur in real applications. It has grown complex and 
    196         # should be refactored. It also overlaps __next__. See #18796. 
     195        # Per PEP 3333, this response body must be bytes. To avoid returning 
     196        # an instance of a subclass, this function returns `bytes(value)`. 
     197        # This doesn't make a copy when `value` already contains bytes. 
     198 
     199        # If content is already encoded (eg. gzip), assume bytes. 
    197200        if self.has_header('Content-Encoding'): 
    198             if isinstance(value, int): 
    199                 value = six.text_type(value) 
    200             if isinstance(value, six.text_type): 
    201                 value = value.encode('ascii') 
    202             # force conversion to bytes in case chunk is a subclass 
     201            if isinstance(value, bytes): 
     202                return bytes(value) 
     203            raise TypeError("Unsupported data in response: %s" % type(value)) 
     204 
     205        # Handle string types 
     206        if isinstance(value, bytes): 
    203207            return bytes(value) 
    204         else: 
    205             return force_bytes(value, self._charset) 
     208        if isinstance(value, six.text_type): 
     209            return bytes(value.encode(self._charset)) 
     210 
     211        # Handle non-string types - not ovbiously useful but supported (#16494) 
     212        return force_bytes(value, self._charset) 
     213 
     214    def __iter__(self): 
     215        return self 
     216 
     217    def __next__(self): 
     218        # Subclasses must define self._iterator for this function. 
     219        return self.make_bytes(next(self._iterator)) 
     220 
     221    next = __next__             # Python 2 compatibility 
    206222 
    207223    # These methods partially implement the file-like object interface. 
    208224    # See http://docs.python.org/lib/bltin-file-objects.html 
    class HttpResponse(HttpResponseBase): 
    287303            self._iterator = iter(self._container) 
    288304        return self 
    289305 
    290     def __next__(self): 
    291         chunk = next(self._iterator) 
    292         if isinstance(chunk, int): 
    293             chunk = six.text_type(chunk) 
    294         if isinstance(chunk, six.text_type): 
    295             chunk = chunk.encode(self._charset) 
    296         # force conversion to bytes in case chunk is a subclass 
    297         return bytes(chunk) 
    298  
    299     next = __next__             # Python 2 compatibility 
    300  
    301306    def write(self, content): 
    302307        self._consume_content() 
    303308        self._container.append(content) 
    class StreamingHttpResponse(HttpResponseBase): 
    331336 
    332337    @property 
    333338    def streaming_content(self): 
    334         return self._iterator 
     339        return map(self.make_bytes, self._iterator) 
    335340 
    336341    @streaming_content.setter 
    337342    def streaming_content(self, value): 
    class StreamingHttpResponse(HttpResponseBase): 
    340345        if hasattr(value, 'close'): 
    341346            self._closable_objects.append(value) 
    342347 
    343     def __iter__(self): 
    344         return self 
    345  
    346     def __next__(self): 
    347         return self.make_bytes(next(self._iterator)) 
    348  
    349     next = __next__             # Python 2 compatibility 
    350  
    351348 
    352349class CompatibleStreamingHttpResponse(StreamingHttpResponse): 
    353350    """ 
  • tests/regressiontests/httpwrappers/tests.py

    diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py
    index 8e20c80..b57c102 100644
    a b class HttpResponseTests(unittest.TestCase): 
    330330        self.assertEqual(r.content, b'123\xde\x9e') 
    331331 
    332332        #with Content-Encoding header 
    333         r = HttpResponse([1,1,2,4,8]) 
     333        r = HttpResponse() 
    334334        r['Content-Encoding'] = 'winning' 
    335         self.assertEqual(r.content, b'11248') 
    336         r.content = ['\u079e',] 
    337         self.assertRaises(UnicodeEncodeError, 
    338                           getattr, r, 'content') 
     335        r.content = [b'abc', b'def'] 
     336        self.assertEqual(r.content, b'abcdef') 
     337        r.content = ['\u079e'] 
     338        self.assertRaises(TypeError, getattr, r, 'content') 
    339339 
    340340        # .content can safely be accessed multiple times. 
    341341        r = HttpResponse(iter(['hello', 'world']))