summaryrefslogtreecommitdiff
path: root/django/middleware/cache.py
blob: f3d03e657a8118ffae85ca86a43f8f20b91381c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
from django.conf import settings
from django.core.cache import cache
from django.utils.httpwrappers import HttpResponseNotModified
from django.utils.text import compress_string
import datetime, md5

class CacheMiddleware:
    """
    Cache middleware. If this is enabled, each Django-powered page will be
    cached for CACHE_MIDDLEWARE_SECONDS seconds. Cache is based on URLs. Pages
    with GET or POST parameters are not cached.

    If the cache is shared across multiple sites using the same Django
    installation, set the CACHE_MIDDLEWARE_KEY_PREFIX to the name of the site,
    or some other string that is unique to this Django instance, to prevent key
    collisions.

    This middleware will also make the following optimizations:

    * If the CACHE_MIDDLEWARE_GZIP setting is True, the content will be
      gzipped.

    * ETags will be added, using a simple MD5 hash of the page's content.
    """
    def process_request(self, request):
        """
        Checks whether the page is already cached. If it is, returns the cached
        version. Also handles ETag stuff.
        """
        if request.GET or request.POST:
            request._cache_middleware_set_cache = False
            return None # Don't bother checking the cache.

        accept_encoding = ''
        if settings.CACHE_MIDDLEWARE_GZIP:
            try:
                accept_encoding = request.META['HTTP_ACCEPT_ENCODING']
            except KeyError:
                pass
        accepts_gzip = 'gzip' in accept_encoding
        request._cache_middleware_accepts_gzip = accepts_gzip

        # This uses the same cache_key as views.decorators.cache.cache_page,
        # so the cache can be shared.
        cache_key = 'views.decorators.cache.cache_page.%s.%s.%s' % \
            (settings.CACHE_MIDDLEWARE_KEY_PREFIX, request.path, accepts_gzip)
        request._cache_middleware_key = cache_key

        response = cache.get(cache_key, None)
        if response is None:
            request._cache_middleware_set_cache = True
            return None
        else:
            request._cache_middleware_set_cache = False
            # Logic is from http://simon.incutio.com/archive/2003/04/23/conditionalGet
            try:
                if_none_match = request.META['HTTP_IF_NONE_MATCH']
            except KeyError:
                if_none_match = None
            try:
                if_modified_since = request.META['HTTP_IF_MODIFIED_SINCE']
            except KeyError:
                if_modified_since = None
            if if_none_match is None and if_modified_since is None:
                pass
            elif if_none_match is not None and response['ETag'] != if_none_match:
                pass
            elif if_modified_since is not None and response['Last-Modified'] != if_modified_since:
                pass
            else:
                return HttpResponseNotModified()
        return response

    def process_response(self, request, response):
        """
        Sets the cache, if needed.
        """
        if request._cache_middleware_set_cache:
            content = response.get_content_as_string('utf-8')
            if request._cache_middleware_accepts_gzip:
                content = compress_string(content)
                response.content = content
                response['Content-Encoding'] = 'gzip'
            response['ETag'] = md5.new(content).hexdigest()
            response['Content-Length'] = '%d' % len(content)
            response['Last-Modified'] = datetime.datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
            cache.set(request._cache_middleware_key, response, settings.CACHE_MIDDLEWARE_SECONDS)
        return response