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
authorBrian Smith <bsmith@mozilla.com>
Thu, 31 May 2012 15:20:05 -0700
changeset 97637 3860eb758cbc5e99440f4818ad17f6cf742cf7be
parent 97636 a2c43de46e331c1c28fc4aabb7a4d2e3267e6ead
child 97638 02ac4081b067d5b71f357c2ba5c64839cdd92313
push idunknown
push userunknown
push dateunknown
reviewershonzab
bugs722034
milestone15.0a1
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
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -136,16 +136,86 @@ AutoRedirectVetoNotifier::ReportRedirect
     NS_QueryNotificationCallbacks(mChannel, 
                                   NS_GET_IID(nsIRedirectResultListener), 
                                   getter_AddRefs(vetoHook));
     mChannel = nsnull;
     if (vetoHook)
         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>
 //-----------------------------------------------------------------------------
 
 nsHttpChannel::nsHttpChannel()
     : ALLOW_THIS_IN_INITIALIZER_LIST(HttpAsyncAborter<nsHttpChannel>(this))
     , mLogicalOffset(0)
     , mCacheAccess(0)
@@ -265,17 +335,17 @@ nsHttpChannel::Connect()
 
     // Don't allow resuming when cache must be used
     if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
         LOG(("Resuming from cache is not supported yet"));
         return NS_ERROR_DOCUMENT_NOT_CACHED;
     }
 
     // open a cache entry for this channel...
-    rv = OpenCacheEntry();
+    rv = OpenCacheEntry(usingSSL);
 
     // do not continue if asyncOpenCacheEntry is in progress
     if (mOnCacheEntryAvailableCallback) {
         NS_ASSERTION(NS_SUCCEEDED(rv), "Unexpected state");
         return NS_OK;
     }
 
     if (NS_FAILED(rv)) {
@@ -306,30 +376,23 @@ nsHttpChannel::Connect()
     return ContinueConnect();
 }
 
 nsresult
 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();
             }
             mozilla::Telemetry::Accumulate(
                     mozilla::Telemetry::HTTP_CACHE_DISPOSITION, kCacheHit);
 
             char* cacheDeviceID = nsnull;
             mCacheEntry->GetDeviceID(&cacheDeviceID);
@@ -1671,31 +1734,31 @@ nsHttpChannel::ResolveProxy()
     PRUint32 resolveFlags = 0;
     if (mConnectionInfo->ProxyInfo())
         mConnectionInfo->ProxyInfo()->GetResolveFlags(&resolveFlags);
 
     return pps->AsyncResolve(mURI, resolveFlags, this, getter_AddRefs(mProxyRequest));
 }
 
 bool
-nsHttpChannel::ResponseWouldVary()
+HttpCacheQuery::ResponseWouldVary() const
 {
     nsresult rv;
     nsCAutoString buf, metaKey;
     mCachedResponseHead->GetHeader(nsHttp::Vary, buf);
     if (!buf.IsEmpty()) {
         NS_NAMED_LITERAL_CSTRING(prefix, "request-");
 
         // enumerate the elements of the Vary header...
         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.
             //
             // We hash values of cookie-headers for the following reasons:
             //
             //   1- cookies can be very large in size
             //
@@ -1709,18 +1772,19 @@ nsHttpChannel::ResponseWouldVary()
 
             // build cache meta data key...
             metaKey = prefix + nsDependentCString(token);
 
             // check the last value of the given request header to see if it has
             // 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);
             const char *newVal = mRequestHead.PeekHeader(atom);
             if (!lastVal.IsEmpty()) {
                 // value for this header in cache, but no value in request
                 if (!newVal)
                     return true; // yes - response would vary
@@ -1732,19 +1796,19 @@ nsHttpChannel::ResponseWouldVary()
                 if (atom == nsHttp::Cookie) {
                     rv = Hash(newVal, hash);
                     // If hash failed, be conservative (the cached hash
                     // exists at this point) and claim response would vary
                     if (NS_FAILED(rv))
                         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))
                     return true; // yes, response would vary
 
             } else if (newVal) { // old value is empty, but newVal is set
                 return true;
             }
@@ -1877,17 +1941,17 @@ nsHttpChannel::EnsureAssocReq()
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel <byte-range>
 //-----------------------------------------------------------------------------
 
 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.
 
     // use strongest validator available...
     const char *val = mCachedResponseHead->PeekHeader(nsHttp::ETag);
     if (!val)
         val = mCachedResponseHead->PeekHeader(nsHttp::Last_Modified);
@@ -2219,17 +2283,17 @@ IsSubRangeRequest(nsHttpRequestHead &aRe
     if (!aRequestHead.PeekHeader(nsHttp::Range))
         return false;
     nsCAutoString byteRange;
     aRequestHead.GetHeader(nsHttp::Range, byteRange);
     return !byteRange.EqualsLiteral("bytes=0-");
 }
 
 nsresult
-nsHttpChannel::OpenCacheEntry()
+nsHttpChannel::OpenCacheEntry(bool usingSSL)
 {
     nsresult rv;
 
     NS_ASSERTION(!mOnCacheEntryAvailableCallback, "Unexpected state");
     mLoadedFromApplicationCache = false;
 
     LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
 
@@ -2296,16 +2360,18 @@ nsHttpChannel::OpenCacheEntry()
 
     nsCOMPtr<nsICacheSession> session;
 
     // If we have an application cache, we check it first.
     if (mApplicationCache) {
         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);
 
         rv = serv->CreateSession(appCacheClientID.get(),
                                  nsICache::STORE_OFFLINE,
                                  nsICache::STREAM_BASED,
                                  getter_AddRefs(session));
@@ -2316,29 +2382,30 @@ nsHttpChannel::OpenCacheEntry()
         mOnCacheEntryAvailableCallback =
             &nsHttpChannel::OnOfflineCacheEntryAvailable;
         // We open with ACCESS_READ only, because we don't want to overwrite
         // the offline cache entry non-atomically. ACCESS_READ will prevent us
         // from writing to the offline cache as a normal cache entry.
         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
 nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                             nsCacheAccessMode aAccess,
                                             nsresult aEntryStatus)
 {
     nsresult rv;
@@ -2346,16 +2413,18 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
     if (NS_SUCCEEDED(aEntryStatus)) {
         // We successfully opened an offline cache session and the entry,
         // so indicate we will load from the offline cache.
         mLoadedFromApplicationCache = true;
         mCacheEntry = 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
         return NS_ERROR_NOT_AVAILABLE;
     }
 
     if (mCanceled && NS_FAILED(mStatus)) {
         LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
@@ -2393,30 +2462,34 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
 
         if (namespaceType &
             nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
             rv = namespaceEntry->GetData(mFallbackKey);
             NS_ENSURE_SUCCESS(rv, rv);
         }
     }
 
-    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");
 
     nsresult rv;
 
     nsCAutoString cacheKey;
     GenerateCacheKey(mPostID, cacheKey);
 
+    mCacheQuery = new HttpCacheQuery(this, cacheKey, usingSSL, false);
+
     nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy();
 
     nsCOMPtr<nsICacheSession> session;
     rv = gHttpHandler->GetCacheSession(storagePolicy,
                                        UsingPrivateBrowsing(),
                                        getter_AddRefs(session));
     if (NS_FAILED(rv))
         return rv;
@@ -2426,22 +2499,23 @@ nsHttpChannel::OpenNormalCacheEntry()
     if (NS_FAILED(rv))
         return rv;
 
     mOnCacheEntryAvailableCallback =
         &nsHttpChannel::OnNormalCacheEntryAvailable;
     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;
 }
 
 nsresult
 nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
                                            nsCacheAccessMode aAccess,
@@ -2642,31 +2716,58 @@ nsHttpChannel::UpdateExpirationTime()
     if (mOfflineCacheEntry) {
         rv = mOfflineCacheEntry->SetExpirationTime(expirationTime);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     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;
 
     bool usingSSL = false;
     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;
 
     // Don't proceed unless we have opened a cache entry for reading.
     if (!mCacheEntry || !(mCacheAccess & nsICache::ACCESS_READ))
         return NS_OK;
 
@@ -2714,19 +2815,19 @@ nsHttpChannel::CheckCache()
 
     // 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 (!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?
             MaybeMarkCacheEntryValid(this, mCacheEntry, mCacheAccess);
         }
         return rv;
     }
 
@@ -2765,17 +2866,17 @@ nsHttpChannel::CheckCache()
                      mCachedResponseHead->IsResumable() &&
                      !mCustomConditionalRequest &&
                      !mCachedResponseHead->NoStore()) {
                     // looks like a partial entry we can reuse; add If-Range
                     // and Range headers.
                     rv = SetupByteRangeRequest(size);
                     mCachedContentIsPartial = NS_SUCCEEDED(rv);
                     if (mCachedContentIsPartial) {
-                        rv = StartBufferingCachedEntity(usingSSL);
+                        rv = StartBufferingCachedEntity();
                     } else {
                         // Make the request unconditional again.
                         mRequestHead.ClearHeader(nsHttp::Range);
                         mRequestHead.ClearHeader(nsHttp::If_Range);
                     }
                 }
                 return rv;
             }
@@ -2787,34 +2888,34 @@ nsHttpChannel::CheckCache()
 
     // Cached entry is not the entity we request (see bug #633743)
     if (ResponseWouldVary()) {
         LOG(("Validating based on Vary headers returning TRUE\n"));
         canAddImsHeader = false;
         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;
         }
         else {
             LOG(("NOT validating based on VALIDATE_NEVER load flag\n"));
             doValidation = false;
         }
     }
@@ -2835,17 +2936,17 @@ nsHttpChannel::CheckCache()
 
         rv = mCacheEntry->GetExpirationTime(&time);
         NS_ENSURE_SUCCESS(rv, rv);
 
         if (NowInSeconds() <= time)
             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
             // is consistent with existing browsers and is generally expected
             // by web authors.
             rv = mCachedResponseHead->ComputeFreshnessLifetime(&time);
             NS_ENSURE_SUCCESS(rv, rv);
 
@@ -2894,30 +2995,27 @@ nsHttpChannel::CheckCache()
 
     // Bug #561276: We maintain a chain of cache-keys which returns cached
     // 3xx-responses (redirects) in order to detect cycles. If a cycle is
     // found, ignore the cached response and hit the net. Otherwise, use
     // the cached response and add the cache-key to the chain. Note that
     // 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;
 
     if (doValidation) {
         //
         // now, we are definitely going to issue a HTTP request to the server.
         // make it conditional if possible.
@@ -2952,17 +3050,17 @@ nsHttpChannel::CheckCache()
     }
 
     // If there's any possibility that we may use the cached response's entity
     // then start reading it into memory now. If we have to revalidate the
     // entry and the revalidation fails, this will be a wasted effort, but
     // 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.
             if (mDidReval) {
                 // Make the request unconditional again.
                 mRequestHead.ClearHeader(nsHttp::If_Modified_Since);
                 mRequestHead.ClearHeader(nsHttp::ETag);
                 mDidReval = false;
@@ -2977,17 +3075,17 @@ nsHttpChannel::CheckCache()
     }
 
     LOG(("nsHTTPChannel::CheckCache exit [this=%p doValidation=%d]\n",
          this, doValidation));
     return rv;
 }
 
 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
     // an expiration-time in the response. See bug #468594
     // Section 13.2.1 (6th paragraph) defines "explicit expiration time"
     if (mRequestHead.Method() == nsHttp::Get)
     {
         nsCAutoString query;
@@ -3041,19 +3139,19 @@ nsHttpChannel::ShouldUpdateOfflineCacheE
     if (docLastModifiedTime > offlineLastModifiedTime) {
         return true;
     }
 
     return false;
 }
 
 nsresult
-nsHttpChannel::StartBufferingCachedEntity(bool usingSSL)
+HttpCacheQuery::StartBufferingCachedEntity()
 {
-    if (usingSSL) {
+    if (mUsingSSL) {
         nsresult rv = mCacheEntry->GetSecurityInfo(
                                       getter_AddRefs(mCachedSecurityInfo));
         if (NS_FAILED(rv)) {
             LOG(("failed to parse security-info [channel=%p, entry=%p]",
                  this, mCacheEntry.get()));
             NS_WARNING("failed to parse security-info");
             return rv;
         }
@@ -3072,17 +3170,18 @@ nsHttpChannel::StartBufferingCachedEntit
 
     if (WillRedirect(mCachedResponseHead)) {
         // Do not even try to read the entity for a redirect because we do not
         // return an entity to the application when we process redirects.
         LOG(("Will skip read of cached redirect entity\n"));
         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) {
             LOG(("Will skip read from cache based on LOAD_ONLY_IF_MODIFIED "
                  "load flag\n"));
             return NS_OK;
         }
 
@@ -5148,27 +5247,43 @@ nsHttpChannel::ResumeAt(PRUint64 aStartP
 // nsHttpChannel::nsICacheListener
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 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);
     }
 
     return NS_OK;
 }
 
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -30,16 +30,18 @@
 #include "nsDNSPrefetch.h"
 #include "TimingStruct.h"
 #include "AutoClose.h"
 
 class nsAHttpConnection;
 
 namespace mozilla { namespace net {
 
+class HttpCacheQuery;
+
 //-----------------------------------------------------------------------------
 // nsHttpChannel
 //-----------------------------------------------------------------------------
 
 class nsHttpChannel : public HttpBaseChannel
                     , public HttpAsyncAborter<nsHttpChannel>
                     , public nsIStreamListener
                     , public nsICachingChannel
@@ -167,17 +169,16 @@ private:
     nsresult ProcessNotModified();
     nsresult AsyncProcessRedirection(PRUint32 httpStatus);
     nsresult ContinueProcessRedirection(nsresult);
     nsresult ContinueProcessRedirectionAfterFallback(nsresult);
     bool     ShouldSSLProxyResponseContinue(PRUint32 httpStatus);
     nsresult ProcessFailedSSLConnect(PRUint32 httpStatus);
     nsresult ProcessFallback(bool *waitingForRedirectCallback);
     nsresult ContinueProcessFallback(nsresult);
-    bool     ResponseWouldVary();
     void     HandleAsyncAbort();
     nsresult EnsureAssocReq();
 
     nsresult ContinueOnStartRequest1(nsresult);
     nsresult ContinueOnStartRequest2(nsresult);
     nsresult ContinueOnStartRequest3(nsresult);
 
     // redirection specific methods
@@ -195,21 +196,21 @@ private:
     nsresult ProxyFailover();
     nsresult AsyncDoReplaceWithProxy(nsIProxyInfo *);
     nsresult ContinueDoReplaceWithProxy(nsresult);
     void HandleAsyncReplaceWithProxy();
     nsresult ContinueHandleAsyncReplaceWithProxy(nsresult);
     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);
     nsresult OpenOfflineCacheEntryForWriting();
     nsresult OnOfflineCacheEntryForWritingAvailable(
         nsICacheEntryDescriptor *aEntry,
         nsCacheAccessMode aAccess,
         nsresult aResult);
@@ -236,22 +237,20 @@ private:
     nsCacheStoragePolicy DetermineStoragePolicy();
     nsresult DetermineCacheAccess(nsCacheAccessMode *_retval);
     void     AsyncOnExamineCachedResponse();
 
     // Handle the bogus Content-Encoding Apache sometimes sends
     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();
     nsresult ContinueAsyncRedirectChannelToHttps(nsresult rv);
 
     /**
      * A function that takes care of reading STS headers and enforcing STS 
      * load rules.  After a secure channel is erected, STS requires the channel
@@ -279,16 +278,17 @@ private:
     nsCOMPtr<nsICancelable>           mProxyRequest;
 
     nsRefPtr<nsInputStreamPump>       mTransactionPump;
     nsRefPtr<nsHttpTransaction>       mTransaction;
 
     PRUint64                          mLogicalOffset;
 
     // cache specific data
+    nsRefPtr<HttpCacheQuery>          mCacheQuery;
     nsCOMPtr<nsICacheEntryDescriptor> mCacheEntry;
     // We must close mCacheAsyncInputStream explicitly to avoid leaks.
     AutoClose<nsIAsyncInputStream>    mCacheAsyncInputStream;
     nsRefPtr<nsInputStreamPump>       mCachePump;
     nsAutoPtr<nsHttpResponseHead>     mCachedResponseHead;
     nsCOMPtr<nsISupports>             mCachedSecurityInfo;
     nsCacheAccessMode                 mCacheAccess;
     PRUint32                          mPostID;
@@ -310,16 +310,18 @@ private:
 
     // If the channel is associated with a cache, and the URI matched
     // a fallback namespace, this will hold the key for the fallback
     // cache entry.
     nsCString                         mFallbackKey;
 
     friend class AutoRedirectVetoNotifier;
     friend class HttpAsyncAborter<nsHttpChannel>;
+    friend class HttpCacheQuery;
+
     nsCOMPtr<nsIURI>                  mRedirectURI;
     nsCOMPtr<nsIChannel>              mRedirectChannel;
     PRUint32                          mRedirectType;
 
     // state flags
     PRUint32                          mCachedContentIsValid     : 1;
     PRUint32                          mCachedContentIsPartial   : 1;
     PRUint32                          mTransactionReplaced      : 1;