summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlavio Curella <flavio.curella@gmail.com>2019-09-26 13:58:14 -0700
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2019-10-10 19:30:51 +0200
commited112fadc1cfa400dbee6080bf82fd7536ea4c72 (patch)
treedc96114d797f815577a61c12abc899dbb8307277
parent2a6f45e08e8cb8c7e5157915c378b453109424d2 (diff)
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
-rw-r--r--django/utils/cache.py34
-rw-r--r--docs/ref/utils.txt5
-rw-r--r--docs/releases/3.1.txt5
-rw-r--r--docs/topics/cache.txt1
-rw-r--r--tests/cache/tests.py6
5 files changed, 43 insertions, 8 deletions
diff --git a/django/utils/cache.py b/django/utils/cache.py
index 3ee01a1e94..14e8256b94 100644
--- a/django/utils/cache.py
+++ b/django/utils/cache.py
@@ -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
diff --git a/docs/ref/utils.txt b/docs/ref/utils.txt
index 63db48b98f..4f174c5efe 100644
--- a/docs/ref/utils.txt
+++ b/docs/ref/utils.txt
@@ -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
diff --git a/docs/releases/3.1.txt b/docs/releases/3.1.txt
index 7fec7246d5..a92c18fc78 100644
--- a/docs/releases/3.1.txt
+++ b/docs/releases/3.1.txt
@@ -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
~~~~
diff --git a/docs/topics/cache.txt b/docs/topics/cache.txt
index 2bd25afd42..ce030d4221 100644
--- a/docs/topics/cache.txt
+++ b/docs/topics/cache.txt
@@ -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).
diff --git a/tests/cache/tests.py b/tests/cache/tests.py
index 648ffbc499..8182d5a8c0 100644
--- a/tests/cache/tests.py
+++ b/tests/cache/tests.py
@@ -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'}),