diff options
| author | Florian Apolloner <florian@apolloner.eu> | 2020-02-07 12:55:59 +0100 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2020-02-11 20:40:24 +0100 |
| commit | 4e8d6a1bafbbebc0720c5d1ed751521356364fe9 (patch) | |
| tree | a50a7b4db7277bda878d1668ede1e133cd3114db /django | |
| parent | 22c25bea54dcad23c76d26bb0bfb8d56eeedeef2 (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.py | 2 | ||||
| -rw-r--r-- | django/core/handlers/wsgi.py | 4 | ||||
| -rw-r--r-- | django/http/response.py | 12 |
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) |
