--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -120,17 +120,16 @@ AutoRedirectVetoNotifier::ReportRedirect
nsHttpChannel::nsHttpChannel()
: HttpAsyncAborter<nsHttpChannel>(this)
, mLogicalOffset(0)
, mCacheAccess(0)
, mPostID(0)
, mRequestTime(0)
, mOnCacheEntryAvailableCallback(nsnull)
- , mAsyncCacheOpen(false)
, mCachedContentIsValid(false)
, mCachedContentIsPartial(false)
, mTransactionReplaced(false)
, mAuthRetryPending(false)
, mResuming(false)
, mInitedCacheEntry(false)
, mCacheForOfflineUse(false)
, mCachingOpportunistically(false)
@@ -241,16 +240,22 @@ nsHttpChannel::Connect(bool firstTime)
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();
+ // do not continue if asyncOpenCacheEntry is in progress
+ if (mOnCacheEntryAvailableCallback) {
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Unexpected state");
+ return NS_OK;
+ }
+
if (NS_FAILED(rv)) {
LOG(("OpenCacheEntry failed [rv=%x]\n", rv));
// if this channel is only allowed to pull from the cache, then
// we must fail if we were unable to open a cache entry.
if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
// If we have a fallback URI (and we're not already
// falling back), process the fallback asynchronously.
if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
@@ -261,20 +266,20 @@ nsHttpChannel::Connect(bool firstTime)
// otherwise, let's just proceed without using the cache.
}
// if cacheForOfflineUse has been set, open up an offline cache
// entry to update
if (mCacheForOfflineUse) {
rv = OpenOfflineCacheEntryForWriting();
if (NS_FAILED(rv)) return rv;
+
+ if (mOnCacheEntryAvailableCallback)
+ return NS_OK;
}
-
- if (NS_SUCCEEDED(rv) && mAsyncCacheOpen)
- return NS_OK;
}
// 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.
rv = CheckCache();
@@ -2029,17 +2034,17 @@ IsSubRangeRequest(nsHttpRequestHead &aRe
return !byteRange.EqualsLiteral("bytes=0-");
}
nsresult
nsHttpChannel::OpenCacheEntry()
{
nsresult rv;
- mAsyncCacheOpen = false;
+ NS_ASSERTION(!mOnCacheEntryAvailableCallback, "Unexpected state");
mLoadedFromApplicationCache = false;
LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
// make sure we're not abusing this function
NS_PRECONDITION(!mCacheEntry, "cache entry already open");
nsCAutoString cacheKey;
@@ -2123,70 +2128,65 @@ nsHttpChannel::OpenCacheEntry()
// 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,
mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
- if (NS_SUCCEEDED(rv)) {
- mAsyncCacheOpen = true;
+ if (NS_SUCCEEDED(rv))
return NS_OK;
- }
+
+ mOnCacheEntryAvailableCallback = nsnull;
// opening cache entry failed
- return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE,
- rv, true);
+ return OnOfflineCacheEntryAvailable(nsnull, nsICache::ACCESS_NONE, rv);
}
- return OpenNormalCacheEntry(true);
+ return OpenNormalCacheEntry();
}
nsresult
nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
nsCacheAccessMode aAccess,
- nsresult aEntryStatus,
- bool aIsSync)
+ nsresult aEntryStatus)
{
nsresult rv;
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;
}
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 aIsSync ? NS_ERROR_NOT_AVAILABLE : Connect(false);
+ return NS_ERROR_NOT_AVAILABLE;
}
if (mCanceled && NS_FAILED(mStatus)) {
LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
return mStatus;
}
if (NS_SUCCEEDED(aEntryStatus))
- // Called from OnCacheEntryAvailable, advance to the next state
- return Connect(false);
+ return NS_OK;
if (!mCacheForOfflineUse && !mFallbackChannel) {
nsCAutoString cacheKey;
GenerateCacheKey(mPostID, cacheKey);
// Check for namespace match.
nsCOMPtr<nsIApplicationCacheNamespace> namespaceEntry;
rv = mApplicationCache->GetMatchingNamespace
(cacheKey, getter_AddRefs(namespaceEntry));
- if (NS_FAILED(rv) && !aIsSync)
- return Connect(false);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 namespaceType = 0;
if (!namespaceEntry ||
NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) ||
(namespaceType &
(nsIApplicationCacheNamespace::NAMESPACE_FALLBACK |
nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC |
@@ -2194,24 +2194,22 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
// When loading from an application cache, only items
// on the whitelist or matching a
// fallback/opportunistic namespace should hit the
// network...
mLoadFlags |= LOAD_ONLY_FROM_CACHE;
// ... and if there were an application cache entry,
// we would have found it earlier.
- return aIsSync ? NS_ERROR_CACHE_KEY_NOT_FOUND : Connect(false);
+ return NS_ERROR_CACHE_KEY_NOT_FOUND;
}
if (namespaceType &
nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
rv = namespaceEntry->GetData(mFallbackKey);
- if (NS_FAILED(rv) && !aIsSync)
- return Connect(false);
NS_ENSURE_SUCCESS(rv, rv);
}
if ((namespaceType &
nsIApplicationCacheNamespace::NAMESPACE_OPPORTUNISTIC) &&
mLoadFlags & LOAD_DOCUMENT_URI) {
// Document loads for items in an opportunistic namespace
// should be placed in the offline cache.
@@ -2219,22 +2217,22 @@ nsHttpChannel::OnOfflineCacheEntryAvaila
mApplicationCache->GetClientID(clientID);
mCacheForOfflineUse = !clientID.IsEmpty();
SetOfflineCacheClientID(clientID);
mCachingOpportunistically = true;
}
}
- return OpenNormalCacheEntry(aIsSync);
+ return OpenNormalCacheEntry();
}
nsresult
-nsHttpChannel::OpenNormalCacheEntry(bool aIsSync)
+nsHttpChannel::OpenNormalCacheEntry()
{
NS_ASSERTION(!mCacheEntry, "We have already mCacheEntry");
nsresult rv;
nsCAutoString cacheKey;
GenerateCacheKey(mPostID, cacheKey);
@@ -2252,36 +2250,29 @@ nsHttpChannel::OpenNormalCacheEntry(bool
mOnCacheEntryAvailableCallback =
&nsHttpChannel::OnNormalCacheEntryAvailable;
rv = session->AsyncOpenCacheEntry(
cacheKey,
accessRequested,
this,
mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
- if (NS_SUCCEEDED(rv)) {
- mAsyncCacheOpen = true;
+ if (NS_SUCCEEDED(rv))
return NS_OK;
- }
-
- if (!aIsSync)
- // Called from OnCacheEntryAvailable, advance to the next state
- rv = Connect(false);
+
+ mOnCacheEntryAvailableCallback = nsnull;
return rv;
}
nsresult
nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
nsCacheAccessMode aAccess,
- nsresult aEntryStatus,
- bool aIsSync)
+ nsresult aEntryStatus)
{
- NS_ASSERTION(!aIsSync, "aIsSync should be false");
-
if (NS_SUCCEEDED(aEntryStatus)) {
mCacheEntry = aEntry;
mCacheAccess = aAccess;
}
if (aEntryStatus == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
LOG(("bypassing local cache since it is busy\n"));
}
@@ -2292,17 +2283,17 @@ nsHttpChannel::OnNormalCacheEntryAvailab
}
if ((mLoadFlags & LOAD_ONLY_FROM_CACHE) && NS_FAILED(aEntryStatus))
// if this channel is only allowed to pull from the cache, then
// we must fail if we were unable to open a cache entry.
return NS_ERROR_DOCUMENT_NOT_CACHED;
// advance to the next state...
- return Connect(false);
+ return NS_OK;
}
nsresult
nsHttpChannel::OpenOfflineCacheEntryForWriting()
{
nsresult rv;
@@ -2339,32 +2330,53 @@ nsHttpChannel::OpenOfflineCacheEntryForW
if (NS_FAILED(rv)) return rv;
rv = serv->CreateSession(mOfflineCacheClientID.get(),
nsICache::STORE_OFFLINE,
nsICache::STREAM_BASED,
getter_AddRefs(session));
if (NS_FAILED(rv)) return rv;
- rv = session->OpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
- false, getter_AddRefs(mOfflineCacheEntry));
-
- if (rv == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
+ mOnCacheEntryAvailableCallback =
+ &nsHttpChannel::OnOfflineCacheEntryForWritingAvailable;
+ rv = session->AsyncOpenCacheEntry(cacheKey, nsICache::ACCESS_READ_WRITE,
+ this, true);
+ if (NS_SUCCEEDED(rv))
+ return NS_OK;
+
+ mOnCacheEntryAvailableCallback = nsnull;
+
+ return rv;
+}
+
+nsresult
+nsHttpChannel::OnOfflineCacheEntryForWritingAvailable(
+ nsICacheEntryDescriptor *aEntry,
+ nsCacheAccessMode aAccess,
+ nsresult aEntryStatus)
+{
+ if (NS_SUCCEEDED(aEntryStatus)) {
+ mOfflineCacheEntry = aEntry;
+ mOfflineCacheAccess = aAccess;
+ }
+
+ if (aEntryStatus == NS_ERROR_CACHE_WAIT_FOR_VALIDATION) {
// access to the cache entry has been denied (because the cache entry
// is probably in use by another channel). Either the cache is being
// read from (we're offline) or it's being updated elsewhere.
- return NS_OK;
+ aEntryStatus = NS_OK;
}
- if (NS_SUCCEEDED(rv)) {
- mOfflineCacheEntry->GetAccessGranted(&mOfflineCacheAccess);
- LOG(("got offline cache entry [access=%x]\n", mOfflineCacheAccess));
+ if (mCanceled && NS_FAILED(mStatus)) {
+ LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
+ return mStatus;
}
- return rv;
+ // advance to the next state...
+ return aEntryStatus;
}
// Generates the proper cache-key for this instance of nsHttpChannel
nsresult
nsHttpChannel::GenerateCacheKey(PRUint32 postID, nsACString &cacheKey)
{
AssembleCacheKey(mFallbackChannel ? mFallbackKey.get() : mSpec.get(),
postID, cacheKey);
@@ -4810,39 +4822,78 @@ nsHttpChannel::OnCacheEntryAvailable(nsI
LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p "
"access=%x status=%x]\n", this, entry, access, status));
// 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;
+}
+
+nsresult
+nsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntryDescriptor *entry,
+ nsCacheAccessMode access,
+ nsresult status)
+{
+ nsresult rv;
+
nsOnCacheEntryAvailableCallback callback = mOnCacheEntryAvailableCallback;
mOnCacheEntryAvailableCallback = nsnull;
NS_ASSERTION(callback,
"nsHttpChannel::OnCacheEntryAvailable called without callback");
- rv = ((*this).*callback)(entry, access, status, false);
-
- if (NS_FAILED(rv)) {
- LOG(("AsyncOpenCacheEntry failed [rv=%x]\n", rv));
- if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
- // If we have a fallback URI (and we're not already
- // falling back), process the fallback asynchronously.
- if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
- rv = AsyncCall(&nsHttpChannel::HandleAsyncFallback);
- if (NS_SUCCEEDED(rv))
- return rv;
+ rv = ((*this).*callback)(entry, access, status);
+
+ if (mOnCacheEntryAvailableCallback) {
+ // callback fired another async open
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Unexpected state");
+ return NS_OK;
+ }
+
+ if (callback != &nsHttpChannel::OnOfflineCacheEntryForWritingAvailable) {
+ if (NS_FAILED(rv)) {
+ LOG(("AsyncOpenCacheEntry failed [rv=%x]\n", rv));
+ if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
+ // If we have a fallback URI (and we're not already
+ // falling back), process the fallback asynchronously.
+ if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
+ return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
+ }
+ return NS_ERROR_DOCUMENT_NOT_CACHED;
}
+ // proceed without using the cache
}
- CloseCacheEntry(true);
- AsyncAbort(rv);
+
+ // if cacheForOfflineUse has been set, open up an offline cache entry
+ // to update
+ if (mCacheForOfflineUse) {
+ rv = OpenOfflineCacheEntryForWriting();
+ if (mOnCacheEntryAvailableCallback) {
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Unexpected state");
+ return NS_OK;
+ }
+
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ } else {
+ // check result of OnOfflineCacheEntryForWritingAvailable()
+ if (NS_FAILED(rv))
+ return rv;
}
- return NS_OK;
+ return Connect(false);
}
nsresult
nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
{
LOG(("nsHttpChannel::DoAuthRetry [this=%p]\n", this));
NS_ASSERTION(!mTransaction, "should not have a transaction");
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -205,24 +205,29 @@ private:
void HandleAsyncReplaceWithProxy();
nsresult ContinueHandleAsyncReplaceWithProxy(nsresult);
nsresult ResolveProxy();
// cache specific methods
nsresult OpenCacheEntry();
nsresult OnOfflineCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
nsCacheAccessMode aAccess,
- nsresult aResult,
- bool aSync);
- nsresult OpenNormalCacheEntry(bool aSync);
+ nsresult aResult);
+ nsresult OpenNormalCacheEntry();
nsresult OnNormalCacheEntryAvailable(nsICacheEntryDescriptor *aEntry,
nsCacheAccessMode aAccess,
- nsresult aResult,
- bool aSync);
+ nsresult aResult);
nsresult OpenOfflineCacheEntryForWriting();
+ nsresult OnOfflineCacheEntryForWritingAvailable(
+ nsICacheEntryDescriptor *aEntry,
+ nsCacheAccessMode aAccess,
+ nsresult aResult);
+ nsresult OnCacheEntryAvailableInternal(nsICacheEntryDescriptor *entry,
+ nsCacheAccessMode access,
+ nsresult status);
nsresult GenerateCacheKey(PRUint32 postID, nsACString &key);
nsresult UpdateExpirationTime();
nsresult CheckCache();
nsresult ShouldUpdateOfflineCacheEntry(bool *shouldCacheForOfflineUse);
nsresult ReadFromCache();
void CloseCacheEntry(bool doomOnFailure);
void CloseOfflineCacheEntry();
nsresult InitCacheEntry();
@@ -294,19 +299,18 @@ private:
nsCOMPtr<nsICacheEntryDescriptor> mCacheEntry;
nsRefPtr<nsInputStreamPump> mCachePump;
nsAutoPtr<nsHttpResponseHead> mCachedResponseHead;
nsCacheAccessMode mCacheAccess;
PRUint32 mPostID;
PRUint32 mRequestTime;
typedef nsresult (nsHttpChannel:: *nsOnCacheEntryAvailableCallback)(
- nsICacheEntryDescriptor *, nsCacheAccessMode, nsresult, bool);
+ nsICacheEntryDescriptor *, nsCacheAccessMode, nsresult);
nsOnCacheEntryAvailableCallback mOnCacheEntryAvailableCallback;
- bool mAsyncCacheOpen;
nsCOMPtr<nsICacheEntryDescriptor> mOfflineCacheEntry;
nsCacheAccessMode mOfflineCacheAccess;
nsCString mOfflineCacheClientID;
// auth specific data
nsCOMPtr<nsIHttpChannelAuthProvider> mAuthProvider;