Fixed #15499 -- Ensure that cache control headers don't try to 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.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@16657 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
2e56066a5b
commit
6cd9023635
|
@ -66,6 +66,12 @@ def patch_cache_control(response, **kwargs):
|
||||||
if 'max-age' in cc and 'max_age' in kwargs:
|
if 'max-age' in cc and 'max_age' in kwargs:
|
||||||
kwargs['max_age'] = min(cc['max-age'], kwargs['max_age'])
|
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():
|
for (k, v) in kwargs.items():
|
||||||
cc[k.replace('_', '-')] = v
|
cc[k.replace('_', '-')] = v
|
||||||
cc = ', '.join([dictvalue(el) for el in cc.items()])
|
cc = ', '.join([dictvalue(el) for el in cc.items()])
|
||||||
|
|
|
@ -1059,6 +1059,28 @@ Django, use the ``cache_control`` view decorator. Example::
|
||||||
This decorator takes care of sending out the appropriate HTTP header behind the
|
This decorator takes care of sending out the appropriate HTTP header behind the
|
||||||
scenes.
|
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
|
There are a few other ways to control cache parameters. For example, HTTP
|
||||||
allows applications to do the following:
|
allows applications to do the following:
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
import warnings
|
import warnings
|
||||||
|
@ -19,7 +20,7 @@ from django.test import RequestFactory
|
||||||
from django.test.utils import get_warnings_state, restore_warnings_state
|
from django.test.utils import get_warnings_state, restore_warnings_state
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
from django.utils import unittest
|
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.views.decorators.cache import cache_page
|
from django.views.decorators.cache import cache_page
|
||||||
|
|
||||||
from regressiontests.cache.models import Poll, expensive_calculation
|
from regressiontests.cache.models import Poll, expensive_calculation
|
||||||
|
@ -1003,6 +1004,31 @@ class CacheUtils(unittest.TestCase):
|
||||||
learn_cache_key(request, response)
|
learn_cache_key(request, response)
|
||||||
self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.HEAD.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
|
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):
|
class PrefixedCacheUtils(CacheUtils):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(PrefixedCacheUtils, self).setUp()
|
super(PrefixedCacheUtils, self).setUp()
|
||||||
|
|
Loading…
Reference in New Issue