[WK2][NetworkCache] Add support for "Cache-Control: max-stale" request header
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 30 Mar 2015 17:35:39 +0000 (17:35 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 30 Mar 2015 17:35:39 +0000 (17:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=143159
<rdar://problem/20333296>

Reviewed by Antti Koivisto.

Source/WebCore:

Add support for "Cache-Control: max-stale" request header:
https://tools.ietf.org/html/rfc7234#section-5.2.1.2

Test: http/tests/cache/disk-cache/disk-cache-request-max-stale.html

* platform/network/CacheValidation.cpp:
(WebCore::parseCacheControlDirectives):
* platform/network/CacheValidation.h:

Source/WebKit2:

Add support for "Cache-Control: max-stale" request header:
https://tools.ietf.org/html/rfc7234#section-5.2.1.2

* NetworkProcess/cache/NetworkCache.cpp:
(WebKit::NetworkCache::responseHasExpired):
(WebKit::NetworkCache::requestRequiresRevalidation):
(WebKit::NetworkCache::canUse):

LayoutTests:

Add layout test to cover support for "Cache-Control: max-stale" request
header.

* http/tests/cache/disk-cache/disk-cache-request-max-stale-expected.txt: Added.
* http/tests/cache/disk-cache/disk-cache-request-max-stale.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@182145 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/http/tests/cache/disk-cache/disk-cache-request-max-stale-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cache/disk-cache/disk-cache-request-max-stale.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/platform/network/CacheValidation.cpp
Source/WebCore/platform/network/CacheValidation.h
Source/WebKit2/ChangeLog
Source/WebKit2/NetworkProcess/cache/NetworkCache.cpp

index 68d13b6..953050e 100644 (file)
@@ -1,3 +1,17 @@
+2015-03-30  Chris Dumez  <cdumez@apple.com>
+
+        [WK2][NetworkCache] Add support for "Cache-Control: max-stale" request header
+        https://bugs.webkit.org/show_bug.cgi?id=143159
+        <rdar://problem/20333296>
+
+        Reviewed by Antti Koivisto.
+
+        Add layout test to cover support for "Cache-Control: max-stale" request
+        header.
+
+        * http/tests/cache/disk-cache/disk-cache-request-max-stale-expected.txt: Added.
+        * http/tests/cache/disk-cache/disk-cache-request-max-stale.html: Added.
+
 2015-03-30  Jer Noble  <jer.noble@apple.com>
 
         [iOS] When Web Audio is interrupted by a phone call, it cannot be restarted.
diff --git a/LayoutTests/http/tests/cache/disk-cache/disk-cache-request-max-stale-expected.txt b/LayoutTests/http/tests/cache/disk-cache/disk-cache-request-max-stale-expected.txt
new file mode 100644 (file)
index 0000000..aac0074
--- /dev/null
@@ -0,0 +1,155 @@
+Test for 'max-stale' request header
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+running 36 tests
+
+response headers: {"Cache-control":"max-age=0"}
+request headers: {"Cache-control":"max-stale=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100"}
+request headers: {"Cache-control":"max-stale=0"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=0","Age":"200"}
+request headers: {"Cache-control":"max-stale=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100","Age":"200"}
+request headers: {"Cache-control":"max-stale=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=0"}
+request headers: {"Cache-control":"max-stale"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=100"}
+request headers: {"Cache-control":"max-stale"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=0","Age":"200"}
+request headers: {"Cache-control":"max-stale"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=100","Age":"200"}
+request headers: {"Cache-control":"max-stale"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=0"}
+request headers: {"Cache-control":"max-stale=100"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=100"}
+request headers: {"Cache-control":"max-stale=100"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=0","Age":"200"}
+request headers: {"Cache-control":"max-stale=100"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100","Age":"200"}
+request headers: {"Cache-control":"max-stale=100"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=0"}
+request headers: {"Cache-control":"max-stale=0, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100"}
+request headers: {"Cache-control":"max-stale=0, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=0","Age":"200"}
+request headers: {"Cache-control":"max-stale=0, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100","Age":"200"}
+request headers: {"Cache-control":"max-stale=0, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=0"}
+request headers: {"Cache-control":"max-stale, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100"}
+request headers: {"Cache-control":"max-stale, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=0","Age":"200"}
+request headers: {"Cache-control":"max-stale, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100","Age":"200"}
+request headers: {"Cache-control":"max-stale, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=0"}
+request headers: {"Cache-control":"max-stale=100, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100"}
+request headers: {"Cache-control":"max-stale=100, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=0","Age":"200"}
+request headers: {"Cache-control":"max-stale=100, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100","Age":"200"}
+request headers: {"Cache-control":"max-stale=100, max-age=0"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=0"}
+request headers: {"Cache-control":"max-stale=0, max-age=100"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100"}
+request headers: {"Cache-control":"max-stale=0, max-age=100"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=0","Age":"200"}
+request headers: {"Cache-control":"max-stale=0, max-age=100"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100","Age":"200"}
+request headers: {"Cache-control":"max-stale=0, max-age=100"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=0"}
+request headers: {"Cache-control":"max-stale, max-age=100"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=100"}
+request headers: {"Cache-control":"max-stale, max-age=100"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=0","Age":"200"}
+request headers: {"Cache-control":"max-stale, max-age=100"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=100","Age":"200"}
+request headers: {"Cache-control":"max-stale, max-age=100"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=0"}
+request headers: {"Cache-control":"max-stale=100, max-age=100"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=100"}
+request headers: {"Cache-control":"max-stale=100, max-age=100"}
+response source: Disk cache
+
+response headers: {"Cache-control":"max-age=0","Age":"200"}
+request headers: {"Cache-control":"max-stale=100, max-age=100"}
+response source: Network
+
+response headers: {"Cache-control":"max-age=100","Age":"200"}
+request headers: {"Cache-control":"max-stale=100, max-age=100"}
+response source: Network
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cache/disk-cache/disk-cache-request-max-stale.html b/LayoutTests/http/tests/cache/disk-cache/disk-cache-request-max-stale.html
new file mode 100644 (file)
index 0000000..25d856a
--- /dev/null
@@ -0,0 +1,38 @@
+<script src="/js-test-resources/js-test-pre.js"></script>
+<script src="resources/cache-test.js"></script>
+<body>
+<script>
+
+var testMatrix =
+[
+ [
+  { responseHeaders: {'Cache-control': 'max-age=0' } },
+  { responseHeaders: {'Cache-control': 'max-age=100' } },
+  ],
+ [
+   {},
+   { responseHeaders: {'Age': '200' } },
+  ],
+ [
+  { requestHeaders: {'Cache-control': 'max-stale=0' } },
+  { requestHeaders: {'Cache-control': 'max-stale' } },
+  { requestHeaders: {'Cache-control': 'max-stale=100' } },
+  ],
+ [
+  {},
+  { requestHeaders: {'Cache-control': 'max-age=0' } },
+  { requestHeaders: {'Cache-control': 'max-age=100' } },
+  ],
+ ];
+
+description("Test for 'max-stale' request header");
+
+var tests = generateTests(testMatrix);
+
+debug("running " + tests.length + " tests");
+debug("");
+
+runTests(tests);
+
+</script>
+<script src="/js-test-resources/js-test-post.js"></script>
index 3fe38ab..1334834 100644 (file)
@@ -1,3 +1,20 @@
+2015-03-30  Chris Dumez  <cdumez@apple.com>
+
+        [WK2][NetworkCache] Add support for "Cache-Control: max-stale" request header
+        https://bugs.webkit.org/show_bug.cgi?id=143159
+        <rdar://problem/20333296>
+
+        Reviewed by Antti Koivisto.
+
+        Add support for "Cache-Control: max-stale" request header:
+        https://tools.ietf.org/html/rfc7234#section-5.2.1.2
+
+        Test: http/tests/cache/disk-cache/disk-cache-request-max-stale.html
+
+        * platform/network/CacheValidation.cpp:
+        (WebCore::parseCacheControlDirectives):
+        * platform/network/CacheValidation.h:
+
 2015-03-30  Simon Fraser  <simon.fraser@apple.com>
 
         Fix iOS internal build after r182132.
index b2e361d..f8b6633 100644 (file)
@@ -274,6 +274,21 @@ CacheControlDirectives parseCacheControlDirectives(const HTTPHeaderMap& headers)
                 double maxAge = directives[i].second.toDouble(&ok);
                 if (ok)
                     result.maxAge = maxAge;
+            } else if (equalIgnoringCase(directives[i].first, "max-stale")) {
+                // https://tools.ietf.org/html/rfc7234#section-5.2.1.2
+                if (!std::isnan(result.maxStale)) {
+                    // First max-stale directive wins if there are multiple ones.
+                    continue;
+                }
+                if (directives[i].second.isEmpty()) {
+                    // if no value is assigned to max-stale, then the client is willing to accept a stale response of any age.
+                    result.maxStale = std::numeric_limits<double>::max();
+                    continue;
+                }
+                bool ok;
+                double maxStale = directives[i].second.toDouble(&ok);
+                if (ok)
+                    result.maxStale = maxStale;
             }
         }
     }
index b828bcf..4a57355 100644 (file)
@@ -55,6 +55,7 @@ WEBCORE_EXPORT bool redirectChainAllowsReuse(RedirectChainCacheStatus, ReuseExpi
 
 struct CacheControlDirectives {
     double maxAge { std::numeric_limits<double>::quiet_NaN() };
+    double maxStale { std::numeric_limits<double>::quiet_NaN() };
     bool noCache { false };
     bool noStore { false };
     bool mustRevalidate { false };
index a8f0620..836eee7 100644 (file)
@@ -1,3 +1,19 @@
+2015-03-30  Chris Dumez  <cdumez@apple.com>
+
+        [WK2][NetworkCache] Add support for "Cache-Control: max-stale" request header
+        https://bugs.webkit.org/show_bug.cgi?id=143159
+        <rdar://problem/20333296>
+
+        Reviewed by Antti Koivisto.
+
+        Add support for "Cache-Control: max-stale" request header:
+        https://tools.ietf.org/html/rfc7234#section-5.2.1.2
+
+        * NetworkProcess/cache/NetworkCache.cpp:
+        (WebKit::NetworkCache::responseHasExpired):
+        (WebKit::NetworkCache::requestRequiresRevalidation):
+        (WebKit::NetworkCache::canUse):
+
 2015-03-30  Csaba Osztrogon√°c  <ossy@webkit.org>
 
         [EFL][GTK] WebKit2's generate-forwarding-headers.pl should fail if clashing headers found
index fa54653..c4fa7d8 100644 (file)
@@ -150,7 +150,7 @@ static bool cachePolicyAllowsExpired(WebCore::ResourceRequestCachePolicy policy)
     return false;
 }
 
-static bool responseHasExpired(const WebCore::ResourceResponse& response, std::chrono::milliseconds timestamp)
+static bool responseHasExpired(const WebCore::ResourceResponse& response, std::chrono::milliseconds timestamp, double maxStale)
 {
     if (response.cacheControlContainsNoCache())
         return true;
@@ -159,17 +159,18 @@ static bool responseHasExpired(const WebCore::ResourceResponse& response, std::c
     double age = WebCore::computeCurrentAge(response, doubleTimeStamp.count());
     double lifetime = WebCore::computeFreshnessLifetimeForHTTPFamily(response, doubleTimeStamp.count());
 
-    bool hasExpired = age > lifetime;
+    maxStale = std::isnan(maxStale) ? 0 : maxStale;
+    bool hasExpired = age - lifetime > maxStale;
 
 #ifndef LOG_DISABLED
     if (hasExpired)
-        LOG(NetworkCache, "(NetworkProcess) needsRevalidation hasExpired age=%f lifetime=%f", age, lifetime);
+        LOG(NetworkCache, "(NetworkProcess) needsRevalidation hasExpired age=%f lifetime=%f max-stale=%g", age, lifetime, maxStale);
 #endif
 
     return hasExpired;
 }
 
-static bool requestRequiresRevalidation(const WebCore::ResourceRequest& request)
+static bool responseNeedsRevalidation(const WebCore::ResourceResponse& response, const WebCore::ResourceRequest& request, std::chrono::milliseconds timestamp)
 {
     auto requestDirectives = WebCore::parseCacheControlDirectives(request.httpHeaderFields());
     if (requestDirectives.noCache)
@@ -178,7 +179,7 @@ static bool requestRequiresRevalidation(const WebCore::ResourceRequest& request)
     if (requestDirectives.maxAge == 0)
         return true;
 
-    return false;
+    return responseHasExpired(response, timestamp, requestDirectives.maxStale);
 }
 
 static UseDecision canUse(const Entry& entry, const WebCore::ResourceRequest& request)
@@ -192,8 +193,7 @@ static UseDecision canUse(const Entry& entry, const WebCore::ResourceRequest& re
     if (cachePolicyAllowsExpired(request.cachePolicy()))
         return UseDecision::Use;
 
-    bool needsRevalidation = requestRequiresRevalidation(request) || responseHasExpired(entry.response(), entry.timeStamp());
-    if (!needsRevalidation)
+    if (!responseNeedsRevalidation(entry.response(), request, entry.timeStamp()))
         return UseDecision::Use;
 
     bool hasValidatorFields = entry.response().hasCacheValidatorFields();