<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[201800] trunk/Source/WebCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/201800">201800</a></dd>
<dt>Author</dt> <dd>antti@apple.com</dd>
<dt>Date</dt> <dd>2016-06-08 02:01:07 -0700 (Wed, 08 Jun 2016)</dd>
</dl>
<h3>Log Message</h3>
<pre>WebKit memory cache doesn't respect Vary header
https://bugs.webkit.org/show_bug.cgi?id=71509
<rdar://problem/26651033>
Reviewed by Sam Weinig.
Implement Vary header support in WebCore memory cache.
The patch moves Vary header code from WebKit2 Network Cache to WebCore and uses it to
verify the headers for CachedResources.
* loader/cache/CachedResource.cpp:
(WebCore::CachedResource::failBeforeStarting):
(WebCore::addAdditionalRequestHeadersToRequest):
Factor into standalone function so we can use it from varyHeaderValuesMatch.
(WebCore::CachedResource::addAdditionalRequestHeaders):
(WebCore::CachedResource::load):
(WebCore::CachedResource::setResponse):
Collect the Vary header values when we receive a response.
(WebCore::CachedResource::responseReceived):
(WebCore::CachedResource::redirectChainAllowsReuse):
(WebCore::CachedResource::varyHeaderValuesMatch):
Test for Vary match.
(WebCore::CachedResource::overheadSize):
* loader/cache/CachedResource.h:
(WebCore::CachedResource::isCacheValidator):
(WebCore::CachedResource::resourceToRevalidate):
* loader/cache/CachedResourceLoader.cpp:
(WebCore::CachedResourceLoader::determineRevalidationPolicy):
Reload on Vary mismatch.
* platform/network/CacheValidation.cpp:
(WebCore::parseCacheControlDirectives):
(WebCore::headerValueForVary):
(WebCore::collectVaryingRequestHeaders):
(WebCore::verifyVaryingRequestHeaders):
Vary header collection and validation code moves here.
* platform/network/CacheValidation.h:</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreloadercacheCachedResourcecpp">trunk/Source/WebCore/loader/cache/CachedResource.cpp</a></li>
<li><a href="#trunkSourceWebCoreloadercacheCachedResourceh">trunk/Source/WebCore/loader/cache/CachedResource.h</a></li>
<li><a href="#trunkSourceWebCoreloadercacheCachedResourceLoadercpp">trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformnetworkCacheValidationcpp">trunk/Source/WebCore/platform/network/CacheValidation.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformnetworkCacheValidationh">trunk/Source/WebCore/platform/network/CacheValidation.h</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (201799 => 201800)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-06-08 08:00:22 UTC (rev 201799)
+++ trunk/Source/WebCore/ChangeLog        2016-06-08 09:01:07 UTC (rev 201800)
</span><span class="lines">@@ -1,3 +1,53 @@
</span><ins>+2016-06-06 Antti Koivisto <antti@apple.com>
+
+ WebKit memory cache doesn't respect Vary header
+ https://bugs.webkit.org/show_bug.cgi?id=71509
+ <rdar://problem/26651033>
+
+ Reviewed by Sam Weinig.
+
+ Implement Vary header support in WebCore memory cache.
+
+ The patch moves Vary header code from WebKit2 Network Cache to WebCore and uses it to
+ verify the headers for CachedResources.
+
+ * loader/cache/CachedResource.cpp:
+ (WebCore::CachedResource::failBeforeStarting):
+ (WebCore::addAdditionalRequestHeadersToRequest):
+
+ Factor into standalone function so we can use it from varyHeaderValuesMatch.
+
+ (WebCore::CachedResource::addAdditionalRequestHeaders):
+ (WebCore::CachedResource::load):
+ (WebCore::CachedResource::setResponse):
+
+ Collect the Vary header values when we receive a response.
+
+ (WebCore::CachedResource::responseReceived):
+ (WebCore::CachedResource::redirectChainAllowsReuse):
+ (WebCore::CachedResource::varyHeaderValuesMatch):
+
+ Test for Vary match.
+
+ (WebCore::CachedResource::overheadSize):
+ * loader/cache/CachedResource.h:
+ (WebCore::CachedResource::isCacheValidator):
+ (WebCore::CachedResource::resourceToRevalidate):
+ * loader/cache/CachedResourceLoader.cpp:
+ (WebCore::CachedResourceLoader::determineRevalidationPolicy):
+
+ Reload on Vary mismatch.
+
+ * platform/network/CacheValidation.cpp:
+ (WebCore::parseCacheControlDirectives):
+ (WebCore::headerValueForVary):
+ (WebCore::collectVaryingRequestHeaders):
+ (WebCore::verifyVaryingRequestHeaders):
+
+ Vary header collection and validation code moves here.
+
+ * platform/network/CacheValidation.h:
+
</ins><span class="cx"> 2016-06-08 Myles C. Maxfield <mmaxfield@apple.com>
</span><span class="cx">
</span><span class="cx"> Extend CSSFontSelector's lifetime to be longer than the Document's lifetime
</span></span></pre></div>
<a id="trunkSourceWebCoreloadercacheCachedResourcecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/cache/CachedResource.cpp (201799 => 201800)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/cache/CachedResource.cpp        2016-06-08 08:00:22 UTC (rev 201799)
+++ trunk/Source/WebCore/loader/cache/CachedResource.cpp        2016-06-08 09:01:07 UTC (rev 201800)
</span><span class="lines">@@ -182,7 +182,7 @@
</span><span class="cx"> error(CachedResource::LoadError);
</span><span class="cx"> }
</span><span class="cx">
</span><del>-void CachedResource::addAdditionalRequestHeaders(CachedResourceLoader& cachedResourceLoader)
</del><ins>+static void addAdditionalRequestHeadersToRequest(ResourceRequest& request, const CachedResourceLoader& cachedResourceLoader)
</ins><span class="cx"> {
</span><span class="cx"> // Note: We skip the Content-Security-Policy check here because we check
</span><span class="cx"> // the Content-Security-Policy at the CachedResourceLoader layer so we can
</span><span class="lines">@@ -191,24 +191,30 @@
</span><span class="cx"> FrameLoader& frameLoader = cachedResourceLoader.frame()->loader();
</span><span class="cx"> String outgoingReferrer;
</span><span class="cx"> String outgoingOrigin;
</span><del>- if (m_resourceRequest.httpReferrer().isNull()) {
</del><ins>+ if (request.httpReferrer().isNull()) {
</ins><span class="cx"> outgoingReferrer = frameLoader.outgoingReferrer();
</span><span class="cx"> outgoingOrigin = frameLoader.outgoingOrigin();
</span><span class="cx"> } else {
</span><del>- outgoingReferrer = m_resourceRequest.httpReferrer();
</del><ins>+ outgoingReferrer = request.httpReferrer();
</ins><span class="cx"> outgoingOrigin = SecurityOrigin::createFromString(outgoingReferrer)->toString();
</span><span class="cx"> }
</span><span class="cx">
</span><del>- outgoingReferrer = SecurityPolicy::generateReferrerHeader(cachedResourceLoader.document()->referrerPolicy(), m_resourceRequest.url(), outgoingReferrer);
</del><ins>+ auto referrerPolicy = cachedResourceLoader.document() ? cachedResourceLoader.document()->referrerPolicy() : ReferrerPolicy::Default;
+ outgoingReferrer = SecurityPolicy::generateReferrerHeader(referrerPolicy, request.url(), outgoingReferrer);
</ins><span class="cx"> if (outgoingReferrer.isEmpty())
</span><del>- m_resourceRequest.clearHTTPReferrer();
</del><ins>+ request.clearHTTPReferrer();
</ins><span class="cx"> else
</span><del>- m_resourceRequest.setHTTPReferrer(outgoingReferrer);
- FrameLoader::addHTTPOriginIfNeeded(m_resourceRequest, outgoingOrigin);
</del><ins>+ request.setHTTPReferrer(outgoingReferrer);
+ FrameLoader::addHTTPOriginIfNeeded(request, outgoingOrigin);
</ins><span class="cx">
</span><del>- frameLoader.addExtraFieldsToSubresourceRequest(m_resourceRequest);
</del><ins>+ frameLoader.addExtraFieldsToSubresourceRequest(request);
</ins><span class="cx"> }
</span><span class="cx">
</span><ins>+void CachedResource::addAdditionalRequestHeaders(CachedResourceLoader& cachedResourceLoader)
+{
+ addAdditionalRequestHeadersToRequest(m_resourceRequest, cachedResourceLoader);
+}
+
</ins><span class="cx"> void CachedResource::load(CachedResourceLoader& cachedResourceLoader, const ResourceLoaderOptions& options)
</span><span class="cx"> {
</span><span class="cx"> if (!cachedResourceLoader.frame()) {
</span><span class="lines">@@ -417,6 +423,8 @@
</span><span class="cx"> m_response = response;
</span><span class="cx"> m_response.setType(m_responseType);
</span><span class="cx"> m_response.setRedirected(m_redirectChainCacheStatus.status != RedirectChainCacheStatus::NoRedirection);
</span><ins>+
+ m_varyingHeaderValues = collectVaryingRequestHeaders(m_resourceRequest, m_response, m_sessionID);
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void CachedResource::responseReceived(const ResourceResponse& response)
</span><span class="lines">@@ -765,6 +773,17 @@
</span><span class="cx"> return WebCore::redirectChainAllowsReuse(m_redirectChainCacheStatus, reuseExpiredRedirection);
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+bool CachedResource::varyHeaderValuesMatch(const ResourceRequest& request, const CachedResourceLoader& cachedResourceLoader)
+{
+ if (m_varyingHeaderValues.isEmpty())
+ return true;
+
+ ResourceRequest requestWithFullHeaders(request);
+ addAdditionalRequestHeadersToRequest(requestWithFullHeaders, cachedResourceLoader);
+
+ return verifyVaryingRequestHeaders(m_varyingHeaderValues, requestWithFullHeaders, m_sessionID);
+}
+
</ins><span class="cx"> unsigned CachedResource::overheadSize() const
</span><span class="cx"> {
</span><span class="cx"> static const int kAverageClientsHashMapSize = 384;
</span></span></pre></div>
<a id="trunkSourceWebCoreloadercacheCachedResourceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/cache/CachedResource.h (201799 => 201800)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/cache/CachedResource.h        2016-06-08 08:00:22 UTC (rev 201799)
+++ trunk/Source/WebCore/loader/cache/CachedResource.h        2016-06-08 09:01:07 UTC (rev 201800)
</span><span class="lines">@@ -242,6 +242,8 @@
</span><span class="cx"> virtual RevalidationDecision makeRevalidationDecision(CachePolicy) const;
</span><span class="cx"> bool redirectChainAllowsReuse(ReuseExpiredRedirectionOrNot) const;
</span><span class="cx">
</span><ins>+ bool varyHeaderValuesMatch(const ResourceRequest&, const CachedResourceLoader&);
+
</ins><span class="cx"> bool isCacheValidator() const { return m_resourceToRevalidate; }
</span><span class="cx"> CachedResource* resourceToRevalidate() const { return m_resourceToRevalidate; }
</span><span class="cx">
</span><span class="lines">@@ -354,6 +356,8 @@
</span><span class="cx">
</span><span class="cx"> RedirectChainCacheStatus m_redirectChainCacheStatus;
</span><span class="cx">
</span><ins>+ Vector<std::pair<String, String>> m_varyingHeaderValues;
+
</ins><span class="cx"> unsigned long m_identifierForLoadWithoutResourceLoader { 0 };
</span><span class="cx"> };
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceWebCoreloadercacheCachedResourceLoadercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp (201799 => 201800)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp        2016-06-08 08:00:22 UTC (rev 201799)
+++ trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp        2016-06-08 09:01:07 UTC (rev 201800)
</span><span class="lines">@@ -753,6 +753,9 @@
</span><span class="cx"> return Reload;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ if (!existingResource->varyHeaderValuesMatch(request, *this))
+ return Reload;
+
</ins><span class="cx"> auto* textDecoder = existingResource->textResourceDecoder();
</span><span class="cx"> if (textDecoder && !textDecoder->hasEqualEncodingForCharset(cachedResourceRequest.charset()))
</span><span class="cx"> return Reload;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformnetworkCacheValidationcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/network/CacheValidation.cpp (201799 => 201800)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/network/CacheValidation.cpp        2016-06-08 08:00:22 UTC (rev 201799)
+++ trunk/Source/WebCore/platform/network/CacheValidation.cpp        2016-06-08 09:01:07 UTC (rev 201800)
</span><span class="lines">@@ -27,6 +27,9 @@
</span><span class="cx"> #include "CacheValidation.h"
</span><span class="cx">
</span><span class="cx"> #include "HTTPHeaderMap.h"
</span><ins>+#include "NetworkStorageSession.h"
+#include "PlatformCookieJar.h"
+#include "ResourceRequest.h"
</ins><span class="cx"> #include "ResourceResponse.h"
</span><span class="cx"> #include <wtf/CurrentTime.h>
</span><span class="cx">
</span><span class="lines">@@ -326,4 +329,54 @@
</span><span class="cx"> return result;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+static String headerValueForVary(SessionID sessionID, const ResourceRequest& request, const String& headerName)
+{
+ // Explicit handling for cookies is needed because they are added magically by the networking layer.
+ // FIXME: The value might have changed between making the request and retrieving the cookie here.
+ // We could fetch the cookie when making the request but that seems overkill as the case is very rare and it
+ // is a blocking operation. This should be sufficient to cover reasonable cases.
+ if (headerName == httpHeaderNameString(HTTPHeaderName::Cookie)) {
+ if (sessionID != SessionID::defaultSessionID()) {
+ // FIXME: Don't know how to get the cookie. There should be a global way to get NetworkStorageSession from sessionID.
+ return "";
+ }
+ return cookieRequestHeaderFieldValue(NetworkStorageSession::defaultStorageSession(), request.firstPartyForCookies(), request.url());
+ }
+ return request.httpHeaderField(headerName);
</ins><span class="cx"> }
</span><ins>+
+Vector<std::pair<String, String>> collectVaryingRequestHeaders(const WebCore::ResourceRequest& request, const WebCore::ResourceResponse& response, SessionID sessionID)
+{
+ String varyValue = response.httpHeaderField(WebCore::HTTPHeaderName::Vary);
+ if (varyValue.isEmpty())
+ return { };
+ Vector<String> varyingHeaderNames;
+ varyValue.split(',', /*allowEmptyEntries*/ false, varyingHeaderNames);
+ Vector<std::pair<String, String>> varyingRequestHeaders;
+ varyingRequestHeaders.reserveCapacity(varyingHeaderNames.size());
+ for (auto& varyHeaderName : varyingHeaderNames) {
+ String headerName = varyHeaderName.stripWhiteSpace();
+ String headerValue = headerValueForVary(sessionID, request, headerName);
+ varyingRequestHeaders.append(std::make_pair(headerName, headerValue));
+ }
+ return varyingRequestHeaders;
+}
+
+bool verifyVaryingRequestHeaders(const Vector<std::pair<String, String>>& varyingRequestHeaders, const WebCore::ResourceRequest& request, SessionID sessionID)
+{
+ for (auto& varyingRequestHeader : varyingRequestHeaders) {
+ // FIXME: Vary: * in response would ideally trigger a cache delete instead of a store.
+ if (varyingRequestHeader.first == "*")
+ return false;
+ if (sessionID != SessionID::defaultSessionID() && varyingRequestHeader.first == httpHeaderNameString(HTTPHeaderName::Cookie)) {
+ // FIXME: See the comment in headerValueForVary.
+ return false;
+ }
+ String headerValue = headerValueForVary(sessionID, request, varyingRequestHeader.first);
+ if (headerValue != varyingRequestHeader.second)
+ return false;
+ }
+ return true;
+}
+
+}
</ins></span></pre></div>
<a id="trunkSourceWebCoreplatformnetworkCacheValidationh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/network/CacheValidation.h (201799 => 201800)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/network/CacheValidation.h        2016-06-08 08:00:22 UTC (rev 201799)
+++ trunk/Source/WebCore/platform/network/CacheValidation.h        2016-06-08 09:01:07 UTC (rev 201800)
</span><span class="lines">@@ -27,11 +27,15 @@
</span><span class="cx"> #define CacheValidation_h
</span><span class="cx">
</span><span class="cx"> #include "PlatformExportMacros.h"
</span><ins>+#include "SessionID.h"
</ins><span class="cx"> #include <wtf/Optional.h>
</span><ins>+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
</ins><span class="cx">
</span><span class="cx"> namespace WebCore {
</span><span class="cx">
</span><span class="cx"> class HTTPHeaderMap;
</span><ins>+class ResourceRequest;
</ins><span class="cx"> class ResourceResponse;
</span><span class="cx">
</span><span class="cx"> struct RedirectChainCacheStatus {
</span><span class="lines">@@ -65,6 +69,9 @@
</span><span class="cx"> };
</span><span class="cx"> WEBCORE_EXPORT CacheControlDirectives parseCacheControlDirectives(const HTTPHeaderMap&);
</span><span class="cx">
</span><ins>+WEBCORE_EXPORT Vector<std::pair<String, String>> collectVaryingRequestHeaders(const ResourceRequest&, const ResourceResponse&, SessionID = SessionID::defaultSessionID());
+WEBCORE_EXPORT bool verifyVaryingRequestHeaders(const Vector<std::pair<String, String>>& varyingRequestHeaders, const ResourceRequest&, SessionID = SessionID::defaultSessionID());
+
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> #endif
</span></span></pre>
</div>
</div>
</body>
</html>