Bug 722034 - Part 4 - Make a copy of the information needed during cache validation in preparation for moving cache validation to the cache thread, r=honzab

This commit is contained in:
Brian Smith 2012-05-31 15:20:05 -07:00
parent 1b457f2f45
commit fbced92697
2 changed files with 171 additions and 54 deletions

View File

@ -141,6 +141,76 @@ AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded)
vetoHook->OnRedirectResult(succeeded);
}
class HttpCacheQuery : public nsICacheListener
{
public:
HttpCacheQuery(nsHttpChannel * channel,
const nsACString & cacheKey,
bool usingSSL,
bool loadedFromApplicationCache)
// in
: mChannel(channel)
, mURI(channel->mURI)
, mLoadFlags(channel->mLoadFlags)
, mCacheForOfflineUse(channel->mCacheForOfflineUse)
, mFallbackChannel(channel->mFallbackChannel)
, mCacheKey(cacheKey)
, mUsingSSL(usingSSL)
, mLoadedFromApplicationCache(loadedFromApplicationCache)
// internal
, mCacheAccess(0)
, mStatus(NS_ERROR_NOT_INITIALIZED)
// in/out
, mRequestHead(channel->mRequestHead)
, mRedirectedCachekeys(channel->mRedirectedCachekeys.forget())
// out
, mCachedContentIsValid(false)
, mCachedContentIsPartial(false)
, mCustomConditionalRequest(false)
, mDidReval(false)
{
MOZ_ASSERT(NS_IsMainThread());
}
private:
NS_DECL_ISUPPORTS
NS_DECL_NSICACHELISTENER
nsresult CheckCache();
bool ResponseWouldVary() const;
bool MustValidateBasedOnQueryUrl() const;
nsresult SetupByteRangeRequest(PRUint32 partialLen);
nsresult StartBufferingCachedEntity();
nsCOMPtr<nsICacheListener> mChannel;
const nsCOMPtr<nsIURI> mURI;
const PRUint32 mLoadFlags;
const bool mCacheForOfflineUse;
const bool mFallbackChannel;
const InfallableCopyCString mCacheKey;
const bool mUsingSSL;
const bool mLoadedFromApplicationCache;
// Used only internally
nsCOMPtr<nsICacheEntryDescriptor> mCacheEntry;
nsCacheAccessMode mCacheAccess;
nsresult mStatus;
// Copied from HttpcacheQuery into nsHttpChannel by nsHttpChannel
friend class nsHttpChannel;
/*in/out*/ nsHttpRequestHead mRequestHead;
/*in/out*/ nsAutoPtr<nsTArray<nsCString> > mRedirectedCachekeys;
/*out*/ AutoClose<nsIAsyncInputStream> mCacheAsyncInputStream;
/*out*/ nsAutoPtr<nsHttpResponseHead> mCachedResponseHead;
/*out*/ nsCOMPtr<nsISupports> mCachedSecurityInfo;
/*out*/ bool mCachedContentIsValid;
/*out*/ bool mCachedContentIsPartial;
/*out*/ bool mCustomConditionalRequest;
/*out*/ bool mDidReval;
};
NS_IMPL_THREADSAFE_ISUPPORTS1(HttpCacheQuery, nsICacheListener)
//-----------------------------------------------------------------------------
// nsHttpChannel <public>
//-----------------------------------------------------------------------------
@ -270,7 +340,7 @@ nsHttpChannel::Connect()
}
// open a cache entry for this channel...
rv = OpenCacheEntry();
rv = OpenCacheEntry(usingSSL);
// do not continue if asyncOpenCacheEntry is in progress
if (mOnCacheEntryAvailableCallback) {
@ -311,20 +381,13 @@ nsHttpChannel::ContinueConnect()
{
// we may or may not have a cache entry at this point
if (mCacheEntry) {
// inspect the cache entry to determine whether or not we need to go
// out to net to validate it. this call sets mCachedContentIsValid
// and may set request headers as required for cache validation.
nsresult rv = CheckCache();
if (NS_FAILED(rv))
NS_WARNING("cache check failed");
// read straight from the cache if possible...
if (mCachedContentIsValid) {
nsRunnableMethod<nsHttpChannel> *event = nsnull;
if (!mCachedContentIsPartial) {
AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse, &event);
}
rv = ReadFromCache(true);
nsresult rv = ReadFromCache(true);
if (NS_FAILED(rv) && event) {
event->Revoke();
}
@ -1676,7 +1739,7 @@ nsHttpChannel::ResolveProxy()
}
bool
nsHttpChannel::ResponseWouldVary()
HttpCacheQuery::ResponseWouldVary() const
{
nsresult rv;
nsCAutoString buf, metaKey;
@ -1688,9 +1751,9 @@ nsHttpChannel::ResponseWouldVary()
char *val = buf.BeginWriting(); // going to munge buf
char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
while (token) {
LOG(("nsHttpChannel::ResponseWouldVary [this=%x] " \
LOG(("HttpCacheQuery::ResponseWouldVary [channel=%p] " \
"processing %s\n",
this, token));
mChannel.get(), token));
//
// if "*", then assume response would vary. technically speaking,
// "Vary: header, *" is not permitted, but we allow it anyways.
@ -1714,8 +1777,9 @@ nsHttpChannel::ResponseWouldVary()
// since changed. if so, then indeed the cached response is invalid.
nsXPIDLCString lastVal;
mCacheEntry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal));
LOG(("nsHttpChannel::ResponseWouldVary [this=%x] " \
"stored value = %c%s%c\n", this, '"', lastVal.get(), '"'));
LOG(("HttpCacheQuery::ResponseWouldVary [channel=%p] "
"stored value = \"%s\"\n",
mChannel.get(), lastVal.get()));
// Look for value of "Cookie" in the request headers
nsHttpAtom atom = nsHttp::ResolveAtom(token);
@ -1737,9 +1801,9 @@ nsHttpChannel::ResponseWouldVary()
return true;
newVal = hash.get();
LOG(("nsHttpChannel::ResponseWouldVary [this=%x] " \
LOG(("HttpCacheQuery::ResponseWouldVary [this=%p] " \
"set-cookie value hashed to %s\n",
this, newVal));
mChannel.get(), newVal));
}
if (strcmp(newVal, lastVal))
@ -1882,7 +1946,7 @@ nsHttpChannel::EnsureAssocReq()
//-----------------------------------------------------------------------------
nsresult
nsHttpChannel::SetupByteRangeRequest(PRUint32 partialLen)
HttpCacheQuery::SetupByteRangeRequest(PRUint32 partialLen)
{
// cached content has been found to be partial, add necessary request
// headers to complete cache entry.
@ -2224,7 +2288,7 @@ IsSubRangeRequest(nsHttpRequestHead &aRequestHead)
}
nsresult
nsHttpChannel::OpenCacheEntry()
nsHttpChannel::OpenCacheEntry(bool usingSSL)
{
nsresult rv;
@ -2301,6 +2365,8 @@ nsHttpChannel::OpenCacheEntry()
nsCAutoString appCacheClientID;
mApplicationCache->GetClientID(appCacheClientID);
mCacheQuery = new HttpCacheQuery(this, cacheKey, usingSSL, true);
nsCOMPtr<nsICacheService> serv =
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
@ -2321,19 +2387,20 @@ nsHttpChannel::OpenCacheEntry()
rv = session->AsyncOpenCacheEntry(
cacheKey,
nsICache::ACCESS_READ,
this,
mCacheQuery,
mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
if (NS_SUCCEEDED(rv))
return NS_OK;
mCacheQuery = nsnull;
mOnCacheEntryAvailableCallback = nsnull;
// opening cache entry failed
return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE, rv);
}
return OpenNormalCacheEntry();
return OpenNormalCacheEntry(usingSSL);
}
nsresult
@ -2351,6 +2418,8 @@ nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
mCacheAccess = aAccess;
}
// XXX: shouldn't we fail here? I thought we're supposed to fail the
// connection if we can't open an offline cache entry for writing.
if (aEntryStatus == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
LOG(("bypassing local cache since it is busy\n"));
// Don't try to load normal cache entry
@ -2398,12 +2467,14 @@ nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
}
}
return OpenNormalCacheEntry();
bool usingSSL = false;
(void) mURI->SchemeIs("https", &usingSSL);
return OpenNormalCacheEntry(usingSSL);
}
nsresult
nsHttpChannel::OpenNormalCacheEntry()
nsHttpChannel::OpenNormalCacheEntry(bool usingSSL)
{
NS_ASSERTION(!mCacheEntry, "We have already mCacheEntry");
@ -2412,6 +2483,8 @@ nsHttpChannel::OpenNormalCacheEntry()
nsCAutoString cacheKey;
GenerateCacheKey(mPostID, cacheKey);
mCacheQuery = new HttpCacheQuery(this, cacheKey, usingSSL, false);
nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy();
nsCOMPtr<nsICacheSession> session;
@ -2431,12 +2504,13 @@ nsHttpChannel::OpenNormalCacheEntry()
rv = session->AsyncOpenCacheEntry(
cacheKey,
accessRequested,
this,
mCacheQuery,
mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
if (NS_SUCCEEDED(rv))
return NS_OK;
mCacheQuery = nsnull;
mOnCacheEntryAvailableCallback = nsnull;
return rv;
@ -2647,12 +2721,39 @@ nsHttpChannel::UpdateExpirationTime()
return NS_OK;
}
// CheckCache is called from Connect after a cache entry has been opened for
// this URL but before going out to net. It's purpose is to set or clear the
// mCachedContentIsValid flag, and to configure an If-Modified-Since request
// if validation is required.
NS_IMETHODIMP
HttpCacheQuery::OnCacheEntryDoomed(nsresult)
{
return NS_ERROR_UNEXPECTED;
}
NS_IMETHODIMP
HttpCacheQuery::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
nsCacheAccessMode access,
nsresult status)
{
LOG(("HttpCacheQuery::OnCacheEntryAvailable [channel=%p entry=%p "
"access=%x status=%x]\n", mChannel.get(), entry, access, status));
mCacheEntry = entry;
mCacheAccess = access;
mStatus = status;
nsresult rv = CheckCache();
if (NS_FAILED(rv))
NS_WARNING("cache check failed");
// break cycles
nsCOMPtr<nsICacheListener> channel = mChannel.forget();
mCacheEntry = nsnull;
rv = channel->OnCacheEntryAvailable(entry, access, status);
return rv;
}
nsresult
nsHttpChannel::CheckCache()
HttpCacheQuery::CheckCache()
{
nsresult rv = NS_OK;
@ -2660,8 +2761,8 @@ nsHttpChannel::CheckCache()
rv = mURI->SchemeIs("https", &usingSSL);
NS_ENSURE_SUCCESS(rv,rv);
LOG(("nsHTTPChannel::CheckCache enter [this=%p entry=%p access=%d]",
this, mCacheEntry.get(), mCacheAccess));
LOG(("HttpCacheQuery::CheckCache enter [channel=%p entry=%p access=%d]",
mChannel.get(), mCacheEntry.get(), mCacheAccess));
// Be pessimistic: assume the cache entry has no useful data.
mCachedContentIsValid = false;
@ -2719,9 +2820,9 @@ nsHttpChannel::CheckCache()
if (!mCacheForOfflineUse &&
(mLoadedFromApplicationCache ||
(mCacheAccess == nsICache::ACCESS_READ &&
!(mLoadFlags & INHIBIT_CACHING)) ||
!(mLoadFlags & nsIRequest::INHIBIT_CACHING)) ||
mFallbackChannel)) {
rv = StartBufferingCachedEntity(usingSSL);
rv = StartBufferingCachedEntity();
if (NS_SUCCEEDED(rv)) {
mCachedContentIsValid = true;
// XXX: Isn't the cache entry already valid?
@ -2770,7 +2871,7 @@ nsHttpChannel::CheckCache()
rv = SetupByteRangeRequest(size);
mCachedContentIsPartial = NS_SUCCEEDED(rv);
if (mCachedContentIsPartial) {
rv = StartBufferingCachedEntity(usingSSL);
rv = StartBufferingCachedEntity();
} else {
// Make the request unconditional again.
mRequestHead.ClearHeader(nsHttp::Range);
@ -2792,24 +2893,24 @@ nsHttpChannel::CheckCache()
doValidation = true;
}
// If the LOAD_FROM_CACHE flag is set, any cached data can simply be used
else if (mLoadFlags & LOAD_FROM_CACHE) {
else if (mLoadFlags & nsIRequest::LOAD_FROM_CACHE) {
LOG(("NOT validating based on LOAD_FROM_CACHE load flag\n"));
doValidation = false;
}
// If the VALIDATE_ALWAYS flag is set, any cached data won't be used until
// it's revalidated with the server.
else if (mLoadFlags & VALIDATE_ALWAYS) {
else if (mLoadFlags & nsIRequest::VALIDATE_ALWAYS) {
LOG(("Validating based on VALIDATE_ALWAYS load flag\n"));
doValidation = true;
}
// Even if the VALIDATE_NEVER flag is set, there are still some cases in
// which we must validate the cached response with the server.
else if (mLoadFlags & VALIDATE_NEVER) {
else if (mLoadFlags & nsIRequest::VALIDATE_NEVER) {
LOG(("VALIDATE_NEVER set\n"));
// if no-store or if no-cache and ssl, validate cached response (see
// bug 112564 for an explanation of this logic)
if (mCachedResponseHead->NoStore() ||
(mCachedResponseHead->NoCache() && mConnectionInfo->UsingSSL())) {
(mCachedResponseHead->NoCache() && mUsingSSL)) {
LOG(("Validating based on (no-store || (no-cache && ssl)) logic\n"));
doValidation = true;
}
@ -2840,7 +2941,7 @@ nsHttpChannel::CheckCache()
doValidation = false;
else if (mCachedResponseHead->MustValidateIfExpired())
doValidation = true;
else if (mLoadFlags & VALIDATE_ONCE_PER_SESSION) {
else if (mLoadFlags & nsIRequest::VALIDATE_ONCE_PER_SESSION) {
// If the cached response does not include expiration infor-
// mation, then we must validate the response, despite whether
// or not this is the first access this session. This behavior
@ -2899,20 +3000,17 @@ nsHttpChannel::CheckCache()
// a limited number of redirects (cached or not) is allowed and is
// enforced independently of this mechanism
if (!doValidation && isCachedRedirect) {
nsCAutoString cacheKey;
GenerateCacheKey(mPostID, cacheKey);
if (!mRedirectedCachekeys)
mRedirectedCachekeys = new nsTArray<nsCString>();
else if (mRedirectedCachekeys->Contains(cacheKey))
else if (mRedirectedCachekeys->Contains(mCacheKey))
doValidation = true;
LOG(("Redirection-chain %s key %s\n",
doValidation ? "contains" : "does not contain", cacheKey.get()));
doValidation ? "contains" : "does not contain", mCacheKey.get()));
// Append cacheKey if not in the chain already
if (!doValidation)
mRedirectedCachekeys->AppendElement(cacheKey);
mRedirectedCachekeys->AppendElement(mCacheKey);
}
mCachedContentIsValid = !doValidation;
@ -2957,7 +3055,7 @@ nsHttpChannel::CheckCache()
// it is much more likely that either we don't need to revalidate the entry
// or the entry will successfully revalidate.
if (mCachedContentIsValid || mDidReval) {
rv = StartBufferingCachedEntity(usingSSL);
rv = StartBufferingCachedEntity();
if (NS_FAILED(rv)) {
// If we can't get the entity then we have to act as though we
// don't have the cache entry.
@ -2982,7 +3080,7 @@ nsHttpChannel::CheckCache()
}
bool
nsHttpChannel::MustValidateBasedOnQueryUrl()
HttpCacheQuery::MustValidateBasedOnQueryUrl() const
{
// RFC 2616, section 13.9 states that GET-requests with a query-url
// MUST NOT be treated as fresh unless the server explicitly provides
@ -3046,9 +3144,9 @@ nsHttpChannel::ShouldUpdateOfflineCacheEntry()
}
nsresult
nsHttpChannel::StartBufferingCachedEntity(bool usingSSL)
HttpCacheQuery::StartBufferingCachedEntity()
{
if (usingSSL) {
if (mUsingSSL) {
nsresult rv = mCacheEntry->GetSecurityInfo(
getter_AddRefs(mCachedSecurityInfo));
if (NS_FAILED(rv)) {
@ -3077,7 +3175,8 @@ nsHttpChannel::StartBufferingCachedEntity(bool usingSSL)
return NS_OK;
}
if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !mCachedContentIsPartial) {
if ((mLoadFlags & nsICachingChannel::LOAD_ONLY_IF_MODIFIED) &&
!mCachedContentIsPartial) {
// For LOAD_ONLY_IF_MODIFIED, we usually don't have to deal with the
// cached entity.
if (!mCacheForOfflineUse) {
@ -5153,17 +5252,33 @@ nsHttpChannel::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
nsCacheAccessMode access,
nsresult status)
{
MOZ_ASSERT(NS_IsMainThread());
nsresult rv;
LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p "
"access=%x status=%x]\n", this, entry, access, status));
if (mCacheQuery) {
mRequestHead = mCacheQuery->mRequestHead;
mRedirectedCachekeys = mCacheQuery->mRedirectedCachekeys.forget();
mCacheAsyncInputStream.takeOver(mCacheQuery->mCacheAsyncInputStream);
mCachedResponseHead = mCacheQuery->mCachedResponseHead.forget();
mCachedSecurityInfo = mCacheQuery->mCachedSecurityInfo.forget();
mCachedContentIsValid = mCacheQuery->mCachedContentIsValid;
mCachedContentIsPartial = mCacheQuery->mCachedContentIsPartial;
mCustomConditionalRequest = mCacheQuery->mCustomConditionalRequest;
mDidReval = mCacheQuery->mDidReval;
mCacheQuery = nsnull;
}
// if the channel's already fired onStopRequest, then we should ignore
// this event.
if (!mIsPending)
return NS_OK;
rv = OnCacheEntryAvailableInternal(entry, access, status);
if (NS_FAILED(rv)) {
CloseCacheEntry(true);
AsyncAbort(rv);

View File

@ -35,6 +35,8 @@ class nsAHttpConnection;
namespace mozilla { namespace net {
class HttpCacheQuery;
//-----------------------------------------------------------------------------
// nsHttpChannel
//-----------------------------------------------------------------------------
@ -172,7 +174,6 @@ private:
nsresult ProcessFailedSSLConnect(PRUint32 httpStatus);
nsresult ProcessFallback(bool *waitingForRedirectCallback);
nsresult ContinueProcessFallback(nsresult);
bool ResponseWouldVary();
void HandleAsyncAbort();
nsresult EnsureAssocReq();
@ -200,11 +201,11 @@ private:
nsresult ResolveProxy();
// cache specific methods
nsresult OpenCacheEntry();
nsresult OpenCacheEntry(bool usingSSL);
nsresult OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
nsCacheAccessMode aAccess,
nsresult aResult);
nsresult OpenNormalCacheEntry();
nsresult OpenNormalCacheEntry(bool usingSSL);
nsresult OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
nsCacheAccessMode aAccess,
nsresult aResult);
@ -241,12 +242,10 @@ private:
void ClearBogusContentEncodingIfNeeded();
// byte range request specific methods
nsresult SetupByteRangeRequest(PRUint32 partialLen);
nsresult ProcessPartialContent();
nsresult OnDoneReadingPartialCacheEntry(bool *streamDone);
nsresult DoAuthRetry(nsAHttpConnection *);
bool MustValidateBasedOnQueryUrl();
void HandleAsyncRedirectChannelToHttps();
nsresult AsyncRedirectChannelToHttps();
@ -284,6 +283,7 @@ private:
PRUint64 mLogicalOffset;
// cache specific data
nsRefPtr<HttpCacheQuery> mCacheQuery;
nsCOMPtr<nsICacheEntryDescriptor> mCacheEntry;
// We must close mCacheAsyncInputStream explicitly to avoid leaks.
AutoClose<nsIAsyncInputStream> mCacheAsyncInputStream;
@ -315,6 +315,8 @@ private:
friend class AutoRedirectVetoNotifier;
friend class HttpAsyncAborter<nsHttpChannel>;
friend class HttpCacheQuery;
nsCOMPtr<nsIURI> mRedirectURI;
nsCOMPtr<nsIChannel> mRedirectChannel;
PRUint32 mRedirectType;