[Cocoa] Treat Epoch as invalid value for "Last-Modified" header
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Aug 2015 17:47:08 +0000 (17:47 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Aug 2015 17:47:08 +0000 (17:47 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148162
rdar://problem/22330837

Reviewed by Antti Koivisto.

Source/WebCore:

Ignore "Last-Modified" header when computing heuristic freshness if it
is Epoch. CFNetwork currently converts a malformed date for Last-Modified
into Epoch so there is no way for us to distinguish Epoch from invalid
input. Without this, we would end up with cached resources that have a
giant lifetime (> 4 years) due to a malformed HTTP header.

Some Websites (e.g. www.popehat.com) also wrongly return Epoch as
Last-Modified value and we would end up caching it overly aggressively.
Now that we consider Epoch as an invalid value for Last-Modified, it will
also work around this content bug.

Test: http/tests/cache/disk-cache/disk-cache-last-modified.html

* platform/network/ResourceResponseBase.cpp:
(WebCore::ResourceResponseBase::lastModified):

LayoutTests:

Add better layout test coverage for using the "Last-Modified" header to
compute heuristic freshness. In particular, it adds coverage for the
following values: Epoch, malformed date.

* http/tests/cache/disk-cache/disk-cache-last-modified-expected.txt: Added.
* http/tests/cache/disk-cache/disk-cache-last-modified.html: Added.
New test.

* http/tests/cache/disk-cache/resources/cache-test.js:
(makeHeaderValue):
makeHeaderValue() was not resolving 'now(-1000)' into a date. This means that the
tests using it would end up sending an invalid "Last-Modified" header which our
networking code was translating to Epoch. We now ignore Epoch as Last-Modified
value for computing heuristic freshness to not cache due to malformed headers.

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

LayoutTests/ChangeLog
LayoutTests/http/tests/cache/disk-cache/disk-cache-last-modified-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cache/disk-cache/disk-cache-last-modified.html [new file with mode: 0644]
LayoutTests/http/tests/cache/disk-cache/resources/cache-test.js
Source/WebCore/ChangeLog
Source/WebCore/platform/network/ResourceResponseBase.cpp

index cf1ec48..85fe2ff 100644 (file)
@@ -1,3 +1,26 @@
+2015-08-20  Chris Dumez  <cdumez@apple.com>
+
+        [Cocoa] Treat Epoch as invalid value for "Last-Modified" header
+        https://bugs.webkit.org/show_bug.cgi?id=148162
+        rdar://problem/22330837
+
+        Reviewed by Antti Koivisto.
+
+        Add better layout test coverage for using the "Last-Modified" header to
+        compute heuristic freshness. In particular, it adds coverage for the
+        following values: Epoch, malformed date.
+
+        * http/tests/cache/disk-cache/disk-cache-last-modified-expected.txt: Added.
+        * http/tests/cache/disk-cache/disk-cache-last-modified.html: Added.
+        New test.
+
+        * http/tests/cache/disk-cache/resources/cache-test.js:
+        (makeHeaderValue):
+        makeHeaderValue() was not resolving 'now(-1000)' into a date. This means that the
+        tests using it would end up sending an invalid "Last-Modified" header which our
+        networking code was translating to Epoch. We now ignore Epoch as Last-Modified
+        value for computing heuristic freshness to not cache due to malformed headers.
+
 2015-08-20  Eric Carlson  <eric.carlson@apple.com>
 
         Revert accidental commit of a new test that isn't ready for prime time.
diff --git a/LayoutTests/http/tests/cache/disk-cache/disk-cache-last-modified-expected.txt b/LayoutTests/http/tests/cache/disk-cache/disk-cache-last-modified-expected.txt
new file mode 100644 (file)
index 0000000..a9d2bf2
--- /dev/null
@@ -0,0 +1,41 @@
+Covers uses of 'Last-Modified' header to compute heuristic freshness.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+running 3 tests
+
+--------Testing loads from disk cache--------
+response headers: {"Last-Modified":"Thu, 01 Jan 2000 00:00:00 GMT"}
+response source: Disk cache
+
+response headers: {"Last-Modified":"Thu, 01 Jan 1970 00:00:00 GMT"}
+response source: Network
+
+response headers: {"Last-Modified":"invalid"}
+response source: Network
+
+--------Testing loads through memory cache (XHR behavior)--------
+response headers: {"Last-Modified":"Thu, 01 Jan 2000 00:00:00 GMT"}
+response source: Memory cache
+
+response headers: {"Last-Modified":"Thu, 01 Jan 1970 00:00:00 GMT"}
+response source: Network
+
+response headers: {"Last-Modified":"invalid"}
+response source: Network
+
+--------Testing loads through memory cache (subresource behavior)--------
+response headers: {"Last-Modified":"Thu, 01 Jan 2000 00:00:00 GMT"}
+response source: Memory cache
+
+response headers: {"Last-Modified":"Thu, 01 Jan 1970 00:00:00 GMT"}
+response source: Network
+
+response headers: {"Last-Modified":"invalid"}
+response source: Network
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/cache/disk-cache/disk-cache-last-modified.html b/LayoutTests/http/tests/cache/disk-cache/disk-cache-last-modified.html
new file mode 100644 (file)
index 0000000..397f817
--- /dev/null
@@ -0,0 +1,21 @@
+<script src="/js-test-resources/js-test-pre.js"></script>
+<script src="resources/cache-test.js"></script>
+<body>
+<script>
+
+var tests =
+[
+ { responseHeaders: {'Last-Modified': 'Thu, 01 Jan 2000 00:00:00 GMT' } }, // Heuristic freshness.
+ { responseHeaders: {'Last-Modified': 'Thu, 01 Jan 1970 00:00:00 GMT' } }, // Epoch.
+ { responseHeaders: {'Last-Modified': 'invalid' } }, // Invalid date
+];
+
+description("Covers uses of 'Last-Modified' header to compute heuristic freshness.");
+
+debug("running " + tests.length + " tests");
+debug("");
+
+runTests(tests);
+
+</script>
+<script src="/js-test-resources/js-test-post.js"></script>
index 797cf92..cbde17b 100644 (file)
@@ -33,6 +33,8 @@ function makeHeaderValue(value)
         return (new Date(new Date().getTime() + serverClientTimeDelta)).toUTCString();
     if (value == 'now(100)')
         return (new Date(new Date().getTime() + serverClientTimeDelta + 100 * 1000)).toUTCString();
+    if (value == 'now(-1000)')
+        return (new Date(new Date().getTime() - serverClientTimeDelta - 1000 * 1000)).toUTCString()
     if (value == 'unique()')
         return "" + uniqueIdCounter++;
     return value;
index 4696002..f78ef96 100644 (file)
@@ -1,3 +1,27 @@
+2015-08-20  Chris Dumez  <cdumez@apple.com>
+
+        [Cocoa] Treat Epoch as invalid value for "Last-Modified" header
+        https://bugs.webkit.org/show_bug.cgi?id=148162
+        rdar://problem/22330837
+
+        Reviewed by Antti Koivisto.
+
+        Ignore "Last-Modified" header when computing heuristic freshness if it
+        is Epoch. CFNetwork currently converts a malformed date for Last-Modified
+        into Epoch so there is no way for us to distinguish Epoch from invalid
+        input. Without this, we would end up with cached resources that have a
+        giant lifetime (> 4 years) due to a malformed HTTP header.
+
+        Some Websites (e.g. www.popehat.com) also wrongly return Epoch as
+        Last-Modified value and we would end up caching it overly aggressively.
+        Now that we consider Epoch as an invalid value for Last-Modified, it will
+        also work around this content bug.
+
+        Test: http/tests/cache/disk-cache/disk-cache-last-modified.html
+
+        * platform/network/ResourceResponseBase.cpp:
+        (WebCore::ResourceResponseBase::lastModified):
+
 2015-08-19  Brian Burg  <bburg@apple.com>
 
         Web Inspector: add TestHarness option to tee all commands to system console
index 1a8eee4..5627734 100644 (file)
@@ -425,6 +425,13 @@ Optional<std::chrono::system_clock::time_point> ResourceResponseBase::lastModifi
 
     if (!m_haveParsedLastModifiedHeader) {
         m_lastModified = parseDateValueInHeader(m_httpHeaderFields, HTTPHeaderName::LastModified);
+#if PLATFORM(COCOA)
+        // CFNetwork converts malformed dates into Epoch so we need to treat Epoch as
+        // an invalid value (rdar://problem/22352838).
+        const std::chrono::system_clock::time_point epoch;
+        if (m_lastModified && m_lastModified.value() == epoch)
+            m_lastModified = Nullopt;
+#endif
         m_haveParsedLastModifiedHeader = true;
     }
     return m_lastModified;