зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1064258 - Allow caching channels only store metadata, r=jduell
This commit is contained in:
Родитель
f443b192ef
Коммит
dcb7bd8028
|
@ -17,7 +17,7 @@ interface nsIFile;
|
|||
* 3) Support for uniquely identifying cached data in cases when the URL
|
||||
* is insufficient (e.g., HTTP form submission).
|
||||
*/
|
||||
[scriptable, uuid(a77b664e-e707-4017-9c03-47bcedcb5b05)]
|
||||
[scriptable, uuid(3d46b469-7405-416e-ba42-84899963b403)]
|
||||
interface nsICachingChannel : nsICacheInfoChannel
|
||||
{
|
||||
/**
|
||||
|
@ -64,6 +64,14 @@ interface nsICachingChannel : nsICacheInfoChannel
|
|||
*/
|
||||
attribute nsISupports cacheKey;
|
||||
|
||||
/**
|
||||
* Instructs the channel to only store the metadata of the entry, and not
|
||||
* the content. When reading an existing entry, this automatically sets
|
||||
* LOAD_ONLY_IF_MODIFIED flag.
|
||||
* Must be called before asyncOpen().
|
||||
*/
|
||||
attribute boolean cacheOnlyMetadata;
|
||||
|
||||
/**************************************************************************
|
||||
* Caching channel specific load flags:
|
||||
*/
|
||||
|
|
|
@ -208,6 +208,7 @@ nsHttpChannel::nsHttpChannel()
|
|||
, mOfflineCacheLastModifiedTime(0)
|
||||
, mCachedContentIsValid(false)
|
||||
, mCachedContentIsPartial(false)
|
||||
, mCacheOnlyMetadata(false)
|
||||
, mTransactionReplaced(false)
|
||||
, mAuthRetryPending(false)
|
||||
, mProxyAuthPending(false)
|
||||
|
@ -2826,8 +2827,16 @@ nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appC
|
|||
if (mCachedContentIsPartial) {
|
||||
rv = OpenCacheInputStream(entry, false, !!appCache);
|
||||
*aResult = ENTRY_NEEDS_REVALIDATION;
|
||||
return rv;
|
||||
} else if (size == 0 && mCacheOnlyMetadata) {
|
||||
// Don't break cache entry load when the entry's data size
|
||||
// is 0 and mCacheOnlyMetadata flag is set. In that case we
|
||||
// want to proceed since the LOAD_ONLY_IF_MODIFIED flag is
|
||||
// also set.
|
||||
MOZ_ASSERT(mLoadFlags & LOAD_ONLY_IF_MODIFIED);
|
||||
} else {
|
||||
return rv;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4003,14 +4012,24 @@ nsHttpChannel::InstallCacheListener(int64_t offset)
|
|||
nsCOMPtr<nsIOutputStream> out;
|
||||
rv = mCacheEntry->OpenOutputStream(offset, getter_AddRefs(out));
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
LOG((" entry doomed, not writing it [channel=%p]", this));
|
||||
// Entry is already doomed.
|
||||
// This may happen when expiration time is set to past and the entry
|
||||
// has been removed by the background eviction logic.
|
||||
return NS_OK;
|
||||
LOG((" entry doomed, not writing it [channel=%p]", this));
|
||||
// Entry is already doomed.
|
||||
// This may happen when expiration time is set to past and the entry
|
||||
// has been removed by the background eviction logic.
|
||||
return NS_OK;
|
||||
}
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (mCacheOnlyMetadata) {
|
||||
LOG(("Not storing content, cacheOnlyMetadata set"));
|
||||
// We must open and then close the output stream of the cache entry.
|
||||
// This way we indicate the content has been written (despite with zero
|
||||
// length) and the entry is now in the ready state with "having data".
|
||||
|
||||
out->Close();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// XXX disk cache does not support overlapped i/o yet
|
||||
#if 0
|
||||
// Mark entry valid inorder to allow simultaneous reading...
|
||||
|
@ -5716,6 +5735,30 @@ nsHttpChannel::SetCacheKey(nsISupports *key)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::GetCacheOnlyMetadata(bool *aOnlyMetadata)
|
||||
{
|
||||
NS_ENSURE_ARG(aOnlyMetadata);
|
||||
*aOnlyMetadata = mCacheOnlyMetadata;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHttpChannel::SetCacheOnlyMetadata(bool aOnlyMetadata)
|
||||
{
|
||||
LOG(("nsHttpChannel::SetCacheOnlyMetadata [this=%p only-metadata=%d]\n",
|
||||
this, aOnlyMetadata));
|
||||
|
||||
ENSURE_CALLED_BEFORE_ASYNC_OPEN();
|
||||
|
||||
mCacheOnlyMetadata = aOnlyMetadata;
|
||||
if (aOnlyMetadata) {
|
||||
mLoadFlags |= LOAD_ONLY_IF_MODIFIED;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpChannel::nsIResumableChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
@ -385,6 +385,7 @@ private:
|
|||
// state flags
|
||||
uint32_t mCachedContentIsValid : 1;
|
||||
uint32_t mCachedContentIsPartial : 1;
|
||||
uint32_t mCacheOnlyMetadata : 1;
|
||||
uint32_t mTransactionReplaced : 1;
|
||||
uint32_t mAuthRetryPending : 1;
|
||||
uint32_t mProxyAuthPending : 1;
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
/**
|
||||
* Check how nsICachingChannel.cacheOnlyMetadata works.
|
||||
* - all channels involved in this test are set cacheOnlyMetadata = true
|
||||
* - do a previously uncached request for a long living content
|
||||
* - check we have downloaded the content from the server (channel provides it)
|
||||
* - check the entry has metadata, but zero-length content
|
||||
* - load the same URL again, now cached
|
||||
* - check the channel is giving no content (no call to OnDataAvailable) but succeeds
|
||||
* - repeat again, but for a different URL that is not cached (immediately expires)
|
||||
* - only difference is that we get a newer version of the content from the server during the second request
|
||||
*/
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "URL", function() {
|
||||
return "http://localhost:" + httpServer.identity.primaryPort;
|
||||
});
|
||||
|
||||
var httpServer = null;
|
||||
|
||||
function make_channel(url, callback, ctx) {
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].
|
||||
getService(Ci.nsIIOService);
|
||||
return ios.newChannel(url, "", null);
|
||||
}
|
||||
|
||||
const responseBody1 = "response body 1";
|
||||
const responseBody2a = "response body 2a";
|
||||
const responseBody2b = "response body 2b";
|
||||
|
||||
function contentHandler1(metadata, response)
|
||||
{
|
||||
response.setHeader("Content-Type", "text/plain");
|
||||
response.setHeader("Cache-control", "max-age=999999");
|
||||
response.bodyOutputStream.write(responseBody1, responseBody1.length);
|
||||
}
|
||||
|
||||
var content2passCount = 0;
|
||||
|
||||
function contentHandler2(metadata, response)
|
||||
{
|
||||
response.setHeader("Content-Type", "text/plain");
|
||||
response.setHeader("Cache-control", "no-cache");
|
||||
switch (content2passCount++) {
|
||||
case 0:
|
||||
response.setHeader("ETag", "testetag");
|
||||
response.bodyOutputStream.write(responseBody2a, responseBody2a.length);
|
||||
break;
|
||||
case 1:
|
||||
do_check_true(metadata.hasHeader("If-None-Match"));
|
||||
do_check_eq(metadata.getHeader("If-None-Match"), "testetag");
|
||||
response.bodyOutputStream.write(responseBody2b, responseBody2b.length);
|
||||
break;
|
||||
default:
|
||||
throw "Unexpected request in the test";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function run_test()
|
||||
{
|
||||
httpServer = new HttpServer();
|
||||
httpServer.registerPathHandler("/content1", contentHandler1);
|
||||
httpServer.registerPathHandler("/content2", contentHandler2);
|
||||
httpServer.start(-1);
|
||||
|
||||
run_test_content1a();
|
||||
do_test_pending();
|
||||
}
|
||||
|
||||
function run_test_content1a()
|
||||
{
|
||||
var chan = make_channel(URL + "/content1");
|
||||
caching = chan.QueryInterface(Ci.nsICachingChannel);
|
||||
caching.cacheOnlyMetadata = true;
|
||||
chan.asyncOpen(new ChannelListener(contentListener1a, null), null);
|
||||
}
|
||||
|
||||
function contentListener1a(request, buffer)
|
||||
{
|
||||
do_check_eq(buffer, responseBody1);
|
||||
|
||||
asyncOpenCacheEntry(URL + "/content1", "disk", 0, null, cacheCheck1)
|
||||
}
|
||||
|
||||
function cacheCheck1(status, entry)
|
||||
{
|
||||
do_check_eq(status, 0);
|
||||
do_check_eq(entry.dataSize, 0);
|
||||
try {
|
||||
do_check_neq(entry.getMetaDataElement("response-head"), null);
|
||||
}
|
||||
catch (ex) {
|
||||
do_throw("Missing response head");
|
||||
}
|
||||
|
||||
var chan = make_channel(URL + "/content1");
|
||||
caching = chan.QueryInterface(Ci.nsICachingChannel);
|
||||
caching.cacheOnlyMetadata = true;
|
||||
chan.asyncOpen(new ChannelListener(contentListener1b, null, CL_IGNORE_CL), null);
|
||||
}
|
||||
|
||||
function contentListener1b(request, buffer)
|
||||
{
|
||||
request.QueryInterface(Ci.nsIHttpChannel);
|
||||
do_check_eq(request.requestMethod, "GET");
|
||||
do_check_eq(request.responseStatus, 200);
|
||||
do_check_eq(request.getResponseHeader("Cache-control"), "max-age=999999");
|
||||
|
||||
do_check_eq(buffer, "");
|
||||
run_test_content2a();
|
||||
}
|
||||
|
||||
// Now same set of steps but this time for an immediately expiring content.
|
||||
|
||||
function run_test_content2a()
|
||||
{
|
||||
var chan = make_channel(URL + "/content2");
|
||||
caching = chan.QueryInterface(Ci.nsICachingChannel);
|
||||
caching.cacheOnlyMetadata = true;
|
||||
chan.asyncOpen(new ChannelListener(contentListener2a, null), null);
|
||||
}
|
||||
|
||||
function contentListener2a(request, buffer)
|
||||
{
|
||||
do_check_eq(buffer, responseBody2a);
|
||||
|
||||
asyncOpenCacheEntry(URL + "/content2", "disk", 0, null, cacheCheck2)
|
||||
}
|
||||
|
||||
function cacheCheck2(status, entry)
|
||||
{
|
||||
do_check_eq(status, 0);
|
||||
do_check_eq(entry.dataSize, 0);
|
||||
try {
|
||||
do_check_neq(entry.getMetaDataElement("response-head"), null);
|
||||
do_check_true(entry.getMetaDataElement("response-head").match('Etag: testetag'));
|
||||
}
|
||||
catch (ex) {
|
||||
do_throw("Missing response head");
|
||||
}
|
||||
|
||||
var chan = make_channel(URL + "/content2");
|
||||
caching = chan.QueryInterface(Ci.nsICachingChannel);
|
||||
caching.cacheOnlyMetadata = true;
|
||||
chan.asyncOpen(new ChannelListener(contentListener2b, null), null);
|
||||
}
|
||||
|
||||
function contentListener2b(request, buffer)
|
||||
{
|
||||
do_check_eq(buffer, responseBody2b);
|
||||
|
||||
httpServer.stop(do_test_finished);
|
||||
}
|
|
@ -158,6 +158,7 @@ skip-if = os == "android"
|
|||
# Allocating 4GB might actually succeed on 64 bit machines
|
||||
skip-if = bits != 32
|
||||
[test_bug935499.js]
|
||||
[test_bug1064258.js]
|
||||
[test_udpsocket.js]
|
||||
[test_doomentry.js]
|
||||
[test_cacheflags.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче