Bug 1120715 - Part 5: Treat a default cache mode Request with a revalidation header as no-store; r=bkelly

This commit is contained in:
Ehsan Akhgari 2016-03-02 18:09:30 -05:00
parent 440ae27065
commit a014521971
7 changed files with 229 additions and 21 deletions

View File

@ -245,12 +245,6 @@ FetchDriver::HttpFetch()
#endif
chan->SetNotificationCallbacks(this);
// FIXME(nsm): Bug 1120715.
// Step 3.4 "If request's cache mode is default and request's header list
// contains a header named `If-Modified-Since`, `If-None-Match`,
// `If-Unmodified-Since`, `If-Match`, or `If-Range`, set request's cache mode
// to no-store."
// Step 3.5 begins "HTTP network or cache fetch".
// HTTP network or cache fetch
// ---------------------------
@ -332,12 +326,11 @@ FetchDriver::HttpFetch()
// dom/workers/ServiceWorkerManager.cpp
internalChan->SetCorsMode(static_cast<uint32_t>(mRequest->Mode()));
internalChan->SetRedirectMode(static_cast<uint32_t>(mRequest->GetRedirectMode()));
mRequest->MaybeSkipCacheIfPerformingRevalidation();
internalChan->SetFetchCacheMode(static_cast<uint32_t>(mRequest->GetCacheMode()));
}
// Step 5. Proxy authentication will be handled by Necko.
// FIXME(nsm): Bug 1120715.
// Step 7-10. "If request's cache mode is neither no-store nor reload..."
// Continue setting up 'HTTPRequest'. Content-Type and body data.
nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);

View File

@ -174,6 +174,17 @@ InternalHeaders::IsSimpleHeader(const nsACString& aName, const nsACString& aValu
nsContentUtils::IsAllowedNonCorsContentType(aValue));
}
// static
bool
InternalHeaders::IsRevalidationHeader(const nsACString& aName)
{
return aName.EqualsLiteral("if-modified-since") ||
aName.EqualsLiteral("if-none-match") ||
aName.EqualsLiteral("if-unmodified-since") ||
aName.EqualsLiteral("if-match") ||
aName.EqualsLiteral("if-range");
}
//static
bool
InternalHeaders::IsInvalidName(const nsACString& aName, ErrorResult& aRv)
@ -283,6 +294,18 @@ InternalHeaders::HasOnlySimpleHeaders() const
return true;
}
bool
InternalHeaders::HasRevalidationHeaders() const
{
for (uint32_t i = 0; i < mList.Length(); ++i) {
if (IsRevalidationHeader(mList[i].mName)) {
return true;
}
}
return false;
}
// static
already_AddRefed<InternalHeaders>
InternalHeaders::BasicHeaders(InternalHeaders* aHeaders)

View File

@ -99,6 +99,8 @@ public:
bool HasOnlySimpleHeaders() const;
bool HasRevalidationHeaders() const;
static already_AddRefed<InternalHeaders>
BasicHeaders(InternalHeaders* aHeaders);
@ -142,6 +144,8 @@ private:
static bool IsSimpleHeader(const nsACString& aName,
const nsACString& aValue);
static bool IsRevalidationHeader(const nsACString& aName);
};
} // namespace dom

View File

@ -363,5 +363,14 @@ InternalRequest::MapChannelToRequestCredentials(nsIChannel* aChannel)
return RequestCredentials::Same_origin;
}
void
InternalRequest::MaybeSkipCacheIfPerformingRevalidation()
{
if (mCacheMode == RequestCache::Default &&
mHeaders->HasRevalidationHeaders()) {
mCacheMode = RequestCache::No_store;
}
}
} // namespace dom
} // namespace mozilla

View File

@ -425,6 +425,9 @@ public:
bool
IsClientRequest() const;
void
MaybeSkipCacheIfPerformingRevalidation();
static RequestMode
MapChannelToRequestMode(nsIChannel* aChannel);

View File

@ -128,6 +128,166 @@
expected_validation_headers: [false, false],
expected_no_cache_headers: [true, false],
},
{
name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"',
state: "stale",
request_cache: ["default", "default"],
request_headers: [{}, {"If-Modified-Since": new Date().toGMTString()}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [false, true],
},
{
name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"',
state: "fresh",
request_cache: ["default", "default"],
request_headers: [{}, {"If-Modified-Since": new Date().toGMTString()}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [false, true],
},
{
name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"',
state: "stale",
request_cache: ["default", "default"],
request_headers: [{"If-Modified-Since": new Date().toGMTString()}, {}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [true, false],
},
{
name: 'RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store"',
state: "fresh",
request_cache: ["default", "default"],
request_headers: [{"If-Modified-Since": new Date().toGMTString()}, {}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [true, false],
},
{
name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"',
state: "stale",
request_cache: ["default", "default"],
request_headers: [{}, {"If-None-Match": '"foo"'}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [false, true],
},
{
name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"',
state: "fresh",
request_cache: ["default", "default"],
request_headers: [{}, {"If-None-Match": '"foo"'}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [false, true],
},
{
name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"',
state: "stale",
request_cache: ["default", "default"],
request_headers: [{"If-None-Match": '"foo"'}, {}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [true, false],
},
{
name: 'RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store"',
state: "fresh",
request_cache: ["default", "default"],
request_headers: [{"If-None-Match": '"foo"'}, {}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [true, false],
},
{
name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"',
state: "stale",
request_cache: ["default", "default"],
request_headers: [{}, {"If-Unmodified-Since": new Date().toGMTString()}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [false, true],
},
{
name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"',
state: "fresh",
request_cache: ["default", "default"],
request_headers: [{}, {"If-Unmodified-Since": new Date().toGMTString()}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [false, true],
},
{
name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"',
state: "stale",
request_cache: ["default", "default"],
request_headers: [{"If-Unmodified-Since": new Date().toGMTString()}, {}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [true, false],
},
{
name: 'RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store"',
state: "fresh",
request_cache: ["default", "default"],
request_headers: [{"If-Unmodified-Since": new Date().toGMTString()}, {}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [true, false],
},
{
name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"',
state: "stale",
request_cache: ["default", "default"],
request_headers: [{}, {"If-Match": '"foo"'}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [false, true],
},
{
name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"',
state: "fresh",
request_cache: ["default", "default"],
request_headers: [{}, {"If-Match": '"foo"'}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [false, true],
},
{
name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"',
state: "stale",
request_cache: ["default", "default"],
request_headers: [{"If-Match": '"foo"'}, {}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [true, false],
},
{
name: 'RequestCache "default" mode with an If-Match header is treated similarly to "no-store"',
state: "fresh",
request_cache: ["default", "default"],
request_headers: [{"If-Match": '"foo"'}, {}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [true, false],
},
{
name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"',
state: "stale",
request_cache: ["default", "default"],
request_headers: [{}, {"If-Range": '"foo"'}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [false, true],
},
{
name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"',
state: "fresh",
request_cache: ["default", "default"],
request_headers: [{}, {"If-Range": '"foo"'}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [false, true],
},
{
name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"',
state: "stale",
request_cache: ["default", "default"],
request_headers: [{"If-Range": '"foo"'}, {}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [true, false],
},
{
name: 'RequestCache "default" mode with an If-Range header is treated similarly to "no-store"',
state: "fresh",
request_cache: ["default", "default"],
request_headers: [{"If-Range": '"foo"'}, {}],
expected_validation_headers: [false, false],
expected_no_cache_headers: [true, false],
},
{
name: 'Responses with the "Cache-Control: no-store" header are not stored in the cache',
state: "stale",
@ -201,10 +361,16 @@
if ("cache_control" in info) {
cache_control = "&cache_control=" + info.cache_control;
}
var ignore_request_headers = "";
if ("request_headers" in info) {
// Ignore the request headers that we send since they may be synthesized by the test.
ignore_request_headers = "&ignore";
}
return "resources/cache.py?token=" + uuid +
"&content=" + content +
"&" + id + "=" + value +
"&expires=" + dates[info.state] + vary + cache_control;
"&expires=" + dates[info.state] +
vary + cache_control + ignore_request_headers;
}
function server_state(uuid) {
return fetch("resources/cache.py?querystate&token=" + uuid)
@ -214,8 +380,12 @@
return JSON.parse(text);
});
}
function populate_cache(url, content, cache) {
return fetch(url, {cache: cache})
function populate_cache(url, content, info) {
var init = {cache: info.request_cache[0]};
if ("request_headers" in info) {
init.headers = info.request_headers[0];
}
return fetch(url, init)
.then(function(response) {
assert_equals(response.status, 200);
assert_equals(response.statusText, "OK");
@ -231,11 +401,15 @@
var content = Math.random().toString();
var url = make_url(uuid, type, identifier, content, info);
var fetch_functions = [function() {
return populate_cache(url, content, info.request_cache[0]);
return populate_cache(url, content, info);
}];
for (var i = 1; i < info.request_cache.length; ++i) {
fetch_functions.push(function(idx) {
return fetch(url, {cache: info.request_cache[idx]})
var init = {cache: info.request_cache[idx]};
if ("request_headers" in info) {
init.headers = info.request_headers[idx];
}
return fetch(url, init)
.then(function(response) {
assert_equals(response.status, 200);
assert_equals(response.statusText, "OK");

View File

@ -14,19 +14,21 @@ def main(request, response):
ims = request.headers.get("If-Modified-Since", None)
pragma = request.headers.get("Pragma", None)
cache_control = request.headers.get("Cache-Control", None)
ignore = "ignore" in request.GET
server_state = request.server.stash.take(token)
if not server_state:
server_state = []
state = dict()
if inm:
state["If-None-Match"] = inm
if ims:
state["If-Modified-Since"] = ims
if pragma:
state["Pragma"] = pragma
if cache_control:
state["Cache-Control"] = cache_control
if not ignore:
if inm:
state["If-None-Match"] = inm
if ims:
state["If-Modified-Since"] = ims
if pragma:
state["Pragma"] = pragma
if cache_control:
state["Cache-Control"] = cache_control
server_state.append(state)
request.server.stash.put(token, server_state)