From 9025de0ae1eae02be47170b8c5780767085b8e72 Mon Sep 17 00:00:00 2001 From: Dave Camp Date: Thu, 11 Dec 2008 21:57:47 -0800 Subject: [PATCH] Bug 441751 - "Directives not to cache pages ignored." [r+sr=bzbarsky] * * * Bug 441751 - "Directives not to cache pages ignored." (fix for offline cache updating) [r+sr=bzbarsky] --- dom/src/base/nsGlobalWindow.cpp | 3 +- netwerk/protocol/http/src/nsHttpChannel.cpp | 12 +- netwerk/test/unit/test_cacheflags.js | 268 ++++++++++++++++++++ netwerk/test/unit/test_redirect_caching.js | 2 +- 4 files changed, 277 insertions(+), 8 deletions(-) create mode 100644 netwerk/test/unit/test_cacheflags.js diff --git a/dom/src/base/nsGlobalWindow.cpp b/dom/src/base/nsGlobalWindow.cpp index 9364446d61f0..3fb516930cb4 100644 --- a/dom/src/base/nsGlobalWindow.cpp +++ b/dom/src/base/nsGlobalWindow.cpp @@ -9399,7 +9399,8 @@ nsNavigator::MozIsLocallyAvailable(const nsAString &aURI, if (aWhenOffline) { loadFlags |= nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE | - nsICachingChannel::LOAD_ONLY_FROM_CACHE; + nsICachingChannel::LOAD_ONLY_FROM_CACHE | + nsIRequest::LOAD_FROM_CACHE; } nsCOMPtr channel; diff --git a/netwerk/protocol/http/src/nsHttpChannel.cpp b/netwerk/protocol/http/src/nsHttpChannel.cpp index 6ec636ccb826..7538834d276a 100644 --- a/netwerk/protocol/http/src/nsHttpChannel.cpp +++ b/netwerk/protocol/http/src/nsHttpChannel.cpp @@ -1543,7 +1543,7 @@ nsHttpChannel::OpenCacheEntry(PRBool offline, PRBool *delayed) // Set the desired cache access mode accordingly... nsCacheAccessMode accessRequested; - if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | INHIBIT_CACHING)) { + if (offline || (mLoadFlags & INHIBIT_CACHING)) { // If we have been asked to bypass the cache and not write to the // cache, then don't use the cache at all. Unless we're actually // offline, which takes precedence over BYPASS_LOCAL_CACHE. @@ -1914,15 +1914,15 @@ nsHttpChannel::CheckCache() NS_ENSURE_SUCCESS(rv, rv); buf.Adopt(0); - // Don't bother to validate LOAD_ONLY_FROM_CACHE items. // Don't bother to validate items that are read-only, // unless they are read-only because of INHIBIT_CACHING or because // we're updating the offline cache. // Don't bother to validate if this is a fallback entry. - if (mLoadFlags & LOAD_ONLY_FROM_CACHE || - (mCacheAccess == nsICache::ACCESS_READ && - !((mLoadFlags & INHIBIT_CACHING) || mCacheForOfflineUse)) || - mFallbackChannel) { + if (!mCacheForOfflineUse && + (mLoadedFromApplicationCache || + (mCacheAccess == nsICache::ACCESS_READ && + !(mLoadFlags & INHIBIT_CACHING)) || + mFallbackChannel)) { mCachedContentIsValid = PR_TRUE; return NS_OK; } diff --git a/netwerk/test/unit/test_cacheflags.js b/netwerk/test/unit/test_cacheflags.js new file mode 100644 index 000000000000..c4fb42e34cd1 --- /dev/null +++ b/netwerk/test/unit/test_cacheflags.js @@ -0,0 +1,268 @@ +do_import_script("netwerk/test/httpserver/httpd.js"); + +var httpserver = null; + +// Need to randomize, because apparently no one clears our cache +var suffix = Math.random(); +var httpBase = "http://localhost:4444"; +var httpsBase = "http://localhost:4445"; +var shortexpPath = "/shortexp" + suffix; +var longexpPath = "/longexp" + suffix; +var nocachePath = "/nocache" + suffix; +var nostorePath = "/nostore" + suffix; + +function make_channel(url, flags) { + var ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + var req = ios.newChannel(url, null, null); + req.loadFlags = flags; + return req; +} + +function Test(path, flags, expectSuccess, readFromCache, hitServer) { + this.path = path; + this.flags = flags; + this.expectSuccess = expectSuccess; + this.readFromCache = readFromCache; + this.hitServer = hitServer; +} + +Test.prototype = { + flags: 0, + expectSuccess: true, + readFromCache: false, + hitServer: true, + _buffer: "", + _isFromCache: false, + + QueryInterface: function(iid) { + if (iid.equals(Components.interfaces.nsIStreamListener) || + iid.equals(Components.interfaces.nsIRequestObserver) || + iid.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + onStartRequest: function(request, context) { + var cachingChannel = request.QueryInterface(Ci.nsICachingChannel); + this._isFromCache = request.isPending() && cachingChannel.isFromCache(); + }, + + onDataAvailable: function(request, context, stream, offset, count) { + this._buffer = this._buffer.concat(read_stream(stream, count)); + }, + + onStopRequest: function(request, context, status) { + do_check_eq(Components.isSuccessCode(status), this.expectSuccess); + do_check_eq(this._isFromCache, this.readFromCache); + do_check_eq(gHitServer, this.hitServer); + + do_timeout(0, "run_next_test();"); + }, + + run: function() { + dump("Running:" + + "\n " + this.path + + "\n " + this.flags + + "\n " + this.expectSuccess + + "\n " + this.readFromCache + + "\n " + this.hitServer + "\n"); + gHitServer = false; + var channel = make_channel(this.path, this.flags); + channel.asyncOpen(this, null); + } +}; + +var gHitServer = false; + +var gTests = [ + new Test(httpBase + shortexpPath, 0, + true, // expect success + false, // read from cache + true), // hit server + new Test(httpBase + shortexpPath, 0, + true, // expect success + true, // read from cache + true), // hit server + new Test(httpBase + shortexpPath, Ci.nsIRequest.LOAD_BYPASS_CACHE, + true, // expect success + false, // read from cache + true), // hit server + new Test(httpBase + shortexpPath, Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE, + false, // expect success + false, // read from cache + false), // hit server + new Test(httpBase + shortexpPath, + Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE | + Ci.nsIRequest.VALIDATE_NEVER, + true, // expect success + true, // read from cache + false), // hit server + new Test(httpBase + shortexpPath, Ci.nsIRequest.LOAD_FROM_CACHE, + true, // expect success + true, // read from cache + false), // hit server + + new Test(httpBase + longexpPath, 0, + true, // expect success + false, // read from cache + true), // hit server + new Test(httpBase + longexpPath, 0, + true, // expect success + true, // read from cache + false), // hit server + new Test(httpBase + longexpPath, Ci.nsIRequest.LOAD_BYPASS_CACHE, + true, // expect success + false, // read from cache + true), // hit server + new Test(httpBase + longexpPath, + Ci.nsIRequest.VALIDATE_ALWAYS, + true, // expect success + true, // read from cache + true), // hit server + new Test(httpBase + longexpPath, Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE, + true, // expect success + true, // read from cache + false), // hit server + new Test(httpBase + longexpPath, + Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE | + Ci.nsIRequest.VALIDATE_NEVER, + true, // expect success + true, // read from cache + false), // hit server + new Test(httpBase + longexpPath, + Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE | + Ci.nsIRequest.VALIDATE_ALWAYS, + false, // expect success + false, // read from cache + false), // hit server + new Test(httpBase + longexpPath, Ci.nsIRequest.LOAD_FROM_CACHE, + true, // expect success + true, // read from cache + false), // hit server + + new Test(httpBase + nocachePath, 0, + true, // expect success + false, // read from cache + true), // hit server + new Test(httpBase + nocachePath, 0, + true, // expect success + true, // read from cache + true), // hit server + new Test(httpBase + nocachePath, Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE, + false, // expect success + false, // read from cache + false), // hit server + new Test(httpBase + nocachePath, Ci.nsIRequest.LOAD_FROM_CACHE, + true, // expect success + true, // read from cache + false), // hit server + // LOAD_ONLY_FROM_CACHE would normally fail (because no-cache forces + // a validation), but VALIDATE_NEVER should override that. + new Test(httpBase + nocachePath, + Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE | + Ci.nsIRequest.VALIDATE_NEVER, + true, // expect success + true, // read from cache + false), // hit server + + // ... however, no-cache over ssl should act like no-store and force + // a validation (and therefore failure) even if VALIDATE_NEVER is + // set. + /* XXX bug 466524: We can't currently start an ssl server in xpcshell tests, + so this test is currently disabled. + new Test(httpsBase + nocachePath, + Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE | + Ci.nsIRequest.VALIDATE_NEVER, + false, // expect success + false, // read from cache + false) // hit server + */ + new Test(httpBase + nostorePath, 0, + true, // expect success + false, // read from cache + true), // hit server + new Test(httpBase + nostorePath, 0, + true, // expect success + false, // read from cache + true), // hit server + new Test(httpBase + nostorePath, Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE, + false, // expect success + false, // read from cache + false), // hit server + new Test(httpBase + nostorePath, Ci.nsIRequest.LOAD_FROM_CACHE, + true, // expect success + true, // read from cache + false), // hit server + // no-store should force the validation (and therefore failure, with + // LOAD_ONLY_FROM_CACHE) even if VALIDATE_NEVER is set. + new Test(httpBase + nostorePath, + Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE | + Ci.nsIRequest.VALIDATE_NEVER, + false, // expect success + false, // read from cache + false) // hit server + ]; + +function run_next_test() +{ + if (gTests.length == 0) { + httpserver.stop(); + do_test_finished(); + return; + } + + var test = gTests.shift(); + test.run(); +} + +function handler(metadata, response) { + gHitServer = true; + try { + var etag = metadata.getHeader("If-None-Match"); + } catch(ex) { + var etag = ""; + } + if (etag == "testtag") { + // Allow using the cached data + response.setStatusLine(metadata.httpVersion, 304, "Not Modified"); + } else { + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("Content-Type", "text/plain", false); + response.setHeader("ETag", "testtag", false); + const body = "data"; + response.bodyOutputStream.write(body, body.length); + } +} + +function nocache_handler(metadata, response) { + response.setHeader("Cache-Control", "no-cache", false); + handler(metadata, response); +} + +function nostore_handler(metadata, response) { + response.setHeader("Cache-Control", "no-store", false); + handler(metadata, response); +} + +function shortexp_handler(metadata, response) { + response.setHeader("Cache-Control", "max-age=0", false); + handler(metadata, response); +} + +function longexp_handler(metadata, response) { + response.setHeader("Cache-Control", "max-age=10000", false); + handler(metadata, response); +} + +function run_test() { + httpserver = new nsHttpServer(); + httpserver.registerPathHandler(shortexpPath, shortexp_handler); + httpserver.registerPathHandler(longexpPath, longexp_handler); + httpserver.registerPathHandler(nocachePath, nocache_handler); + httpserver.registerPathHandler(nostorePath, nostore_handler); + httpserver.start(4444); + + run_next_test(); + do_test_pending(); +} diff --git a/netwerk/test/unit/test_redirect_caching.js b/netwerk/test/unit/test_redirect_caching.js index 1fda7c4abb6c..c752998d30ab 100644 --- a/netwerk/test/unit/test_redirect_caching.js +++ b/netwerk/test/unit/test_redirect_caching.js @@ -30,7 +30,7 @@ function firstTimeThrough(request, buffer) { do_check_eq(buffer, responseBody); var chan = make_channel(randomURI); - chan.loadFlags |= Ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE; + chan.loadFlags |= Ci.nsICachingChannel.LOAD_FROM_CACHE; chan.asyncOpen(new ChannelListener(finish_test, null), null); }