Fixed #23755 -- Added support for multiple field names in the no-cache Cache-Control directive to patch_cache_control().

https://tools.ietf.org/html/rfc7234#section-5.2.2.2
This commit is contained in:
Flavio Curella 2019-09-26 13:58:14 -07:00 committed by Mariusz Felisiak
parent 2a6f45e08e
commit ed112fadc1
5 changed files with 43 additions and 8 deletions

View File

@ -19,6 +19,7 @@ An example: i18n middleware would need to distinguish caches by the
import hashlib
import re
import time
from collections import defaultdict
from django.conf import settings
from django.core.cache import caches
@ -53,17 +54,21 @@ def patch_cache_control(response, **kwargs):
else:
return (t[0].lower(), True)
def dictvalue(t):
def dictvalue(*t):
if t[1] is True:
return t[0]
else:
return '%s=%s' % (t[0], t[1])
cc = defaultdict(set)
if response.get('Cache-Control'):
cc = cc_delim_re.split(response['Cache-Control'])
cc = dict(dictitem(el) for el in cc)
else:
cc = {}
for field in cc_delim_re.split(response['Cache-Control']):
directive, value = dictitem(field)
if directive == 'no-cache':
# no-cache supports multiple field names.
cc[directive].add(value)
else:
cc[directive] = value
# If there's already a max-age header but we're being asked to set a new
# max-age, use the minimum of the two ages. In practice this happens when
@ -78,8 +83,23 @@ def patch_cache_control(response, **kwargs):
del cc['public']
for (k, v) in kwargs.items():
cc[k.replace('_', '-')] = v
cc = ', '.join(dictvalue(el) for el in cc.items())
directive = k.replace('_', '-')
if directive == 'no-cache':
# no-cache supports multiple field names.
cc[directive].add(v)
else:
cc[directive] = v
directives = []
for directive, values in cc.items():
if isinstance(values, set):
if True in values:
# True takes precedence.
values = {True}
directives.extend([dictvalue(directive, value) for value in values])
else:
directives.append(dictvalue(directive, values))
cc = ', '.join(directives)
response['Cache-Control'] = cc

View File

@ -43,6 +43,11 @@ need to distinguish caches by the ``Accept-language`` header.
* All other parameters are added with their value, after applying
``str()`` to it.
.. versionchanged:: 3.1
Support for multiple field names in the ``no-cache`` directive was
added.
.. function:: get_max_age(response)
Returns the max-age from the response Cache-Control header as an integer

View File

@ -104,7 +104,10 @@ Minor features
Cache
~~~~~
* ...
* The :func:`~django.views.decorators.cache.cache_control` decorator and
:func:`~django.utils.cache.patch_cache_control` method now support multiple
field names in the ``no-cache`` directive for the ``Cache-Control`` header,
according to :rfc:`7234#section-5.2.2.2`.
CSRF
~~~~

View File

@ -1277,6 +1277,7 @@ Here are some more examples:
* ``no_transform=True``
* ``must_revalidate=True``
* ``stale_while_revalidate=num_seconds``
* ``no_cache=True``
The full list of known directives can be found in the `IANA registry`_
(note that not all of them apply to responses).

View File

@ -1705,6 +1705,12 @@ class CacheUtils(SimpleTestCase):
('', {'no-cache': 'Set-Cookie'}, {'no-cache=Set-Cookie'}),
('no-cache=Set-Cookie', {'no_cache': True}, {'no-cache'}),
('no-cache=Set-Cookie,no-cache=Link', {'no_cache': True}, {'no-cache'}),
('no-cache=Set-Cookie', {'no_cache': 'Link'}, {'no-cache=Set-Cookie', 'no-cache=Link'}),
(
'no-cache=Set-Cookie,no-cache=Link',
{'no_cache': 'Custom'},
{'no-cache=Set-Cookie', 'no-cache=Link', 'no-cache=Custom'},
),
# Test whether private/public attributes are mutually exclusive
('private', {'private': True}, {'private'}),
('private', {'public': True}, {'public'}),