Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #15281 -- Made the static view use an iterator when serving a f…

…ile, effectively making this less of a memory hog. Also use the appropriate attributes of the stat object instead of indexes. Thanks for the initial patch, FunkyBob and aaugustin.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@15701 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit be4a2e3f3e0a04c87e6c9127cb1011df7c53e9e9 1 parent 8f19878
@jezdez jezdez authored
View
25 django/core/servers/basehttp.py
@@ -19,6 +19,7 @@
from django.utils.http import http_date
from django.utils._os import safe_join
from django.views import static
+from django.views.static import FileWrapper # for backwards compatibility, #15281
from django.contrib.staticfiles import handlers
@@ -32,30 +33,6 @@
class WSGIServerException(Exception):
pass
-class FileWrapper(object):
- """Wrapper to convert file-like objects to iterables"""
-
- def __init__(self, filelike, blksize=8192):
- self.filelike = filelike
- self.blksize = blksize
- if hasattr(filelike,'close'):
- self.close = filelike.close
-
- def __getitem__(self,key):
- data = self.filelike.read(self.blksize)
- if data:
- return data
- raise IndexError
-
- def __iter__(self):
- return self
-
- def next(self):
- data = self.filelike.read(self.blksize)
- if data:
- return data
- raise StopIteration
-
# Regular expression that matches `special' characters in parameters, the
# existence of which force quoting of the parameter value.
tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]')
View
38 django/views/static.py
@@ -7,14 +7,41 @@
import os
import posixpath
import re
-import stat
import urllib
from django.template import loader
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseNotModified
+
from django.template import Template, Context, TemplateDoesNotExist
from django.utils.http import http_date, parse_http_date
+
+class FileWrapper(object):
+ """
+ Wrapper to convert file-like objects to iterables
+ """
+ def __init__(self, filelike, blksize=8192):
+ self.filelike = filelike
+ self.blksize = blksize
+ if hasattr(filelike,'close'):
+ self.close = filelike.close
+
+ def __getitem__(self,key):
+ data = self.filelike.read(self.blksize)
+ if data:
+ return data
+ raise IndexError
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ data = self.filelike.read(self.blksize)
+ if data:
+ return data
+ raise StopIteration
+
+
def serve(request, path, document_root=None, show_indexes=False):
"""
Serve static files below a given point in the directory structure.
@@ -56,12 +83,11 @@ def serve(request, path, document_root=None, show_indexes=False):
mimetype, encoding = mimetypes.guess_type(fullpath)
mimetype = mimetype or 'application/octet-stream'
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
- statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]):
+ statobj.st_mtime, statobj.st_size):
return HttpResponseNotModified(mimetype=mimetype)
- contents = open(fullpath, 'rb').read()
- response = HttpResponse(contents, mimetype=mimetype)
- response["Last-Modified"] = http_date(statobj[stat.ST_MTIME])
- response["Content-Length"] = len(contents)
+ response = HttpResponse(FileWrapper(open(fullpath, 'rb')), mimetype=mimetype)
+ response["Last-Modified"] = http_date(statobj.st_mtime)
+ response["Content-Length"] = statobj.st_size
if encoding:
response["Content-Encoding"] = encoding
return response
View
24 tests/regressiontests/views/tests/static.py
@@ -26,10 +26,18 @@ def test_serve(self):
for filename in media_files:
response = self.client.get('/views/%s/%s' % (self.prefix, filename))
file_path = path.join(media_dir, filename)
- self.assertEquals(open(file_path).read(), response.content)
- self.assertEquals(len(response.content), int(response['Content-Length']))
+ content = str(response.content)
+ self.assertEquals(open(file_path).read(), content)
+ self.assertEquals(len(content), int(response['Content-Length']))
self.assertEquals(mimetypes.guess_type(file_path)[1], response.get('Content-Encoding', None))
+ def test_serve_does_not_buffer_files(self):
+ "The static view uses an iterator - see #15281"
+ response = self.client.get('/views/%s/file.txt' % self.prefix)
+ # response objects can't be used as file-like objects when they are
+ # initialized with an iterator
+ self.assertRaises(Exception, response.write, 'more text')
+
def test_unknown_mime_type(self):
response = self.client.get('/views/%s/file.unknown' % self.prefix)
self.assertEquals('application/octet-stream', response['Content-Type'])
@@ -68,9 +76,9 @@ def test_invalid_if_modified_since(self):
response = self.client.get('/views/%s/%s' % (self.prefix, file_name),
HTTP_IF_MODIFIED_SINCE=invalid_date)
file = open(path.join(media_dir, file_name))
- self.assertEquals(file.read(), response.content)
- self.assertEquals(len(response.content),
- int(response['Content-Length']))
+ content = str(response.content)
+ self.assertEquals(file.read(), content)
+ self.assertEquals(len(content), int(response['Content-Length']))
def test_invalid_if_modified_since2(self):
"""Handle even more bogus If-Modified-Since values gracefully
@@ -82,10 +90,10 @@ def test_invalid_if_modified_since2(self):
invalid_date = ': 1291108438, Wed, 20 Oct 2010 14:05:00 GMT'
response = self.client.get('/views/%s/%s' % (self.prefix, file_name),
HTTP_IF_MODIFIED_SINCE=invalid_date)
+ content = str(response.content)
file = open(path.join(media_dir, file_name))
- self.assertEquals(file.read(), response.content)
- self.assertEquals(len(response.content),
- int(response['Content-Length']))
+ self.assertEquals(file.read(), content)
+ self.assertEquals(len(content), int(response['Content-Length']))
class StaticHelperTest(StaticTests):
Please sign in to comment.
Something went wrong with that request. Please try again.