Ticket #18796: 18796.patch

File 18796.patch, 4.8 KB (added by Aymeric Augustin, 12 years 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']))
Back to Top