Ticket #14560: fix_head_cache.diff

File fix_head_cache.diff, 6.7 KB (added by Maxim Bublis, 13 years ago)

Patch that fixes caching of HEAD requests, provides tests and docs.

  • django/middleware/cache.py

     
    3434  and effective way of avoiding the caching of the Django admin (and any other
    3535  user-specific content).
    3636
    37 * This middleware expects that a HEAD request is answered with a response
    38   exactly like the corresponding GET request.
     37* This middleware expects that a HEAD request is answered with the same response
     38  headers exactly like the corresponding GET request.
    3939
    4040* When a hit occurs, a shallow copy of the original response object is returned
    4141  from process_request.
     
    7171        if not hasattr(request, '_cache_update_cache') or not request._cache_update_cache:
    7272            # We don't need to update the cache, just return.
    7373            return response
    74         if request.method != 'GET':
    75             # This is a stronger requirement than above. It is needed
    76             # because of interactions between this middleware and the
    77             # HTTPMiddleware, which throws the body of a HEAD-request
    78             # away before this middleware gets a chance to cache it.
    79             return response
    8074        if not response.status_code == 200:
    8175            return response
    8276        # Try to get the timeout from the "max-age" section of the "Cache-
     
    9084            return response
    9185        patch_response_headers(response, timeout)
    9286        if timeout:
    93             cache_key = learn_cache_key(request, response, timeout, self.key_prefix)
     87            cache_key = learn_cache_key(request, response, timeout, '%s.%s' % (self.key_prefix, request.method))
    9488            cache.set(cache_key, response, timeout)
    9589        return response
    9690
     
    123117            request._cache_update_cache = False
    124118            return None # Don't cache requests from authenticated users.
    125119
    126         cache_key = get_cache_key(request, self.key_prefix)
    127         if cache_key is None:
    128             request._cache_update_cache = True
    129             return None # No cache information available, need to rebuild.
     120        possible_cache_keys = [get_cache_key(request, '%s.%s' % (self.key_prefix, request.method))]
    130121
    131         response = cache.get(cache_key, None)
    132         if response is None:
    133             request._cache_update_cache = True
    134             return None # No cache information available, need to rebuild.
     122        if request.method == 'HEAD':
     123            possible_cache_keys.append(get_cache_key(request, '%s.%s' % (self.key_prefix, 'GET')))
    135124
    136         request._cache_update_cache = False
    137         return response
     125        for cache_key in possible_cache_keys:
     126            if cache_key is None:
     127                continue # Check another cache key.
    138128
     129            response = cache.get(cache_key, None)
     130            if response is None:
     131                continue # Check another cache key.
     132
     133            request._cache_update_cache = False
     134            return response
     135
     136        request.cache_update_cache = True
     137        return None # No cache information available, need to rebuild.
     138
    139139class CacheMiddleware(UpdateCacheMiddleware, FetchFromCacheMiddleware):
    140140    """
    141141    Cache middleware that provides basic behavior for many simple sites.
  • tests/regressiontests/cache/tests.py

     
    557557        learn_cache_key(request, response)
    558558        self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
    559559
     560class CacheHEADTest(unittest.TestCase):
     561
     562    def setUp(self):
     563        self.orig_cache_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS
     564        self.orig_cache_middleware_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
     565        self.orig_cache_backend = settings.CACHE_BACKEND
     566        settings.CACHE_MIDDLEWARE_SECONDS = 60
     567        settings.CACHE_MIDDLEWARE_KEY_PREFIX = 'test'
     568        settings.CACHE_BACKEND = 'locmem:///'
     569        self.path = '/cache/test/'
     570
     571    def tearDown(self):
     572        settings.CACHE_MIDDLEWARE_SECONDS = self.orig_cache_middleware_seconds
     573        settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.orig_cache_middleware_key_prefix
     574        settings.CACHE_BACKEND = self.orig_cache_backend
     575
     576    def _get_request(self, method):
     577        request = HttpRequest()
     578        request.META = {
     579            'SERVER_NAME': 'testserver',
     580            'SERVER_PORT': 80,
     581        }
     582        request.method = method
     583        request.path = request.path_info = self.path
     584        return request
     585
     586    def _get_request_cache(self, method):
     587        request = self._get_request(method)
     588        request._cache_update_cache = True
     589        return request
     590
     591    def _set_cache(self, request, msg):
     592        response = HttpResponse()
     593        response.content = msg
     594        return UpdateCacheMiddleware().process_response(request, response)
     595
     596    def test_head_caches_correctly(self):
     597        test_content = 'test content'
     598
     599        request = self._get_request_cache('HEAD')
     600        self._set_cache(request, test_content)
     601
     602        request = self._get_request('HEAD')
     603        get_cache_data = FetchFromCacheMiddleware().process_request(request)
     604        self.assertNotEqual(get_cache_data, None)
     605        self.assertEqual(test_content, get_cache_data.content)
     606
     607    def test_head_with_cached_get(self):
     608        test_content = 'test content'
     609
     610        request = self._get_request_cache('GET')
     611        self._set_cache(request, test_content)
     612
     613        request = self._get_request('HEAD')
     614        get_cache_data = FetchFromCacheMiddleware().process_request(request)
     615        self.assertNotEqual(get_cache_data, None)
     616        self.assertEqual(test_content, get_cache_data.content)
     617
    560618class CacheI18nTest(unittest.TestCase):
    561619
    562620    def setUp(self):
  • docs/topics/cache.txt

     
    328328will be cached. This is a simple and effective way of disabling caching for any
    329329user-specific pages (include Django's admin interface). Note that if you use
    330330``CACHE_MIDDLEWARE_ANONYMOUS_ONLY``, you should make sure you've activated
    331 ``AuthenticationMiddleware``.
     331``AuthenticationMiddleware``. The cache middleware expects that a HEAD request
     332is answered with the same response headers exactly like the corresponding GET
     333request, in that case it could return cached GET response for HEAD request.
    332334
    333335Additionally, the cache middleware automatically sets a few headers in each
    334336``HttpResponse``:
Back to Top