Skip to content

Commit a954e24

Browse files
committed
Fix behaviour of Cache-Control: no-store on responses
According to RFC 7234, section 5.2.2.3: Response Cache-Control Directives, no-store: "The "no-store" response directive indicates that a cache MUST NOT store any part of either the immediate request or response. This directive applies to both private and shared caches. "MUST NOT store" in this context means that the cache MUST NOT intentionally store the information in non-volatile storage, and MUST make a best-effort attempt to remove the information from volatile storage as promptly as possible after forwarding it.[...]" https://tools.ietf.org/html/rfc7234#section-5.2.2.3 The second part ("[...] MUST make a best-effort attempt to remove the information [...]") was already handled, but if any other conditions for caching responses became true, for example presence of Etags, permanent redirect etc. the response was cached even with Cache-Control: no-store. This behaviour is fixed by just returning if the no_store flag is set, potentially after having purged the cache entry.
1 parent dc4e179 commit a954e24

File tree

2 files changed

+18
-0
lines changed

2 files changed

+18
-0
lines changed

cachecontrol/controller.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ def cache_response(self, request, response, body=None,
287287
if no_store and self.cache.get(cache_url):
288288
logger.debug('Purging existing cache entry to honor "no-store"')
289289
self.cache.delete(cache_url)
290+
if no_store:
291+
return
290292

291293
# If we've been given an etag, then keep the response
292294
if self.cache_etags and 'etag' in response_headers:

tests/test_cache_control.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,22 @@ def test_cache_response_no_store(self):
115115
cc.cache_response(self.req(), resp)
116116
assert not cc.cache.get(cache_url)
117117

118+
def test_cache_response_no_store_with_etag(self):
119+
resp = Mock()
120+
cache = DictCache({self.url: resp})
121+
cc = CacheController(cache)
122+
123+
cache_url = cc.cache_url(self.url)
124+
125+
resp = self.resp({'cache-control': 'no-store', 'ETag': 'jfd9094r808'})
126+
assert cc.cache.get(cache_url)
127+
128+
# skip serializer as it can't handle mocks
129+
cc.serializer = Mock()
130+
cc.serializer.loads.return_value = resp
131+
cc.cache_response(self.req(), resp)
132+
assert not cc.cache.get(cache_url)
133+
118134
def test_update_cached_response_with_valid_headers(self):
119135
cached_resp = Mock(headers={'ETag': 'jfd9094r808', 'Content-Length': 100})
120136

0 commit comments

Comments
 (0)