Ticket #13283: 13283_github_pull_4.diff

File 13283_github_pull_4.diff, 9.1 KB (added by natrius, 4 years ago)

Fix by caching all non-user-variable requests

  • django/middleware/cache.py

    diff --git a/django/middleware/cache.py b/django/middleware/cache.py
    index a3076ac..68345cc 100644
    a b More details about how the caching works: 
    5050
    5151from django.conf import settings
    5252from django.core.cache import cache
    53 from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
     53from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age, has_vary_header
    5454
    5555class UpdateCacheMiddleware(object):
    5656    """
    class UpdateCacheMiddleware(object): 
    6666        self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
    6767        self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
    6868
     69    def _should_update_cache(self, request, response):
     70        if not hasattr(request, '_cache_update_cache') or not request._cache_update_cache:
     71            return False
     72        if self.cache_anonymous_only and has_vary_header(response, 'Cookie'):
     73            assert hasattr(request, 'user'), "The Django cache middleware with CACHE_MIDDLEWARE_ANONYMOUS_ONLY=True requires authentication middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.auth.middleware.AuthenticationMiddleware' before the CacheMiddleware."
     74            if request.user.is_authenticated():
     75                # Don't cache user-variable requests from authenticated users.
     76                return False
     77        return True
     78
    6979    def process_response(self, request, response):
    7080        """Sets the cache, if needed."""
    71         if not hasattr(request, '_cache_update_cache') or not request._cache_update_cache:
     81        if not self._should_update_cache(request, response):
    7282            # We don't need to update the cache, just return.
    7383            return response
    7484        if not response.status_code == 200:
    class FetchFromCacheMiddleware(object): 
    106116        Checks whether the page is already cached and returns the cached
    107117        version if available.
    108118        """
    109         if self.cache_anonymous_only:
    110             assert hasattr(request, 'user'), "The Django cache middleware with CACHE_MIDDLEWARE_ANONYMOUS_ONLY=True requires authentication middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.auth.middleware.AuthenticationMiddleware' before the CacheMiddleware."
    111 
    112119        if not request.method in ('GET', 'HEAD') or request.GET:
    113120            request._cache_update_cache = False
    114121            return None # Don't bother checking the cache.
    115122
    116         if self.cache_anonymous_only and request.user.is_authenticated():
    117             request._cache_update_cache = False
    118             return None # Don't cache requests from authenticated users.
    119 
    120123        # try and get the cached GET response
    121124        cache_key = get_cache_key(request, self.key_prefix, 'GET')
    122125
  • django/utils/cache.py

    diff --git a/django/utils/cache.py b/django/utils/cache.py
    index b1b35b5..a2e7d6b 100644
    a b def patch_vary_headers(response, newheaders): 
    134134                          if newheader.lower() not in existing_headers]
    135135    response['Vary'] = ', '.join(vary_headers + additional_headers)
    136136
     137def has_vary_header(response, header_query):
     138    """
     139    Checks to see if the response has a given header name in its Vary header.
     140    """
     141    if not response.has_header('Vary'):
     142        return False
     143    vary_headers = cc_delim_re.split(response['Vary'])
     144    existing_headers = set([header.lower() for header in vary_headers])
     145    return header_query.lower() in existing_headers
     146
    137147def _i18n_cache_key_suffix(request, cache_key):
    138148    """If enabled, returns the cache key ending with a locale."""
    139149    if settings.USE_I18N:
  • docs/topics/cache.txt

    diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
    index f2b7a59..2fdc601 100644
    a b Then, add the following required settings to your Django settings file: 
    323323  collisions. Use an empty string if you don't care.
    324324
    325325The cache middleware caches every page that doesn't have GET or POST
    326 parameters. Optionally, if the ``CACHE_MIDDLEWARE_ANONYMOUS_ONLY`` setting is
    327 ``True``, only anonymous requests (i.e., not those made by a logged-in user)
    328 will be cached. This is a simple and effective way of disabling caching for any
    329 user-specific pages (include Django's admin interface). Note that if you use
    330 ``CACHE_MIDDLEWARE_ANONYMOUS_ONLY``, you should make sure you've activated
    331 ``AuthenticationMiddleware``. The cache middleware expects that a HEAD request
    332 is answered with the same response headers exactly like the corresponding GET
    333 request, in that case it could return cached GET response for HEAD request.
     326parameters. Pages are cached separately based on the values of the HTTP headers
     327they vary by, so user-specific pages will never be shown to the wrong person if
     328you're accessing the user's information via
     329:attr:`request.user <django.http.HttpRequest.user>`, which results in the
     330appropriate ``Vary`` headers being set. The cache middleware expects that a HEAD
     331request is answered with the same response headers exactly like the
     332corresponding GET request, in that case it could return cached GET response for
     333HEAD request.
    334334
    335335Additionally, the cache middleware automatically sets a few headers in each
    336336``HttpResponse``:
  • tests/regressiontests/cache/tests.py

    diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
    index da4c9a8..ee9c272 100644
    a b from django.core.cache import get_cache 
    1515from django.core.cache.backends.base import InvalidCacheBackendError, CacheKeyWarning
    1616from django.http import HttpResponse, HttpRequest
    1717from django.middleware.cache import FetchFromCacheMiddleware, UpdateCacheMiddleware
     18from django.test import TestCase
    1819from django.utils import translation
    1920from django.utils import unittest
    20 from django.utils.cache import patch_vary_headers, get_cache_key, learn_cache_key
     21from django.utils.cache import patch_vary_headers, get_cache_key, learn_cache_key, has_vary_header
    2122from django.utils.hashcompat import md5_constructor
    2223from regressiontests.cache.models import Poll, expensive_calculation
    2324
    class CacheUtils(unittest.TestCase): 
    544545            patch_vary_headers(response, newheaders)
    545546            self.assertEqual(response['Vary'], resulting_vary)
    546547
     548    def test_has_vary_header(self):
     549        possible_headers = ('cookie', 'accept-encoding')
     550        sample_values = (
     551            # Vary header value, has_vary_header value for the possible_headers
     552            ('Cookie', (True, False)),
     553            ('COOKIE', (True, False)),
     554            ('Accept-Encoding', (False, True)),
     555            ('Cookie, Accept-Encoding', (True, True)),
     556            ('Accept-Encoding, Cookie', (True, True)),
     557            ('Cookie    ,     Accept-Encoding', (True, True)),
     558        )
     559        for vary_header, results in sample_values:
     560            response = HttpResponse()
     561            response['Vary'] = vary_header
     562            for test_header, result in zip(possible_headers, results):
     563                self.assertEqual(has_vary_header(response, test_header), result)
     564
    547565    def test_get_cache_key(self):
    548566        request = self._get_request(self.path)
    549567        response = HttpResponse()
    class CacheI18nTest(unittest.TestCase): 
    718736        get_cache_data = FetchFromCacheMiddleware().process_request(request)
    719737        self.assertEqual(get_cache_data.content, es_message)
    720738
     739class CacheMiddlewareTests(TestCase):
     740    urls = 'regressiontests.cache.urls'
     741
     742    def setUp(self):
     743        self._orig_cache_middleware_anonymous_only = \
     744            getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
     745        self._orig_middleware_classes = settings.MIDDLEWARE_CLASSES
     746
     747        settings.MIDDLEWARE_CLASSES = list(settings.MIDDLEWARE_CLASSES)
     748        settings.MIDDLEWARE_CLASSES.insert(0, 'django.middleware.cache.UpdateCacheMiddleware')
     749        settings.MIDDLEWARE_CLASSES += ['django.middleware.cache.FetchFromCacheMiddleware']
     750
     751    def tearDown(self):
     752        settings.CACHE_MIDDLEWARE_ANONYMOUS_ONLY = self._orig_cache_middleware_anonymous_only
     753        settings.MIDDLEWARE_CLASSES = self._orig_middleware_classes
     754
     755    def test_cache_middleware_anonymous_only_does_not_cause_vary_cookie(self):
     756        settings.CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True
     757        response = self.client.get('/')
     758        self.failIf('Cookie' in response.get('Vary', ''))
     759
     760
    721761if __name__ == '__main__':
    722762    unittest.main()
  • new file tests/regressiontests/cache/urls.py

    diff --git a/tests/regressiontests/cache/urls.py b/tests/regressiontests/cache/urls.py
    new file mode 100644
    index 0000000..78b2125
    - +  
     1from django.conf.urls.defaults import *
     2
     3
     4urlpatterns = patterns('regressiontests.cache.views',
     5    (r'^$', 'home'),
     6)
  • new file tests/regressiontests/cache/views.py

    diff --git a/tests/regressiontests/cache/views.py b/tests/regressiontests/cache/views.py
    new file mode 100644
    index 0000000..d8dc8b6
    - +  
     1from django.http import HttpResponse
     2
     3
     4def home(request):
     5    return HttpResponse('Hello World!')
Back to Top