Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[1.3.X] Fixed #15499 -- Ensure that cache control headers don't try t…

…o set public and private as a result of multiple calls to patch_cache_control with different arguments. Thanks to AndiDog for the report and patch.

Backport of r16657 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.3.X@16673 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit 3e7d79b6ace41587ce4a63f688caa34447f4aef6 1 parent e9a1c03
@freakboy3742 freakboy3742 authored
View
6 django/utils/cache.py
@@ -67,6 +67,12 @@ def dictvalue(t):
if 'max-age' in cc and 'max_age' in kwargs:
kwargs['max_age'] = min(cc['max-age'], kwargs['max_age'])
+ # Allow overriding private caching and vice versa
+ if 'private' in cc and 'public' in kwargs:
+ del cc['private']
+ elif 'public' in cc and 'private' in kwargs:
+ del cc['public']
+
for (k, v) in kwargs.items():
cc[k.replace('_', '-')] = v
cc = ', '.join([dictvalue(el) for el in cc.items()])
View
22 docs/topics/cache.txt
@@ -1062,6 +1062,28 @@ Django, use the ``cache_control`` view decorator. Example::
This decorator takes care of sending out the appropriate HTTP header behind the
scenes.
+Note that the cache control settings "private" and "public" are mutually
+exclusive. The decorator ensures that the "public" directive is removed if
+"private" should be set (and vice versa). An example use of the two directives
+would be a blog site that offers both private and public entries. Public
+entries may be cached on any shared cache. The following code uses
+``patch_cache_control``, the manual way to modify the cache control header
+(it is internally called by the ``cache_control`` decorator)::
+
+ from django.views.decorators.cache import patch_cache_control
+ from django.views.decorators.vary import vary_on_cookie
+
+ @vary_on_cookie
+ def list_blog_entries_view(request):
+ if request.user.is_anonymous():
+ response = render_only_public_entries()
+ patch_cache_control(response, public=True)
+ else:
+ response = render_private_and_public_entries(request.user)
+ patch_cache_control(response, private=True)
+
+ return response
+
There are a few other ways to control cache parameters. For example, HTTP
allows applications to do the following:
View
28 tests/regressiontests/cache/tests.py
@@ -4,6 +4,7 @@
# Uses whatever cache backend is set in the test settings file.
import os
+import re
import tempfile
import time
import warnings
@@ -18,7 +19,7 @@
from django.test.utils import get_warnings_state, restore_warnings_state
from django.utils import translation
from django.utils import unittest
-from django.utils.cache import patch_vary_headers, get_cache_key, learn_cache_key
+from django.utils.cache import patch_vary_headers, get_cache_key, learn_cache_key, patch_cache_control
from django.utils.hashcompat import md5_constructor
from django.views.decorators.cache import cache_page
@@ -970,6 +971,31 @@ def test_learn_cache_key(self):
learn_cache_key(request, response)
self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.HEAD.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
+ def test_patch_cache_control(self):
+ tests = (
+ # Initial Cache-Control, kwargs to patch_cache_control, expected Cache-Control parts
+ (None, {'private' : True}, set(['private'])),
+
+ # Test whether private/public attributes are mutually exclusive
+ ('private', {'private' : True}, set(['private'])),
+ ('private', {'public' : True}, set(['public'])),
+ ('public', {'public' : True}, set(['public'])),
+ ('public', {'private' : True}, set(['private'])),
+ ('must-revalidate,max-age=60,private', {'public' : True}, set(['must-revalidate', 'max-age=60', 'public'])),
+ ('must-revalidate,max-age=60,public', {'private' : True}, set(['must-revalidate', 'max-age=60', 'private'])),
+ ('must-revalidate,max-age=60', {'public' : True}, set(['must-revalidate', 'max-age=60', 'public'])),
+ )
+
+ cc_delim_re = re.compile(r'\s*,\s*')
+
+ for initial_cc, newheaders, expected_cc in tests:
+ response = HttpResponse()
+ if initial_cc is not None:
+ response['Cache-Control'] = initial_cc
+ patch_cache_control(response, **newheaders)
+ parts = set(cc_delim_re.split(response['Cache-Control']))
+ self.assertEqual(parts, expected_cc)
+
class PrefixedCacheUtils(CacheUtils):
def setUp(self):
super(PrefixedCacheUtils, self).setUp()
Please sign in to comment.
Something went wrong with that request. Please try again.