mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 475156 - correcting cache behavior when using custom conditional request headers, r=bzbarsky
This commit is contained in:
parent
1d0897eeec
commit
8f6a5222bd
@ -321,6 +321,8 @@ _TEST_FILES = test_bug5141.html \
|
||||
test_classList.html \
|
||||
test_bug514487.html \
|
||||
test_range_bounds.html \
|
||||
test_bug475156.html \
|
||||
bug475156.sjs \
|
||||
$(NULL)
|
||||
|
||||
# Disabled; see bug 492181
|
||||
|
27
content/base/test/bug475156.sjs
Normal file
27
content/base/test/bug475156.sjs
Normal file
@ -0,0 +1,27 @@
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
if (request.queryString == "")
|
||||
{
|
||||
var etag = request.hasHeader("If-Match") ? request.getHeader("If-Match") : null;
|
||||
if (!etag || etag == getState("etag"))
|
||||
{
|
||||
response.setStatusLine(request.httpVersion, 200, "Ok");
|
||||
response.setHeader("Content-Type", "text/html");
|
||||
response.setHeader("ETag", getState("etag"));
|
||||
response.setHeader("Cache-control", "max-age=36000");
|
||||
response.write(getState("etag"));
|
||||
}
|
||||
else if (etag)
|
||||
{
|
||||
response.setStatusLine(request.httpVersion, 412, "Precondition Failed");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var etag = request.queryString.match(/^etag=(.*)$/);
|
||||
if (etag)
|
||||
setState("etag", etag[1]);
|
||||
|
||||
response.setStatusLine(request.httpVersion, 204, "No content");
|
||||
}
|
||||
}
|
288
content/base/test/test_bug475156.html
Normal file
288
content/base/test/test_bug475156.html
Normal file
@ -0,0 +1,288 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=475156
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 475156</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body onload="drive(tests.shift());">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var path = "http://localhost:8888/tests/content/base/test/";
|
||||
|
||||
function fromCache(xhr)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var ch = xhr.channel.QueryInterface(Components.interfaces.nsICachingChannel);
|
||||
return ch.isFromCache();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
// First just init the file with an ETag
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs?etag=a1");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
},
|
||||
},
|
||||
|
||||
// Try to load the file the first time regularly, we have to get 200 OK
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
is(fromCache(xhr), false, "Not coming from the cache");
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
is(xhr.status, 200, "We get a fresh version of the file");
|
||||
is(xhr.getResponseHeader("Etag"), "a1", "We got correct ETag");
|
||||
is(xhr.responseText, "a1", "We got the expected file body");
|
||||
},
|
||||
},
|
||||
|
||||
// Try to load the file the second time regularly, we have to get 304 Not Modified
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs");
|
||||
xhr.setRequestHeader("If-Match", "a1");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
is(fromCache(xhr), true, "Coming from the cache");
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
is(xhr.status, 200, "We got cached version");
|
||||
is(xhr.getResponseHeader("Etag"), "a1", "We got correct ETag");
|
||||
is(xhr.responseText, "a1", "We got the expected file body");
|
||||
},
|
||||
},
|
||||
|
||||
// Try to load the file the third time regularly, we have to get 304 Not Modified
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs");
|
||||
xhr.setRequestHeader("If-Match", "a1");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
is(fromCache(xhr), true, "Coming from the cache");
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
is(xhr.status, 200, "We got cached version");
|
||||
is(xhr.getResponseHeader("Etag"), "a1", "We got correct ETag");
|
||||
is(xhr.responseText, "a1", "We got the expected file body");
|
||||
},
|
||||
},
|
||||
|
||||
// Now modify the ETag
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs?etag=a2");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
},
|
||||
},
|
||||
|
||||
// Try to load the file, we have to get 200 OK with the new content
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs");
|
||||
xhr.setRequestHeader("If-Match", "a2");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
is(fromCache(xhr), false, "Not coming from the cache");
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
is(xhr.status, 200, "We get a fresh version of the file");
|
||||
is(xhr.getResponseHeader("Etag"), "a2", "We got correct ETag");
|
||||
is(xhr.responseText, "a2", "We got the expected file body");
|
||||
},
|
||||
},
|
||||
|
||||
// Try to load the file the second time regularly, we have to get 304 Not Modified
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs");
|
||||
xhr.setRequestHeader("If-Match", "a2");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
is(fromCache(xhr), true, "Coming from the cache");
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
is(xhr.status, 200, "We got cached version");
|
||||
is(xhr.getResponseHeader("Etag"), "a2", "We got correct ETag");
|
||||
is(xhr.responseText, "a2", "We got the expected file body");
|
||||
},
|
||||
},
|
||||
|
||||
// Try to load the file the third time regularly, we have to get 304 Not Modified
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs");
|
||||
xhr.setRequestHeader("If-Match", "a2");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
is(fromCache(xhr), true, "Coming from the cache");
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
is(xhr.status, 200, "We got cached version");
|
||||
is(xhr.getResponseHeader("Etag"), "a2", "We got correct ETag");
|
||||
is(xhr.responseText, "a2", "We got the expected file body");
|
||||
},
|
||||
},
|
||||
|
||||
// Now modify the ETag ones more
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs?etag=a3");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
},
|
||||
},
|
||||
|
||||
// Try to load the file, we have to get 200 OK with the new content
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs");
|
||||
xhr.setRequestHeader("If-Match", "a3");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
is(fromCache(xhr), false, "Not coming from the cache");
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
is(xhr.status, 200, "We get a fresh version of the file");
|
||||
is(xhr.getResponseHeader("Etag"), "a3", "We got correct ETag");
|
||||
is(xhr.responseText, "a3", "We got the expected file body");
|
||||
},
|
||||
},
|
||||
|
||||
// Try to load the file the second time regularly, we have to get 304 Not Modified
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs");
|
||||
xhr.setRequestHeader("If-Match", "a3");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
is(fromCache(xhr), true, "Coming from the cache");
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
is(xhr.status, 200, "We got cached version");
|
||||
is(xhr.getResponseHeader("Etag"), "a3", "We got correct ETag");
|
||||
is(xhr.responseText, "a3", "We got the expected file body");
|
||||
},
|
||||
},
|
||||
|
||||
// Try to load the file the third time regularly, we have to get 304 Not Modified
|
||||
{
|
||||
init: function(xhr)
|
||||
{
|
||||
xhr.open("GET", path + "bug475156.sjs");
|
||||
xhr.setRequestHeader("If-Match", "a3");
|
||||
},
|
||||
|
||||
loading: function(xhr)
|
||||
{
|
||||
is(fromCache(xhr), true, "Coming from the cache");
|
||||
},
|
||||
|
||||
done: function(xhr)
|
||||
{
|
||||
is(xhr.status, 200, "We got cached version");
|
||||
is(xhr.getResponseHeader("Etag"), "a3", "We got correct ETag");
|
||||
is(xhr.responseText, "a3", "We got the expected file body");
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
function drive(test)
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
test.init(xhr);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState == 3) {
|
||||
test.loading(this);
|
||||
}
|
||||
if (this.readyState == 4) {
|
||||
test.done(this);
|
||||
if (tests.length == 0)
|
||||
SimpleTest.finish();
|
||||
else
|
||||
drive(tests.shift());
|
||||
}
|
||||
}
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -138,6 +138,7 @@ nsHttpChannel::nsHttpChannel()
|
||||
, mLoadedFromApplicationCache(PR_FALSE)
|
||||
, mTracingEnabled(PR_TRUE)
|
||||
, mForceAllowThirdPartyCookie(PR_FALSE)
|
||||
, mCustomConditionalRequest(PR_FALSE)
|
||||
{
|
||||
LOG(("Creating nsHttpChannel @%x\n", this));
|
||||
|
||||
@ -273,17 +274,6 @@ nsHttpChannel::AsyncCall(nsAsyncCallback funcPtr,
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHttpChannel::RequestIsConditional()
|
||||
{
|
||||
// Is our consumer issuing a conditional request?
|
||||
return mRequestHead.PeekHeader(nsHttp::If_Modified_Since) ||
|
||||
mRequestHead.PeekHeader(nsHttp::If_None_Match) ||
|
||||
mRequestHead.PeekHeader(nsHttp::If_Unmodified_Since) ||
|
||||
mRequestHead.PeekHeader(nsHttp::If_Match) ||
|
||||
mRequestHead.PeekHeader(nsHttp::If_Range);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpChannel::Connect(PRBool firstTime)
|
||||
{
|
||||
@ -1501,6 +1491,9 @@ nsHttpChannel::ProcessNotModified()
|
||||
|
||||
LOG(("nsHttpChannel::ProcessNotModified [this=%x]\n", this));
|
||||
|
||||
if (mCustomConditionalRequest)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
|
||||
|
||||
@ -1682,12 +1675,6 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed)
|
||||
if (IsSubRangeRequest(mRequestHead))
|
||||
return NS_OK;
|
||||
|
||||
if (RequestIsConditional()) {
|
||||
// don't use the cache if our consumer is making a conditional request
|
||||
// (see bug 331825).
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
GenerateCacheKey(mPostID, cacheKey);
|
||||
|
||||
// Get a cache session with appropriate storage policy
|
||||
@ -1882,12 +1869,6 @@ nsHttpChannel::OpenOfflineCacheEntryForWriting()
|
||||
if (IsSubRangeRequest(mRequestHead))
|
||||
return NS_OK;
|
||||
|
||||
if (RequestIsConditional()) {
|
||||
// don't use the cache if our consumer is making a conditional request
|
||||
// (see bug 331825).
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCAutoString cacheKey;
|
||||
GenerateCacheKey(mPostID, cacheKey);
|
||||
|
||||
@ -2108,9 +2089,12 @@ nsHttpChannel::CheckCache()
|
||||
PRBool doValidation = PR_FALSE;
|
||||
PRBool canAddImsHeader = PR_TRUE;
|
||||
|
||||
// Be optimistic: assume that we won't need to do validation
|
||||
mRequestHead.ClearHeader(nsHttp::If_Modified_Since);
|
||||
mRequestHead.ClearHeader(nsHttp::If_None_Match);
|
||||
mCustomConditionalRequest =
|
||||
mRequestHead.PeekHeader(nsHttp::If_Modified_Since) ||
|
||||
mRequestHead.PeekHeader(nsHttp::If_None_Match) ||
|
||||
mRequestHead.PeekHeader(nsHttp::If_Unmodified_Since) ||
|
||||
mRequestHead.PeekHeader(nsHttp::If_Match) ||
|
||||
mRequestHead.PeekHeader(nsHttp::If_Range);
|
||||
|
||||
// If the LOAD_FROM_CACHE flag is set, any cached data can simply be used.
|
||||
if (mLoadFlags & LOAD_FROM_CACHE) {
|
||||
@ -2187,6 +2171,20 @@ nsHttpChannel::CheckCache()
|
||||
LOG(("%salidating based on expiration time\n", doValidation ? "V" : "Not v"));
|
||||
}
|
||||
|
||||
if (!doValidation && mRequestHead.PeekHeader(nsHttp::If_Match) &&
|
||||
(method == nsHttp::Get || method == nsHttp::Head)) {
|
||||
const char *requestedETag, *cachedETag;
|
||||
cachedETag = mCachedResponseHead->PeekHeader(nsHttp::ETag);
|
||||
requestedETag = mRequestHead.PeekHeader(nsHttp::If_Match);
|
||||
if (cachedETag && (!strncmp(cachedETag, "W/", 2) ||
|
||||
strcmp(requestedETag, cachedETag))) {
|
||||
// User has defined If-Match header, if the cached entry is not
|
||||
// matching the provided header value or the cached ETag is weak,
|
||||
// force validation.
|
||||
doValidation = PR_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doValidation) {
|
||||
//
|
||||
// Check the authorization headers used to generate the cache entry.
|
||||
@ -2226,9 +2224,11 @@ nsHttpChannel::CheckCache()
|
||||
//
|
||||
// the request method MUST be either GET or HEAD (see bug 175641).
|
||||
//
|
||||
// do not override conditional headers when consumer has defined its own
|
||||
if (!mCachedResponseHead->NoStore() &&
|
||||
(mRequestHead.Method() == nsHttp::Get ||
|
||||
mRequestHead.Method() == nsHttp::Head)) {
|
||||
mRequestHead.Method() == nsHttp::Head) &&
|
||||
!mCustomConditionalRequest) {
|
||||
const char *val;
|
||||
// Add If-Modified-Since header if a Last-Modified was given
|
||||
// and we are allowed to do this (see bugs 510359 and 269303)
|
||||
|
@ -385,6 +385,10 @@ private:
|
||||
PRUint32 mLoadedFromApplicationCache : 1;
|
||||
PRUint32 mTracingEnabled : 1;
|
||||
PRUint32 mForceAllowThirdPartyCookie : 1;
|
||||
// True if consumer added it's own If-None-Match or If-Modified-Since
|
||||
// headers. In such a case we must not override them in the cache code
|
||||
// and also we want to pass possible 304 code response through.
|
||||
PRUint32 mCustomConditionalRequest : 1;
|
||||
|
||||
class nsContentEncodings : public nsIUTF8StringEnumerator
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user