netwerk/protocol/http/nsHttpChannel.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim:set expandtab ts=4 sw=4 sts=4 cin: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 // HttpLog.h should generally be included first
     8 #include "HttpLog.h"
    10 #include "nsHttp.h"
    11 #include "nsHttpChannel.h"
    12 #include "nsHttpHandler.h"
    13 #include "nsIApplicationCacheService.h"
    14 #include "nsIApplicationCacheContainer.h"
    15 #include "nsICacheStorageService.h"
    16 #include "nsICacheStorage.h"
    17 #include "nsICacheEntry.h"
    18 #include "nsICryptoHash.h"
    19 #include "nsIStringBundle.h"
    20 #include "nsIStreamListenerTee.h"
    21 #include "nsISeekableStream.h"
    22 #include "nsILoadGroupChild.h"
    23 #include "nsIProtocolProxyService2.h"
    24 #include "nsMimeTypes.h"
    25 #include "nsNetUtil.h"
    26 #include "prprf.h"
    27 #include "prnetdb.h"
    28 #include "nsEscape.h"
    29 #include "nsStreamUtils.h"
    30 #include "nsIOService.h"
    31 #include "nsDNSPrefetch.h"
    32 #include "nsChannelClassifier.h"
    33 #include "nsIRedirectResultListener.h"
    34 #include "mozilla/TimeStamp.h"
    35 #include "nsError.h"
    36 #include "nsPrintfCString.h"
    37 #include "nsAlgorithm.h"
    38 #include "GeckoProfiler.h"
    39 #include "nsIConsoleService.h"
    40 #include "mozilla/Attributes.h"
    41 #include "mozilla/VisualEventTracer.h"
    42 #include "nsISSLSocketControl.h"
    43 #include "sslt.h"
    44 #include "nsContentUtils.h"
    45 #include "nsIPermissionManager.h"
    46 #include "nsIPrincipal.h"
    47 #include "nsIScriptSecurityManager.h"
    48 #include "nsISSLStatus.h"
    49 #include "nsISSLStatusProvider.h"
    50 #include "LoadContextInfo.h"
    51 #include "netCore.h"
    52 #include "nsHttpTransaction.h"
    53 #include "nsICacheEntryDescriptor.h"
    54 #include "nsICancelable.h"
    55 #include "nsIHttpChannelAuthProvider.h"
    56 #include "nsIHttpEventSink.h"
    57 #include "nsIPrompt.h"
    58 #include "nsInputStreamPump.h"
    59 #include "nsURLHelper.h"
    60 #include "nsISocketTransport.h"
    61 #include "nsICacheSession.h"
    62 #include "nsIStreamConverterService.h"
    63 #include "nsISiteSecurityService.h"
    64 #include "nsCRT.h"
    65 #include "nsPIDOMWindow.h"
    66 #include "nsPerformance.h"
    67 #include "CacheObserver.h"
    68 #include "mozilla/Telemetry.h"
    69 #include "mozIThirdPartyUtil.h"
    71 namespace mozilla { namespace net {
    73 namespace {
    75 // True if the local cache should be bypassed when processing a request.
    76 #define BYPASS_LOCAL_CACHE(loadFlags) \
    77         (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
    78                       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))
    80 #define CACHE_FILE_GONE(result) \
    81         ((result) == NS_ERROR_FILE_NOT_FOUND || \
    82          (result) == NS_ERROR_FILE_CORRUPTED)
    84 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
    85 static NS_DEFINE_CID(kStreamTransportServiceCID,
    86                      NS_STREAMTRANSPORTSERVICE_CID);
    88 enum CacheDisposition {
    89     kCacheHit = 1,
    90     kCacheHitViaReval = 2,
    91     kCacheMissedViaReval = 3,
    92     kCacheMissed = 4
    93 };
    95 void
    96 AccumulateCacheHitTelemetry(CacheDisposition hitOrMiss)
    97 {
    98     if (!CacheObserver::UseNewCache()) {
    99         Telemetry::Accumulate(Telemetry::HTTP_CACHE_DISPOSITION_2, hitOrMiss);
   100     }
   101     else {
   102         Telemetry::Accumulate(Telemetry::HTTP_CACHE_DISPOSITION_2_V2, hitOrMiss);
   104         int32_t experiment = CacheObserver::HalfLifeExperiment();
   105         if (experiment > 0 && hitOrMiss == kCacheMissed) {
   106             Telemetry::Accumulate(Telemetry::HTTP_CACHE_MISS_HALFLIFE_EXPERIMENT,
   107                                   experiment - 1);
   108         }
   109     }
   110 }
   112 // Computes and returns a SHA1 hash of the input buffer. The input buffer
   113 // must be a null-terminated string.
   114 nsresult
   115 Hash(const char *buf, nsACString &hash)
   116 {
   117     nsresult rv;
   119     nsCOMPtr<nsICryptoHash> hasher
   120       = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   121     NS_ENSURE_SUCCESS(rv, rv);
   123     rv = hasher->Init(nsICryptoHash::SHA1);
   124     NS_ENSURE_SUCCESS(rv, rv);
   126     rv = hasher->Update(reinterpret_cast<unsigned const char*>(buf),
   127                          strlen(buf));
   128     NS_ENSURE_SUCCESS(rv, rv);
   130     rv = hasher->Finish(true, hash);
   131     NS_ENSURE_SUCCESS(rv, rv);
   133     return NS_OK;
   134 }
   136 bool IsRedirectStatus(uint32_t status)
   137 {
   138     // 305 disabled as a security measure (see bug 187996).
   139     return status == 300 || status == 301 || status == 302 || status == 303 ||
   140            status == 307 || status == 308;
   141 }
   143 // We only treat 3xx responses as redirects if they have a Location header and
   144 // the status code is in a whitelist.
   145 bool
   146 WillRedirect(const nsHttpResponseHead * response)
   147 {
   148     return IsRedirectStatus(response->Status()) &&
   149            response->PeekHeader(nsHttp::Location);
   150 }
   152 } // unnamed namespace
   154 class AutoRedirectVetoNotifier
   155 {
   156 public:
   157     AutoRedirectVetoNotifier(nsHttpChannel* channel) : mChannel(channel)
   158     {
   159       if (mChannel->mHasAutoRedirectVetoNotifier) {
   160         MOZ_CRASH("Nested AutoRedirectVetoNotifier on the stack");
   161         mChannel = nullptr;
   162         return;
   163       }
   165       mChannel->mHasAutoRedirectVetoNotifier = true;
   166     }
   167     ~AutoRedirectVetoNotifier() {ReportRedirectResult(false);}
   168     void RedirectSucceeded() {ReportRedirectResult(true);}
   170 private:
   171     nsHttpChannel* mChannel;
   172     void ReportRedirectResult(bool succeeded);
   173 };
   175 void
   176 AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded)
   177 {
   178     if (!mChannel)
   179         return;
   181     mChannel->mRedirectChannel = nullptr;
   183     nsCOMPtr<nsIRedirectResultListener> vetoHook;
   184     NS_QueryNotificationCallbacks(mChannel,
   185                                   NS_GET_IID(nsIRedirectResultListener),
   186                                   getter_AddRefs(vetoHook));
   188     nsHttpChannel* channel = mChannel;
   189     mChannel = nullptr;
   191     if (vetoHook)
   192         vetoHook->OnRedirectResult(succeeded);
   194     // Drop after the notification
   195     channel->mHasAutoRedirectVetoNotifier = false;
   197     MOZ_EVENT_TRACER_DONE(channel, "net::http::redirect-callbacks");
   198 }
   200 //-----------------------------------------------------------------------------
   201 // nsHttpChannel <public>
   202 //-----------------------------------------------------------------------------
   204 nsHttpChannel::nsHttpChannel()
   205     : HttpAsyncAborter<nsHttpChannel>(MOZ_THIS_IN_INITIALIZER_LIST())
   206     , mLogicalOffset(0)
   207     , mPostID(0)
   208     , mRequestTime(0)
   209     , mOfflineCacheLastModifiedTime(0)
   210     , mCachedContentIsValid(false)
   211     , mCachedContentIsPartial(false)
   212     , mTransactionReplaced(false)
   213     , mAuthRetryPending(false)
   214     , mProxyAuthPending(false)
   215     , mResuming(false)
   216     , mInitedCacheEntry(false)
   217     , mFallbackChannel(false)
   218     , mCustomConditionalRequest(false)
   219     , mFallingBack(false)
   220     , mWaitingForRedirectCallback(false)
   221     , mRequestTimeInitialized(false)
   222     , mCacheEntryIsReadOnly(false)
   223     , mCacheEntryIsWriteOnly(false)
   224     , mCacheEntriesToWaitFor(0)
   225     , mHasQueryString(0)
   226     , mConcurentCacheAccess(0)
   227     , mIsPartialRequest(0)
   228     , mHasAutoRedirectVetoNotifier(0)
   229     , mDidReval(false)
   230     , mForcePending(false)
   231 {
   232     LOG(("Creating nsHttpChannel [this=%p]\n", this));
   233     mChannelCreationTime = PR_Now();
   234     mChannelCreationTimestamp = TimeStamp::Now();
   235 }
   237 nsHttpChannel::~nsHttpChannel()
   238 {
   239     LOG(("Destroying nsHttpChannel [this=%p]\n", this));
   241     if (mAuthProvider)
   242         mAuthProvider->Disconnect(NS_ERROR_ABORT);
   243 }
   245 nsresult
   246 nsHttpChannel::Init(nsIURI *uri,
   247                     uint32_t caps,
   248                     nsProxyInfo *proxyInfo,
   249                     uint32_t proxyResolveFlags,
   250                     nsIURI *proxyURI)
   251 {
   252 #ifdef MOZ_VISUAL_EVENT_TRACER
   253     nsAutoCString url;
   254     uri->GetAsciiSpec(url);
   255     MOZ_EVENT_TRACER_NAME_OBJECT(this, url.get());
   256 #endif
   258     nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo,
   259                                         proxyResolveFlags, proxyURI);
   260     if (NS_FAILED(rv))
   261         return rv;
   263     LOG(("nsHttpChannel::Init [this=%p]\n", this));
   265     return rv;
   266 }
   267 //-----------------------------------------------------------------------------
   268 // nsHttpChannel <private>
   269 //-----------------------------------------------------------------------------
   271 nsresult
   272 nsHttpChannel::Connect()
   273 {
   274     nsresult rv;
   276     LOG(("nsHttpChannel::Connect [this=%p]\n", this));
   278     // Even if we're in private browsing mode, we still enforce existing STS
   279     // data (it is read-only).
   280     // if the connection is not using SSL and either the exact host matches or
   281     // a superdomain wants to force HTTPS, do it.
   282     bool usingSSL = false;
   283     rv = mURI->SchemeIs("https", &usingSSL);
   284     NS_ENSURE_SUCCESS(rv,rv);
   286     if (!usingSSL) {
   287         // enforce Strict-Transport-Security
   288         nsISiteSecurityService* sss = gHttpHandler->GetSSService();
   289         NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
   291         bool isStsHost = false;
   292         uint32_t flags = mPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
   293         rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, mURI, flags,
   294                               &isStsHost);
   296         // if the SSS check fails, it's likely because this load is on a
   297         // malformed URI or something else in the setup is wrong, so any error
   298         // should be reported.
   299         NS_ENSURE_SUCCESS(rv, rv);
   301         if (isStsHost) {
   302             LOG(("nsHttpChannel::Connect() STS permissions found\n"));
   303             return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
   304         }
   305     }
   307     // ensure that we are using a valid hostname
   308     if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Host())))
   309         return NS_ERROR_UNKNOWN_HOST;
   311     // Finalize ConnectionInfo flags before SpeculativeConnect
   312     mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
   313     mConnectionInfo->SetPrivate(mPrivateBrowsing);
   315     // Consider opening a TCP connection right away
   316     RetrieveSSLOptions();
   317     SpeculativeConnect();
   319     // Don't allow resuming when cache must be used
   320     if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
   321         LOG(("Resuming from cache is not supported yet"));
   322         return NS_ERROR_DOCUMENT_NOT_CACHED;
   323     }
   325     if (!gHttpHandler->UseCache()) {
   326         return ContinueConnect();
   327     }
   329     // open a cache entry for this channel...
   330     rv = OpenCacheEntry(usingSSL);
   332     // do not continue if asyncOpenCacheEntry is in progress
   333     if (mCacheEntriesToWaitFor) {
   334         MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state");
   335         return NS_OK;
   336     }
   338     if (NS_FAILED(rv)) {
   339         LOG(("OpenCacheEntry failed [rv=%x]\n", rv));
   340         // if this channel is only allowed to pull from the cache, then
   341         // we must fail if we were unable to open a cache entry.
   342         if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
   343             // If we have a fallback URI (and we're not already
   344             // falling back), process the fallback asynchronously.
   345             if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
   346                 return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
   347             }
   348             return NS_ERROR_DOCUMENT_NOT_CACHED;
   349         }
   350         // otherwise, let's just proceed without using the cache.
   351     }
   353     return ContinueConnect();
   354 }
   356 nsresult
   357 nsHttpChannel::ContinueConnect()
   358 {
   359     // we may or may not have a cache entry at this point
   360     if (mCacheEntry) {
   361         // read straight from the cache if possible...
   362         if (mCachedContentIsValid) {
   363             nsRunnableMethod<nsHttpChannel> *event = nullptr;
   364             if (!mCachedContentIsPartial) {
   365                 AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse, &event);
   366             }
   367             nsresult rv = ReadFromCache(true);
   368             if (NS_FAILED(rv) && event) {
   369                 event->Revoke();
   370             }
   372             AccumulateCacheHitTelemetry(kCacheHit);
   374             return rv;
   375         }
   376         else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
   377             // the cache contains the requested resource, but it must be
   378             // validated before we can reuse it.  since we are not allowed
   379             // to hit the net, there's nothing more to do.  the document
   380             // is effectively not in the cache.
   381             LOG(("  !mCachedContentIsValid && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
   382             return NS_ERROR_DOCUMENT_NOT_CACHED;
   383         }
   384     }
   385     else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
   386         // If we have a fallback URI (and we're not already
   387         // falling back), process the fallback asynchronously.
   388         if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
   389             return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
   390         }
   391         LOG(("  !mCachedEntry && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
   392         return NS_ERROR_DOCUMENT_NOT_CACHED;
   393     }
   395     if (mLoadFlags & LOAD_NO_NETWORK_IO) {
   396         LOG(("  mLoadFlags & LOAD_NO_NETWORK_IO"));
   397         return NS_ERROR_DOCUMENT_NOT_CACHED;
   398     }
   400     // hit the net...
   401     nsresult rv = SetupTransaction();
   402     if (NS_FAILED(rv)) return rv;
   404     rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
   405     if (NS_FAILED(rv)) return rv;
   407     rv = mTransactionPump->AsyncRead(this, nullptr);
   408     if (NS_FAILED(rv)) return rv;
   410     uint32_t suspendCount = mSuspendCount;
   411     while (suspendCount--)
   412         mTransactionPump->Suspend();
   414     return NS_OK;
   415 }
   417 void
   418 nsHttpChannel::SpeculativeConnect()
   419 {
   420     // Before we take the latency hit of dealing with the cache, try and
   421     // get the TCP (and SSL) handshakes going so they can overlap.
   423     // don't speculate on uses of the offline application cache,
   424     // if we are offline, when doing http upgrade (i.e. websockets bootstrap),
   425     // or if we can't do keep-alive (because then we couldn't reuse
   426     // the speculative connection anyhow).
   427     if (mApplicationCache || gIOService->IsOffline() ||
   428         mUpgradeProtocolCallback || !(mCaps & NS_HTTP_ALLOW_KEEPALIVE))
   429         return;
   431     // LOAD_ONLY_FROM_CACHE and LOAD_NO_NETWORK_IO must not hit network.
   432     // LOAD_FROM_CACHE and LOAD_CHECK_OFFLINE_CACHE are unlikely to hit network,
   433     // so skip preconnects for them.
   434     if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE |
   435                       LOAD_NO_NETWORK_IO | LOAD_CHECK_OFFLINE_CACHE))
   436         return;
   438     nsCOMPtr<nsIInterfaceRequestor> callbacks;
   439     NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
   440                                            getter_AddRefs(callbacks));
   441     if (!callbacks)
   442         return;
   444     gHttpHandler->SpeculativeConnect(
   445         mConnectionInfo, callbacks,
   446         mCaps & (NS_HTTP_ALLOW_RSA_FALSESTART | NS_HTTP_DISALLOW_SPDY));
   447 }
   449 void
   450 nsHttpChannel::DoNotifyListenerCleanup()
   451 {
   452     // We don't need this info anymore
   453     CleanRedirectCacheChainIfNecessary();
   454 }
   456 void
   457 nsHttpChannel::HandleAsyncRedirect()
   458 {
   459     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
   461     if (mSuspendCount) {
   462         LOG(("Waiting until resume to do async redirect [this=%p]\n", this));
   463         mCallOnResume = &nsHttpChannel::HandleAsyncRedirect;
   464         return;
   465     }
   467     nsresult rv = NS_OK;
   469     LOG(("nsHttpChannel::HandleAsyncRedirect [this=%p]\n", this));
   471     // since this event is handled asynchronously, it is possible that this
   472     // channel could have been canceled, in which case there would be no point
   473     // in processing the redirect.
   474     if (NS_SUCCEEDED(mStatus)) {
   475         PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
   476         rv = AsyncProcessRedirection(mResponseHead->Status());
   477         if (NS_FAILED(rv)) {
   478             PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
   479             // TODO: if !DoNotRender3xxBody(), render redirect body instead.
   480             // But first we need to cache 3xx bodies (bug 748510)
   481             ContinueHandleAsyncRedirect(rv);
   482         }
   483     }
   484     else {
   485         ContinueHandleAsyncRedirect(NS_OK);
   486     }
   487 }
   489 nsresult
   490 nsHttpChannel::ContinueHandleAsyncRedirect(nsresult rv)
   491 {
   492     if (NS_FAILED(rv)) {
   493         // If AsyncProcessRedirection fails, then we have to send out the
   494         // OnStart/OnStop notifications.
   495         LOG(("ContinueHandleAsyncRedirect got failure result [rv=%x]\n", rv));
   496         mStatus = rv;
   497         DoNotifyListener();
   498     }
   500     // close the cache entry.  Blow it away if we couldn't process the redirect
   501     // for some reason (the cache entry might be corrupt).
   502     if (mCacheEntry) {
   503         if (NS_FAILED(rv))
   504             mCacheEntry->AsyncDoom(nullptr);
   505     }
   506     CloseCacheEntry(false);
   508     mIsPending = false;
   510     if (mLoadGroup)
   511         mLoadGroup->RemoveRequest(this, nullptr, mStatus);
   513     return NS_OK;
   514 }
   516 void
   517 nsHttpChannel::HandleAsyncNotModified()
   518 {
   519     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
   521     if (mSuspendCount) {
   522         LOG(("Waiting until resume to do async not-modified [this=%p]\n",
   523              this));
   524         mCallOnResume = &nsHttpChannel::HandleAsyncNotModified;
   525         return;
   526     }
   528     LOG(("nsHttpChannel::HandleAsyncNotModified [this=%p]\n", this));
   530     DoNotifyListener();
   532     CloseCacheEntry(true);
   534     mIsPending = false;
   536     if (mLoadGroup)
   537         mLoadGroup->RemoveRequest(this, nullptr, mStatus);
   538 }
   540 void
   541 nsHttpChannel::HandleAsyncFallback()
   542 {
   543     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
   545     if (mSuspendCount) {
   546         LOG(("Waiting until resume to do async fallback [this=%p]\n", this));
   547         mCallOnResume = &nsHttpChannel::HandleAsyncFallback;
   548         return;
   549     }
   551     nsresult rv = NS_OK;
   553     LOG(("nsHttpChannel::HandleAsyncFallback [this=%p]\n", this));
   555     // since this event is handled asynchronously, it is possible that this
   556     // channel could have been canceled, in which case there would be no point
   557     // in processing the fallback.
   558     if (!mCanceled) {
   559         PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
   560         bool waitingForRedirectCallback;
   561         rv = ProcessFallback(&waitingForRedirectCallback);
   562         if (waitingForRedirectCallback)
   563             return;
   564         PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
   565     }
   567     ContinueHandleAsyncFallback(rv);
   568 }
   570 nsresult
   571 nsHttpChannel::ContinueHandleAsyncFallback(nsresult rv)
   572 {
   573     if (!mCanceled && (NS_FAILED(rv) || !mFallingBack)) {
   574         // If ProcessFallback fails, then we have to send out the
   575         // OnStart/OnStop notifications.
   576         LOG(("ProcessFallback failed [rv=%x, %d]\n", rv, mFallingBack));
   577         mStatus = NS_FAILED(rv) ? rv : NS_ERROR_DOCUMENT_NOT_CACHED;
   578         DoNotifyListener();
   579     }
   581     mIsPending = false;
   583     if (mLoadGroup)
   584         mLoadGroup->RemoveRequest(this, nullptr, mStatus);
   586     return rv;
   587 }
   589 void
   590 nsHttpChannel::SetupTransactionLoadGroupInfo()
   591 {
   592     // Find the loadgroup at the end of the chain in order
   593     // to make sure all channels derived from the load group
   594     // use the same connection scope.
   595     nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(mLoadGroup);
   596     if (!childLoadGroup)
   597         return;
   599     nsCOMPtr<nsILoadGroup> rootLoadGroup;
   600     childLoadGroup->GetRootLoadGroup(getter_AddRefs(rootLoadGroup));
   601     if (!rootLoadGroup)
   602         return;
   604     // Set the load group connection scope on the transaction
   605     nsCOMPtr<nsILoadGroupConnectionInfo> ci;
   606     rootLoadGroup->GetConnectionInfo(getter_AddRefs(ci));
   607     if (ci)
   608         mTransaction->SetLoadGroupConnectionInfo(ci);
   609 }
   611 void
   612 nsHttpChannel::RetrieveSSLOptions()
   613 {
   614     if (!IsHTTPS() || mPrivateBrowsing)
   615         return;
   617     nsIPrincipal *principal = GetPrincipal();
   618     if (!principal)
   619         return;
   621     nsCOMPtr<nsIPermissionManager> permMgr =
   622         do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
   623     if (!permMgr)
   624         return;
   626     uint32_t perm;
   627     nsresult rv = permMgr->TestPermissionFromPrincipal(principal,
   628                                                        "falsestart-rsa", &perm);
   629     if (NS_SUCCEEDED(rv) && perm == nsIPermissionManager::ALLOW_ACTION) {
   630         LOG(("nsHttpChannel::RetrieveSSLOptions [this=%p] "
   631              "falsestart-rsa permission found\n", this));
   632         mCaps |= NS_HTTP_ALLOW_RSA_FALSESTART;
   633     }
   634 }
   636 static bool
   637 SafeForPipelining(nsHttpRequestHead::ParsedMethodType method,
   638                   const nsCString &methodString)
   639 {
   640     if (method == nsHttpRequestHead::kMethod_Get ||
   641         method == nsHttpRequestHead::kMethod_Head ||
   642         method == nsHttpRequestHead::kMethod_Options) {
   643         return true;
   644     }
   646     if (method != nsHttpRequestHead::kMethod_Custom) {
   647         return false;
   648     }
   650     return (!strcmp(methodString.get(), "PROPFIND") ||
   651             !strcmp(methodString.get(), "PROPPATCH"));
   652 }
   654 nsresult
   655 nsHttpChannel::SetupTransaction()
   656 {
   657     LOG(("nsHttpChannel::SetupTransaction [this=%p]\n", this));
   659     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED);
   661     nsresult rv;
   663     if (mCaps & NS_HTTP_ALLOW_PIPELINING) {
   664         //
   665         // disable pipelining if:
   666         //   (1) pipelining has been disabled by config
   667         //   (2) pipelining has been disabled by connection mgr info
   668         //   (3) request corresponds to a top-level document load (link click)
   669         //   (4) request method is non-idempotent
   670         //   (5) request is marked slow (e.g XHR)
   671         //
   672         if (!mAllowPipelining ||
   673            (mLoadFlags & (LOAD_INITIAL_DOCUMENT_URI | INHIBIT_PIPELINE)) ||
   674             !SafeForPipelining(mRequestHead.ParsedMethod(), mRequestHead.Method())) {
   675             LOG(("  pipelining disallowed\n"));
   676             mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
   677         }
   678     }
   680     if (!mAllowSpdy)
   681         mCaps |= NS_HTTP_DISALLOW_SPDY;
   683     // Use the URI path if not proxying (transparent proxying such as proxy
   684     // CONNECT does not count here). Also figure out what HTTP version to use.
   685     nsAutoCString buf, path;
   686     nsCString* requestURI;
   687     if (mConnectionInfo->UsingConnect() ||
   688         !mConnectionInfo->UsingHttpProxy()) {
   689         rv = mURI->GetPath(path);
   690         if (NS_FAILED(rv)) return rv;
   691         // path may contain UTF-8 characters, so ensure that they're escaped.
   692         if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII, buf))
   693             requestURI = &buf;
   694         else
   695             requestURI = &path;
   696         mRequestHead.SetVersion(gHttpHandler->HttpVersion());
   697     }
   698     else {
   699         rv = mURI->GetUserPass(buf);
   700         if (NS_FAILED(rv)) return rv;
   701         if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) ||
   702                                 strncmp(mSpec.get(), "https:", 6) == 0)) {
   703             nsCOMPtr<nsIURI> tempURI;
   704             rv = mURI->Clone(getter_AddRefs(tempURI));
   705             if (NS_FAILED(rv)) return rv;
   706             rv = tempURI->SetUserPass(EmptyCString());
   707             if (NS_FAILED(rv)) return rv;
   708             rv = tempURI->GetAsciiSpec(path);
   709             if (NS_FAILED(rv)) return rv;
   710             requestURI = &path;
   711         }
   712         else
   713             requestURI = &mSpec;
   714         mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion());
   715     }
   717     // trim off the #ref portion if any...
   718     int32_t ref = requestURI->FindChar('#');
   719     if (ref != kNotFound)
   720         requestURI->SetLength(ref);
   722     mRequestHead.SetRequestURI(*requestURI);
   724     // set the request time for cache expiration calculations
   725     mRequestTime = NowInSeconds();
   726     mRequestTimeInitialized = true;
   728     // if doing a reload, force end-to-end
   729     if (mLoadFlags & LOAD_BYPASS_CACHE) {
   730         // We need to send 'Pragma:no-cache' to inhibit proxy caching even if
   731         // no proxy is configured since we might be talking with a transparent
   732         // proxy, i.e. one that operates at the network level.  See bug #14772.
   733         mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
   734         // If we're configured to speak HTTP/1.1 then also send 'Cache-control:
   735         // no-cache'
   736         if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1)
   737             mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true);
   738     }
   739     else if ((mLoadFlags & VALIDATE_ALWAYS) && !mCacheEntryIsWriteOnly) {
   740         // We need to send 'Cache-Control: max-age=0' to force each cache along
   741         // the path to the origin server to revalidate its own entry, if any,
   742         // with the next cache or server.  See bug #84847.
   743         //
   744         // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache'
   745         if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1)
   746             mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "max-age=0", true);
   747         else
   748             mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
   749     }
   751     if (mResuming) {
   752         char byteRange[32];
   753         PR_snprintf(byteRange, sizeof(byteRange), "bytes=%llu-", mStartPos);
   754         mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(byteRange));
   756         if (!mEntityID.IsEmpty()) {
   757             // Also, we want an error if this resource changed in the meantime
   758             // Format of the entity id is: escaped_etag/size/lastmod
   759             nsCString::const_iterator start, end, slash;
   760             mEntityID.BeginReading(start);
   761             mEntityID.EndReading(end);
   762             mEntityID.BeginReading(slash);
   764             if (FindCharInReadable('/', slash, end)) {
   765                 nsAutoCString ifMatch;
   766                 mRequestHead.SetHeader(nsHttp::If_Match,
   767                         NS_UnescapeURL(Substring(start, slash), 0, ifMatch));
   769                 ++slash; // Incrementing, so that searching for '/' won't find
   770                          // the same slash again
   771             }
   773             if (FindCharInReadable('/', slash, end)) {
   774                 mRequestHead.SetHeader(nsHttp::If_Unmodified_Since,
   775                         Substring(++slash, end));
   776             }
   777         }
   778     }
   780     // create wrapper for this channel's notification callbacks
   781     nsCOMPtr<nsIInterfaceRequestor> callbacks;
   782     NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
   783                                            getter_AddRefs(callbacks));
   784     if (!callbacks)
   785         return NS_ERROR_OUT_OF_MEMORY;
   787     // create the transaction object
   788     mTransaction = new nsHttpTransaction();
   789     if (!mTransaction)
   790         return NS_ERROR_OUT_OF_MEMORY;
   792     // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.
   793     if (mLoadFlags & LOAD_ANONYMOUS)
   794         mCaps |= NS_HTTP_LOAD_ANONYMOUS;
   796     if (mTimingEnabled)
   797         mCaps |= NS_HTTP_TIMING_ENABLED;
   799     if (mUpgradeProtocolCallback) {
   800         mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false);
   801         mRequestHead.SetHeaderOnce(nsHttp::Connection,
   802                                    nsHttp::Upgrade.get(),
   803                                    true);
   804         mCaps |=  NS_HTTP_STICKY_CONNECTION;
   805         mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
   806         mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
   807         mCaps |=  NS_HTTP_DISALLOW_SPDY;
   808     }
   810     nsCOMPtr<nsIAsyncInputStream> responseStream;
   811     rv = mTransaction->Init(mCaps, mConnectionInfo, &mRequestHead,
   812                             mUploadStream, mUploadStreamHasHeaders,
   813                             NS_GetCurrentThread(), callbacks, this,
   814                             getter_AddRefs(responseStream));
   815     if (NS_FAILED(rv)) {
   816         mTransaction = nullptr;
   817         return rv;
   818     }
   820     SetupTransactionLoadGroupInfo();
   822     rv = nsInputStreamPump::Create(getter_AddRefs(mTransactionPump),
   823                                    responseStream);
   824     return rv;
   825 }
   827 // NOTE: This function duplicates code from nsBaseChannel. This will go away
   828 // once HTTP uses nsBaseChannel (part of bug 312760)
   829 static void
   830 CallTypeSniffers(void *aClosure, const uint8_t *aData, uint32_t aCount)
   831 {
   832   nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
   834   nsAutoCString newType;
   835   NS_SniffContent(NS_CONTENT_SNIFFER_CATEGORY, chan, aData, aCount, newType);
   836   if (!newType.IsEmpty()) {
   837     chan->SetContentType(newType);
   838   }
   839 }
   841 nsresult
   842 nsHttpChannel::CallOnStartRequest()
   843 {
   844     nsresult rv;
   846     mTracingEnabled = false;
   848     // Allow consumers to override our content type
   849     if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
   850         // NOTE: We can have both a txn pump and a cache pump when the cache
   851         // content is partial. In that case, we need to read from the cache,
   852         // because that's the one that has the initial contents. If that fails
   853         // then give the transaction pump a shot.
   855         nsIChannel* thisChannel = static_cast<nsIChannel*>(this);
   857         bool typeSniffersCalled = false;
   858         if (mCachePump) {
   859           typeSniffersCalled =
   860             NS_SUCCEEDED(mCachePump->PeekStream(CallTypeSniffers, thisChannel));
   861         }
   863         if (!typeSniffersCalled && mTransactionPump) {
   864           mTransactionPump->PeekStream(CallTypeSniffers, thisChannel);
   865         }
   866     }
   868     bool shouldSniff = mResponseHead && (mResponseHead->ContentType().IsEmpty() ||
   869         ((mResponseHead->ContentType().EqualsLiteral(APPLICATION_OCTET_STREAM) &&
   870         (mLoadFlags & LOAD_TREAT_APPLICATION_OCTET_STREAM_AS_UNKNOWN))));
   872     if (shouldSniff) {
   873         MOZ_ASSERT(mConnectionInfo, "Should have connection info here");
   874         if (!mContentTypeHint.IsEmpty())
   875             mResponseHead->SetContentType(mContentTypeHint);
   876         else if (mResponseHead->Version() == NS_HTTP_VERSION_0_9 &&
   877                  mConnectionInfo->Port() != mConnectionInfo->DefaultPort())
   878             mResponseHead->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN));
   879         else {
   880             // Uh-oh.  We had better find out what type we are!
   882             // XXX This does not work with content-encodings...  but
   883             // neither does applying the conversion from the URILoader
   885             nsCOMPtr<nsIStreamConverterService> serv;
   886             rv = gHttpHandler->
   887                 GetStreamConverterService(getter_AddRefs(serv));
   888             // If we failed, we just fall through to the "normal" case
   889             if (NS_SUCCEEDED(rv)) {
   890                 nsCOMPtr<nsIStreamListener> converter;
   891                 rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
   892                                             "*/*",
   893                                             mListener,
   894                                             mListenerContext,
   895                                             getter_AddRefs(converter));
   896                 if (NS_SUCCEEDED(rv)) {
   897                     mListener = converter;
   898                 }
   899             }
   900         }
   901     }
   903     if (mResponseHead && mResponseHead->ContentCharset().IsEmpty())
   904         mResponseHead->SetContentCharset(mContentCharsetHint);
   906     if (mResponseHead && mCacheEntry) {
   907         // If we have a cache entry, set its predicted size to ContentLength to
   908         // avoid caching an entry that will exceed the max size limit.
   909         rv = mCacheEntry->SetPredictedDataSize(
   910             mResponseHead->ContentLength());
   911         if (NS_ERROR_FILE_TOO_BIG == rv) {
   912           mCacheEntry = nullptr;
   913           LOG(("  entry too big, throwing away"));
   914         } else {
   915           NS_ENSURE_SUCCESS(rv, rv);
   916         }
   917     }
   919     LOG(("  calling mListener->OnStartRequest\n"));
   920     if (mListener) {
   921         rv = mListener->OnStartRequest(this, mListenerContext);
   922         if (NS_FAILED(rv))
   923             return rv;
   924     } else {
   925         NS_WARNING("OnStartRequest skipped because of null listener");
   926     }
   928     // install stream converter if required
   929     rv = ApplyContentConversions();
   930     if (NS_FAILED(rv)) return rv;
   932     rv = EnsureAssocReq();
   933     if (NS_FAILED(rv))
   934         return rv;
   936     // if this channel is for a download, close off access to the cache.
   937     if (mCacheEntry && mChannelIsForDownload) {
   938         mCacheEntry->AsyncDoom(nullptr);
   940         // We must keep the cache entry in case of partial request.
   941         // Concurrent access is the same, we need the entry in
   942         // OnStopRequest.
   943         if (!mCachedContentIsPartial && !mConcurentCacheAccess)
   944             CloseCacheEntry(false);
   945     }
   947     if (!mCanceled) {
   948         // create offline cache entry if offline caching was requested
   949         if (ShouldUpdateOfflineCacheEntry()) {
   950             LOG(("writing to the offline cache"));
   951             rv = InitOfflineCacheEntry();
   952             if (NS_FAILED(rv)) return rv;
   954             // InitOfflineCacheEntry may have closed mOfflineCacheEntry
   955             if (mOfflineCacheEntry) {
   956                 rv = InstallOfflineCacheListener();
   957                 if (NS_FAILED(rv)) return rv;
   958             }
   959         } else if (mApplicationCacheForWrite) {
   960             LOG(("offline cache is up to date, not updating"));
   961             CloseOfflineCacheEntry();
   962         }
   963     }
   965     return NS_OK;
   966 }
   968 nsresult
   969 nsHttpChannel::ProcessFailedProxyConnect(uint32_t httpStatus)
   970 {
   971     // Failure to set up a proxy tunnel via CONNECT means one of the following:
   972     // 1) Proxy wants authorization, or forbids.
   973     // 2) DNS at proxy couldn't resolve target URL.
   974     // 3) Proxy connection to target failed or timed out.
   975     // 4) Eve intercepted our CONNECT, and is replying with malicious HTML.
   976     //
   977     // Our current architecture would parse the proxy's response content with
   978     // the permission of the target URL.  Given #4, we must avoid rendering the
   979     // body of the reply, and instead give the user a (hopefully helpful)
   980     // boilerplate error page, based on just the HTTP status of the reply.
   982     MOZ_ASSERT(mConnectionInfo->UsingConnect(),
   983                "proxy connect failed but not using CONNECT?");
   984     nsresult rv;
   985     switch (httpStatus)
   986     {
   987     case 300: case 301: case 302: case 303: case 307: case 308:
   988         // Bad redirect: not top-level, or it's a POST, bad/missing Location,
   989         // or ProcessRedirect() failed for some other reason.  Legal
   990         // redirects that fail because site not available, etc., are handled
   991         // elsewhere, in the regular codepath.
   992         rv = NS_ERROR_CONNECTION_REFUSED;
   993         break;
   994     case 403: // HTTP/1.1: "Forbidden"
   995     case 407: // ProcessAuthentication() failed
   996     case 501: // HTTP/1.1: "Not Implemented"
   997         // user sees boilerplate Mozilla "Proxy Refused Connection" page.
   998         rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
   999         break;
  1000     // Squid sends 404 if DNS fails (regular 404 from target is tunneled)
  1001     case 404: // HTTP/1.1: "Not Found"
  1002     // RFC 2616: "some deployed proxies are known to return 400 or 500 when
  1003     // DNS lookups time out."  (Squid uses 500 if it runs out of sockets: so
  1004     // we have a conflict here).
  1005     case 400: // HTTP/1.1 "Bad Request"
  1006     case 500: // HTTP/1.1: "Internal Server Error"
  1007         /* User sees: "Address Not Found: Firefox can't find the server at
  1008          * www.foo.com."
  1009          */
  1010         rv = NS_ERROR_UNKNOWN_HOST;
  1011         break;
  1012     case 502: // HTTP/1.1: "Bad Gateway" (invalid resp from target server)
  1013     // Squid returns 503 if target request fails for anything but DNS.
  1014     case 503: // HTTP/1.1: "Service Unavailable"
  1015         /* User sees: "Failed to Connect:
  1016          *  Firefox can't establish a connection to the server at
  1017          *  www.foo.com.  Though the site seems valid, the browser
  1018          *  was unable to establish a connection."
  1019          */
  1020         rv = NS_ERROR_CONNECTION_REFUSED;
  1021         break;
  1022     // RFC 2616 uses 504 for both DNS and target timeout, so not clear what to
  1023     // do here: picking target timeout, as DNS covered by 400/404/500
  1024     case 504: // HTTP/1.1: "Gateway Timeout"
  1025         // user sees: "Network Timeout: The server at www.foo.com
  1026         //              is taking too long to respond."
  1027         rv = NS_ERROR_NET_TIMEOUT;
  1028         break;
  1029     // Confused proxy server or malicious response
  1030     default:
  1031         rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
  1032         break;
  1034     LOG(("Cancelling failed proxy CONNECT [this=%p httpStatus=%u]\n",
  1035          this, httpStatus));
  1036     Cancel(rv);
  1037     CallOnStartRequest();
  1038     return rv;
  1041 /**
  1042  * Decide whether or not to remember Strict-Transport-Security, and whether
  1043  * or not to enforce channel integrity.
  1045  * @return NS_ERROR_FAILURE if there's security information missing even though
  1046  *             it's an HTTPS connection.
  1047  */
  1048 nsresult
  1049 nsHttpChannel::ProcessSTSHeader()
  1051     nsresult rv;
  1052     bool isHttps = false;
  1053     rv = mURI->SchemeIs("https", &isHttps);
  1054     NS_ENSURE_SUCCESS(rv, rv);
  1056     // If this channel is not loading securely, STS doesn't do anything.
  1057     // The upgrade to HTTPS takes place earlier in the channel load process.
  1058     if (!isHttps)
  1059         return NS_OK;
  1061     nsAutoCString asciiHost;
  1062     rv = mURI->GetAsciiHost(asciiHost);
  1063     NS_ENSURE_SUCCESS(rv, NS_OK);
  1065     // If the channel is not a hostname, but rather an IP, STS doesn't do
  1066     // anything.
  1067     PRNetAddr hostAddr;
  1068     if (PR_SUCCESS == PR_StringToNetAddr(asciiHost.get(), &hostAddr))
  1069         return NS_OK;
  1071     nsISiteSecurityService* sss = gHttpHandler->GetSSService();
  1072     NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
  1074     // mSecurityInfo may not always be present, and if it's not then it is okay
  1075     // to just disregard any STS headers since we know nothing about the
  1076     // security of the connection.
  1077     NS_ENSURE_TRUE(mSecurityInfo, NS_OK);
  1079     // Check the trustworthiness of the channel (are there any cert errors?)
  1080     // If there are certificate errors, we still load the data, we just ignore
  1081     // any STS headers that are present.
  1082     bool tlsIsBroken = false;
  1083     rv = sss->ShouldIgnoreHeaders(mSecurityInfo, &tlsIsBroken);
  1084     NS_ENSURE_SUCCESS(rv, NS_OK);
  1086     // If this was already an STS host, the connection should have been aborted
  1087     // by the bad cert handler in the case of cert errors.  If it didn't abort the connection,
  1088     // there's probably something funny going on.
  1089     // If this wasn't an STS host, errors are allowed, but no more STS processing
  1090     // will happen during the session.
  1091     bool wasAlreadySTSHost;
  1092     uint32_t flags =
  1093       NS_UsePrivateBrowsing(this) ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
  1094     rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, mURI, flags,
  1095                           &wasAlreadySTSHost);
  1096     // Failure here means STS is broken.  Don't prevent the load, but this
  1097     // shouldn't fail.
  1098     NS_ENSURE_SUCCESS(rv, NS_OK);
  1099     MOZ_ASSERT(!(wasAlreadySTSHost && tlsIsBroken),
  1100                "connection should have been aborted by nss-bad-cert-handler");
  1102     // Any STS header is ignored if the channel is not trusted due to
  1103     // certificate errors (STS Spec 7.1) -- there is nothing else to do, and
  1104     // the load may progress.
  1105     if (tlsIsBroken) {
  1106         LOG(("STS: Transport layer is not trustworthy, ignoring "
  1107              "STS headers and continuing load\n"));
  1108         return NS_OK;
  1111     // If there's a STS header, process it (STS Spec 7.1).  At this point in
  1112     // processing, the channel is trusted, so the header should not be ignored.
  1113     const nsHttpAtom atom = nsHttp::ResolveAtom("Strict-Transport-Security");
  1114     nsAutoCString stsHeader;
  1115     rv = mResponseHead->GetHeader(atom, stsHeader);
  1116     if (rv == NS_ERROR_NOT_AVAILABLE) {
  1117         LOG(("STS: No STS header, continuing load.\n"));
  1118         return NS_OK;
  1120     // All other failures are fatal.
  1121     NS_ENSURE_SUCCESS(rv, rv);
  1123     rv = sss->ProcessHeader(nsISiteSecurityService::HEADER_HSTS, mURI,
  1124                             stsHeader.get(), flags, nullptr, nullptr);
  1125     if (NS_FAILED(rv)) {
  1126         AddSecurityMessage(NS_LITERAL_STRING("InvalidSTSHeaders"),
  1127                 NS_LITERAL_STRING("Invalid HSTS Headers"));
  1128         LOG(("STS: Failed to parse STS header, continuing load.\n"));
  1131     return NS_OK;
  1134 bool
  1135 nsHttpChannel::IsHTTPS()
  1137     bool isHttps;
  1138     if (NS_FAILED(mURI->SchemeIs("https", &isHttps)) || !isHttps)
  1139         return false;
  1140     return true;
  1143 void
  1144 nsHttpChannel::ProcessSSLInformation()
  1146     // If this is HTTPS, record any use of RSA so that Key Exchange Algorithm
  1147     // can be whitelisted for TLS False Start in future sessions. We could
  1148     // do the same for DH but its rarity doesn't justify the lookup.
  1150     if (mCanceled || NS_FAILED(mStatus) || !mSecurityInfo ||
  1151         !IsHTTPS() || mPrivateBrowsing)
  1152         return;
  1154     nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(mSecurityInfo);
  1155     nsCOMPtr<nsISSLStatusProvider> statusProvider =
  1156         do_QueryInterface(mSecurityInfo);
  1157     if (!ssl || !statusProvider)
  1158         return;
  1159     nsCOMPtr<nsISSLStatus> sslstat;
  1160     statusProvider->GetSSLStatus(getter_AddRefs(sslstat));
  1161     if (!sslstat)
  1162         return;
  1164     // If certificate exceptions are being used don't record this information
  1165     // in the permission manager.
  1166     bool trustCheck;
  1167     if (NS_FAILED(sslstat->GetIsDomainMismatch(&trustCheck)) || trustCheck)
  1168         return;
  1169     if (NS_FAILED(sslstat->GetIsNotValidAtThisTime(&trustCheck)) || trustCheck)
  1170         return;
  1171     if (NS_FAILED(sslstat->GetIsUntrusted(&trustCheck)) || trustCheck)
  1172         return;
  1174     int16_t kea = ssl->GetKEAUsed();
  1176     nsIPrincipal *principal = GetPrincipal();
  1177     if (!principal)
  1178         return;
  1180     // set a permission manager flag that future transactions can
  1181     // use via RetrieveSSLOptions(()
  1183     nsCOMPtr<nsIPermissionManager> permMgr =
  1184         do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
  1185     if (!permMgr)
  1186         return;
  1188     // Allow this to stand for a week
  1189     int64_t expireTime = (PR_Now() / PR_USEC_PER_MSEC) +
  1190         (86400 * 7 * PR_MSEC_PER_SEC);
  1192     if (kea == ssl_kea_rsa) {
  1193         permMgr->AddFromPrincipal(principal, "falsestart-rsa",
  1194                                   nsIPermissionManager::ALLOW_ACTION,
  1195                                   nsIPermissionManager::EXPIRE_TIME,
  1196                                   expireTime);
  1197         LOG(("nsHttpChannel::ProcessSSLInformation [this=%p] "
  1198              "falsestart-rsa permission granted for this host\n", this));
  1199     } else {
  1200         permMgr->RemoveFromPrincipal(principal, "falsestart-rsa");
  1204 nsresult
  1205 nsHttpChannel::ProcessResponse()
  1207     nsresult rv;
  1208     uint32_t httpStatus = mResponseHead->Status();
  1210     // Gather data on whether the transaction and page (if this is
  1211     // the initial page load) is being loaded with SSL.
  1212     Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_IS_SSL,
  1213                           mConnectionInfo->UsingSSL());
  1214     if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
  1215         Telemetry::Accumulate(Telemetry::HTTP_PAGELOAD_IS_SSL,
  1216                               mConnectionInfo->UsingSSL());
  1219     LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n",
  1220         this, httpStatus));
  1222     if (mTransaction->ProxyConnectFailed()) {
  1223         // Only allow 407 (authentication required) to continue
  1224         if (httpStatus != 407)
  1225             return ProcessFailedProxyConnect(httpStatus);
  1226         // If proxy CONNECT response needs to complete, wait to process connection
  1227         // for Strict-Transport-Security.
  1228     } else {
  1229         // Given a successful connection, process any STS data that's relevant.
  1230         rv = ProcessSTSHeader();
  1231         MOZ_ASSERT(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load.");
  1234     MOZ_ASSERT(!mCachedContentIsValid);
  1236     ProcessSSLInformation();
  1238     // notify "http-on-examine-response" observers
  1239     gHttpHandler->OnExamineResponse(this);
  1241     SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie));
  1243     // handle unused username and password in url (see bug 232567)
  1244     if (httpStatus != 401 && httpStatus != 407) {
  1245         if (!mAuthRetryPending)
  1246             mAuthProvider->CheckForSuperfluousAuth();
  1247         if (mCanceled)
  1248             return CallOnStartRequest();
  1250         // reset the authentication's current continuation state because our
  1251         // last authentication attempt has been completed successfully
  1252         mAuthProvider->Disconnect(NS_ERROR_ABORT);
  1253         mAuthProvider = nullptr;
  1254         LOG(("  continuation state has been reset"));
  1257     bool successfulReval = false;
  1259     // handle different server response categories.  Note that we handle
  1260     // caching or not caching of error pages in
  1261     // nsHttpResponseHead::MustValidate; if you change this switch, update that
  1262     // one
  1263     switch (httpStatus) {
  1264     case 200:
  1265     case 203:
  1266         // Per RFC 2616, 14.35.2, "A server MAY ignore the Range header".
  1267         // So if a server does that and sends 200 instead of 206 that we
  1268         // expect, notify our caller.
  1269         // However, if we wanted to start from the beginning, let it go through
  1270         if (mResuming && mStartPos != 0) {
  1271             LOG(("Server ignored our Range header, cancelling [this=%p]\n", this));
  1272             Cancel(NS_ERROR_NOT_RESUMABLE);
  1273             rv = CallOnStartRequest();
  1274             break;
  1276         // these can normally be cached
  1277         rv = ProcessNormal();
  1278         MaybeInvalidateCacheEntryForSubsequentGet();
  1279         break;
  1280     case 206:
  1281         if (mCachedContentIsPartial) // an internal byte range request...
  1282             rv = ProcessPartialContent();
  1283         else {
  1284             mCacheInputStream.CloseAndRelease();
  1285             rv = ProcessNormal();
  1287         break;
  1288     case 300:
  1289     case 301:
  1290     case 302:
  1291     case 307:
  1292     case 308:
  1293     case 303:
  1294 #if 0
  1295     case 305: // disabled as a security measure (see bug 187996).
  1296 #endif
  1297         // don't store the response body for redirects
  1298         MaybeInvalidateCacheEntryForSubsequentGet();
  1299         PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse);
  1300         rv = AsyncProcessRedirection(httpStatus);
  1301         if (NS_FAILED(rv)) {
  1302             PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse);
  1303             LOG(("AsyncProcessRedirection failed [rv=%x]\n", rv));
  1304             // don't cache failed redirect responses.
  1305             if (mCacheEntry)
  1306                 mCacheEntry->AsyncDoom(nullptr);
  1307             if (DoNotRender3xxBody(rv)) {
  1308                 mStatus = rv;
  1309                 DoNotifyListener();
  1310             } else {
  1311                 rv = ContinueProcessResponse(rv);
  1314         break;
  1315     case 304:
  1316         rv = ProcessNotModified();
  1317         if (NS_FAILED(rv)) {
  1318             LOG(("ProcessNotModified failed [rv=%x]\n", rv));
  1319             mCacheInputStream.CloseAndRelease();
  1320             rv = ProcessNormal();
  1322         else {
  1323             successfulReval = true;
  1325         break;
  1326     case 401:
  1327     case 407:
  1328         rv = mAuthProvider->ProcessAuthentication(
  1329             httpStatus, mConnectionInfo->UsingSSL() &&
  1330                         mTransaction->ProxyConnectFailed());
  1331         if (rv == NS_ERROR_IN_PROGRESS)  {
  1332             // authentication prompt has been invoked and result
  1333             // is expected asynchronously
  1334             mAuthRetryPending = true;
  1335             if (httpStatus == 407 || mTransaction->ProxyConnectFailed())
  1336                 mProxyAuthPending = true;
  1338             // suspend the transaction pump to stop receiving the
  1339             // unauthenticated content data. We will throw that data
  1340             // away when user provides credentials or resume the pump
  1341             // when user refuses to authenticate.
  1342             LOG(("Suspending the transaction, asynchronously prompting for credentials"));
  1343             mTransactionPump->Suspend();
  1344             rv = NS_OK;
  1346         else if (NS_FAILED(rv)) {
  1347             LOG(("ProcessAuthentication failed [rv=%x]\n", rv));
  1348             if (mTransaction->ProxyConnectFailed())
  1349                 return ProcessFailedProxyConnect(httpStatus);
  1350             if (!mAuthRetryPending)
  1351                 mAuthProvider->CheckForSuperfluousAuth();
  1352             rv = ProcessNormal();
  1354         else
  1355             mAuthRetryPending = true; // see DoAuthRetry
  1356         break;
  1357     default:
  1358         rv = ProcessNormal();
  1359         MaybeInvalidateCacheEntryForSubsequentGet();
  1360         break;
  1363     CacheDisposition cacheDisposition;
  1364     if (!mDidReval)
  1365         cacheDisposition = kCacheMissed;
  1366     else if (successfulReval)
  1367         cacheDisposition = kCacheHitViaReval;
  1368     else
  1369         cacheDisposition = kCacheMissedViaReval;
  1371     AccumulateCacheHitTelemetry(cacheDisposition);
  1373     return rv;
  1376 nsresult
  1377 nsHttpChannel::ContinueProcessResponse(nsresult rv)
  1379     bool doNotRender = DoNotRender3xxBody(rv);
  1381     if (rv == NS_ERROR_DOM_BAD_URI && mRedirectURI) {
  1382         bool isHTTP = false;
  1383         if (NS_FAILED(mRedirectURI->SchemeIs("http", &isHTTP)))
  1384             isHTTP = false;
  1385         if (!isHTTP && NS_FAILED(mRedirectURI->SchemeIs("https", &isHTTP)))
  1386             isHTTP = false;
  1388         if (!isHTTP) {
  1389             // This was a blocked attempt to redirect and subvert the system by
  1390             // redirecting to another protocol (perhaps javascript:)
  1391             // In that case we want to throw an error instead of displaying the
  1392             // non-redirected response body.
  1393             LOG(("ContinueProcessResponse detected rejected Non-HTTP Redirection"));
  1394             doNotRender = true;
  1395             rv = NS_ERROR_CORRUPTED_CONTENT;
  1399     if (doNotRender) {
  1400         Cancel(rv);
  1401         DoNotifyListener();
  1402         return rv;
  1405     if (NS_SUCCEEDED(rv)) {
  1406         UpdateInhibitPersistentCachingFlag();
  1408         InitCacheEntry();
  1409         CloseCacheEntry(false);
  1411         if (mApplicationCacheForWrite) {
  1412             // Store response in the offline cache
  1413             InitOfflineCacheEntry();
  1414             CloseOfflineCacheEntry();
  1416         return NS_OK;
  1419     LOG(("ContinueProcessResponse got failure result [rv=%x]\n", rv));
  1420     if (mTransaction->ProxyConnectFailed()) {
  1421         return ProcessFailedProxyConnect(mRedirectType);
  1423     return ProcessNormal();
  1426 nsresult
  1427 nsHttpChannel::ProcessNormal()
  1429     nsresult rv;
  1431     LOG(("nsHttpChannel::ProcessNormal [this=%p]\n", this));
  1433     bool succeeded;
  1434     rv = GetRequestSucceeded(&succeeded);
  1435     if (NS_SUCCEEDED(rv) && !succeeded) {
  1436         PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
  1437         bool waitingForRedirectCallback;
  1438         (void)ProcessFallback(&waitingForRedirectCallback);
  1439         if (waitingForRedirectCallback) {
  1440             // The transaction has been suspended by ProcessFallback.
  1441             return NS_OK;
  1443         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
  1446     return ContinueProcessNormal(NS_OK);
  1449 nsresult
  1450 nsHttpChannel::ContinueProcessNormal(nsresult rv)
  1452     if (NS_FAILED(rv)) {
  1453         // Fill the failure status here, we have failed to fall back, thus we
  1454         // have to report our status as failed.
  1455         mStatus = rv;
  1456         DoNotifyListener();
  1457         return rv;
  1460     if (mFallingBack) {
  1461         // Do not continue with normal processing, fallback is in
  1462         // progress now.
  1463         return NS_OK;
  1466     // if we're here, then any byte-range requests failed to result in a partial
  1467     // response.  we must clear this flag to prevent BufferPartialContent from
  1468     // being called inside our OnDataAvailable (see bug 136678).
  1469     mCachedContentIsPartial = false;
  1471     ClearBogusContentEncodingIfNeeded();
  1473     UpdateInhibitPersistentCachingFlag();
  1475     // this must be called before firing OnStartRequest, since http clients,
  1476     // such as imagelib, expect our cache entry to already have the correct
  1477     // expiration time (bug 87710).
  1478     if (mCacheEntry) {
  1479         rv = InitCacheEntry();
  1480         if (NS_FAILED(rv))
  1481             CloseCacheEntry(true);
  1484     // Check that the server sent us what we were asking for
  1485     if (mResuming) {
  1486         // Create an entity id from the response
  1487         nsAutoCString id;
  1488         rv = GetEntityID(id);
  1489         if (NS_FAILED(rv)) {
  1490             // If creating an entity id is not possible -> error
  1491             Cancel(NS_ERROR_NOT_RESUMABLE);
  1493         else if (mResponseHead->Status() != 206 &&
  1494                  mResponseHead->Status() != 200) {
  1495             // Probably 404 Not Found, 412 Precondition Failed or
  1496             // 416 Invalid Range -> error
  1497             LOG(("Unexpected response status while resuming, aborting [this=%p]\n",
  1498                  this));
  1499             Cancel(NS_ERROR_ENTITY_CHANGED);
  1501         // If we were passed an entity id, verify it's equal to the server's
  1502         else if (!mEntityID.IsEmpty()) {
  1503             if (!mEntityID.Equals(id)) {
  1504                 LOG(("Entity mismatch, expected '%s', got '%s', aborting [this=%p]",
  1505                      mEntityID.get(), id.get(), this));
  1506                 Cancel(NS_ERROR_ENTITY_CHANGED);
  1511     rv = CallOnStartRequest();
  1512     if (NS_FAILED(rv)) return rv;
  1514     // install cache listener if we still have a cache entry open
  1515     if (mCacheEntry && !mLoadedFromApplicationCache) {
  1516         rv = InstallCacheListener();
  1517         if (NS_FAILED(rv)) return rv;
  1520     return NS_OK;
  1523 nsresult
  1524 nsHttpChannel::PromptTempRedirect()
  1526     if (!gHttpHandler->PromptTempRedirect()) {
  1527         return NS_OK;
  1529     nsresult rv;
  1530     nsCOMPtr<nsIStringBundleService> bundleService =
  1531             do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
  1532     if (NS_FAILED(rv)) return rv;
  1534     nsCOMPtr<nsIStringBundle> stringBundle;
  1535     rv = bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(stringBundle));
  1536     if (NS_FAILED(rv)) return rv;
  1538     nsXPIDLString messageString;
  1539     rv = stringBundle->GetStringFromName(MOZ_UTF16("RepostFormData"), getter_Copies(messageString));
  1540     // GetStringFromName can return NS_OK and nullptr messageString.
  1541     if (NS_SUCCEEDED(rv) && messageString) {
  1542         bool repost = false;
  1544         nsCOMPtr<nsIPrompt> prompt;
  1545         GetCallback(prompt);
  1546         if (!prompt)
  1547             return NS_ERROR_NO_INTERFACE;
  1549         prompt->Confirm(nullptr, messageString, &repost);
  1550         if (!repost)
  1551             return NS_ERROR_FAILURE;
  1554     return rv;
  1557 nsresult
  1558 nsHttpChannel::ProxyFailover()
  1560     LOG(("nsHttpChannel::ProxyFailover [this=%p]\n", this));
  1562     nsresult rv;
  1564     nsCOMPtr<nsIProtocolProxyService> pps =
  1565             do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
  1566     if (NS_FAILED(rv))
  1567         return rv;
  1569     nsCOMPtr<nsIProxyInfo> pi;
  1570     rv = pps->GetFailoverForProxy(mConnectionInfo->ProxyInfo(), mURI, mStatus,
  1571                                   getter_AddRefs(pi));
  1572     if (NS_FAILED(rv))
  1573         return rv;
  1575     // XXXbz so where does this codepath remove us from the loadgroup,
  1576     // exactly?
  1577     return AsyncDoReplaceWithProxy(pi);
  1580 void
  1581 nsHttpChannel::HandleAsyncRedirectChannelToHttps()
  1583     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
  1585     if (mSuspendCount) {
  1586         LOG(("Waiting until resume to do async redirect to https [this=%p]\n", this));
  1587         mCallOnResume = &nsHttpChannel::HandleAsyncRedirectChannelToHttps;
  1588         return;
  1591     nsresult rv = StartRedirectChannelToHttps();
  1592     if (NS_FAILED(rv))
  1593         ContinueAsyncRedirectChannelToURI(rv);
  1596 nsresult
  1597 nsHttpChannel::StartRedirectChannelToHttps()
  1599     nsresult rv = NS_OK;
  1600     LOG(("nsHttpChannel::HandleAsyncRedirectChannelToHttps() [STS]\n"));
  1602     nsCOMPtr<nsIURI> upgradedURI;
  1604     rv = mURI->Clone(getter_AddRefs(upgradedURI));
  1605     NS_ENSURE_SUCCESS(rv,rv);
  1607     upgradedURI->SetScheme(NS_LITERAL_CSTRING("https"));
  1609     int32_t oldPort = -1;
  1610     rv = mURI->GetPort(&oldPort);
  1611     if (NS_FAILED(rv)) return rv;
  1613     // Keep any nonstandard ports so only the scheme is changed.
  1614     // For example:
  1615     //  http://foo.com:80 -> https://foo.com:443
  1616     //  http://foo.com:81 -> https://foo.com:81
  1618     if (oldPort == 80 || oldPort == -1)
  1619         upgradedURI->SetPort(-1);
  1620     else
  1621         upgradedURI->SetPort(oldPort);
  1623     return StartRedirectChannelToURI(upgradedURI,
  1624                                      nsIChannelEventSink::REDIRECT_PERMANENT);
  1627 void
  1628 nsHttpChannel::HandleAsyncAPIRedirect()
  1630     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
  1631     NS_PRECONDITION(mAPIRedirectToURI, "How did that happen?");
  1633     if (mSuspendCount) {
  1634         LOG(("Waiting until resume to do async API redirect [this=%p]\n", this));
  1635         mCallOnResume = &nsHttpChannel::HandleAsyncAPIRedirect;
  1636         return;
  1639     nsresult rv = StartRedirectChannelToURI(mAPIRedirectToURI,
  1640                                             nsIChannelEventSink::REDIRECT_PERMANENT);
  1641     if (NS_FAILED(rv))
  1642         ContinueAsyncRedirectChannelToURI(rv);
  1644     return;
  1647 nsresult
  1648 nsHttpChannel::StartRedirectChannelToURI(nsIURI *upgradedURI, uint32_t flags)
  1650     nsresult rv = NS_OK;
  1651     LOG(("nsHttpChannel::StartRedirectChannelToURI()\n"));
  1653     nsCOMPtr<nsIChannel> newChannel;
  1655     nsCOMPtr<nsIIOService> ioService;
  1656     rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
  1657     NS_ENSURE_SUCCESS(rv, rv);
  1659     rv = ioService->NewChannelFromURI(upgradedURI, getter_AddRefs(newChannel));
  1660     NS_ENSURE_SUCCESS(rv, rv);
  1662     rv = SetupReplacementChannel(upgradedURI, newChannel, true);
  1663     NS_ENSURE_SUCCESS(rv, rv);
  1665     // Inform consumers about this fake redirect
  1666     mRedirectChannel = newChannel;
  1668     PushRedirectAsyncFunc(
  1669         &nsHttpChannel::ContinueAsyncRedirectChannelToURI);
  1670     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
  1672     if (NS_SUCCEEDED(rv))
  1673         rv = WaitForRedirectCallback();
  1675     if (NS_FAILED(rv)) {
  1676         AutoRedirectVetoNotifier notifier(this);
  1678         /* Remove the async call to ContinueAsyncRedirectChannelToURI().
  1679          * It is called directly by our callers upon return (to clean up
  1680          * the failed redirect). */
  1681         PopRedirectAsyncFunc(
  1682             &nsHttpChannel::ContinueAsyncRedirectChannelToURI);
  1685     return rv;
  1688 nsresult
  1689 nsHttpChannel::ContinueAsyncRedirectChannelToURI(nsresult rv)
  1691     if (NS_SUCCEEDED(rv))
  1692         rv = OpenRedirectChannel(rv);
  1694     if (NS_FAILED(rv)) {
  1695         // Fill the failure status here, the update to https had been vetoed
  1696         // but from the security reasons we have to discard the whole channel
  1697         // load.
  1698         mStatus = rv;
  1701     if (mLoadGroup)
  1702         mLoadGroup->RemoveRequest(this, nullptr, mStatus);
  1704     if (NS_FAILED(rv)) {
  1705         // We have to manually notify the listener because there is not any pump
  1706         // that would call our OnStart/StopRequest after resume from waiting for
  1707         // the redirect callback.
  1708         DoNotifyListener();
  1711     return rv;
  1714 nsresult
  1715 nsHttpChannel::OpenRedirectChannel(nsresult rv)
  1717     AutoRedirectVetoNotifier notifier(this);
  1719     // Make sure to do this _after_ calling OnChannelRedirect
  1720     mRedirectChannel->SetOriginalURI(mOriginalURI);
  1722     // And now, notify observers the deprecated way
  1723     nsCOMPtr<nsIHttpEventSink> httpEventSink;
  1724     GetCallback(httpEventSink);
  1725     if (httpEventSink) {
  1726         // NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8
  1727         // versions.
  1728         rv = httpEventSink->OnRedirect(this, mRedirectChannel);
  1729         if (NS_FAILED(rv)) {
  1730             return rv;
  1734     // open new channel
  1735     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
  1736     if (NS_FAILED(rv)) {
  1737         return rv;
  1740     mStatus = NS_BINDING_REDIRECTED;
  1742     notifier.RedirectSucceeded();
  1744     ReleaseListeners();
  1746     return NS_OK;
  1749 nsresult
  1750 nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi)
  1752     LOG(("nsHttpChannel::AsyncDoReplaceWithProxy [this=%p pi=%p]", this, pi));
  1753     nsresult rv;
  1755     nsCOMPtr<nsIChannel> newChannel;
  1756     rv = gHttpHandler->NewProxiedChannel(mURI, pi, mProxyResolveFlags,
  1757                                          mProxyURI, getter_AddRefs(newChannel));
  1758     if (NS_FAILED(rv))
  1759         return rv;
  1761     rv = SetupReplacementChannel(mURI, newChannel, true);
  1762     if (NS_FAILED(rv))
  1763         return rv;
  1765     // Inform consumers about this fake redirect
  1766     mRedirectChannel = newChannel;
  1767     uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL;
  1769     PushRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
  1770     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
  1772     if (NS_SUCCEEDED(rv))
  1773         rv = WaitForRedirectCallback();
  1775     if (NS_FAILED(rv)) {
  1776         AutoRedirectVetoNotifier notifier(this);
  1777         PopRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
  1780     return rv;
  1783 nsresult
  1784 nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv)
  1786     AutoRedirectVetoNotifier notifier(this);
  1788     if (NS_FAILED(rv))
  1789         return rv;
  1791     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
  1793     // Make sure to do this _after_ calling OnChannelRedirect
  1794     mRedirectChannel->SetOriginalURI(mOriginalURI);
  1796     // open new channel
  1797     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
  1798     if (NS_FAILED(rv))
  1799         return rv;
  1801     mStatus = NS_BINDING_REDIRECTED;
  1803     notifier.RedirectSucceeded();
  1805     ReleaseListeners();
  1807     return rv;
  1810 nsresult
  1811 nsHttpChannel::ResolveProxy()
  1813     LOG(("nsHttpChannel::ResolveProxy [this=%p]\n", this));
  1815     nsresult rv;
  1817     nsCOMPtr<nsIProtocolProxyService> pps =
  1818             do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
  1819     if (NS_FAILED(rv))
  1820         return rv;
  1822     // using the nsIProtocolProxyService2 allows a minor performance
  1823     // optimization, but if an add-on has only provided the original interface
  1824     // then it is ok to use that version.
  1825     nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps);
  1826     if (pps2) {
  1827         rv = pps2->AsyncResolve2(this, mProxyResolveFlags,
  1828                                  this, getter_AddRefs(mProxyRequest));
  1829     } else {
  1830         rv = pps->AsyncResolve(this, mProxyResolveFlags,
  1831                                this, getter_AddRefs(mProxyRequest));
  1834     return rv;
  1837 bool
  1838 nsHttpChannel::ResponseWouldVary(nsICacheEntry* entry) const
  1840     nsresult rv;
  1841     nsAutoCString buf, metaKey;
  1842     mCachedResponseHead->GetHeader(nsHttp::Vary, buf);
  1843     if (!buf.IsEmpty()) {
  1844         NS_NAMED_LITERAL_CSTRING(prefix, "request-");
  1846         // enumerate the elements of the Vary header...
  1847         char *val = buf.BeginWriting(); // going to munge buf
  1848         char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
  1849         while (token) {
  1850             LOG(("nsHttpChannel::ResponseWouldVary [channel=%p] " \
  1851                  "processing %s\n",
  1852                  this, token));
  1853             //
  1854             // if "*", then assume response would vary.  technically speaking,
  1855             // "Vary: header, *" is not permitted, but we allow it anyways.
  1856             //
  1857             // We hash values of cookie-headers for the following reasons:
  1858             //
  1859             //   1- cookies can be very large in size
  1860             //
  1861             //   2- cookies may contain sensitive information.  (for parity with
  1862             //      out policy of not storing Set-cookie headers in the cache
  1863             //      meta data, we likewise do not want to store cookie headers
  1864             //      here.)
  1865             //
  1866             if (*token == '*')
  1867                 return true; // if we encounter this, just get out of here
  1869             // build cache meta data key...
  1870             metaKey = prefix + nsDependentCString(token);
  1872             // check the last value of the given request header to see if it has
  1873             // since changed.  if so, then indeed the cached response is invalid.
  1874             nsXPIDLCString lastVal;
  1875             entry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal));
  1876             LOG(("nsHttpChannel::ResponseWouldVary [channel=%p] "
  1877                      "stored value = \"%s\"\n",
  1878                  this, lastVal.get()));
  1880             // Look for value of "Cookie" in the request headers
  1881             nsHttpAtom atom = nsHttp::ResolveAtom(token);
  1882             const char *newVal = mRequestHead.PeekHeader(atom);
  1883             if (!lastVal.IsEmpty()) {
  1884                 // value for this header in cache, but no value in request
  1885                 if (!newVal)
  1886                     return true; // yes - response would vary
  1888                 // If this is a cookie-header, stored metadata is not
  1889                 // the value itself but the hash. So we also hash the
  1890                 // outgoing value here in order to compare the hashes
  1891                 nsAutoCString hash;
  1892                 if (atom == nsHttp::Cookie) {
  1893                     rv = Hash(newVal, hash);
  1894                     // If hash failed, be conservative (the cached hash
  1895                     // exists at this point) and claim response would vary
  1896                     if (NS_FAILED(rv))
  1897                         return true;
  1898                     newVal = hash.get();
  1900                     LOG(("nsHttpChannel::ResponseWouldVary [this=%p] " \
  1901                             "set-cookie value hashed to %s\n",
  1902                          this, newVal));
  1905                 if (strcmp(newVal, lastVal))
  1906                     return true; // yes, response would vary
  1908             } else if (newVal) { // old value is empty, but newVal is set
  1909                 return true;
  1912             // next token...
  1913             token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
  1916     return false;
  1919 // We need to have an implementation of this function just so that we can keep
  1920 // all references to mCallOnResume of type nsHttpChannel:  it's not OK in C++
  1921 // to set a member function ptr to  a base class function.
  1922 void
  1923 nsHttpChannel::HandleAsyncAbort()
  1925     HttpAsyncAborter<nsHttpChannel>::HandleAsyncAbort();
  1929 nsresult
  1930 nsHttpChannel::EnsureAssocReq()
  1932     // Confirm Assoc-Req response header on pipelined transactions
  1933     // per draft-nottingham-http-pipeline-01.txt
  1934     // of the form: GET http://blah.com/foo/bar?qv
  1935     // return NS_OK as long as we don't find a violation
  1936     // (i.e. no header is ok, as are malformed headers, as are
  1937     // transactions that have not been pipelined (unless those have been
  1938     // opted in via pragma))
  1940     if (!mResponseHead)
  1941         return NS_OK;
  1943     const char *assoc_val = mResponseHead->PeekHeader(nsHttp::Assoc_Req);
  1944     if (!assoc_val)
  1945         return NS_OK;
  1947     if (!mTransaction || !mURI)
  1948         return NS_OK;
  1950     if (!mTransaction->PipelinePosition()) {
  1951         // "Pragma: X-Verify-Assoc-Req" can be used to verify even non pipelined
  1952         // transactions. It is used by test harness.
  1954         const char *pragma_val = mResponseHead->PeekHeader(nsHttp::Pragma);
  1955         if (!pragma_val ||
  1956             !nsHttp::FindToken(pragma_val, "X-Verify-Assoc-Req",
  1957                                HTTP_HEADER_VALUE_SEPS))
  1958             return NS_OK;
  1961     char *method = net_FindCharNotInSet(assoc_val, HTTP_LWS);
  1962     if (!method)
  1963         return NS_OK;
  1965     bool equals;
  1966     char *endofmethod;
  1968     assoc_val = nullptr;
  1969     endofmethod = net_FindCharInSet(method, HTTP_LWS);
  1970     if (endofmethod)
  1971         assoc_val = net_FindCharNotInSet(endofmethod, HTTP_LWS);
  1972     if (!assoc_val)
  1973         return NS_OK;
  1975     // check the method
  1976     int32_t methodlen = strlen(mRequestHead.Method().get());
  1977     if ((methodlen != (endofmethod - method)) ||
  1978         PL_strncmp(method,
  1979                    mRequestHead.Method().get(),
  1980                    endofmethod - method)) {
  1981         LOG(("  Assoc-Req failure Method %s", method));
  1982         if (mConnectionInfo)
  1983             gHttpHandler->ConnMgr()->
  1984                 PipelineFeedbackInfo(mConnectionInfo,
  1985                                      nsHttpConnectionMgr::RedCorruptedContent,
  1986                                      nullptr, 0);
  1988         nsCOMPtr<nsIConsoleService> consoleService =
  1989             do_GetService(NS_CONSOLESERVICE_CONTRACTID);
  1990         if (consoleService) {
  1991             nsAutoString message
  1992                 (NS_LITERAL_STRING("Failed Assoc-Req. Received "));
  1993             AppendASCIItoUTF16(
  1994                 mResponseHead->PeekHeader(nsHttp::Assoc_Req),
  1995                 message);
  1996             message += NS_LITERAL_STRING(" expected method ");
  1997             AppendASCIItoUTF16(mRequestHead.Method().get(), message);
  1998             consoleService->LogStringMessage(message.get());
  2001         if (gHttpHandler->EnforceAssocReq())
  2002             return NS_ERROR_CORRUPTED_CONTENT;
  2003         return NS_OK;
  2006     // check the URL
  2007     nsCOMPtr<nsIURI> assoc_url;
  2008     if (NS_FAILED(NS_NewURI(getter_AddRefs(assoc_url), assoc_val)) ||
  2009         !assoc_url)
  2010         return NS_OK;
  2012     mURI->Equals(assoc_url, &equals);
  2013     if (!equals) {
  2014         LOG(("  Assoc-Req failure URL %s", assoc_val));
  2015         if (mConnectionInfo)
  2016             gHttpHandler->ConnMgr()->
  2017                 PipelineFeedbackInfo(mConnectionInfo,
  2018                                      nsHttpConnectionMgr::RedCorruptedContent,
  2019                                      nullptr, 0);
  2021         nsCOMPtr<nsIConsoleService> consoleService =
  2022             do_GetService(NS_CONSOLESERVICE_CONTRACTID);
  2023         if (consoleService) {
  2024             nsAutoString message
  2025                 (NS_LITERAL_STRING("Failed Assoc-Req. Received "));
  2026             AppendASCIItoUTF16(
  2027                 mResponseHead->PeekHeader(nsHttp::Assoc_Req),
  2028                 message);
  2029             message += NS_LITERAL_STRING(" expected URL ");
  2030             AppendASCIItoUTF16(mSpec.get(), message);
  2031             consoleService->LogStringMessage(message.get());
  2034         if (gHttpHandler->EnforceAssocReq())
  2035             return NS_ERROR_CORRUPTED_CONTENT;
  2037     return NS_OK;
  2040 //-----------------------------------------------------------------------------
  2041 // nsHttpChannel <byte-range>
  2042 //-----------------------------------------------------------------------------
  2044 bool
  2045 nsHttpChannel::IsResumable(int64_t partialLen, int64_t contentLength,
  2046                            bool ignoreMissingPartialLen) const
  2048     bool hasContentEncoding =
  2049         mCachedResponseHead->PeekHeader(nsHttp::Content_Encoding)
  2050         != nullptr;
  2052     return (partialLen < contentLength) &&
  2053            (partialLen > 0 || ignoreMissingPartialLen) &&
  2054            !hasContentEncoding &&
  2055            mCachedResponseHead->IsResumable() &&
  2056            !mCustomConditionalRequest &&
  2057            !mCachedResponseHead->NoStore();
  2060 nsresult
  2061 nsHttpChannel::MaybeSetupByteRangeRequest(int64_t partialLen, int64_t contentLength)
  2063     // Be pesimistic
  2064     mIsPartialRequest = false;
  2066     if (!IsResumable(partialLen, contentLength))
  2067       return NS_ERROR_NOT_RESUMABLE;
  2069     // looks like a partial entry we can reuse; add If-Range
  2070     // and Range headers.
  2071     nsresult rv = SetupByteRangeRequest(partialLen);
  2072     if (NS_FAILED(rv)) {
  2073         // Make the request unconditional again.
  2074         mRequestHead.ClearHeader(nsHttp::Range);
  2075         mRequestHead.ClearHeader(nsHttp::If_Range);
  2078     return rv;
  2081 nsresult
  2082 nsHttpChannel::SetupByteRangeRequest(int64_t partialLen)
  2084     // cached content has been found to be partial, add necessary request
  2085     // headers to complete cache entry.
  2087     // use strongest validator available...
  2088     const char *val = mCachedResponseHead->PeekHeader(nsHttp::ETag);
  2089     if (!val)
  2090         val = mCachedResponseHead->PeekHeader(nsHttp::Last_Modified);
  2091     if (!val) {
  2092         // if we hit this code it means mCachedResponseHead->IsResumable() is
  2093         // either broken or not being called.
  2094         NS_NOTREACHED("no cache validator");
  2095         mIsPartialRequest = false;
  2096         return NS_ERROR_FAILURE;
  2099     char buf[64];
  2100     PR_snprintf(buf, sizeof(buf), "bytes=%lld-", partialLen);
  2102     mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(buf));
  2103     mRequestHead.SetHeader(nsHttp::If_Range, nsDependentCString(val));
  2104     mIsPartialRequest = true;
  2106     return NS_OK;
  2109 nsresult
  2110 nsHttpChannel::ProcessPartialContent()
  2112     // ok, we've just received a 206
  2113     //
  2114     // we need to stream whatever data is in the cache out first, and then
  2115     // pick up whatever data is on the wire, writing it into the cache.
  2117     LOG(("nsHttpChannel::ProcessPartialContent [this=%p]\n", this));
  2119     NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
  2120     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
  2122     // Make sure to clear bogus content-encodings before looking at the header
  2123     ClearBogusContentEncodingIfNeeded();
  2125     // Check if the content-encoding we now got is different from the one we
  2126     // got before
  2127     if (PL_strcasecmp(mResponseHead->PeekHeader(nsHttp::Content_Encoding),
  2128                       mCachedResponseHead->PeekHeader(nsHttp::Content_Encoding))
  2129                       != 0) {
  2130         Cancel(NS_ERROR_INVALID_CONTENT_ENCODING);
  2131         return CallOnStartRequest();
  2134     nsresult rv;
  2136     int64_t cachedContentLength = mCachedResponseHead->ContentLength();
  2137     int64_t entitySize = mResponseHead->TotalEntitySize();
  2139     LOG(("nsHttpChannel::ProcessPartialContent [this=%p trans=%p] "
  2140          "original content-length %lld, entity-size %lld, content-range %s\n",
  2141          this, mTransaction.get(), cachedContentLength, entitySize,
  2142          mResponseHead->PeekHeader(nsHttp::Content_Range)));
  2144     if ((entitySize >= 0) && (cachedContentLength >= 0) &&
  2145         (entitySize != cachedContentLength)) {
  2146         LOG(("nsHttpChannel::ProcessPartialContent [this=%p] "
  2147              "206 has different total entity size than the content length "
  2148              "of the original partially cached entity.\n", this));
  2150         mCacheEntry->AsyncDoom(nullptr);
  2151         Cancel(NS_ERROR_CORRUPTED_CONTENT);
  2152         return CallOnStartRequest();
  2155     if (mConcurentCacheAccess) {
  2156         // We started to read cached data sooner than its write has been done.
  2157         // But the concurrent write has not finished completely, so we had to
  2158         // do a range request.  Now let the content coming from the network
  2159         // be presented to consumers and also stored to the cache entry.
  2161         rv = InstallCacheListener(mLogicalOffset);
  2162         if (NS_FAILED(rv)) return rv;
  2164         if (mOfflineCacheEntry) {
  2165             rv = InstallOfflineCacheListener(mLogicalOffset);
  2166             if (NS_FAILED(rv)) return rv;
  2169         // merge any new headers with the cached response headers
  2170         rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
  2171         if (NS_FAILED(rv)) return rv;
  2173         // update the cached response head
  2174         nsAutoCString head;
  2175         mCachedResponseHead->Flatten(head, true);
  2176         rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
  2177         if (NS_FAILED(rv)) return rv;
  2179         UpdateInhibitPersistentCachingFlag();
  2181         rv = UpdateExpirationTime();
  2182         if (NS_FAILED(rv)) return rv;
  2184         mCachedContentIsPartial = false;
  2186         // notify observers interested in looking at a response that has been
  2187         // merged with any cached headers (http-on-examine-merged-response).
  2188         gHttpHandler->OnExamineMergedResponse(this);
  2190     else {
  2191         // suspend the current transaction
  2192         rv = mTransactionPump->Suspend();
  2193         if (NS_FAILED(rv)) return rv;
  2195         // merge any new headers with the cached response headers
  2196         rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
  2197         if (NS_FAILED(rv)) return rv;
  2199         // update the cached response head
  2200         nsAutoCString head;
  2201         mCachedResponseHead->Flatten(head, true);
  2202         rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
  2203         if (NS_FAILED(rv)) return rv;
  2205         // make the cached response be the current response
  2206         mResponseHead = mCachedResponseHead;
  2208         UpdateInhibitPersistentCachingFlag();
  2210         rv = UpdateExpirationTime();
  2211         if (NS_FAILED(rv)) return rv;
  2213         // notify observers interested in looking at a response that has been
  2214         // merged with any cached headers (http-on-examine-merged-response).
  2215         gHttpHandler->OnExamineMergedResponse(this);
  2217         // the cached content is valid, although incomplete.
  2218         mCachedContentIsValid = true;
  2219         rv = ReadFromCache(false);
  2222     return rv;
  2225 nsresult
  2226 nsHttpChannel::OnDoneReadingPartialCacheEntry(bool *streamDone)
  2228     nsresult rv;
  2230     LOG(("nsHttpChannel::OnDoneReadingPartialCacheEntry [this=%p]", this));
  2232     // by default, assume we would have streamed all data or failed...
  2233     *streamDone = true;
  2235     // setup cache listener to append to cache entry
  2236     int64_t size;
  2237     rv = mCacheEntry->GetDataSize(&size);
  2238     if (NS_FAILED(rv)) return rv;
  2240     rv = InstallCacheListener(size);
  2241     if (NS_FAILED(rv)) return rv;
  2243     // Entry is valid, do it now, after the output stream has been opened,
  2244     // otherwise when done earlier, pending readers would consider the cache
  2245     // entry still as partial (CacheEntry::GetDataSize would return the partial
  2246     // data size) and consumers would do the conditional request again.
  2247     rv = mCacheEntry->SetValid();
  2248     if (NS_FAILED(rv)) return rv;
  2250     // need to track the logical offset of the data being sent to our listener
  2251     mLogicalOffset = size;
  2253     // we're now completing the cached content, so we can clear this flag.
  2254     // this puts us in the state of a regular download.
  2255     mCachedContentIsPartial = false;
  2257     // resume the transaction if it exists, otherwise the pipe contained the
  2258     // remaining part of the document and we've now streamed all of the data.
  2259     if (mTransactionPump) {
  2260         rv = mTransactionPump->Resume();
  2261         if (NS_SUCCEEDED(rv))
  2262             *streamDone = false;
  2264     else
  2265         NS_NOTREACHED("no transaction");
  2266     return rv;
  2269 //-----------------------------------------------------------------------------
  2270 // nsHttpChannel <cache>
  2271 //-----------------------------------------------------------------------------
  2273 nsresult
  2274 nsHttpChannel::ProcessNotModified()
  2276     nsresult rv;
  2278     LOG(("nsHttpChannel::ProcessNotModified [this=%p]\n", this));
  2280     if (mCustomConditionalRequest) {
  2281         LOG(("Bypassing ProcessNotModified due to custom conditional headers"));
  2282         return NS_ERROR_FAILURE;
  2285     if (!mDidReval) {
  2286         LOG(("Server returned a 304 response even though we did not send a "
  2287              "conditional request"));
  2288         return NS_ERROR_FAILURE;
  2291     MOZ_ASSERT(mCachedResponseHead);
  2292     MOZ_ASSERT(mCacheEntry);
  2293     NS_ENSURE_TRUE(mCachedResponseHead && mCacheEntry, NS_ERROR_UNEXPECTED);
  2295     // If the 304 response contains a Last-Modified different than the
  2296     // one in our cache that is pretty suspicious and is, in at least the
  2297     // case of bug 716840, a sign of the server having previously corrupted
  2298     // our cache with a bad response. Take the minor step here of just dooming
  2299     // that cache entry so there is a fighting chance of getting things on the
  2300     // right track as well as disabling pipelining for that host.
  2302     nsAutoCString lastModifiedCached;
  2303     nsAutoCString lastModified304;
  2305     rv = mCachedResponseHead->GetHeader(nsHttp::Last_Modified,
  2306                                         lastModifiedCached);
  2307     if (NS_SUCCEEDED(rv)) {
  2308         rv = mResponseHead->GetHeader(nsHttp::Last_Modified,
  2309                                       lastModified304);
  2312     if (NS_SUCCEEDED(rv) && !lastModified304.Equals(lastModifiedCached)) {
  2313         LOG(("Cache Entry and 304 Last-Modified Headers Do Not Match "
  2314              "[%s] and [%s]\n",
  2315              lastModifiedCached.get(), lastModified304.get()));
  2317         mCacheEntry->AsyncDoom(nullptr);
  2318         if (mConnectionInfo)
  2319             gHttpHandler->ConnMgr()->
  2320                 PipelineFeedbackInfo(mConnectionInfo,
  2321                                      nsHttpConnectionMgr::RedCorruptedContent,
  2322                                      nullptr, 0);
  2323         Telemetry::Accumulate(Telemetry::CACHE_LM_INCONSISTENT, true);
  2326     // merge any new headers with the cached response headers
  2327     rv = mCachedResponseHead->UpdateHeaders(mResponseHead->Headers());
  2328     if (NS_FAILED(rv)) return rv;
  2330     // update the cached response head
  2331     nsAutoCString head;
  2332     mCachedResponseHead->Flatten(head, true);
  2333     rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
  2334     if (NS_FAILED(rv)) return rv;
  2336     // make the cached response be the current response
  2337     mResponseHead = mCachedResponseHead;
  2339     UpdateInhibitPersistentCachingFlag();
  2341     rv = UpdateExpirationTime();
  2342     if (NS_FAILED(rv)) return rv;
  2344     rv = AddCacheEntryHeaders(mCacheEntry);
  2345     if (NS_FAILED(rv)) return rv;
  2347     // notify observers interested in looking at a reponse that has been
  2348     // merged with any cached headers
  2349     gHttpHandler->OnExamineMergedResponse(this);
  2351     mCachedContentIsValid = true;
  2353     // Tell other consumers the entry is OK to use
  2354     rv = mCacheEntry->SetValid();
  2355     if (NS_FAILED(rv)) return rv;
  2357     rv = ReadFromCache(false);
  2358     if (NS_FAILED(rv)) return rv;
  2360     mTransactionReplaced = true;
  2361     return NS_OK;
  2364 nsresult
  2365 nsHttpChannel::ProcessFallback(bool *waitingForRedirectCallback)
  2367     LOG(("nsHttpChannel::ProcessFallback [this=%p]\n", this));
  2368     nsresult rv;
  2370     *waitingForRedirectCallback = false;
  2371     mFallingBack = false;
  2373     // At this point a load has failed (either due to network problems
  2374     // or an error returned on the server).  Perform an application
  2375     // cache fallback if we have a URI to fall back to.
  2376     if (!mApplicationCache || mFallbackKey.IsEmpty() || mFallbackChannel) {
  2377         LOG(("  choosing not to fallback [%p,%s,%d]",
  2378              mApplicationCache.get(), mFallbackKey.get(), mFallbackChannel));
  2379         return NS_OK;
  2382     // Make sure the fallback entry hasn't been marked as a foreign
  2383     // entry.
  2384     uint32_t fallbackEntryType;
  2385     rv = mApplicationCache->GetTypes(mFallbackKey, &fallbackEntryType);
  2386     NS_ENSURE_SUCCESS(rv, rv);
  2388     if (fallbackEntryType & nsIApplicationCache::ITEM_FOREIGN) {
  2389         // This cache points to a fallback that refers to a different
  2390         // manifest.  Refuse to fall back.
  2391         return NS_OK;
  2394     MOZ_ASSERT(fallbackEntryType & nsIApplicationCache::ITEM_FALLBACK,
  2395                "Fallback entry not marked correctly!");
  2397     // Kill any offline cache entry, and disable offline caching for the
  2398     // fallback.
  2399     if (mOfflineCacheEntry) {
  2400         mOfflineCacheEntry->AsyncDoom(nullptr);
  2401         mOfflineCacheEntry = nullptr;
  2404     mApplicationCacheForWrite = nullptr;
  2405     mOfflineCacheEntry = nullptr;
  2407     // Close the current cache entry.
  2408     CloseCacheEntry(true);
  2410     // Create a new channel to load the fallback entry.
  2411     nsRefPtr<nsIChannel> newChannel;
  2412     rv = gHttpHandler->NewChannel(mURI, getter_AddRefs(newChannel));
  2413     NS_ENSURE_SUCCESS(rv, rv);
  2415     rv = SetupReplacementChannel(mURI, newChannel, true);
  2416     NS_ENSURE_SUCCESS(rv, rv);
  2418     // Make sure the new channel loads from the fallback key.
  2419     nsCOMPtr<nsIHttpChannelInternal> httpInternal =
  2420         do_QueryInterface(newChannel, &rv);
  2421     NS_ENSURE_SUCCESS(rv, rv);
  2423     rv = httpInternal->SetupFallbackChannel(mFallbackKey.get());
  2424     NS_ENSURE_SUCCESS(rv, rv);
  2426     // ... and fallbacks should only load from the cache.
  2427     uint32_t newLoadFlags = mLoadFlags | LOAD_REPLACE | LOAD_ONLY_FROM_CACHE;
  2428     rv = newChannel->SetLoadFlags(newLoadFlags);
  2430     // Inform consumers about this fake redirect
  2431     mRedirectChannel = newChannel;
  2432     uint32_t redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL;
  2434     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
  2435     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
  2437     if (NS_SUCCEEDED(rv))
  2438         rv = WaitForRedirectCallback();
  2440     if (NS_FAILED(rv)) {
  2441         AutoRedirectVetoNotifier notifier(this);
  2442         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
  2443         return rv;
  2446     // Indicate we are now waiting for the asynchronous redirect callback
  2447     // if all went OK.
  2448     *waitingForRedirectCallback = true;
  2449     return NS_OK;
  2452 nsresult
  2453 nsHttpChannel::ContinueProcessFallback(nsresult rv)
  2455     AutoRedirectVetoNotifier notifier(this);
  2457     if (NS_FAILED(rv))
  2458         return rv;
  2460     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
  2462     // Make sure to do this _after_ calling OnChannelRedirect
  2463     mRedirectChannel->SetOriginalURI(mOriginalURI);
  2465     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
  2466     if (NS_FAILED(rv))
  2467         return rv;
  2469     if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
  2470         Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,
  2471                               true);
  2474     // close down this channel
  2475     Cancel(NS_BINDING_REDIRECTED);
  2477     notifier.RedirectSucceeded();
  2479     ReleaseListeners();
  2481     mFallingBack = true;
  2483     return NS_OK;
  2486 // Determines if a request is a byte range request for a subrange,
  2487 // i.e. is a byte range request, but not a 0- byte range request.
  2488 static bool
  2489 IsSubRangeRequest(nsHttpRequestHead &aRequestHead)
  2491     if (!aRequestHead.PeekHeader(nsHttp::Range))
  2492         return false;
  2493     nsAutoCString byteRange;
  2494     aRequestHead.GetHeader(nsHttp::Range, byteRange);
  2495     return !byteRange.EqualsLiteral("bytes=0-");
  2498 nsresult
  2499 nsHttpChannel::OpenCacheEntry(bool usingSSL)
  2501     MOZ_EVENT_TRACER_EXEC(this, "net::http::OpenCacheEntry");
  2503     // Handle correctly mCacheEntriesToWaitFor
  2504     AutoCacheWaitFlags waitFlags(this);
  2506     // Drop this flag here
  2507     mConcurentCacheAccess = 0;
  2509     nsresult rv;
  2511     mLoadedFromApplicationCache = false;
  2512     mHasQueryString = HasQueryString(mRequestHead.ParsedMethod(), mURI);
  2514     LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
  2516     // make sure we're not abusing this function
  2517     NS_PRECONDITION(!mCacheEntry, "cache entry already open");
  2519     nsAutoCString cacheKey;
  2521     if (mRequestHead.IsPost()) {
  2522         // If the post id is already set then this is an attempt to replay
  2523         // a post transaction via the cache.  Otherwise, we need a unique
  2524         // post id for this transaction.
  2525         if (mPostID == 0)
  2526             mPostID = gHttpHandler->GenerateUniqueID();
  2528     else if (!mRequestHead.IsGet() && !mRequestHead.IsHead()) {
  2529         // don't use the cache for other types of requests
  2530         return NS_OK;
  2533     if (mResuming) {
  2534         // We don't support caching for requests initiated
  2535         // via nsIResumableChannel.
  2536         return NS_OK;
  2539     // Don't cache byte range requests which are subranges, only cache 0-
  2540     // byte range requests.
  2541     if (IsSubRangeRequest(mRequestHead))
  2542         return NS_OK;
  2544     // Pick up an application cache from the notification
  2545     // callbacks if available
  2546     if (!mApplicationCache && mInheritApplicationCache) {
  2547         nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
  2548         GetCallback(appCacheContainer);
  2550         if (appCacheContainer) {
  2551             appCacheContainer->GetApplicationCache(getter_AddRefs(mApplicationCache));
  2555     nsCOMPtr<nsICacheStorageService> cacheStorageService =
  2556         do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
  2557     NS_ENSURE_SUCCESS(rv, rv);
  2559     nsRefPtr<LoadContextInfo> info = GetLoadContextInfo(this);
  2560     nsCOMPtr<nsICacheStorage> cacheStorage;
  2561     nsCOMPtr<nsIURI> openURI;
  2563     /* Obtain optional third party isolation domain */
  2564     nsAutoCString cacheDomain;
  2565     nsCOMPtr<nsIURI> firstPartyIsolationURI;
  2566     nsCOMPtr<mozIThirdPartyUtil> thirdPartySvc
  2567          = do_GetService(THIRDPARTYUTIL_CONTRACTID);
  2568     rv = thirdPartySvc->GetFirstPartyIsolationURI(this, nullptr,
  2569                                            getter_AddRefs(firstPartyIsolationURI));
  2570     if (NS_SUCCEEDED(rv) && firstPartyIsolationURI) {
  2571         thirdPartySvc->GetFirstPartyHostForIsolation(firstPartyIsolationURI,
  2572                 cacheDomain);
  2575     if (!mFallbackKey.IsEmpty() && mFallbackChannel) {
  2576         // This is a fallback channel, open fallback URI instead
  2577         rv = NS_NewURI(getter_AddRefs(openURI), mFallbackKey);
  2578         NS_ENSURE_SUCCESS(rv, rv);
  2580     else {
  2581         openURI = mURI;
  2584     uint32_t cacheEntryOpenFlags;
  2585     bool offline = gIOService->IsOffline();
  2586     if (offline || (mLoadFlags & INHIBIT_CACHING)) {
  2587         if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline) {
  2588             goto bypassCacheEntryOpen;
  2590         cacheEntryOpenFlags = nsICacheStorage::OPEN_READONLY;
  2591         mCacheEntryIsReadOnly = true;
  2593     else if (BYPASS_LOCAL_CACHE(mLoadFlags) && !mApplicationCache) {
  2594         cacheEntryOpenFlags = nsICacheStorage::OPEN_TRUNCATE;
  2596     else {
  2597         cacheEntryOpenFlags = nsICacheStorage::OPEN_NORMALLY
  2598                             | nsICacheStorage::CHECK_MULTITHREADED;
  2601     if (mApplicationCache) {
  2602         rv = cacheStorageService->AppCacheStorage(info, 
  2603             mApplicationCache,
  2604             getter_AddRefs(cacheStorage));
  2606     else if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) {
  2607         rv = cacheStorageService->MemoryCacheStorage(info, // ? choose app cache as well...
  2608             getter_AddRefs(cacheStorage));
  2610     else {
  2611         rv = cacheStorageService->DiskCacheStorage(info,
  2612             mChooseApplicationCache || (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE),
  2613             getter_AddRefs(cacheStorage));
  2615     NS_ENSURE_SUCCESS(rv, rv);
  2617     // Don't consider mLoadUnblocked here, since it's not indication of a demand
  2618     // to load prioritly. It's mostly used to load XHR requests, but those should
  2619     // not be considered as influencing the page load performance.
  2620     if (mLoadAsBlocking || (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI))
  2621         cacheEntryOpenFlags |= nsICacheStorage::OPEN_PRIORITY;
  2623     // Only for backward compatibility with the old cache back end.
  2624     // When removed, remove the flags and related code snippets.
  2625     if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY)
  2626         cacheEntryOpenFlags |= nsICacheStorage::OPEN_BYPASS_IF_BUSY;
  2628     rv = cacheStorage->AsyncOpenURI(
  2629         openURI, nsPrintfCString("%s@%d", cacheDomain.get(), mPostID),
  2630         cacheEntryOpenFlags, this);
  2631     NS_ENSURE_SUCCESS(rv, rv);
  2633     waitFlags.Keep(WAIT_FOR_CACHE_ENTRY);
  2635 bypassCacheEntryOpen:
  2636     if (!mApplicationCacheForWrite)
  2637         return NS_OK;
  2639     // If there is an app cache to write to, open the entry right now in parallel.
  2641     // make sure we're not abusing this function
  2642     NS_PRECONDITION(!mOfflineCacheEntry, "cache entry already open");
  2644     if (offline) {
  2645         // only put things in the offline cache while online
  2646         return NS_OK;
  2649     if (mLoadFlags & INHIBIT_CACHING) {
  2650         // respect demand not to cache
  2651         return NS_OK;
  2654     if (!mRequestHead.IsGet()) {
  2655         // only cache complete documents offline
  2656         return NS_OK;
  2659     rv = cacheStorageService->AppCacheStorage(info, mApplicationCacheForWrite,
  2660                                               getter_AddRefs(cacheStorage));
  2661     NS_ENSURE_SUCCESS(rv, rv);
  2663     rv = cacheStorage->AsyncOpenURI(
  2664       mURI, cacheDomain, nsICacheStorage::OPEN_TRUNCATE, this);
  2665     NS_ENSURE_SUCCESS(rv, rv);
  2667     waitFlags.Keep(WAIT_FOR_OFFLINE_CACHE_ENTRY);
  2669     return NS_OK;
  2672 nsresult
  2673 nsHttpChannel::CheckPartial(nsICacheEntry* aEntry, int64_t *aSize, int64_t *aContentLength)
  2675     nsresult rv;
  2677     rv = aEntry->GetDataSize(aSize);
  2679     if (NS_ERROR_IN_PROGRESS == rv) {
  2680         *aSize = -1;
  2681         rv = NS_OK;
  2684     NS_ENSURE_SUCCESS(rv, rv);
  2686     nsHttpResponseHead* responseHead = mCachedResponseHead
  2687         ? mCachedResponseHead
  2688         : mResponseHead;
  2690     if (!responseHead)
  2691         return NS_ERROR_UNEXPECTED;
  2693     *aContentLength = responseHead->ContentLength();
  2695     return NS_OK;
  2698 NS_IMETHODIMP
  2699 nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appCache,
  2700                                  uint32_t* aResult)
  2702     nsresult rv = NS_OK;
  2704     LOG(("nsHttpChannel::OnCacheEntryCheck enter [channel=%p entry=%p]",
  2705         this, entry));
  2707     // Remember the request is a custom conditional request so that we can
  2708     // process any 304 response correctly.
  2709     mCustomConditionalRequest =
  2710         mRequestHead.PeekHeader(nsHttp::If_Modified_Since) ||
  2711         mRequestHead.PeekHeader(nsHttp::If_None_Match) ||
  2712         mRequestHead.PeekHeader(nsHttp::If_Unmodified_Since) ||
  2713         mRequestHead.PeekHeader(nsHttp::If_Match) ||
  2714         mRequestHead.PeekHeader(nsHttp::If_Range);
  2716     // Be pessimistic: assume the cache entry has no useful data.
  2717     *aResult = ENTRY_WANTED;
  2718     mCachedContentIsValid = false;
  2720     nsXPIDLCString buf;
  2722     // Get the method that was used to generate the cached response
  2723     rv = entry->GetMetaDataElement("request-method", getter_Copies(buf));
  2724     NS_ENSURE_SUCCESS(rv, rv);
  2726     bool methodWasHead = buf.EqualsLiteral("HEAD");
  2727     bool methodWasGet = buf.EqualsLiteral("GET");
  2729     if (methodWasHead) {
  2730         // The cached response does not contain an entity.  We can only reuse
  2731         // the response if the current request is also HEAD.
  2732         if (!mRequestHead.IsHead()) {
  2733             return NS_OK;
  2736     buf.Adopt(0);
  2738     // We'll need this value in later computations...
  2739     uint32_t lastModifiedTime;
  2740     rv = entry->GetLastModified(&lastModifiedTime);
  2741     NS_ENSURE_SUCCESS(rv, rv);
  2743     // Determine if this is the first time that this cache entry
  2744     // has been accessed during this session.
  2745     bool fromPreviousSession =
  2746             (gHttpHandler->SessionStartTime() > lastModifiedTime);
  2748     // Get the cached HTTP response headers
  2749     rv = entry->GetMetaDataElement("response-head", getter_Copies(buf));
  2750     NS_ENSURE_SUCCESS(rv, rv);
  2752     // Parse the cached HTTP response headers
  2753     mCachedResponseHead = new nsHttpResponseHead();
  2754     rv = mCachedResponseHead->Parse((char *) buf.get());
  2755     NS_ENSURE_SUCCESS(rv, rv);
  2756     buf.Adopt(0);
  2758     bool isCachedRedirect = WillRedirect(mCachedResponseHead);
  2760     // Do not return 304 responses from the cache, and also do not return
  2761     // any other non-redirect 3xx responses from the cache (see bug 759043).
  2762     NS_ENSURE_TRUE((mCachedResponseHead->Status() / 100 != 3) ||
  2763                    isCachedRedirect, NS_ERROR_ABORT);
  2765     // Don't bother to validate items that are read-only,
  2766     // unless they are read-only because of INHIBIT_CACHING or because
  2767     // we're updating the offline cache.
  2768     // Don't bother to validate if this is a fallback entry.
  2769     if (!mApplicationCacheForWrite &&
  2770         (appCache ||
  2771          (mCacheEntryIsReadOnly && !(mLoadFlags & nsIRequest::INHIBIT_CACHING)) ||
  2772          mFallbackChannel)) {
  2773         rv = OpenCacheInputStream(entry, true);
  2774         if (NS_SUCCEEDED(rv)) {
  2775             mCachedContentIsValid = true;
  2776             entry->MaybeMarkValid();
  2778         return rv;
  2781     bool wantCompleteEntry = false;
  2783     if (!methodWasHead && !isCachedRedirect) {
  2784         // If the cached content-length is set and it does not match the data
  2785         // size of the cached content, then the cached response is partial...
  2786         // either we need to issue a byte range request or we need to refetch
  2787         // the entire document.
  2788         //
  2789         // We exclude redirects from this check because we (usually) strip the
  2790         // entity when we store the cache entry, and even if we didn't, we
  2791         // always ignore a cached redirect's entity anyway. See bug 759043.
  2792         int64_t size, contentLength;
  2793         rv = CheckPartial(entry, &size, &contentLength);
  2794         NS_ENSURE_SUCCESS(rv,rv);
  2796         if (size == int64_t(-1)) {
  2797             LOG(("  write is in progress"));
  2798             if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
  2799                 LOG(("  not interested in the entry, "
  2800                      "LOAD_BYPASS_LOCAL_CACHE_IF_BUSY specified"));
  2802                 *aResult = ENTRY_NOT_WANTED;
  2803                 return NS_OK;
  2806             // Ignore !(size > 0) from the resumability condition
  2807             if (!IsResumable(size, contentLength, true)) {
  2808                 LOG(("  wait for entry completion, "
  2809                      "response is not resumable"));
  2811                 wantCompleteEntry = true;
  2813             else {
  2814                 mConcurentCacheAccess = 1;
  2817         else if (contentLength != int64_t(-1) && contentLength != size) {
  2818             LOG(("Cached data size does not match the Content-Length header "
  2819                  "[content-length=%lld size=%lld]\n", contentLength, size));
  2821             rv = MaybeSetupByteRangeRequest(size, contentLength);
  2822             mCachedContentIsPartial = NS_SUCCEEDED(rv) && mIsPartialRequest;
  2823             if (mCachedContentIsPartial) {
  2824                 rv = OpenCacheInputStream(entry, false);
  2825                 *aResult = ENTRY_NEEDS_REVALIDATION;
  2827             return rv;
  2831     bool usingSSL = false;
  2832     rv = mURI->SchemeIs("https", &usingSSL);
  2833     NS_ENSURE_SUCCESS(rv,rv);
  2835     bool doValidation = false;
  2836     bool canAddImsHeader = true;
  2838     // Cached entry is not the entity we request (see bug #633743)
  2839     if (ResponseWouldVary(entry)) {
  2840         LOG(("Validating based on Vary headers returning TRUE\n"));
  2841         canAddImsHeader = false;
  2842         doValidation = true;
  2844     // If the LOAD_FROM_CACHE flag is set, any cached data can simply be used
  2845     else if (mLoadFlags & nsIRequest::LOAD_FROM_CACHE) {
  2846         LOG(("NOT validating based on LOAD_FROM_CACHE load flag\n"));
  2847         doValidation = false;
  2849     // If the VALIDATE_ALWAYS flag is set, any cached data won't be used until
  2850     // it's revalidated with the server.
  2851     else if (mLoadFlags & nsIRequest::VALIDATE_ALWAYS) {
  2852         LOG(("Validating based on VALIDATE_ALWAYS load flag\n"));
  2853         doValidation = true;
  2855     // Even if the VALIDATE_NEVER flag is set, there are still some cases in
  2856     // which we must validate the cached response with the server.
  2857     else if (mLoadFlags & nsIRequest::VALIDATE_NEVER) {
  2858         LOG(("VALIDATE_NEVER set\n"));
  2859         // if no-store or if no-cache and ssl, validate cached response (see
  2860         // bug 112564 for an explanation of this logic)
  2861         if (mCachedResponseHead->NoStore() ||
  2862            (mCachedResponseHead->NoCache() && usingSSL)) {
  2863             LOG(("Validating based on (no-store || (no-cache && ssl)) logic\n"));
  2864             doValidation = true;
  2866         else {
  2867             LOG(("NOT validating based on VALIDATE_NEVER load flag\n"));
  2868             doValidation = false;
  2871     // check if validation is strictly required...
  2872     else if (mCachedResponseHead->MustValidate()) {
  2873         LOG(("Validating based on MustValidate() returning TRUE\n"));
  2874         doValidation = true;
  2876     else if (MustValidateBasedOnQueryUrl()) {
  2877         LOG(("Validating based on RFC 2616 section 13.9 "
  2878              "(query-url w/o explicit expiration-time)\n"));
  2879         doValidation = true;
  2881     // Check if the cache entry has expired...
  2882     else {
  2883         uint32_t time = 0; // a temporary variable for storing time values...
  2885         rv = entry->GetExpirationTime(&time);
  2886         NS_ENSURE_SUCCESS(rv, rv);
  2888         LOG(("  NowInSeconds()=%u, time=%u", NowInSeconds(), time));
  2889         if (NowInSeconds() <= time)
  2890             doValidation = false;
  2891         else if (mCachedResponseHead->MustValidateIfExpired())
  2892             doValidation = true;
  2893         else if (mLoadFlags & nsIRequest::VALIDATE_ONCE_PER_SESSION) {
  2894             // If the cached response does not include expiration infor-
  2895             // mation, then we must validate the response, despite whether
  2896             // or not this is the first access this session.  This behavior
  2897             // is consistent with existing browsers and is generally expected
  2898             // by web authors.
  2899             rv = mCachedResponseHead->ComputeFreshnessLifetime(&time);
  2900             NS_ENSURE_SUCCESS(rv, rv);
  2902             if (time == 0)
  2903                 doValidation = true;
  2904             else
  2905                 doValidation = fromPreviousSession;
  2907         else
  2908             doValidation = true;
  2910         LOG(("%salidating based on expiration time\n", doValidation ? "V" : "Not v"));
  2913     if (!doValidation && mRequestHead.PeekHeader(nsHttp::If_Match) &&
  2914         (methodWasGet || methodWasHead)) {
  2915         const char *requestedETag, *cachedETag;
  2916         cachedETag = mCachedResponseHead->PeekHeader(nsHttp::ETag);
  2917         requestedETag = mRequestHead.PeekHeader(nsHttp::If_Match);
  2918         if (cachedETag && (!strncmp(cachedETag, "W/", 2) ||
  2919             strcmp(requestedETag, cachedETag))) {
  2920             // User has defined If-Match header, if the cached entry is not
  2921             // matching the provided header value or the cached ETag is weak,
  2922             // force validation.
  2923             doValidation = true;
  2927     if (!doValidation) {
  2928         //
  2929         // Check the authorization headers used to generate the cache entry.
  2930         // We must validate the cache entry if:
  2931         //
  2932         // 1) the cache entry was generated prior to this session w/
  2933         //    credentials (see bug 103402).
  2934         // 2) the cache entry was generated w/o credentials, but would now
  2935         //    require credentials (see bug 96705).
  2936         //
  2937         // NOTE: this does not apply to proxy authentication.
  2938         //
  2939         entry->GetMetaDataElement("auth", getter_Copies(buf));
  2940         doValidation =
  2941             (fromPreviousSession && !buf.IsEmpty()) ||
  2942             (buf.IsEmpty() && mRequestHead.PeekHeader(nsHttp::Authorization));
  2945     // Bug #561276: We maintain a chain of cache-keys which returns cached
  2946     // 3xx-responses (redirects) in order to detect cycles. If a cycle is
  2947     // found, ignore the cached response and hit the net. Otherwise, use
  2948     // the cached response and add the cache-key to the chain. Note that
  2949     // a limited number of redirects (cached or not) is allowed and is
  2950     // enforced independently of this mechanism
  2951     if (!doValidation && isCachedRedirect) {
  2952         nsAutoCString cacheKey;
  2953         GenerateCacheKey(mPostID, cacheKey);
  2955         if (!mRedirectedCachekeys)
  2956             mRedirectedCachekeys = new nsTArray<nsCString>();
  2957         else if (mRedirectedCachekeys->Contains(cacheKey))
  2958             doValidation = true;
  2960         LOG(("Redirection-chain %s key %s\n",
  2961              doValidation ? "contains" : "does not contain", cacheKey.get()));
  2963         // Append cacheKey if not in the chain already
  2964         if (!doValidation)
  2965             mRedirectedCachekeys->AppendElement(cacheKey);
  2968     mCachedContentIsValid = !doValidation;
  2970     if (doValidation) {
  2971         //
  2972         // now, we are definitely going to issue a HTTP request to the server.
  2973         // make it conditional if possible.
  2974         //
  2975         // do not attempt to validate no-store content, since servers will not
  2976         // expect it to be cached.  (we only keep it in our cache for the
  2977         // purposes of back/forward, etc.)
  2978         //
  2979         // the request method MUST be either GET or HEAD (see bug 175641).
  2980         //
  2981         // do not override conditional headers when consumer has defined its own
  2982         if (!mCachedResponseHead->NoStore() &&
  2983             (mRequestHead.IsGet() || mRequestHead.IsHead()) &&
  2984              !mCustomConditionalRequest) {
  2986             if (mConcurentCacheAccess) {
  2987                 // In case of concurrent read and also validation request we
  2988                 // must wait for the current writer to close the output stream
  2989                 // first.  Otherwise, when the writer's job would have been interrupted
  2990                 // before all the data were downloaded, we'd have to do a range request
  2991                 // which would be a second request in line during this channel's
  2992                 // life-time.  nsHttpChannel is not designed to do that, so rather
  2993                 // turn off concurrent read and wait for entry's completion.
  2994                 // Then only re-validation or range-re-validation request will go out.
  2995                 mConcurentCacheAccess = 0;
  2996                 // This will cause that OnCacheEntryCheck is called again with the same
  2997                 // entry after the writer is done.
  2998                 wantCompleteEntry = true;
  2999             } else {
  3000                 const char *val;
  3001                 // Add If-Modified-Since header if a Last-Modified was given
  3002                 // and we are allowed to do this (see bugs 510359 and 269303)
  3003                 if (canAddImsHeader) {
  3004                     val = mCachedResponseHead->PeekHeader(nsHttp::Last_Modified);
  3005                     if (val)
  3006                         mRequestHead.SetHeader(nsHttp::If_Modified_Since,
  3007                                                nsDependentCString(val));
  3009                 // Add If-None-Match header if an ETag was given in the response
  3010                 val = mCachedResponseHead->PeekHeader(nsHttp::ETag);
  3011                 if (val)
  3012                     mRequestHead.SetHeader(nsHttp::If_None_Match,
  3013                                            nsDependentCString(val));
  3014                 mDidReval = true;
  3019     if (mCachedContentIsValid || mDidReval) {
  3020         rv = OpenCacheInputStream(entry, mCachedContentIsValid);
  3021         if (NS_FAILED(rv)) {
  3022             // If we can't get the entity then we have to act as though we
  3023             // don't have the cache entry.
  3024             if (mDidReval) {
  3025                 // Make the request unconditional again.
  3026                 mRequestHead.ClearHeader(nsHttp::If_Modified_Since);
  3027                 mRequestHead.ClearHeader(nsHttp::If_None_Match);
  3028                 mRequestHead.ClearHeader(nsHttp::ETag);
  3029                 mDidReval = false;
  3031             mCachedContentIsValid = false;
  3035     if (mDidReval)
  3036         *aResult = ENTRY_NEEDS_REVALIDATION;
  3037     else if (wantCompleteEntry)
  3038         *aResult = RECHECK_AFTER_WRITE_FINISHED;
  3039     else
  3040         *aResult = ENTRY_WANTED;
  3042     if (mCachedContentIsValid) {
  3043         entry->MaybeMarkValid();
  3046     LOG(("nsHTTPChannel::OnCacheEntryCheck exit [this=%p doValidation=%d result=%d]\n",
  3047          this, doValidation, *aResult));
  3048     return rv;
  3051 NS_IMETHODIMP
  3052 nsHttpChannel::OnCacheEntryAvailable(nsICacheEntry *entry,
  3053                                      bool aNew,
  3054                                      nsIApplicationCache* aAppCache,
  3055                                      nsresult status)
  3057     MOZ_ASSERT(NS_IsMainThread());
  3059     nsresult rv;
  3061     LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p "
  3062          "new=%d appcache=%p status=%x]\n", this, entry, aNew, aAppCache, status));
  3064     // if the channel's already fired onStopRequest, then we should ignore
  3065     // this event.
  3066     if (!mIsPending) {
  3067         mCacheInputStream.CloseAndRelease();
  3068         return NS_OK;
  3071     rv = OnCacheEntryAvailableInternal(entry, aNew, aAppCache, status);
  3072     if (NS_FAILED(rv)) {
  3073         CloseCacheEntry(true);
  3074         AsyncAbort(rv);
  3077     return NS_OK;
  3080 nsresult
  3081 nsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntry *entry,
  3082                                              bool aNew,
  3083                                              nsIApplicationCache* aAppCache,
  3084                                              nsresult status)
  3086     nsresult rv;
  3088     if (mCanceled) {
  3089         LOG(("channel was canceled [this=%p status=%x]\n", this, mStatus));
  3090         return mStatus;
  3093     if (aAppCache) {
  3094         if (mApplicationCache == aAppCache && !mCacheEntry) {
  3095             rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status);
  3097         else if (mApplicationCacheForWrite == aAppCache && aNew && !mOfflineCacheEntry) {
  3098             rv = OnOfflineCacheEntryForWritingAvailable(entry, aAppCache, status);
  3100         else {
  3101             rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status);
  3104     else {
  3105         rv = OnNormalCacheEntryAvailable(entry, aNew, status);
  3108     if (NS_FAILED(rv) && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
  3109         // If we have a fallback URI (and we're not already
  3110         // falling back), process the fallback asynchronously.
  3111         if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
  3112             return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
  3114         return NS_ERROR_DOCUMENT_NOT_CACHED;
  3117     if (NS_FAILED(rv))
  3118       return rv;
  3120     // We may be waiting for more callbacks...
  3121     if (mCacheEntriesToWaitFor)
  3122       return NS_OK;
  3124     return ContinueConnect();
  3127 nsresult
  3128 nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntry *aEntry,
  3129                                            bool aNew,
  3130                                            nsresult aEntryStatus)
  3132     mCacheEntriesToWaitFor &= ~WAIT_FOR_CACHE_ENTRY;
  3134     if (NS_FAILED(aEntryStatus) || aNew) {
  3135         // Make sure this flag is dropped.  It may happen the entry is doomed
  3136         // between OnCacheEntryCheck and OnCacheEntryAvailable.
  3137         mCachedContentIsValid = false;
  3139         if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
  3140             // if this channel is only allowed to pull from the cache, then
  3141             // we must fail if we were unable to open a cache entry for read.
  3142             return NS_ERROR_DOCUMENT_NOT_CACHED;
  3146     if (NS_SUCCEEDED(aEntryStatus)) {
  3147         mCacheEntry = aEntry;
  3148         mCacheEntryIsWriteOnly = aNew;
  3150         if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
  3151             Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,
  3152                                   false);
  3156     return NS_OK;
  3159 nsresult
  3160 nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntry *aEntry,
  3161                                             bool aNew,
  3162                                             nsIApplicationCache* aAppCache,
  3163                                             nsresult aEntryStatus)
  3165     MOZ_ASSERT(!mApplicationCache || aAppCache == mApplicationCache);
  3166     MOZ_ASSERT(!aNew || !aEntry || mApplicationCacheForWrite);
  3168     mCacheEntriesToWaitFor &= ~WAIT_FOR_CACHE_ENTRY;
  3170     nsresult rv;
  3172     if (!mApplicationCache)
  3173         mApplicationCache = aAppCache;
  3175     if (NS_SUCCEEDED(aEntryStatus)) {
  3176         // We successfully opened an offline cache session and the entry,
  3177         // so indicate we will load from the offline cache.
  3178         mLoadedFromApplicationCache = true;
  3179         mCacheEntryIsReadOnly = true;
  3180         mCacheEntry = aEntry;
  3181         mCacheEntryIsWriteOnly = false;
  3183         if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI && !mApplicationCacheForWrite) {
  3184             Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,
  3185                                   true);
  3188         return NS_OK;
  3191     if (!mApplicationCacheForWrite && !mFallbackChannel) {
  3192         // Check for namespace match.
  3193         nsCOMPtr<nsIApplicationCacheNamespace> namespaceEntry;
  3194         rv = mApplicationCache->GetMatchingNamespace(mSpec,
  3195             getter_AddRefs(namespaceEntry));
  3196         NS_ENSURE_SUCCESS(rv, rv);
  3198         uint32_t namespaceType = 0;
  3199         if (!namespaceEntry ||
  3200             NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) ||
  3201             (namespaceType &
  3202              (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK |
  3203               nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) == 0) {
  3204             // When loading from an application cache, only items
  3205             // on the whitelist or matching a
  3206             // fallback namespace should hit the network...
  3207             mLoadFlags |= LOAD_ONLY_FROM_CACHE;
  3209             // ... and if there were an application cache entry,
  3210             // we would have found it earlier.
  3211             return NS_ERROR_CACHE_KEY_NOT_FOUND;
  3214         if (namespaceType &
  3215             nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
  3216             rv = namespaceEntry->GetData(mFallbackKey);
  3217             NS_ENSURE_SUCCESS(rv, rv);
  3221     return NS_OK;
  3224 nsresult
  3225 nsHttpChannel::OnOfflineCacheEntryForWritingAvailable(nsICacheEntry *aEntry,
  3226                                                       nsIApplicationCache* aAppCache,
  3227                                                       nsresult aEntryStatus)
  3229     MOZ_ASSERT(mApplicationCacheForWrite && aAppCache == mApplicationCacheForWrite);
  3231     mCacheEntriesToWaitFor &= ~WAIT_FOR_OFFLINE_CACHE_ENTRY;
  3233     if (NS_SUCCEEDED(aEntryStatus)) {
  3234         mOfflineCacheEntry = aEntry;
  3235         if (NS_FAILED(aEntry->GetLastModified(&mOfflineCacheLastModifiedTime))) {
  3236             mOfflineCacheLastModifiedTime = 0;
  3240     return aEntryStatus;
  3243 // Generates the proper cache-key for this instance of nsHttpChannel
  3244 nsresult
  3245 nsHttpChannel::GenerateCacheKey(uint32_t postID, nsACString &cacheKey)
  3247     AssembleCacheKey(mFallbackChannel ? mFallbackKey.get() : mSpec.get(),
  3248                      postID, cacheKey);
  3249     return NS_OK;
  3252 // Assembles a cache-key from the given pieces of information and |mLoadFlags|
  3253 void
  3254 nsHttpChannel::AssembleCacheKey(const char *spec, uint32_t postID,
  3255                                 nsACString &cacheKey)
  3257     cacheKey.Truncate();
  3259     if (mLoadFlags & LOAD_ANONYMOUS) {
  3260         cacheKey.AssignLiteral("anon&");
  3263     if (postID) {
  3264         char buf[32];
  3265         PR_snprintf(buf, sizeof(buf), "id=%x&", postID);
  3266         cacheKey.Append(buf);
  3269     if (strlen(mCacheDomain.get()) > 0) {
  3270         cacheKey.AppendLiteral("domain=");
  3271         cacheKey.Append(mCacheDomain.get());
  3272         cacheKey.AppendLiteral("&");
  3275     if (!cacheKey.IsEmpty()) {
  3276         cacheKey.AppendLiteral("uri=");
  3279     // Strip any trailing #ref from the URL before using it as the key
  3280     const char *p = strchr(spec, '#');
  3281     if (p)
  3282         cacheKey.Append(spec, p - spec);
  3283     else
  3284         cacheKey.Append(spec);
  3287 // UpdateExpirationTime is called when a new response comes in from the server.
  3288 // It updates the stored response-time and sets the expiration time on the
  3289 // cache entry.
  3290 //
  3291 // From section 13.2.4 of RFC2616, we compute expiration time as follows:
  3292 //
  3293 //    timeRemaining = freshnessLifetime - currentAge
  3294 //    expirationTime = now + timeRemaining
  3295 //
  3296 nsresult
  3297 nsHttpChannel::UpdateExpirationTime()
  3299     NS_ENSURE_TRUE(mResponseHead, NS_ERROR_FAILURE);
  3301     nsresult rv;
  3303     uint32_t expirationTime = 0;
  3304     if (!mResponseHead->MustValidate()) {
  3305         uint32_t freshnessLifetime = 0;
  3307         rv = mResponseHead->ComputeFreshnessLifetime(&freshnessLifetime);
  3308         if (NS_FAILED(rv)) return rv;
  3310         if (freshnessLifetime > 0) {
  3311             uint32_t now = NowInSeconds(), currentAge = 0;
  3313             rv = mResponseHead->ComputeCurrentAge(now, mRequestTime, &currentAge);
  3314             if (NS_FAILED(rv)) return rv;
  3316             LOG(("freshnessLifetime = %u, currentAge = %u\n",
  3317                 freshnessLifetime, currentAge));
  3319             if (freshnessLifetime > currentAge) {
  3320                 uint32_t timeRemaining = freshnessLifetime - currentAge;
  3321                 // be careful... now + timeRemaining may overflow
  3322                 if (now + timeRemaining < now)
  3323                     expirationTime = uint32_t(-1);
  3324                 else
  3325                     expirationTime = now + timeRemaining;
  3327             else
  3328                 expirationTime = now;
  3332     rv = mCacheEntry->SetExpirationTime(expirationTime);
  3333     NS_ENSURE_SUCCESS(rv, rv);
  3335     if (mOfflineCacheEntry) {
  3336         rv = mOfflineCacheEntry->SetExpirationTime(expirationTime);
  3337         NS_ENSURE_SUCCESS(rv, rv);
  3340     return NS_OK;
  3343 /*static*/ inline bool
  3344 nsHttpChannel::HasQueryString(nsHttpRequestHead::ParsedMethodType method, nsIURI * uri)
  3346     // Must be called on the main thread because nsIURI does not implement
  3347     // thread-safe QueryInterface.
  3348     MOZ_ASSERT(NS_IsMainThread());
  3350     if (method != nsHttpRequestHead::kMethod_Get &&
  3351         method != nsHttpRequestHead::kMethod_Head)
  3352         return false;
  3354     nsAutoCString query;
  3355     nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
  3356     nsresult rv = url->GetQuery(query);
  3357     return NS_SUCCEEDED(rv) && !query.IsEmpty();
  3360 bool
  3361 nsHttpChannel::MustValidateBasedOnQueryUrl() const
  3363     // RFC 2616, section 13.9 states that GET-requests with a query-url
  3364     // MUST NOT be treated as fresh unless the server explicitly provides
  3365     // an expiration-time in the response. See bug #468594
  3366     // Section 13.2.1 (6th paragraph) defines "explicit expiration time"
  3367     if (mHasQueryString)
  3369         uint32_t tmp; // we don't need the value, just whether it's set
  3370         nsresult rv = mCachedResponseHead->GetExpiresValue(&tmp);
  3371         if (NS_FAILED(rv)) {
  3372             rv = mCachedResponseHead->GetMaxAgeValue(&tmp);
  3373             if (NS_FAILED(rv)) {
  3374                 return true;
  3378     return false;
  3382 bool
  3383 nsHttpChannel::ShouldUpdateOfflineCacheEntry()
  3385     if (!mApplicationCacheForWrite || !mOfflineCacheEntry) {
  3386         return false;
  3389     // if we're updating the cache entry, update the offline cache entry too
  3390     if (mCacheEntry && mCacheEntryIsWriteOnly) {
  3391         return true;
  3394     // if there's nothing in the offline cache, add it
  3395     if (mOfflineCacheEntry) {
  3396         return true;
  3399     // if the document is newer than the offline entry, update it
  3400     uint32_t docLastModifiedTime;
  3401     nsresult rv = mResponseHead->GetLastModifiedValue(&docLastModifiedTime);
  3402     if (NS_FAILED(rv)) {
  3403         return true;
  3406     if (mOfflineCacheLastModifiedTime == 0) {
  3407         return false;
  3410     if (docLastModifiedTime > mOfflineCacheLastModifiedTime) {
  3411         return true;
  3414     return false;
  3417 nsresult
  3418 nsHttpChannel::OpenCacheInputStream(nsICacheEntry* cacheEntry, bool startBuffering)
  3420     nsresult rv;
  3422     bool usingSSL = false;
  3423     rv = mURI->SchemeIs("https", &usingSSL);
  3424     NS_ENSURE_SUCCESS(rv,rv);
  3426     if (usingSSL) {
  3427         rv = cacheEntry->GetSecurityInfo(
  3428                                       getter_AddRefs(mCachedSecurityInfo));
  3429         if (NS_FAILED(rv)) {
  3430             LOG(("failed to parse security-info [channel=%p, entry=%p]",
  3431                  this, cacheEntry));
  3432             NS_WARNING("failed to parse security-info");
  3433             return rv;
  3436         // XXX: We should not be skilling this check in the offline cache
  3437         // case, but we have to do so now to work around bug 794507.
  3438         MOZ_ASSERT(mCachedSecurityInfo || mLoadedFromApplicationCache);
  3439         if (!mCachedSecurityInfo && !mLoadedFromApplicationCache) {
  3440             LOG(("mCacheEntry->GetSecurityInfo returned success but did not "
  3441                  "return the security info [channel=%p, entry=%p]",
  3442                  this, cacheEntry));
  3443             return NS_ERROR_UNEXPECTED; // XXX error code
  3447     // Keep the conditions below in sync with the conditions in ReadFromCache.
  3449     rv = NS_OK;
  3451     if (WillRedirect(mCachedResponseHead)) {
  3452         // Do not even try to read the entity for a redirect because we do not
  3453         // return an entity to the application when we process redirects.
  3454         LOG(("Will skip read of cached redirect entity\n"));
  3455         return NS_OK;
  3458     if ((mLoadFlags & nsICachingChannel::LOAD_ONLY_IF_MODIFIED) &&
  3459         !mCachedContentIsPartial) {
  3460         // For LOAD_ONLY_IF_MODIFIED, we usually don't have to deal with the
  3461         // cached entity.
  3462         if (!mApplicationCacheForWrite) {
  3463             LOG(("Will skip read from cache based on LOAD_ONLY_IF_MODIFIED "
  3464                  "load flag\n"));
  3465             return NS_OK;
  3468         // If offline caching has been requested and the offline cache needs
  3469         // updating, we must complete the call even if the main cache entry
  3470         // is up to date. We don't know yet for sure whether the offline
  3471         // cache needs updating because at this point we haven't opened it
  3472         // for writing yet, so we have to start reading the cached entity now
  3473         // just in case.
  3474         LOG(("May skip read from cache based on LOAD_ONLY_IF_MODIFIED "
  3475               "load flag\n"));
  3478     // Open an input stream for the entity, so that the call to OpenInputStream
  3479     // happens off the main thread.
  3480     nsCOMPtr<nsIInputStream> stream;
  3481     rv = cacheEntry->OpenInputStream(0, getter_AddRefs(stream));
  3483     if (NS_FAILED(rv)) {
  3484         LOG(("Failed to open cache input stream [channel=%p, "
  3485              "mCacheEntry=%p]", this, cacheEntry));
  3486         return rv;
  3489     if (startBuffering) {
  3490         bool nonBlocking;
  3491         rv = stream->IsNonBlocking(&nonBlocking);
  3492         if (NS_SUCCEEDED(rv) && nonBlocking)
  3493             startBuffering = false;
  3496     if (!startBuffering) {
  3497         // Bypass wrapping the input stream for the new cache back-end since
  3498         // nsIStreamTransportService expects a blocking stream.  Preloading of
  3499         // the data must be done on the level of the cache backend, internally.
  3500         //
  3501         // We do not connect the stream to the stream transport service if we
  3502         // have to validate the entry with the server. If we did, we would get
  3503         // into a race condition between the stream transport service reading
  3504         // the existing contents and the opening of the cache entry's output
  3505         // stream to write the new contents in the case where we get a non-304
  3506         // response.
  3507         LOG(("Opened cache input stream without buffering [channel=%p, "
  3508               "mCacheEntry=%p, stream=%p]", this,
  3509               cacheEntry, stream.get()));
  3510         mCacheInputStream.takeOver(stream);
  3511         return rv;
  3514     // Have the stream transport service start reading the entity on one of its
  3515     // background threads.
  3517     nsCOMPtr<nsITransport> transport;
  3518     nsCOMPtr<nsIInputStream> wrapper;
  3520     nsCOMPtr<nsIStreamTransportService> sts =
  3521         do_GetService(kStreamTransportServiceCID, &rv);
  3522     if (NS_SUCCEEDED(rv)) {
  3523         rv = sts->CreateInputTransport(stream, int64_t(-1), int64_t(-1),
  3524                                         true, getter_AddRefs(transport));
  3526     if (NS_SUCCEEDED(rv)) {
  3527         rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(wrapper));
  3529     if (NS_SUCCEEDED(rv)) {
  3530         LOG(("Opened cache input stream [channel=%p, wrapper=%p, "
  3531               "transport=%p, stream=%p]", this, wrapper.get(),
  3532               transport.get(), stream.get()));
  3533     } else {
  3534         LOG(("Failed to open cache input stream [channel=%p, "
  3535               "wrapper=%p, transport=%p, stream=%p]", this,
  3536               wrapper.get(), transport.get(), stream.get()));
  3538         stream->Close();
  3539         return rv;
  3542     mCacheInputStream.takeOver(wrapper);
  3544     return NS_OK;
  3547 // Actually process the cached response that we started to handle in CheckCache
  3548 // and/or StartBufferingCachedEntity.
  3549 nsresult
  3550 nsHttpChannel::ReadFromCache(bool alreadyMarkedValid)
  3552     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE);
  3553     NS_ENSURE_TRUE(mCachedContentIsValid, NS_ERROR_FAILURE);
  3555     LOG(("nsHttpChannel::ReadFromCache [this=%p] "
  3556          "Using cached copy of: %s\n", this, mSpec.get()));
  3558     if (mCachedResponseHead)
  3559         mResponseHead = mCachedResponseHead;
  3561     UpdateInhibitPersistentCachingFlag();
  3563     // if we don't already have security info, try to get it from the cache
  3564     // entry. there are two cases to consider here: 1) we are just reading
  3565     // from the cache, or 2) this may be due to a 304 not modified response,
  3566     // in which case we could have security info from a socket transport.
  3567     if (!mSecurityInfo)
  3568         mSecurityInfo = mCachedSecurityInfo;
  3570     if (!alreadyMarkedValid && !mCachedContentIsPartial) {
  3571         // We validated the entry, and we have write access to the cache, so
  3572         // mark the cache entry as valid in order to allow others access to
  3573         // this cache entry.
  3574         //
  3575         // TODO: This should be done asynchronously so we don't take the cache
  3576         // service lock on the main thread.
  3577         mCacheEntry->MaybeMarkValid();
  3580     nsresult rv;
  3582     // Keep the conditions below in sync with the conditions in
  3583     // StartBufferingCachedEntity.
  3585     if (WillRedirect(mResponseHead)) {
  3586         // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here,
  3587         // to avoid event dispatching latency.
  3588         MOZ_ASSERT(!mCacheInputStream);
  3589         LOG(("Skipping skip read of cached redirect entity\n"));
  3590         return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
  3593     if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !mCachedContentIsPartial) {
  3594         if (!mApplicationCacheForWrite) {
  3595             LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
  3596                  "load flag\n"));
  3597             MOZ_ASSERT(!mCacheInputStream);
  3598             // TODO: Bug 759040 - We should call HandleAsyncNotModified directly
  3599             // here, to avoid event dispatching latency.
  3600             return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
  3603         if (!ShouldUpdateOfflineCacheEntry()) {
  3604             LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
  3605                  "load flag (mApplicationCacheForWrite not null case)\n"));
  3606             mCacheInputStream.CloseAndRelease();
  3607             // TODO: Bug 759040 - We should call HandleAsyncNotModified directly
  3608             // here, to avoid event dispatching latency.
  3609             return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
  3613     MOZ_ASSERT(mCacheInputStream);
  3614     if (!mCacheInputStream) {
  3615         NS_ERROR("mCacheInputStream is null but we're expecting to "
  3616                         "be able to read from it.");
  3617         return NS_ERROR_UNEXPECTED;
  3621     nsCOMPtr<nsIInputStream> inputStream = mCacheInputStream.forget();
  3623     rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), inputStream,
  3624                                    int64_t(-1), int64_t(-1), 0, 0, true);
  3625     if (NS_FAILED(rv)) {
  3626         inputStream->Close();
  3627         return rv;
  3630     rv = mCachePump->AsyncRead(this, mListenerContext);
  3631     if (NS_FAILED(rv)) return rv;
  3633     if (mTimingEnabled)
  3634         mCacheReadStart = TimeStamp::Now();
  3636     uint32_t suspendCount = mSuspendCount;
  3637     while (suspendCount--)
  3638         mCachePump->Suspend();
  3640     return NS_OK;
  3643 void
  3644 nsHttpChannel::CloseCacheEntry(bool doomOnFailure)
  3646     mCacheInputStream.CloseAndRelease();
  3648     if (!mCacheEntry)
  3649         return;
  3651     LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%x mCacheEntryIsWriteOnly=%x",
  3652          this, mStatus, mCacheEntryIsWriteOnly));
  3654     // If we have begun to create or replace a cache entry, and that cache
  3655     // entry is not complete and not resumable, then it needs to be doomed.
  3656     // Otherwise, CheckCache will make the mistake of thinking that the
  3657     // partial cache entry is complete.
  3659     bool doom = false;
  3660     if (mInitedCacheEntry) {
  3661         MOZ_ASSERT(mResponseHead, "oops");
  3662         if (NS_FAILED(mStatus) && doomOnFailure &&
  3663             mCacheEntryIsWriteOnly && !mResponseHead->IsResumable())
  3664             doom = true;
  3666     else if (mCacheEntryIsWriteOnly)
  3667         doom = true;
  3669     if (doom) {
  3670         LOG(("  dooming cache entry!!"));
  3671         mCacheEntry->AsyncDoom(nullptr);
  3674     mCachedResponseHead = nullptr;
  3676     mCachePump = nullptr;
  3677     mCacheEntry = nullptr;
  3678     mCacheEntryIsWriteOnly = false;
  3679     mInitedCacheEntry = false;
  3683 void
  3684 nsHttpChannel::CloseOfflineCacheEntry()
  3686     if (!mOfflineCacheEntry)
  3687         return;
  3689     LOG(("nsHttpChannel::CloseOfflineCacheEntry [this=%p]", this));
  3691     if (NS_FAILED(mStatus)) {
  3692         mOfflineCacheEntry->AsyncDoom(nullptr);
  3694     else {
  3695         bool succeeded;
  3696         if (NS_SUCCEEDED(GetRequestSucceeded(&succeeded)) && !succeeded)
  3697             mOfflineCacheEntry->AsyncDoom(nullptr);
  3700     mOfflineCacheEntry = nullptr;
  3704 // Initialize the cache entry for writing.
  3705 //  - finalize storage policy
  3706 //  - store security info
  3707 //  - update expiration time
  3708 //  - store headers and other meta data
  3709 nsresult
  3710 nsHttpChannel::InitCacheEntry()
  3712     nsresult rv;
  3714     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_UNEXPECTED);
  3715     // if only reading, nothing to be done here.
  3716     if (mCacheEntryIsReadOnly)
  3717         return NS_OK;
  3719     // Don't cache the response again if already cached...
  3720     if (mCachedContentIsValid)
  3721         return NS_OK;
  3723     LOG(("nsHttpChannel::InitCacheEntry [this=%p entry=%p]\n",
  3724         this, mCacheEntry.get()));
  3726     bool recreate = !mCacheEntryIsWriteOnly;
  3727     bool dontPersist = mLoadFlags & INHIBIT_PERSISTENT_CACHING;
  3729     if (!recreate && dontPersist) {
  3730         // If the current entry is persistent but we inhibit peristence
  3731         // then force recreation of the entry as memory/only.
  3732         rv = mCacheEntry->GetPersistent(&recreate);
  3733         if (NS_FAILED(rv))
  3734             return rv;
  3737     if (recreate) {
  3738         LOG(("  we have a ready entry, but reading it again from the server -> recreating cache entry\n"));
  3739         nsCOMPtr<nsICacheEntry> currentEntry;
  3740         currentEntry.swap(mCacheEntry);
  3741         rv = currentEntry->Recreate(dontPersist, getter_AddRefs(mCacheEntry));
  3742         if (NS_FAILED(rv)) {
  3743           LOG(("  recreation failed, the response will not be cached"));
  3744           return NS_OK;
  3747         mCacheEntryIsWriteOnly = true;
  3750     // Set the expiration time for this cache entry
  3751     rv = UpdateExpirationTime();
  3752     if (NS_FAILED(rv)) return rv;
  3754     rv = AddCacheEntryHeaders(mCacheEntry);
  3755     if (NS_FAILED(rv)) return rv;
  3757     mInitedCacheEntry = true;
  3759     // Don't perform the check when writing (doesn't make sense)
  3760     mConcurentCacheAccess = 0;
  3762     return NS_OK;
  3765 void
  3766 nsHttpChannel::UpdateInhibitPersistentCachingFlag()
  3768     // The no-store directive within the 'Cache-Control:' header indicates
  3769     // that we must not store the response in a persistent cache.
  3770     if (mResponseHead->NoStore())
  3771         mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
  3773     // Only cache SSL content on disk if the pref is set
  3774     if (!gHttpHandler->IsPersistentHttpsCachingEnabled() &&
  3775         mConnectionInfo->UsingSSL())
  3776         mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
  3779 nsresult
  3780 nsHttpChannel::InitOfflineCacheEntry()
  3782     // This function can be called even when we fail to connect (bug 551990)
  3784     if (!mOfflineCacheEntry) {
  3785         return NS_OK;
  3788     if (!mResponseHead || mResponseHead->NoStore()) {
  3789         if (mResponseHead && mResponseHead->NoStore()) {
  3790             mOfflineCacheEntry->AsyncDoom(nullptr);
  3793         CloseOfflineCacheEntry();
  3795         if (mResponseHead && mResponseHead->NoStore()) {
  3796             return NS_ERROR_NOT_AVAILABLE;
  3799         return NS_OK;
  3802     // This entry's expiration time should match the main entry's expiration
  3803     // time.  UpdateExpirationTime() will keep it in sync once the offline
  3804     // cache entry has been created.
  3805     if (mCacheEntry) {
  3806         uint32_t expirationTime;
  3807         nsresult rv = mCacheEntry->GetExpirationTime(&expirationTime);
  3808         NS_ENSURE_SUCCESS(rv, rv);
  3810         mOfflineCacheEntry->SetExpirationTime(expirationTime);
  3813     return AddCacheEntryHeaders(mOfflineCacheEntry);
  3817 nsresult
  3818 nsHttpChannel::AddCacheEntryHeaders(nsICacheEntry *entry)
  3820     nsresult rv;
  3822     LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] begin", this));
  3823     // Store secure data in memory only
  3824     if (mSecurityInfo)
  3825         entry->SetSecurityInfo(mSecurityInfo);
  3827     // Store the HTTP request method with the cache entry so we can distinguish
  3828     // for example GET and HEAD responses.
  3829     rv = entry->SetMetaDataElement("request-method",
  3830                                    mRequestHead.Method().get());
  3831     if (NS_FAILED(rv)) return rv;
  3833     // Store the HTTP authorization scheme used if any...
  3834     rv = StoreAuthorizationMetaData(entry);
  3835     if (NS_FAILED(rv)) return rv;
  3837     // Iterate over the headers listed in the Vary response header, and
  3838     // store the value of the corresponding request header so we can verify
  3839     // that it has not varied when we try to re-use the cached response at
  3840     // a later time.  Take care to store "Cookie" headers only as hashes
  3841     // due to security considerations and the fact that they can be pretty
  3842     // large (bug 468426). We take care of "Vary: cookie" in ResponseWouldVary.
  3843     //
  3844     // NOTE: if "Vary: accept, cookie", then we will store the "accept" header
  3845     // in the cache.  we could try to avoid needlessly storing the "accept"
  3846     // header in this case, but it doesn't seem worth the extra code to perform
  3847     // the check.
  3849         nsAutoCString buf, metaKey;
  3850         mResponseHead->GetHeader(nsHttp::Vary, buf);
  3851         if (!buf.IsEmpty()) {
  3852             NS_NAMED_LITERAL_CSTRING(prefix, "request-");
  3854             char *val = buf.BeginWriting(); // going to munge buf
  3855             char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
  3856             while (token) {
  3857                 LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \
  3858                         "processing %s", this, token));
  3859                 if (*token != '*') {
  3860                     nsHttpAtom atom = nsHttp::ResolveAtom(token);
  3861                     const char *val = mRequestHead.PeekHeader(atom);
  3862                     nsAutoCString hash;
  3863                     if (val) {
  3864                         // If cookie-header, store a hash of the value
  3865                         if (atom == nsHttp::Cookie) {
  3866                             LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \
  3867                                     "cookie-value %s", this, val));
  3868                             rv = Hash(val, hash);
  3869                             // If hash failed, store a string not very likely
  3870                             // to be the result of subsequent hashes
  3871                             if (NS_FAILED(rv))
  3872                                 val = "<hash failed>";
  3873                             else
  3874                                 val = hash.get();
  3876                             LOG(("   hashed to %s\n", val));
  3879                         // build cache meta data key and set meta data element...
  3880                         metaKey = prefix + nsDependentCString(token);
  3881                         entry->SetMetaDataElement(metaKey.get(), val);
  3882                     } else {
  3883                         LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \
  3884                                 "clearing metadata for %s", this, token));
  3885                         metaKey = prefix + nsDependentCString(token);
  3886                         entry->SetMetaDataElement(metaKey.get(), nullptr);
  3889                 token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
  3895     // Store the received HTTP head with the cache entry as an element of
  3896     // the meta data.
  3897     nsAutoCString head;
  3898     mResponseHead->Flatten(head, true);
  3899     rv = entry->SetMetaDataElement("response-head", head.get());
  3900     if (NS_FAILED(rv)) return rv;
  3902     // Indicate we have successfully finished setting metadata on the cache entry.
  3903     rv = entry->MetaDataReady();
  3905     return rv;
  3908 inline void
  3909 GetAuthType(const char *challenge, nsCString &authType)
  3911     const char *p;
  3913     // get the challenge type
  3914     if ((p = strchr(challenge, ' ')) != nullptr)
  3915         authType.Assign(challenge, p - challenge);
  3916     else
  3917         authType.Assign(challenge);
  3920 nsresult
  3921 nsHttpChannel::StoreAuthorizationMetaData(nsICacheEntry *entry)
  3923     // Not applicable to proxy authorization...
  3924     const char *val = mRequestHead.PeekHeader(nsHttp::Authorization);
  3925     if (!val)
  3926         return NS_OK;
  3928     // eg. [Basic realm="wally world"]
  3929     nsAutoCString buf;
  3930     GetAuthType(val, buf);
  3931     return entry->SetMetaDataElement("auth", buf.get());
  3934 // Finalize the cache entry
  3935 //  - may need to rewrite response headers if any headers changed
  3936 //  - may need to recalculate the expiration time if any headers changed
  3937 //  - called only for freshly written cache entries
  3938 nsresult
  3939 nsHttpChannel::FinalizeCacheEntry()
  3941     LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p]\n", this));
  3943     if (mResponseHead && mResponseHeadersModified) {
  3944         // Set the expiration time for this cache entry
  3945         nsresult rv = UpdateExpirationTime();
  3946         if (NS_FAILED(rv)) return rv;
  3948     return NS_OK;
  3951 // Open an output stream to the cache entry and insert a listener tee into
  3952 // the chain of response listeners.
  3953 nsresult
  3954 nsHttpChannel::InstallCacheListener(int64_t offset)
  3956     nsresult rv;
  3958     LOG(("Preparing to write data into the cache [uri=%s]\n", mSpec.get()));
  3960     MOZ_ASSERT(mCacheEntry);
  3961     MOZ_ASSERT(mCacheEntryIsWriteOnly || mCachedContentIsPartial);
  3962     MOZ_ASSERT(mListener);
  3964     // If the content is compressible and the server has not compressed it,
  3965     // mark the cache entry for compression.
  3966     if ((mResponseHead->PeekHeader(nsHttp::Content_Encoding) == nullptr) && (
  3967          mResponseHead->ContentType().EqualsLiteral(TEXT_HTML) ||
  3968          mResponseHead->ContentType().EqualsLiteral(TEXT_PLAIN) ||
  3969          mResponseHead->ContentType().EqualsLiteral(TEXT_CSS) ||
  3970          mResponseHead->ContentType().EqualsLiteral(TEXT_JAVASCRIPT) ||
  3971          mResponseHead->ContentType().EqualsLiteral(TEXT_ECMASCRIPT) ||
  3972          mResponseHead->ContentType().EqualsLiteral(TEXT_XML) ||
  3973          mResponseHead->ContentType().EqualsLiteral(APPLICATION_JAVASCRIPT) ||
  3974          mResponseHead->ContentType().EqualsLiteral(APPLICATION_ECMASCRIPT) ||
  3975          mResponseHead->ContentType().EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
  3976          mResponseHead->ContentType().EqualsLiteral(APPLICATION_XHTML_XML))) {
  3977         rv = mCacheEntry->SetMetaDataElement("uncompressed-len", "0");
  3978         if (NS_FAILED(rv)) {
  3979             LOG(("unable to mark cache entry for compression"));
  3983     LOG(("Trading cache input stream for output stream [channel=%p]", this));
  3985     // We must close the input stream first because cache entries do not
  3986     // correctly handle having an output stream and input streams open at
  3987     // the same time.
  3988     mCacheInputStream.CloseAndRelease();
  3990     nsCOMPtr<nsIOutputStream> out;
  3991     rv = mCacheEntry->OpenOutputStream(offset, getter_AddRefs(out));
  3992     if (rv == NS_ERROR_NOT_AVAILABLE) {
  3993       LOG(("  entry doomed, not writing it [channel=%p]", this));
  3994       // Entry is already doomed.
  3995       // This may happen when expiration time is set to past and the entry
  3996       // has been removed by the background eviction logic.
  3997       return NS_OK;
  3999     if (NS_FAILED(rv)) return rv;
  4001     // XXX disk cache does not support overlapped i/o yet
  4002 #if 0
  4003     // Mark entry valid inorder to allow simultaneous reading...
  4004     rv = mCacheEntry->MarkValid();
  4005     if (NS_FAILED(rv)) return rv;
  4006 #endif
  4008     nsCOMPtr<nsIStreamListenerTee> tee =
  4009         do_CreateInstance(kStreamListenerTeeCID, &rv);
  4010     if (NS_FAILED(rv)) return rv;
  4012     nsCOMPtr<nsICacheStorageService> serv =
  4013         do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
  4014     NS_ENSURE_SUCCESS(rv, rv);
  4016     nsCOMPtr<nsIEventTarget> cacheIOTarget;
  4017     serv->GetIoTarget(getter_AddRefs(cacheIOTarget));
  4019     if (!cacheIOTarget) {
  4020         LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%x "
  4021              "cacheIOTarget=%p", tee.get(), rv, cacheIOTarget.get()));
  4022         rv = tee->Init(mListener, out, nullptr);
  4023     } else {
  4024         LOG(("nsHttpChannel::InstallCacheListener async tee %p", tee.get()));
  4025         rv = tee->InitAsync(mListener, cacheIOTarget, out, nullptr);
  4028     if (NS_FAILED(rv)) return rv;
  4029     mListener = tee;
  4030     return NS_OK;
  4033 nsresult
  4034 nsHttpChannel::InstallOfflineCacheListener(int64_t offset)
  4036     nsresult rv;
  4038     LOG(("Preparing to write data into the offline cache [uri=%s]\n",
  4039          mSpec.get()));
  4041     MOZ_ASSERT(mOfflineCacheEntry);
  4042     MOZ_ASSERT(mListener);
  4044     nsCOMPtr<nsIOutputStream> out;
  4045     rv = mOfflineCacheEntry->OpenOutputStream(offset, getter_AddRefs(out));
  4046     if (NS_FAILED(rv)) return rv;
  4048     nsCOMPtr<nsIStreamListenerTee> tee =
  4049         do_CreateInstance(kStreamListenerTeeCID, &rv);
  4050     if (NS_FAILED(rv)) return rv;
  4052     rv = tee->Init(mListener, out, nullptr);
  4053     if (NS_FAILED(rv)) return rv;
  4055     mListener = tee;
  4057     return NS_OK;
  4060 void
  4061 nsHttpChannel::ClearBogusContentEncodingIfNeeded()
  4063     // For .gz files, apache sends both a Content-Type: application/x-gzip
  4064     // as well as Content-Encoding: gzip, which is completely wrong.  In
  4065     // this case, we choose to ignore the rogue Content-Encoding header. We
  4066     // must do this early on so as to prevent it from being seen up stream.
  4067     // The same problem exists for Content-Encoding: compress in default
  4068     // Apache installs.
  4069     if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "gzip") && (
  4070         mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP) ||
  4071         mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP2) ||
  4072         mResponseHead->ContentType().EqualsLiteral(APPLICATION_GZIP3))) {
  4073         // clear the Content-Encoding header
  4074         mResponseHead->ClearHeader(nsHttp::Content_Encoding);
  4076     else if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "compress") && (
  4077              mResponseHead->ContentType().EqualsLiteral(APPLICATION_COMPRESS) ||
  4078              mResponseHead->ContentType().EqualsLiteral(APPLICATION_COMPRESS2))) {
  4079         // clear the Content-Encoding header
  4080         mResponseHead->ClearHeader(nsHttp::Content_Encoding);
  4084 //-----------------------------------------------------------------------------
  4085 // nsHttpChannel <redirect>
  4086 //-----------------------------------------------------------------------------
  4088 nsresult
  4089 nsHttpChannel::SetupReplacementChannel(nsIURI       *newURI,
  4090                                        nsIChannel   *newChannel,
  4091                                        bool          preserveMethod)
  4093     LOG(("nsHttpChannel::SetupReplacementChannel "
  4094          "[this=%p newChannel=%p preserveMethod=%d]",
  4095          this, newChannel, preserveMethod));
  4097     nsresult rv = HttpBaseChannel::SetupReplacementChannel(newURI, newChannel, preserveMethod);
  4098     if (NS_FAILED(rv))
  4099         return rv;
  4101     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
  4102     if (!httpChannel)
  4103         return NS_OK; // no other options to set
  4105     // convey the mApplyConversion flag (bug 91862)
  4106     nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel);
  4107     if (encodedChannel)
  4108         encodedChannel->SetApplyConversion(mApplyConversion);
  4110     // transfer the resume information
  4111     if (mResuming) {
  4112         nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(newChannel));
  4113         if (!resumableChannel) {
  4114             NS_WARNING("Got asked to resume, but redirected to non-resumable channel!");
  4115             return NS_ERROR_NOT_RESUMABLE;
  4117         resumableChannel->ResumeAt(mStartPos, mEntityID);
  4120     return NS_OK;
  4123 nsresult
  4124 nsHttpChannel::AsyncProcessRedirection(uint32_t redirectType)
  4126     LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n",
  4127         this, redirectType));
  4129     // The channel is actually starting its operation now, at least because
  4130     // we want it to appear like being in the waiting phase until now.
  4131     MOZ_EVENT_TRACER_EXEC(this, "net::http::channel");
  4132     MOZ_EVENT_TRACER_EXEC(this, "net::http::redirect-callbacks");
  4134     const char *location = mResponseHead->PeekHeader(nsHttp::Location);
  4136     // if a location header was not given, then we can't perform the redirect,
  4137     // so just carry on as though this were a normal response.
  4138     if (!location)
  4139         return NS_ERROR_FAILURE;
  4141     // make sure non-ASCII characters in the location header are escaped.
  4142     nsAutoCString locationBuf;
  4143     if (NS_EscapeURL(location, -1, esc_OnlyNonASCII, locationBuf))
  4144         location = locationBuf.get();
  4146     if (mRedirectionLimit == 0) {
  4147         LOG(("redirection limit reached!\n"));
  4148         return NS_ERROR_REDIRECT_LOOP;
  4151     mRedirectType = redirectType;
  4153     LOG(("redirecting to: %s [redirection-limit=%u]\n",
  4154         location, uint32_t(mRedirectionLimit)));
  4156     nsresult rv = CreateNewURI(location, getter_AddRefs(mRedirectURI));
  4158     if (NS_FAILED(rv)) {
  4159         LOG(("Invalid URI for redirect: Location: %s\n", location));
  4160         return NS_ERROR_CORRUPTED_CONTENT;
  4163     if (mApplicationCache) {
  4164         // if we are redirected to a different origin check if there is a fallback
  4165         // cache entry to fall back to. we don't care about file strict
  4166         // checking, at least mURI is not a file URI.
  4167         if (!NS_SecurityCompareURIs(mURI, mRedirectURI, false)) {
  4168             PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback);
  4169             bool waitingForRedirectCallback;
  4170             (void)ProcessFallback(&waitingForRedirectCallback);
  4171             if (waitingForRedirectCallback)
  4172                 return NS_OK;
  4173             PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback);
  4177     return ContinueProcessRedirectionAfterFallback(NS_OK);
  4180 // Creates an URI to the given location using current URI for base and charset
  4181 nsresult
  4182 nsHttpChannel::CreateNewURI(const char *loc, nsIURI **newURI)
  4184     nsCOMPtr<nsIIOService> ioService;
  4185     nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
  4186     if (NS_FAILED(rv)) return rv;
  4188     // the new uri should inherit the origin charset of the current uri
  4189     nsAutoCString originCharset;
  4190     rv = mURI->GetOriginCharset(originCharset);
  4191     if (NS_FAILED(rv))
  4192         originCharset.Truncate();
  4194     return ioService->NewURI(nsDependentCString(loc),
  4195                              originCharset.get(),
  4196                              mURI,
  4197                              newURI);
  4200 nsresult
  4201 nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv)
  4203     if (NS_SUCCEEDED(rv) && mFallingBack) {
  4204         // do not continue with redirect processing, fallback is in
  4205         // progress now.
  4206         return NS_OK;
  4209     // Kill the current cache entry if we are redirecting
  4210     // back to ourself.
  4211     bool redirectingBackToSameURI = false;
  4212     if (mCacheEntry && mCacheEntryIsWriteOnly &&
  4213         NS_SUCCEEDED(mURI->Equals(mRedirectURI, &redirectingBackToSameURI)) &&
  4214         redirectingBackToSameURI)
  4215             mCacheEntry->AsyncDoom(nullptr);
  4217     // move the reference of the old location to the new one if the new
  4218     // one has none.
  4219     nsAutoCString ref;
  4220     rv = mRedirectURI->GetRef(ref);
  4221     if (NS_SUCCEEDED(rv) && ref.IsEmpty()) {
  4222         mURI->GetRef(ref);
  4223         if (!ref.IsEmpty()) {
  4224             // NOTE: SetRef will fail if mRedirectURI is immutable
  4225             // (e.g. an about: URI)... Oh well.
  4226             mRedirectURI->SetRef(ref);
  4230     bool rewriteToGET = ShouldRewriteRedirectToGET(mRedirectType,
  4231                                                    mRequestHead.ParsedMethod());
  4233     // prompt if the method is not safe (such as POST, PUT, DELETE, ...)
  4234     if (!rewriteToGET && !mRequestHead.IsSafeMethod()) {
  4235         rv = PromptTempRedirect();
  4236         if (NS_FAILED(rv)) return rv;
  4239     nsCOMPtr<nsIIOService> ioService;
  4240     rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
  4241     if (NS_FAILED(rv)) return rv;
  4243     nsCOMPtr<nsIChannel> newChannel;
  4244     rv = ioService->NewChannelFromURI(mRedirectURI, getter_AddRefs(newChannel));
  4245     if (NS_FAILED(rv)) return rv;
  4247     rv = SetupReplacementChannel(mRedirectURI, newChannel, !rewriteToGET);
  4248     if (NS_FAILED(rv)) return rv;
  4250     uint32_t redirectFlags;
  4251     if (nsHttp::IsPermanentRedirect(mRedirectType))
  4252         redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
  4253     else
  4254         redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
  4256     // verify that this is a legal redirect
  4257     mRedirectChannel = newChannel;
  4259     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
  4260     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
  4262     if (NS_SUCCEEDED(rv))
  4263         rv = WaitForRedirectCallback();
  4265     if (NS_FAILED(rv)) {
  4266         AutoRedirectVetoNotifier notifier(this);
  4267         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
  4270     return rv;
  4273 nsresult
  4274 nsHttpChannel::ContinueProcessRedirection(nsresult rv)
  4276     AutoRedirectVetoNotifier notifier(this);
  4278     LOG(("ContinueProcessRedirection [rv=%x]\n", rv));
  4279     if (NS_FAILED(rv))
  4280         return rv;
  4282     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
  4284     // Make sure to do this _after_ calling OnChannelRedirect
  4285     mRedirectChannel->SetOriginalURI(mOriginalURI);
  4287     // And now, the deprecated way
  4288     nsCOMPtr<nsIHttpEventSink> httpEventSink;
  4289     GetCallback(httpEventSink);
  4290     if (httpEventSink) {
  4291         // NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8
  4292         // versions.
  4293         rv = httpEventSink->OnRedirect(this, mRedirectChannel);
  4294         if (NS_FAILED(rv))
  4295             return rv;
  4297     // XXX we used to talk directly with the script security manager, but that
  4298     // should really be handled by the event sink implementation.
  4300     // begin loading the new channel
  4301     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
  4303     if (NS_FAILED(rv))
  4304         return rv;
  4306     // close down this channel
  4307     Cancel(NS_BINDING_REDIRECTED);
  4309     notifier.RedirectSucceeded();
  4311     ReleaseListeners();
  4313     return NS_OK;
  4316 //-----------------------------------------------------------------------------
  4317 // nsHttpChannel <auth>
  4318 //-----------------------------------------------------------------------------
  4320 NS_IMETHODIMP nsHttpChannel::OnAuthAvailable()
  4322     LOG(("nsHttpChannel::OnAuthAvailable [this=%p]", this));
  4324     // setting mAuthRetryPending flag and resuming the transaction
  4325     // triggers process of throwing away the unauthenticated data already
  4326     // coming from the network
  4327     mAuthRetryPending = true;
  4328     mProxyAuthPending = false;
  4329     LOG(("Resuming the transaction, we got credentials from user"));
  4330     mTransactionPump->Resume();
  4332     return NS_OK;
  4335 NS_IMETHODIMP nsHttpChannel::OnAuthCancelled(bool userCancel)
  4337     LOG(("nsHttpChannel::OnAuthCancelled [this=%p]", this));
  4339     if (mTransactionPump) {
  4340         // If the channel is trying to authenticate to a proxy and
  4341         // that was canceled we cannot show the http response body
  4342         // from the 40x as that might mislead the user into thinking
  4343         // it was a end host response instead of a proxy reponse.
  4344         // This must check explicitly whether a proxy auth was being done
  4345         // because we do want to show the content if this is an error from
  4346         // the origin server.
  4347         if (mProxyAuthPending)
  4348             Cancel(NS_ERROR_PROXY_CONNECTION_REFUSED);
  4350         // ensure call of OnStartRequest of the current listener here,
  4351         // it would not be called otherwise at all
  4352         nsresult rv = CallOnStartRequest();
  4354         // drop mAuthRetryPending flag and resume the transaction
  4355         // this resumes load of the unauthenticated content data (which
  4356         // may have been canceled if we don't want to show it)
  4357         mAuthRetryPending = false;
  4358         LOG(("Resuming the transaction, user cancelled the auth dialog"));
  4359         mTransactionPump->Resume();
  4361         if (NS_FAILED(rv))
  4362             mTransactionPump->Cancel(rv);
  4365     mProxyAuthPending = false;
  4366     return NS_OK;
  4369 //-----------------------------------------------------------------------------
  4370 // nsHttpChannel::nsISupports
  4371 //-----------------------------------------------------------------------------
  4373 NS_IMPL_ADDREF_INHERITED(nsHttpChannel, HttpBaseChannel)
  4374 NS_IMPL_RELEASE_INHERITED(nsHttpChannel, HttpBaseChannel)
  4376 NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
  4377     NS_INTERFACE_MAP_ENTRY(nsIRequest)
  4378     NS_INTERFACE_MAP_ENTRY(nsIChannel)
  4379     NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
  4380     NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
  4381     NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
  4382     NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
  4383     NS_INTERFACE_MAP_ENTRY(nsICachingChannel)
  4384     NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
  4385     NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
  4386     NS_INTERFACE_MAP_ENTRY(nsICacheEntryOpenCallback)
  4387     NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
  4388     NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
  4389     NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
  4390     NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
  4391     NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
  4392     NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
  4393     NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
  4394     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
  4395     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
  4396     NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
  4397     NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
  4398     NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
  4399     NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
  4400     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  4401 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
  4403 //-----------------------------------------------------------------------------
  4404 // nsHttpChannel::nsIRequest
  4405 //-----------------------------------------------------------------------------
  4407 NS_IMETHODIMP
  4408 nsHttpChannel::Cancel(nsresult status)
  4410     MOZ_ASSERT(NS_IsMainThread());
  4412     LOG(("nsHttpChannel::Cancel [this=%p status=%x]\n", this, status));
  4413     if (mCanceled) {
  4414         LOG(("  ignoring; already canceled\n"));
  4415         return NS_OK;
  4417     if (mWaitingForRedirectCallback) {
  4418         LOG(("channel canceled during wait for redirect callback"));
  4420     mCanceled = true;
  4421     mStatus = status;
  4422     if (mProxyRequest)
  4423         mProxyRequest->Cancel(status);
  4424     if (mTransaction)
  4425         gHttpHandler->CancelTransaction(mTransaction, status);
  4426     if (mTransactionPump)
  4427         mTransactionPump->Cancel(status);
  4428     mCacheInputStream.CloseAndRelease();
  4429     if (mCachePump)
  4430         mCachePump->Cancel(status);
  4431     if (mAuthProvider)
  4432         mAuthProvider->Cancel(status);
  4433     return NS_OK;
  4436 NS_IMETHODIMP
  4437 nsHttpChannel::Suspend()
  4439     NS_ENSURE_TRUE(mIsPending, NS_ERROR_NOT_AVAILABLE);
  4441     LOG(("nsHttpChannel::Suspend [this=%p]\n", this));
  4443     ++mSuspendCount;
  4445     if (mTransactionPump)
  4446         return mTransactionPump->Suspend();
  4447     if (mCachePump)
  4448         return mCachePump->Suspend();
  4450     return NS_OK;
  4453 NS_IMETHODIMP
  4454 nsHttpChannel::Resume()
  4456     NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
  4458     LOG(("nsHttpChannel::Resume [this=%p]\n", this));
  4460     if (--mSuspendCount == 0 && mCallOnResume) {
  4461         nsresult rv = AsyncCall(mCallOnResume);
  4462         mCallOnResume = nullptr;
  4463         NS_ENSURE_SUCCESS(rv, rv);
  4466     if (mTransactionPump)
  4467         return mTransactionPump->Resume();
  4468     if (mCachePump)
  4469         return mCachePump->Resume();
  4471     return NS_OK;
  4474 //-----------------------------------------------------------------------------
  4475 // nsHttpChannel::nsIChannel
  4476 //-----------------------------------------------------------------------------
  4478 NS_IMETHODIMP
  4479 nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo)
  4481     NS_ENSURE_ARG_POINTER(securityInfo);
  4482     *securityInfo = mSecurityInfo;
  4483     NS_IF_ADDREF(*securityInfo);
  4484     return NS_OK;
  4487 NS_IMETHODIMP
  4488 nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
  4490     MOZ_EVENT_TRACER_WAIT(this, "net::http::channel");
  4492     LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
  4494     NS_ENSURE_ARG_POINTER(listener);
  4495     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
  4496     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
  4498     nsresult rv;
  4500     rv = NS_CheckPortSafety(mURI);
  4501     if (NS_FAILED(rv)) {
  4502         ReleaseListeners();
  4503         return rv;
  4506     // Remember the cookie header that was set, if any
  4507     const char *cookieHeader = mRequestHead.PeekHeader(nsHttp::Cookie);
  4508     if (cookieHeader) {
  4509         mUserSetCookieHeader = cookieHeader;
  4512     AddCookiesToRequest();
  4514     // notify "http-on-opening-request" observers, but not if this is a redirect
  4515     if (!(mLoadFlags & LOAD_REPLACE)) {
  4516         gHttpHandler->OnOpeningRequest(this);
  4519     mIsPending = true;
  4520     mWasOpened = true;
  4522     mListener = listener;
  4523     mListenerContext = context;
  4525     // add ourselves to the load group.  from this point forward, we'll report
  4526     // all failures asynchronously.
  4527     if (mLoadGroup)
  4528         mLoadGroup->AddRequest(this, nullptr);
  4530     // record asyncopen time unconditionally and clear it if we
  4531     // don't want it after OnModifyRequest() weighs in. But waiting for
  4532     // that to complete would mean we don't include proxy resolution in the
  4533     // timing.
  4534     mAsyncOpenTime = TimeStamp::Now();
  4536     // the only time we would already know the proxy information at this
  4537     // point would be if we were proxying a non-http protocol like ftp
  4538     if (!mProxyInfo && NS_SUCCEEDED(ResolveProxy()))
  4539         return NS_OK;
  4541     rv = BeginConnect();
  4542     if (NS_FAILED(rv))
  4543         ReleaseListeners();
  4545     return rv;
  4548 nsresult
  4549 nsHttpChannel::BeginConnect()
  4551     LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this));
  4552     nsresult rv;
  4554     // Construct connection info object
  4555     nsAutoCString host;
  4556     int32_t port = -1;
  4557     nsAutoCString username;
  4558     bool usingSSL = false;
  4560     rv = mURI->SchemeIs("https", &usingSSL);
  4561     if (NS_SUCCEEDED(rv))
  4562         rv = mURI->GetAsciiHost(host);
  4563     if (NS_SUCCEEDED(rv))
  4564         rv = mURI->GetPort(&port);
  4565     if (NS_SUCCEEDED(rv))
  4566         mURI->GetUsername(username);
  4567     if (NS_SUCCEEDED(rv))
  4568         rv = mURI->GetAsciiSpec(mSpec);
  4569     if (NS_FAILED(rv))
  4570         return rv;
  4572     // Reject the URL if it doesn't specify a host
  4573     if (host.IsEmpty())
  4574         return NS_ERROR_MALFORMED_URI;
  4575     LOG(("host=%s port=%d\n", host.get(), port));
  4576     LOG(("uri=%s\n", mSpec.get()));
  4578     nsCOMPtr<nsProxyInfo> proxyInfo;
  4579     if (mProxyInfo)
  4580         proxyInfo = do_QueryInterface(mProxyInfo);
  4582     mConnectionInfo = new nsHttpConnectionInfo(host, port, username, proxyInfo, usingSSL);
  4584     mAuthProvider =
  4585         do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
  4586                           &rv);
  4587     if (NS_SUCCEEDED(rv))
  4588         rv = mAuthProvider->Init(this);
  4589     if (NS_FAILED(rv))
  4590         return rv;
  4592     // check to see if authorization headers should be included
  4593     mAuthProvider->AddAuthorizationHeaders();
  4595     // notify "http-on-modify-request" observers
  4596     CallOnModifyRequestObservers();
  4598     // Check to see if we should redirect this channel elsewhere by
  4599     // nsIHttpChannel.redirectTo API request
  4600     if (mAPIRedirectToURI) {
  4601         return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
  4604     // If mTimingEnabled flag is not set after OnModifyRequest() then
  4605     // clear the already recorded AsyncOpen value for consistency.
  4606     if (!mTimingEnabled)
  4607         mAsyncOpenTime = TimeStamp();
  4609     // when proxying only use the pipeline bit if ProxyPipelining() allows it.
  4610     if (!mConnectionInfo->UsingConnect() && mConnectionInfo->UsingHttpProxy()) {
  4611         mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
  4612         if (gHttpHandler->ProxyPipelining())
  4613             mCaps |= NS_HTTP_ALLOW_PIPELINING;
  4616     // if this somehow fails we can go on without it
  4617     gHttpHandler->AddConnectionHeader(&mRequestHead.Headers(), mCaps);
  4619     if (mLoadFlags & VALIDATE_ALWAYS || BYPASS_LOCAL_CACHE(mLoadFlags))
  4620         mCaps |= NS_HTTP_REFRESH_DNS;
  4622     if (!mConnectionInfo->UsingHttpProxy()) {
  4623         // Start a DNS lookup very early in case the real open is queued the DNS can
  4624         // happen in parallel. Do not do so in the presence of an HTTP proxy as
  4625         // all lookups other than for the proxy itself are done by the proxy.
  4626         //
  4627         // We keep the DNS prefetch object around so that we can retrieve
  4628         // timing information from it. There is no guarantee that we actually
  4629         // use the DNS prefetch data for the real connection, but as we keep
  4630         // this data around for 3 minutes by default, this should almost always
  4631         // be correct, and even when it isn't, the timing still represents _a_
  4632         // valid DNS lookup timing for the site, even if it is not _the_
  4633         // timing we used.
  4634         LOG(("nsHttpChannel::BeginConnect [this=%p] prefetching%s\n",
  4635              this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : ""));
  4636         mDNSPrefetch = new nsDNSPrefetch(mURI, this, mTimingEnabled);
  4637         mDNSPrefetch->PrefetchHigh(mCaps & NS_HTTP_REFRESH_DNS);
  4640     // Adjust mCaps according to our request headers:
  4641     //  - If "Connection: close" is set as a request header, then do not bother
  4642     //    trying to establish a keep-alive connection.
  4643     if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close"))
  4644         mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE | NS_HTTP_ALLOW_PIPELINING);
  4646     if (gHttpHandler->CriticalRequestPrioritization()) {
  4647         if (mLoadAsBlocking)
  4648             mCaps |= NS_HTTP_LOAD_AS_BLOCKING;
  4649         if (mLoadUnblocked)
  4650             mCaps |= NS_HTTP_LOAD_UNBLOCKED;
  4653     // Force-Reload should reset the persistent connection pool for this host
  4654     if (mLoadFlags & LOAD_FRESH_CONNECTION) {
  4655         // just the initial document resets the whole pool
  4656         if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
  4657             gHttpHandler->ConnMgr()->DoShiftReloadConnectionCleanup(mConnectionInfo);
  4659         // each sub resource gets a fresh connection
  4660         mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE | NS_HTTP_ALLOW_PIPELINING);
  4663     // We may have been cancelled already, either by on-modify-request
  4664     // listeners or by load group observers; in that case, we should
  4665     // not send the request to the server
  4666     if (mCanceled)
  4667         rv = mStatus;
  4668     else
  4669         rv = Connect();
  4670     if (NS_FAILED(rv)) {
  4671         LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled));
  4672         CloseCacheEntry(true);
  4673         AsyncAbort(rv);
  4674     } else if (mLoadFlags & LOAD_CLASSIFY_URI) {
  4675         nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
  4676         rv = classifier->Start(this);
  4677         if (NS_FAILED(rv)) {
  4678             Cancel(rv);
  4679             return rv;
  4683     return NS_OK;
  4686 //-----------------------------------------------------------------------------
  4687 // nsHttpChannel::nsIHttpChannelInternal
  4688 //-----------------------------------------------------------------------------
  4690 NS_IMETHODIMP
  4691 nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey)
  4693     ENSURE_CALLED_BEFORE_CONNECT();
  4695     LOG(("nsHttpChannel::SetupFallbackChannel [this=%p, key=%s]",
  4696          this, aFallbackKey));
  4697     mFallbackChannel = true;
  4698     mFallbackKey = aFallbackKey;
  4700     return NS_OK;
  4703 //-----------------------------------------------------------------------------
  4704 // nsHttpChannel::nsISupportsPriority
  4705 //-----------------------------------------------------------------------------
  4707 NS_IMETHODIMP
  4708 nsHttpChannel::SetPriority(int32_t value)
  4710     int16_t newValue = clamped<int32_t>(value, INT16_MIN, INT16_MAX);
  4711     if (mPriority == newValue)
  4712         return NS_OK;
  4713     mPriority = newValue;
  4714     if (mTransaction)
  4715         gHttpHandler->RescheduleTransaction(mTransaction, mPriority);
  4716     return NS_OK;
  4719 //-----------------------------------------------------------------------------
  4720 // nsHttpChannel::nsIProtocolProxyCallback
  4721 //-----------------------------------------------------------------------------
  4723 NS_IMETHODIMP
  4724 nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
  4725                                 nsIProxyInfo *pi, nsresult status)
  4727     LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%x mStatus=%x]\n",
  4728          this, pi, status, mStatus));
  4729     mProxyRequest = nullptr;
  4731     nsresult rv;
  4733     // If status is a failure code, then it means that we failed to resolve
  4734     // proxy info.  That is a non-fatal error assuming it wasn't because the
  4735     // request was canceled.  We just failover to DIRECT when proxy resolution
  4736     // fails (failure can mean that the PAC URL could not be loaded).
  4738     if (NS_SUCCEEDED(status))
  4739         mProxyInfo = pi;
  4741     if (!gHttpHandler->Active()) {
  4742         LOG(("nsHttpChannel::OnProxyAvailable [this=%p] "
  4743              "Handler no longer active.\n", this));
  4744         rv = NS_ERROR_NOT_AVAILABLE;
  4746     else {
  4747         rv = BeginConnect();
  4750     if (NS_FAILED(rv)) {
  4751         Cancel(rv);
  4752         // Calling OnStart/OnStop synchronously here would mean doing it before
  4753         // returning from AsyncOpen which is a contract violation. Do it async.
  4754         nsRefPtr<nsRunnableMethod<HttpBaseChannel> > event =
  4755             NS_NewRunnableMethod(this, &nsHttpChannel::DoNotifyListener);
  4756         rv = NS_DispatchToCurrentThread(event);
  4757         if (NS_FAILED(rv)) {
  4758             NS_WARNING("Failed To Dispatch DoNotifyListener");
  4761     return rv;
  4764 //-----------------------------------------------------------------------------
  4765 // nsHttpChannel::nsIProxiedChannel
  4766 //-----------------------------------------------------------------------------
  4768 NS_IMETHODIMP
  4769 nsHttpChannel::GetProxyInfo(nsIProxyInfo **result)
  4771     if (!mConnectionInfo)
  4772         *result = mProxyInfo;
  4773     else
  4774         *result = mConnectionInfo->ProxyInfo();
  4775     NS_IF_ADDREF(*result);
  4776     return NS_OK;
  4779 //-----------------------------------------------------------------------------
  4780 // nsHttpChannel::nsITimedChannel
  4781 //-----------------------------------------------------------------------------
  4783 NS_IMETHODIMP
  4784 nsHttpChannel::GetDomainLookupStart(TimeStamp* _retval) {
  4785     if (mDNSPrefetch && mDNSPrefetch->TimingsValid())
  4786         *_retval = mDNSPrefetch->StartTimestamp();
  4787     else if (mTransaction)
  4788         *_retval = mTransaction->Timings().domainLookupStart;
  4789     else
  4790         *_retval = mTransactionTimings.domainLookupStart;
  4791     return NS_OK;
  4794 NS_IMETHODIMP
  4795 nsHttpChannel::GetDomainLookupEnd(TimeStamp* _retval) {
  4796     if (mDNSPrefetch && mDNSPrefetch->TimingsValid())
  4797         *_retval = mDNSPrefetch->EndTimestamp();
  4798     else if (mTransaction)
  4799         *_retval = mTransaction->Timings().domainLookupEnd;
  4800     else
  4801         *_retval = mTransactionTimings.domainLookupEnd;
  4802     return NS_OK;
  4805 NS_IMETHODIMP
  4806 nsHttpChannel::GetConnectStart(TimeStamp* _retval) {
  4807     if (mTransaction)
  4808         *_retval = mTransaction->Timings().connectStart;
  4809     else
  4810         *_retval = mTransactionTimings.connectStart;
  4811     return NS_OK;
  4814 NS_IMETHODIMP
  4815 nsHttpChannel::GetConnectEnd(TimeStamp* _retval) {
  4816     if (mTransaction)
  4817         *_retval = mTransaction->Timings().connectEnd;
  4818     else
  4819         *_retval = mTransactionTimings.connectEnd;
  4820     return NS_OK;
  4823 NS_IMETHODIMP
  4824 nsHttpChannel::GetRequestStart(TimeStamp* _retval) {
  4825     if (mTransaction)
  4826         *_retval = mTransaction->Timings().requestStart;
  4827     else
  4828         *_retval = mTransactionTimings.requestStart;
  4829     return NS_OK;
  4832 NS_IMETHODIMP
  4833 nsHttpChannel::GetResponseStart(TimeStamp* _retval) {
  4834     if (mTransaction)
  4835         *_retval = mTransaction->Timings().responseStart;
  4836     else
  4837         *_retval = mTransactionTimings.responseStart;
  4838     return NS_OK;
  4841 NS_IMETHODIMP
  4842 nsHttpChannel::GetResponseEnd(TimeStamp* _retval) {
  4843     if (mTransaction)
  4844         *_retval = mTransaction->Timings().responseEnd;
  4845     else
  4846         *_retval = mTransactionTimings.responseEnd;
  4847     return NS_OK;
  4850 //-----------------------------------------------------------------------------
  4851 // nsHttpChannel::nsIHttpAuthenticableChannel
  4852 //-----------------------------------------------------------------------------
  4854 NS_IMETHODIMP
  4855 nsHttpChannel::GetIsSSL(bool *aIsSSL)
  4857     *aIsSSL = mConnectionInfo->UsingSSL();
  4858     return NS_OK;
  4861 NS_IMETHODIMP
  4862 nsHttpChannel::GetProxyMethodIsConnect(bool *aProxyMethodIsConnect)
  4864     *aProxyMethodIsConnect = mConnectionInfo->UsingConnect();
  4865     return NS_OK;
  4868 NS_IMETHODIMP
  4869 nsHttpChannel::GetServerResponseHeader(nsACString &value)
  4871     if (!mResponseHead)
  4872         return NS_ERROR_NOT_AVAILABLE;
  4873     return mResponseHead->GetHeader(nsHttp::Server, value);
  4876 NS_IMETHODIMP
  4877 nsHttpChannel::GetProxyChallenges(nsACString &value)
  4879     if (!mResponseHead)
  4880         return NS_ERROR_UNEXPECTED;
  4881     return mResponseHead->GetHeader(nsHttp::Proxy_Authenticate, value);
  4884 NS_IMETHODIMP
  4885 nsHttpChannel::GetWWWChallenges(nsACString &value)
  4887     if (!mResponseHead)
  4888         return NS_ERROR_UNEXPECTED;
  4889     return mResponseHead->GetHeader(nsHttp::WWW_Authenticate, value);
  4892 NS_IMETHODIMP
  4893 nsHttpChannel::SetProxyCredentials(const nsACString &value)
  4895     return mRequestHead.SetHeader(nsHttp::Proxy_Authorization, value);
  4898 NS_IMETHODIMP
  4899 nsHttpChannel::SetWWWCredentials(const nsACString &value)
  4901     return mRequestHead.SetHeader(nsHttp::Authorization, value);
  4904 //-----------------------------------------------------------------------------
  4905 // Methods that nsIHttpAuthenticableChannel dupes from other IDLs, which we
  4906 // get from HttpBaseChannel, must be explicitly forwarded, because C++ sucks.
  4907 //
  4909 NS_IMETHODIMP
  4910 nsHttpChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
  4912     return HttpBaseChannel::GetLoadFlags(aLoadFlags);
  4915 NS_IMETHODIMP
  4916 nsHttpChannel::GetURI(nsIURI **aURI)
  4918     return HttpBaseChannel::GetURI(aURI);
  4921 NS_IMETHODIMP
  4922 nsHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
  4924     return HttpBaseChannel::GetNotificationCallbacks(aCallbacks);
  4927 NS_IMETHODIMP
  4928 nsHttpChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
  4930     return HttpBaseChannel::GetLoadGroup(aLoadGroup);
  4933 NS_IMETHODIMP
  4934 nsHttpChannel::GetRequestMethod(nsACString& aMethod)
  4936     return HttpBaseChannel::GetRequestMethod(aMethod);
  4940 //-----------------------------------------------------------------------------
  4941 // nsHttpChannel::nsIRequestObserver
  4942 //-----------------------------------------------------------------------------
  4944 NS_IMETHODIMP
  4945 nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
  4947     PROFILER_LABEL("nsHttpChannel", "OnStartRequest");
  4948     if (!(mCanceled || NS_FAILED(mStatus))) {
  4949         // capture the request's status, so our consumers will know ASAP of any
  4950         // connection failures, etc - bug 93581
  4951         request->GetStatus(&mStatus);
  4954     LOG(("nsHttpChannel::OnStartRequest [this=%p request=%p status=%x]\n",
  4955         this, request, mStatus));
  4957     // Make sure things are what we expect them to be...
  4958     MOZ_ASSERT(request == mCachePump || request == mTransactionPump,
  4959                "Unexpected request");
  4960     MOZ_ASSERT(!(mTransactionPump && mCachePump) || mCachedContentIsPartial,
  4961                "If we have both pumps, the cache content must be partial");
  4963     if (!mSecurityInfo && !mCachePump && mTransaction) {
  4964         // grab the security info from the connection object; the transaction
  4965         // is guaranteed to own a reference to the connection.
  4966         mSecurityInfo = mTransaction->SecurityInfo();
  4969     // don't enter this block if we're reading from the cache...
  4970     if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) {
  4971         // mTransactionPump doesn't hit OnInputStreamReady and call this until
  4972         // all of the response headers have been acquired, so we can take ownership
  4973         // of them from the transaction.
  4974         mResponseHead = mTransaction->TakeResponseHead();
  4975         // the response head may be null if the transaction was cancelled.  in
  4976         // which case we just need to call OnStartRequest/OnStopRequest.
  4977         if (mResponseHead)
  4978             return ProcessResponse();
  4980         NS_WARNING("No response head in OnStartRequest");
  4983     // cache file could be deleted on our behalf, reload from network here.
  4984     if (mCacheEntry && mCachePump && CACHE_FILE_GONE(mStatus)) {
  4985         LOG(("  cache file gone, reloading from server"));
  4986         mCacheEntry->AsyncDoom(nullptr);
  4987         nsresult rv = StartRedirectChannelToURI(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
  4988         if (NS_SUCCEEDED(rv))
  4989             return NS_OK;
  4992     // avoid crashing if mListener happens to be null...
  4993     if (!mListener) {
  4994         NS_NOTREACHED("mListener is null");
  4995         return NS_OK;
  4998     // on proxy errors, try to failover
  4999     if (mConnectionInfo->ProxyInfo() &&
  5000        (mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
  5001         mStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
  5002         mStatus == NS_ERROR_NET_TIMEOUT)) {
  5004         PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
  5005         if (NS_SUCCEEDED(ProxyFailover()))
  5006             return NS_OK;
  5007         PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
  5010     return ContinueOnStartRequest2(NS_OK);
  5013 nsresult
  5014 nsHttpChannel::ContinueOnStartRequest1(nsresult result)
  5016     // Success indicates we passed ProxyFailover, in that case we must not continue
  5017     // with this code chain.
  5018     if (NS_SUCCEEDED(result))
  5019         return NS_OK;
  5021     return ContinueOnStartRequest2(result);
  5024 nsresult
  5025 nsHttpChannel::ContinueOnStartRequest2(nsresult result)
  5027     // on other request errors, try to fall back
  5028     if (NS_FAILED(mStatus)) {
  5029         PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
  5030         bool waitingForRedirectCallback;
  5031         ProcessFallback(&waitingForRedirectCallback);
  5032         if (waitingForRedirectCallback)
  5033             return NS_OK;
  5034         PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
  5037     return ContinueOnStartRequest3(NS_OK);
  5040 nsresult
  5041 nsHttpChannel::ContinueOnStartRequest3(nsresult result)
  5043     if (mFallingBack)
  5044         return NS_OK;
  5046     return CallOnStartRequest();
  5049 NS_IMETHODIMP
  5050 nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
  5052     PROFILER_LABEL("network", "nsHttpChannel::OnStopRequest");
  5053     LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%x]\n",
  5054         this, request, status));
  5056     if (mTimingEnabled && request == mCachePump) {
  5057         mCacheReadEnd = TimeStamp::Now();
  5060     // allow content to be cached if it was loaded successfully (bug #482935)
  5061     bool contentComplete = NS_SUCCEEDED(status);
  5063     // honor the cancelation status even if the underlying transaction completed.
  5064     if (mCanceled || NS_FAILED(mStatus))
  5065         status = mStatus;
  5067     if (mCachedContentIsPartial) {
  5068         if (NS_SUCCEEDED(status)) {
  5069             // mTransactionPump should be suspended
  5070             MOZ_ASSERT(request != mTransactionPump,
  5071                        "byte-range transaction finished prematurely");
  5073             if (request == mCachePump) {
  5074                 bool streamDone;
  5075                 status = OnDoneReadingPartialCacheEntry(&streamDone);
  5076                 if (NS_SUCCEEDED(status) && !streamDone)
  5077                     return status;
  5078                 // otherwise, fall through and fire OnStopRequest...
  5080             else if (request == mTransactionPump) {
  5081                 MOZ_ASSERT(mConcurentCacheAccess);
  5083             else
  5084                 NS_NOTREACHED("unexpected request");
  5086         // Do not to leave the transaction in a suspended state in error cases.
  5087         if (NS_FAILED(status) && mTransaction)
  5088             gHttpHandler->CancelTransaction(mTransaction, status);
  5091     if (mTransaction) {
  5092         // determine if we should call DoAuthRetry
  5093         bool authRetry = mAuthRetryPending && NS_SUCCEEDED(status);
  5095         //
  5096         // grab references to connection in case we need to retry an
  5097         // authentication request over it or use it for an upgrade
  5098         // to another protocol.
  5099         //
  5100         // this code relies on the code in nsHttpTransaction::Close, which
  5101         // tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to
  5102         // keep the connection around after the transaction is finished.
  5103         //
  5104         nsRefPtr<nsAHttpConnection> conn;
  5105         if (authRetry && (mCaps & NS_HTTP_STICKY_CONNECTION)) {
  5106             conn = mTransaction->GetConnectionReference();
  5107             // This is so far a workaround to fix leak when reusing unpersistent
  5108             // connection for authentication retry. See bug 459620 comment 4
  5109             // for details.
  5110             if (conn && !conn->IsPersistent())
  5111                 conn = nullptr;
  5114         nsRefPtr<nsAHttpConnection> stickyConn;
  5115         if (mCaps & NS_HTTP_STICKY_CONNECTION)
  5116             stickyConn = mTransaction->GetConnectionReference();
  5118         // at this point, we're done with the transaction
  5119         mTransactionTimings = mTransaction->Timings();
  5120         mTransaction = nullptr;
  5121         mTransactionPump = nullptr;
  5123         // We no longer need the dns prefetch object
  5124         if (mDNSPrefetch && mDNSPrefetch->TimingsValid()) {
  5125             mTransactionTimings.domainLookupStart =
  5126                 mDNSPrefetch->StartTimestamp();
  5127             mTransactionTimings.domainLookupEnd =
  5128                 mDNSPrefetch->EndTimestamp();
  5130         mDNSPrefetch = nullptr;
  5132         // handle auth retry...
  5133         if (authRetry) {
  5134             mAuthRetryPending = false;
  5135             status = DoAuthRetry(conn);
  5136             if (NS_SUCCEEDED(status))
  5137                 return NS_OK;
  5140         // If DoAuthRetry failed, or if we have been cancelled since showing
  5141         // the auth. dialog, then we need to send OnStartRequest now
  5142         if (authRetry || (mAuthRetryPending && NS_FAILED(status))) {
  5143             MOZ_ASSERT(NS_FAILED(status), "should have a failure code here");
  5144             // NOTE: since we have a failure status, we can ignore the return
  5145             // value from onStartRequest.
  5146             if (mListener) {
  5147                 mListener->OnStartRequest(this, mListenerContext);
  5148             } else {
  5149                 NS_WARNING("OnStartRequest skipped because of null listener");
  5153         // if this transaction has been replaced, then bail.
  5154         if (mTransactionReplaced)
  5155             return NS_OK;
  5157         if (mUpgradeProtocolCallback && stickyConn &&
  5158             mResponseHead && mResponseHead->Status() == 101) {
  5159             gHttpHandler->ConnMgr()->CompleteUpgrade(stickyConn,
  5160                                                      mUpgradeProtocolCallback);
  5164     mIsPending = false;
  5166     // if needed, check cache entry has all data we expect
  5167     if (mCacheEntry && mCachePump &&
  5168         mConcurentCacheAccess && contentComplete) {
  5169         int64_t size, contentLength;
  5170         nsresult rv = CheckPartial(mCacheEntry, &size, &contentLength);
  5171         if (NS_SUCCEEDED(rv)) {
  5172             if (size == int64_t(-1)) {
  5173                 // mayhemer TODO - we have to restart read from cache here at the size offset
  5174                 MOZ_ASSERT(false);
  5175                 LOG(("  cache entry write is still in progress, but we just "
  5176                      "finished reading the cache entry"));
  5178             else if (contentLength != int64_t(-1) && contentLength != size) {
  5179                 LOG(("  concurrent cache entry write has been interrupted"));
  5180                 mCachedResponseHead = mResponseHead;
  5181                 rv = MaybeSetupByteRangeRequest(size, contentLength);
  5182                 if (NS_SUCCEEDED(rv) && mIsPartialRequest) {
  5183                     // Prevent read from cache again
  5184                     mCachedContentIsValid = 0;
  5185                     mCachedContentIsPartial = 1;
  5187                     // Perform the range request
  5188                     rv = ContinueConnect();
  5189                     if (NS_SUCCEEDED(rv)) {
  5190                         LOG(("  performing range request"));
  5191                         mCachePump = nullptr;
  5192                         return NS_OK;
  5193                     } else {
  5194                         LOG(("  but range request perform failed 0x%08x", rv));
  5195                         status = NS_ERROR_NET_INTERRUPT;
  5198                 else {
  5199                     LOG(("  but range request setup failed rv=0x%08x, failing load", rv));
  5205     mStatus = status;
  5207     // perform any final cache operations before we close the cache entry.
  5208     if (mCacheEntry && mRequestTimeInitialized) {
  5209         bool writeAccess;
  5210         // New implementation just returns value of the !mCacheEntryIsReadOnly flag passed in.
  5211         // Old implementation checks on nsICache::ACCESS_WRITE flag.
  5212         mCacheEntry->HasWriteAccess(!mCacheEntryIsReadOnly, &writeAccess);
  5213         if (writeAccess) {
  5214             FinalizeCacheEntry();
  5218     // Register entry to the Performance resource timing
  5219     nsPerformance* documentPerformance = GetPerformance();
  5220     if (documentPerformance) {
  5221         documentPerformance->AddEntry(this, this);
  5224     if (mListener) {
  5225         LOG(("  calling OnStopRequest\n"));
  5226         mListener->OnStopRequest(this, mListenerContext, status);
  5229     MOZ_EVENT_TRACER_DONE(this, "net::http::channel");
  5231     CloseCacheEntry(!contentComplete);
  5233     if (mOfflineCacheEntry)
  5234         CloseOfflineCacheEntry();
  5236     if (mLoadGroup)
  5237         mLoadGroup->RemoveRequest(this, nullptr, status);
  5239     // We don't need this info anymore
  5240     CleanRedirectCacheChainIfNecessary();
  5242     ReleaseListeners();
  5244     return NS_OK;
  5247 //-----------------------------------------------------------------------------
  5248 // nsHttpChannel::nsIStreamListener
  5249 //-----------------------------------------------------------------------------
  5251 class OnTransportStatusAsyncEvent : public nsRunnable
  5253 public:
  5254     OnTransportStatusAsyncEvent(nsITransportEventSink* aEventSink,
  5255                                 nsresult aTransportStatus,
  5256                                 uint64_t aProgress,
  5257                                 uint64_t aProgressMax)
  5258     : mEventSink(aEventSink)
  5259     , mTransportStatus(aTransportStatus)
  5260     , mProgress(aProgress)
  5261     , mProgressMax(aProgressMax)
  5263         MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be created on main thread");
  5266     NS_IMETHOD Run()
  5268         MOZ_ASSERT(NS_IsMainThread(), "Should run on main thread");
  5269         if (mEventSink) {
  5270             mEventSink->OnTransportStatus(nullptr, mTransportStatus,
  5271                                           mProgress, mProgressMax);
  5273         return NS_OK;
  5275 private:
  5276     nsCOMPtr<nsITransportEventSink> mEventSink;
  5277     nsresult mTransportStatus;
  5278     uint64_t mProgress;
  5279     uint64_t mProgressMax;
  5280 };
  5282 NS_IMETHODIMP
  5283 nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
  5284                                nsIInputStream *input,
  5285                                uint64_t offset, uint32_t count)
  5287     PROFILER_LABEL("network", "nsHttpChannel::OnDataAvailable");
  5288     LOG(("nsHttpChannel::OnDataAvailable [this=%p request=%p offset=%llu count=%u]\n",
  5289         this, request, offset, count));
  5291     // don't send out OnDataAvailable notifications if we've been canceled.
  5292     if (mCanceled)
  5293         return mStatus;
  5295     MOZ_ASSERT(mResponseHead, "No response head in ODA!!");
  5297     MOZ_ASSERT(!(mCachedContentIsPartial && (request == mTransactionPump)),
  5298                "transaction pump not suspended");
  5300     if (mAuthRetryPending || (request == mTransactionPump && mTransactionReplaced)) {
  5301         uint32_t n;
  5302         return input->ReadSegments(NS_DiscardSegment, nullptr, count, &n);
  5305     if (mListener) {
  5306         //
  5307         // synthesize transport progress event.  we do this here since we want
  5308         // to delay OnProgress events until we start streaming data.  this is
  5309         // crucially important since it impacts the lock icon (see bug 240053).
  5310         //
  5311         nsresult transportStatus;
  5312         if (request == mCachePump)
  5313             transportStatus = NS_NET_STATUS_READING;
  5314         else
  5315             transportStatus = NS_NET_STATUS_RECEIVING_FROM;
  5317         // mResponseHead may reference new or cached headers, but either way it
  5318         // holds our best estimate of the total content length.  Even in the case
  5319         // of a byte range request, the content length stored in the cached
  5320         // response headers is what we want to use here.
  5322         uint64_t progressMax(uint64_t(mResponseHead->ContentLength()));
  5323         uint64_t progress = mLogicalOffset + uint64_t(count);
  5325         if (progress > progressMax)
  5326             NS_WARNING("unexpected progress values - "
  5327                        "is server exceeding content length?");
  5329         if (NS_IsMainThread()) {
  5330             OnTransportStatus(nullptr, transportStatus, progress, progressMax);
  5331         } else {
  5332             nsresult rv = NS_DispatchToMainThread(
  5333                 new OnTransportStatusAsyncEvent(this, transportStatus,
  5334                                                 progress, progressMax));
  5335             NS_ENSURE_SUCCESS(rv, rv);
  5338         //
  5339         // we have to manually keep the logical offset of the stream up-to-date.
  5340         // we cannot depend solely on the offset provided, since we may have
  5341         // already streamed some data from another source (see, for example,
  5342         // OnDoneReadingPartialCacheEntry).
  5343         //
  5344         if (!mLogicalOffset)
  5345             MOZ_EVENT_TRACER_EXEC(this, "net::http::channel");
  5347         int64_t offsetBefore = 0;
  5348         nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(input);
  5349         if (seekable && NS_FAILED(seekable->Tell(&offsetBefore))) {
  5350             seekable = nullptr;
  5353         nsresult rv =  mListener->OnDataAvailable(this,
  5354                                                   mListenerContext,
  5355                                                   input,
  5356                                                   mLogicalOffset,
  5357                                                   count);
  5358         if (NS_SUCCEEDED(rv)) {
  5359             // by contract mListener must read all of "count" bytes, but
  5360             // nsInputStreamPump is tolerant to seekable streams that violate that
  5361             // and it will redeliver incompletely read data. So we need to do
  5362             // the same thing when updating the progress counter to stay in sync.
  5363             int64_t offsetAfter, delta;
  5364             if (seekable && NS_SUCCEEDED(seekable->Tell(&offsetAfter))) {
  5365                 delta = offsetAfter - offsetBefore;
  5366                 if (delta != count) {
  5367                     count = delta;
  5369                     NS_WARNING("Listener OnDataAvailable contract violation");
  5370                     nsCOMPtr<nsIConsoleService> consoleService =
  5371                         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
  5372                     nsAutoString message
  5373                         (NS_LITERAL_STRING(
  5374                         "http channel Listener OnDataAvailable contract violation"));
  5375                     if (consoleService) {
  5376                         consoleService->LogStringMessage(message.get());
  5380             mLogicalOffset += count;
  5383         return rv;
  5386     return NS_ERROR_ABORT;
  5389 //-----------------------------------------------------------------------------
  5390 // nsHttpChannel::nsIThreadRetargetableRequest
  5391 //-----------------------------------------------------------------------------
  5393 NS_IMETHODIMP
  5394 nsHttpChannel::RetargetDeliveryTo(nsIEventTarget* aNewTarget)
  5396     MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only");
  5398     NS_ENSURE_ARG(aNewTarget);
  5399     if (aNewTarget == NS_GetCurrentThread()) {
  5400         NS_WARNING("Retargeting delivery to same thread");
  5401         return NS_OK;
  5403     NS_ENSURE_TRUE(mTransactionPump || mCachePump, NS_ERROR_NOT_AVAILABLE);
  5405     nsresult rv = NS_OK;
  5406     // If both cache pump and transaction pump exist, we're probably dealing
  5407     // with partially cached content. So, we must be able to retarget both.
  5408     nsCOMPtr<nsIThreadRetargetableRequest> retargetableCachePump;
  5409     nsCOMPtr<nsIThreadRetargetableRequest> retargetableTransactionPump;
  5410     if (mCachePump) {
  5411         retargetableCachePump = do_QueryObject(mCachePump);
  5412         // nsInputStreamPump should implement this interface.
  5413         MOZ_ASSERT(retargetableCachePump);
  5414         rv = retargetableCachePump->RetargetDeliveryTo(aNewTarget);
  5416     if (NS_SUCCEEDED(rv) && mTransactionPump) {
  5417         retargetableTransactionPump = do_QueryObject(mTransactionPump);
  5418         // nsInputStreamPump should implement this interface.
  5419         MOZ_ASSERT(retargetableTransactionPump);
  5420         rv = retargetableTransactionPump->RetargetDeliveryTo(aNewTarget);
  5422         // If retarget fails for transaction pump, we must restore mCachePump.
  5423         if (NS_FAILED(rv) && retargetableCachePump) {
  5424             nsCOMPtr<nsIThread> mainThread;
  5425             rv = NS_GetMainThread(getter_AddRefs(mainThread));
  5426             NS_ENSURE_SUCCESS(rv, rv);
  5427             rv = retargetableCachePump->RetargetDeliveryTo(mainThread);
  5430     return rv;
  5433 //-----------------------------------------------------------------------------
  5434 // nsHttpChannel::nsThreadRetargetableStreamListener
  5435 //-----------------------------------------------------------------------------
  5437 NS_IMETHODIMP
  5438 nsHttpChannel::CheckListenerChain()
  5440     NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!");
  5441     nsresult rv = NS_OK;
  5442     nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
  5443         do_QueryInterface(mListener, &rv);
  5444     if (retargetableListener) {
  5445         rv = retargetableListener->CheckListenerChain();
  5447     return rv;
  5450 //-----------------------------------------------------------------------------
  5451 // nsHttpChannel::nsITransportEventSink
  5452 //-----------------------------------------------------------------------------
  5454 NS_IMETHODIMP
  5455 nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
  5456                                  uint64_t progress, uint64_t progressMax)
  5458     MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread only");
  5459     // cache the progress sink so we don't have to query for it each time.
  5460     if (!mProgressSink)
  5461         GetCallback(mProgressSink);
  5463     if (status == NS_NET_STATUS_CONNECTED_TO ||
  5464         status == NS_NET_STATUS_WAITING_FOR) {
  5465         nsCOMPtr<nsISocketTransport> socketTransport =
  5466             do_QueryInterface(trans);
  5467         if (socketTransport) {
  5468             socketTransport->GetSelfAddr(&mSelfAddr);
  5469             socketTransport->GetPeerAddr(&mPeerAddr);
  5473     // block socket status event after Cancel or OnStopRequest has been called.
  5474     if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && !(mLoadFlags & LOAD_BACKGROUND)) {
  5475         LOG(("sending status notification [this=%p status=%x progress=%llu/%llu]\n",
  5476             this, status, progress, progressMax));
  5478         nsAutoCString host;
  5479         mURI->GetHost(host);
  5480         mProgressSink->OnStatus(this, nullptr, status,
  5481                                 NS_ConvertUTF8toUTF16(host).get());
  5483         if (progress > 0) {
  5484             MOZ_ASSERT(progress <= progressMax, "unexpected progress values");
  5485             // Try to get mProgressSink if it was nulled out during OnStatus.
  5486             if (!mProgressSink) {
  5487                 GetCallback(mProgressSink);
  5489             if (mProgressSink) {
  5490                 mProgressSink->OnProgress(this, nullptr, progress, progressMax);
  5494 #ifdef DEBUG
  5495     else
  5496         LOG(("skipping status notification [this=%p sink=%p pending=%u background=%x]\n",
  5497             this, mProgressSink.get(), mIsPending, (mLoadFlags & LOAD_BACKGROUND)));
  5498 #endif
  5500     return NS_OK;
  5503 //-----------------------------------------------------------------------------
  5504 // nsHttpChannel::nsICacheInfoChannel
  5505 //-----------------------------------------------------------------------------
  5507 NS_IMETHODIMP
  5508 nsHttpChannel::IsFromCache(bool *value)
  5510     if (!mIsPending)
  5511         return NS_ERROR_NOT_AVAILABLE;
  5513     // return false if reading a partial cache entry; the data isn't entirely
  5514     // from the cache!
  5516     *value = (mCachePump || (mLoadFlags & LOAD_ONLY_IF_MODIFIED)) &&
  5517               mCachedContentIsValid && !mCachedContentIsPartial;
  5519     return NS_OK;
  5522 NS_IMETHODIMP
  5523 nsHttpChannel::GetCacheTokenExpirationTime(uint32_t *_retval)
  5525     NS_ENSURE_ARG_POINTER(_retval);
  5526     if (!mCacheEntry)
  5527         return NS_ERROR_NOT_AVAILABLE;
  5529     return mCacheEntry->GetExpirationTime(_retval);
  5532 NS_IMETHODIMP
  5533 nsHttpChannel::GetCacheTokenCachedCharset(nsACString &_retval)
  5535     nsresult rv;
  5537     if (!mCacheEntry)
  5538         return NS_ERROR_NOT_AVAILABLE;
  5540     nsXPIDLCString cachedCharset;
  5541     rv = mCacheEntry->GetMetaDataElement("charset",
  5542                                          getter_Copies(cachedCharset));
  5543     if (NS_SUCCEEDED(rv))
  5544         _retval = cachedCharset;
  5546     return rv;
  5549 NS_IMETHODIMP
  5550 nsHttpChannel::SetCacheTokenCachedCharset(const nsACString &aCharset)
  5552     if (!mCacheEntry)
  5553         return NS_ERROR_NOT_AVAILABLE;
  5555     return mCacheEntry->SetMetaDataElement("charset",
  5556                                            PromiseFlatCString(aCharset).get());
  5560 NS_IMETHODIMP
  5561 nsHttpChannel::GetCacheDomain(nsACString &value)
  5563     value = mCacheDomain;
  5565     return NS_OK;
  5568 NS_IMETHODIMP
  5569 nsHttpChannel::SetCacheDomain(const nsACString &value)
  5571     mCacheDomain = value;
  5573     return NS_OK;
  5576 //-----------------------------------------------------------------------------
  5577 // nsHttpChannel::nsICachingChannel
  5578 //-----------------------------------------------------------------------------
  5580 NS_IMETHODIMP
  5581 nsHttpChannel::GetCacheToken(nsISupports **token)
  5583     NS_ENSURE_ARG_POINTER(token);
  5584     if (!mCacheEntry)
  5585         return NS_ERROR_NOT_AVAILABLE;
  5586     return CallQueryInterface(mCacheEntry, token);
  5589 NS_IMETHODIMP
  5590 nsHttpChannel::SetCacheToken(nsISupports *token)
  5592     return NS_ERROR_NOT_IMPLEMENTED;
  5595 NS_IMETHODIMP
  5596 nsHttpChannel::GetOfflineCacheToken(nsISupports **token)
  5598     NS_ENSURE_ARG_POINTER(token);
  5599     if (!mOfflineCacheEntry)
  5600         return NS_ERROR_NOT_AVAILABLE;
  5601     return CallQueryInterface(mOfflineCacheEntry, token);
  5604 NS_IMETHODIMP
  5605 nsHttpChannel::SetOfflineCacheToken(nsISupports *token)
  5607     return NS_ERROR_NOT_IMPLEMENTED;
  5610 class nsHttpChannelCacheKey MOZ_FINAL : public nsISupportsPRUint32,
  5611                                         public nsISupportsCString
  5613     NS_DECL_ISUPPORTS
  5615     NS_DECL_NSISUPPORTSPRIMITIVE
  5616     NS_FORWARD_NSISUPPORTSPRUINT32(mSupportsPRUint32->)
  5618     // Both interfaces declares toString method with the same signature.
  5619     // Thus we have to delegate only to nsISupportsPRUint32 implementation.
  5620     NS_IMETHOD GetData(nsACString & aData)
  5622         return mSupportsCString->GetData(aData);
  5624     NS_IMETHOD SetData(const nsACString & aData)
  5626         return mSupportsCString->SetData(aData);
  5629 public:
  5630     nsresult SetData(uint32_t aPostID, const nsACString& aKey);
  5632 protected:
  5633     nsCOMPtr<nsISupportsPRUint32> mSupportsPRUint32;
  5634     nsCOMPtr<nsISupportsCString> mSupportsCString;
  5635 };
  5637 NS_IMPL_ADDREF(nsHttpChannelCacheKey)
  5638 NS_IMPL_RELEASE(nsHttpChannelCacheKey)
  5639 NS_INTERFACE_TABLE_HEAD(nsHttpChannelCacheKey)
  5640 NS_INTERFACE_TABLE_BEGIN
  5641 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsHttpChannelCacheKey,
  5642                                    nsISupports, nsISupportsPRUint32)
  5643 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(nsHttpChannelCacheKey,
  5644                                    nsISupportsPrimitive, nsISupportsPRUint32)
  5645 NS_INTERFACE_TABLE_ENTRY(nsHttpChannelCacheKey,
  5646                          nsISupportsPRUint32)
  5647 NS_INTERFACE_TABLE_ENTRY(nsHttpChannelCacheKey,
  5648                          nsISupportsCString)
  5649 NS_INTERFACE_TABLE_END
  5650 NS_INTERFACE_TABLE_TAIL
  5652 NS_IMETHODIMP nsHttpChannelCacheKey::GetType(uint16_t *aType)
  5654     NS_ENSURE_ARG_POINTER(aType);
  5656     *aType = TYPE_PRUINT32;
  5657     return NS_OK;
  5660 nsresult nsHttpChannelCacheKey::SetData(uint32_t aPostID,
  5661                                         const nsACString& aKey)
  5663     nsresult rv;
  5665     mSupportsCString =
  5666         do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
  5667     if (NS_FAILED(rv)) return rv;
  5669     mSupportsCString->SetData(aKey);
  5670     if (NS_FAILED(rv)) return rv;
  5672     mSupportsPRUint32 =
  5673         do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
  5674     if (NS_FAILED(rv)) return rv;
  5676     mSupportsPRUint32->SetData(aPostID);
  5677     if (NS_FAILED(rv)) return rv;
  5679     return NS_OK;
  5682 NS_IMETHODIMP
  5683 nsHttpChannel::GetCacheKey(nsISupports **key)
  5685     // mayhemer: TODO - do we need this API?
  5687     nsresult rv;
  5688     NS_ENSURE_ARG_POINTER(key);
  5690     LOG(("nsHttpChannel::GetCacheKey [this=%p]\n", this));
  5692     *key = nullptr;
  5694     nsRefPtr<nsHttpChannelCacheKey> container =
  5695         new nsHttpChannelCacheKey();
  5697     if (!container)
  5698         return NS_ERROR_OUT_OF_MEMORY;
  5700     nsAutoCString cacheKey;
  5701     rv = GenerateCacheKey(mPostID, cacheKey);
  5702     if (NS_FAILED(rv)) return rv;
  5704     rv = container->SetData(mPostID, cacheKey);
  5705     if (NS_FAILED(rv)) return rv;
  5707     return CallQueryInterface(container.get(), key);
  5710 NS_IMETHODIMP
  5711 nsHttpChannel::SetCacheKey(nsISupports *key)
  5713     nsresult rv;
  5715     LOG(("nsHttpChannel::SetCacheKey [this=%p key=%p]\n", this, key));
  5717     ENSURE_CALLED_BEFORE_CONNECT();
  5719     if (!key)
  5720         mPostID = 0;
  5721     else {
  5722         // extract the post id
  5723         nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(key, &rv);
  5724         if (NS_FAILED(rv)) return rv;
  5726         rv = container->GetData(&mPostID);
  5727         if (NS_FAILED(rv)) return rv;
  5729     return NS_OK;
  5732 //-----------------------------------------------------------------------------
  5733 // nsHttpChannel::nsIResumableChannel
  5734 //-----------------------------------------------------------------------------
  5736 NS_IMETHODIMP
  5737 nsHttpChannel::ResumeAt(uint64_t aStartPos,
  5738                         const nsACString& aEntityID)
  5740     LOG(("nsHttpChannel::ResumeAt [this=%p startPos=%llu id='%s']\n",
  5741          this, aStartPos, PromiseFlatCString(aEntityID).get()));
  5742     mEntityID = aEntityID;
  5743     mStartPos = aStartPos;
  5744     mResuming = true;
  5745     return NS_OK;
  5748 nsresult
  5749 nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
  5751     LOG(("nsHttpChannel::DoAuthRetry [this=%p]\n", this));
  5753     MOZ_ASSERT(!mTransaction, "should not have a transaction");
  5754     nsresult rv;
  5756     // toggle mIsPending to allow nsIObserver implementations to modify
  5757     // the request headers (bug 95044).
  5758     mIsPending = false;
  5760     // fetch cookies, and add them to the request header.
  5761     // the server response could have included cookies that must be sent with
  5762     // this authentication attempt (bug 84794).
  5763     // TODO: save cookies from auth response and send them here (bug 572151).
  5764     AddCookiesToRequest();
  5766     // notify "http-on-modify-request" observers
  5767     CallOnModifyRequestObservers();
  5769     mIsPending = true;
  5771     // get rid of the old response headers
  5772     mResponseHead = nullptr;
  5774     // rewind the upload stream
  5775     if (mUploadStream) {
  5776         nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
  5777         if (seekable)
  5778             seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
  5781     // set sticky connection flag and disable pipelining.
  5782     mCaps |=  NS_HTTP_STICKY_CONNECTION;
  5783     mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
  5785     // and create a new one...
  5786     rv = SetupTransaction();
  5787     if (NS_FAILED(rv)) return rv;
  5789     // transfer ownership of connection to transaction
  5790     if (conn)
  5791         mTransaction->SetConnection(conn);
  5793     rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
  5794     if (NS_FAILED(rv)) return rv;
  5796     rv = mTransactionPump->AsyncRead(this, nullptr);
  5797     if (NS_FAILED(rv)) return rv;
  5799     uint32_t suspendCount = mSuspendCount;
  5800     while (suspendCount--)
  5801         mTransactionPump->Suspend();
  5803     return NS_OK;
  5806 //-----------------------------------------------------------------------------
  5807 // nsHttpChannel::nsIApplicationCacheChannel
  5808 //-----------------------------------------------------------------------------
  5810 NS_IMETHODIMP
  5811 nsHttpChannel::GetApplicationCache(nsIApplicationCache **out)
  5813     NS_IF_ADDREF(*out = mApplicationCache);
  5814     return NS_OK;
  5817 NS_IMETHODIMP
  5818 nsHttpChannel::SetApplicationCache(nsIApplicationCache *appCache)
  5820     ENSURE_CALLED_BEFORE_CONNECT();
  5822     mApplicationCache = appCache;
  5823     return NS_OK;
  5826 NS_IMETHODIMP
  5827 nsHttpChannel::GetApplicationCacheForWrite(nsIApplicationCache **out)
  5829     NS_IF_ADDREF(*out = mApplicationCacheForWrite);
  5830     return NS_OK;
  5833 NS_IMETHODIMP
  5834 nsHttpChannel::SetApplicationCacheForWrite(nsIApplicationCache *appCache)
  5836     ENSURE_CALLED_BEFORE_CONNECT();
  5838     mApplicationCacheForWrite = appCache;
  5839     return NS_OK;
  5842 NS_IMETHODIMP
  5843 nsHttpChannel::GetLoadedFromApplicationCache(bool *aLoadedFromApplicationCache)
  5845     *aLoadedFromApplicationCache = mLoadedFromApplicationCache;
  5846     return NS_OK;
  5849 NS_IMETHODIMP
  5850 nsHttpChannel::GetInheritApplicationCache(bool *aInherit)
  5852     *aInherit = mInheritApplicationCache;
  5853     return NS_OK;
  5856 NS_IMETHODIMP
  5857 nsHttpChannel::SetInheritApplicationCache(bool aInherit)
  5859     ENSURE_CALLED_BEFORE_CONNECT();
  5861     mInheritApplicationCache = aInherit;
  5862     return NS_OK;
  5865 NS_IMETHODIMP
  5866 nsHttpChannel::GetChooseApplicationCache(bool *aChoose)
  5868     *aChoose = mChooseApplicationCache;
  5869     return NS_OK;
  5872 NS_IMETHODIMP
  5873 nsHttpChannel::SetChooseApplicationCache(bool aChoose)
  5875     ENSURE_CALLED_BEFORE_CONNECT();
  5877     mChooseApplicationCache = aChoose;
  5878     return NS_OK;
  5881 nsHttpChannel::OfflineCacheEntryAsForeignMarker*
  5882 nsHttpChannel::GetOfflineCacheEntryAsForeignMarker()
  5884     if (!mApplicationCache)
  5885         return nullptr;
  5887     return new OfflineCacheEntryAsForeignMarker(mApplicationCache, mURI);
  5890 nsresult
  5891 nsHttpChannel::OfflineCacheEntryAsForeignMarker::MarkAsForeign()
  5893     nsresult rv;
  5895     nsCOMPtr<nsIURI> noRefURI;
  5896     rv = mCacheURI->CloneIgnoringRef(getter_AddRefs(noRefURI));
  5897     NS_ENSURE_SUCCESS(rv, rv);
  5899     nsAutoCString spec;
  5900     rv = noRefURI->GetAsciiSpec(spec);
  5901     NS_ENSURE_SUCCESS(rv, rv);
  5903     return mApplicationCache->MarkEntry(spec,
  5904                                         nsIApplicationCache::ITEM_FOREIGN);
  5907 NS_IMETHODIMP
  5908 nsHttpChannel::MarkOfflineCacheEntryAsForeign()
  5910     nsresult rv;
  5912     nsAutoPtr<OfflineCacheEntryAsForeignMarker> marker(
  5913         GetOfflineCacheEntryAsForeignMarker());
  5915     if (!marker)
  5916         return NS_ERROR_NOT_AVAILABLE;
  5918     rv = marker->MarkAsForeign();
  5919     NS_ENSURE_SUCCESS(rv, rv);
  5921     return NS_OK;
  5924 //-----------------------------------------------------------------------------
  5925 // nsHttpChannel::nsIAsyncVerifyRedirectCallback
  5926 //-----------------------------------------------------------------------------
  5928 nsresult
  5929 nsHttpChannel::WaitForRedirectCallback()
  5931     nsresult rv;
  5932     LOG(("nsHttpChannel::WaitForRedirectCallback [this=%p]\n", this));
  5934     if (mTransactionPump) {
  5935         rv = mTransactionPump->Suspend();
  5936         NS_ENSURE_SUCCESS(rv, rv);
  5938     if (mCachePump) {
  5939         rv = mCachePump->Suspend();
  5940         if (NS_FAILED(rv) && mTransactionPump) {
  5941 #ifdef DEBUG
  5942             nsresult resume =
  5943 #endif
  5944             mTransactionPump->Resume();
  5945             MOZ_ASSERT(NS_SUCCEEDED(resume),
  5946                        "Failed to resume transaction pump");
  5948         NS_ENSURE_SUCCESS(rv, rv);
  5951     mWaitingForRedirectCallback = true;
  5952     return NS_OK;
  5955 NS_IMETHODIMP
  5956 nsHttpChannel::OnRedirectVerifyCallback(nsresult result)
  5958     LOG(("nsHttpChannel::OnRedirectVerifyCallback [this=%p] "
  5959          "result=%x stack=%d mWaitingForRedirectCallback=%u\n",
  5960          this, result, mRedirectFuncStack.Length(), mWaitingForRedirectCallback));
  5961     MOZ_ASSERT(mWaitingForRedirectCallback,
  5962                "Someone forgot to call WaitForRedirectCallback() ?!");
  5963     mWaitingForRedirectCallback = false;
  5965     if (mCanceled && NS_SUCCEEDED(result))
  5966         result = NS_BINDING_ABORTED;
  5968     for (uint32_t i = mRedirectFuncStack.Length(); i > 0;) {
  5969         --i;
  5970         // Pop the last function pushed to the stack
  5971         nsContinueRedirectionFunc func = mRedirectFuncStack[i];
  5972         mRedirectFuncStack.RemoveElementAt(mRedirectFuncStack.Length() - 1);
  5974         // Call it with the result we got from the callback or the deeper
  5975         // function call.
  5976         result = (this->*func)(result);
  5978         // If a new function has been pushed to the stack and placed us in the
  5979         // waiting state, we need to break the chain and wait for the callback
  5980         // again.
  5981         if (mWaitingForRedirectCallback)
  5982             break;
  5985     if (NS_FAILED(result) && !mCanceled) {
  5986         // First, cancel this channel if we are in failure state to set mStatus
  5987         // and let it be propagated to pumps.
  5988         Cancel(result);
  5991     if (!mWaitingForRedirectCallback) {
  5992         // We are not waiting for the callback. At this moment we must release
  5993         // reference to the redirect target channel, otherwise we may leak.
  5994         mRedirectChannel = nullptr;
  5995         MOZ_EVENT_TRACER_DONE(this, "net::http::channel");
  5998     // We always resume the pumps here. If all functions on stack have been
  5999     // called we need OnStopRequest to be triggered, and if we broke out of the
  6000     // loop above (and are thus waiting for a new callback) the suspension
  6001     // count must be balanced in the pumps.
  6002     if (mTransactionPump)
  6003         mTransactionPump->Resume();
  6004     if (mCachePump)
  6005         mCachePump->Resume();
  6007     return result;
  6010 void
  6011 nsHttpChannel::PushRedirectAsyncFunc(nsContinueRedirectionFunc func)
  6013     mRedirectFuncStack.AppendElement(func);
  6016 void
  6017 nsHttpChannel::PopRedirectAsyncFunc(nsContinueRedirectionFunc func)
  6019     MOZ_ASSERT(func == mRedirectFuncStack[mRedirectFuncStack.Length() - 1],
  6020                "Trying to pop wrong method from redirect async stack!");
  6022     mRedirectFuncStack.TruncateLength(mRedirectFuncStack.Length() - 1);
  6025 //-----------------------------------------------------------------------------
  6026 // nsIDNSListener functions
  6027 //-----------------------------------------------------------------------------
  6029 NS_IMETHODIMP
  6030 nsHttpChannel::OnLookupComplete(nsICancelable *request,
  6031                                 nsIDNSRecord  *rec,
  6032                                 nsresult       status)
  6034     MOZ_ASSERT(NS_IsMainThread(), "Expecting DNS callback on main thread.");
  6036     LOG(("nsHttpChannel::OnLookupComplete [this=%p] prefetch complete%s: "
  6037          "%s status[0x%x]\n",
  6038          this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "",
  6039          NS_SUCCEEDED(status) ? "success" : "failure", status));
  6041     // We no longer need the dns prefetch object. Note: mDNSPrefetch could be
  6042     // validly null if OnStopRequest has already been called.
  6043     if (mDNSPrefetch && mDNSPrefetch->TimingsValid()) {
  6044         mTransactionTimings.domainLookupStart =
  6045             mDNSPrefetch->StartTimestamp();
  6046         mTransactionTimings.domainLookupEnd =
  6047             mDNSPrefetch->EndTimestamp();
  6049     mDNSPrefetch = nullptr;
  6051     // Unset DNS cache refresh if it was requested,
  6052     if (mCaps & NS_HTTP_REFRESH_DNS) {
  6053         mCaps &= ~NS_HTTP_REFRESH_DNS;
  6054         if (mTransaction) {
  6055             mTransaction->SetDNSWasRefreshed();
  6059     return NS_OK;
  6062 //-----------------------------------------------------------------------------
  6063 // nsHttpChannel internal functions
  6064 //-----------------------------------------------------------------------------
  6066 void
  6067 nsHttpChannel::MaybeInvalidateCacheEntryForSubsequentGet()
  6069     // See RFC 2616 section 5.1.1. These are considered valid
  6070     // methods which DO NOT invalidate cache-entries for the
  6071     // referred resource. POST, PUT and DELETE as well as any
  6072     // other method not listed here will potentially invalidate
  6073     // any cached copy of the resource
  6074     if (mRequestHead.IsGet() || mRequestHead.IsOptions() ||
  6075         mRequestHead.IsHead() || mRequestHead.IsTrace() ||
  6076         mRequestHead.IsConnect()) {
  6077         return;
  6080     // Invalidate the request-uri.
  6081 #ifdef PR_LOGGING
  6082     nsAutoCString key;
  6083     mURI->GetAsciiSpec(key);
  6084     LOG(("MaybeInvalidateCacheEntryForSubsequentGet [this=%p uri=%s]\n",
  6085         this, key.get()));
  6086 #endif
  6088     DoInvalidateCacheEntry(mURI);
  6090     // Invalidate Location-header if set
  6091     const char *location = mResponseHead->PeekHeader(nsHttp::Location);
  6092     if (location) {
  6093         LOG(("  Location-header=%s\n", location));
  6094         InvalidateCacheEntryForLocation(location);
  6097     // Invalidate Content-Location-header if set
  6098     location = mResponseHead->PeekHeader(nsHttp::Content_Location);
  6099     if (location) {
  6100         LOG(("  Content-Location-header=%s\n", location));
  6101         InvalidateCacheEntryForLocation(location);
  6105 void
  6106 nsHttpChannel::InvalidateCacheEntryForLocation(const char *location)
  6108     nsAutoCString tmpCacheKey, tmpSpec;
  6109     nsCOMPtr<nsIURI> resultingURI;
  6110     nsresult rv = CreateNewURI(location, getter_AddRefs(resultingURI));
  6111     if (NS_SUCCEEDED(rv) && HostPartIsTheSame(resultingURI)) {
  6112         DoInvalidateCacheEntry(resultingURI);
  6113     } else {
  6114         LOG(("  hosts not matching\n"));
  6118 void
  6119 nsHttpChannel::DoInvalidateCacheEntry(nsIURI* aURI)
  6121     // NOTE:
  6122     // Following comments 24,32 and 33 in bug #327765, we only care about
  6123     // the cache in the protocol-handler, not the application cache.
  6124     // The logic below deviates from the original logic in OpenCacheEntry on
  6125     // one point by using only READ_ONLY access-policy. I think this is safe.
  6127     nsresult rv;
  6129 #ifdef PR_LOGGING
  6130     nsAutoCString key;
  6131     aURI->GetAsciiSpec(key);
  6132     LOG(("DoInvalidateCacheEntry [channel=%p key=%s]", this, key.get()));
  6133 #endif
  6135     nsCOMPtr<nsICacheStorageService> cacheStorageService =
  6136         do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
  6138     nsCOMPtr<nsICacheStorage> cacheStorage;
  6139     if (NS_SUCCEEDED(rv)) {
  6140         nsRefPtr<LoadContextInfo> info = GetLoadContextInfo(this);
  6141         rv = cacheStorageService->DiskCacheStorage(info, false, getter_AddRefs(cacheStorage));
  6144     if (NS_SUCCEEDED(rv)) {
  6145         rv = cacheStorage->AsyncDoomURI(aURI, EmptyCString(), nullptr);
  6148     LOG(("DoInvalidateCacheEntry [channel=%p key=%s rv=%d]", this, key.get(), int(rv)));
  6151 void
  6152 nsHttpChannel::AsyncOnExamineCachedResponse()
  6154     gHttpHandler->OnExamineCachedResponse(this);
  6158 void
  6159 nsHttpChannel::UpdateAggregateCallbacks()
  6161     if (!mTransaction) {
  6162         return;
  6164     nsCOMPtr<nsIInterfaceRequestor> callbacks;
  6165     NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
  6166                                            NS_GetCurrentThread(),
  6167                                            getter_AddRefs(callbacks));
  6168     mTransaction->SetSecurityCallbacks(callbacks);
  6171 nsIPrincipal *
  6172 nsHttpChannel::GetPrincipal()
  6174     if (mPrincipal)
  6175         return mPrincipal;
  6177     nsIScriptSecurityManager *securityManager =
  6178         nsContentUtils::GetSecurityManager();
  6180     if (!securityManager)
  6181         return nullptr;
  6183     securityManager->GetChannelPrincipal(this, getter_AddRefs(mPrincipal));
  6184     if (!mPrincipal)
  6185         return nullptr;
  6187     // principals with unknown app ids do not work with the permission manager
  6188     if (mPrincipal->GetUnknownAppId())
  6189         mPrincipal = nullptr;
  6191     return mPrincipal;
  6195 NS_IMETHODIMP
  6196 nsHttpChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
  6198     MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
  6200     nsresult rv = HttpBaseChannel::SetLoadGroup(aLoadGroup);
  6201     if (NS_SUCCEEDED(rv)) {
  6202         UpdateAggregateCallbacks();
  6204     return rv;
  6207 NS_IMETHODIMP
  6208 nsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
  6210     MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
  6212     nsresult rv = HttpBaseChannel::SetNotificationCallbacks(aCallbacks);
  6213     if (NS_SUCCEEDED(rv)) {
  6214         UpdateAggregateCallbacks();
  6216     return rv;
  6219 nsPerformance*
  6220 nsHttpChannel::GetPerformance()
  6222     // If performance timing is disabled, there is no need for the nsPerformance
  6223     // object anymore.
  6224     if (!mTimingEnabled) {
  6225         return nullptr;
  6227     nsCOMPtr<nsILoadContext> loadContext;
  6228     NS_QueryNotificationCallbacks(this, loadContext);
  6229     if (!loadContext) {
  6230         return nullptr;
  6232     nsCOMPtr<nsIDOMWindow> domWindow;
  6233     loadContext->GetAssociatedWindow(getter_AddRefs(domWindow));
  6234     if (!domWindow) {
  6235         return nullptr;
  6237     nsCOMPtr<nsPIDOMWindow> pDomWindow = do_QueryInterface(domWindow);
  6238     if (!pDomWindow) {
  6239         return nullptr;
  6241     if (!pDomWindow->IsInnerWindow()) {
  6242         pDomWindow = pDomWindow->GetCurrentInnerWindow();
  6243         if (!pDomWindow) {
  6244             return nullptr;
  6248     nsPerformance* docPerformance = pDomWindow->GetPerformance();
  6249     if (!docPerformance) {
  6250       return nullptr;
  6252     // iframes should be added to the parent's entries list.
  6253     if (mLoadFlags & LOAD_DOCUMENT_URI) {
  6254       return docPerformance->GetParentPerformance();
  6256     return docPerformance;
  6259 void
  6260 nsHttpChannel::ForcePending(bool aForcePending)
  6262     // Set true here so IsPending will return true.
  6263     // Required for callback diversion from child back to parent. In such cases
  6264     // OnStopRequest can be called in the parent before callbacks are diverted
  6265     // back from the child to the listener in the parent.
  6266     mForcePending = aForcePending;
  6269 NS_IMETHODIMP
  6270 nsHttpChannel::IsPending(bool *aIsPending)
  6272     NS_ENSURE_ARG_POINTER(aIsPending);
  6273     *aIsPending = mIsPending || mForcePending;
  6274     return NS_OK;
  6277 } } // namespace mozilla::net

mercurial