cache.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. """
  2. This module contains helper functions for controlling caching. It does so by
  3. managing the "Vary" header of responses. It includes functions to patch the
  4. header of response objects directly and decorators that change functions to do
  5. that header-patching themselves.
  6. For information on the Vary header, see RFC 9110 Section 12.5.5.
  7. Essentially, the "Vary" HTTP header defines which headers a cache should take
  8. into account when building its cache key. Requests with the same path but
  9. different header content for headers named in "Vary" need to get different
  10. cache keys to prevent delivery of wrong content.
  11. An example: i18n middleware would need to distinguish caches by the
  12. "Accept-language" header.
  13. """
  14. import time
  15. from collections import defaultdict
  16. from hashlib import md5
  17. from django.conf import settings
  18. from django.core.cache import caches
  19. from django.http import HttpResponse, HttpResponseNotModified
  20. from django.utils.http import (
  21. http_date,
  22. parse_etags,
  23. parse_http_date_safe,
  24. quote_etag,
  25. split_header_value,
  26. )
  27. from django.utils.log import log_response
  28. from django.utils.timezone import get_current_timezone_name
  29. from django.utils.translation import get_language
  30. def patch_cache_control(response, **kwargs):
  31. """
  32. Patch the Cache-Control header by adding all keyword arguments to it.
  33. The transformation is as follows:
  34. * All keyword parameter names are turned to lowercase, and underscores
  35. are converted to hyphens.
  36. * If the value of a parameter is True (exactly True, not just a
  37. true value), only the parameter name is added to the header.
  38. * All other parameters are added with their value, after applying
  39. str() to it.
  40. """
  41. def dictitem(s):
  42. t = s.split("=", 1)
  43. if len(t) > 1:
  44. return (t[0].strip().lower(), t[1])
  45. else:
  46. return (t[0].lower(), True)
  47. def dictvalue(*t):
  48. if t[1] is True:
  49. return t[0]
  50. else:
  51. return "%s=%s" % (t[0], t[1])
  52. cc = defaultdict(set)
  53. if response.get("Cache-Control"):
  54. for field in split_header_value(response.headers["Cache-Control"]):
  55. directive, value = dictitem(field)
  56. if directive == "no-cache":
  57. # no-cache supports multiple field names.
  58. cc[directive].add(value)
  59. else:
  60. cc[directive] = value
  61. # If there's already a max-age header but we're being asked to set a new
  62. # max-age, use the minimum of the two ages. In practice this happens when
  63. # a decorator and a piece of middleware both operate on a given view.
  64. if "max-age" in cc and "max_age" in kwargs:
  65. kwargs["max_age"] = min(int(cc["max-age"]), kwargs["max_age"])
  66. # Allow overriding private caching and vice versa
  67. if "private" in cc and "public" in kwargs:
  68. del cc["private"]
  69. elif "public" in cc and "private" in kwargs:
  70. del cc["public"]
  71. for k, v in kwargs.items():
  72. directive = k.replace("_", "-")
  73. if directive == "no-cache":
  74. # no-cache supports multiple field names.
  75. cc[directive].add(v)
  76. else:
  77. cc[directive] = v
  78. directives = []
  79. for directive, values in cc.items():
  80. if isinstance(values, set):
  81. if True in values:
  82. # True takes precedence.
  83. values = {True}
  84. directives.extend([dictvalue(directive, value) for value in values])
  85. else:
  86. directives.append(dictvalue(directive, values))
  87. cc = ", ".join(directives)
  88. response.headers["Cache-Control"] = cc
  89. def get_max_age(response):
  90. """
  91. Return the max-age from the response Cache-Control header as an integer,
  92. or None if it wasn't found or wasn't an integer.
  93. """
  94. if not response.has_header("Cache-Control"):
  95. return
  96. cc = dict(
  97. _to_tuple(el) for el in split_header_value(response.headers["Cache-Control"])
  98. )
  99. try:
  100. return int(cc["max-age"])
  101. except (ValueError, TypeError, KeyError):
  102. pass
  103. def set_response_etag(response):
  104. if not response.streaming and response.content:
  105. response.headers["ETag"] = quote_etag(
  106. md5(response.content, usedforsecurity=False).hexdigest(),
  107. )
  108. return response
  109. def _precondition_failed(request):
  110. response = HttpResponse(status=412)
  111. log_response(
  112. "Precondition Failed: %s",
  113. request.path,
  114. response=response,
  115. request=request,
  116. )
  117. return response
  118. def _not_modified(request, response=None):
  119. new_response = HttpResponseNotModified()
  120. if response:
  121. # Preserve the headers required by RFC 9110 Section 15.4.5, as well as
  122. # Last-Modified.
  123. for header in (
  124. "Cache-Control",
  125. "Content-Location",
  126. "Date",
  127. "ETag",
  128. "Expires",
  129. "Last-Modified",
  130. "Vary",
  131. ):
  132. if header in response:
  133. new_response.headers[header] = response.headers[header]
  134. # Preserve cookies as per the cookie specification: "If a proxy server
  135. # receives a response which contains a Set-cookie header, it should
  136. # propagate the Set-cookie header to the client, regardless of whether
  137. # the response was 304 (Not Modified) or 200 (OK).
  138. # https://curl.haxx.se/rfc/cookie_spec.html
  139. new_response.cookies = response.cookies
  140. return new_response
  141. def get_conditional_response(request, etag=None, last_modified=None, response=None):
  142. # Only return conditional responses on successful requests.
  143. if response and not (200 <= response.status_code < 300):
  144. return response
  145. # Get HTTP request headers.
  146. if_match_etags = parse_etags(request.META.get("HTTP_IF_MATCH", ""))
  147. if_unmodified_since = request.META.get("HTTP_IF_UNMODIFIED_SINCE")
  148. if_unmodified_since = if_unmodified_since and parse_http_date_safe(
  149. if_unmodified_since
  150. )
  151. if_none_match_etags = parse_etags(request.META.get("HTTP_IF_NONE_MATCH", ""))
  152. if_modified_since = request.META.get("HTTP_IF_MODIFIED_SINCE")
  153. if_modified_since = if_modified_since and parse_http_date_safe(if_modified_since)
  154. # Evaluation of request preconditions below follows RFC 9110 Section
  155. # 13.2.2.
  156. # Step 1: Test the If-Match precondition.
  157. if if_match_etags and not _if_match_passes(etag, if_match_etags):
  158. return _precondition_failed(request)
  159. # Step 2: Test the If-Unmodified-Since precondition.
  160. if (
  161. not if_match_etags
  162. and if_unmodified_since
  163. and not _if_unmodified_since_passes(last_modified, if_unmodified_since)
  164. ):
  165. return _precondition_failed(request)
  166. # Step 3: Test the If-None-Match precondition.
  167. if if_none_match_etags and not _if_none_match_passes(etag, if_none_match_etags):
  168. if request.method in ("GET", "HEAD"):
  169. return _not_modified(request, response)
  170. else:
  171. return _precondition_failed(request)
  172. # Step 4: Test the If-Modified-Since precondition.
  173. if (
  174. not if_none_match_etags
  175. and if_modified_since
  176. and not _if_modified_since_passes(last_modified, if_modified_since)
  177. and request.method in ("GET", "HEAD")
  178. ):
  179. return _not_modified(request, response)
  180. # Step 5: Test the If-Range precondition (not supported).
  181. # Step 6: Return original response since there isn't a conditional
  182. # response.
  183. return response
  184. def _if_match_passes(target_etag, etags):
  185. """
  186. Test the If-Match comparison as defined in RFC 9110 Section 13.1.1.
  187. """
  188. if not target_etag:
  189. # If there isn't an ETag, then there can't be a match.
  190. return False
  191. elif etags == ["*"]:
  192. # The existence of an ETag means that there is "a current
  193. # representation for the target resource", even if the ETag is weak,
  194. # so there is a match to '*'.
  195. return True
  196. elif target_etag.startswith("W/"):
  197. # A weak ETag can never strongly match another ETag.
  198. return False
  199. else:
  200. # Since the ETag is strong, this will only return True if there's a
  201. # strong match.
  202. return target_etag in etags
  203. def _if_unmodified_since_passes(last_modified, if_unmodified_since):
  204. """
  205. Test the If-Unmodified-Since comparison as defined in RFC 9110 Section
  206. 13.1.4.
  207. """
  208. return last_modified and last_modified <= if_unmodified_since
  209. def _if_none_match_passes(target_etag, etags):
  210. """
  211. Test the If-None-Match comparison as defined in RFC 9110 Section 13.1.2.
  212. """
  213. if not target_etag:
  214. # If there isn't an ETag, then there isn't a match.
  215. return True
  216. elif etags == ["*"]:
  217. # The existence of an ETag means that there is "a current
  218. # representation for the target resource", so there is a match to '*'.
  219. return False
  220. else:
  221. # The comparison should be weak, so look for a match after stripping
  222. # off any weak indicators.
  223. target_etag = target_etag.strip("W/")
  224. etags = (etag.strip("W/") for etag in etags)
  225. return target_etag not in etags
  226. def _if_modified_since_passes(last_modified, if_modified_since):
  227. """
  228. Test the If-Modified-Since comparison as defined in RFC 9110 Section
  229. 13.1.3.
  230. """
  231. return not last_modified or last_modified > if_modified_since
  232. def patch_response_headers(response, cache_timeout=None):
  233. """
  234. Add HTTP caching headers to the given HttpResponse: Expires and
  235. Cache-Control.
  236. Each header is only added if it isn't already set.
  237. cache_timeout is in seconds. The CACHE_MIDDLEWARE_SECONDS setting is used
  238. by default.
  239. """
  240. if cache_timeout is None:
  241. cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
  242. if cache_timeout < 0:
  243. cache_timeout = 0 # Can't have max-age negative
  244. if not response.has_header("Expires"):
  245. response.headers["Expires"] = http_date(time.time() + cache_timeout)
  246. patch_cache_control(response, max_age=cache_timeout)
  247. def add_never_cache_headers(response):
  248. """
  249. Add headers to a response to indicate that a page should never be cached.
  250. """
  251. patch_response_headers(response, cache_timeout=-1)
  252. patch_cache_control(
  253. response, no_cache=True, no_store=True, must_revalidate=True, private=True
  254. )
  255. def patch_vary_headers(response, newheaders):
  256. """
  257. Add (or update) the "Vary" header in the given HttpResponse object.
  258. newheaders is a list of header names that should be in "Vary". If headers
  259. contains an asterisk, then "Vary" header will consist of a single asterisk
  260. '*'. Otherwise, existing headers in "Vary" aren't removed.
  261. """
  262. # Note that we need to keep the original order intact, because cache
  263. # implementations may rely on the order of the Vary contents in, say,
  264. # computing an MD5 hash.
  265. if response.has_header("Vary"):
  266. vary_headers = list(split_header_value(response.headers["Vary"]))
  267. else:
  268. vary_headers = []
  269. # Use .lower() here so we treat headers as case-insensitive.
  270. existing_headers = {header.lower() for header in vary_headers}
  271. newheaders = [newheader.strip() for newheader in newheaders]
  272. additional_headers = [
  273. newheader
  274. for newheader in newheaders
  275. if newheader.lower() not in existing_headers
  276. ]
  277. vary_headers += additional_headers
  278. if "*" in vary_headers:
  279. response.headers["Vary"] = "*"
  280. else:
  281. response.headers["Vary"] = ", ".join(vary_headers)
  282. def has_vary_header(response, header_query):
  283. """
  284. Check to see if the response has a given header name in its Vary header.
  285. """
  286. if not response.has_header("Vary"):
  287. return False
  288. existing_headers = {h.lower() for h in split_header_value(response.headers["Vary"])}
  289. return header_query.lower().strip() in existing_headers
  290. def _i18n_cache_key_suffix(request, cache_key):
  291. """If necessary, add the current locale or time zone to the cache key."""
  292. if settings.USE_I18N:
  293. # first check if LocaleMiddleware or another middleware added
  294. # LANGUAGE_CODE to request, then fall back to the active language
  295. # which in turn can also fall back to settings.LANGUAGE_CODE
  296. cache_key += ".%s" % getattr(request, "LANGUAGE_CODE", get_language())
  297. if settings.USE_TZ:
  298. cache_key += ".%s" % get_current_timezone_name()
  299. return cache_key
  300. def _generate_cache_key(request, method, headerlist, key_prefix):
  301. """Return a cache key from the headers given in the header list."""
  302. ctx = md5(usedforsecurity=False)
  303. for header in headerlist:
  304. value = request.META.get(header)
  305. if value is None:
  306. value = ""
  307. data = value.encode()
  308. # Use the netstring delimiter (with trailing comma).
  309. ctx.update(b"%d:%s," % (len(data), data))
  310. url = md5(request.build_absolute_uri().encode("ascii"), usedforsecurity=False)
  311. cache_key = "views.decorators.cache.cache_page.%s.%s.%s.%s" % (
  312. key_prefix,
  313. method,
  314. url.hexdigest(),
  315. ctx.hexdigest(),
  316. )
  317. return _i18n_cache_key_suffix(request, cache_key)
  318. def _generate_cache_header_key(key_prefix, request):
  319. """Return a cache key for the header cache."""
  320. url = md5(request.build_absolute_uri().encode("ascii"), usedforsecurity=False)
  321. cache_key = "views.decorators.cache.cache_header.%s.%s" % (
  322. key_prefix,
  323. url.hexdigest(),
  324. )
  325. return _i18n_cache_key_suffix(request, cache_key)
  326. def get_cache_key(request, key_prefix=None, method="GET", cache=None):
  327. """
  328. Return a cache key based on the request URL and query. It can be used
  329. in the request phase because it pulls the list of headers to take into
  330. account from the global URL registry and uses those to build a cache key
  331. to check against.
  332. If there isn't a headerlist stored, return None, indicating that the page
  333. needs to be rebuilt.
  334. """
  335. if key_prefix is None:
  336. key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
  337. cache_key = _generate_cache_header_key(key_prefix, request)
  338. if cache is None:
  339. cache = caches[settings.CACHE_MIDDLEWARE_ALIAS]
  340. headerlist = cache.get(cache_key)
  341. if headerlist is not None:
  342. return _generate_cache_key(request, method, headerlist, key_prefix)
  343. else:
  344. return None
  345. def learn_cache_key(request, response, cache_timeout=None, key_prefix=None, cache=None):
  346. """
  347. Learn what headers to take into account for some request URL from the
  348. response object. Store those headers in a global URL registry so that
  349. later access to that URL will know what headers to take into account
  350. without building the response object itself. The headers are named in the
  351. Vary header of the response, but we want to prevent response generation.
  352. The list of headers to use for cache key generation is stored in the same
  353. cache as the pages themselves. If the cache ages some data out of the
  354. cache, this just means that we have to build the response once to get at
  355. the Vary header and so at the list of headers to use for the cache key.
  356. """
  357. if key_prefix is None:
  358. key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
  359. if cache_timeout is None:
  360. cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
  361. cache_key = _generate_cache_header_key(key_prefix, request)
  362. if cache is None:
  363. cache = caches[settings.CACHE_MIDDLEWARE_ALIAS]
  364. if response.has_header("Vary"):
  365. is_accept_language_redundant = settings.USE_I18N
  366. # If i18n is used, the generated cache key will be suffixed with the
  367. # current locale. Adding the raw value of Accept-Language is redundant
  368. # in that case and would result in storing the same content under
  369. # multiple keys in the cache. See #18191 for details.
  370. headerlist = []
  371. for header in split_header_value(response.headers["Vary"]):
  372. header = header.upper().replace("-", "_")
  373. if header != "ACCEPT_LANGUAGE" or not is_accept_language_redundant:
  374. headerlist.append("HTTP_" + header)
  375. headerlist.sort()
  376. cache.set(cache_key, headerlist, cache_timeout)
  377. return _generate_cache_key(request, request.method, headerlist, key_prefix)
  378. else:
  379. # if there is no Vary header, we still need a cache key
  380. # for the request.build_absolute_uri()
  381. cache.set(cache_key, [], cache_timeout)
  382. return _generate_cache_key(request, request.method, [], key_prefix)
  383. def _to_tuple(s):
  384. t = s.split("=", 1)
  385. if len(t) == 2:
  386. return t[0].lower(), t[1]
  387. return t[0].lower(), True