Commit 5233b700 authored by Johannes Hoppe's avatar Johannes Hoppe Committed by Tim Graham
Browse files

Fixed #25725 -- Made HttpReponse immediately close objects.

parent a6c803a2
Loading
Loading
Loading
Loading
+7 −4
Original line number Diff line number Diff line
@@ -316,13 +316,16 @@ class HttpResponse(HttpResponseBase):
    def content(self, value):
        # Consume iterators upon assignment to allow repeated iteration.
        if hasattr(value, '__iter__') and not isinstance(value, (bytes, six.string_types)):
            content = b''.join(self.make_bytes(chunk) for chunk in value)
            if hasattr(value, 'close'):
                self._closable_objects.append(value)
            value = b''.join(self.make_bytes(chunk) for chunk in value)
                try:
                    value.close()
                except Exception:
                    pass
        else:
            value = self.make_bytes(value)
            content = self.make_bytes(value)
        # Create a list of properly encoded bytestrings to support write().
        self._container = [value]
        self._container = [content]

    def __iter__(self):
        return iter(self._container)
+7 −1
Original line number Diff line number Diff line
@@ -608,11 +608,17 @@ Passing iterators

Finally, you can pass ``HttpResponse`` an iterator rather than strings.
``HttpResponse`` will consume the iterator immediately, store its content as a
string, and discard it.
string, and discard it. Objects with a ``close()`` method such as files and
generators are immediately closed.

If you need the response to be streamed from the iterator to the client, you
must use the :class:`StreamingHttpResponse` class instead.

.. versionchanged:: 1.10

    Objects with a ``close()`` method used to be closed when the WSGI server
    called ``close()`` on the response.

Setting header fields
~~~~~~~~~~~~~~~~~~~~~

+4 −0
Original line number Diff line number Diff line
@@ -357,6 +357,10 @@ Miscellaneous
* The ``add_postgis_srs()`` backwards compatibility alias for
  ``django.contrib.gis.utils.add_srs_entry()`` is removed.

* Objects with a ``close()`` method such as files and generators passed to
  :class:`~django.http.HttpResponse` are now closed immediately instead of when
  the WSGI server calls ``close()`` on the response.

.. _deprecated-features-1.10:

Features deprecated in 1.10
+0 −13
Original line number Diff line number Diff line
@@ -588,18 +588,8 @@ class FileCloseTests(SimpleTestCase):
        # file isn't closed until we close the response.
        file1 = open(filename)
        r = HttpResponse(file1)
        self.assertFalse(file1.closed)
        r.close()
        self.assertTrue(file1.closed)

        # don't automatically close file when we finish iterating the response.
        file1 = open(filename)
        r = HttpResponse(file1)
        self.assertFalse(file1.closed)
        list(r)
        self.assertFalse(file1.closed)
        r.close()
        self.assertTrue(file1.closed)

        # when multiple file are assigned as content, make sure they are all
        # closed with the response.
@@ -607,9 +597,6 @@ class FileCloseTests(SimpleTestCase):
        file2 = open(filename)
        r = HttpResponse(file1)
        r.content = file2
        self.assertFalse(file1.closed)
        self.assertFalse(file2.closed)
        r.close()
        self.assertTrue(file1.closed)
        self.assertTrue(file2.closed)

+11 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import io

from django.conf import settings
from django.core.cache import cache
from django.http import HttpResponse
from django.http.response import HttpResponseBase
from django.test import SimpleTestCase
@@ -121,3 +122,13 @@ class HttpResponseTests(SimpleTestCase):
        with io.TextIOWrapper(r, UTF8) as buf:
            buf.write(content)
        self.assertEqual(r.content, content.encode(UTF8))

    def test_generator_cache(self):
        generator = ("{}".format(i) for i in range(10))
        response = HttpResponse(content=generator)
        self.assertEqual(response.content, b'0123456789')
        self.assertRaises(StopIteration, next, generator)

        cache.set('my-response-key', response)
        response = cache.get('my-response-key')
        self.assertEqual(response.content, b'0123456789')