web/lib/django/utils/cache.py
author ymh <ymh.work@gmail.com>
Wed, 02 Jun 2010 18:57:35 +0200
changeset 38 77b6da96e6f1
permissions -rw-r--r--
update django
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
38
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
This module contains helper functions for controlling caching. It does so by
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
managing the "Vary" header of responses. It includes functions to patch the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
header of response objects directly and decorators that change functions to do
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
that header-patching themselves.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
For information on the Vary header, see:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
    http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
Essentially, the "Vary" HTTP header defines which headers a cache should take
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
into account when building its cache key. Requests with the same path but
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
different header content for headers named in "Vary" need to get different
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
cache keys to prevent delivery of wrong content.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
An example: i18n middleware would need to distinguish caches by the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
"Accept-language" header.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
"""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
import re
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
import time
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
from django.conf import settings
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
from django.core.cache import cache
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
from django.utils.encoding import smart_str, iri_to_uri
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
from django.utils.http import http_date
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
from django.utils.hashcompat import md5_constructor
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
from django.utils.translation import get_language
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
from django.http import HttpRequest
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
cc_delim_re = re.compile(r'\s*,\s*')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
def patch_cache_control(response, **kwargs):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
    This function patches the Cache-Control header by adding all
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
    keyword arguments to it. The transformation is as follows:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
    * All keyword parameter names are turned to lowercase, and underscores
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
      are converted to hyphens.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
    * If the value of a parameter is True (exactly True, not just a
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
      true value), only the parameter name is added to the header.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
    * All other parameters are added with their value, after applying
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
      str() to it.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
    def dictitem(s):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
        t = s.split('=', 1)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
        if len(t) > 1:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
            return (t[0].lower(), t[1])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
            return (t[0].lower(), True)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
    def dictvalue(t):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
        if t[1] is True:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
            return t[0]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
        else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
            return t[0] + '=' + smart_str(t[1])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
    if response.has_header('Cache-Control'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
        cc = cc_delim_re.split(response['Cache-Control'])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
        cc = dict([dictitem(el) for el in cc])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
    else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
        cc = {}
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
    # If there's already a max-age header but we're being asked to set a new
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
    # max-age, use the minimum of the two ages. In practice this happens when
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
    # a decorator and a piece of middleware both operate on a given view.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
    if 'max-age' in cc and 'max_age' in kwargs:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
        kwargs['max_age'] = min(cc['max-age'], kwargs['max_age'])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
    for (k, v) in kwargs.items():
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
        cc[k.replace('_', '-')] = v
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
    cc = ', '.join([dictvalue(el) for el in cc.items()])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
    response['Cache-Control'] = cc
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
def get_max_age(response):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
    Returns the max-age from the response Cache-Control header as an integer
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
    (or ``None`` if it wasn't found or wasn't an integer.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
    if not response.has_header('Cache-Control'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
        return
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
    cc = dict([_to_tuple(el) for el in
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
        cc_delim_re.split(response['Cache-Control'])])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
    if 'max-age' in cc:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
        try:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
            return int(cc['max-age'])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
        except (ValueError, TypeError):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
            pass
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
def patch_response_headers(response, cache_timeout=None):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
    Adds some useful headers to the given HttpResponse object:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
        ETag, Last-Modified, Expires and Cache-Control
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
    Each header is only added if it isn't already set.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
    cache_timeout is in seconds. The CACHE_MIDDLEWARE_SECONDS setting is used
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
    by default.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
    if cache_timeout is None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
        cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
    if cache_timeout < 0:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
        cache_timeout = 0 # Can't have max-age negative
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
    if not response.has_header('ETag'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
        response['ETag'] = '"%s"' % md5_constructor(response.content).hexdigest()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
    if not response.has_header('Last-Modified'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
        response['Last-Modified'] = http_date()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
    if not response.has_header('Expires'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
        response['Expires'] = http_date(time.time() + cache_timeout)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
    patch_cache_control(response, max_age=cache_timeout)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
def add_never_cache_headers(response):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
    Adds headers to a response to indicate that a page should never be cached.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
    patch_response_headers(response, cache_timeout=-1)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
def patch_vary_headers(response, newheaders):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
    Adds (or updates) the "Vary" header in the given HttpResponse object.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
    newheaders is a list of header names that should be in "Vary". Existing
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
    headers in "Vary" aren't removed.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
    # Note that we need to keep the original order intact, because cache
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
    # implementations may rely on the order of the Vary contents in, say,
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
    # computing an MD5 hash.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
    if response.has_header('Vary'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
        vary_headers = cc_delim_re.split(response['Vary'])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
    else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
        vary_headers = []
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
    # Use .lower() here so we treat headers as case-insensitive.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
    existing_headers = set([header.lower() for header in vary_headers])
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
    additional_headers = [newheader for newheader in newheaders
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
                          if newheader.lower() not in existing_headers]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
    response['Vary'] = ', '.join(vary_headers + additional_headers)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
def _i18n_cache_key_suffix(request, cache_key):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
    """If enabled, returns the cache key ending with a locale."""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
    if settings.USE_I18N:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
        # first check if LocaleMiddleware or another middleware added
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
        # LANGUAGE_CODE to request, then fall back to the active language
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
        # which in turn can also fall back to settings.LANGUAGE_CODE
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
        cache_key += '.%s' % getattr(request, 'LANGUAGE_CODE', get_language())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
    return cache_key
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
def _generate_cache_key(request, headerlist, key_prefix):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
    """Returns a cache key from the headers given in the header list."""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
    ctx = md5_constructor()
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
    for header in headerlist:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
        value = request.META.get(header, None)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
        if value is not None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
            ctx.update(value)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
    path = md5_constructor(iri_to_uri(request.path))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
    cache_key = 'views.decorators.cache.cache_page.%s.%s.%s' % (
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
        key_prefix, path.hexdigest(), ctx.hexdigest())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
    return _i18n_cache_key_suffix(request, cache_key)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   158
def _generate_cache_header_key(key_prefix, request):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   159
    """Returns a cache key for the header cache."""
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   160
    path = md5_constructor(iri_to_uri(request.path))
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   161
    cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   162
        key_prefix, path.hexdigest())
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   163
    return _i18n_cache_key_suffix(request, cache_key)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   164
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   165
def get_cache_key(request, key_prefix=None):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   166
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   167
    Returns a cache key based on the request path. It can be used in the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   168
    request phase because it pulls the list of headers to take into account
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   169
    from the global path registry and uses those to build a cache key to check
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   170
    against.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   171
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   172
    If there is no headerlist stored, the page needs to be rebuilt, so this
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   173
    function returns None.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   174
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   175
    if key_prefix is None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   176
        key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   177
    cache_key = _generate_cache_header_key(key_prefix, request)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   178
    headerlist = cache.get(cache_key, None)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   179
    if headerlist is not None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   180
        return _generate_cache_key(request, headerlist, key_prefix)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   181
    else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   182
        return None
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   183
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   184
def learn_cache_key(request, response, cache_timeout=None, key_prefix=None):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   185
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   186
    Learns what headers to take into account for some request path from the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   187
    response object. It stores those headers in a global path registry so that
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   188
    later access to that path will know what headers to take into account
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   189
    without building the response object itself. The headers are named in the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   190
    Vary header of the response, but we want to prevent response generation.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   191
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   192
    The list of headers to use for cache key generation is stored in the same
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   193
    cache as the pages themselves. If the cache ages some data out of the
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   194
    cache, this just means that we have to build the response once to get at
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   195
    the Vary header and so at the list of headers to use for the cache key.
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   196
    """
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   197
    if key_prefix is None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   198
        key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   199
    if cache_timeout is None:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   200
        cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   201
    cache_key = _generate_cache_header_key(key_prefix, request)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   202
    if response.has_header('Vary'):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   203
        headerlist = ['HTTP_'+header.upper().replace('-', '_')
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   204
                      for header in cc_delim_re.split(response['Vary'])]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   205
        cache.set(cache_key, headerlist, cache_timeout)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   206
        return _generate_cache_key(request, headerlist, key_prefix)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   207
    else:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   208
        # if there is no Vary header, we still need a cache key
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   209
        # for the request.path
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   210
        cache.set(cache_key, [], cache_timeout)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   211
        return _generate_cache_key(request, [], key_prefix)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   212
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   213
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   214
def _to_tuple(s):
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   215
    t = s.split('=',1)
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   216
    if len(t) == 2:
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   217
        return t[0].lower(), t[1]
77b6da96e6f1 update django
ymh <ymh.work@gmail.com>
parents:
diff changeset
   218
    return t[0].lower(), True