Bug 722034 - Part 5 - Move cache validation to the cache thread, r=honzab
authorBrian Smith <bsmith@mozilla.com>
Thu, 31 May 2012 15:20:05 -0700
changeset 97638 02ac4081b067d5b71f357c2ba5c64839cdd92313
parent 97637 3860eb758cbc5e99440f4818ad17f6cf742cf7be
child 97639 e0dc97a39caf77574bea496823689a648f968427
push idunknown
push userunknown
push dateunknown
reviewershonzab
bugs722034
milestone15.0a1
Bug 722034 - Part 5 - Move cache validation to the cache thread, r=honzab
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/http/nsHttpHandler.cpp
netwerk/protocol/http/nsHttpHandler.h
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -45,16 +45,34 @@ const char kMemoryDeviceID[] = "memory";
 const char kOfflineDeviceID[] = "offline";
 
 // True if the local cache should be bypassed when processing a request.
 #define BYPASS_LOCAL_CACHE(loadFlags) \
         (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
                       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))
 
 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
+static NS_DEFINE_CID(kStreamTransportServiceCID,
+                     NS_STREAMTRANSPORTSERVICE_CID);
+
+const char *
+GetCacheSessionNameForStoragePolicy(nsCacheStoragePolicy storagePolicy,
+                                    bool isPrivate)
+{
+    MOZ_ASSERT(!isPrivate || storagePolicy == nsICache::STORE_IN_MEMORY);
+
+    switch (storagePolicy) {
+    case nsICache::STORE_IN_MEMORY:
+        return isPrivate ? "HTTP-memory-only-PB" : "HTTP-memory-only";
+    case nsICache::STORE_OFFLINE:
+        return "HTTP-offline";
+    default:
+        return "HTTP";
+    }
+}
 
 // Computes and returns a SHA1 hash of the input buffer. The input buffer
 // must be a null-terminated string.
 nsresult
 Hash(const char *buf, nsACString &hash)
 {
     nsresult rv;
       
@@ -136,30 +154,41 @@ AutoRedirectVetoNotifier::ReportRedirect
     NS_QueryNotificationCallbacks(mChannel, 
                                   NS_GET_IID(nsIRedirectResultListener), 
                                   getter_AddRefs(vetoHook));
     mChannel = nsnull;
     if (vetoHook)
         vetoHook->OnRedirectResult(succeeded);
 }
 
-class HttpCacheQuery : public nsICacheListener
+class HttpCacheQuery : public nsRunnable, public nsICacheListener
 {
 public:
     HttpCacheQuery(nsHttpChannel * channel,
+                   const nsACString & clientID,
+                   nsCacheStoragePolicy storagePolicy,
+                   bool usingPrivateBrowsing,
                    const nsACString & cacheKey,
+                   nsCacheAccessMode accessToRequest,
+                   bool noWait,
                    bool usingSSL,
                    bool loadedFromApplicationCache)
         // in
         : mChannel(channel)
-        , mURI(channel->mURI)
+        , mHasQueryString(HasQueryString(channel->mRequestHead.Method(),
+                                         channel->mURI))
         , mLoadFlags(channel->mLoadFlags)
         , mCacheForOfflineUse(channel->mCacheForOfflineUse)
         , mFallbackChannel(channel->mFallbackChannel)
+        , mClientID(clientID)
+        , mStoragePolicy(storagePolicy)
+        , mUsingPrivateBrowsing(usingPrivateBrowsing)
         , mCacheKey(cacheKey)
+        , mAccessToRequest(accessToRequest)
+        , mNoWait(noWait)
         , mUsingSSL(usingSSL)
         , mLoadedFromApplicationCache(loadedFromApplicationCache)
         // internal
         , mCacheAccess(0)
         , mStatus(NS_ERROR_NOT_INITIALIZED)
         // in/out
         , mRequestHead(channel->mRequestHead)
         , mRedirectedCachekeys(channel->mRedirectedCachekeys.forget())
@@ -167,36 +196,57 @@ public:
         , mCachedContentIsValid(false)
         , mCachedContentIsPartial(false)
         , mCustomConditionalRequest(false)
         , mDidReval(false)
     {
         MOZ_ASSERT(NS_IsMainThread());
     }
 
+    nsresult Dispatch();
+
 private:
-    NS_DECL_ISUPPORTS
+    NS_DECL_ISUPPORTS_INHERITED
+    NS_DECL_NSIRUNNABLE
     NS_DECL_NSICACHELISTENER
 
+    MOZ_ALWAYS_INLINE void AssertOnCacheThread() const
+    {
+        MOZ_ASSERT(mCacheThread);
+#ifdef DEBUG
+        bool onCacheThread;
+        nsresult rv = mCacheThread->IsOnCurrentThread(&onCacheThread);
+        MOZ_ASSERT(NS_SUCCEEDED(rv));
+        MOZ_ASSERT(onCacheThread);
+#endif
+    }
+
+    static bool HasQueryString(nsHttpAtom method, nsIURI * uri);
     nsresult CheckCache();
     bool ResponseWouldVary() const;
     bool MustValidateBasedOnQueryUrl() const;
     nsresult SetupByteRangeRequest(PRUint32 partialLen);
     nsresult StartBufferingCachedEntity();
 
     nsCOMPtr<nsICacheListener> mChannel;
-    const nsCOMPtr<nsIURI> mURI;
+    const bool mHasQueryString;
     const PRUint32 mLoadFlags;
     const bool mCacheForOfflineUse;
     const bool mFallbackChannel;
+    const InfallableCopyCString mClientID;
+    const nsCacheStoragePolicy mStoragePolicy;
+    const bool mUsingPrivateBrowsing;
     const InfallableCopyCString mCacheKey;
+    const nsCacheAccessMode mAccessToRequest;
+    const bool mNoWait;
     const bool mUsingSSL;
     const bool mLoadedFromApplicationCache;
 
     // Used only internally 
+    nsCOMPtr<nsIEventTarget> mCacheThread;
     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;
@@ -204,17 +254,17 @@ private:
     /*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)
+NS_IMPL_ISUPPORTS_INHERITED1(HttpCacheQuery, nsRunnable, nsICacheListener)
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel <public>
 //-----------------------------------------------------------------------------
 
 nsHttpChannel::nsHttpChannel()
     : ALLOW_THIS_IN_INITIALIZER_LIST(HttpAsyncAborter<nsHttpChannel>(this))
     , mLogicalOffset(0)
@@ -334,16 +384,19 @@ nsHttpChannel::Connect()
         return ResolveProxy();  // Lazily resolve proxy info
 
     // 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;
     }
 
+    if (!gHttpHandler->UseCache())
+        return ContinueConnect();
+
     // open a cache entry for this channel...
     rv = OpenCacheEntry(usingSSL);
 
     // do not continue if asyncOpenCacheEntry is in progress
     if (mOnCacheEntryAvailableCallback) {
         NS_ASSERTION(NS_SUCCEEDED(rv), "Unexpected state");
         return NS_OK;
     }
@@ -529,18 +582,18 @@ nsHttpChannel::ContinueHandleAsyncRedire
         DoNotifyListener();
     }
 
     // close the cache entry.  Blow it away if we couldn't process the redirect
     // for some reason (the cache entry might be corrupt).
     if (mCacheEntry) {
         if (NS_FAILED(rv))
             mCacheEntry->Doom();
-        CloseCacheEntry(false);
-    }
+    }
+    CloseCacheEntry(false);
 
     mIsPending = false;
 
     if (mLoadGroup)
         mLoadGroup->RemoveRequest(this, nsnull, mStatus);
 
     return NS_OK;
 }
@@ -1736,16 +1789,18 @@ nsHttpChannel::ResolveProxy()
         mConnectionInfo->ProxyInfo()->GetResolveFlags(&resolveFlags);
 
     return pps->AsyncResolve(mURI, resolveFlags, this, getter_AddRefs(mProxyRequest));
 }
 
 bool
 HttpCacheQuery::ResponseWouldVary() const
 {
+    AssertOnCacheThread();
+
     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
@@ -1943,16 +1998,18 @@ nsHttpChannel::EnsureAssocReq()
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel <byte-range>
 //-----------------------------------------------------------------------------
 
 nsresult
 HttpCacheQuery::SetupByteRangeRequest(PRUint32 partialLen)
 {
+    AssertOnCacheThread();
+
     // 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);
     if (!val) {
@@ -2190,18 +2247,17 @@ nsHttpChannel::ProcessFallback(bool *wai
     }
 
     mCacheForOfflineUse = false;
     mOfflineCacheClientID.Truncate();
     mOfflineCacheEntry = 0;
     mOfflineCacheAccess = 0;
 
     // Close the current cache entry.
-    if (mCacheEntry)
-        CloseCacheEntry(true);
+    CloseCacheEntry(true);
 
     // Create a new channel to load the fallback entry.
     nsRefPtr<nsIChannel> newChannel;
     rv = gHttpHandler->NewChannel(mURI, getter_AddRefs(newChannel));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = SetupReplacementChannel(mURI, newChannel, true, false);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -2358,48 +2414,39 @@ nsHttpChannel::OpenCacheEntry(bool using
         }
     }
 
     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));
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        session->SetIsPrivate(UsingPrivateBrowsing());
-
-        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,
-            mCacheQuery,
-            mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
-
-        if (NS_SUCCEEDED(rv))
-            return NS_OK;
-
-        mCacheQuery = nsnull;
-        mOnCacheEntryAvailableCallback = nsnull;
+        rv = mApplicationCache->GetClientID(appCacheClientID);
+        if (NS_SUCCEEDED(rv)) {
+            // 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.
+            mCacheQuery = new HttpCacheQuery(
+                                this, appCacheClientID,
+                                nsICache::STORE_OFFLINE, UsingPrivateBrowsing(),
+                                cacheKey, nsICache::ACCESS_READ,
+                                mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY,
+                                usingSSL, true);
+
+            mOnCacheEntryAvailableCallback =
+                &nsHttpChannel::OnOfflineCacheEntryAvailable;
+
+            rv = mCacheQuery->Dispatch();
+
+            if (NS_SUCCEEDED(rv))
+                return NS_OK;
+
+            mCacheQuery = nsnull;
+            mOnCacheEntryAvailableCallback = nsnull;
+        }
 
         // opening cache entry failed
         return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE, rv);
     }
 
     return OpenNormalCacheEntry(usingSSL);
 }
 
@@ -2475,43 +2522,40 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
 
 nsresult
 nsHttpChannel::OpenNormalCacheEntry(bool usingSSL)
 {
     NS_ASSERTION(!mCacheEntry, "We have already mCacheEntry");
 
     nsresult rv;
 
+    bool isPrivate = UsingPrivateBrowsing();
+    nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy(isPrivate);
+    nsDependentCString clientID(
+        GetCacheSessionNameForStoragePolicy(storagePolicy, isPrivate));
+
     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;
-
-    nsCacheAccessMode accessRequested = 0;
+    nsCacheAccessMode accessRequested;
     rv = DetermineCacheAccess(&accessRequested);
     if (NS_FAILED(rv))
         return rv;
+ 
+    mCacheQuery = new HttpCacheQuery(
+                                this, clientID, storagePolicy,
+                                UsingPrivateBrowsing(), cacheKey,
+                                accessRequested,
+                                mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY,
+                                usingSSL, false);
 
     mOnCacheEntryAvailableCallback =
         &nsHttpChannel::OnNormalCacheEntryAvailable;
-    rv = session->AsyncOpenCacheEntry(
-        cacheKey,
-        accessRequested,
-        mCacheQuery,
-        mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
-
+
+    rv = mCacheQuery->Dispatch();
     if (NS_SUCCEEDED(rv))
         return NS_OK;
 
     mCacheQuery = nsnull;
     mOnCacheEntryAvailableCallback = nsnull;
 
     return rv;
 }
@@ -2722,50 +2766,117 @@ nsHttpChannel::UpdateExpirationTime()
 }
 
 NS_IMETHODIMP
 HttpCacheQuery::OnCacheEntryDoomed(nsresult)
 {
     return NS_ERROR_UNEXPECTED;
 }
 
+nsresult
+HttpCacheQuery::Dispatch()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsresult rv;
+
+    // XXX: Start the cache service; otherwise DispatchToCacheIOThread will
+    // fail.
+    nsCOMPtr<nsICacheService> service = 
+        do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
+
+    // Ensure the stream transport service gets initialized on the main thread
+    if (NS_SUCCEEDED(rv)) {
+        nsCOMPtr<nsIStreamTransportService> sts =
+            do_GetService(kStreamTransportServiceCID, &rv);
+    }
+
+    if (NS_SUCCEEDED(rv)) {
+        rv = service->GetCacheIOTarget(getter_AddRefs(mCacheThread));
+    }
+
+    if (NS_SUCCEEDED(rv)) {
+        rv = mCacheThread->Dispatch(this, NS_DISPATCH_NORMAL);
+    }
+
+    return rv;
+}
+
+NS_IMETHODIMP
+HttpCacheQuery::Run()
+{
+    nsresult rv;
+    if (!NS_IsMainThread()) {
+        AssertOnCacheThread();
+
+        nsCOMPtr<nsICacheService> serv =
+            do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
+        nsCOMPtr<nsICacheSession> session;
+        if (NS_SUCCEEDED(rv)) {
+            rv = serv->CreateSession(mClientID.get(), mStoragePolicy,
+                                     nsICache::STREAM_BASED,
+                                     getter_AddRefs(session));
+        }
+        if (NS_SUCCEEDED(rv)) {
+            rv = session->SetIsPrivate(mUsingPrivateBrowsing);
+        }
+        if (NS_SUCCEEDED(rv)) {
+            rv = session->SetDoomEntriesIfExpired(false);
+        }
+        if (NS_SUCCEEDED(rv)) {
+            // AsyncOpenCacheEntry isn't really async when its called on the
+            // cache service thread.
+            rv = session->AsyncOpenCacheEntry(mCacheKey, mAccessToRequest, this,
+                                              mNoWait);
+        }
+        if (NS_FAILED(rv)) {
+            rv = OnCacheEntryAvailable(nsnull, 0, rv);
+        }
+    } else {
+        // break cycles
+        nsCOMPtr<nsICacheListener> channel = mChannel.forget();
+        mCacheThread = nsnull;
+        nsCOMPtr<nsICacheEntryDescriptor> entry = mCacheEntry.forget();
+
+        rv = channel->OnCacheEntryAvailable(entry, mCacheAccess, mStatus);
+    }
+    
+    return rv;
+}
+
 NS_IMETHODIMP
 HttpCacheQuery::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
                                       nsCacheAccessMode access,
                                       nsresult status)
 
 {
+    AssertOnCacheThread();
+
     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);
+    rv = NS_DispatchToMainThread(this);
     return rv;
 }
 
 nsresult
 HttpCacheQuery::CheckCache()
 {
+    AssertOnCacheThread();
+
     nsresult rv = NS_OK;
 
-    bool usingSSL = false;
-    rv = mURI->SchemeIs("https", &usingSSL);
-    NS_ENSURE_SUCCESS(rv,rv);
-
     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))
@@ -3074,36 +3185,49 @@ HttpCacheQuery::CheckCache()
         MaybeMarkCacheEntryValid(this, mCacheEntry, mCacheAccess);
     }
 
     LOG(("nsHTTPChannel::CheckCache exit [this=%p doValidation=%d]\n",
          this, doValidation));
     return rv;
 }
 
+/*static*/ inline bool
+HttpCacheQuery::HasQueryString(nsHttpAtom method, nsIURI * uri)
+{
+    // Must be called on the main thread because nsIURI does not implement
+    // thread-safe QueryInterface.
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (method != nsHttp::Get && method != nsHttp::Head)
+        return false;
+
+    nsCAutoString query;
+    nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
+    nsresult rv = url->GetQuery(query);
+    return NS_SUCCEEDED(rv) && !query.IsEmpty();
+}
+
 bool
 HttpCacheQuery::MustValidateBasedOnQueryUrl() const
 {
+    AssertOnCacheThread();
+
     // 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)
+    if (mHasQueryString)
     {
-        nsCAutoString query;
-        nsCOMPtr<nsIURL> url = do_QueryInterface(mURI);
-        nsresult rv = url->GetQuery(query);
-        if (NS_SUCCEEDED(rv) && !query.IsEmpty()) {
-            PRUint32 tmp; // we don't need the value, just whether it's set
-            rv = mCachedResponseHead->GetExpiresValue(&tmp);
+        PRUint32 tmp; // we don't need the value, just whether it's set
+        nsresult rv = mCachedResponseHead->GetExpiresValue(&tmp);
+        if (NS_FAILED(rv)) {
+            rv = mCachedResponseHead->GetMaxAgeValue(&tmp);
             if (NS_FAILED(rv)) {
-                rv = mCachedResponseHead->GetMaxAgeValue(&tmp);
-                if (NS_FAILED(rv)) {
-                    return true;
-                }
+                return true;
             }
         }
     }
     return false;
 }
 
 
 bool
@@ -3141,16 +3265,18 @@ nsHttpChannel::ShouldUpdateOfflineCacheE
     }
 
     return false;
 }
 
 nsresult
 HttpCacheQuery::StartBufferingCachedEntity()
 {
+    AssertOnCacheThread();
+
     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;
@@ -3201,18 +3327,16 @@ HttpCacheQuery::StartBufferingCachedEnti
     // transport service to start reading the entity on one of its
     // background threads while we do validation (if any).
 
     nsCOMPtr<nsIInputStream> wrapper;
 
     nsCOMPtr<nsIInputStream> stream;
     nsCOMPtr<nsITransport> transport;
 
-    static NS_DEFINE_CID(kStreamTransportServiceCID,
-                          NS_STREAMTRANSPORTSERVICE_CID);
     nsCOMPtr<nsIStreamTransportService> sts =
         do_GetService(kStreamTransportServiceCID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(stream));
     if (NS_SUCCEEDED(rv)) {
         rv = sts->CreateInputTransport(stream, PRInt64(-1), PRInt64(-1),
                                         true, getter_AddRefs(transport));
@@ -3335,16 +3459,17 @@ nsHttpChannel::ReadFromCache(bool alread
         mCachePump->Suspend();
 
     return NS_OK;
 }
 
 void
 nsHttpChannel::CloseCacheEntry(bool doomOnFailure)
 {
+    mCacheQuery = nsnull;
     mCacheAsyncInputStream.CloseAndRelease();
 
     if (!mCacheEntry)
         return;
 
     LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%x mCacheAccess=%x",
          this, mStatus, mCacheAccess));
 
@@ -4085,16 +4210,17 @@ nsHttpChannel::Cancel(nsresult status)
     mCanceled = true;
     mStatus = status;
     if (mProxyRequest)
         mProxyRequest->Cancel(status);
     if (mTransaction)
         gHttpHandler->CancelTransaction(mTransaction, status);
     if (mTransactionPump)
         mTransactionPump->Cancel(status);
+    mCacheQuery = nsnull;
     mCacheAsyncInputStream.CloseAndRelease();
     if (mCachePump)
         mCachePump->Cancel(status);
     if (mAuthProvider)
         mAuthProvider->Cancel(status);
     return NS_OK;
 }
 
@@ -4801,18 +4927,18 @@ nsHttpChannel::OnStopRequest(nsIRequest 
             // blob response) may hold the token to the cache
             // entry. So we mark the cache valid here.
             // We also need to check the entry is stored as file
             // because we write to the cache asynchronously when
             // it isn't stored in the file and it isn't completely
             // written to the disk yet.
             mCacheEntry->MarkValid();
         }
-        CloseCacheEntry(!contentComplete);
-    }
+    }
+    CloseCacheEntry(!contentComplete);
 
     if (mOfflineCacheEntry)
         CloseOfflineCacheEntry();
 
     if (mLoadGroup)
         mLoadGroup->RemoveRequest(this, nsnull, status);
 
     // We don't need this info anymore
@@ -5269,18 +5395,20 @@ nsHttpChannel::OnCacheEntryAvailable(nsI
         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)
+    if (!mIsPending) {
+        mCacheAsyncInputStream.CloseAndRelease();
         return NS_OK;
+    }
 
     rv = OnCacheEntryAvailableInternal(entry, access, status);
 
     if (NS_FAILED(rv)) {
         CloseCacheEntry(true);
         AsyncAbort(rv);
     }
 
@@ -5663,43 +5791,58 @@ nsHttpChannel::InvalidateCacheEntryForLo
         } else
             NS_WARNING(("  failed getting ascii-spec\n"));
     } else {
         LOG(("  hosts not matching\n"));
     }
 }
 
 void
-nsHttpChannel::DoInvalidateCacheEntry(nsACString &key)
+nsHttpChannel::DoInvalidateCacheEntry(const nsCString &key)
 {
     // NOTE:
     // Following comments 24,32 and 33 in bug #327765, we only care about
     // the cache in the protocol-handler, not the application cache.
     // The logic below deviates from the original logic in OpenCacheEntry on
     // one point by using only READ_ONLY access-policy. I think this is safe.
 
     // First, find session holding the cache-entry - use current storage-policy
+    bool isPrivate = UsingPrivateBrowsing();
+    nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy(isPrivate);
+    const char * clientID = GetCacheSessionNameForStoragePolicy(storagePolicy,
+                                                                isPrivate);
+
+    LOG(("DoInvalidateCacheEntry [channel=%p session=%s policy=%d key=%s]",
+         this, clientID, PRIntn(storagePolicy), key.get()));
+
+    nsresult rv;
+    nsCOMPtr<nsICacheService> serv =
+        do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
     nsCOMPtr<nsICacheSession> session;
-    nsCacheStoragePolicy storagePolicy = DetermineStoragePolicy();
-
-    nsresult rv = gHttpHandler->GetCacheSession(storagePolicy,
-                                                UsingPrivateBrowsing(),
-                                                getter_AddRefs(session));
-
-    if (NS_FAILED(rv))
-        return;
-
-    session->DoomEntry(key, nsnull);
+    if (NS_SUCCEEDED(rv)) {
+        rv = serv->CreateSession(clientID, storagePolicy,  
+                                 nsICache::STREAM_BASED,
+                                 getter_AddRefs(session));
+    }
+    if (NS_SUCCEEDED(rv)) {
+        rv = session->SetIsPrivate(UsingPrivateBrowsing());
+    }
+    if (NS_SUCCEEDED(rv)) {
+        rv = session->DoomEntry(key, nsnull);
+    }
+
+    LOG(("DoInvalidateCacheEntry [channel=%p session=%s policy=%d key=%s rv=%d]",
+         this, clientID, PRIntn(storagePolicy), key.get(), PRIntn(rv)));
 }
 
 nsCacheStoragePolicy
-nsHttpChannel::DetermineStoragePolicy()
+nsHttpChannel::DetermineStoragePolicy(bool isPrivate)
 {
     nsCacheStoragePolicy policy = nsICache::STORE_ANYWHERE;
-    if (UsingPrivateBrowsing())
+    if (isPrivate)
         policy = nsICache::STORE_IN_MEMORY;
     else if (mLoadFlags & INHIBIT_PERSISTENT_CACHING)
         policy = nsICache::STORE_IN_MEMORY;
 
     return policy;
 }
 
 nsresult
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -229,17 +229,17 @@ private:
     void     UpdateInhibitPersistentCachingFlag();
     nsresult InitOfflineCacheEntry();
     nsresult AddCacheEntryHeaders(nsICacheEntryDescriptor *entry);
     nsresult StoreAuthorizationMetaData(nsICacheEntryDescriptor *entry);
     nsresult FinalizeCacheEntry();
     nsresult InstallCacheListener(PRUint32 offset = 0);
     nsresult InstallOfflineCacheListener();
     void     MaybeInvalidateCacheEntryForSubsequentGet();
-    nsCacheStoragePolicy DetermineStoragePolicy();
+    nsCacheStoragePolicy DetermineStoragePolicy(bool isPrivate);
     nsresult DetermineCacheAccess(nsCacheAccessMode *_retval);
     void     AsyncOnExamineCachedResponse();
 
     // Handle the bogus Content-Encoding Apache sometimes sends
     void ClearBogusContentEncodingIfNeeded();
 
     // byte range request specific methods
     nsresult ProcessPartialContent();
@@ -257,17 +257,17 @@ private:
      * to be trusted or any STS header data on the channel is ignored.
      * This is called from ProcessResponse.
      */
     nsresult ProcessSTSHeader();
 
     void InvalidateCacheEntryForLocation(const char *location);
     void AssembleCacheKey(const char *spec, PRUint32 postID, nsACString &key);
     nsresult CreateNewURI(const char *loc, nsIURI **newURI);
-    void DoInvalidateCacheEntry(nsACString &key);
+    void DoInvalidateCacheEntry(const nsCString &key);
 
     // Ref RFC2616 13.10: "invalidation... MUST only be performed if
     // the host part is the same as in the Request-URI"
     inline bool HostPartIsTheSame(nsIURI *uri) {
         nsCAutoString tmpHost1, tmpHost2;
         return (NS_SUCCEEDED(mURI->GetAsciiHost(tmpHost1)) &&
                 NS_SUCCEEDED(uri->GetAsciiHost(tmpHost2)) &&
                 (tmpHost1 == tmpHost2));
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -402,62 +402,16 @@ nsHttpHandler::IsAcceptableEncoding(cons
     // to accept.
     if (!PL_strncasecmp(enc, "x-", 2))
         enc += 2;
     
     return nsHttp::FindToken(mAcceptEncodings.get(), enc, HTTP_LWS ",") != nsnull;
 }
 
 nsresult
-nsHttpHandler::GetCacheSession(nsCacheStoragePolicy storagePolicy,
-                               bool isPrivate,
-                               nsICacheSession **result)
-{
-    nsresult rv;
-
-    // Skip cache if disabled in preferences
-    if (!mUseCache)
-        return NS_ERROR_NOT_AVAILABLE;
-
-    // We want to get the pointer to the cache service each time we're called,
-    // because it's possible for some add-ons (such as Google Gears) to swap
-    // in new cache services on the fly, and we want to pick them up as
-    // appropriate.
-    nsCOMPtr<nsICacheService> serv = do_GetService(NS_CACHESERVICE_CONTRACTID,
-                                                   &rv);
-    if (NS_FAILED(rv)) return rv;
-
-    const char *sessionName = "HTTP";
-    switch (storagePolicy) {
-    case nsICache::STORE_IN_MEMORY:
-        sessionName = isPrivate ? "HTTP-memory-only-PB" : "HTTP-memory-only";
-        break;
-    case nsICache::STORE_OFFLINE:
-        sessionName = "HTTP-offline";
-        break;
-    default:
-        break;
-    }
-
-    nsCOMPtr<nsICacheSession> cacheSession;
-    rv = serv->CreateSession(sessionName,
-                             storagePolicy,
-                             nsICache::STREAM_BASED,
-                             getter_AddRefs(cacheSession));
-    if (NS_FAILED(rv)) return rv;
-
-    rv = cacheSession->SetDoomEntriesIfExpired(false);
-    if (NS_FAILED(rv)) return rv;
-
-    NS_ADDREF(*result = cacheSession);
-
-    return NS_OK;
-}
-
-nsresult
 nsHttpHandler::GetStreamConverterService(nsIStreamConverterService **result)
 {
     if (!mStreamConvSvc) {
         nsresult rv;
         mStreamConvSvc = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
         if (NS_FAILED(rv)) return rv;
     }
     *result = mStreamConvSvc;
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -96,17 +96,17 @@ public:
     PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
 
     bool           PromptTempRedirect()      { return mPromptTempRedirect; }
 
     nsHttpAuthCache     *AuthCache() { return &mAuthCache; }
     nsHttpConnectionMgr *ConnMgr()   { return mConnMgr; }
 
     // cache support
-    nsresult GetCacheSession(nsCacheStoragePolicy, bool isPrivate, nsICacheSession **);
+    bool UseCache() const { return mUseCache; }
     PRUint32 GenerateUniqueID() { return ++mLastUniqueID; }
     PRUint32 SessionStartTime() { return mSessionStartTime; }
 
     //
     // Connection management methods:
     //
     // - the handler only owns idle connections; it does not own active
     //   connections.