summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorFlorian Apolloner <florian@apolloner.eu>2020-02-07 12:55:59 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-02-11 20:40:24 +0100
commit4e8d6a1bafbbebc0720c5d1ed751521356364fe9 (patch)
treea50a7b4db7277bda878d1668ede1e133cd3114db /django
parent22c25bea54dcad23c76d26bb0bfb8d56eeedeef2 (diff)
[3.0.x] Fixed #31240 -- Properly closed FileResponse when wsgi.file_wrapper is used.
Thanks to Oskar Persson for the report. Backport of 41a3b3d18647b258331104520e76f977406c590d from master
Diffstat (limited to 'django')
-rw-r--r--django/core/handlers/base.py2
-rw-r--r--django/core/handlers/wsgi.py4
-rw-r--r--django/http/response.py12
3 files changed, 12 insertions, 6 deletions
diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py
index 2304e7761d..a7b8e61f60 100644
--- a/django/core/handlers/base.py
+++ b/django/core/handlers/base.py
@@ -73,7 +73,7 @@ class BaseHandler:
# Setup default url resolver for this thread
set_urlconf(settings.ROOT_URLCONF)
response = self._middleware_chain(request)
- response._closable_objects.append(request)
+ response._resource_closers.append(request.close)
if response.status_code >= 400:
log_response(
'%s: %s', response.reason_phrase, request.path,
diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py
index cb740e5c50..4dff1a299b 100644
--- a/django/core/handlers/wsgi.py
+++ b/django/core/handlers/wsgi.py
@@ -141,6 +141,10 @@ class WSGIHandler(base.BaseHandler):
]
start_response(status, response_headers)
if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
+ # If `wsgi.file_wrapper` is used the WSGI server does not call
+ # .close on the response, but on the file wrapper. Patch it to use
+ # response.close instead which takes care of closing all files.
+ response.file_to_stream.close = response.close
response = environ['wsgi.file_wrapper'](response.file_to_stream, response.block_size)
return response
diff --git a/django/http/response.py b/django/http/response.py
index 04efbd6bef..c33feb97c4 100644
--- a/django/http/response.py
+++ b/django/http/response.py
@@ -40,7 +40,7 @@ class HttpResponseBase:
# the header (required for working with legacy systems) and the header
# value. Both the name of the header and its value are ASCII strings.
self._headers = {}
- self._closable_objects = []
+ self._resource_closers = []
# This parameter is set by the handler. It's necessary to preserve the
# historical behavior of request_finished.
self._handler_class = None
@@ -242,11 +242,13 @@ class HttpResponseBase:
# The WSGI server must call this method upon completion of the request.
# See http://blog.dscpl.com.au/2012/10/obligations-for-calling-close-on.html
def close(self):
- for closable in self._closable_objects:
+ for closer in self._resource_closers:
try:
- closable.close()
+ closer()
except Exception:
pass
+ # Free resources that were still referenced.
+ self._resource_closers.clear()
self.closed = True
signals.request_finished.send(sender=self._handler_class)
@@ -377,7 +379,7 @@ class StreamingHttpResponse(HttpResponseBase):
# Ensure we can never iterate on "value" more than once.
self._iterator = iter(value)
if hasattr(value, 'close'):
- self._closable_objects.append(value)
+ self._resource_closers.append(value.close)
def __iter__(self):
return self.streaming_content
@@ -404,7 +406,7 @@ class FileResponse(StreamingHttpResponse):
self.file_to_stream = filelike = value
if hasattr(filelike, 'close'):
- self._closable_objects.append(filelike)
+ self._resource_closers.append(filelike.close)
value = iter(lambda: filelike.read(self.block_size), b'')
self.set_headers(filelike)
super()._set_streaming_content(value)