2
0
Эх сурвалжийг харах

Fixed #27225 -- Added "Age" header when fetching cached responses.

Co-Authored-By: Author: Alexander Lazarević <laza@e11bits.com>
Rinat Khabibiev 8 жил өмнө
parent
commit
3580b47ed3

+ 12 - 0
django/middleware/cache.py

@@ -43,6 +43,8 @@ More details about how the caching works:
 
 """
 
+import time
+
 from django.conf import settings
 from django.core.cache import DEFAULT_CACHE_ALIAS, caches
 from django.utils.cache import (
@@ -53,6 +55,7 @@ from django.utils.cache import (
     patch_response_headers,
 )
 from django.utils.deprecation import MiddlewareMixin
+from django.utils.http import parse_http_date_safe
 
 
 class UpdateCacheMiddleware(MiddlewareMixin):
@@ -171,6 +174,15 @@ class FetchFromCacheMiddleware(MiddlewareMixin):
             request._cache_update_cache = True
             return None  # No cache information available, need to rebuild.
 
+        # Derive the age estimation of the cached response.
+        if (max_age_seconds := get_max_age(response)) is not None and (
+            expires_timestamp := parse_http_date_safe(response["Expires"])
+        ) is not None:
+            now_timestamp = int(time.time())
+            remaining_seconds = expires_timestamp - now_timestamp
+            # Use Age: 0 if local clock got turned back.
+            response["Age"] = max(0, max_age_seconds - remaining_seconds)
+
         # hit, return cached response
         request._cache_update_cache = False
         return response

+ 31 - 0
tests/cache/tests.py

@@ -2752,6 +2752,37 @@ class CacheMiddlewareTest(SimpleTestCase):
 
         self.assertIsNot(thread_caches[0], thread_caches[1])
 
+    def test_cache_control_max_age(self):
+        view = cache_page(2)(hello_world_view)
+        request = self.factory.get("/view/")
+
+        # First request. Freshly created response gets returned with no Age
+        # header.
+        with mock.patch.object(
+            time, "time", return_value=1468749600
+        ):  # Sun, 17 Jul 2016 10:00:00 GMT
+            response = view(request, 1)
+            response.close()
+            self.assertIn("Expires", response)
+            self.assertEqual(response["Expires"], "Sun, 17 Jul 2016 10:00:02 GMT")
+            self.assertIn("Cache-Control", response)
+            self.assertEqual(response["Cache-Control"], "max-age=2")
+            self.assertNotIn("Age", response)
+
+        # Second request one second later. Response from the cache gets
+        # returned with an Age header set to 1 (second).
+        with mock.patch.object(
+            time, "time", return_value=1468749601
+        ):  # Sun, 17 Jul 2016 10:00:01 GMT
+            response = view(request, 1)
+            response.close()
+            self.assertIn("Expires", response)
+            self.assertEqual(response["Expires"], "Sun, 17 Jul 2016 10:00:02 GMT")
+            self.assertIn("Cache-Control", response)
+            self.assertEqual(response["Cache-Control"], "max-age=2")
+            self.assertIn("Age", response)
+            self.assertEqual(response["Age"], "1")
+
 
 @override_settings(
     CACHE_MIDDLEWARE_KEY_PREFIX="settingsprefix",