diff --git a/AUTHORS b/AUTHORS index c983289fe2..1e061703c6 100644 --- a/AUTHORS +++ b/AUTHORS @@ -37,6 +37,7 @@ answer newbie questions, and generally made Django that much better: Arthur Jiri Barton Ned Batchelder + Shannon -jj Behrens James Bennett Paul Bissex Simon Blanchard diff --git a/django/views/static.py b/django/views/static.py index 536720f5e8..1499dd4847 100644 --- a/django/views/static.py +++ b/django/views/static.py @@ -2,9 +2,13 @@ import os import urllib import posixpath import mimetypes +import re +import rfc822 +import stat from django.core import template_loader from django.core.exceptions import Http404, ImproperlyConfigured -from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect +from django.utils.httpwrappers import HttpResponse, HttpResponseRedirect, \ + HttpResponseNotModified from django.core.template import Template, Context, TemplateDoesNotExist def serve(request, path, document_root=None, show_indexes=False): @@ -41,13 +45,19 @@ def serve(request, path, document_root=None, show_indexes=False): if os.path.isdir(fullpath): if show_indexes: return directory_index(newpath, fullpath) - else: - raise Http404, "Directory indexes are not allowed here." - elif not os.path.exists(fullpath): + raise Http404, "Directory indexes are not allowed here." + if not os.path.exists(fullpath): raise Http404, '"%s" does not exist' % fullpath - else: - mimetype = mimetypes.guess_type(fullpath)[0] - return HttpResponse(open(fullpath, 'rb').read(), mimetype=mimetype) + # Respect the If-Modified-Since header. + statobj = os.stat(fullpath) + if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), + statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]): + return HttpResponseNotModified() + mimetype = mimetypes.guess_type(fullpath)[0] + contents = open(fullpath, 'rb').read() + response = HttpResponse(contents, mimetype=mimetype) + response["Last-Modified"] = rfc822.formatdate(statobj[stat.ST_MTIME]) + return response DEFAULT_DIRECTORY_INDEX_TEMPLATE = """ @@ -85,3 +95,33 @@ def directory_index(path, fullpath): 'file_list' : files, }) return HttpResponse(t.render(c)) + +def was_modified_since(header=None, mtime=0, size=0): + """ + Was something modified since the user last downloaded it? + + header + This is the value of the If-Modified-Since header. If this is None, + I'll just return True. + + mtime + This is the modification time of the item we're talking about. + + size + This is the size of the item we're talking about. + """ + try: + if header is None: + raise ValueError + matches = re.match(r"^([^;]+)(; length=([0-9]+))?$", header, + re.IGNORECASE) + header_mtime = rfc822.mktime_tz(rfc822.parsedate_tz( + matches.group(1))) + header_len = matches.group(3) + if header_len and int(header_len) != size: + raise ValueError + if mtime > header_mtime: + raise ValueError + except (AttributeError, ValueError): + return True + return False