LCOV - code coverage report
Current view: top level - netwerk/protocol/http - nsHttpChannel.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1616 4325 37.4 %
Date: 2017-07-14 16:53:18 Functions: 144 267 53.9 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : // HttpLog.h should generally be included first
       8             : #include "HttpLog.h"
       9             : 
      10             : #include <inttypes.h>
      11             : 
      12             : #include "mozilla/dom/nsCSPContext.h"
      13             : #include "mozilla/ScopeExit.h"
      14             : #include "mozilla/SizePrintfMacros.h"
      15             : #include "mozilla/Sprintf.h"
      16             : 
      17             : #include "nsHttp.h"
      18             : #include "nsHttpChannel.h"
      19             : #include "nsHttpHandler.h"
      20             : #include "nsIApplicationCacheService.h"
      21             : #include "nsIApplicationCacheContainer.h"
      22             : #include "nsICacheStorageService.h"
      23             : #include "nsICacheStorage.h"
      24             : #include "nsICacheEntry.h"
      25             : #include "nsICaptivePortalService.h"
      26             : #include "nsICryptoHash.h"
      27             : #include "nsINetworkInterceptController.h"
      28             : #include "nsINSSErrorsService.h"
      29             : #include "nsISecurityReporter.h"
      30             : #include "nsIStringBundle.h"
      31             : #include "nsIStreamListenerTee.h"
      32             : #include "nsISeekableStream.h"
      33             : #include "nsILoadGroupChild.h"
      34             : #include "nsIProtocolProxyService2.h"
      35             : #include "nsIURIClassifier.h"
      36             : #include "nsMimeTypes.h"
      37             : #include "nsNetCID.h"
      38             : #include "nsNetUtil.h"
      39             : #include "nsIURL.h"
      40             : #include "nsIStreamTransportService.h"
      41             : #include "prnetdb.h"
      42             : #include "nsEscape.h"
      43             : #include "nsStreamUtils.h"
      44             : #include "nsIOService.h"
      45             : #include "nsDNSPrefetch.h"
      46             : #include "nsChannelClassifier.h"
      47             : #include "nsIRedirectResultListener.h"
      48             : #include "mozilla/dom/ContentVerifier.h"
      49             : #include "mozilla/TimeStamp.h"
      50             : #include "nsError.h"
      51             : #include "nsPrintfCString.h"
      52             : #include "nsAlgorithm.h"
      53             : #include "nsQueryObject.h"
      54             : #include "nsThreadUtils.h"
      55             : #include "GeckoProfiler.h"
      56             : #include "nsIConsoleService.h"
      57             : #include "mozilla/Attributes.h"
      58             : #include "mozilla/DebugOnly.h"
      59             : #include "mozilla/Preferences.h"
      60             : #include "nsISSLSocketControl.h"
      61             : #include "sslt.h"
      62             : #include "nsContentUtils.h"
      63             : #include "nsContentSecurityManager.h"
      64             : #include "nsIClassOfService.h"
      65             : #include "nsIPermissionManager.h"
      66             : #include "nsIPrincipal.h"
      67             : #include "nsIScriptError.h"
      68             : #include "nsIScriptSecurityManager.h"
      69             : #include "nsISSLStatus.h"
      70             : #include "nsISSLStatusProvider.h"
      71             : #include "nsITransportSecurityInfo.h"
      72             : #include "nsIWebProgressListener.h"
      73             : #include "LoadContextInfo.h"
      74             : #include "netCore.h"
      75             : #include "nsHttpTransaction.h"
      76             : #include "nsICacheEntryDescriptor.h"
      77             : #include "nsICancelable.h"
      78             : #include "nsIHttpChannelAuthProvider.h"
      79             : #include "nsIHttpChannelInternal.h"
      80             : #include "nsIHttpEventSink.h"
      81             : #include "nsIPrompt.h"
      82             : #include "nsInputStreamPump.h"
      83             : #include "nsURLHelper.h"
      84             : #include "nsISocketTransport.h"
      85             : #include "nsIStreamConverterService.h"
      86             : #include "nsISiteSecurityService.h"
      87             : #include "nsString.h"
      88             : #include "nsCRT.h"
      89             : #include "CacheObserver.h"
      90             : #include "mozilla/dom/Performance.h"
      91             : #include "mozilla/Telemetry.h"
      92             : #include "AlternateServices.h"
      93             : #include "InterceptedChannel.h"
      94             : #include "nsIHttpPushListener.h"
      95             : #include "nsIX509Cert.h"
      96             : #include "ScopedNSSTypes.h"
      97             : #include "NullPrincipal.h"
      98             : #include "nsIDeprecationWarner.h"
      99             : #include "nsIDocument.h"
     100             : #include "nsIDOMDocument.h"
     101             : #include "nsICompressConvStats.h"
     102             : #include "nsCORSListenerProxy.h"
     103             : #include "nsISocketProvider.h"
     104             : #include "mozilla/net/Predictor.h"
     105             : #include "mozilla/MathAlgorithms.h"
     106             : #include "CacheControlParser.h"
     107             : #include "nsMixedContentBlocker.h"
     108             : #include "HSTSPrimerListener.h"
     109             : #include "CacheStorageService.h"
     110             : #include "HttpChannelParent.h"
     111             : #include "nsIBufferedStreams.h"
     112             : #include "nsIFileStreams.h"
     113             : #include "nsIMIMEInputStream.h"
     114             : #include "nsIMultiplexInputStream.h"
     115             : #include "../../cache2/CacheFileUtils.h"
     116             : 
     117             : #ifdef MOZ_TASK_TRACER
     118             : #include "GeckoTaskTracer.h"
     119             : #endif
     120             : 
     121             : namespace mozilla { namespace net {
     122             : 
     123             : namespace {
     124             : 
     125             : // Monotonically increasing ID for generating unique cache entries per
     126             : // intercepted channel.
     127             : static uint64_t gNumIntercepted = 0;
     128             : static bool sRCWNEnabled = false;
     129             : static uint32_t sRCWNQueueSizeNormal = 50;
     130             : static uint32_t sRCWNQueueSizePriority = 10;
     131             : static uint32_t sRCWNSmallResourceSizeKB = 256;
     132             : static uint32_t sRCWNMaxWaitMs = 500;
     133             : 
     134             : // True if the local cache should be bypassed when processing a request.
     135             : #define BYPASS_LOCAL_CACHE(loadFlags) \
     136             :         (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
     137             :                       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))
     138             : 
     139             : #define RECOVER_FROM_CACHE_FILE_ERROR(result) \
     140             :         ((result) == NS_ERROR_FILE_NOT_FOUND || \
     141             :          (result) == NS_ERROR_FILE_CORRUPTED || \
     142             :          (result) == NS_ERROR_OUT_OF_MEMORY)
     143             : 
     144             : #define WRONG_RACING_RESPONSE_SOURCE(req)                                                  \
     145             :     (mRaceCacheWithNetwork &&                                                                 \
     146             :         (((mFirstResponseSource == RESPONSE_FROM_CACHE) && (req != mCachePump)) ||         \
     147             :          ((mFirstResponseSource == RESPONSE_FROM_NETWORK) && (req != mTransactionPump))))
     148             : 
     149             : static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
     150             : 
     151             : enum CacheDisposition {
     152             :     kCacheHit = 1,
     153             :     kCacheHitViaReval = 2,
     154             :     kCacheMissedViaReval = 3,
     155             :     kCacheMissed = 4
     156             : };
     157             : 
     158             : void
     159           3 : AccumulateCacheHitTelemetry(CacheDisposition hitOrMiss)
     160             : {
     161           3 :     if (!CacheObserver::UseNewCache()) {
     162           0 :         Telemetry::Accumulate(Telemetry::HTTP_CACHE_DISPOSITION_2, hitOrMiss);
     163             :     }
     164             :     else {
     165           3 :         Telemetry::Accumulate(Telemetry::HTTP_CACHE_DISPOSITION_2_V2, hitOrMiss);
     166             : 
     167           3 :         int32_t experiment = CacheObserver::HalfLifeExperiment();
     168           3 :         if (experiment > 0 && hitOrMiss == kCacheMissed) {
     169           0 :             Telemetry::Accumulate(Telemetry::HTTP_CACHE_MISS_HALFLIFE_EXPERIMENT_2,
     170           0 :                                   experiment - 1);
     171             :         }
     172             :     }
     173           3 : }
     174             : 
     175             : // Computes and returns a SHA1 hash of the input buffer. The input buffer
     176             : // must be a null-terminated string.
     177             : nsresult
     178           0 : Hash(const char *buf, nsACString &hash)
     179             : {
     180             :     nsresult rv;
     181             : 
     182             :     nsCOMPtr<nsICryptoHash> hasher
     183           0 :       = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
     184           0 :     NS_ENSURE_SUCCESS(rv, rv);
     185             : 
     186           0 :     rv = hasher->Init(nsICryptoHash::SHA1);
     187           0 :     NS_ENSURE_SUCCESS(rv, rv);
     188             : 
     189           0 :     rv = hasher->Update(reinterpret_cast<unsigned const char*>(buf),
     190           0 :                          strlen(buf));
     191           0 :     NS_ENSURE_SUCCESS(rv, rv);
     192             : 
     193           0 :     rv = hasher->Finish(true, hash);
     194           0 :     NS_ENSURE_SUCCESS(rv, rv);
     195             : 
     196           0 :     return NS_OK;
     197             : }
     198             : 
     199             : bool
     200           0 : IsInSubpathOfAppCacheManifest(nsIApplicationCache *cache, nsACString const& uriSpec)
     201             : {
     202           0 :     MOZ_ASSERT(cache);
     203             : 
     204             :     nsresult rv;
     205             : 
     206           0 :     nsCOMPtr<nsIURI> uri;
     207           0 :     rv = NS_NewURI(getter_AddRefs(uri), uriSpec);
     208           0 :     if (NS_FAILED(rv)) {
     209           0 :       return false;
     210             :     }
     211             : 
     212           0 :     nsCOMPtr<nsIURL> url(do_QueryInterface(uri, &rv));
     213           0 :     if (NS_FAILED(rv)) {
     214           0 :       return false;
     215             :     }
     216             : 
     217           0 :     nsAutoCString directory;
     218           0 :     rv = url->GetDirectory(directory);
     219           0 :     if (NS_FAILED(rv)) {
     220           0 :       return false;
     221             :     }
     222             : 
     223           0 :     nsCOMPtr<nsIURI> manifestURI;
     224           0 :     rv = cache->GetManifestURI(getter_AddRefs(manifestURI));
     225           0 :     if (NS_FAILED(rv)) {
     226           0 :       return false;
     227             :     }
     228             : 
     229           0 :     nsCOMPtr<nsIURL> manifestURL(do_QueryInterface(manifestURI, &rv));
     230           0 :     if (NS_FAILED(rv)) {
     231           0 :       return false;
     232             :     }
     233             : 
     234           0 :     nsAutoCString manifestDirectory;
     235           0 :     rv = manifestURL->GetDirectory(manifestDirectory);
     236           0 :     if (NS_FAILED(rv)) {
     237           0 :       return false;
     238             :     }
     239             : 
     240           0 :     return StringBeginsWith(directory, manifestDirectory);
     241             : }
     242             : 
     243             : } // unnamed namespace
     244             : 
     245             : // We only treat 3xx responses as redirects if they have a Location header and
     246             : // the status code is in a whitelist.
     247             : bool
     248          11 : nsHttpChannel::WillRedirect(nsHttpResponseHead * response)
     249             : {
     250          11 :     return IsRedirectStatus(response->Status()) &&
     251          11 :            response->HasHeader(nsHttp::Location);
     252             : }
     253             : 
     254             : nsresult
     255             : StoreAuthorizationMetaData(nsICacheEntry *entry, nsHttpRequestHead *requestHead);
     256             : 
     257             : class AutoRedirectVetoNotifier
     258             : {
     259             : public:
     260           0 :     explicit AutoRedirectVetoNotifier(nsHttpChannel* channel) : mChannel(channel)
     261             :     {
     262           0 :       if (mChannel->mHasAutoRedirectVetoNotifier) {
     263           0 :         MOZ_CRASH("Nested AutoRedirectVetoNotifier on the stack");
     264             :         mChannel = nullptr;
     265             :         return;
     266             :       }
     267             : 
     268           0 :       mChannel->mHasAutoRedirectVetoNotifier = true;
     269             :     }
     270           0 :     ~AutoRedirectVetoNotifier() {ReportRedirectResult(false);}
     271           0 :     void RedirectSucceeded() {ReportRedirectResult(true);}
     272             : 
     273             : private:
     274             :     nsHttpChannel* mChannel;
     275             :     void ReportRedirectResult(bool succeeded);
     276             : };
     277             : 
     278             : void
     279           0 : AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded)
     280             : {
     281           0 :     if (!mChannel)
     282           0 :         return;
     283             : 
     284           0 :     mChannel->mRedirectChannel = nullptr;
     285             : 
     286           0 :     nsCOMPtr<nsIRedirectResultListener> vetoHook;
     287           0 :     NS_QueryNotificationCallbacks(mChannel,
     288             :                                   NS_GET_IID(nsIRedirectResultListener),
     289           0 :                                   getter_AddRefs(vetoHook));
     290             : 
     291           0 :     nsHttpChannel* channel = mChannel;
     292           0 :     mChannel = nullptr;
     293             : 
     294           0 :     if (vetoHook)
     295           0 :         vetoHook->OnRedirectResult(succeeded);
     296             : 
     297             :     // Drop after the notification
     298           0 :     channel->mHasAutoRedirectVetoNotifier = false;
     299             : }
     300             : 
     301             : //-----------------------------------------------------------------------------
     302             : // nsHttpChannel <public>
     303             : //-----------------------------------------------------------------------------
     304             : 
     305           7 : nsHttpChannel::nsHttpChannel()
     306             :     : HttpAsyncAborter<nsHttpChannel>(this)
     307             :     , mLogicalOffset(0)
     308             :     , mPostID(0)
     309             :     , mRequestTime(0)
     310             :     , mOfflineCacheLastModifiedTime(0)
     311             :     , mSuspendTotalTime(0)
     312             :     , mInterceptCache(DO_NOT_INTERCEPT)
     313           7 :     , mInterceptionID(gNumIntercepted++)
     314             :     , mCacheOpenWithPriority(false)
     315             :     , mCacheQueueSizeWhenOpen(0)
     316             :     , mCachedContentIsValid(false)
     317             :     , mCachedContentIsPartial(false)
     318             :     , mCacheOnlyMetadata(false)
     319             :     , mTransactionReplaced(false)
     320             :     , mAuthRetryPending(false)
     321             :     , mProxyAuthPending(false)
     322             :     , mCustomAuthHeader(false)
     323             :     , mResuming(false)
     324             :     , mInitedCacheEntry(false)
     325             :     , mFallbackChannel(false)
     326             :     , mCustomConditionalRequest(false)
     327             :     , mFallingBack(false)
     328             :     , mWaitingForRedirectCallback(false)
     329             :     , mRequestTimeInitialized(false)
     330             :     , mCacheEntryIsReadOnly(false)
     331             :     , mCacheEntryIsWriteOnly(false)
     332             :     , mCacheEntriesToWaitFor(0)
     333             :     , mHasQueryString(0)
     334             :     , mConcurrentCacheAccess(0)
     335             :     , mIsPartialRequest(0)
     336             :     , mHasAutoRedirectVetoNotifier(0)
     337             :     , mPinCacheContent(0)
     338             :     , mIsCorsPreflightDone(0)
     339             :     , mStronglyFramed(false)
     340             :     , mUsedNetwork(0)
     341             :     , mAuthConnectionRestartable(0)
     342             :     , mReqContentLengthDetermined(0)
     343             :     , mReqContentLength(0U)
     344             :     , mPushedStream(nullptr)
     345             :     , mLocalBlocklist(false)
     346             :     , mWarningReporter(nullptr)
     347             :     , mIsReadingFromCache(false)
     348             :     , mOnCacheAvailableCalled(false)
     349             :     , mRaceCacheWithNetwork(false)
     350             :     , mRaceDelay(0)
     351             :     , mCacheAsyncOpenCalled(false)
     352          14 :     , mDidReval(false)
     353             : {
     354           7 :     LOG(("Creating nsHttpChannel [this=%p]\n", this));
     355           7 :     mChannelCreationTime = PR_Now();
     356           7 :     mChannelCreationTimestamp = TimeStamp::Now();
     357           7 : }
     358             : 
     359          15 : nsHttpChannel::~nsHttpChannel()
     360             : {
     361           5 :     LOG(("Destroying nsHttpChannel [this=%p]\n", this));
     362             : 
     363           5 :     if (mAuthProvider) {
     364           6 :         DebugOnly<nsresult> rv = mAuthProvider->Disconnect(NS_ERROR_ABORT);
     365           3 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
     366             :     }
     367             : 
     368           5 :     ReleaseMainThreadOnlyReferences();
     369          15 : }
     370             : 
     371             : void
     372           5 : nsHttpChannel::ReleaseMainThreadOnlyReferences()
     373             : {
     374           5 :     if (NS_IsMainThread()) {
     375             :         // Already on main thread, let dtor to
     376             :         // take care of releasing references
     377           5 :         return;
     378             :     }
     379             : 
     380           0 :     nsTArray<nsCOMPtr<nsISupports>> arrayToRelease;
     381           0 :     arrayToRelease.AppendElement(mApplicationCacheForWrite.forget());
     382           0 :     arrayToRelease.AppendElement(mAuthProvider.forget());
     383           0 :     arrayToRelease.AppendElement(mRedirectURI.forget());
     384           0 :     arrayToRelease.AppendElement(mRedirectChannel.forget());
     385           0 :     arrayToRelease.AppendElement(mPreflightChannel.forget());
     386             : 
     387           0 :     NS_DispatchToMainThread(new ProxyReleaseRunnable(Move(arrayToRelease)));
     388             : }
     389             : 
     390             : nsresult
     391           7 : nsHttpChannel::Init(nsIURI *uri,
     392             :                     uint32_t caps,
     393             :                     nsProxyInfo *proxyInfo,
     394             :                     uint32_t proxyResolveFlags,
     395             :                     nsIURI *proxyURI,
     396             :                     uint64_t channelId)
     397             : {
     398           7 :     nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo,
     399           7 :                                         proxyResolveFlags, proxyURI, channelId);
     400           7 :     if (NS_FAILED(rv))
     401           0 :         return rv;
     402             : 
     403           7 :     LOG(("nsHttpChannel::Init [this=%p]\n", this));
     404             : 
     405           7 :     return rv;
     406             : }
     407             : 
     408             : nsresult
     409           0 : nsHttpChannel::AddSecurityMessage(const nsAString& aMessageTag,
     410             :                                   const nsAString& aMessageCategory)
     411             : {
     412           0 :     if (mWarningReporter) {
     413           0 :         return mWarningReporter->ReportSecurityMessage(aMessageTag,
     414           0 :                                                        aMessageCategory);
     415             :     }
     416           0 :     return HttpBaseChannel::AddSecurityMessage(aMessageTag,
     417           0 :                                                aMessageCategory);
     418             : }
     419             : 
     420             : //-----------------------------------------------------------------------------
     421             : // nsHttpChannel <private>
     422             : //-----------------------------------------------------------------------------
     423             : 
     424             : nsresult
     425           6 : nsHttpChannel::Connect()
     426             : {
     427             :     nsresult rv;
     428             : 
     429           6 :     LOG(("nsHttpChannel::Connect [this=%p]\n", this));
     430             : 
     431             :     // Note that we are only setting the "Upgrade-Insecure-Requests" request
     432             :     // header for *all* navigational requests instead of all requests as
     433             :     // defined in the spec, see:
     434             :     // https://www.w3.org/TR/upgrade-insecure-requests/#preference
     435          12 :     nsContentPolicyType type = mLoadInfo ?
     436           6 :                                mLoadInfo->GetExternalContentPolicyType() :
     437          12 :                                nsIContentPolicy::TYPE_OTHER;
     438             : 
     439           6 :     if (type == nsIContentPolicy::TYPE_DOCUMENT ||
     440             :         type == nsIContentPolicy::TYPE_SUBDOCUMENT) {
     441           4 :         rv = SetRequestHeader(NS_LITERAL_CSTRING("Upgrade-Insecure-Requests"),
     442           4 :                               NS_LITERAL_CSTRING("1"), false);
     443           1 :         NS_ENSURE_SUCCESS(rv, rv);
     444             :     }
     445             : 
     446           6 :     bool isHttps = false;
     447           6 :     rv = mURI->SchemeIs("https", &isHttps);
     448           6 :     NS_ENSURE_SUCCESS(rv,rv);
     449          12 :     nsCOMPtr<nsIPrincipal> resultPrincipal;
     450           6 :     if (!isHttps && mLoadInfo) {
     451           6 :         nsContentUtils::GetSecurityManager()->
     452           6 :           GetChannelResultPrincipal(this, getter_AddRefs(resultPrincipal));
     453             :     }
     454          12 :     OriginAttributes originAttributes;
     455           6 :     if (!NS_GetOriginAttributes(this, originAttributes)) {
     456           0 :         return NS_ERROR_FAILURE;
     457             :     }
     458           6 :     bool isHttp = false;
     459           6 :     rv = mURI->SchemeIs("http", &isHttp);
     460           6 :     NS_ENSURE_SUCCESS(rv,rv);
     461             : 
     462           6 :     if (isHttp) {
     463           6 :         bool shouldUpgrade = false;
     464          12 :         rv = NS_ShouldSecureUpgrade(mURI,
     465             :                                     mLoadInfo,
     466             :                                     resultPrincipal,
     467           6 :                                     mPrivateBrowsing,
     468             :                                     mAllowSTS,
     469             :                                     originAttributes,
     470          12 :                                     shouldUpgrade);
     471           6 :         NS_ENSURE_SUCCESS(rv, rv);
     472           6 :         if (shouldUpgrade) {
     473           0 :             return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
     474             :         }
     475             :     }
     476             : 
     477             :     // ensure that we are using a valid hostname
     478           6 :     if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Origin())))
     479           0 :         return NS_ERROR_UNKNOWN_HOST;
     480             : 
     481           6 :     if (mUpgradeProtocolCallback) {
     482           0 :         mCaps |=  NS_HTTP_DISALLOW_SPDY;
     483             :     }
     484             : 
     485             :     // Finalize ConnectionInfo flags before SpeculativeConnect
     486           6 :     mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
     487           6 :     mConnectionInfo->SetPrivate(mPrivateBrowsing);
     488           6 :     mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY);
     489           6 :     mConnectionInfo->SetBeConservative((mCaps & NS_HTTP_BE_CONSERVATIVE) || mBeConservative);
     490             : 
     491             :     // Consider opening a TCP connection right away.
     492           6 :     SpeculativeConnect();
     493             : 
     494             :     // Don't allow resuming when cache must be used
     495           6 :     if (mResuming && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
     496           0 :         LOG(("Resuming from cache is not supported yet"));
     497           0 :         return NS_ERROR_DOCUMENT_NOT_CACHED;
     498             :     }
     499             : 
     500             :     // open a cache entry for this channel...
     501           6 :     rv = OpenCacheEntry(isHttps);
     502             : 
     503             :     // do not continue if asyncOpenCacheEntry is in progress
     504           6 :     if (AwaitingCacheCallbacks()) {
     505           4 :         LOG(("nsHttpChannel::Connect %p AwaitingCacheCallbacks forces async\n", this));
     506           4 :         MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state");
     507             : 
     508           4 :         if (mNetworkTriggered && mWaitingForProxy) {
     509             :             // Someone has called TriggerNetwork(), meaning we are racing the
     510             :             // network with the cache.
     511           0 :             mWaitingForProxy = false;
     512           0 :             return TryHSTSPriming();
     513             :         }
     514             : 
     515           4 :         return NS_OK;
     516             :     }
     517             : 
     518           2 :     if (NS_FAILED(rv)) {
     519           0 :         LOG(("OpenCacheEntry failed [rv=%" PRIx32 "]\n", static_cast<uint32_t>(rv)));
     520             :         // if this channel is only allowed to pull from the cache, then
     521             :         // we must fail if we were unable to open a cache entry.
     522           0 :         if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
     523             :             // If we have a fallback URI (and we're not already
     524             :             // falling back), process the fallback asynchronously.
     525           0 :             if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
     526           0 :                 return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
     527             :             }
     528           0 :             return NS_ERROR_DOCUMENT_NOT_CACHED;
     529             :         }
     530             :         // otherwise, let's just proceed without using the cache.
     531             :     }
     532             : 
     533             :     // When racing, if OnCacheEntryAvailable is called before AsyncOpenURI
     534             :     // returns, then we may not have started reading from the cache.
     535             :     // If the content is valid, we should attempt to do so, as technically the
     536             :     // cache has won the race.
     537           2 :     if (sRCWNEnabled && mCachedContentIsValid && mNetworkTriggered) {
     538           0 :         Unused << ReadFromCache(true);
     539             :     }
     540             : 
     541           2 :     return TriggerNetwork(0);
     542             : }
     543             : 
     544             : nsresult
     545           6 : nsHttpChannel::TryHSTSPriming()
     546             : {
     547             :     bool isHttpScheme;
     548           6 :     nsresult rv = mURI->SchemeIs("http", &isHttpScheme);
     549           6 :     NS_ENSURE_SUCCESS(rv, rv);
     550             :     bool isHttpsScheme;
     551           6 :     rv = mURI->SchemeIs("https", &isHttpsScheme);
     552           6 :     NS_ENSURE_SUCCESS(rv, rv);
     553             : 
     554           6 :     if ((isHttpScheme || isHttpsScheme) && mLoadInfo) {
     555           6 :         if (mLoadInfo->GetIsHSTSPriming()) {
     556             :             // shortcut priming requests so they don't get counted
     557           0 :             return ContinueConnect();
     558             :         }
     559             : 
     560             :         // HSTS priming requires the LoadInfo provided with AsyncOpen2
     561             :         bool requireHSTSPriming =
     562           6 :             mLoadInfo->GetForceHSTSPriming();
     563             : 
     564           6 :         if (requireHSTSPriming &&
     565           0 :                 nsMixedContentBlocker::sSendHSTSPriming &&
     566           0 :                 mInterceptCache == DO_NOT_INTERCEPT) {
     567           0 :             if (!isHttpsScheme) {
     568           0 :                 rv = HSTSPrimingListener::StartHSTSPriming(this, this);
     569             : 
     570           0 :                 if (NS_FAILED(rv)) {
     571           0 :                     CloseCacheEntry(false);
     572             :                     Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
     573           0 :                                       HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_ERROR);
     574           0 :                     return rv;
     575             :                 }
     576             : 
     577           0 :                 return NS_OK;
     578             :             }
     579             : 
     580           0 :             if (!mLoadInfo->GetIsHSTSPrimingUpgrade()) {
     581             :                 // The request was already upgraded, for example by a prior
     582             :                 // successful priming request
     583           0 :                 LOG(("HSTS Priming: request already upgraded"));
     584             :                 Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
     585           0 :                               HSTSPrimingResult::eHSTS_PRIMING_ALREADY_UPGRADED);
     586             : 
     587             :                 // No HSTS Priming request was sent.
     588             :                 Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
     589           0 :                               HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_ALREADY_UPGRADED);
     590             :             }
     591             : 
     592           0 :             mLoadInfo->ClearHSTSPriming();
     593           0 :             return ContinueConnect();
     594             :         }
     595             : 
     596           6 :         if (!mLoadInfo->GetIsHSTSPrimingUpgrade()) {
     597             :             // No HSTS Priming request was sent, and we didn't already record this request
     598             :             Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
     599           6 :                                   HSTSPrimingRequest::eHSTS_PRIMING_NO_REQUEST);
     600             :         }
     601             :     } else {
     602             :         Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_REQUESTS,
     603           0 :                           HSTSPrimingRequest::eHSTS_PRIMING_REQUEST_NO_LOAD_INFO);
     604             :     }
     605             : 
     606           6 :     return ContinueConnect();
     607             : }
     608             : 
     609             : // nsIInputAvailableCallback (nsIStreamTransportService.idl)
     610             : NS_IMETHODIMP
     611           0 : nsHttpChannel::OnInputAvailableComplete(uint64_t size, nsresult status)
     612             : {
     613           0 :     MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
     614           0 :     LOG(("nsHttpChannel::OnInputAvailableComplete %p %" PRIx32 "\n",
     615             :          this, static_cast<uint32_t>(status)));
     616           0 :     if (NS_SUCCEEDED(status)) {
     617           0 :         mReqContentLength = size;
     618             :     } else {
     619             :         // fall back to synchronous on the error path. should not happen.
     620           0 :         if (NS_SUCCEEDED(mUploadStream->Available(&size))) {
     621           0 :             mReqContentLength = size;
     622             :         }
     623             :     }
     624             : 
     625           0 :     LOG(("nsHttpChannel::DetermineContentLength %p from sts\n", this));
     626           0 :     mReqContentLengthDetermined = 1;
     627           0 :     nsresult rv = mCanceled ? mStatus : ContinueConnect();
     628           0 :     if (NS_FAILED(rv)) {
     629           0 :         CloseCacheEntry(false);
     630           0 :         Unused << AsyncAbort(rv);
     631             :     }
     632           0 :     return NS_OK;
     633             : }
     634             : 
     635             : // nsIFileStream needs to be sent to a worker thread
     636             : // to do Available() as it may cause disk/IO. Unfortunately
     637             : // we have to look at the streams wrapped by a few other
     638             : // abstractions to be sure.
     639             : static
     640           1 : bool isFileStream(nsIInputStream *stream)
     641             : {
     642           1 :     if (!stream) {
     643           0 :         return false;
     644             :     }
     645             : 
     646           2 :     nsCOMPtr<nsIFileInputStream> fileStream = do_QueryInterface(stream);
     647           1 :     if (fileStream) {
     648           0 :         return true;
     649             :     }
     650             : 
     651           2 :     nsCOMPtr<nsIBufferedInputStream> bufferedStream = do_QueryInterface(stream);
     652           1 :     if (bufferedStream) {
     653           0 :         nsCOMPtr<nsIInputStream> innerStream;
     654           0 :         if (NS_SUCCEEDED(bufferedStream->GetData(getter_AddRefs(innerStream)))) {
     655           0 :             return isFileStream(innerStream);
     656             :         }
     657             :     }
     658             : 
     659           2 :     nsCOMPtr<nsIMIMEInputStream> mimeStream = do_QueryInterface(stream);
     660           1 :     if (mimeStream) {
     661           0 :         nsCOMPtr<nsIInputStream> innerStream;
     662           0 :         if (NS_SUCCEEDED(mimeStream->GetData(getter_AddRefs(innerStream)))) {
     663           0 :             return isFileStream(innerStream);
     664             :         }
     665             :     }
     666             : 
     667           2 :     nsCOMPtr<nsIMultiplexInputStream> muxStream = do_QueryInterface(stream);
     668           1 :     uint32_t muxCount = 0;
     669           1 :     if (muxStream) {
     670           0 :         muxStream->GetCount(&muxCount);
     671           0 :         for (uint32_t i = 0; i < muxCount; ++i) {
     672           0 :             nsCOMPtr<nsIInputStream> subStream;
     673           0 :             if (NS_SUCCEEDED(muxStream->GetStream(i, getter_AddRefs(subStream))) &&
     674           0 :                 isFileStream(subStream)) {
     675           0 :                 return true;
     676             :             }
     677             :         }
     678             :     }
     679             : 
     680           1 :     return false;
     681             : }
     682             : 
     683             : void
     684           6 : nsHttpChannel::DetermineContentLength()
     685             : {
     686           6 :     nsCOMPtr<nsIStreamTransportService> sts(services::GetStreamTransportService());
     687             : 
     688           6 :     if (!mUploadStream || !sts) {
     689           5 :         LOG(("nsHttpChannel::DetermineContentLength %p no body\n", this));
     690           5 :         mReqContentLength = 0U;
     691           5 :         mReqContentLengthDetermined = 1;
     692           5 :         return;
     693             :     }
     694             : 
     695           1 :     if (!isFileStream(mUploadStream)) {
     696           1 :         mUploadStream->Available(&mReqContentLength);
     697           1 :         LOG(("nsHttpChannel::DetermineContentLength %p from mem\n", this));
     698           1 :         mReqContentLengthDetermined = 1;
     699           1 :         return;
     700             :     }
     701             : 
     702           0 :     LOG(("nsHttpChannel::DetermineContentLength Async [this=%p]\n", this));
     703           0 :     sts->InputAvailable(mUploadStream, this);
     704             : }
     705             : 
     706             : nsresult
     707           6 : nsHttpChannel::ContinueConnect()
     708             : {
     709             :     // If we have a request body that is going to require bouncing to the STS
     710             :     // in order to determine the content-length as doing it on the main thread
     711             :     // will incur file IO some of the time.
     712           6 :     if (!mReqContentLengthDetermined) {
     713             :         // C-L might be determined sync or async. Sync will set
     714             :         // mReqContentLengthDetermined to true in DetermineContentLength()
     715           6 :         DetermineContentLength();
     716             :     }
     717           6 :     if (!mReqContentLengthDetermined) {
     718           0 :         return NS_OK;
     719             :     }
     720             : 
     721             :     // If we have had HSTS priming, we need to reevaluate whether we need
     722             :     // a CORS preflight. Bug: 1272440
     723             :     // If we need to start a CORS preflight, do it now!
     724             :     // Note that it is important to do this before the early returns below.
     725           6 :     if (!mIsCorsPreflightDone && mRequireCORSPreflight &&
     726           0 :         mInterceptCache != INTERCEPTED) {
     727           0 :         MOZ_ASSERT(!mPreflightChannel);
     728             :         nsresult rv =
     729           0 :             nsCORSListenerProxy::StartCORSPreflight(this, this,
     730             :                                                     mUnsafeHeaders,
     731           0 :                                                     getter_AddRefs(mPreflightChannel));
     732           0 :         return rv;
     733             :     }
     734             : 
     735           6 :     MOZ_RELEASE_ASSERT(!(mRequireCORSPreflight &&
     736             :                          mInterceptCache != INTERCEPTED) ||
     737             :                        mIsCorsPreflightDone,
     738             :                        "CORS preflight must have been finished by the time we "
     739             :                        "do the rest of ContinueConnect");
     740             : 
     741             :     // we may or may not have a cache entry at this point
     742           6 :     if (mCacheEntry) {
     743             :         // read straight from the cache if possible...
     744           5 :         if (mCachedContentIsValid) {
     745           3 :             nsRunnableMethod<nsHttpChannel> *event = nullptr;
     746             :             nsresult rv;
     747           3 :             if (!mCachedContentIsPartial) {
     748           6 :                 rv = AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse,
     749           6 :                                &event);
     750           3 :                 if (NS_FAILED(rv)) {
     751           0 :                     LOG(("  AsyncCall failed (%08x)",
     752             :                          static_cast<uint32_t>(rv)));
     753             :                 }
     754             :             }
     755           3 :             rv = ReadFromCache(true);
     756           3 :             if (NS_FAILED(rv) && event) {
     757           0 :                 event->Revoke();
     758             :             }
     759             : 
     760             :             // Don't accumulate the cache hit telemetry for intercepted channels.
     761           3 :             if (mInterceptCache != INTERCEPTED) {
     762           3 :                 AccumulateCacheHitTelemetry(kCacheHit);
     763             :             }
     764             : 
     765           3 :             return rv;
     766             :         }
     767           2 :         else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
     768             :             // the cache contains the requested resource, but it must be
     769             :             // validated before we can reuse it.  since we are not allowed
     770             :             // to hit the net, there's nothing more to do.  the document
     771             :             // is effectively not in the cache.
     772           0 :             LOG(("  !mCachedContentIsValid && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
     773           0 :             return NS_ERROR_DOCUMENT_NOT_CACHED;
     774             :         }
     775             :     }
     776           1 :     else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
     777             :         // If we have a fallback URI (and we're not already
     778             :         // falling back), process the fallback asynchronously.
     779           0 :         if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
     780           0 :             return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
     781             :         }
     782           0 :         LOG(("  !mCacheEntry && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
     783           0 :         return NS_ERROR_DOCUMENT_NOT_CACHED;
     784             :     }
     785             : 
     786           3 :     if (mLoadFlags & LOAD_NO_NETWORK_IO) {
     787           0 :         LOG(("  mLoadFlags & LOAD_NO_NETWORK_IO"));
     788           0 :         return NS_ERROR_DOCUMENT_NOT_CACHED;
     789             :     }
     790             : 
     791             :     // hit the net...
     792           3 :     nsresult rv = SetupTransaction();
     793           3 :     if (NS_FAILED(rv)) return rv;
     794             : 
     795           3 :     rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
     796           3 :     if (NS_FAILED(rv)) return rv;
     797             : 
     798           3 :     rv = mTransactionPump->AsyncRead(this, nullptr);
     799           3 :     if (NS_FAILED(rv)) return rv;
     800             : 
     801           3 :     uint32_t suspendCount = mSuspendCount;
     802           3 :     while (suspendCount--)
     803           0 :         mTransactionPump->Suspend();
     804             : 
     805           3 :     return NS_OK;
     806             : }
     807             : 
     808             : void
     809           6 : nsHttpChannel::SpeculativeConnect()
     810             : {
     811             :     // Before we take the latency hit of dealing with the cache, try and
     812             :     // get the TCP (and SSL) handshakes going so they can overlap.
     813             : 
     814             :     // don't speculate if we are on a local blocklist, on uses of the offline
     815             :     // application cache, if we are offline, when doing http upgrade (i.e.
     816             :     // websockets bootstrap), or if we can't do keep-alive (because then we
     817             :     // couldn't reuse the speculative connection anyhow).
     818          24 :     if (mLocalBlocklist || mApplicationCache || gIOService->IsOffline() ||
     819          18 :         mUpgradeProtocolCallback || !(mCaps & NS_HTTP_ALLOW_KEEPALIVE))
     820           1 :         return;
     821             : 
     822             :     // LOAD_ONLY_FROM_CACHE and LOAD_NO_NETWORK_IO must not hit network.
     823             :     // LOAD_FROM_CACHE and LOAD_CHECK_OFFLINE_CACHE are unlikely to hit network,
     824             :     // so skip preconnects for them.
     825           6 :     if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE |
     826             :                       LOAD_NO_NETWORK_IO | LOAD_CHECK_OFFLINE_CACHE))
     827           1 :         return;
     828             : 
     829           5 :     if (mAllowStaleCacheContent) {
     830           0 :         return;
     831             :     }
     832             : 
     833          10 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
     834           5 :     NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
     835          10 :                                            getter_AddRefs(callbacks));
     836           5 :     if (!callbacks)
     837           0 :         return;
     838             : 
     839           5 :     Unused << gHttpHandler->SpeculativeConnect(
     840           5 :         mConnectionInfo, callbacks, mCaps & NS_HTTP_DISALLOW_SPDY);
     841             : }
     842             : 
     843             : void
     844           0 : nsHttpChannel::DoNotifyListenerCleanup()
     845             : {
     846             :     // We don't need this info anymore
     847           0 :     CleanRedirectCacheChainIfNecessary();
     848           0 : }
     849             : 
     850             : void
     851           6 : nsHttpChannel::ReleaseListeners()
     852             : {
     853           6 :     HttpBaseChannel::ReleaseListeners();
     854           6 :     mChannelClassifier = nullptr;
     855           6 : }
     856             : 
     857             : void
     858           0 : nsHttpChannel::HandleAsyncRedirect()
     859             : {
     860           0 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
     861             : 
     862           0 :     if (mSuspendCount) {
     863           0 :         LOG(("Waiting until resume to do async redirect [this=%p]\n", this));
     864           0 :         mCallOnResume = &nsHttpChannel::HandleAsyncRedirect;
     865           0 :         return;
     866             :     }
     867             : 
     868           0 :     nsresult rv = NS_OK;
     869             : 
     870           0 :     LOG(("nsHttpChannel::HandleAsyncRedirect [this=%p]\n", this));
     871             : 
     872             :     // since this event is handled asynchronously, it is possible that this
     873             :     // channel could have been canceled, in which case there would be no point
     874             :     // in processing the redirect.
     875           0 :     if (NS_SUCCEEDED(mStatus)) {
     876           0 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
     877           0 :         rv = AsyncProcessRedirection(mResponseHead->Status());
     878           0 :         if (NS_FAILED(rv)) {
     879           0 :             PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
     880             :             // TODO: if !DoNotRender3xxBody(), render redirect body instead.
     881             :             // But first we need to cache 3xx bodies (bug 748510)
     882           0 :             rv = ContinueHandleAsyncRedirect(rv);
     883           0 :             MOZ_ASSERT(NS_SUCCEEDED(rv));
     884             :         }
     885             :     }
     886             :     else {
     887           0 :         rv = ContinueHandleAsyncRedirect(mStatus);
     888           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
     889             :     }
     890             : }
     891             : 
     892             : nsresult
     893           0 : nsHttpChannel::ContinueHandleAsyncRedirect(nsresult rv)
     894             : {
     895           0 :     if (NS_FAILED(rv)) {
     896             :         // If AsyncProcessRedirection fails, then we have to send out the
     897             :         // OnStart/OnStop notifications.
     898           0 :         LOG(("ContinueHandleAsyncRedirect got failure result [rv=%" PRIx32 "]\n",
     899             :              static_cast<uint32_t>(rv)));
     900             : 
     901             :         bool redirectsEnabled =
     902           0 :             !mLoadInfo || !mLoadInfo->GetDontFollowRedirects();
     903             : 
     904           0 :         if (redirectsEnabled) {
     905             :             // TODO: stop failing original channel if redirect vetoed?
     906           0 :             mStatus = rv;
     907             : 
     908           0 :             DoNotifyListener();
     909             : 
     910             :             // Blow away cache entry if we couldn't process the redirect
     911             :             // for some reason (the cache entry might be corrupt).
     912           0 :             if (mCacheEntry) {
     913           0 :                 mCacheEntry->AsyncDoom(nullptr);
     914             :             }
     915             :         }
     916             :         else {
     917           0 :             DoNotifyListener();
     918             :         }
     919             :     }
     920             : 
     921           0 :     CloseCacheEntry(true);
     922             : 
     923           0 :     mIsPending = false;
     924             : 
     925           0 :     if (mLoadGroup)
     926           0 :         mLoadGroup->RemoveRequest(this, nullptr, mStatus);
     927             : 
     928           0 :     return NS_OK;
     929             : }
     930             : 
     931             : void
     932           0 : nsHttpChannel::HandleAsyncNotModified()
     933             : {
     934           0 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
     935             : 
     936           0 :     if (mSuspendCount) {
     937           0 :         LOG(("Waiting until resume to do async not-modified [this=%p]\n",
     938             :              this));
     939           0 :         mCallOnResume = &nsHttpChannel::HandleAsyncNotModified;
     940           0 :         return;
     941             :     }
     942             : 
     943           0 :     LOG(("nsHttpChannel::HandleAsyncNotModified [this=%p]\n", this));
     944             : 
     945           0 :     DoNotifyListener();
     946             : 
     947           0 :     CloseCacheEntry(false);
     948             : 
     949           0 :     mIsPending = false;
     950             : 
     951           0 :     if (mLoadGroup)
     952           0 :         mLoadGroup->RemoveRequest(this, nullptr, mStatus);
     953             : }
     954             : 
     955             : void
     956           0 : nsHttpChannel::HandleAsyncFallback()
     957             : {
     958           0 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
     959             : 
     960           0 :     if (mSuspendCount) {
     961           0 :         LOG(("Waiting until resume to do async fallback [this=%p]\n", this));
     962           0 :         mCallOnResume = &nsHttpChannel::HandleAsyncFallback;
     963           0 :         return;
     964             :     }
     965             : 
     966           0 :     nsresult rv = NS_OK;
     967             : 
     968           0 :     LOG(("nsHttpChannel::HandleAsyncFallback [this=%p]\n", this));
     969             : 
     970             :     // since this event is handled asynchronously, it is possible that this
     971             :     // channel could have been canceled, in which case there would be no point
     972             :     // in processing the fallback.
     973           0 :     if (!mCanceled) {
     974           0 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
     975             :         bool waitingForRedirectCallback;
     976           0 :         rv = ProcessFallback(&waitingForRedirectCallback);
     977           0 :         if (waitingForRedirectCallback)
     978           0 :             return;
     979           0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncFallback);
     980             :     }
     981             : 
     982           0 :     rv = ContinueHandleAsyncFallback(rv);
     983           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
     984             : }
     985             : 
     986             : nsresult
     987           0 : nsHttpChannel::ContinueHandleAsyncFallback(nsresult rv)
     988             : {
     989           0 :     if (!mCanceled && (NS_FAILED(rv) || !mFallingBack)) {
     990             :         // If ProcessFallback fails, then we have to send out the
     991             :         // OnStart/OnStop notifications.
     992           0 :         LOG(("ProcessFallback failed [rv=%" PRIx32 ", %d]\n",
     993             :              static_cast<uint32_t>(rv), mFallingBack));
     994           0 :         mStatus = NS_FAILED(rv) ? rv : NS_ERROR_DOCUMENT_NOT_CACHED;
     995           0 :         DoNotifyListener();
     996             :     }
     997             : 
     998           0 :     mIsPending = false;
     999             : 
    1000           0 :     if (mLoadGroup)
    1001           0 :         mLoadGroup->RemoveRequest(this, nullptr, mStatus);
    1002             : 
    1003           0 :     return rv;
    1004             : }
    1005             : 
    1006             : void
    1007           3 : nsHttpChannel::SetupTransactionRequestContext()
    1008             : {
    1009           3 :     if (!EnsureRequestContextID()) {
    1010           4 :         return;
    1011             :     }
    1012             : 
    1013             :     nsIRequestContextService *rcsvc =
    1014           1 :         gHttpHandler->GetRequestContextService();
    1015           1 :     if (!rcsvc) {
    1016           0 :         return;
    1017             :     }
    1018             : 
    1019           2 :     nsCOMPtr<nsIRequestContext> rc;
    1020           1 :     nsresult rv = rcsvc->GetRequestContext(mRequestContextID,
    1021           2 :                                            getter_AddRefs(rc));
    1022             : 
    1023           1 :     if (NS_FAILED(rv)) {
    1024           0 :         return;
    1025             :     }
    1026             : 
    1027           1 :     mTransaction->SetRequestContext(rc);
    1028             : }
    1029             : 
    1030             : nsresult
    1031           3 : nsHttpChannel::SetupTransaction()
    1032             : {
    1033           3 :     LOG(("nsHttpChannel::SetupTransaction [this=%p]\n", this));
    1034             : 
    1035           3 :     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED);
    1036             : 
    1037             :     nsresult rv;
    1038             : 
    1039           3 :     mUsedNetwork = 1;
    1040             : 
    1041           3 :     if (!mAllowSpdy) {
    1042           0 :         mCaps |= NS_HTTP_DISALLOW_SPDY;
    1043             :     }
    1044           3 :     if (mBeConservative) {
    1045           0 :         mCaps |= NS_HTTP_BE_CONSERVATIVE;
    1046             :     }
    1047             : 
    1048             :     // Use the URI path if not proxying (transparent proxying such as proxy
    1049             :     // CONNECT does not count here). Also figure out what HTTP version to use.
    1050           6 :     nsAutoCString buf, path;
    1051             :     nsCString* requestURI;
    1052             : 
    1053             :     // This is the normal e2e H1 path syntax "/index.html"
    1054           3 :     rv = mURI->GetPath(path);
    1055           3 :     if (NS_FAILED(rv)) {
    1056           0 :         return rv;
    1057             :     }
    1058             : 
    1059             :     // path may contain UTF-8 characters, so ensure that they're escaped.
    1060           3 :     if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII, buf)) {
    1061           0 :         requestURI = &buf;
    1062             :     } else {
    1063           3 :         requestURI = &path;
    1064             :     }
    1065             : 
    1066             :     // trim off the #ref portion if any...
    1067           3 :     int32_t ref1 = requestURI->FindChar('#');
    1068           3 :     if (ref1 != kNotFound) {
    1069           0 :         requestURI->SetLength(ref1);
    1070             :     }
    1071             : 
    1072           3 :     if (mConnectionInfo->UsingConnect() || !mConnectionInfo->UsingHttpProxy()) {
    1073           3 :         mRequestHead.SetVersion(gHttpHandler->HttpVersion());
    1074             :     }
    1075             :     else {
    1076           0 :         mRequestHead.SetPath(*requestURI);
    1077             : 
    1078             :         // RequestURI should be the absolute uri H1 proxy syntax "http://foo/index.html"
    1079             :         // so we will overwrite the relative version in requestURI
    1080           0 :         rv = mURI->GetUserPass(buf);
    1081           0 :         if (NS_FAILED(rv)) return rv;
    1082           0 :         if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) ||
    1083           0 :                                 strncmp(mSpec.get(), "https:", 6) == 0)) {
    1084           0 :             nsCOMPtr<nsIURI> tempURI;
    1085           0 :             rv = mURI->Clone(getter_AddRefs(tempURI));
    1086           0 :             if (NS_FAILED(rv)) return rv;
    1087           0 :             rv = tempURI->SetUserPass(EmptyCString());
    1088           0 :             if (NS_FAILED(rv)) return rv;
    1089           0 :             rv = tempURI->GetAsciiSpec(path);
    1090           0 :             if (NS_FAILED(rv)) return rv;
    1091           0 :             requestURI = &path;
    1092             :         } else {
    1093           0 :             requestURI = &mSpec;
    1094             :         }
    1095             : 
    1096             :         // trim off the #ref portion if any...
    1097           0 :         int32_t ref2 = requestURI->FindChar('#');
    1098           0 :         if (ref2 != kNotFound) {
    1099           0 :             requestURI->SetLength(ref2);
    1100             :         }
    1101             : 
    1102           0 :         mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion());
    1103             :     }
    1104             : 
    1105           3 :     mRequestHead.SetRequestURI(*requestURI);
    1106             : 
    1107             :     // set the request time for cache expiration calculations
    1108           3 :     mRequestTime = NowInSeconds();
    1109           3 :     mRequestTimeInitialized = true;
    1110             : 
    1111             :     // if doing a reload, force end-to-end
    1112           3 :     if (mLoadFlags & LOAD_BYPASS_CACHE) {
    1113             :         // We need to send 'Pragma:no-cache' to inhibit proxy caching even if
    1114             :         // no proxy is configured since we might be talking with a transparent
    1115             :         // proxy, i.e. one that operates at the network level.  See bug #14772.
    1116           1 :         rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
    1117           1 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
    1118             :         // If we're configured to speak HTTP/1.1 then also send 'Cache-control:
    1119             :         // no-cache'
    1120           1 :         if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1) {
    1121           1 :             rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true);
    1122           1 :             MOZ_ASSERT(NS_SUCCEEDED(rv));
    1123             :         }
    1124             :     }
    1125           2 :     else if ((mLoadFlags & VALIDATE_ALWAYS) && !mCacheEntryIsWriteOnly) {
    1126             :         // We need to send 'Cache-Control: max-age=0' to force each cache along
    1127             :         // the path to the origin server to revalidate its own entry, if any,
    1128             :         // with the next cache or server.  See bug #84847.
    1129             :         //
    1130             :         // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache'
    1131           0 :         if (mRequestHead.Version() >= NS_HTTP_VERSION_1_1)
    1132           0 :             rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "max-age=0", true);
    1133             :         else
    1134           0 :             rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
    1135           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
    1136             :     }
    1137             : 
    1138           3 :     if (mResuming) {
    1139             :         char byteRange[32];
    1140           0 :         SprintfLiteral(byteRange, "bytes=%" PRIu64 "-", mStartPos);
    1141           0 :         rv = mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(byteRange));
    1142           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
    1143             : 
    1144           0 :         if (!mEntityID.IsEmpty()) {
    1145             :             // Also, we want an error if this resource changed in the meantime
    1146             :             // Format of the entity id is: escaped_etag/size/lastmod
    1147           0 :             nsCString::const_iterator start, end, slash;
    1148           0 :             mEntityID.BeginReading(start);
    1149           0 :             mEntityID.EndReading(end);
    1150           0 :             mEntityID.BeginReading(slash);
    1151             : 
    1152           0 :             if (FindCharInReadable('/', slash, end)) {
    1153           0 :                 nsAutoCString ifMatch;
    1154           0 :                 rv = mRequestHead.SetHeader(nsHttp::If_Match,
    1155           0 :                         NS_UnescapeURL(Substring(start, slash), 0, ifMatch));
    1156           0 :                 MOZ_ASSERT(NS_SUCCEEDED(rv));
    1157             : 
    1158           0 :                 ++slash; // Incrementing, so that searching for '/' won't find
    1159             :                          // the same slash again
    1160             :             }
    1161             : 
    1162           0 :             if (FindCharInReadable('/', slash, end)) {
    1163           0 :                 rv = mRequestHead.SetHeader(nsHttp::If_Unmodified_Since,
    1164           0 :                                             Substring(++slash, end));
    1165           0 :                 MOZ_ASSERT(NS_SUCCEEDED(rv));
    1166             :             }
    1167             :         }
    1168             :     }
    1169             : 
    1170             :     // create wrapper for this channel's notification callbacks
    1171           6 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
    1172           3 :     NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
    1173           6 :                                            getter_AddRefs(callbacks));
    1174             : 
    1175             :     // create the transaction object
    1176           3 :     mTransaction = new nsHttpTransaction();
    1177           3 :     LOG(("nsHttpChannel %p created nsHttpTransaction %p\n", this, mTransaction.get()));
    1178           3 :     mTransaction->SetTransactionObserver(mTransactionObserver);
    1179           3 :     mTransactionObserver = nullptr;
    1180             : 
    1181             :     // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.
    1182           3 :     if (mLoadFlags & LOAD_ANONYMOUS)
    1183           0 :         mCaps |= NS_HTTP_LOAD_ANONYMOUS;
    1184             : 
    1185           3 :     if (mTimingEnabled)
    1186           1 :         mCaps |= NS_HTTP_TIMING_ENABLED;
    1187             : 
    1188           3 :     if (mUpgradeProtocolCallback) {
    1189           0 :         rv = mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false);
    1190           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
    1191           0 :         rv = mRequestHead.SetHeaderOnce(nsHttp::Connection,
    1192             :                                         nsHttp::Upgrade.get(),
    1193           0 :                                         true);
    1194           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
    1195           0 :         mCaps |=  NS_HTTP_STICKY_CONNECTION;
    1196           0 :         mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
    1197             :     }
    1198             : 
    1199           3 :     if (mPushedStream) {
    1200           0 :         mTransaction->SetPushedStream(mPushedStream);
    1201           0 :         mPushedStream = nullptr;
    1202             :     }
    1203             : 
    1204           6 :     nsCOMPtr<nsIHttpPushListener> pushListener;
    1205           3 :     NS_QueryNotificationCallbacks(mCallbacks,
    1206             :                                   mLoadGroup,
    1207             :                                   NS_GET_IID(nsIHttpPushListener),
    1208           6 :                                   getter_AddRefs(pushListener));
    1209           3 :     if (pushListener) {
    1210           0 :         mCaps |= NS_HTTP_ONPUSH_LISTENER;
    1211             :     }
    1212             : 
    1213           3 :     EnsureTopLevelOuterContentWindowId();
    1214             : 
    1215           6 :     nsCOMPtr<nsIAsyncInputStream> responseStream;
    1216           6 :     rv = mTransaction->Init(mCaps, mConnectionInfo, &mRequestHead,
    1217             :                             mUploadStream, mReqContentLength,
    1218             :                             mUploadStreamHasHeaders,
    1219             :                             GetCurrentThreadEventTarget(), callbacks, this,
    1220             :                             mTopLevelOuterContentWindowId,
    1221           9 :                             getter_AddRefs(responseStream));
    1222           3 :     if (NS_FAILED(rv)) {
    1223           0 :         mTransaction = nullptr;
    1224           0 :         return rv;
    1225             :     }
    1226             : 
    1227           3 :     mTransaction->SetClassOfService(mClassOfService);
    1228           3 :     SetupTransactionRequestContext();
    1229             : 
    1230           6 :     rv = nsInputStreamPump::Create(getter_AddRefs(mTransactionPump),
    1231           6 :                                    responseStream);
    1232           3 :     return rv;
    1233             : }
    1234             : 
    1235             : // NOTE: This function duplicates code from nsBaseChannel. This will go away
    1236             : // once HTTP uses nsBaseChannel (part of bug 312760)
    1237             : static void
    1238           1 : CallTypeSniffers(void *aClosure, const uint8_t *aData, uint32_t aCount)
    1239             : {
    1240           1 :   nsIChannel *chan = static_cast<nsIChannel*>(aClosure);
    1241             : 
    1242           2 :   nsAutoCString newType;
    1243           1 :   NS_SniffContent(NS_CONTENT_SNIFFER_CATEGORY, chan, aData, aCount, newType);
    1244           1 :   if (!newType.IsEmpty()) {
    1245           0 :     chan->SetContentType(newType);
    1246             :   }
    1247           1 : }
    1248             : 
    1249             : // Helper Function to report messages to the console when loading
    1250             : // a resource was blocked due to a MIME type mismatch.
    1251             : void
    1252           0 : ReportTypeBlocking(nsIURI* aURI,
    1253             :                    nsILoadInfo* aLoadInfo,
    1254             :                    const char* aMessageName)
    1255             : {
    1256           0 :     NS_ConvertUTF8toUTF16 specUTF16(aURI->GetSpecOrDefault());
    1257           0 :     const char16_t* params[] = { specUTF16.get() };
    1258           0 :     nsCOMPtr<nsIDocument> doc;
    1259           0 :     if (aLoadInfo) {
    1260           0 :         nsCOMPtr<nsIDOMDocument> domDoc;
    1261           0 :         aLoadInfo->GetLoadingDocument(getter_AddRefs(domDoc));
    1262           0 :         if (domDoc) {
    1263           0 :             doc = do_QueryInterface(domDoc);
    1264             :         }
    1265             :     }
    1266           0 :     nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
    1267           0 :                                     NS_LITERAL_CSTRING("MIMEMISMATCH"),
    1268             :                                     doc,
    1269             :                                     nsContentUtils::eSECURITY_PROPERTIES,
    1270             :                                     aMessageName,
    1271           0 :                                     params, ArrayLength(params));
    1272           0 : }
    1273             : 
    1274             : // Check and potentially enforce X-Content-Type-Options: nosniff
    1275             : nsresult
    1276           6 : ProcessXCTO(nsIURI* aURI, nsHttpResponseHead* aResponseHead, nsILoadInfo* aLoadInfo)
    1277             : {
    1278           6 :     if (!aURI || !aResponseHead || !aLoadInfo) {
    1279             :         // if there is no uri, no response head or no loadInfo, then there is nothing to do
    1280           0 :         return NS_OK;
    1281             :     }
    1282             : 
    1283             :     // 1) Query the XCTO header and check if 'nosniff' is the first value.
    1284          12 :     nsAutoCString contentTypeOptionsHeader;
    1285           6 :     Unused << aResponseHead->GetHeader(nsHttp::X_Content_Type_Options,
    1286             :                                        contentTypeOptionsHeader);
    1287           6 :     if (contentTypeOptionsHeader.IsEmpty()) {
    1288             :         // if there is no XCTO header, then there is nothing to do.
    1289           6 :         return NS_OK;
    1290             :     }
    1291             :     // XCTO header might contain multiple values which are comma separated, so:
    1292             :     // a) let's skip all subsequent values
    1293             :     //     e.g. "   NoSniFF   , foo " will be "   NoSniFF   "
    1294           0 :     int32_t idx = contentTypeOptionsHeader.Find(",");
    1295           0 :     if (idx > 0) {
    1296           0 :       contentTypeOptionsHeader = Substring(contentTypeOptionsHeader, 0, idx);
    1297             :     }
    1298             :     // b) let's trim all surrounding whitespace
    1299             :     //    e.g. "   NoSniFF   " -> "NoSniFF"
    1300           0 :     contentTypeOptionsHeader.StripWhitespace();
    1301             :     // c) let's compare the header (ignoring case)
    1302             :     //    e.g. "NoSniFF" -> "nosniff"
    1303             :     //    if it's not 'nosniff' then there is nothing to do here
    1304           0 :     if (!contentTypeOptionsHeader.EqualsIgnoreCase("nosniff")) {
    1305             :         // since we are getting here, the XCTO header was sent;
    1306             :         // a non matching value most likely means a mistake happenend;
    1307             :         // e.g. sending 'nosnif' instead of 'nosniff', let's log a warning.
    1308           0 :         NS_ConvertUTF8toUTF16 char16_header(contentTypeOptionsHeader);
    1309           0 :         const char16_t* params[] = { char16_header.get() };
    1310           0 :         nsCOMPtr<nsIDocument> doc;
    1311           0 :         nsCOMPtr<nsIDOMDocument> domDoc;
    1312           0 :         aLoadInfo->GetLoadingDocument(getter_AddRefs(domDoc));
    1313           0 :         if (domDoc) {
    1314           0 :           doc = do_QueryInterface(domDoc);
    1315             :         }
    1316           0 :         nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
    1317           0 :                                         NS_LITERAL_CSTRING("XCTO"),
    1318             :                                         doc,
    1319             :                                         nsContentUtils::eSECURITY_PROPERTIES,
    1320             :                                         "XCTOHeaderValueMissing",
    1321           0 :                                         params, ArrayLength(params));
    1322           0 :         return NS_OK;
    1323             :     }
    1324             : 
    1325             :     // 2) Query the content type from the channel
    1326           0 :     nsAutoCString contentType;
    1327           0 :     aResponseHead->ContentType(contentType);
    1328             : 
    1329             :     // 3) Compare the expected MIME type with the actual type
    1330           0 :     if (aLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_STYLESHEET) {
    1331           0 :         if (contentType.EqualsLiteral(TEXT_CSS)) {
    1332           0 :             return NS_OK;
    1333             :         }
    1334           0 :         ReportTypeBlocking(aURI, aLoadInfo, "MimeTypeMismatch");
    1335           0 :         return NS_ERROR_CORRUPTED_CONTENT;
    1336             :     }
    1337             : 
    1338           0 :     if (aLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_IMAGE) {
    1339           0 :         if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("image/"))) {
    1340           0 :             Accumulate(Telemetry::XCTO_NOSNIFF_BLOCK_IMAGE, 0);
    1341           0 :             return NS_OK;
    1342             :         }
    1343           0 :         Accumulate(Telemetry::XCTO_NOSNIFF_BLOCK_IMAGE, 1);
    1344             :         // Instead of consulting Preferences::GetBool() all the time we
    1345             :         // can cache the result to speed things up.
    1346             :         static bool sXCTONosniffBlockImages = false;
    1347             :         static bool sIsInited = false;
    1348           0 :         if (!sIsInited) {
    1349           0 :             sIsInited = true;
    1350             :             Preferences::AddBoolVarCache(&sXCTONosniffBlockImages,
    1351           0 :             "security.xcto_nosniff_block_images");
    1352             :         }
    1353           0 :         if (!sXCTONosniffBlockImages) {
    1354           0 :             return NS_OK;
    1355             :         }
    1356           0 :         ReportTypeBlocking(aURI, aLoadInfo, "MimeTypeMismatch");
    1357           0 :         return NS_ERROR_CORRUPTED_CONTENT;
    1358             :     }
    1359             : 
    1360           0 :     if (aLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_SCRIPT) {
    1361           0 :         if (nsContentUtils::IsScriptType(contentType)) {
    1362           0 :             return NS_OK;
    1363             :         }
    1364           0 :         ReportTypeBlocking(aURI, aLoadInfo, "MimeTypeMismatch");
    1365           0 :         return NS_ERROR_CORRUPTED_CONTENT;
    1366             :     }
    1367           0 :     return NS_OK;
    1368             : }
    1369             : 
    1370             : // Ensure that a load of type script has correct MIME type
    1371             : nsresult
    1372           6 : EnsureMIMEOfScript(nsIURI* aURI, nsHttpResponseHead* aResponseHead, nsILoadInfo* aLoadInfo)
    1373             : {
    1374           6 :     if (!aURI || !aResponseHead || !aLoadInfo) {
    1375             :         // if there is no uri, no response head or no loadInfo, then there is nothing to do
    1376           0 :         return NS_OK;
    1377             :     }
    1378             : 
    1379           6 :     if (aLoadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_SCRIPT) {
    1380             :         // if this is not a script load, then there is nothing to do
    1381           4 :         return NS_OK;
    1382             :     }
    1383             : 
    1384           4 :     nsAutoCString contentType;
    1385           2 :     aResponseHead->ContentType(contentType);
    1386           4 :     NS_ConvertUTF8toUTF16 typeString(contentType);
    1387             : 
    1388           2 :     if (nsContentUtils::IsJavascriptMIMEType(typeString)) {
    1389             :         // script load has type script
    1390           2 :         Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 1);
    1391           2 :         return NS_OK;
    1392             :     }
    1393             : 
    1394           0 :     bool block = false;
    1395           0 :     if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("image/"))) {
    1396             :         // script load has type image
    1397           0 :         Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 2);
    1398           0 :         block = true;
    1399           0 :     } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("audio/"))) {
    1400             :         // script load has type audio
    1401           0 :         Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 3);
    1402           0 :         block = true;
    1403           0 :     } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("video/"))) {
    1404             :         // script load has type video
    1405           0 :         Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 4);
    1406           0 :         block = true;
    1407           0 :     } else if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/csv"))) {
    1408             :         // script load has type text/csv
    1409           0 :         Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 6);
    1410           0 :         block = true;
    1411             :     }
    1412             : 
    1413           0 :     if (block) {
    1414             :         // Instead of consulting Preferences::GetBool() all the time we
    1415             :         // can cache the result to speed things up.
    1416             :         static bool sCachedBlockScriptWithWrongMime = false;
    1417             :         static bool sIsInited = false;
    1418           0 :         if (!sIsInited) {
    1419           0 :             sIsInited = true;
    1420             :             Preferences::AddBoolVarCache(&sCachedBlockScriptWithWrongMime,
    1421           0 :             "security.block_script_with_wrong_mime");
    1422             :         }
    1423             : 
    1424             :         // Do not block the load if the feature is not enabled.
    1425           0 :         if (!sCachedBlockScriptWithWrongMime) {
    1426           0 :             return NS_OK;
    1427             :         }
    1428             : 
    1429           0 :         ReportTypeBlocking(aURI, aLoadInfo, "BlockScriptWithWrongMimeType");
    1430           0 :         return NS_ERROR_CORRUPTED_CONTENT;
    1431             :     }
    1432             : 
    1433           0 :     if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/plain"))) {
    1434             :         // script load has type text/plain
    1435           0 :         Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 5);
    1436           0 :         return NS_OK;
    1437             :     }
    1438             : 
    1439           0 :     if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/xml"))) {
    1440             :         // script load has type text/xml
    1441           0 :         Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 7);
    1442           0 :         return NS_OK;
    1443             :     }
    1444             : 
    1445           0 :     if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("application/octet-stream"))) {
    1446             :         // script load has type application/octet-stream
    1447           0 :         Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 8);
    1448           0 :         return NS_OK;
    1449             :     }
    1450             : 
    1451           0 :     if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("application/xml"))) {
    1452             :         // script load has type application/xml
    1453           0 :         Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 9);
    1454           0 :         return NS_OK;
    1455             :     }
    1456             : 
    1457           0 :     if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("text/html"))) {
    1458             :         // script load has type text/html
    1459           0 :         Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 10);
    1460           0 :         return NS_OK;
    1461             :     }
    1462             : 
    1463           0 :     if (contentType.IsEmpty()) {
    1464             :         // script load has no type
    1465           0 :         Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 11);
    1466           0 :         return NS_OK;
    1467             :     }
    1468             : 
    1469             :     // script load has unknown type
    1470           0 :     Telemetry::Accumulate(Telemetry::SCRIPT_BLOCK_INCORRECT_MIME, 0);
    1471           0 :     return NS_OK;
    1472             : }
    1473             : 
    1474             : 
    1475             : nsresult
    1476           6 : nsHttpChannel::CallOnStartRequest()
    1477             : {
    1478           6 :     LOG(("nsHttpChannel::CallOnStartRequest [this=%p]", this));
    1479             : 
    1480           6 :     MOZ_RELEASE_ASSERT(!(mRequireCORSPreflight &&
    1481             :                          mInterceptCache != INTERCEPTED) ||
    1482             :                        mIsCorsPreflightDone,
    1483             :                        "CORS preflight must have been finished by the time we "
    1484             :                        "call OnStartRequest");
    1485             : 
    1486           6 :     if (mOnStartRequestCalled) {
    1487             :         // This can only happen when a range request loading rest of the data
    1488             :         // after interrupted concurrent cache read asynchronously failed, e.g.
    1489             :         // the response range bytes are not as expected or this channel has
    1490             :         // been externally canceled.
    1491             :         //
    1492             :         // It's legal to bypass CallOnStartRequest for that case since we've
    1493             :         // already called OnStartRequest on our listener and also added all
    1494             :         // content converters before.
    1495           0 :         MOZ_ASSERT(mConcurrentCacheAccess);
    1496           0 :         LOG(("CallOnStartRequest already invoked before"));
    1497           0 :         return mStatus;
    1498             :     }
    1499             : 
    1500           6 :     mTracingEnabled = false;
    1501             : 
    1502             :     // Ensure mListener->OnStartRequest will be invoked before exiting
    1503             :     // this function.
    1504           0 :     auto onStartGuard = MakeScopeExit([&] {
    1505           0 :         LOG(("  calling mListener->OnStartRequest by ScopeExit [this=%p, "
    1506             :              "listener=%p]\n", this, mListener.get()));
    1507           0 :         MOZ_ASSERT(!mOnStartRequestCalled);
    1508             : 
    1509           0 :         if (mListener) {
    1510           0 :             nsCOMPtr<nsIStreamListener> deleteProtector(mListener);
    1511           0 :             deleteProtector->OnStartRequest(this, mListenerContext);
    1512             :         }
    1513             : 
    1514           0 :         mOnStartRequestCalled = true;
    1515          12 :     });
    1516             : 
    1517           6 :     nsresult rv = EnsureMIMEOfScript(mURI, mResponseHead, mLoadInfo);
    1518           6 :     NS_ENSURE_SUCCESS(rv, rv);
    1519             : 
    1520           6 :     rv = ProcessXCTO(mURI, mResponseHead, mLoadInfo);
    1521           6 :     NS_ENSURE_SUCCESS(rv, rv);
    1522             : 
    1523             :     // Allow consumers to override our content type
    1524           6 :     if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
    1525             :         // NOTE: We can have both a txn pump and a cache pump when the cache
    1526             :         // content is partial. In that case, we need to read from the cache,
    1527             :         // because that's the one that has the initial contents. If that fails
    1528             :         // then give the transaction pump a shot.
    1529             : 
    1530           1 :         nsIChannel* thisChannel = static_cast<nsIChannel*>(this);
    1531             : 
    1532           1 :         bool typeSniffersCalled = false;
    1533           1 :         if (mCachePump) {
    1534           0 :           typeSniffersCalled =
    1535           0 :             NS_SUCCEEDED(mCachePump->PeekStream(CallTypeSniffers, thisChannel));
    1536             :         }
    1537             : 
    1538           1 :         if (!typeSniffersCalled && mTransactionPump) {
    1539           1 :           mTransactionPump->PeekStream(CallTypeSniffers, thisChannel);
    1540             :         }
    1541             :     }
    1542             : 
    1543           6 :     bool unknownDecoderStarted = false;
    1544           6 :     if (mResponseHead && !mResponseHead->HasContentType()) {
    1545           0 :         MOZ_ASSERT(mConnectionInfo, "Should have connection info here");
    1546           0 :         if (!mContentTypeHint.IsEmpty())
    1547           0 :             mResponseHead->SetContentType(mContentTypeHint);
    1548           0 :         else if (mResponseHead->Version() == NS_HTTP_VERSION_0_9 &&
    1549           0 :                  mConnectionInfo->OriginPort() != mConnectionInfo->DefaultPort())
    1550           0 :             mResponseHead->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN));
    1551             :         else {
    1552             :             // Uh-oh.  We had better find out what type we are!
    1553           0 :             nsCOMPtr<nsIStreamConverterService> serv;
    1554             :             rv = gHttpHandler->
    1555           0 :                 GetStreamConverterService(getter_AddRefs(serv));
    1556             :             // If we failed, we just fall through to the "normal" case
    1557           0 :             if (NS_SUCCEEDED(rv)) {
    1558           0 :                 nsCOMPtr<nsIStreamListener> converter;
    1559           0 :                 rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
    1560             :                                             "*/*",
    1561             :                                             mListener,
    1562             :                                             mListenerContext,
    1563           0 :                                             getter_AddRefs(converter));
    1564           0 :                 if (NS_SUCCEEDED(rv)) {
    1565           0 :                     mListener = converter;
    1566           0 :                     unknownDecoderStarted = true;
    1567             :                 }
    1568             :             }
    1569             :         }
    1570             :     }
    1571             : 
    1572           6 :     if (mResponseHead && !mResponseHead->HasContentCharset())
    1573           5 :         mResponseHead->SetContentCharset(mContentCharsetHint);
    1574             : 
    1575           6 :     if (mResponseHead && mCacheEntry) {
    1576             :         // If we have a cache entry, set its predicted size to TotalEntitySize to
    1577             :         // avoid caching an entry that will exceed the max size limit.
    1578          10 :         rv = mCacheEntry->SetPredictedDataSize(
    1579          10 :             mResponseHead->TotalEntitySize());
    1580           5 :         if (NS_ERROR_FILE_TOO_BIG == rv) {
    1581             :           // Don't throw the entry away, we will need it later.
    1582           0 :           LOG(("  entry too big"));
    1583             :         } else {
    1584           5 :           NS_ENSURE_SUCCESS(rv, rv);
    1585             :         }
    1586             :     }
    1587             : 
    1588           6 :     LOG(("  calling mListener->OnStartRequest [this=%p, listener=%p]\n", this, mListener.get()));
    1589             : 
    1590             :     // About to call OnStartRequest, dismiss the guard object.
    1591           6 :     onStartGuard.release();
    1592             : 
    1593           6 :     if (mListener) {
    1594           6 :         MOZ_ASSERT(!mOnStartRequestCalled,
    1595             :                    "We should not call OsStartRequest twice");
    1596          11 :         nsCOMPtr<nsIStreamListener> deleteProtector(mListener);
    1597           6 :         rv = deleteProtector->OnStartRequest(this, mListenerContext);
    1598           6 :         mOnStartRequestCalled = true;
    1599           6 :         if (NS_FAILED(rv))
    1600           1 :             return rv;
    1601             :     } else {
    1602           0 :         NS_WARNING("OnStartRequest skipped because of null listener");
    1603           0 :         mOnStartRequestCalled = true;
    1604             :     }
    1605             : 
    1606             :     // Install stream converter if required.
    1607             :     // If we use unknownDecoder, stream converters will be installed later (in
    1608             :     // nsUnknownDecoder) after OnStartRequest is called for the real listener.
    1609           5 :     if (!unknownDecoderStarted) {
    1610          10 :       nsCOMPtr<nsIStreamListener> listener;
    1611           5 :       nsISupports *ctxt = mListenerContext;
    1612           5 :       rv = DoApplyContentConversions(mListener, getter_AddRefs(listener), ctxt);
    1613           5 :       if (NS_FAILED(rv)) {
    1614           0 :         return rv;
    1615             :       }
    1616           5 :       if (listener) {
    1617           0 :         mListener = listener;
    1618           0 :         mCompressListener = listener;
    1619             :       }
    1620             :     }
    1621             : 
    1622             :     // if this channel is for a download, close off access to the cache.
    1623           5 :     if (mCacheEntry && mChannelIsForDownload) {
    1624           0 :         mCacheEntry->AsyncDoom(nullptr);
    1625             : 
    1626             :         // We must keep the cache entry in case of partial request.
    1627             :         // Concurrent access is the same, we need the entry in
    1628             :         // OnStopRequest.
    1629           0 :         if (!mCachedContentIsPartial && !mConcurrentCacheAccess)
    1630           0 :             CloseCacheEntry(false);
    1631             :     }
    1632             : 
    1633           5 :     if (!mCanceled) {
    1634             :         // create offline cache entry if offline caching was requested
    1635           5 :         if (ShouldUpdateOfflineCacheEntry()) {
    1636           0 :             LOG(("writing to the offline cache"));
    1637           0 :             rv = InitOfflineCacheEntry();
    1638           0 :             if (NS_FAILED(rv)) return rv;
    1639             : 
    1640             :             // InitOfflineCacheEntry may have closed mOfflineCacheEntry
    1641           0 :             if (mOfflineCacheEntry) {
    1642           0 :                 rv = InstallOfflineCacheListener();
    1643           0 :                 if (NS_FAILED(rv)) return rv;
    1644             :             }
    1645           5 :         } else if (mApplicationCacheForWrite) {
    1646           0 :             LOG(("offline cache is up to date, not updating"));
    1647           0 :             CloseOfflineCacheEntry();
    1648             :         }
    1649             :     }
    1650             : 
    1651             :     // Check for a Content-Signature header and inject mediator if the header is
    1652             :     // requested and available.
    1653             :     // If requested (mLoadInfo->GetVerifySignedContent), but not present, or
    1654             :     // present but not valid, fail this channel and return
    1655             :     // NS_ERROR_INVALID_SIGNATURE to indicate a signature error and trigger a
    1656             :     // fallback load in nsDocShell.
    1657             :     // Note that OnStartRequest has already been called on the target stream
    1658             :     // listener at this point. We have to add the listener here that late to
    1659             :     // ensure that it's the last listener and can thus block the load in
    1660             :     // OnStopRequest.
    1661           5 :     if (!mCanceled) {
    1662           5 :         rv = ProcessContentSignatureHeader(mResponseHead);
    1663           5 :         if (NS_FAILED(rv)) {
    1664           0 :             LOG(("Content-signature verification failed.\n"));
    1665           0 :             return rv;
    1666             :         }
    1667             :     }
    1668             : 
    1669           5 :     return NS_OK;
    1670             : }
    1671             : 
    1672             : nsresult
    1673           0 : nsHttpChannel::ProcessFailedProxyConnect(uint32_t httpStatus)
    1674             : {
    1675             :     // Failure to set up a proxy tunnel via CONNECT means one of the following:
    1676             :     // 1) Proxy wants authorization, or forbids.
    1677             :     // 2) DNS at proxy couldn't resolve target URL.
    1678             :     // 3) Proxy connection to target failed or timed out.
    1679             :     // 4) Eve intercepted our CONNECT, and is replying with malicious HTML.
    1680             :     //
    1681             :     // Our current architecture would parse the proxy's response content with
    1682             :     // the permission of the target URL.  Given #4, we must avoid rendering the
    1683             :     // body of the reply, and instead give the user a (hopefully helpful)
    1684             :     // boilerplate error page, based on just the HTTP status of the reply.
    1685             : 
    1686           0 :     MOZ_ASSERT(mConnectionInfo->UsingConnect(),
    1687             :                "proxy connect failed but not using CONNECT?");
    1688             :     nsresult rv;
    1689           0 :     switch (httpStatus)
    1690             :     {
    1691             :     case 300: case 301: case 302: case 303: case 307: case 308:
    1692             :         // Bad redirect: not top-level, or it's a POST, bad/missing Location,
    1693             :         // or ProcessRedirect() failed for some other reason.  Legal
    1694             :         // redirects that fail because site not available, etc., are handled
    1695             :         // elsewhere, in the regular codepath.
    1696           0 :         rv = NS_ERROR_CONNECTION_REFUSED;
    1697           0 :         break;
    1698             :     case 403: // HTTP/1.1: "Forbidden"
    1699             :     case 407: // ProcessAuthentication() failed
    1700             :     case 501: // HTTP/1.1: "Not Implemented"
    1701             :         // user sees boilerplate Mozilla "Proxy Refused Connection" page.
    1702           0 :         rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
    1703           0 :         break;
    1704             :     // Squid sends 404 if DNS fails (regular 404 from target is tunneled)
    1705             :     case 404: // HTTP/1.1: "Not Found"
    1706             :     // RFC 2616: "some deployed proxies are known to return 400 or 500 when
    1707             :     // DNS lookups time out."  (Squid uses 500 if it runs out of sockets: so
    1708             :     // we have a conflict here).
    1709             :     case 400: // HTTP/1.1 "Bad Request"
    1710             :     case 500: // HTTP/1.1: "Internal Server Error"
    1711             :         /* User sees: "Address Not Found: Firefox can't find the server at
    1712             :          * www.foo.com."
    1713             :          */
    1714           0 :         rv = NS_ERROR_UNKNOWN_HOST;
    1715           0 :         break;
    1716             :     case 502: // HTTP/1.1: "Bad Gateway" (invalid resp from target server)
    1717             :     // Squid returns 503 if target request fails for anything but DNS.
    1718             :     case 503: // HTTP/1.1: "Service Unavailable"
    1719             :         /* User sees: "Failed to Connect:
    1720             :          *  Firefox can't establish a connection to the server at
    1721             :          *  www.foo.com.  Though the site seems valid, the browser
    1722             :          *  was unable to establish a connection."
    1723             :          */
    1724           0 :         rv = NS_ERROR_CONNECTION_REFUSED;
    1725           0 :         break;
    1726             :     // RFC 2616 uses 504 for both DNS and target timeout, so not clear what to
    1727             :     // do here: picking target timeout, as DNS covered by 400/404/500
    1728             :     case 504: // HTTP/1.1: "Gateway Timeout"
    1729             :         // user sees: "Network Timeout: The server at www.foo.com
    1730             :         //              is taking too long to respond."
    1731           0 :         rv = NS_ERROR_NET_TIMEOUT;
    1732           0 :         break;
    1733             :     // Confused proxy server or malicious response
    1734             :     default:
    1735           0 :         rv = NS_ERROR_PROXY_CONNECTION_REFUSED;
    1736           0 :         break;
    1737             :     }
    1738           0 :     LOG(("Cancelling failed proxy CONNECT [this=%p httpStatus=%u]\n",
    1739             :          this, httpStatus));
    1740           0 :     Cancel(rv);
    1741             :     {
    1742           0 :         nsresult rv = CallOnStartRequest();
    1743           0 :         if (NS_FAILED(rv)) {
    1744           0 :             LOG(("CallOnStartRequest failed [this=%p httpStatus=%u rv=%08x]\n",
    1745             :                  this, httpStatus, static_cast<uint32_t>(rv)));
    1746             :         }
    1747             :     }
    1748           0 :     return rv;
    1749             : }
    1750             : 
    1751             : static void
    1752           0 : GetSTSConsoleErrorTag(uint32_t failureResult, nsAString& consoleErrorTag)
    1753             : {
    1754           0 :     switch (failureResult) {
    1755             :         case nsISiteSecurityService::ERROR_UNTRUSTWORTHY_CONNECTION:
    1756           0 :             consoleErrorTag = NS_LITERAL_STRING("STSUntrustworthyConnection");
    1757           0 :             break;
    1758             :         case nsISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER:
    1759           0 :             consoleErrorTag = NS_LITERAL_STRING("STSCouldNotParseHeader");
    1760           0 :             break;
    1761             :         case nsISiteSecurityService::ERROR_NO_MAX_AGE:
    1762           0 :             consoleErrorTag = NS_LITERAL_STRING("STSNoMaxAge");
    1763           0 :             break;
    1764             :         case nsISiteSecurityService::ERROR_MULTIPLE_MAX_AGES:
    1765           0 :             consoleErrorTag = NS_LITERAL_STRING("STSMultipleMaxAges");
    1766           0 :             break;
    1767             :         case nsISiteSecurityService::ERROR_INVALID_MAX_AGE:
    1768           0 :             consoleErrorTag = NS_LITERAL_STRING("STSInvalidMaxAge");
    1769           0 :             break;
    1770             :         case nsISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS:
    1771           0 :             consoleErrorTag = NS_LITERAL_STRING("STSMultipleIncludeSubdomains");
    1772           0 :             break;
    1773             :         case nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS:
    1774           0 :             consoleErrorTag = NS_LITERAL_STRING("STSInvalidIncludeSubdomains");
    1775           0 :             break;
    1776             :         case nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE:
    1777           0 :             consoleErrorTag = NS_LITERAL_STRING("STSCouldNotSaveState");
    1778           0 :             break;
    1779             :         default:
    1780           0 :             consoleErrorTag = NS_LITERAL_STRING("STSUnknownError");
    1781           0 :             break;
    1782             :     }
    1783           0 : }
    1784             : 
    1785             : static void
    1786           0 : GetPKPConsoleErrorTag(uint32_t failureResult, nsAString& consoleErrorTag)
    1787             : {
    1788           0 :     switch (failureResult) {
    1789             :         case nsISiteSecurityService::ERROR_UNTRUSTWORTHY_CONNECTION:
    1790           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPUntrustworthyConnection");
    1791           0 :             break;
    1792             :         case nsISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER:
    1793           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPCouldNotParseHeader");
    1794           0 :             break;
    1795             :         case nsISiteSecurityService::ERROR_NO_MAX_AGE:
    1796           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPNoMaxAge");
    1797           0 :             break;
    1798             :         case nsISiteSecurityService::ERROR_MULTIPLE_MAX_AGES:
    1799           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPMultipleMaxAges");
    1800           0 :             break;
    1801             :         case nsISiteSecurityService::ERROR_INVALID_MAX_AGE:
    1802           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPInvalidMaxAge");
    1803           0 :             break;
    1804             :         case nsISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS:
    1805           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPMultipleIncludeSubdomains");
    1806           0 :             break;
    1807             :         case nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS:
    1808           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPInvalidIncludeSubdomains");
    1809           0 :             break;
    1810             :         case nsISiteSecurityService::ERROR_INVALID_PIN:
    1811           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPInvalidPin");
    1812           0 :             break;
    1813             :         case nsISiteSecurityService::ERROR_MULTIPLE_REPORT_URIS:
    1814           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPMultipleReportURIs");
    1815           0 :             break;
    1816             :         case nsISiteSecurityService::ERROR_PINSET_DOES_NOT_MATCH_CHAIN:
    1817           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPPinsetDoesNotMatch");
    1818           0 :             break;
    1819             :         case nsISiteSecurityService::ERROR_NO_BACKUP_PIN:
    1820           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPNoBackupPin");
    1821           0 :             break;
    1822             :         case nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE:
    1823           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPCouldNotSaveState");
    1824           0 :             break;
    1825             :         case nsISiteSecurityService::ERROR_ROOT_NOT_BUILT_IN:
    1826           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPRootNotBuiltIn");
    1827           0 :             break;
    1828             :         default:
    1829           0 :             consoleErrorTag = NS_LITERAL_STRING("PKPUnknownError");
    1830           0 :             break;
    1831             :     }
    1832           0 : }
    1833             : 
    1834             : /**
    1835             :  * Process a single security header. Only two types are supported: HSTS and HPKP.
    1836             :  */
    1837             : nsresult
    1838           0 : nsHttpChannel::ProcessSingleSecurityHeader(uint32_t aType,
    1839             :                                            nsISSLStatus *aSSLStatus,
    1840             :                                            uint32_t aFlags)
    1841             : {
    1842             :     nsHttpAtom atom;
    1843           0 :     switch (aType) {
    1844             :         case nsISiteSecurityService::HEADER_HSTS:
    1845           0 :             atom = nsHttp::ResolveAtom("Strict-Transport-Security");
    1846           0 :             break;
    1847             :         case nsISiteSecurityService::HEADER_HPKP:
    1848           0 :             atom = nsHttp::ResolveAtom("Public-Key-Pins");
    1849           0 :             break;
    1850             :         default:
    1851           0 :             NS_NOTREACHED("Invalid security header type");
    1852           0 :             return NS_ERROR_FAILURE;
    1853             :     }
    1854             : 
    1855           0 :     nsAutoCString securityHeader;
    1856           0 :     nsresult rv = mResponseHead->GetHeader(atom, securityHeader);
    1857           0 :     if (NS_SUCCEEDED(rv)) {
    1858           0 :         nsISiteSecurityService* sss = gHttpHandler->GetSSService();
    1859           0 :         NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
    1860             :         // Process header will now discard the headers itself if the channel
    1861             :         // wasn't secure (whereas before it had to be checked manually)
    1862           0 :         OriginAttributes originAttributes;
    1863           0 :         NS_GetOriginAttributes(this, originAttributes);
    1864             :         uint32_t failureResult;
    1865           0 :         uint32_t headerSource = nsISiteSecurityService::SOURCE_ORGANIC_REQUEST;
    1866           0 :         if (mLoadInfo && mLoadInfo->GetIsHSTSPriming()) {
    1867           0 :             headerSource = nsISiteSecurityService::SOURCE_HSTS_PRIMING;
    1868             :         }
    1869           0 :         rv = sss->ProcessHeader(aType, mURI, securityHeader, aSSLStatus,
    1870             :                                 aFlags, headerSource, originAttributes,
    1871           0 :                                 nullptr, nullptr, &failureResult);
    1872           0 :         if (NS_FAILED(rv)) {
    1873           0 :             nsAutoString consoleErrorCategory;
    1874           0 :             nsAutoString consoleErrorTag;
    1875           0 :             switch (aType) {
    1876             :                 case nsISiteSecurityService::HEADER_HSTS:
    1877           0 :                     GetSTSConsoleErrorTag(failureResult, consoleErrorTag);
    1878           0 :                     consoleErrorCategory = NS_LITERAL_STRING("Invalid HSTS Headers");
    1879           0 :                     break;
    1880             :                 case nsISiteSecurityService::HEADER_HPKP:
    1881           0 :                     GetPKPConsoleErrorTag(failureResult, consoleErrorTag);
    1882           0 :                     consoleErrorCategory = NS_LITERAL_STRING("Invalid HPKP Headers");
    1883           0 :                     break;
    1884             :                 default:
    1885           0 :                     return NS_ERROR_FAILURE;
    1886             :             }
    1887           0 :             Unused << AddSecurityMessage(consoleErrorTag, consoleErrorCategory);
    1888           0 :             LOG(("nsHttpChannel: Failed to parse %s header, continuing load.\n",
    1889             :                  atom.get()));
    1890             :         }
    1891             :     } else {
    1892           0 :         if (rv != NS_ERROR_NOT_AVAILABLE) {
    1893             :             // All other errors are fatal
    1894           0 :             NS_ENSURE_SUCCESS(rv, rv);
    1895             :         }
    1896           0 :         LOG(("nsHttpChannel: No %s header, continuing load.\n",
    1897             :              atom.get()));
    1898             :     }
    1899           0 :     return NS_OK;
    1900             : }
    1901             : 
    1902             : /**
    1903             :  * Decide whether or not to remember Strict-Transport-Security, and whether
    1904             :  * or not to enforce channel integrity.
    1905             :  *
    1906             :  * @return NS_ERROR_FAILURE if there's security information missing even though
    1907             :  *             it's an HTTPS connection.
    1908             :  */
    1909             : nsresult
    1910           3 : nsHttpChannel::ProcessSecurityHeaders()
    1911             : {
    1912             :     nsresult rv;
    1913           3 :     bool isHttps = false;
    1914           3 :     rv = mURI->SchemeIs("https", &isHttps);
    1915           3 :     NS_ENSURE_SUCCESS(rv, rv);
    1916             : 
    1917             :     // If this channel is not loading securely, STS or PKP doesn't do anything.
    1918             :     // In the case of HSTS, the upgrade to HTTPS takes place earlier in the
    1919             :     // channel load process.
    1920           3 :     if (!isHttps)
    1921           3 :         return NS_OK;
    1922             : 
    1923           0 :     nsAutoCString asciiHost;
    1924           0 :     rv = mURI->GetAsciiHost(asciiHost);
    1925           0 :     NS_ENSURE_SUCCESS(rv, NS_OK);
    1926             : 
    1927             :     // If the channel is not a hostname, but rather an IP, do not process STS
    1928             :     // or PKP headers
    1929             :     PRNetAddr hostAddr;
    1930           0 :     if (PR_SUCCESS == PR_StringToNetAddr(asciiHost.get(), &hostAddr))
    1931           0 :         return NS_OK;
    1932             : 
    1933             :     // mSecurityInfo may not always be present, and if it's not then it is okay
    1934             :     // to just disregard any security headers since we know nothing about the
    1935             :     // security of the connection.
    1936           0 :     NS_ENSURE_TRUE(mSecurityInfo, NS_OK);
    1937             : 
    1938             :     uint32_t flags =
    1939           0 :       NS_UsePrivateBrowsing(this) ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
    1940             : 
    1941             :     // Get the SSLStatus
    1942           0 :     nsCOMPtr<nsISSLStatusProvider> sslprov = do_QueryInterface(mSecurityInfo);
    1943           0 :     NS_ENSURE_TRUE(sslprov, NS_ERROR_FAILURE);
    1944           0 :     nsCOMPtr<nsISSLStatus> sslStatus;
    1945           0 :     rv = sslprov->GetSSLStatus(getter_AddRefs(sslStatus));
    1946           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1947           0 :     NS_ENSURE_TRUE(sslStatus, NS_ERROR_FAILURE);
    1948             : 
    1949           0 :     rv = ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HSTS,
    1950           0 :                                      sslStatus, flags);
    1951           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1952             : 
    1953           0 :     rv = ProcessSingleSecurityHeader(nsISiteSecurityService::HEADER_HPKP,
    1954           0 :                                      sslStatus, flags);
    1955           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1956             : 
    1957           0 :     return NS_OK;
    1958             : }
    1959             : 
    1960             : nsresult
    1961           5 : nsHttpChannel::ProcessContentSignatureHeader(nsHttpResponseHead *aResponseHead)
    1962             : {
    1963           5 :     nsresult rv = NS_OK;
    1964             : 
    1965             :     // we only do this if we require it in loadInfo
    1966           5 :     if (!mLoadInfo || !mLoadInfo->GetVerifySignedContent()) {
    1967           5 :         return NS_OK;
    1968             :     }
    1969             : 
    1970           0 :     NS_ENSURE_TRUE(aResponseHead, NS_ERROR_ABORT);
    1971           0 :     nsAutoCString contentSignatureHeader;
    1972           0 :     nsHttpAtom atom = nsHttp::ResolveAtom("Content-Signature");
    1973           0 :     rv = aResponseHead->GetHeader(atom, contentSignatureHeader);
    1974           0 :     if (NS_FAILED(rv)) {
    1975           0 :         LOG(("Content-Signature header is missing but expected."));
    1976           0 :         DoInvalidateCacheEntry(mURI);
    1977           0 :         return NS_ERROR_INVALID_SIGNATURE;
    1978             :     }
    1979             : 
    1980             :     // if we require a signature but it is empty, fail
    1981           0 :     if (contentSignatureHeader.IsEmpty()) {
    1982           0 :       DoInvalidateCacheEntry(mURI);
    1983           0 :       LOG(("An expected content-signature header is missing.\n"));
    1984           0 :       return NS_ERROR_INVALID_SIGNATURE;
    1985             :     }
    1986             : 
    1987             :     // we ensure a content type here to avoid running into problems with
    1988             :     // content sniffing, which might sniff parts of the content before we can
    1989             :     // verify the signature
    1990           0 :     if (!aResponseHead->HasContentType()) {
    1991             :         NS_WARNING("Empty content type can get us in trouble when verifying "
    1992           0 :                    "content signatures");
    1993           0 :         return NS_ERROR_INVALID_SIGNATURE;
    1994             :     }
    1995             :     // create a new listener that meadiates the content
    1996             :     RefPtr<ContentVerifier> contentVerifyingMediator =
    1997           0 :       new ContentVerifier(mListener, mListenerContext);
    1998           0 :     rv = contentVerifyingMediator->Init(contentSignatureHeader, this,
    1999           0 :                                         mListenerContext);
    2000           0 :     NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_SIGNATURE);
    2001           0 :     mListener = contentVerifyingMediator;
    2002             : 
    2003           0 :     return NS_OK;
    2004             : }
    2005             : 
    2006             : /**
    2007             :  * Decide whether or not to send a security report and, if so, give the
    2008             :  * SecurityReporter the information required to send such a report.
    2009             :  */
    2010             : void
    2011           2 : nsHttpChannel::ProcessSecurityReport(nsresult status) {
    2012             :     uint32_t errorClass;
    2013             :     nsCOMPtr<nsINSSErrorsService> errSvc =
    2014           2 :             do_GetService("@mozilla.org/nss_errors_service;1");
    2015             :     // getErrorClass will throw a generic NS_ERROR_FAILURE if the error code is
    2016             :     // not in the set of errors covered by the NSS errors service.
    2017           2 :     nsresult rv = errSvc->GetErrorClass(status, &errorClass);
    2018           2 :     if (!NS_SUCCEEDED(rv)) {
    2019           2 :         return;
    2020             :     }
    2021             : 
    2022             :     // if the content was not loaded succesfully and we have security info,
    2023             :     // send a TLS error report - we must do this early as other parts of
    2024             :     // OnStopRequest can return early
    2025             :     bool reportingEnabled =
    2026           0 :             Preferences::GetBool("security.ssl.errorReporting.enabled");
    2027             :     bool reportingAutomatic =
    2028           0 :             Preferences::GetBool("security.ssl.errorReporting.automatic");
    2029           0 :     if (!mSecurityInfo || !reportingEnabled || !reportingAutomatic) {
    2030           0 :         return;
    2031             :     }
    2032             : 
    2033             :     nsCOMPtr<nsITransportSecurityInfo> secInfo =
    2034           0 :             do_QueryInterface(mSecurityInfo);
    2035             :     nsCOMPtr<nsISecurityReporter> errorReporter =
    2036           0 :             do_GetService("@mozilla.org/securityreporter;1");
    2037             : 
    2038           0 :     if (!secInfo || !mURI) {
    2039           0 :         return;
    2040             :     }
    2041             : 
    2042           0 :     nsAutoCString hostStr;
    2043             :     int32_t port;
    2044           0 :     rv = mURI->GetHost(hostStr);
    2045           0 :     if (!NS_SUCCEEDED(rv)) {
    2046           0 :         return;
    2047             :     }
    2048             : 
    2049           0 :     rv = mURI->GetPort(&port);
    2050             : 
    2051           0 :     if (NS_SUCCEEDED(rv)) {
    2052           0 :         errorReporter->ReportTLSError(secInfo, hostStr, port);
    2053             :     }
    2054             : }
    2055             : 
    2056             : bool
    2057           6 : nsHttpChannel::IsHTTPS()
    2058             : {
    2059             :     bool isHttps;
    2060           6 :     if (NS_FAILED(mURI->SchemeIs("https", &isHttps)) || !isHttps)
    2061           6 :         return false;
    2062           0 :     return true;
    2063             : }
    2064             : 
    2065             : void
    2066           3 : nsHttpChannel::ProcessSSLInformation()
    2067             : {
    2068             :     // If this is HTTPS, record any use of RSA so that Key Exchange Algorithm
    2069             :     // can be whitelisted for TLS False Start in future sessions. We could
    2070             :     // do the same for DH but its rarity doesn't justify the lookup.
    2071             : 
    2072           9 :     if (mCanceled || NS_FAILED(mStatus) || !mSecurityInfo ||
    2073           3 :         !IsHTTPS() || mPrivateBrowsing)
    2074           6 :         return;
    2075             : 
    2076             :     nsCOMPtr<nsISSLStatusProvider> statusProvider =
    2077           0 :         do_QueryInterface(mSecurityInfo);
    2078           0 :     if (!statusProvider)
    2079           0 :         return;
    2080           0 :     nsCOMPtr<nsISSLStatus> sslstat;
    2081           0 :     statusProvider->GetSSLStatus(getter_AddRefs(sslstat));
    2082           0 :     if (!sslstat)
    2083           0 :         return;
    2084             : 
    2085             :     nsCOMPtr<nsITransportSecurityInfo> securityInfo =
    2086           0 :         do_QueryInterface(mSecurityInfo);
    2087             :     uint32_t state;
    2088           0 :     if (securityInfo &&
    2089           0 :         NS_SUCCEEDED(securityInfo->GetSecurityState(&state)) &&
    2090           0 :         (state & nsIWebProgressListener::STATE_IS_BROKEN)) {
    2091             :         // Send weak crypto warnings to the web console
    2092           0 :         if (state & nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
    2093           0 :             nsString consoleErrorTag = NS_LITERAL_STRING("WeakCipherSuiteWarning");
    2094           0 :             nsString consoleErrorCategory = NS_LITERAL_STRING("SSL");
    2095           0 :             Unused << AddSecurityMessage(consoleErrorTag, consoleErrorCategory);
    2096             :         }
    2097             :     }
    2098             : 
    2099             :     // Send (SHA-1) signature algorithm errors to the web console
    2100           0 :     nsCOMPtr<nsIX509Cert> cert;
    2101           0 :     sslstat->GetServerCert(getter_AddRefs(cert));
    2102           0 :     if (cert) {
    2103           0 :         UniqueCERTCertificate nssCert(cert->GetCert());
    2104           0 :         if (nssCert) {
    2105           0 :             SECOidTag tag = SECOID_GetAlgorithmTag(&nssCert->signature);
    2106           0 :             LOG(("Checking certificate signature: The OID tag is %i [this=%p]\n", tag, this));
    2107             :             // Check to see if the signature is sha-1 based.
    2108             :             // Not including checks for SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE
    2109             :             // from http://tools.ietf.org/html/rfc2437#section-8 since I
    2110             :             // can't see reference to it outside this spec
    2111           0 :             if (tag == SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION ||
    2112           0 :                 tag == SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST ||
    2113             :                 tag == SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE) {
    2114           0 :                 nsString consoleErrorTag = NS_LITERAL_STRING("SHA1Sig");
    2115             :                 nsString consoleErrorMessage
    2116           0 :                         = NS_LITERAL_STRING("SHA-1 Signature");
    2117           0 :                 Unused << AddSecurityMessage(consoleErrorTag, consoleErrorMessage);
    2118             :             }
    2119             :         }
    2120             :     }
    2121             : }
    2122             : 
    2123             : void
    2124           3 : nsHttpChannel::ProcessAltService()
    2125             : {
    2126             :     // e.g. Alt-Svc: h2=":443"; ma=60
    2127             :     // e.g. Alt-Svc: h2="otherhost:443"
    2128             :     // Alt-Svc       = 1#( alternative *( OWS ";" OWS parameter ) )
    2129             :     // alternative   = protocol-id "=" alt-authority
    2130             :     // protocol-id   = token ; percent-encoded ALPN protocol identifier
    2131             :     // alt-authority = quoted-string ;  containing [ uri-host ] ":" port
    2132             : 
    2133           3 :     if (!mAllowAltSvc) { // per channel opt out
    2134           3 :         return;
    2135             :     }
    2136             : 
    2137           3 :     if (!gHttpHandler->AllowAltSvc() || (mCaps & NS_HTTP_DISALLOW_SPDY)) {
    2138           0 :         return;
    2139             :     }
    2140             : 
    2141           3 :     nsAutoCString scheme;
    2142           3 :     mURI->GetScheme(scheme);
    2143           3 :     bool isHttp = scheme.Equals(NS_LITERAL_CSTRING("http"));
    2144           3 :     if (!isHttp && !scheme.Equals(NS_LITERAL_CSTRING("https"))) {
    2145           0 :         return;
    2146             :     }
    2147             : 
    2148           3 :     nsAutoCString altSvc;
    2149           3 :     Unused << mResponseHead->GetHeader(nsHttp::Alternate_Service, altSvc);
    2150           3 :     if (altSvc.IsEmpty()) {
    2151           3 :         return;
    2152             :     }
    2153             : 
    2154           0 :     if (!nsHttp::IsReasonableHeaderValue(altSvc)) {
    2155           0 :         LOG(("Alt-Svc Response Header seems unreasonable - skipping\n"));
    2156           0 :         return;
    2157             :     }
    2158             : 
    2159           0 :     nsAutoCString originHost;
    2160           0 :     int32_t originPort = 80;
    2161           0 :     mURI->GetPort(&originPort);
    2162           0 :     if (NS_FAILED(mURI->GetHost(originHost))) {
    2163           0 :         return;
    2164             :     }
    2165             : 
    2166           0 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
    2167           0 :     nsCOMPtr<nsProxyInfo> proxyInfo;
    2168           0 :     NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
    2169           0 :                                            getter_AddRefs(callbacks));
    2170           0 :     if (mProxyInfo) {
    2171           0 :         proxyInfo = do_QueryInterface(mProxyInfo);
    2172             :     }
    2173             : 
    2174           0 :     OriginAttributes originAttributes;
    2175           0 :     NS_GetOriginAttributes(this, originAttributes);
    2176             : 
    2177           0 :     AltSvcMapping::ProcessHeader(altSvc, scheme, originHost, originPort,
    2178           0 :                                  mUsername, mPrivateBrowsing, callbacks, proxyInfo,
    2179           0 :                                  mCaps & NS_HTTP_DISALLOW_SPDY,
    2180           0 :                                  originAttributes);
    2181             : }
    2182             : 
    2183             : nsresult
    2184           3 : nsHttpChannel::ProcessResponse()
    2185             : {
    2186           3 :     uint32_t httpStatus = mResponseHead->Status();
    2187             : 
    2188           3 :     LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n",
    2189             :         this, httpStatus));
    2190             : 
    2191             :     // do some telemetry
    2192           3 :     if (gHttpHandler->IsTelemetryEnabled()) {
    2193             :         // Gather data on whether the transaction and page (if this is
    2194             :         // the initial page load) is being loaded with SSL.
    2195           0 :         Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_IS_SSL,
    2196           0 :                               mConnectionInfo->EndToEndSSL());
    2197           0 :         if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
    2198           0 :             Telemetry::Accumulate(Telemetry::HTTP_PAGELOAD_IS_SSL,
    2199           0 :                                   mConnectionInfo->EndToEndSSL());
    2200             :         }
    2201             : 
    2202             :         // how often do we see something like Alt-Svc: "443:quic,p=1"
    2203           0 :         nsAutoCString alt_service;
    2204           0 :         Unused << mResponseHead->GetHeader(nsHttp::Alternate_Service, alt_service);
    2205           0 :         bool saw_quic = (!alt_service.IsEmpty() &&
    2206           0 :                          PL_strstr(alt_service.get(), "quic")) ? 1 : 0;
    2207           0 :         Telemetry::Accumulate(Telemetry::HTTP_SAW_QUIC_ALT_PROTOCOL, saw_quic);
    2208             : 
    2209             :         // Gather data on how many URLS get redirected
    2210           0 :         switch (httpStatus) {
    2211             :             case 200:
    2212           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 0);
    2213           0 :                 break;
    2214             :             case 301:
    2215           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 1);
    2216           0 :                 break;
    2217             :             case 302:
    2218           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 2);
    2219           0 :                 break;
    2220             :             case 304:
    2221           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 3);
    2222           0 :                 break;
    2223             :             case 307:
    2224           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 4);
    2225           0 :                 break;
    2226             :             case 308:
    2227           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 5);
    2228           0 :                 break;
    2229             :             case 400:
    2230           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 6);
    2231           0 :                 break;
    2232             :             case 401:
    2233           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 7);
    2234           0 :                 break;
    2235             :             case 403:
    2236           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 8);
    2237           0 :                 break;
    2238             :             case 404:
    2239           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 9);
    2240           0 :                 break;
    2241             :             case 500:
    2242           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 10);
    2243           0 :                 break;
    2244             :             default:
    2245           0 :                 Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_STATUS_CODE, 11);
    2246           0 :                 break;
    2247             :         }
    2248             :     }
    2249             : 
    2250             :     // Let the predictor know whether this was a cacheable response or not so
    2251             :     // that it knows whether or not to possibly prefetch this resource in the
    2252             :     // future.
    2253             :     // We use GetReferringPage because mReferrer may not be set at all, or may
    2254             :     // not be a full URI (HttpBaseChannel::SetReferrer has the gorey details).
    2255             :     // If that's null, though, we'll fall back to mReferrer just in case (this
    2256             :     // is especially useful in xpcshell tests, where we don't have an actual
    2257             :     // pageload to get a referrer from).
    2258           6 :     nsCOMPtr<nsIURI> referrer = GetReferringPage();
    2259           3 :     if (!referrer) {
    2260           3 :         referrer = mReferrer;
    2261             :     }
    2262           3 :     if (referrer) {
    2263           0 :         nsCOMPtr<nsILoadContextInfo> lci = GetLoadContextInfo(this);
    2264           0 :         mozilla::net::Predictor::UpdateCacheability(referrer, mURI, httpStatus,
    2265             :                                                     mRequestHead, mResponseHead,
    2266           0 :                                                     lci);
    2267             :     }
    2268             : 
    2269           3 :     if (mTransaction->ProxyConnectFailed()) {
    2270             :         // Only allow 407 (authentication required) to continue
    2271           0 :         if (httpStatus != 407)
    2272           0 :             return ProcessFailedProxyConnect(httpStatus);
    2273             :         // If proxy CONNECT response needs to complete, wait to process connection
    2274             :         // for Strict-Transport-Security.
    2275             :     } else {
    2276             :         // Given a successful connection, process any STS or PKP data that's
    2277             :         // relevant.
    2278           6 :         DebugOnly<nsresult> rv = ProcessSecurityHeaders();
    2279           3 :         MOZ_ASSERT(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load.");
    2280             :     }
    2281             : 
    2282           3 :     MOZ_ASSERT(!mCachedContentIsValid || mRaceCacheWithNetwork,
    2283             :                "We should not be hitting the network if we have valid cached "
    2284             :                "content unless we are racing the network and cache");
    2285             : 
    2286           3 :     ProcessSSLInformation();
    2287             : 
    2288             :     // notify "http-on-examine-response" observers
    2289           3 :     gHttpHandler->OnExamineResponse(this);
    2290             : 
    2291           3 :     return ContinueProcessResponse1();
    2292             : }
    2293             : 
    2294             : void
    2295           0 : nsHttpChannel::AsyncContinueProcessResponse()
    2296             : {
    2297             :     nsresult rv;
    2298           0 :     rv = ContinueProcessResponse1();
    2299           0 :     if (NS_FAILED(rv)) {
    2300             :         // A synchronous failure here would normally be passed as the return
    2301             :         // value from OnStartRequest, which would in turn cancel the request.
    2302             :         // If we're continuing asynchronously, we need to cancel the request
    2303             :         // ourselves.
    2304           0 :         Unused << Cancel(rv);
    2305             :     }
    2306           0 : }
    2307             : 
    2308             : nsresult
    2309           3 : nsHttpChannel::ContinueProcessResponse1()
    2310             : {
    2311           3 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
    2312             :     nsresult rv;
    2313             : 
    2314           3 :     if (mSuspendCount) {
    2315           0 :         LOG(("Waiting until resume to finish processing response [this=%p]\n", this));
    2316           0 :         mCallOnResume = &nsHttpChannel::AsyncContinueProcessResponse;
    2317           0 :         return NS_OK;
    2318             :     }
    2319             : 
    2320             :     // Check if request was cancelled during http-on-examine-response.
    2321           3 :     if (mCanceled) {
    2322           0 :         return CallOnStartRequest();
    2323             :     }
    2324             : 
    2325           3 :     uint32_t httpStatus = mResponseHead->Status();
    2326             : 
    2327             :     // Cookies and Alt-Service should not be handled on proxy failure either.
    2328             :     // This would be consolidated with ProcessSecurityHeaders but it should
    2329             :     // happen after OnExamineResponse.
    2330           3 :     if (!mTransaction->ProxyConnectFailed() && (httpStatus != 407)) {
    2331           6 :         nsAutoCString cookie;
    2332           3 :         if (NS_SUCCEEDED(mResponseHead->GetHeader(nsHttp::Set_Cookie, cookie))) {
    2333           0 :             SetCookie(cookie.get());
    2334             :         }
    2335           3 :         if ((httpStatus < 500) && (httpStatus != 421)) {
    2336           3 :             ProcessAltService();
    2337             :         }
    2338             :     }
    2339             : 
    2340           3 :     if (mConcurrentCacheAccess && mCachedContentIsPartial && httpStatus != 206) {
    2341           0 :         LOG(("  only expecting 206 when doing partial request during "
    2342             :              "interrupted cache concurrent read"));
    2343           0 :         return NS_ERROR_CORRUPTED_CONTENT;
    2344             :     }
    2345             : 
    2346             :     // handle unused username and password in url (see bug 232567)
    2347           3 :     if (httpStatus != 401 && httpStatus != 407) {
    2348           3 :         if (!mAuthRetryPending) {
    2349           3 :             rv = mAuthProvider->CheckForSuperfluousAuth();
    2350           3 :             if (NS_FAILED(rv)) {
    2351           0 :                 LOG(("  CheckForSuperfluousAuth failed (%08x)",
    2352             :                      static_cast<uint32_t>(rv)));
    2353             :             }
    2354             :         }
    2355           3 :         if (mCanceled)
    2356           0 :             return CallOnStartRequest();
    2357             : 
    2358             :         // reset the authentication's current continuation state because our
    2359             :         // last authentication attempt has been completed successfully
    2360           3 :         rv = mAuthProvider->Disconnect(NS_ERROR_ABORT);
    2361           3 :         if (NS_FAILED(rv)) {
    2362           0 :             LOG(("  Disconnect failed (%08x)", static_cast<uint32_t>(rv)));
    2363             :         }
    2364           3 :         mAuthProvider = nullptr;
    2365           3 :         LOG(("  continuation state has been reset"));
    2366             :     }
    2367             : 
    2368           3 :     if (mAPIRedirectToURI && !mCanceled) {
    2369           0 :         MOZ_ASSERT(!mOnStartRequestCalled);
    2370           0 :         nsCOMPtr<nsIURI> redirectTo;
    2371           0 :         mAPIRedirectToURI.swap(redirectTo);
    2372             : 
    2373           0 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);
    2374           0 :         rv = StartRedirectChannelToURI(redirectTo, nsIChannelEventSink::REDIRECT_TEMPORARY);
    2375           0 :         if (NS_SUCCEEDED(rv)) {
    2376           0 :             return NS_OK;
    2377             :         }
    2378           0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse2);
    2379             :     }
    2380             : 
    2381             :     // Hack: ContinueProcessResponse2 uses NS_OK to detect successful
    2382             :     // redirects, so we distinguish this codepath (a non-redirect that's
    2383             :     // processing normally) by passing in a bogus error code.
    2384           3 :     return ContinueProcessResponse2(NS_BINDING_FAILED);
    2385             : }
    2386             : 
    2387             : nsresult
    2388           3 : nsHttpChannel::ContinueProcessResponse2(nsresult rv)
    2389             : {
    2390           3 :     LOG(("nsHttpChannel::ContinueProcessResponse1 [this=%p, rv=%" PRIx32 "]",
    2391             :          this, static_cast<uint32_t>(rv)));
    2392             : 
    2393           3 :     if (NS_SUCCEEDED(rv)) {
    2394             :         // redirectTo() has passed through, we don't want to go on with
    2395             :         // this channel.  It will now be canceled by the redirect handling
    2396             :         // code that called this function.
    2397           0 :         return NS_OK;
    2398             :     }
    2399             : 
    2400           3 :     rv = NS_OK;
    2401             : 
    2402           3 :     uint32_t httpStatus = mResponseHead->Status();
    2403             : 
    2404           3 :     bool successfulReval = false;
    2405             : 
    2406             :     // handle different server response categories.  Note that we handle
    2407             :     // caching or not caching of error pages in
    2408             :     // nsHttpResponseHead::MustValidate; if you change this switch, update that
    2409             :     // one
    2410           3 :     switch (httpStatus) {
    2411             :     case 200:
    2412             :     case 203:
    2413             :         // Per RFC 2616, 14.35.2, "A server MAY ignore the Range header".
    2414             :         // So if a server does that and sends 200 instead of 206 that we
    2415             :         // expect, notify our caller.
    2416             :         // However, if we wanted to start from the beginning, let it go through
    2417           1 :         if (mResuming && mStartPos != 0) {
    2418           0 :             LOG(("Server ignored our Range header, cancelling [this=%p]\n", this));
    2419           0 :             Cancel(NS_ERROR_NOT_RESUMABLE);
    2420           0 :             rv = CallOnStartRequest();
    2421           0 :             break;
    2422             :         }
    2423             :         // these can normally be cached
    2424           1 :         rv = ProcessNormal();
    2425           1 :         MaybeInvalidateCacheEntryForSubsequentGet();
    2426           1 :         break;
    2427             :     case 206:
    2428           0 :         if (mCachedContentIsPartial) // an internal byte range request...
    2429           0 :             rv = ProcessPartialContent();
    2430             :         else {
    2431           0 :             mCacheInputStream.CloseAndRelease();
    2432           0 :             rv = ProcessNormal();
    2433             :         }
    2434           0 :         break;
    2435             :     case 300:
    2436             :     case 301:
    2437             :     case 302:
    2438             :     case 307:
    2439             :     case 308:
    2440             :     case 303:
    2441             : #if 0
    2442             :     case 305: // disabled as a security measure (see bug 187996).
    2443             : #endif
    2444             :         // don't store the response body for redirects
    2445           0 :         MaybeInvalidateCacheEntryForSubsequentGet();
    2446           0 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);
    2447           0 :         rv = AsyncProcessRedirection(httpStatus);
    2448           0 :         if (NS_FAILED(rv)) {
    2449           0 :             PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);
    2450           0 :             LOG(("AsyncProcessRedirection failed [rv=%" PRIx32 "]\n",
    2451             :                  static_cast<uint32_t>(rv)));
    2452             :             // don't cache failed redirect responses.
    2453           0 :             if (mCacheEntry)
    2454           0 :                 mCacheEntry->AsyncDoom(nullptr);
    2455           0 :             if (DoNotRender3xxBody(rv)) {
    2456           0 :                 mStatus = rv;
    2457           0 :                 DoNotifyListener();
    2458             :             } else {
    2459           0 :                 rv = ContinueProcessResponse3(rv);
    2460             :             }
    2461             :         }
    2462           0 :         break;
    2463             :     case 304:
    2464           0 :         if (!ShouldBypassProcessNotModified()) {
    2465           0 :             rv = ProcessNotModified();
    2466           0 :             if (NS_SUCCEEDED(rv)) {
    2467           0 :                 successfulReval = true;
    2468           0 :                 break;
    2469             :             }
    2470             : 
    2471           0 :             LOG(("ProcessNotModified failed [rv=%" PRIx32 "]\n",
    2472             :                  static_cast<uint32_t>(rv)));
    2473             : 
    2474             :             // We cannot read from the cache entry, it might be in an
    2475             :             // incosistent state.  Doom it and redirect the channel
    2476             :             // to the same URI to reload from the network.
    2477           0 :             mCacheInputStream.CloseAndRelease();
    2478           0 :             if (mCacheEntry) {
    2479           0 :                 mCacheEntry->AsyncDoom(nullptr);
    2480           0 :                 mCacheEntry = nullptr;
    2481             :             }
    2482             : 
    2483           0 :             rv = StartRedirectChannelToURI(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
    2484           0 :             if (NS_SUCCEEDED(rv)) {
    2485           0 :                 return NS_OK;
    2486             :             }
    2487             :         }
    2488             : 
    2489             :         // Don't cache uninformative 304
    2490           0 :         if (mCustomConditionalRequest) {
    2491           0 :             CloseCacheEntry(false);
    2492             :         }
    2493             : 
    2494           0 :         if (ShouldBypassProcessNotModified() || NS_FAILED(rv)) {
    2495           0 :             rv = ProcessNormal();
    2496             :         }
    2497           0 :         break;
    2498             :     case 401:
    2499             :     case 407:
    2500           0 :         if (MOZ_UNLIKELY(mCustomAuthHeader) && httpStatus == 401) {
    2501             :             // When a custom auth header fails, we don't want to try
    2502             :             // any cached credentials, nor we want to ask the user.
    2503             :             // It's up to the consumer to re-try w/o setting a custom
    2504             :             // auth header if cached credentials should be attempted.
    2505           0 :             rv = NS_ERROR_FAILURE;
    2506             :         } else {
    2507           0 :             rv = mAuthProvider->ProcessAuthentication(
    2508             :                 httpStatus,
    2509           0 :                 mConnectionInfo->EndToEndSSL() && mTransaction->ProxyConnectFailed());
    2510             :         }
    2511           0 :         if (rv == NS_ERROR_IN_PROGRESS)  {
    2512             :             // authentication prompt has been invoked and result
    2513             :             // is expected asynchronously
    2514           0 :             mAuthRetryPending = true;
    2515           0 :             if (httpStatus == 407 || mTransaction->ProxyConnectFailed())
    2516           0 :                 mProxyAuthPending = true;
    2517             : 
    2518             :             // suspend the transaction pump to stop receiving the
    2519             :             // unauthenticated content data. We will throw that data
    2520             :             // away when user provides credentials or resume the pump
    2521             :             // when user refuses to authenticate.
    2522           0 :             LOG(("Suspending the transaction, asynchronously prompting for credentials"));
    2523           0 :             mTransactionPump->Suspend();
    2524           0 :             rv = NS_OK;
    2525           0 :         } else if (NS_FAILED(rv)) {
    2526           0 :             LOG(("ProcessAuthentication failed [rv=%" PRIx32 "]\n",
    2527             :                  static_cast<uint32_t>(rv)));
    2528           0 :             if (mTransaction->ProxyConnectFailed())
    2529           0 :                 return ProcessFailedProxyConnect(httpStatus);
    2530           0 :             if (!mAuthRetryPending) {
    2531           0 :                 rv = mAuthProvider->CheckForSuperfluousAuth();
    2532           0 :                 if (NS_FAILED(rv)) {
    2533           0 :                     LOG(("CheckForSuperfluousAuth failed [rv=%x]\n",
    2534             :                          static_cast<uint32_t>(rv)));
    2535             :                 }
    2536             :             }
    2537           0 :             rv = ProcessNormal();
    2538             :         } else {
    2539           0 :             mAuthRetryPending = true; // see DoAuthRetry
    2540             :         }
    2541           0 :         break;
    2542             :     default:
    2543           2 :         rv = ProcessNormal();
    2544           2 :         MaybeInvalidateCacheEntryForSubsequentGet();
    2545           2 :         break;
    2546             :     }
    2547             : 
    2548           3 :     if (gHttpHandler->IsTelemetryEnabled()) {
    2549             :         CacheDisposition cacheDisposition;
    2550           0 :         if (!mDidReval) {
    2551           0 :             cacheDisposition = kCacheMissed;
    2552           0 :         } else if (successfulReval) {
    2553           0 :             cacheDisposition = kCacheHitViaReval;
    2554             :         } else {
    2555           0 :             cacheDisposition = kCacheMissedViaReval;
    2556             :         }
    2557           0 :         AccumulateCacheHitTelemetry(cacheDisposition);
    2558             : 
    2559           0 :         Telemetry::Accumulate(Telemetry::HTTP_RESPONSE_VERSION,
    2560           0 :                               mResponseHead->Version());
    2561             : 
    2562           0 :         if (mResponseHead->Version() == NS_HTTP_VERSION_0_9) {
    2563             :             // DefaultPortTopLevel = 0, DefaultPortSubResource = 1,
    2564             :             // NonDefaultPortTopLevel = 2, NonDefaultPortSubResource = 3
    2565           0 :             uint32_t v09Info = 0;
    2566           0 :             if (!(mLoadFlags & LOAD_INITIAL_DOCUMENT_URI)) {
    2567           0 :                 v09Info += 1;
    2568             :             }
    2569           0 :             if (mConnectionInfo->OriginPort() != mConnectionInfo->DefaultPort()) {
    2570           0 :                 v09Info += 2;
    2571             :             }
    2572           0 :             Telemetry::Accumulate(Telemetry::HTTP_09_INFO, v09Info);
    2573             :         }
    2574             :     }
    2575           3 :     return rv;
    2576             : }
    2577             : 
    2578             : nsresult
    2579           0 : nsHttpChannel::ContinueProcessResponse3(nsresult rv)
    2580             : {
    2581           0 :     bool doNotRender = DoNotRender3xxBody(rv);
    2582             : 
    2583           0 :     if (rv == NS_ERROR_DOM_BAD_URI && mRedirectURI) {
    2584           0 :         bool isHTTP = false;
    2585           0 :         if (NS_FAILED(mRedirectURI->SchemeIs("http", &isHTTP)))
    2586           0 :             isHTTP = false;
    2587           0 :         if (!isHTTP && NS_FAILED(mRedirectURI->SchemeIs("https", &isHTTP)))
    2588           0 :             isHTTP = false;
    2589             : 
    2590           0 :         if (!isHTTP) {
    2591             :             // This was a blocked attempt to redirect and subvert the system by
    2592             :             // redirecting to another protocol (perhaps javascript:)
    2593             :             // In that case we want to throw an error instead of displaying the
    2594             :             // non-redirected response body.
    2595           0 :             LOG(("ContinueProcessResponse3 detected rejected Non-HTTP Redirection"));
    2596           0 :             doNotRender = true;
    2597           0 :             rv = NS_ERROR_CORRUPTED_CONTENT;
    2598             :         }
    2599             :     }
    2600             : 
    2601           0 :     if (doNotRender) {
    2602           0 :         Cancel(rv);
    2603           0 :         DoNotifyListener();
    2604           0 :         return rv;
    2605             :     }
    2606             : 
    2607           0 :     if (NS_SUCCEEDED(rv)) {
    2608           0 :         UpdateInhibitPersistentCachingFlag();
    2609             : 
    2610           0 :         rv = InitCacheEntry();
    2611           0 :         if (NS_FAILED(rv)) {
    2612           0 :             LOG(("ContinueProcessResponse3 "
    2613             :                  "failed to init cache entry [rv=%x]\n",
    2614             :                  static_cast<uint32_t>(rv)));
    2615             :         }
    2616           0 :         CloseCacheEntry(false);
    2617             : 
    2618           0 :         if (mApplicationCacheForWrite) {
    2619             :             // Store response in the offline cache
    2620           0 :             Unused << InitOfflineCacheEntry();
    2621           0 :             CloseOfflineCacheEntry();
    2622             :         }
    2623           0 :         return NS_OK;
    2624             :     }
    2625             : 
    2626           0 :     LOG(("ContinueProcessResponse3 got failure result [rv=%" PRIx32 "]\n",
    2627             :          static_cast<uint32_t>(rv)));
    2628           0 :     if (mTransaction && mTransaction->ProxyConnectFailed()) {
    2629           0 :         return ProcessFailedProxyConnect(mRedirectType);
    2630             :     }
    2631           0 :     return ProcessNormal();
    2632             : }
    2633             : 
    2634             : nsresult
    2635           3 : nsHttpChannel::ProcessNormal()
    2636             : {
    2637             :     nsresult rv;
    2638             : 
    2639           3 :     LOG(("nsHttpChannel::ProcessNormal [this=%p]\n", this));
    2640             : 
    2641             :     bool succeeded;
    2642           3 :     rv = GetRequestSucceeded(&succeeded);
    2643           3 :     if (NS_SUCCEEDED(rv) && !succeeded) {
    2644           2 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
    2645             :         bool waitingForRedirectCallback;
    2646           2 :         Unused << ProcessFallback(&waitingForRedirectCallback);
    2647           2 :         if (waitingForRedirectCallback) {
    2648             :             // The transaction has been suspended by ProcessFallback.
    2649           0 :             return NS_OK;
    2650             :         }
    2651           2 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessNormal);
    2652             :     }
    2653             : 
    2654           3 :     return ContinueProcessNormal(NS_OK);
    2655             : }
    2656             : 
    2657             : nsresult
    2658           3 : nsHttpChannel::ContinueProcessNormal(nsresult rv)
    2659             : {
    2660           3 :     LOG(("nsHttpChannel::ContinueProcessNormal [this=%p]", this));
    2661             : 
    2662           3 :     if (NS_FAILED(rv)) {
    2663             :         // Fill the failure status here, we have failed to fall back, thus we
    2664             :         // have to report our status as failed.
    2665           0 :         mStatus = rv;
    2666           0 :         DoNotifyListener();
    2667           0 :         return rv;
    2668             :     }
    2669             : 
    2670           3 :     if (mFallingBack) {
    2671             :         // Do not continue with normal processing, fallback is in
    2672             :         // progress now.
    2673           0 :         return NS_OK;
    2674             :     }
    2675             : 
    2676             :     // if we're here, then any byte-range requests failed to result in a partial
    2677             :     // response.  we must clear this flag to prevent BufferPartialContent from
    2678             :     // being called inside our OnDataAvailable (see bug 136678).
    2679           3 :     mCachedContentIsPartial = false;
    2680             : 
    2681           3 :     ClearBogusContentEncodingIfNeeded();
    2682             : 
    2683           3 :     UpdateInhibitPersistentCachingFlag();
    2684             : 
    2685             :     // this must be called before firing OnStartRequest, since http clients,
    2686             :     // such as imagelib, expect our cache entry to already have the correct
    2687             :     // expiration time (bug 87710).
    2688           3 :     if (mCacheEntry) {
    2689           2 :         rv = InitCacheEntry();
    2690           2 :         if (NS_FAILED(rv))
    2691           0 :             CloseCacheEntry(true);
    2692             :     }
    2693             : 
    2694             :     // Check that the server sent us what we were asking for
    2695           3 :     if (mResuming) {
    2696             :         // Create an entity id from the response
    2697           0 :         nsAutoCString id;
    2698           0 :         rv = GetEntityID(id);
    2699           0 :         if (NS_FAILED(rv)) {
    2700             :             // If creating an entity id is not possible -> error
    2701           0 :             Cancel(NS_ERROR_NOT_RESUMABLE);
    2702             :         }
    2703           0 :         else if (mResponseHead->Status() != 206 &&
    2704           0 :                  mResponseHead->Status() != 200) {
    2705             :             // Probably 404 Not Found, 412 Precondition Failed or
    2706             :             // 416 Invalid Range -> error
    2707           0 :             LOG(("Unexpected response status while resuming, aborting [this=%p]\n",
    2708             :                  this));
    2709           0 :             Cancel(NS_ERROR_ENTITY_CHANGED);
    2710             :         }
    2711             :         // If we were passed an entity id, verify it's equal to the server's
    2712           0 :         else if (!mEntityID.IsEmpty()) {
    2713           0 :             if (!mEntityID.Equals(id)) {
    2714           0 :                 LOG(("Entity mismatch, expected '%s', got '%s', aborting [this=%p]",
    2715             :                      mEntityID.get(), id.get(), this));
    2716           0 :                 Cancel(NS_ERROR_ENTITY_CHANGED);
    2717             :             }
    2718             :         }
    2719             :     }
    2720             : 
    2721           3 :     rv = CallOnStartRequest();
    2722           3 :     if (NS_FAILED(rv)) return rv;
    2723             : 
    2724             :     // install cache listener if we still have a cache entry open
    2725           2 :     if (mCacheEntry && !mCacheEntryIsReadOnly) {
    2726           2 :         rv = InstallCacheListener();
    2727           2 :         if (NS_FAILED(rv)) return rv;
    2728             :     }
    2729             : 
    2730           2 :     return NS_OK;
    2731             : }
    2732             : 
    2733             : nsresult
    2734           0 : nsHttpChannel::PromptTempRedirect()
    2735             : {
    2736           0 :     if (!gHttpHandler->PromptTempRedirect()) {
    2737           0 :         return NS_OK;
    2738             :     }
    2739             :     nsresult rv;
    2740             :     nsCOMPtr<nsIStringBundleService> bundleService =
    2741           0 :             do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
    2742           0 :     if (NS_FAILED(rv)) return rv;
    2743             : 
    2744           0 :     nsCOMPtr<nsIStringBundle> stringBundle;
    2745           0 :     rv = bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(stringBundle));
    2746           0 :     if (NS_FAILED(rv)) return rv;
    2747             : 
    2748           0 :     nsXPIDLString messageString;
    2749           0 :     rv = stringBundle->GetStringFromName(u"RepostFormData", getter_Copies(messageString));
    2750             :     // GetStringFromName can return NS_OK and nullptr messageString.
    2751           0 :     if (NS_SUCCEEDED(rv) && messageString) {
    2752           0 :         bool repost = false;
    2753             : 
    2754           0 :         nsCOMPtr<nsIPrompt> prompt;
    2755           0 :         GetCallback(prompt);
    2756           0 :         if (!prompt)
    2757           0 :             return NS_ERROR_NO_INTERFACE;
    2758             : 
    2759           0 :         prompt->Confirm(nullptr, messageString, &repost);
    2760           0 :         if (!repost)
    2761           0 :             return NS_ERROR_FAILURE;
    2762             :     }
    2763             : 
    2764           0 :     return rv;
    2765             : }
    2766             : 
    2767             : nsresult
    2768           0 : nsHttpChannel::ProxyFailover()
    2769             : {
    2770           0 :     LOG(("nsHttpChannel::ProxyFailover [this=%p]\n", this));
    2771             : 
    2772             :     nsresult rv;
    2773             : 
    2774             :     nsCOMPtr<nsIProtocolProxyService> pps =
    2775           0 :             do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
    2776           0 :     if (NS_FAILED(rv))
    2777           0 :         return rv;
    2778             : 
    2779           0 :     nsCOMPtr<nsIProxyInfo> pi;
    2780           0 :     rv = pps->GetFailoverForProxy(mConnectionInfo->ProxyInfo(), mURI, mStatus,
    2781           0 :                                   getter_AddRefs(pi));
    2782           0 :     if (NS_FAILED(rv))
    2783           0 :         return rv;
    2784             : 
    2785             :     // XXXbz so where does this codepath remove us from the loadgroup,
    2786             :     // exactly?
    2787           0 :     return AsyncDoReplaceWithProxy(pi);
    2788             : }
    2789             : 
    2790             : void
    2791           0 : nsHttpChannel::HandleAsyncRedirectChannelToHttps()
    2792             : {
    2793           0 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
    2794             : 
    2795           0 :     if (mSuspendCount) {
    2796           0 :         LOG(("Waiting until resume to do async redirect to https [this=%p]\n", this));
    2797           0 :         mCallOnResume = &nsHttpChannel::HandleAsyncRedirectChannelToHttps;
    2798           0 :         return;
    2799             :     }
    2800             : 
    2801           0 :     nsresult rv = StartRedirectChannelToHttps();
    2802           0 :     if (NS_FAILED(rv)) {
    2803           0 :         rv = ContinueAsyncRedirectChannelToURI(rv);
    2804           0 :         if (NS_FAILED(rv)) {
    2805           0 :             LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n",
    2806             :                  static_cast<uint32_t>(rv), this));
    2807             :         }
    2808             :     }
    2809             : }
    2810             : 
    2811             : nsresult
    2812           0 : nsHttpChannel::StartRedirectChannelToHttps()
    2813             : {
    2814           0 :     LOG(("nsHttpChannel::HandleAsyncRedirectChannelToHttps() [STS]\n"));
    2815             : 
    2816           0 :     nsCOMPtr<nsIURI> upgradedURI;
    2817           0 :     nsresult rv = NS_GetSecureUpgradedURI(mURI, getter_AddRefs(upgradedURI));
    2818           0 :     NS_ENSURE_SUCCESS(rv,rv);
    2819             : 
    2820           0 :     return StartRedirectChannelToURI(upgradedURI,
    2821             :                                      nsIChannelEventSink::REDIRECT_PERMANENT |
    2822           0 :                                      nsIChannelEventSink::REDIRECT_STS_UPGRADE);
    2823             : }
    2824             : 
    2825             : void
    2826           0 : nsHttpChannel::HandleAsyncAPIRedirect()
    2827             : {
    2828           0 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
    2829           0 :     NS_PRECONDITION(mAPIRedirectToURI, "How did that happen?");
    2830             : 
    2831           0 :     if (mSuspendCount) {
    2832           0 :         LOG(("Waiting until resume to do async API redirect [this=%p]\n", this));
    2833           0 :         mCallOnResume = &nsHttpChannel::HandleAsyncAPIRedirect;
    2834           0 :         return;
    2835             :     }
    2836             : 
    2837           0 :     nsresult rv = StartRedirectChannelToURI(mAPIRedirectToURI,
    2838           0 :                                             nsIChannelEventSink::REDIRECT_PERMANENT);
    2839           0 :     if (NS_FAILED(rv)) {
    2840           0 :         rv = ContinueAsyncRedirectChannelToURI(rv);
    2841           0 :         if (NS_FAILED(rv)) {
    2842           0 :             LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n",
    2843             :                  static_cast<uint32_t>(rv), this));
    2844             :         }
    2845             :     }
    2846             : 
    2847           0 :     return;
    2848             : }
    2849             : 
    2850             : nsresult
    2851           0 : nsHttpChannel::StartRedirectChannelToURI(nsIURI *upgradedURI, uint32_t flags)
    2852             : {
    2853           0 :     nsresult rv = NS_OK;
    2854           0 :     LOG(("nsHttpChannel::StartRedirectChannelToURI()\n"));
    2855             : 
    2856           0 :     nsCOMPtr<nsIChannel> newChannel;
    2857           0 :     nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(upgradedURI,
    2858           0 :                                                                       flags);
    2859             : 
    2860           0 :     nsCOMPtr<nsIIOService> ioService;
    2861           0 :     rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
    2862           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2863             : 
    2864           0 :     rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
    2865             :                                upgradedURI,
    2866             :                                redirectLoadInfo,
    2867             :                                nullptr, // aLoadGroup
    2868             :                                nullptr, // aCallbacks
    2869             :                                nsIRequest::LOAD_NORMAL,
    2870           0 :                                ioService);
    2871           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2872             : 
    2873           0 :     rv = SetupReplacementChannel(upgradedURI, newChannel, true, flags);
    2874           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2875             : 
    2876             :     // Inform consumers about this fake redirect
    2877           0 :     mRedirectChannel = newChannel;
    2878             : 
    2879           0 :     if (!(flags & nsIChannelEventSink::REDIRECT_STS_UPGRADE) &&
    2880           0 :         mInterceptCache == INTERCEPTED) {
    2881             :         // Mark the channel as intercepted in order to propagate the response URL.
    2882           0 :         nsCOMPtr<nsIHttpChannelInternal> httpRedirect = do_QueryInterface(mRedirectChannel);
    2883           0 :         if (httpRedirect) {
    2884           0 :             rv = httpRedirect->ForceIntercepted(mInterceptionID);
    2885           0 :             MOZ_ASSERT(NS_SUCCEEDED(rv));
    2886             :         }
    2887             :     }
    2888             : 
    2889             :     PushRedirectAsyncFunc(
    2890           0 :         &nsHttpChannel::ContinueAsyncRedirectChannelToURI);
    2891           0 :     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
    2892             : 
    2893           0 :     if (NS_SUCCEEDED(rv))
    2894           0 :         rv = WaitForRedirectCallback();
    2895             : 
    2896           0 :     if (NS_FAILED(rv)) {
    2897           0 :         AutoRedirectVetoNotifier notifier(this);
    2898             : 
    2899             :         /* Remove the async call to ContinueAsyncRedirectChannelToURI().
    2900             :          * It is called directly by our callers upon return (to clean up
    2901             :          * the failed redirect). */
    2902             :         PopRedirectAsyncFunc(
    2903           0 :             &nsHttpChannel::ContinueAsyncRedirectChannelToURI);
    2904             :     }
    2905             : 
    2906           0 :     return rv;
    2907             : }
    2908             : 
    2909             : nsresult
    2910           0 : nsHttpChannel::ContinueAsyncRedirectChannelToURI(nsresult rv)
    2911             : {
    2912           0 :     LOG(("nsHttpChannel::ContinueAsyncRedirectChannelToURI [this=%p]", this));
    2913             : 
    2914             :     // Since we handle mAPIRedirectToURI also after on-examine-response handler
    2915             :     // rather drop it here to avoid any redirect loops, even just hypothetical.
    2916           0 :     mAPIRedirectToURI = nullptr;
    2917             : 
    2918           0 :     if (NS_SUCCEEDED(rv)) {
    2919           0 :         rv = OpenRedirectChannel(rv);
    2920             :     }
    2921             : 
    2922           0 :     if (NS_FAILED(rv)) {
    2923             :         // Cancel the channel here, the update to https had been vetoed
    2924             :         // but from the security reasons we have to discard the whole channel
    2925             :         // load.
    2926           0 :         Cancel(rv);
    2927             :     }
    2928             : 
    2929           0 :     if (mLoadGroup) {
    2930           0 :         mLoadGroup->RemoveRequest(this, nullptr, mStatus);
    2931             :     }
    2932             : 
    2933           0 :     if (NS_FAILED(rv) && !mCachePump && !mTransactionPump) {
    2934             :         // We have to manually notify the listener because there is not any pump
    2935             :         // that would call our OnStart/StopRequest after resume from waiting for
    2936             :         // the redirect callback.
    2937           0 :         DoNotifyListener();
    2938             :     }
    2939             : 
    2940           0 :     return rv;
    2941             : }
    2942             : 
    2943             : nsresult
    2944           0 : nsHttpChannel::OpenRedirectChannel(nsresult rv)
    2945             : {
    2946           0 :     AutoRedirectVetoNotifier notifier(this);
    2947             : 
    2948             :     // Make sure to do this after we received redirect veto answer,
    2949             :     // i.e. after all sinks had been notified
    2950           0 :     mRedirectChannel->SetOriginalURI(mOriginalURI);
    2951             : 
    2952             :     // And now, notify observers the deprecated way
    2953           0 :     nsCOMPtr<nsIHttpEventSink> httpEventSink;
    2954           0 :     GetCallback(httpEventSink);
    2955           0 :     if (httpEventSink) {
    2956             :         // NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8
    2957             :         // versions.
    2958           0 :         rv = httpEventSink->OnRedirect(this, mRedirectChannel);
    2959           0 :         if (NS_FAILED(rv)) {
    2960           0 :             return rv;
    2961             :         }
    2962             :     }
    2963             : 
    2964             :     // open new channel
    2965           0 :     if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
    2966           0 :         MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!");
    2967           0 :         rv = mRedirectChannel->AsyncOpen2(mListener);
    2968             :     }
    2969             :     else {
    2970           0 :         rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
    2971             :     }
    2972           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2973             : 
    2974           0 :     mStatus = NS_BINDING_REDIRECTED;
    2975             : 
    2976           0 :     notifier.RedirectSucceeded();
    2977             : 
    2978           0 :     ReleaseListeners();
    2979             : 
    2980           0 :     return NS_OK;
    2981             : }
    2982             : 
    2983             : nsresult
    2984           0 : nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi)
    2985             : {
    2986           0 :     LOG(("nsHttpChannel::AsyncDoReplaceWithProxy [this=%p pi=%p]", this, pi));
    2987             :     nsresult rv;
    2988             : 
    2989           0 :     nsCOMPtr<nsIChannel> newChannel;
    2990           0 :     rv = gHttpHandler->NewProxiedChannel2(mURI, pi, mProxyResolveFlags,
    2991             :                                           mProxyURI, mLoadInfo,
    2992           0 :                                           getter_AddRefs(newChannel));
    2993           0 :     if (NS_FAILED(rv))
    2994           0 :         return rv;
    2995             : 
    2996           0 :     uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL;
    2997             : 
    2998           0 :     rv = SetupReplacementChannel(mURI, newChannel, true, flags);
    2999           0 :     if (NS_FAILED(rv))
    3000           0 :         return rv;
    3001             : 
    3002             :     // Inform consumers about this fake redirect
    3003           0 :     mRedirectChannel = newChannel;
    3004             : 
    3005           0 :     PushRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
    3006           0 :     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
    3007             : 
    3008           0 :     if (NS_SUCCEEDED(rv))
    3009           0 :         rv = WaitForRedirectCallback();
    3010             : 
    3011           0 :     if (NS_FAILED(rv)) {
    3012           0 :         AutoRedirectVetoNotifier notifier(this);
    3013           0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy);
    3014             :     }
    3015             : 
    3016           0 :     return rv;
    3017             : }
    3018             : 
    3019             : nsresult
    3020           0 : nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv)
    3021             : {
    3022           0 :     AutoRedirectVetoNotifier notifier(this);
    3023             : 
    3024           0 :     if (NS_FAILED(rv))
    3025           0 :         return rv;
    3026             : 
    3027           0 :     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
    3028             : 
    3029             :     // Make sure to do this after we received redirect veto answer,
    3030             :     // i.e. after all sinks had been notified
    3031           0 :     mRedirectChannel->SetOriginalURI(mOriginalURI);
    3032             : 
    3033             :     // open new channel
    3034           0 :     if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
    3035           0 :         MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!");
    3036           0 :         rv = mRedirectChannel->AsyncOpen2(mListener);
    3037             :     }
    3038             :     else {
    3039           0 :         rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
    3040             :     }
    3041           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3042             : 
    3043           0 :     mStatus = NS_BINDING_REDIRECTED;
    3044             : 
    3045           0 :     notifier.RedirectSucceeded();
    3046             : 
    3047           0 :     ReleaseListeners();
    3048             : 
    3049           0 :     return rv;
    3050             : }
    3051             : 
    3052             : nsresult
    3053           6 : nsHttpChannel::ResolveProxy()
    3054             : {
    3055           6 :     LOG(("nsHttpChannel::ResolveProxy [this=%p]\n", this));
    3056             : 
    3057             :     nsresult rv;
    3058             : 
    3059             :     nsCOMPtr<nsIProtocolProxyService> pps =
    3060          12 :             do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
    3061           6 :     if (NS_FAILED(rv))
    3062           0 :         return rv;
    3063             : 
    3064             :     // using the nsIProtocolProxyService2 allows a minor performance
    3065             :     // optimization, but if an add-on has only provided the original interface
    3066             :     // then it is ok to use that version.
    3067          12 :     nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps);
    3068           6 :     if (pps2) {
    3069          12 :         rv = pps2->AsyncResolve2(this, mProxyResolveFlags, this,
    3070          12 :                                  nullptr, getter_AddRefs(mProxyRequest));
    3071             :     } else {
    3072           0 :         rv = pps->AsyncResolve(static_cast<nsIChannel*>(this), mProxyResolveFlags,
    3073           0 :                                this, nullptr, getter_AddRefs(mProxyRequest));
    3074             :     }
    3075             : 
    3076           6 :     return rv;
    3077             : }
    3078             : 
    3079             : bool
    3080           4 : nsHttpChannel::ResponseWouldVary(nsICacheEntry* entry)
    3081             : {
    3082             :     nsresult rv;
    3083           8 :     nsAutoCString buf, metaKey;
    3084           4 :     Unused << mCachedResponseHead->GetHeader(nsHttp::Vary, buf);
    3085           4 :     if (!buf.IsEmpty()) {
    3086           0 :         NS_NAMED_LITERAL_CSTRING(prefix, "request-");
    3087             : 
    3088             :         // enumerate the elements of the Vary header...
    3089           0 :         char *val = buf.BeginWriting(); // going to munge buf
    3090           0 :         char *token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
    3091           0 :         while (token) {
    3092           0 :             LOG(("nsHttpChannel::ResponseWouldVary [channel=%p] " \
    3093             :                  "processing %s\n",
    3094             :                  this, token));
    3095             :             //
    3096             :             // if "*", then assume response would vary.  technically speaking,
    3097             :             // "Vary: header, *" is not permitted, but we allow it anyways.
    3098             :             //
    3099             :             // We hash values of cookie-headers for the following reasons:
    3100             :             //
    3101             :             //   1- cookies can be very large in size
    3102             :             //
    3103             :             //   2- cookies may contain sensitive information.  (for parity with
    3104             :             //      out policy of not storing Set-cookie headers in the cache
    3105             :             //      meta data, we likewise do not want to store cookie headers
    3106             :             //      here.)
    3107             :             //
    3108           0 :             if (*token == '*')
    3109           0 :                 return true; // if we encounter this, just get out of here
    3110             : 
    3111             :             // build cache meta data key...
    3112           0 :             metaKey = prefix + nsDependentCString(token);
    3113             : 
    3114             :             // check the last value of the given request header to see if it has
    3115             :             // since changed.  if so, then indeed the cached response is invalid.
    3116           0 :             nsXPIDLCString lastVal;
    3117           0 :             entry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal));
    3118           0 :             LOG(("nsHttpChannel::ResponseWouldVary [channel=%p] "
    3119             :                      "stored value = \"%s\"\n",
    3120             :                  this, lastVal.get()));
    3121             : 
    3122             :             // Look for value of "Cookie" in the request headers
    3123           0 :             nsHttpAtom atom = nsHttp::ResolveAtom(token);
    3124           0 :             nsAutoCString newVal;
    3125           0 :             bool hasHeader = NS_SUCCEEDED(mRequestHead.GetHeader(atom,
    3126             :                                                                  newVal));
    3127           0 :             if (!lastVal.IsEmpty()) {
    3128             :                 // value for this header in cache, but no value in request
    3129           0 :                 if (!hasHeader) {
    3130           0 :                     return true; // yes - response would vary
    3131             :                 }
    3132             : 
    3133             :                 // If this is a cookie-header, stored metadata is not
    3134             :                 // the value itself but the hash. So we also hash the
    3135             :                 // outgoing value here in order to compare the hashes
    3136           0 :                 nsAutoCString hash;
    3137           0 :                 if (atom == nsHttp::Cookie) {
    3138           0 :                     rv = Hash(newVal.get(), hash);
    3139             :                     // If hash failed, be conservative (the cached hash
    3140             :                     // exists at this point) and claim response would vary
    3141           0 :                     if (NS_FAILED(rv))
    3142           0 :                         return true;
    3143           0 :                     newVal = hash;
    3144             : 
    3145           0 :                     LOG(("nsHttpChannel::ResponseWouldVary [this=%p] " \
    3146             :                             "set-cookie value hashed to %s\n",
    3147             :                          this, newVal.get()));
    3148             :                 }
    3149             : 
    3150           0 :                 if (!newVal.Equals(lastVal)) {
    3151           0 :                     return true; // yes, response would vary
    3152             :                 }
    3153             : 
    3154           0 :             } else if (hasHeader) { // old value is empty, but newVal is set
    3155           0 :                 return true;
    3156             :             }
    3157             : 
    3158             :             // next token...
    3159           0 :             token = nsCRT::strtok(val, NS_HTTP_HEADER_SEPS, &val);
    3160             :         }
    3161             :     }
    3162           4 :     return false;
    3163             : }
    3164             : 
    3165             : // We need to have an implementation of this function just so that we can keep
    3166             : // all references to mCallOnResume of type nsHttpChannel:  it's not OK in C++
    3167             : // to set a member function ptr to  a base class function.
    3168             : void
    3169           0 : nsHttpChannel::HandleAsyncAbort()
    3170             : {
    3171           0 :     HttpAsyncAborter<nsHttpChannel>::HandleAsyncAbort();
    3172           0 : }
    3173             : 
    3174             : 
    3175             : //-----------------------------------------------------------------------------
    3176             : // nsHttpChannel <byte-range>
    3177             : //-----------------------------------------------------------------------------
    3178             : 
    3179             : bool
    3180           1 : nsHttpChannel::IsResumable(int64_t partialLen, int64_t contentLength,
    3181             :                            bool ignoreMissingPartialLen) const
    3182             : {
    3183             :     bool hasContentEncoding =
    3184           1 :         mCachedResponseHead->HasHeader(nsHttp::Content_Encoding);
    3185             : 
    3186           2 :     nsAutoCString etag;
    3187           1 :     Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, etag);
    3188           1 :     bool hasWeakEtag = !etag.IsEmpty() &&
    3189           1 :                        StringBeginsWith(etag, NS_LITERAL_CSTRING("W/"));
    3190             : 
    3191           0 :     return (partialLen < contentLength) &&
    3192           0 :            (partialLen > 0 || ignoreMissingPartialLen) &&
    3193           0 :            !hasContentEncoding && !hasWeakEtag &&
    3194           0 :            mCachedResponseHead->IsResumable() &&
    3195           1 :            !mCustomConditionalRequest &&
    3196           2 :            !mCachedResponseHead->NoStore();
    3197             : }
    3198             : 
    3199             : nsresult
    3200           0 : nsHttpChannel::MaybeSetupByteRangeRequest(int64_t partialLen, int64_t contentLength,
    3201             :                                           bool ignoreMissingPartialLen)
    3202             : {
    3203             :     // Be pesimistic
    3204           0 :     mIsPartialRequest = false;
    3205             : 
    3206           0 :     if (!IsResumable(partialLen, contentLength, ignoreMissingPartialLen))
    3207           0 :       return NS_ERROR_NOT_RESUMABLE;
    3208             : 
    3209             :     // looks like a partial entry we can reuse; add If-Range
    3210             :     // and Range headers.
    3211           0 :     nsresult rv = SetupByteRangeRequest(partialLen);
    3212           0 :     if (NS_FAILED(rv)) {
    3213             :         // Make the request unconditional again.
    3214           0 :         UntieByteRangeRequest();
    3215             :     }
    3216             : 
    3217           0 :     return rv;
    3218             : }
    3219             : 
    3220             : nsresult
    3221           0 : nsHttpChannel::SetupByteRangeRequest(int64_t partialLen)
    3222             : {
    3223             :     // cached content has been found to be partial, add necessary request
    3224             :     // headers to complete cache entry.
    3225             : 
    3226             :     // use strongest validator available...
    3227           0 :     nsAutoCString val;
    3228           0 :     Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, val);
    3229           0 :     if (val.IsEmpty())
    3230           0 :         Unused << mCachedResponseHead->GetHeader(nsHttp::Last_Modified, val);
    3231           0 :     if (val.IsEmpty()) {
    3232             :         // if we hit this code it means mCachedResponseHead->IsResumable() is
    3233             :         // either broken or not being called.
    3234           0 :         NS_NOTREACHED("no cache validator");
    3235           0 :         mIsPartialRequest = false;
    3236           0 :         return NS_ERROR_FAILURE;
    3237             :     }
    3238             : 
    3239             :     char buf[64];
    3240           0 :     SprintfLiteral(buf, "bytes=%" PRId64 "-", partialLen);
    3241             : 
    3242           0 :     DebugOnly<nsresult> rv;
    3243           0 :     rv = mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(buf));
    3244           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    3245           0 :     rv = mRequestHead.SetHeader(nsHttp::If_Range, val);
    3246           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    3247           0 :     mIsPartialRequest = true;
    3248             : 
    3249           0 :     return NS_OK;
    3250             : }
    3251             : 
    3252             : void
    3253           0 : nsHttpChannel::UntieByteRangeRequest()
    3254             : {
    3255           0 :     DebugOnly<nsresult> rv;
    3256           0 :     rv = mRequestHead.ClearHeader(nsHttp::Range);
    3257           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    3258           0 :     rv = mRequestHead.ClearHeader(nsHttp::If_Range);
    3259           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    3260           0 : }
    3261             : 
    3262             : nsresult
    3263           0 : nsHttpChannel::ProcessPartialContent()
    3264             : {
    3265             :     // ok, we've just received a 206
    3266             :     //
    3267             :     // we need to stream whatever data is in the cache out first, and then
    3268             :     // pick up whatever data is on the wire, writing it into the cache.
    3269             : 
    3270           0 :     LOG(("nsHttpChannel::ProcessPartialContent [this=%p]\n", this));
    3271             : 
    3272           0 :     NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
    3273           0 :     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
    3274             : 
    3275             :     // Make sure to clear bogus content-encodings before looking at the header
    3276           0 :     ClearBogusContentEncodingIfNeeded();
    3277             : 
    3278             :     // Check if the content-encoding we now got is different from the one we
    3279             :     // got before
    3280           0 :     nsAutoCString contentEncoding, cachedContentEncoding;
    3281             :     // It is possible that there is not such headers
    3282           0 :     Unused << mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
    3283           0 :     Unused << mCachedResponseHead->GetHeader(nsHttp::Content_Encoding,
    3284             :                                              cachedContentEncoding);
    3285           0 :     if (PL_strcasecmp(contentEncoding.get(), cachedContentEncoding.get())
    3286             :         != 0) {
    3287           0 :         Cancel(NS_ERROR_INVALID_CONTENT_ENCODING);
    3288           0 :         return CallOnStartRequest();
    3289             :     }
    3290             : 
    3291             :     nsresult rv;
    3292             : 
    3293           0 :     int64_t cachedContentLength = mCachedResponseHead->ContentLength();
    3294           0 :     int64_t entitySize = mResponseHead->TotalEntitySize();
    3295             : 
    3296           0 :     nsAutoCString contentRange;
    3297           0 :     Unused << mResponseHead->GetHeader(nsHttp::Content_Range, contentRange);
    3298           0 :     LOG(("nsHttpChannel::ProcessPartialContent [this=%p trans=%p] "
    3299             :          "original content-length %" PRId64
    3300             :          ", entity-size %" PRId64 ", content-range %s\n",
    3301             :          this, mTransaction.get(), cachedContentLength, entitySize,
    3302             :          contentRange.get()));
    3303             : 
    3304           0 :     if ((entitySize >= 0) && (cachedContentLength >= 0) &&
    3305             :         (entitySize != cachedContentLength)) {
    3306           0 :         LOG(("nsHttpChannel::ProcessPartialContent [this=%p] "
    3307             :              "206 has different total entity size than the content length "
    3308             :              "of the original partially cached entity.\n", this));
    3309             : 
    3310           0 :         mCacheEntry->AsyncDoom(nullptr);
    3311           0 :         Cancel(NS_ERROR_CORRUPTED_CONTENT);
    3312           0 :         return CallOnStartRequest();
    3313             :     }
    3314             : 
    3315           0 :     if (mConcurrentCacheAccess) {
    3316             :         // We started to read cached data sooner than its write has been done.
    3317             :         // But the concurrent write has not finished completely, so we had to
    3318             :         // do a range request.  Now let the content coming from the network
    3319             :         // be presented to consumers and also stored to the cache entry.
    3320             : 
    3321           0 :         rv = InstallCacheListener(mLogicalOffset);
    3322           0 :         if (NS_FAILED(rv)) return rv;
    3323             : 
    3324           0 :         if (mOfflineCacheEntry) {
    3325           0 :             rv = InstallOfflineCacheListener(mLogicalOffset);
    3326           0 :             if (NS_FAILED(rv)) return rv;
    3327             :         }
    3328             :     } else {
    3329             :         // suspend the current transaction
    3330           0 :         rv = mTransactionPump->Suspend();
    3331           0 :         if (NS_FAILED(rv)) return rv;
    3332             :     }
    3333             : 
    3334             :     // merge any new headers with the cached response headers
    3335           0 :     rv = mCachedResponseHead->UpdateHeaders(mResponseHead);
    3336           0 :     if (NS_FAILED(rv)) return rv;
    3337             : 
    3338             :     // update the cached response head
    3339           0 :     nsAutoCString head;
    3340           0 :     mCachedResponseHead->Flatten(head, true);
    3341           0 :     rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
    3342           0 :     if (NS_FAILED(rv)) return rv;
    3343             : 
    3344             :     // make the cached response be the current response
    3345           0 :     mResponseHead = Move(mCachedResponseHead);
    3346             : 
    3347           0 :     UpdateInhibitPersistentCachingFlag();
    3348             : 
    3349           0 :     rv = UpdateExpirationTime();
    3350           0 :     if (NS_FAILED(rv)) return rv;
    3351             : 
    3352             :     // notify observers interested in looking at a response that has been
    3353             :     // merged with any cached headers (http-on-examine-merged-response).
    3354           0 :     gHttpHandler->OnExamineMergedResponse(this);
    3355             : 
    3356           0 :     if (mConcurrentCacheAccess) {
    3357           0 :         mCachedContentIsPartial = false;
    3358             :         // Leave the mConcurrentCacheAccess flag set, we want to use it
    3359             :         // to prevent duplicate OnStartRequest call on the target listener
    3360             :         // in case this channel is canceled before it gets its OnStartRequest
    3361             :         // from the http transaction.
    3362             : 
    3363             :         // Now we continue reading the network response.
    3364             :     } else {
    3365             :         // the cached content is valid, although incomplete.
    3366           0 :         mCachedContentIsValid = true;
    3367           0 :         rv = ReadFromCache(false);
    3368             :     }
    3369             : 
    3370           0 :     return rv;
    3371             : }
    3372             : 
    3373             : nsresult
    3374           0 : nsHttpChannel::OnDoneReadingPartialCacheEntry(bool *streamDone)
    3375             : {
    3376             :     nsresult rv;
    3377             : 
    3378           0 :     LOG(("nsHttpChannel::OnDoneReadingPartialCacheEntry [this=%p]", this));
    3379             : 
    3380             :     // by default, assume we would have streamed all data or failed...
    3381           0 :     *streamDone = true;
    3382             : 
    3383             :     // setup cache listener to append to cache entry
    3384             :     int64_t size;
    3385           0 :     rv = mCacheEntry->GetDataSize(&size);
    3386           0 :     if (NS_FAILED(rv)) return rv;
    3387             : 
    3388           0 :     rv = InstallCacheListener(size);
    3389           0 :     if (NS_FAILED(rv)) return rv;
    3390             : 
    3391             :     // Entry is valid, do it now, after the output stream has been opened,
    3392             :     // otherwise when done earlier, pending readers would consider the cache
    3393             :     // entry still as partial (CacheEntry::GetDataSize would return the partial
    3394             :     // data size) and consumers would do the conditional request again.
    3395           0 :     rv = mCacheEntry->SetValid();
    3396           0 :     if (NS_FAILED(rv)) return rv;
    3397             : 
    3398             :     // need to track the logical offset of the data being sent to our listener
    3399           0 :     mLogicalOffset = size;
    3400             : 
    3401             :     // we're now completing the cached content, so we can clear this flag.
    3402             :     // this puts us in the state of a regular download.
    3403           0 :     mCachedContentIsPartial = false;
    3404             :     // The cache input stream pump is finished, we do not need it any more.
    3405             :     // (see bug 1313923)
    3406           0 :     mCachePump = nullptr;
    3407             : 
    3408             :     // resume the transaction if it exists, otherwise the pipe contained the
    3409             :     // remaining part of the document and we've now streamed all of the data.
    3410           0 :     if (mTransactionPump) {
    3411           0 :         rv = mTransactionPump->Resume();
    3412           0 :         if (NS_SUCCEEDED(rv))
    3413           0 :             *streamDone = false;
    3414             :     }
    3415             :     else
    3416           0 :         NS_NOTREACHED("no transaction");
    3417           0 :     return rv;
    3418             : }
    3419             : 
    3420             : //-----------------------------------------------------------------------------
    3421             : // nsHttpChannel <cache>
    3422             : //-----------------------------------------------------------------------------
    3423             : 
    3424             : bool
    3425           0 : nsHttpChannel::ShouldBypassProcessNotModified()
    3426             : {
    3427           0 :     if (mCustomConditionalRequest) {
    3428           0 :         LOG(("Bypassing ProcessNotModified due to custom conditional headers"));
    3429           0 :         return true;
    3430             :     }
    3431             : 
    3432           0 :     if (!mDidReval) {
    3433           0 :         LOG(("Server returned a 304 response even though we did not send a "
    3434             :              "conditional request"));
    3435           0 :         return true;
    3436             :     }
    3437             : 
    3438           0 :     return false;
    3439             : }
    3440             : 
    3441             : nsresult
    3442           0 : nsHttpChannel::ProcessNotModified()
    3443             : {
    3444             :     nsresult rv;
    3445             : 
    3446           0 :     LOG(("nsHttpChannel::ProcessNotModified [this=%p]\n", this));
    3447             : 
    3448             :     // Assert ShouldBypassProcessNotModified() has been checked before call to
    3449             :     // ProcessNotModified().
    3450           0 :     MOZ_ASSERT(!ShouldBypassProcessNotModified());
    3451             : 
    3452           0 :     MOZ_ASSERT(mCachedResponseHead);
    3453           0 :     MOZ_ASSERT(mCacheEntry);
    3454           0 :     NS_ENSURE_TRUE(mCachedResponseHead && mCacheEntry, NS_ERROR_UNEXPECTED);
    3455             : 
    3456             :     // If the 304 response contains a Last-Modified different than the
    3457             :     // one in our cache that is pretty suspicious and is, in at least the
    3458             :     // case of bug 716840, a sign of the server having previously corrupted
    3459             :     // our cache with a bad response. Take the minor step here of just dooming
    3460             :     // that cache entry so there is a fighting chance of getting things on the
    3461             :     // right track.
    3462             : 
    3463           0 :     nsAutoCString lastModifiedCached;
    3464           0 :     nsAutoCString lastModified304;
    3465             : 
    3466           0 :     rv = mCachedResponseHead->GetHeader(nsHttp::Last_Modified,
    3467           0 :                                         lastModifiedCached);
    3468           0 :     if (NS_SUCCEEDED(rv)) {
    3469           0 :         rv = mResponseHead->GetHeader(nsHttp::Last_Modified,
    3470           0 :                                       lastModified304);
    3471             :     }
    3472             : 
    3473           0 :     if (NS_SUCCEEDED(rv) && !lastModified304.Equals(lastModifiedCached)) {
    3474           0 :         LOG(("Cache Entry and 304 Last-Modified Headers Do Not Match "
    3475             :              "[%s] and [%s]\n",
    3476             :              lastModifiedCached.get(), lastModified304.get()));
    3477             : 
    3478           0 :         mCacheEntry->AsyncDoom(nullptr);
    3479           0 :         Telemetry::Accumulate(Telemetry::CACHE_LM_INCONSISTENT, true);
    3480             :     }
    3481             : 
    3482             :     // merge any new headers with the cached response headers
    3483           0 :     rv = mCachedResponseHead->UpdateHeaders(mResponseHead);
    3484           0 :     if (NS_FAILED(rv)) return rv;
    3485             : 
    3486             :     // update the cached response head
    3487           0 :     nsAutoCString head;
    3488           0 :     mCachedResponseHead->Flatten(head, true);
    3489           0 :     rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
    3490           0 :     if (NS_FAILED(rv)) return rv;
    3491             : 
    3492             :     // make the cached response be the current response
    3493           0 :     mResponseHead = Move(mCachedResponseHead);
    3494             : 
    3495           0 :     UpdateInhibitPersistentCachingFlag();
    3496             : 
    3497           0 :     rv = UpdateExpirationTime();
    3498           0 :     if (NS_FAILED(rv)) return rv;
    3499             : 
    3500           0 :     rv = AddCacheEntryHeaders(mCacheEntry);
    3501           0 :     if (NS_FAILED(rv)) return rv;
    3502             : 
    3503             :     // notify observers interested in looking at a reponse that has been
    3504             :     // merged with any cached headers
    3505           0 :     gHttpHandler->OnExamineMergedResponse(this);
    3506             : 
    3507           0 :     mCachedContentIsValid = true;
    3508             : 
    3509             :     // Tell other consumers the entry is OK to use
    3510           0 :     rv = mCacheEntry->SetValid();
    3511           0 :     if (NS_FAILED(rv)) return rv;
    3512             : 
    3513           0 :     rv = ReadFromCache(false);
    3514           0 :     if (NS_FAILED(rv)) return rv;
    3515             : 
    3516           0 :     mTransactionReplaced = true;
    3517           0 :     return NS_OK;
    3518             : }
    3519             : 
    3520             : nsresult
    3521           2 : nsHttpChannel::ProcessFallback(bool *waitingForRedirectCallback)
    3522             : {
    3523           2 :     LOG(("nsHttpChannel::ProcessFallback [this=%p]\n", this));
    3524             :     nsresult rv;
    3525             : 
    3526           2 :     *waitingForRedirectCallback = false;
    3527           2 :     mFallingBack = false;
    3528             : 
    3529             :     // At this point a load has failed (either due to network problems
    3530             :     // or an error returned on the server).  Perform an application
    3531             :     // cache fallback if we have a URI to fall back to.
    3532           2 :     if (!mApplicationCache || mFallbackKey.IsEmpty() || mFallbackChannel) {
    3533           2 :         LOG(("  choosing not to fallback [%p,%s,%d]",
    3534             :              mApplicationCache.get(), mFallbackKey.get(), mFallbackChannel));
    3535           2 :         return NS_OK;
    3536             :     }
    3537             : 
    3538             :     // Make sure the fallback entry hasn't been marked as a foreign
    3539             :     // entry.
    3540             :     uint32_t fallbackEntryType;
    3541           0 :     rv = mApplicationCache->GetTypes(mFallbackKey, &fallbackEntryType);
    3542           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3543             : 
    3544           0 :     if (fallbackEntryType & nsIApplicationCache::ITEM_FOREIGN) {
    3545             :         // This cache points to a fallback that refers to a different
    3546             :         // manifest.  Refuse to fall back.
    3547           0 :         return NS_OK;
    3548             :     }
    3549             : 
    3550           0 :     if (!IsInSubpathOfAppCacheManifest(mApplicationCache, mFallbackKey)) {
    3551             :         // Refuse to fallback if the fallback key is not contained in the same
    3552             :         // path as the cache manifest.
    3553           0 :         return NS_OK;
    3554             :     }
    3555             : 
    3556           0 :     MOZ_ASSERT(fallbackEntryType & nsIApplicationCache::ITEM_FALLBACK,
    3557             :                "Fallback entry not marked correctly!");
    3558             : 
    3559             :     // Kill any offline cache entry, and disable offline caching for the
    3560             :     // fallback.
    3561           0 :     if (mOfflineCacheEntry) {
    3562           0 :         mOfflineCacheEntry->AsyncDoom(nullptr);
    3563           0 :         mOfflineCacheEntry = nullptr;
    3564             :     }
    3565             : 
    3566           0 :     mApplicationCacheForWrite = nullptr;
    3567           0 :     mOfflineCacheEntry = nullptr;
    3568             : 
    3569             :     // Close the current cache entry.
    3570           0 :     CloseCacheEntry(true);
    3571             : 
    3572             :     // Create a new channel to load the fallback entry.
    3573           0 :     RefPtr<nsIChannel> newChannel;
    3574           0 :     rv = gHttpHandler->NewChannel2(mURI,
    3575             :                                    mLoadInfo,
    3576           0 :                                    getter_AddRefs(newChannel));
    3577           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3578             : 
    3579           0 :     uint32_t redirectFlags = nsIChannelEventSink::REDIRECT_INTERNAL;
    3580           0 :     rv = SetupReplacementChannel(mURI, newChannel, true, redirectFlags);
    3581           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3582             : 
    3583             :     // Make sure the new channel loads from the fallback key.
    3584             :     nsCOMPtr<nsIHttpChannelInternal> httpInternal =
    3585           0 :         do_QueryInterface(newChannel, &rv);
    3586           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3587             : 
    3588           0 :     rv = httpInternal->SetupFallbackChannel(mFallbackKey.get());
    3589           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3590             : 
    3591             :     // ... and fallbacks should only load from the cache.
    3592           0 :     uint32_t newLoadFlags = mLoadFlags | LOAD_REPLACE | LOAD_ONLY_FROM_CACHE;
    3593           0 :     rv = newChannel->SetLoadFlags(newLoadFlags);
    3594             : 
    3595             :     // Inform consumers about this fake redirect
    3596           0 :     mRedirectChannel = newChannel;
    3597             : 
    3598           0 :     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
    3599           0 :     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
    3600             : 
    3601           0 :     if (NS_SUCCEEDED(rv))
    3602           0 :         rv = WaitForRedirectCallback();
    3603             : 
    3604           0 :     if (NS_FAILED(rv)) {
    3605           0 :         AutoRedirectVetoNotifier notifier(this);
    3606           0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback);
    3607           0 :         return rv;
    3608             :     }
    3609             : 
    3610             :     // Indicate we are now waiting for the asynchronous redirect callback
    3611             :     // if all went OK.
    3612           0 :     *waitingForRedirectCallback = true;
    3613           0 :     return NS_OK;
    3614             : }
    3615             : 
    3616             : nsresult
    3617           0 : nsHttpChannel::ContinueProcessFallback(nsresult rv)
    3618             : {
    3619           0 :     AutoRedirectVetoNotifier notifier(this);
    3620             : 
    3621           0 :     if (NS_FAILED(rv))
    3622           0 :         return rv;
    3623             : 
    3624           0 :     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
    3625             : 
    3626             :     // Make sure to do this after we received redirect veto answer,
    3627             :     // i.e. after all sinks had been notified
    3628           0 :     mRedirectChannel->SetOriginalURI(mOriginalURI);
    3629             : 
    3630           0 :     if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
    3631           0 :         MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!");
    3632           0 :         rv = mRedirectChannel->AsyncOpen2(mListener);
    3633             :     }
    3634             :     else {
    3635           0 :         rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
    3636             :     }
    3637           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3638             : 
    3639           0 :     if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
    3640           0 :         MaybeWarnAboutAppCache();
    3641             :     }
    3642             : 
    3643             :     // close down this channel
    3644           0 :     Cancel(NS_BINDING_REDIRECTED);
    3645             : 
    3646           0 :     notifier.RedirectSucceeded();
    3647             : 
    3648           0 :     ReleaseListeners();
    3649             : 
    3650           0 :     mFallingBack = true;
    3651             : 
    3652           0 :     return NS_OK;
    3653             : }
    3654             : 
    3655             : // Determines if a request is a byte range request for a subrange,
    3656             : // i.e. is a byte range request, but not a 0- byte range request.
    3657             : static bool
    3658           6 : IsSubRangeRequest(nsHttpRequestHead &aRequestHead)
    3659             : {
    3660          12 :     nsAutoCString byteRange;
    3661           6 :     if (NS_FAILED(aRequestHead.GetHeader(nsHttp::Range, byteRange))) {
    3662           6 :         return false;
    3663             :     }
    3664           0 :     return !byteRange.EqualsLiteral("bytes=0-");
    3665             : }
    3666             : 
    3667             : nsresult
    3668           6 : nsHttpChannel::OpenCacheEntry(bool isHttps)
    3669             : {
    3670             :     // Handle correctly mCacheEntriesToWaitFor
    3671          12 :     AutoCacheWaitFlags waitFlags(this);
    3672             : 
    3673             :     // Drop this flag here
    3674           6 :     mConcurrentCacheAccess = 0;
    3675             : 
    3676             :     nsresult rv;
    3677             : 
    3678           6 :     mLoadedFromApplicationCache = false;
    3679           6 :     mHasQueryString = HasQueryString(mRequestHead.ParsedMethod(), mURI);
    3680             : 
    3681           6 :     LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
    3682             : 
    3683             :     // make sure we're not abusing this function
    3684           6 :     NS_PRECONDITION(!mCacheEntry, "cache entry already open");
    3685             : 
    3686          12 :     nsAutoCString cacheKey;
    3687          12 :     nsAutoCString extension;
    3688             : 
    3689           6 :     if (mRequestHead.IsPost()) {
    3690             :         // If the post id is already set then this is an attempt to replay
    3691             :         // a post transaction via the cache.  Otherwise, we need a unique
    3692             :         // post id for this transaction.
    3693           1 :         if (mPostID == 0)
    3694           1 :             mPostID = gHttpHandler->GenerateUniqueID();
    3695             :     }
    3696           5 :     else if (!PossiblyIntercepted() && !mRequestHead.IsGet() && !mRequestHead.IsHead()) {
    3697             :         // don't use the cache for other types of requests
    3698           0 :         return NS_OK;
    3699             :     }
    3700             : 
    3701           6 :     if (mResuming) {
    3702             :         // We don't support caching for requests initiated
    3703             :         // via nsIResumableChannel.
    3704           0 :         return NS_OK;
    3705             :     }
    3706             : 
    3707             :     // Don't cache byte range requests which are subranges, only cache 0-
    3708             :     // byte range requests.
    3709           6 :     if (IsSubRangeRequest(mRequestHead))
    3710           0 :         return NS_OK;
    3711             : 
    3712             :     // Pick up an application cache from the notification
    3713             :     // callbacks if available and if we are not an intercepted channel.
    3714          12 :     if (!PossiblyIntercepted() && !mApplicationCache &&
    3715           6 :         mInheritApplicationCache) {
    3716           6 :         nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
    3717           3 :         GetCallback(appCacheContainer);
    3718             : 
    3719           3 :         if (appCacheContainer) {
    3720           1 :             appCacheContainer->GetApplicationCache(getter_AddRefs(mApplicationCache));
    3721             :         }
    3722             :     }
    3723             : 
    3724          12 :     nsCOMPtr<nsICacheStorageService> cacheStorageService(services::GetCacheStorageService());
    3725           6 :     if (!cacheStorageService) {
    3726           0 :         return NS_ERROR_NOT_AVAILABLE;
    3727             :     }
    3728             : 
    3729          12 :     nsCOMPtr<nsICacheStorage> cacheStorage;
    3730          12 :     nsCOMPtr<nsIURI> openURI;
    3731           6 :     if (!mFallbackKey.IsEmpty() && mFallbackChannel) {
    3732             :         // This is a fallback channel, open fallback URI instead
    3733           0 :         rv = NS_NewURI(getter_AddRefs(openURI), mFallbackKey);
    3734           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3735             :     }
    3736             :     else {
    3737             :         // In the case of intercepted channels, we need to construct the cache
    3738             :         // entry key based on the original URI, so that in case the intercepted
    3739             :         // channel is redirected, the cache entry key before and after the
    3740             :         // redirect is the same.
    3741           6 :         if (PossiblyIntercepted()) {
    3742           0 :             openURI = mOriginalURI;
    3743             :         } else {
    3744           6 :             openURI = mURI;
    3745             :         }
    3746             :     }
    3747             : 
    3748          12 :     RefPtr<LoadContextInfo> info = GetLoadContextInfo(this);
    3749           6 :     if (!info) {
    3750           0 :         return NS_ERROR_FAILURE;
    3751             :     }
    3752             : 
    3753             :     uint32_t cacheEntryOpenFlags;
    3754           6 :     bool offline = gIOService->IsOffline();
    3755             : 
    3756          12 :     nsAutoCString cacheControlRequestHeader;
    3757           6 :     Unused << mRequestHead.GetHeader(nsHttp::Cache_Control, cacheControlRequestHeader);
    3758          12 :     CacheControlParser cacheControlRequest(cacheControlRequestHeader);
    3759           6 :     if (cacheControlRequest.NoStore() && !PossiblyIntercepted()) {
    3760           0 :         goto bypassCacheEntryOpen;
    3761             :     }
    3762             : 
    3763           6 :     if (offline || (mLoadFlags & INHIBIT_CACHING)) {
    3764           1 :         if (BYPASS_LOCAL_CACHE(mLoadFlags) && !offline && !PossiblyIntercepted()) {
    3765           1 :             goto bypassCacheEntryOpen;
    3766             :         }
    3767           0 :         cacheEntryOpenFlags = nsICacheStorage::OPEN_READONLY;
    3768           0 :         mCacheEntryIsReadOnly = true;
    3769             :     }
    3770           5 :     else if (BYPASS_LOCAL_CACHE(mLoadFlags) && !mApplicationCache) {
    3771           0 :         cacheEntryOpenFlags = nsICacheStorage::OPEN_TRUNCATE;
    3772             :     }
    3773             :     else {
    3774           5 :         cacheEntryOpenFlags = nsICacheStorage::OPEN_NORMALLY
    3775             :                             | nsICacheStorage::CHECK_MULTITHREADED;
    3776             :     }
    3777             : 
    3778             :     // Remember the request is a custom conditional request so that we can
    3779             :     // process any 304 response correctly.
    3780           5 :     mCustomConditionalRequest =
    3781          10 :         mRequestHead.HasHeader(nsHttp::If_Modified_Since) ||
    3782          10 :         mRequestHead.HasHeader(nsHttp::If_None_Match) ||
    3783          10 :         mRequestHead.HasHeader(nsHttp::If_Unmodified_Since) ||
    3784          15 :         mRequestHead.HasHeader(nsHttp::If_Match) ||
    3785           5 :         mRequestHead.HasHeader(nsHttp::If_Range);
    3786             : 
    3787           5 :     if (!mPostID && mApplicationCache) {
    3788           0 :         rv = cacheStorageService->AppCacheStorage(info,
    3789             :             mApplicationCache,
    3790           0 :             getter_AddRefs(cacheStorage));
    3791           5 :     } else if (PossiblyIntercepted()) {
    3792             :         // The synthesized cache has less restrictions on file size and so on.
    3793           0 :         rv = cacheStorageService->SynthesizedCacheStorage(info,
    3794           0 :             getter_AddRefs(cacheStorage));
    3795           5 :     } else if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) {
    3796           0 :         rv = cacheStorageService->MemoryCacheStorage(info, // ? choose app cache as well...
    3797           0 :             getter_AddRefs(cacheStorage));
    3798             :     }
    3799           5 :     else if (mPinCacheContent) {
    3800           0 :         rv = cacheStorageService->PinningCacheStorage(info,
    3801           0 :             getter_AddRefs(cacheStorage));
    3802             :     }
    3803             :     else {
    3804          15 :         rv = cacheStorageService->DiskCacheStorage(info,
    3805           5 :             !mPostID && (mChooseApplicationCache || (mLoadFlags & LOAD_CHECK_OFFLINE_CACHE)),
    3806          10 :             getter_AddRefs(cacheStorage));
    3807             :     }
    3808           5 :     NS_ENSURE_SUCCESS(rv, rv);
    3809             : 
    3810           8 :     if ((mClassOfService & nsIClassOfService::Leader) ||
    3811           3 :         (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI))
    3812           3 :         cacheEntryOpenFlags |= nsICacheStorage::OPEN_PRIORITY;
    3813             : 
    3814             :     // Only for backward compatibility with the old cache back end.
    3815             :     // When removed, remove the flags and related code snippets.
    3816           5 :     if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY)
    3817           0 :         cacheEntryOpenFlags |= nsICacheStorage::OPEN_BYPASS_IF_BUSY;
    3818             : 
    3819           5 :     if (PossiblyIntercepted()) {
    3820           0 :         extension.Append(nsPrintfCString("u%" PRIu64, mInterceptionID));
    3821           5 :     } else if (mPostID) {
    3822           0 :         extension.Append(nsPrintfCString("%d", mPostID));
    3823             :     }
    3824             : 
    3825             :     // If this channel should be intercepted, we do not open a cache entry for this channel
    3826             :     // until the interception process is complete and the consumer decides what to do with it.
    3827           5 :     if (mInterceptCache == MAYBE_INTERCEPT) {
    3828           0 :         DebugOnly<bool> exists;
    3829           0 :         MOZ_ASSERT(NS_FAILED(cacheStorage->Exists(openURI, extension, &exists)) || !exists,
    3830             :                    "The entry must not exist in the cache before we create it here");
    3831             : 
    3832           0 :         nsCOMPtr<nsICacheEntry> entry;
    3833           0 :         rv = cacheStorage->OpenTruncate(openURI, extension, getter_AddRefs(entry));
    3834           0 :         NS_ENSURE_SUCCESS(rv, rv);
    3835             : 
    3836           0 :         nsCOMPtr<nsINetworkInterceptController> controller;
    3837           0 :         GetCallback(controller);
    3838             : 
    3839             :         RefPtr<InterceptedChannelChrome> intercepted =
    3840           0 :                 new InterceptedChannelChrome(this, controller, entry);
    3841           0 :         intercepted->NotifyController();
    3842             :     } else {
    3843           5 :         if (mInterceptCache == INTERCEPTED) {
    3844           0 :             cacheEntryOpenFlags |= nsICacheStorage::OPEN_INTERCEPTED;
    3845             :             // Clear OPEN_TRUNCATE for the fake cache entry, since otherwise
    3846             :             // cache storage will close the current entry which breaks the
    3847             :             // response synthesis.
    3848           0 :             cacheEntryOpenFlags &= ~nsICacheStorage::OPEN_TRUNCATE;
    3849           0 :             DebugOnly<bool> exists;
    3850           0 :             MOZ_ASSERT(NS_SUCCEEDED(cacheStorage->Exists(openURI, extension, &exists)) && exists,
    3851             :                        "The entry must exist in the cache after we create it here");
    3852             :         }
    3853             : 
    3854           5 :         mCacheOpenWithPriority = cacheEntryOpenFlags & nsICacheStorage::OPEN_PRIORITY;
    3855           5 :         mCacheQueueSizeWhenOpen = CacheStorageService::CacheQueueSize(mCacheOpenWithPriority);
    3856             : 
    3857           5 :         bool hasAltData = false;
    3858           5 :         uint32_t sizeInKb = 0;
    3859          10 :         rv = cacheStorage->GetCacheIndexEntryAttrs(openURI, extension,
    3860          10 :                                                    &hasAltData, &sizeInKb);
    3861             : 
    3862             :         // We will attempt to race the network vs the cache if we've found this
    3863             :         // entry in the cache index, and it has appropriate
    3864             :         // attributes (doesn't have alt-data, and has a small size)
    3865           5 :         if (sRCWNEnabled && mInterceptCache != INTERCEPTED &&
    3866           5 :             NS_SUCCEEDED(rv) && !hasAltData && sizeInKb < sRCWNSmallResourceSizeKB) {
    3867           0 :             MaybeRaceCacheWithNetwork();
    3868             :         }
    3869             : 
    3870           5 :         if (!mCacheOpenDelay) {
    3871           5 :             MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread");
    3872           5 :             mCacheAsyncOpenCalled = true;
    3873           5 :             if (mNetworkTriggered) {
    3874           0 :                 mRaceCacheWithNetwork = true;
    3875             :             }
    3876           5 :             rv = cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags, this);
    3877             :         } else {
    3878             :             // We pass `this` explicitly as a parameter due to the raw pointer
    3879             :             // to refcounted object in lambda analysis.
    3880           0 :             mCacheOpenFunc = [openURI, extension, cacheEntryOpenFlags, cacheStorage] (nsHttpChannel* self) -> void {
    3881           0 :                 MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread");
    3882           0 :                 self->mCacheAsyncOpenCalled = true;
    3883           0 :                 if (self->mNetworkTriggered) {
    3884           0 :                     self->mRaceCacheWithNetwork = true;
    3885             :                 }
    3886           0 :                 cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags, self);
    3887           0 :             };
    3888             : 
    3889           0 :             mCacheOpenTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
    3890             :             // calls nsHttpChannel::Notify after `mCacheOpenDelay` milliseconds
    3891           0 :             mCacheOpenTimer->InitWithCallback(this, mCacheOpenDelay, nsITimer::TYPE_ONE_SHOT);
    3892             : 
    3893             :         }
    3894           5 :         NS_ENSURE_SUCCESS(rv, rv);
    3895             :     }
    3896             : 
    3897           5 :     waitFlags.Keep(WAIT_FOR_CACHE_ENTRY);
    3898             : 
    3899             : bypassCacheEntryOpen:
    3900           6 :     if (!mApplicationCacheForWrite)
    3901           6 :         return NS_OK;
    3902             : 
    3903             :     // If there is an app cache to write to, open the entry right now in parallel.
    3904             : 
    3905             :     // make sure we're not abusing this function
    3906           0 :     NS_PRECONDITION(!mOfflineCacheEntry, "cache entry already open");
    3907             : 
    3908           0 :     if (offline) {
    3909             :         // only put things in the offline cache while online
    3910           0 :         return NS_OK;
    3911             :     }
    3912             : 
    3913           0 :     if (mLoadFlags & INHIBIT_CACHING) {
    3914             :         // respect demand not to cache
    3915           0 :         return NS_OK;
    3916             :     }
    3917             : 
    3918           0 :     if (!mRequestHead.IsGet()) {
    3919             :         // only cache complete documents offline
    3920           0 :         return NS_OK;
    3921             :     }
    3922             : 
    3923           0 :     rv = cacheStorageService->AppCacheStorage(info, mApplicationCacheForWrite,
    3924           0 :                                               getter_AddRefs(cacheStorage));
    3925           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3926             : 
    3927           0 :     rv = cacheStorage->AsyncOpenURI(
    3928           0 :       mURI, EmptyCString(), nsICacheStorage::OPEN_TRUNCATE, this);
    3929           0 :     NS_ENSURE_SUCCESS(rv, rv);
    3930             : 
    3931           0 :     waitFlags.Keep(WAIT_FOR_OFFLINE_CACHE_ENTRY);
    3932             : 
    3933           0 :     return NS_OK;
    3934             : }
    3935             : 
    3936             : nsresult
    3937           4 : nsHttpChannel::CheckPartial(nsICacheEntry* aEntry, int64_t *aSize, int64_t *aContentLength)
    3938             : {
    3939             :     nsresult rv;
    3940             : 
    3941           4 :     rv = aEntry->GetDataSize(aSize);
    3942             : 
    3943           4 :     if (NS_ERROR_IN_PROGRESS == rv) {
    3944           1 :         *aSize = -1;
    3945           1 :         rv = NS_OK;
    3946             :     }
    3947             : 
    3948           4 :     NS_ENSURE_SUCCESS(rv, rv);
    3949             : 
    3950             :     nsHttpResponseHead* responseHead = mCachedResponseHead
    3951             :         ? mCachedResponseHead
    3952           4 :         : mResponseHead;
    3953             : 
    3954           4 :     if (!responseHead)
    3955           0 :         return NS_ERROR_UNEXPECTED;
    3956             : 
    3957           4 :     *aContentLength = responseHead->ContentLength();
    3958             : 
    3959           4 :     return NS_OK;
    3960             : }
    3961             : 
    3962             : void
    3963           0 : nsHttpChannel::UntieValidationRequest()
    3964             : {
    3965           0 :     DebugOnly<nsresult> rv;
    3966             :     // Make the request unconditional again.
    3967           0 :     rv = mRequestHead.ClearHeader(nsHttp::If_Modified_Since);
    3968           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    3969           0 :     rv = mRequestHead.ClearHeader(nsHttp::If_None_Match);
    3970           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    3971           0 :     rv = mRequestHead.ClearHeader(nsHttp::ETag);
    3972           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    3973           0 : }
    3974             : 
    3975             : NS_IMETHODIMP
    3976           4 : nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, nsIApplicationCache* appCache,
    3977             :                                  uint32_t* aResult)
    3978             : {
    3979           4 :     nsresult rv = NS_OK;
    3980             : 
    3981           4 :     LOG(("nsHttpChannel::OnCacheEntryCheck enter [channel=%p entry=%p]",
    3982             :         this, entry));
    3983             : 
    3984           4 :     if (mRaceCacheWithNetwork && mFirstResponseSource == RESPONSE_FROM_NETWORK) {
    3985           0 :         LOG(("Not using cached response because we've already got one from the network\n"));
    3986           0 :         *aResult = ENTRY_NOT_WANTED;
    3987             : 
    3988             :         // Net-win indicates that mOnStartRequestTimestamp is from net.
    3989           0 :         int64_t savedTime = (TimeStamp::Now() - mOnStartRequestTimestamp).ToMilliseconds();
    3990           0 :         Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_SAVED_TIME, savedTime);
    3991           0 :         return NS_OK;
    3992           4 :     } else if (mRaceCacheWithNetwork && mFirstResponseSource == RESPONSE_PENDING) {
    3993           0 :         mOnCacheEntryCheckTimestamp = TimeStamp::Now();
    3994             :     }
    3995             : 
    3996           8 :     nsAutoCString cacheControlRequestHeader;
    3997           4 :     Unused << mRequestHead.GetHeader(nsHttp::Cache_Control, cacheControlRequestHeader);
    3998           8 :     CacheControlParser cacheControlRequest(cacheControlRequestHeader);
    3999             : 
    4000           4 :     if (cacheControlRequest.NoStore()) {
    4001           0 :         LOG(("Not using cached response based on no-store request cache directive\n"));
    4002           0 :         *aResult = ENTRY_NOT_WANTED;
    4003           0 :         return NS_OK;
    4004             :     }
    4005             : 
    4006             :     // Be pessimistic: assume the cache entry has no useful data.
    4007           4 :     *aResult = ENTRY_WANTED;
    4008           4 :     mCachedContentIsValid = false;
    4009             : 
    4010           8 :     nsXPIDLCString buf;
    4011             : 
    4012             :     // Get the method that was used to generate the cached response
    4013           4 :     rv = entry->GetMetaDataElement("request-method", getter_Copies(buf));
    4014           4 :     NS_ENSURE_SUCCESS(rv, rv);
    4015             : 
    4016           4 :     bool methodWasHead = buf.EqualsLiteral("HEAD");
    4017           4 :     bool methodWasGet = buf.EqualsLiteral("GET");
    4018             : 
    4019           4 :     if (methodWasHead) {
    4020             :         // The cached response does not contain an entity.  We can only reuse
    4021             :         // the response if the current request is also HEAD.
    4022           0 :         if (!mRequestHead.IsHead()) {
    4023           0 :             return NS_OK;
    4024             :         }
    4025             :     }
    4026           4 :     buf.Adopt(0);
    4027             : 
    4028             :     // We'll need this value in later computations...
    4029             :     uint32_t lastModifiedTime;
    4030           4 :     rv = entry->GetLastModified(&lastModifiedTime);
    4031           4 :     NS_ENSURE_SUCCESS(rv, rv);
    4032             : 
    4033             :     // Determine if this is the first time that this cache entry
    4034             :     // has been accessed during this session.
    4035             :     bool fromPreviousSession =
    4036           4 :             (gHttpHandler->SessionStartTime() > lastModifiedTime);
    4037             : 
    4038             :     // Get the cached HTTP response headers
    4039           4 :     mCachedResponseHead = new nsHttpResponseHead();
    4040             : 
    4041             :     // A "original-response-headers" metadata element holds network original headers,
    4042             :     // i.e. the headers in the form as they arrieved from the network.
    4043             :     // We need to get the network original headers first, because we need to keep them
    4044             :     // in order.
    4045           4 :     rv = entry->GetMetaDataElement("original-response-headers", getter_Copies(buf));
    4046           4 :     if (NS_SUCCEEDED(rv)) {
    4047           4 :         rv = mCachedResponseHead->ParseCachedOriginalHeaders((char *) buf.get());
    4048           4 :         if (NS_FAILED(rv)) {
    4049           0 :             LOG(("  failed to parse original-response-headers\n"));
    4050             :         }
    4051             :     }
    4052             : 
    4053           4 :     buf.Adopt(0);
    4054             :     // A "response-head" metadata element holds response head, e.g. response status
    4055             :     // line and headers in the form Firefox uses them internally (no dupicate
    4056             :     // headers, etc.).
    4057           4 :     rv = entry->GetMetaDataElement("response-head", getter_Copies(buf));
    4058           4 :     NS_ENSURE_SUCCESS(rv, rv);
    4059             : 
    4060             :     // Parse string stored in a "response-head" metadata element.
    4061             :     // These response headers will be merged with the orignal headers (i.e. the
    4062             :     // headers stored in a "original-response-headers" metadata element).
    4063           4 :     rv = mCachedResponseHead->ParseCachedHead(buf.get());
    4064           4 :     NS_ENSURE_SUCCESS(rv, rv);
    4065           4 :     buf.Adopt(0);
    4066             : 
    4067           4 :     bool isCachedRedirect = WillRedirect(mCachedResponseHead);
    4068             : 
    4069             :     // Do not return 304 responses from the cache, and also do not return
    4070             :     // any other non-redirect 3xx responses from the cache (see bug 759043).
    4071           4 :     NS_ENSURE_TRUE((mCachedResponseHead->Status() / 100 != 3) ||
    4072             :                    isCachedRedirect, NS_ERROR_ABORT);
    4073             : 
    4074           4 :     if (mCachedResponseHead->NoStore() && mCacheEntryIsReadOnly) {
    4075             :         // This prevents loading no-store responses when navigating back
    4076             :         // while the browser is set to work offline.
    4077           0 :         LOG(("  entry loading as read-only but is no-store, set INHIBIT_CACHING"));
    4078           0 :         mLoadFlags |= nsIRequest::INHIBIT_CACHING;
    4079             :     }
    4080             : 
    4081             :     // Don't bother to validate items that are read-only,
    4082             :     // unless they are read-only because of INHIBIT_CACHING or because
    4083             :     // we're updating the offline cache.
    4084             :     // Don't bother to validate if this is a fallback entry.
    4085           4 :     if (!mApplicationCacheForWrite &&
    4086           4 :         (appCache ||
    4087           8 :          (mCacheEntryIsReadOnly && !(mLoadFlags & nsIRequest::INHIBIT_CACHING)) ||
    4088           4 :          mFallbackChannel)) {
    4089           0 :         rv = OpenCacheInputStream(entry, true, !!appCache);
    4090           0 :         if (NS_SUCCEEDED(rv)) {
    4091           0 :             mCachedContentIsValid = true;
    4092           0 :             entry->MaybeMarkValid();
    4093             :         }
    4094           0 :         return rv;
    4095             :     }
    4096             : 
    4097           4 :     bool wantCompleteEntry = false;
    4098             : 
    4099           4 :     if (!methodWasHead && !isCachedRedirect) {
    4100             :         // If the cached content-length is set and it does not match the data
    4101             :         // size of the cached content, then the cached response is partial...
    4102             :         // either we need to issue a byte range request or we need to refetch
    4103             :         // the entire document.
    4104             :         //
    4105             :         // We exclude redirects from this check because we (usually) strip the
    4106             :         // entity when we store the cache entry, and even if we didn't, we
    4107             :         // always ignore a cached redirect's entity anyway. See bug 759043.
    4108             :         int64_t size, contentLength;
    4109           4 :         rv = CheckPartial(entry, &size, &contentLength);
    4110           4 :         NS_ENSURE_SUCCESS(rv,rv);
    4111             : 
    4112           4 :         if (size == int64_t(-1)) {
    4113           1 :             LOG(("  write is in progress"));
    4114           1 :             if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
    4115           0 :                 LOG(("  not interested in the entry, "
    4116             :                      "LOAD_BYPASS_LOCAL_CACHE_IF_BUSY specified"));
    4117             : 
    4118           0 :                 *aResult = ENTRY_NOT_WANTED;
    4119           0 :                 return NS_OK;
    4120             :             }
    4121             : 
    4122             :             // Ignore !(size > 0) from the resumability condition
    4123           1 :             if (!IsResumable(size, contentLength, true)) {
    4124           1 :                 LOG(("  wait for entry completion, "
    4125             :                      "response is not resumable"));
    4126             : 
    4127           1 :                 wantCompleteEntry = true;
    4128             :             }
    4129             :             else {
    4130           0 :                 mConcurrentCacheAccess = 1;
    4131             :             }
    4132             :         }
    4133           3 :         else if (contentLength != int64_t(-1) && contentLength != size) {
    4134           0 :             LOG(("Cached data size does not match the Content-Length header "
    4135             :                  "[content-length=%" PRId64 " size=%" PRId64 "]\n", contentLength, size));
    4136             : 
    4137           0 :             rv = MaybeSetupByteRangeRequest(size, contentLength);
    4138           0 :             mCachedContentIsPartial = NS_SUCCEEDED(rv) && mIsPartialRequest;
    4139           0 :             if (mCachedContentIsPartial) {
    4140           0 :                 rv = OpenCacheInputStream(entry, false, !!appCache);
    4141           0 :                 if (NS_FAILED(rv)) {
    4142           0 :                     UntieByteRangeRequest();
    4143           0 :                     return rv;
    4144             :                 }
    4145             : 
    4146           0 :                 *aResult = ENTRY_NEEDS_REVALIDATION;
    4147           0 :                 return NS_OK;
    4148             :             }
    4149             : 
    4150           0 :             if (size == 0 && mCacheOnlyMetadata) {
    4151             :                 // Don't break cache entry load when the entry's data size
    4152             :                 // is 0 and mCacheOnlyMetadata flag is set. In that case we
    4153             :                 // want to proceed since the LOAD_ONLY_IF_MODIFIED flag is
    4154             :                 // also set.
    4155           0 :                 MOZ_ASSERT(mLoadFlags & LOAD_ONLY_IF_MODIFIED);
    4156           0 :             } else if (mInterceptCache != INTERCEPTED) {
    4157           0 :                 return rv;
    4158             :             }
    4159             :         }
    4160             :     }
    4161             : 
    4162           4 :     bool isHttps = false;
    4163           4 :     rv = mURI->SchemeIs("https", &isHttps);
    4164           4 :     NS_ENSURE_SUCCESS(rv,rv);
    4165             : 
    4166           4 :     bool doValidation = false;
    4167           4 :     bool canAddImsHeader = true;
    4168             : 
    4169           4 :     bool isForcedValid = false;
    4170           4 :     entry->GetIsForcedValid(&isForcedValid);
    4171             : 
    4172           8 :     nsXPIDLCString framedBuf;
    4173           4 :     rv = entry->GetMetaDataElement("strongly-framed", getter_Copies(framedBuf));
    4174             :     // describe this in terms of explicitly weakly framed so as to be backwards
    4175             :     // compatible with old cache contents which dont have strongly-framed makers
    4176           4 :     bool weaklyFramed = NS_SUCCEEDED(rv) && framedBuf.EqualsLiteral("0");
    4177           4 :     bool isImmutable = !weaklyFramed && isHttps && mCachedResponseHead->Immutable();
    4178             : 
    4179             :     // Cached entry is not the entity we request (see bug #633743)
    4180           4 :     if (ResponseWouldVary(entry)) {
    4181           0 :         LOG(("Validating based on Vary headers returning TRUE\n"));
    4182           0 :         canAddImsHeader = false;
    4183           0 :         doValidation = true;
    4184             :     }
    4185             :     // Check isForcedValid to see if it is possible to skip validation.
    4186             :     // Don't skip validation if we have serious reason to believe that this
    4187             :     // content is invalid (it's expired).
    4188             :     // See netwerk/cache2/nsICacheEntry.idl for details
    4189           4 :     else if (isForcedValid &&
    4190           0 :              (!mCachedResponseHead->ExpiresInPast() ||
    4191           0 :               !mCachedResponseHead->MustValidateIfExpired())) {
    4192           0 :         LOG(("NOT validating based on isForcedValid being true.\n"));
    4193           0 :         Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PREFETCHES_USED> used;
    4194           0 :         ++used;
    4195           0 :         doValidation = false;
    4196             :     }
    4197             :     // If the LOAD_FROM_CACHE flag is set, any cached data can simply be used
    4198           4 :     else if (mLoadFlags & nsIRequest::LOAD_FROM_CACHE || mAllowStaleCacheContent) {
    4199           2 :         LOG(("NOT validating based on LOAD_FROM_CACHE load flag\n"));
    4200           2 :         doValidation = false;
    4201             :     }
    4202             :     // If the VALIDATE_ALWAYS flag is set, any cached data won't be used until
    4203             :     // it's revalidated with the server.
    4204           2 :     else if ((mLoadFlags & nsIRequest::VALIDATE_ALWAYS) && !isImmutable) {
    4205           0 :         LOG(("Validating based on VALIDATE_ALWAYS load flag\n"));
    4206           0 :         doValidation = true;
    4207             :     }
    4208             :     // Even if the VALIDATE_NEVER flag is set, there are still some cases in
    4209             :     // which we must validate the cached response with the server.
    4210           2 :     else if (mLoadFlags & nsIRequest::VALIDATE_NEVER) {
    4211           0 :         LOG(("VALIDATE_NEVER set\n"));
    4212             :         // if no-store validate cached response (see bug 112564)
    4213           0 :         if (mCachedResponseHead->NoStore()) {
    4214           0 :             LOG(("Validating based on no-store logic\n"));
    4215           0 :             doValidation = true;
    4216             :         }
    4217             :         else {
    4218           0 :             LOG(("NOT validating based on VALIDATE_NEVER load flag\n"));
    4219           0 :             doValidation = false;
    4220             :         }
    4221             :     }
    4222             :     // check if validation is strictly required...
    4223           2 :     else if (mCachedResponseHead->MustValidate()) {
    4224           0 :         LOG(("Validating based on MustValidate() returning TRUE\n"));
    4225           0 :         doValidation = true;
    4226             :     // possibly serve from cache for a custom If-Match/If-Unmodified-Since
    4227             :     // conditional request
    4228           2 :     } else if (mCustomConditionalRequest &&
    4229           2 :                !mRequestHead.HasHeader(nsHttp::If_Match) &&
    4230           0 :                !mRequestHead.HasHeader(nsHttp::If_Unmodified_Since)) {
    4231           0 :         LOG(("Validating based on a custom conditional request\n"));
    4232           0 :         doValidation = true;
    4233             :     } else {
    4234             :         // previously we also checked for a query-url w/out expiration
    4235             :         // and didn't do heuristic on it. but defacto that is allowed now.
    4236             :         //
    4237             :         // Check if the cache entry has expired...
    4238             : 
    4239           2 :         uint32_t now = NowInSeconds();
    4240             : 
    4241           2 :         uint32_t age = 0;
    4242           2 :         rv = mCachedResponseHead->ComputeCurrentAge(now, now, &age);
    4243           2 :         NS_ENSURE_SUCCESS(rv, rv);
    4244             : 
    4245           2 :         uint32_t freshness = 0;
    4246           2 :         rv = mCachedResponseHead->ComputeFreshnessLifetime(&freshness);
    4247           2 :         NS_ENSURE_SUCCESS(rv, rv);
    4248             : 
    4249           2 :         uint32_t expiration = 0;
    4250           2 :         rv = entry->GetExpirationTime(&expiration);
    4251           2 :         NS_ENSURE_SUCCESS(rv, rv);
    4252             : 
    4253             :         uint32_t maxAgeRequest, maxStaleRequest, minFreshRequest;
    4254             : 
    4255           2 :         LOG(("  NowInSeconds()=%u, expiration time=%u, freshness lifetime=%u, age=%u",
    4256             :              now, expiration, freshness, age));
    4257             : 
    4258           2 :         if (cacheControlRequest.NoCache()) {
    4259           0 :             LOG(("  validating, no-cache request"));
    4260           0 :             doValidation = true;
    4261           2 :         } else if (cacheControlRequest.MaxStale(&maxStaleRequest)) {
    4262           0 :             uint32_t staleTime = age > freshness ? age - freshness : 0;
    4263           0 :             doValidation = staleTime > maxStaleRequest;
    4264           0 :             LOG(("  validating=%d, max-stale=%u requested", doValidation, maxStaleRequest));
    4265           2 :         } else if (cacheControlRequest.MaxAge(&maxAgeRequest)) {
    4266           0 :             doValidation = age > maxAgeRequest;
    4267           0 :             LOG(("  validating=%d, max-age=%u requested", doValidation, maxAgeRequest));
    4268           2 :         } else if (cacheControlRequest.MinFresh(&minFreshRequest)) {
    4269           0 :             uint32_t freshTime = freshness > age ? freshness - age : 0;
    4270           0 :             doValidation = freshTime < minFreshRequest;
    4271           0 :             LOG(("  validating=%d, min-fresh=%u requested", doValidation, minFreshRequest));
    4272           2 :         } else if (now <= expiration) {
    4273           2 :             doValidation = false;
    4274           2 :             LOG(("  not validating, expire time not in the past"));
    4275           0 :         } else if (mCachedResponseHead->MustValidateIfExpired()) {
    4276           0 :             doValidation = true;
    4277           0 :         } else if (mLoadFlags & nsIRequest::VALIDATE_ONCE_PER_SESSION) {
    4278             :             // If the cached response does not include expiration infor-
    4279             :             // mation, then we must validate the response, despite whether
    4280             :             // or not this is the first access this session.  This behavior
    4281             :             // is consistent with existing browsers and is generally expected
    4282             :             // by web authors.
    4283           0 :             if (freshness == 0)
    4284           0 :                 doValidation = true;
    4285             :             else
    4286           0 :                 doValidation = fromPreviousSession;
    4287             :         }
    4288             :         else
    4289           0 :             doValidation = true;
    4290             : 
    4291           2 :         LOG(("%salidating based on expiration time\n", doValidation ? "V" : "Not v"));
    4292             :     }
    4293             : 
    4294             : 
    4295             :     // If a content signature is expected to be valid in this load,
    4296             :     // set doValidation to force a signature check.
    4297          12 :     if (!doValidation &&
    4298           8 :         mLoadInfo && mLoadInfo->GetVerifySignedContent()) {
    4299           0 :         doValidation = true;
    4300             :     }
    4301             : 
    4302           8 :     nsAutoCString requestedETag;
    4303          12 :     if (!doValidation &&
    4304           4 :         NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::If_Match, requestedETag)) &&
    4305           0 :         (methodWasGet || methodWasHead)) {
    4306           0 :         nsAutoCString cachedETag;
    4307           0 :         Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, cachedETag);
    4308           0 :         if (!cachedETag.IsEmpty() &&
    4309           0 :             (StringBeginsWith(cachedETag, NS_LITERAL_CSTRING("W/")) ||
    4310           0 :              !requestedETag.Equals(cachedETag))) {
    4311             :             // User has defined If-Match header, if the cached entry is not
    4312             :             // matching the provided header value or the cached ETag is weak,
    4313             :             // force validation.
    4314           0 :             doValidation = true;
    4315             :         }
    4316             :     }
    4317             : 
    4318             :     // Previous error should not be propagated.
    4319           4 :     rv = NS_OK;
    4320             : 
    4321           4 :     if (!doValidation) {
    4322             :         //
    4323             :         // Check the authorization headers used to generate the cache entry.
    4324             :         // We must validate the cache entry if:
    4325             :         //
    4326             :         // 1) the cache entry was generated prior to this session w/
    4327             :         //    credentials (see bug 103402).
    4328             :         // 2) the cache entry was generated w/o credentials, but would now
    4329             :         //    require credentials (see bug 96705).
    4330             :         //
    4331             :         // NOTE: this does not apply to proxy authentication.
    4332             :         //
    4333           4 :         entry->GetMetaDataElement("auth", getter_Copies(buf));
    4334           4 :         doValidation =
    4335           8 :             (fromPreviousSession && !buf.IsEmpty()) ||
    4336           8 :             (buf.IsEmpty() && mRequestHead.HasHeader(nsHttp::Authorization));
    4337             :     }
    4338             : 
    4339             :     // Bug #561276: We maintain a chain of cache-keys which returns cached
    4340             :     // 3xx-responses (redirects) in order to detect cycles. If a cycle is
    4341             :     // found, ignore the cached response and hit the net. Otherwise, use
    4342             :     // the cached response and add the cache-key to the chain. Note that
    4343             :     // a limited number of redirects (cached or not) is allowed and is
    4344             :     // enforced independently of this mechanism
    4345           4 :     if (!doValidation && isCachedRedirect) {
    4346           0 :         nsAutoCString cacheKey;
    4347           0 :         rv = GenerateCacheKey(mPostID, cacheKey);
    4348           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
    4349             : 
    4350           0 :         if (!mRedirectedCachekeys)
    4351           0 :             mRedirectedCachekeys = new nsTArray<nsCString>();
    4352           0 :         else if (mRedirectedCachekeys->Contains(cacheKey))
    4353           0 :             doValidation = true;
    4354             : 
    4355           0 :         LOG(("Redirection-chain %s key %s\n",
    4356             :              doValidation ? "contains" : "does not contain", cacheKey.get()));
    4357             : 
    4358             :         // Append cacheKey if not in the chain already
    4359           0 :         if (!doValidation)
    4360           0 :             mRedirectedCachekeys->AppendElement(cacheKey);
    4361             :     }
    4362             : 
    4363           4 :     if (doValidation && mInterceptCache == INTERCEPTED) {
    4364           0 :         doValidation = false;
    4365             :     }
    4366             : 
    4367           4 :     mCachedContentIsValid = !doValidation;
    4368             : 
    4369           4 :     if (doValidation) {
    4370             :         //
    4371             :         // now, we are definitely going to issue a HTTP request to the server.
    4372             :         // make it conditional if possible.
    4373             :         //
    4374             :         // do not attempt to validate no-store content, since servers will not
    4375             :         // expect it to be cached.  (we only keep it in our cache for the
    4376             :         // purposes of back/forward, etc.)
    4377             :         //
    4378             :         // the request method MUST be either GET or HEAD (see bug 175641) and
    4379             :         // the cached response code must be < 400
    4380             :         //
    4381             :         // the cached content must not be weakly framed or marked immutable
    4382             :         //
    4383             :         // do not override conditional headers when consumer has defined its own
    4384           0 :         if (!mCachedResponseHead->NoStore() &&
    4385           0 :             (mRequestHead.IsGet() || mRequestHead.IsHead()) &&
    4386           0 :             !mCustomConditionalRequest && !weaklyFramed && !isImmutable &&
    4387           0 :             (mCachedResponseHead->Status() < 400)) {
    4388             : 
    4389           0 :             if (mConcurrentCacheAccess) {
    4390             :                 // In case of concurrent read and also validation request we
    4391             :                 // must wait for the current writer to close the output stream
    4392             :                 // first.  Otherwise, when the writer's job would have been interrupted
    4393             :                 // before all the data were downloaded, we'd have to do a range request
    4394             :                 // which would be a second request in line during this channel's
    4395             :                 // life-time.  nsHttpChannel is not designed to do that, so rather
    4396             :                 // turn off concurrent read and wait for entry's completion.
    4397             :                 // Then only re-validation or range-re-validation request will go out.
    4398           0 :                 mConcurrentCacheAccess = 0;
    4399             :                 // This will cause that OnCacheEntryCheck is called again with the same
    4400             :                 // entry after the writer is done.
    4401           0 :                 wantCompleteEntry = true;
    4402             :             } else {
    4403           0 :                 nsAutoCString val;
    4404             :                 // Add If-Modified-Since header if a Last-Modified was given
    4405             :                 // and we are allowed to do this (see bugs 510359 and 269303)
    4406           0 :                 if (canAddImsHeader) {
    4407           0 :                     Unused << mCachedResponseHead->GetHeader(nsHttp::Last_Modified, val);
    4408           0 :                     if (!val.IsEmpty()) {
    4409           0 :                         rv = mRequestHead.SetHeader(nsHttp::If_Modified_Since, val);
    4410           0 :                         MOZ_ASSERT(NS_SUCCEEDED(rv));
    4411             :                     }
    4412             :                 }
    4413             :                 // Add If-None-Match header if an ETag was given in the response
    4414           0 :                 Unused << mCachedResponseHead->GetHeader(nsHttp::ETag, val);
    4415           0 :                 if (!val.IsEmpty()) {
    4416           0 :                     rv = mRequestHead.SetHeader(nsHttp::If_None_Match, val);
    4417           0 :                     MOZ_ASSERT(NS_SUCCEEDED(rv));
    4418             :                 }
    4419           0 :                 mDidReval = true;
    4420             :             }
    4421             :         }
    4422             :     }
    4423             : 
    4424           4 :     if (mCachedContentIsValid || mDidReval) {
    4425           4 :         rv = OpenCacheInputStream(entry, mCachedContentIsValid, !!appCache);
    4426           4 :         if (NS_FAILED(rv)) {
    4427             :             // If we can't get the entity then we have to act as though we
    4428             :             // don't have the cache entry.
    4429           0 :             if (mDidReval) {
    4430           0 :                 UntieValidationRequest();
    4431           0 :                 mDidReval = false;
    4432             :             }
    4433           0 :             mCachedContentIsValid = false;
    4434             :         }
    4435             :     }
    4436             : 
    4437           4 :     if (mDidReval)
    4438           0 :         *aResult = ENTRY_NEEDS_REVALIDATION;
    4439           4 :     else if (wantCompleteEntry)
    4440           1 :         *aResult = RECHECK_AFTER_WRITE_FINISHED;
    4441             :     else {
    4442           3 :         *aResult = ENTRY_WANTED;
    4443             :     }
    4444             : 
    4445           4 :     if (mCachedContentIsValid) {
    4446           4 :         entry->MaybeMarkValid();
    4447             :     }
    4448             : 
    4449           4 :     LOG(("nsHTTPChannel::OnCacheEntryCheck exit [this=%p doValidation=%d result=%d]\n",
    4450             :          this, doValidation, *aResult));
    4451           4 :     return rv;
    4452             : }
    4453             : 
    4454             : NS_IMETHODIMP
    4455           5 : nsHttpChannel::OnCacheEntryAvailable(nsICacheEntry *entry,
    4456             :                                      bool aNew,
    4457             :                                      nsIApplicationCache* aAppCache,
    4458             :                                      nsresult status)
    4459             : {
    4460           5 :     MOZ_ASSERT(NS_IsMainThread());
    4461           5 :     mOnCacheAvailableCalled = true;
    4462             : 
    4463             :     nsresult rv;
    4464             : 
    4465           5 :     LOG(("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p "
    4466             :          "new=%d appcache=%p status=%" PRIx32 " mAppCache=%p mAppCacheForWrite=%p]\n",
    4467             :          this, entry, aNew, aAppCache, static_cast<uint32_t>(status),
    4468             :          mApplicationCache.get(), mApplicationCacheForWrite.get()));
    4469             : 
    4470             :     // if the channel's already fired onStopRequest, then we should ignore
    4471             :     // this event.
    4472           5 :     if (!mIsPending) {
    4473           0 :         mCacheInputStream.CloseAndRelease();
    4474           0 :         return NS_OK;
    4475             :     }
    4476             : 
    4477           5 :     rv = OnCacheEntryAvailableInternal(entry, aNew, aAppCache, status);
    4478           5 :     if (NS_FAILED(rv)) {
    4479           0 :         CloseCacheEntry(false);
    4480           0 :         Unused << AsyncAbort(rv);
    4481             :     }
    4482             : 
    4483           5 :     return NS_OK;
    4484             : }
    4485             : 
    4486             : nsresult
    4487           5 : nsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntry *entry,
    4488             :                                              bool aNew,
    4489             :                                              nsIApplicationCache* aAppCache,
    4490             :                                              nsresult status)
    4491             : {
    4492             :     nsresult rv;
    4493             : 
    4494           5 :     if (mCanceled) {
    4495           0 :         LOG(("channel was canceled [this=%p status=%" PRIx32 "]\n",
    4496             :              this, static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
    4497           0 :         return mStatus;
    4498             :     }
    4499             : 
    4500           5 :     if (aAppCache) {
    4501           0 :         if (mApplicationCache == aAppCache && !mCacheEntry) {
    4502           0 :             rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status);
    4503             :         }
    4504           0 :         else if (mApplicationCacheForWrite == aAppCache && aNew && !mOfflineCacheEntry) {
    4505           0 :             rv = OnOfflineCacheEntryForWritingAvailable(entry, aAppCache, status);
    4506             :         }
    4507             :         else {
    4508           0 :             rv = OnOfflineCacheEntryAvailable(entry, aNew, aAppCache, status);
    4509             :         }
    4510             :     }
    4511             :     else {
    4512           5 :         rv = OnNormalCacheEntryAvailable(entry, aNew, status);
    4513             :     }
    4514             : 
    4515           5 :     if (NS_FAILED(rv) && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
    4516             :         // If we have a fallback URI (and we're not already
    4517             :         // falling back), process the fallback asynchronously.
    4518           0 :         if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
    4519           0 :             return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
    4520             :         }
    4521             : 
    4522           0 :         return NS_ERROR_DOCUMENT_NOT_CACHED;
    4523             :     }
    4524             : 
    4525           5 :     if (NS_FAILED(rv)) {
    4526           0 :         return rv;
    4527             :     }
    4528             : 
    4529             :     // We may be waiting for more callbacks...
    4530           5 :     if (AwaitingCacheCallbacks()) {
    4531           1 :         return NS_OK;
    4532             :     }
    4533             : 
    4534           4 :     if (mCachedContentIsValid && mNetworkTriggered) {
    4535           0 :         Unused << ReadFromCache(true);
    4536             :     }
    4537             : 
    4538           4 :     return TriggerNetwork(0);
    4539             : }
    4540             : 
    4541             : nsresult
    4542           5 : nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntry *aEntry,
    4543             :                                            bool aNew,
    4544             :                                            nsresult aEntryStatus)
    4545             : {
    4546           5 :     mCacheEntriesToWaitFor &= ~WAIT_FOR_CACHE_ENTRY;
    4547             : 
    4548           5 :     if (NS_FAILED(aEntryStatus) || aNew) {
    4549             :         // Make sure this flag is dropped.  It may happen the entry is doomed
    4550             :         // between OnCacheEntryCheck and OnCacheEntryAvailable.
    4551           2 :         mCachedContentIsValid = false;
    4552             : 
    4553             :         // From the same reason remove any conditional headers added
    4554             :         // in OnCacheEntryCheck.
    4555           2 :         if (mDidReval) {
    4556           0 :             LOG(("  Removing conditional request headers"));
    4557           0 :             UntieValidationRequest();
    4558           0 :             mDidReval = false;
    4559             :         }
    4560             : 
    4561           2 :         if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
    4562             :             // if this channel is only allowed to pull from the cache, then
    4563             :             // we must fail if we were unable to open a cache entry for read.
    4564           0 :             return NS_ERROR_DOCUMENT_NOT_CACHED;
    4565             :         }
    4566             :     }
    4567             : 
    4568           5 :     if (NS_SUCCEEDED(aEntryStatus)) {
    4569           5 :         mCacheEntry = aEntry;
    4570           5 :         mCacheEntryIsWriteOnly = aNew;
    4571             : 
    4572           5 :         if (!aNew && !mAsyncOpenTime.IsNull()) {
    4573             :             // We use microseconds for IO operations. For consistency let's use
    4574             :             // microseconds here too.
    4575           3 :             uint32_t duration = (TimeStamp::Now() - mAsyncOpenTime).ToMicroseconds();
    4576           3 :             bool isSlow = false;
    4577           6 :             if ((mCacheOpenWithPriority && mCacheQueueSizeWhenOpen >= sRCWNQueueSizePriority) ||
    4578           4 :                 (!mCacheOpenWithPriority && mCacheQueueSizeWhenOpen >= sRCWNQueueSizeNormal)) {
    4579           0 :                 isSlow = true;
    4580             :             }
    4581           3 :             CacheFileUtils::CachePerfStats::AddValue(
    4582           3 :                 CacheFileUtils::CachePerfStats::ENTRY_OPEN, duration, isSlow);
    4583             :         }
    4584             : 
    4585           5 :         if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
    4586             :             Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,
    4587           1 :                                   false);
    4588             :         }
    4589             :     }
    4590             : 
    4591           5 :     return NS_OK;
    4592             : }
    4593             : 
    4594             : nsresult
    4595           0 : nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntry *aEntry,
    4596             :                                             bool aNew,
    4597             :                                             nsIApplicationCache* aAppCache,
    4598             :                                             nsresult aEntryStatus)
    4599             : {
    4600           0 :     MOZ_ASSERT(!mApplicationCache || aAppCache == mApplicationCache);
    4601           0 :     MOZ_ASSERT(!aNew || !aEntry || mApplicationCacheForWrite);
    4602             : 
    4603           0 :     mCacheEntriesToWaitFor &= ~WAIT_FOR_CACHE_ENTRY;
    4604             : 
    4605             :     nsresult rv;
    4606             : 
    4607           0 :     if (NS_SUCCEEDED(aEntryStatus)) {
    4608           0 :         if (!mApplicationCache) {
    4609           0 :             mApplicationCache = aAppCache;
    4610             :         }
    4611             : 
    4612             :         // We successfully opened an offline cache session and the entry,
    4613             :         // so indicate we will load from the offline cache.
    4614           0 :         mLoadedFromApplicationCache = true;
    4615           0 :         mCacheEntryIsReadOnly = true;
    4616           0 :         mCacheEntry = aEntry;
    4617           0 :         mCacheEntryIsWriteOnly = false;
    4618             : 
    4619           0 :         if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI && !mApplicationCacheForWrite) {
    4620           0 :             MaybeWarnAboutAppCache();
    4621             :         }
    4622             : 
    4623           0 :         return NS_OK;
    4624             :     }
    4625             : 
    4626           0 :     if (!mApplicationCacheForWrite && !mFallbackChannel) {
    4627           0 :         if (!mApplicationCache) {
    4628           0 :             mApplicationCache = aAppCache;
    4629             :         }
    4630             : 
    4631             :         // Check for namespace match.
    4632           0 :         nsCOMPtr<nsIApplicationCacheNamespace> namespaceEntry;
    4633           0 :         rv = mApplicationCache->GetMatchingNamespace(mSpec,
    4634           0 :             getter_AddRefs(namespaceEntry));
    4635           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4636             : 
    4637           0 :         uint32_t namespaceType = 0;
    4638           0 :         if (!namespaceEntry ||
    4639           0 :             NS_FAILED(namespaceEntry->GetItemType(&namespaceType)) ||
    4640           0 :             (namespaceType &
    4641             :              (nsIApplicationCacheNamespace::NAMESPACE_FALLBACK |
    4642             :               nsIApplicationCacheNamespace::NAMESPACE_BYPASS)) == 0) {
    4643             :             // When loading from an application cache, only items
    4644             :             // on the whitelist or matching a
    4645             :             // fallback namespace should hit the network...
    4646           0 :             mLoadFlags |= LOAD_ONLY_FROM_CACHE;
    4647             : 
    4648             :             // ... and if there were an application cache entry,
    4649             :             // we would have found it earlier.
    4650           0 :             return NS_ERROR_CACHE_KEY_NOT_FOUND;
    4651             :         }
    4652             : 
    4653           0 :         if (namespaceType &
    4654             :             nsIApplicationCacheNamespace::NAMESPACE_FALLBACK) {
    4655             : 
    4656           0 :             nsAutoCString namespaceSpec;
    4657           0 :             rv = namespaceEntry->GetNamespaceSpec(namespaceSpec);
    4658           0 :             NS_ENSURE_SUCCESS(rv, rv);
    4659             : 
    4660             :             // This prevents fallback attacks injected by an insecure subdirectory
    4661             :             // for the whole origin (or a parent directory).
    4662           0 :             if (!IsInSubpathOfAppCacheManifest(mApplicationCache, namespaceSpec)) {
    4663           0 :                 return NS_OK;
    4664             :             }
    4665             : 
    4666           0 :             rv = namespaceEntry->GetData(mFallbackKey);
    4667           0 :             NS_ENSURE_SUCCESS(rv, rv);
    4668             :         }
    4669             :     }
    4670             : 
    4671           0 :     return NS_OK;
    4672             : }
    4673             : 
    4674             : nsresult
    4675           0 : nsHttpChannel::OnOfflineCacheEntryForWritingAvailable(nsICacheEntry *aEntry,
    4676             :                                                       nsIApplicationCache* aAppCache,
    4677             :                                                       nsresult aEntryStatus)
    4678             : {
    4679           0 :     MOZ_ASSERT(mApplicationCacheForWrite && aAppCache == mApplicationCacheForWrite);
    4680             : 
    4681           0 :     mCacheEntriesToWaitFor &= ~WAIT_FOR_OFFLINE_CACHE_ENTRY;
    4682             : 
    4683           0 :     if (NS_SUCCEEDED(aEntryStatus)) {
    4684           0 :         mOfflineCacheEntry = aEntry;
    4685           0 :         if (NS_FAILED(aEntry->GetLastModified(&mOfflineCacheLastModifiedTime))) {
    4686           0 :             mOfflineCacheLastModifiedTime = 0;
    4687             :         }
    4688             :     }
    4689             : 
    4690           0 :     return aEntryStatus;
    4691             : }
    4692             : 
    4693             : // Generates the proper cache-key for this instance of nsHttpChannel
    4694             : nsresult
    4695           0 : nsHttpChannel::GenerateCacheKey(uint32_t postID, nsACString &cacheKey)
    4696             : {
    4697           0 :     AssembleCacheKey(mFallbackChannel ? mFallbackKey.get() : mSpec.get(),
    4698           0 :                      postID, cacheKey);
    4699           0 :     return NS_OK;
    4700             : }
    4701             : 
    4702             : // Assembles a cache-key from the given pieces of information and |mLoadFlags|
    4703             : void
    4704           0 : nsHttpChannel::AssembleCacheKey(const char *spec, uint32_t postID,
    4705             :                                 nsACString &cacheKey)
    4706             : {
    4707           0 :     cacheKey.Truncate();
    4708             : 
    4709           0 :     if (mLoadFlags & LOAD_ANONYMOUS) {
    4710           0 :         cacheKey.AssignLiteral("anon&");
    4711             :     }
    4712             : 
    4713           0 :     if (postID) {
    4714             :         char buf[32];
    4715           0 :         SprintfLiteral(buf, "id=%x&", postID);
    4716           0 :         cacheKey.Append(buf);
    4717             :     }
    4718             : 
    4719           0 :     if (!cacheKey.IsEmpty()) {
    4720           0 :         cacheKey.AppendLiteral("uri=");
    4721             :     }
    4722             : 
    4723             :     // Strip any trailing #ref from the URL before using it as the key
    4724           0 :     const char *p = strchr(spec, '#');
    4725           0 :     if (p)
    4726           0 :         cacheKey.Append(spec, p - spec);
    4727             :     else
    4728           0 :         cacheKey.Append(spec);
    4729           0 : }
    4730             : 
    4731             : nsresult
    4732           2 : DoUpdateExpirationTime(nsHttpChannel* aSelf,
    4733             :                        nsICacheEntry* aCacheEntry,
    4734             :                        nsHttpResponseHead* aResponseHead,
    4735             :                        uint32_t& aExpirationTime)
    4736             : {
    4737           2 :     MOZ_ASSERT(aExpirationTime == 0);
    4738           2 :     NS_ENSURE_TRUE(aResponseHead, NS_ERROR_FAILURE);
    4739             : 
    4740             :     nsresult rv;
    4741             : 
    4742           2 :     if (!aResponseHead->MustValidate()) {
    4743           1 :         uint32_t freshnessLifetime = 0;
    4744             : 
    4745           1 :         rv = aResponseHead->ComputeFreshnessLifetime(&freshnessLifetime);
    4746           1 :         if (NS_FAILED(rv)) return rv;
    4747             : 
    4748           1 :         if (freshnessLifetime > 0) {
    4749           1 :             uint32_t now = NowInSeconds(), currentAge = 0;
    4750             : 
    4751           1 :             rv = aResponseHead->ComputeCurrentAge(now, aSelf->GetRequestTime(), &currentAge);
    4752           1 :             if (NS_FAILED(rv)) return rv;
    4753             : 
    4754           1 :             LOG(("freshnessLifetime = %u, currentAge = %u\n",
    4755             :                 freshnessLifetime, currentAge));
    4756             : 
    4757           1 :             if (freshnessLifetime > currentAge) {
    4758           1 :                 uint32_t timeRemaining = freshnessLifetime - currentAge;
    4759             :                 // be careful... now + timeRemaining may overflow
    4760           1 :                 if (now + timeRemaining < now)
    4761           0 :                     aExpirationTime = uint32_t(-1);
    4762             :                 else
    4763           1 :                     aExpirationTime = now + timeRemaining;
    4764             :             }
    4765             :             else
    4766           0 :                 aExpirationTime = 0;
    4767             :         }
    4768             :     }
    4769             : 
    4770           2 :     rv = aCacheEntry->SetExpirationTime(aExpirationTime);
    4771           2 :     NS_ENSURE_SUCCESS(rv, rv);
    4772             : 
    4773           2 :     return rv;
    4774             : }
    4775             : 
    4776             : // UpdateExpirationTime is called when a new response comes in from the server.
    4777             : // It updates the stored response-time and sets the expiration time on the
    4778             : // cache entry.
    4779             : //
    4780             : // From section 13.2.4 of RFC2616, we compute expiration time as follows:
    4781             : //
    4782             : //    timeRemaining = freshnessLifetime - currentAge
    4783             : //    expirationTime = now + timeRemaining
    4784             : //
    4785             : nsresult
    4786           2 : nsHttpChannel::UpdateExpirationTime()
    4787             : {
    4788           2 :     uint32_t expirationTime = 0;
    4789           2 :     nsresult rv = DoUpdateExpirationTime(this, mCacheEntry, mResponseHead, expirationTime);
    4790           2 :     NS_ENSURE_SUCCESS(rv, rv);
    4791             : 
    4792           2 :     if (mOfflineCacheEntry) {
    4793           0 :         rv = mOfflineCacheEntry->SetExpirationTime(expirationTime);
    4794           0 :         NS_ENSURE_SUCCESS(rv, rv);
    4795             :     }
    4796             : 
    4797           2 :     return NS_OK;
    4798             : }
    4799             : 
    4800             : /*static*/ inline bool
    4801           6 : nsHttpChannel::HasQueryString(nsHttpRequestHead::ParsedMethodType method, nsIURI * uri)
    4802             : {
    4803             :     // Must be called on the main thread because nsIURI does not implement
    4804             :     // thread-safe QueryInterface.
    4805           6 :     MOZ_ASSERT(NS_IsMainThread());
    4806             : 
    4807           6 :     if (method != nsHttpRequestHead::kMethod_Get &&
    4808             :         method != nsHttpRequestHead::kMethod_Head)
    4809           1 :         return false;
    4810             : 
    4811          10 :     nsAutoCString query;
    4812          10 :     nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
    4813           5 :     nsresult rv = url->GetQuery(query);
    4814           5 :     return NS_SUCCEEDED(rv) && !query.IsEmpty();
    4815             : }
    4816             : 
    4817             : bool
    4818           5 : nsHttpChannel::ShouldUpdateOfflineCacheEntry()
    4819             : {
    4820           5 :     if (!mApplicationCacheForWrite || !mOfflineCacheEntry) {
    4821           5 :         return false;
    4822             :     }
    4823             : 
    4824             :     // if we're updating the cache entry, update the offline cache entry too
    4825           0 :     if (mCacheEntry && mCacheEntryIsWriteOnly) {
    4826           0 :         return true;
    4827             :     }
    4828             : 
    4829             :     // if there's nothing in the offline cache, add it
    4830           0 :     if (mOfflineCacheEntry) {
    4831           0 :         return true;
    4832             :     }
    4833             : 
    4834             :     // if the document is newer than the offline entry, update it
    4835             :     uint32_t docLastModifiedTime;
    4836           0 :     nsresult rv = mResponseHead->GetLastModifiedValue(&docLastModifiedTime);
    4837           0 :     if (NS_FAILED(rv)) {
    4838           0 :         return true;
    4839             :     }
    4840             : 
    4841           0 :     if (mOfflineCacheLastModifiedTime == 0) {
    4842           0 :         return false;
    4843             :     }
    4844             : 
    4845           0 :     if (docLastModifiedTime > mOfflineCacheLastModifiedTime) {
    4846           0 :         return true;
    4847             :     }
    4848             : 
    4849           0 :     return false;
    4850             : }
    4851             : 
    4852             : nsresult
    4853           4 : nsHttpChannel::OpenCacheInputStream(nsICacheEntry* cacheEntry, bool startBuffering,
    4854             :                                     bool checkingAppCacheEntry)
    4855             : {
    4856             :     nsresult rv;
    4857             : 
    4858           4 :     bool isHttps = false;
    4859           4 :     rv = mURI->SchemeIs("https", &isHttps);
    4860           4 :     NS_ENSURE_SUCCESS(rv,rv);
    4861             : 
    4862           4 :     if (isHttps) {
    4863           0 :         rv = cacheEntry->GetSecurityInfo(
    4864           0 :                                       getter_AddRefs(mCachedSecurityInfo));
    4865           0 :         if (NS_FAILED(rv)) {
    4866           0 :             LOG(("failed to parse security-info [channel=%p, entry=%p]",
    4867             :                  this, cacheEntry));
    4868           0 :             NS_WARNING("failed to parse security-info");
    4869           0 :             cacheEntry->AsyncDoom(nullptr);
    4870           0 :             return rv;
    4871             :         }
    4872             : 
    4873             :         // XXX: We should not be skilling this check in the offline cache
    4874             :         // case, but we have to do so now to work around bug 794507.
    4875           0 :         bool mustHaveSecurityInfo = !mLoadedFromApplicationCache && !checkingAppCacheEntry;
    4876           0 :         MOZ_ASSERT(mCachedSecurityInfo || !mustHaveSecurityInfo);
    4877           0 :         if (!mCachedSecurityInfo && mustHaveSecurityInfo) {
    4878           0 :             LOG(("mCacheEntry->GetSecurityInfo returned success but did not "
    4879             :                  "return the security info [channel=%p, entry=%p]",
    4880             :                  this, cacheEntry));
    4881           0 :             cacheEntry->AsyncDoom(nullptr);
    4882           0 :             return NS_ERROR_UNEXPECTED; // XXX error code
    4883             :         }
    4884             :     }
    4885             : 
    4886             :     // Keep the conditions below in sync with the conditions in ReadFromCache.
    4887             : 
    4888           4 :     rv = NS_OK;
    4889             : 
    4890           4 :     if (WillRedirect(mCachedResponseHead)) {
    4891             :         // Do not even try to read the entity for a redirect because we do not
    4892             :         // return an entity to the application when we process redirects.
    4893           0 :         LOG(("Will skip read of cached redirect entity\n"));
    4894           0 :         return NS_OK;
    4895             :     }
    4896             : 
    4897           8 :     if ((mLoadFlags & nsICachingChannel::LOAD_ONLY_IF_MODIFIED) &&
    4898           4 :         !mCachedContentIsPartial) {
    4899             :         // For LOAD_ONLY_IF_MODIFIED, we usually don't have to deal with the
    4900             :         // cached entity.
    4901           0 :         if (!mApplicationCacheForWrite) {
    4902           0 :             LOG(("Will skip read from cache based on LOAD_ONLY_IF_MODIFIED "
    4903             :                  "load flag\n"));
    4904           0 :             return NS_OK;
    4905             :         }
    4906             : 
    4907             :         // If offline caching has been requested and the offline cache needs
    4908             :         // updating, we must complete the call even if the main cache entry
    4909             :         // is up to date. We don't know yet for sure whether the offline
    4910             :         // cache needs updating because at this point we haven't opened it
    4911             :         // for writing yet, so we have to start reading the cached entity now
    4912             :         // just in case.
    4913           0 :         LOG(("May skip read from cache based on LOAD_ONLY_IF_MODIFIED "
    4914             :               "load flag\n"));
    4915             :     }
    4916             : 
    4917             :     // Open an input stream for the entity, so that the call to OpenInputStream
    4918             :     // happens off the main thread.
    4919           8 :     nsCOMPtr<nsIInputStream> stream;
    4920             : 
    4921             :     // If an alternate representation was requested, try to open the alt
    4922             :     // input stream.
    4923           4 :     if (!mPreferredCachedAltDataType.IsEmpty()) {
    4924           0 :         rv = cacheEntry->OpenAlternativeInputStream(mPreferredCachedAltDataType,
    4925           0 :                                                     getter_AddRefs(stream));
    4926           0 :         if (NS_SUCCEEDED(rv)) {
    4927             :             // We have succeeded.
    4928           0 :             mAvailableCachedAltDataType = mPreferredCachedAltDataType;
    4929             :             // Set the correct data size on the channel.
    4930             :             int64_t altDataSize;
    4931           0 :             if (NS_SUCCEEDED(cacheEntry->GetAltDataSize(&altDataSize))) {
    4932           0 :                 mAltDataLength = altDataSize;
    4933             :             }
    4934             :         }
    4935             :     }
    4936             : 
    4937           4 :     if (!stream) {
    4938           4 :         rv = cacheEntry->OpenInputStream(0, getter_AddRefs(stream));
    4939             :     }
    4940             : 
    4941           4 :     if (NS_FAILED(rv)) {
    4942           0 :         LOG(("Failed to open cache input stream [channel=%p, "
    4943             :              "mCacheEntry=%p]", this, cacheEntry));
    4944           0 :         return rv;
    4945             :     }
    4946             : 
    4947           4 :     if (startBuffering) {
    4948             :         bool nonBlocking;
    4949           4 :         rv = stream->IsNonBlocking(&nonBlocking);
    4950           4 :         if (NS_SUCCEEDED(rv) && nonBlocking)
    4951           4 :             startBuffering = false;
    4952             :     }
    4953             : 
    4954           4 :     if (!startBuffering) {
    4955             :         // Bypass wrapping the input stream for the new cache back-end since
    4956             :         // nsIStreamTransportService expects a blocking stream.  Preloading of
    4957             :         // the data must be done on the level of the cache backend, internally.
    4958             :         //
    4959             :         // We do not connect the stream to the stream transport service if we
    4960             :         // have to validate the entry with the server. If we did, we would get
    4961             :         // into a race condition between the stream transport service reading
    4962             :         // the existing contents and the opening of the cache entry's output
    4963             :         // stream to write the new contents in the case where we get a non-304
    4964             :         // response.
    4965           4 :         LOG(("Opened cache input stream without buffering [channel=%p, "
    4966             :               "mCacheEntry=%p, stream=%p]", this,
    4967             :               cacheEntry, stream.get()));
    4968           4 :         mCacheInputStream.takeOver(stream);
    4969           4 :         return rv;
    4970             :     }
    4971             : 
    4972             :     // Have the stream transport service start reading the entity on one of its
    4973             :     // background threads.
    4974             : 
    4975           0 :     nsCOMPtr<nsITransport> transport;
    4976           0 :     nsCOMPtr<nsIInputStream> wrapper;
    4977             : 
    4978           0 :     nsCOMPtr<nsIStreamTransportService> sts(services::GetStreamTransportService());
    4979           0 :     rv = sts ? NS_OK : NS_ERROR_NOT_AVAILABLE;
    4980           0 :     if (NS_SUCCEEDED(rv)) {
    4981           0 :         rv = sts->CreateInputTransport(stream, int64_t(-1), int64_t(-1),
    4982           0 :                                         true, getter_AddRefs(transport));
    4983             :     }
    4984           0 :     if (NS_SUCCEEDED(rv)) {
    4985           0 :         rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(wrapper));
    4986             :     }
    4987           0 :     if (NS_SUCCEEDED(rv)) {
    4988           0 :         LOG(("Opened cache input stream [channel=%p, wrapper=%p, "
    4989             :               "transport=%p, stream=%p]", this, wrapper.get(),
    4990             :               transport.get(), stream.get()));
    4991             :     } else {
    4992           0 :         LOG(("Failed to open cache input stream [channel=%p, "
    4993             :               "wrapper=%p, transport=%p, stream=%p]", this,
    4994             :               wrapper.get(), transport.get(), stream.get()));
    4995             : 
    4996           0 :         stream->Close();
    4997           0 :         return rv;
    4998             :     }
    4999             : 
    5000           0 :     mCacheInputStream.takeOver(wrapper);
    5001             : 
    5002           0 :     return NS_OK;
    5003             : }
    5004             : 
    5005             : // Actually process the cached response that we started to handle in CheckCache
    5006             : // and/or StartBufferingCachedEntity.
    5007             : nsresult
    5008           3 : nsHttpChannel::ReadFromCache(bool alreadyMarkedValid)
    5009             : {
    5010           3 :     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE);
    5011           3 :     NS_ENSURE_TRUE(mCachedContentIsValid, NS_ERROR_FAILURE);
    5012           3 :     NS_ENSURE_TRUE(!mCachePump, NS_OK); // already opened
    5013             : 
    5014           3 :     LOG(("nsHttpChannel::ReadFromCache [this=%p] "
    5015             :          "Using cached copy of: %s\n", this, mSpec.get()));
    5016             : 
    5017           3 :     if (mRaceCacheWithNetwork) {
    5018           0 :         MOZ_ASSERT(mFirstResponseSource != RESPONSE_FROM_CACHE);
    5019           0 :         if (mFirstResponseSource == RESPONSE_PENDING) {
    5020           0 :             LOG(("First response from cache\n"));
    5021           0 :             mFirstResponseSource = RESPONSE_FROM_CACHE;
    5022             : 
    5023             :             // Cancel the transaction because we will serve the request from the cache
    5024           0 :             CancelNetworkRequest(NS_BINDING_ABORTED);
    5025           0 :             if (mTransactionPump && mSuspendCount) {
    5026           0 :                 uint32_t suspendCount = mSuspendCount;
    5027           0 :                 while (suspendCount--) {
    5028           0 :                     mTransactionPump->Resume();
    5029             :                 }
    5030             :             }
    5031           0 :             mTransaction = nullptr;
    5032           0 :             mTransactionPump = nullptr;
    5033             :         } else {
    5034           0 :             MOZ_ASSERT(mFirstResponseSource == RESPONSE_FROM_NETWORK);
    5035           0 :             LOG(("Skipping read from cache because first response was from network\n"));
    5036             : 
    5037           0 :             if (!mOnCacheEntryCheckTimestamp.IsNull()) {
    5038           0 :                 TimeStamp currentTime = TimeStamp::Now();
    5039           0 :                 int64_t savedTime = (currentTime - mOnStartRequestTimestamp).ToMilliseconds();
    5040           0 :                 Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_SAVED_TIME, savedTime);
    5041             : 
    5042           0 :                 int64_t diffTime = (currentTime - mOnCacheEntryCheckTimestamp).ToMilliseconds();
    5043           0 :                 Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_WITH_NETWORK_OCEC_ON_START_DIFF, diffTime);
    5044             :             }
    5045           0 :             return NS_OK;
    5046             :         }
    5047             :     }
    5048             : 
    5049           3 :     if (mCachedResponseHead)
    5050           3 :         mResponseHead = Move(mCachedResponseHead);
    5051             : 
    5052           3 :     UpdateInhibitPersistentCachingFlag();
    5053             : 
    5054             :     // if we don't already have security info, try to get it from the cache
    5055             :     // entry. there are two cases to consider here: 1) we are just reading
    5056             :     // from the cache, or 2) this may be due to a 304 not modified response,
    5057             :     // in which case we could have security info from a socket transport.
    5058           3 :     if (!mSecurityInfo)
    5059           3 :         mSecurityInfo = mCachedSecurityInfo;
    5060             : 
    5061           3 :     if (!alreadyMarkedValid && !mCachedContentIsPartial) {
    5062             :         // We validated the entry, and we have write access to the cache, so
    5063             :         // mark the cache entry as valid in order to allow others access to
    5064             :         // this cache entry.
    5065             :         //
    5066             :         // TODO: This should be done asynchronously so we don't take the cache
    5067             :         // service lock on the main thread.
    5068           0 :         mCacheEntry->MaybeMarkValid();
    5069             :     }
    5070             : 
    5071             :     nsresult rv;
    5072             : 
    5073             :     // Keep the conditions below in sync with the conditions in
    5074             :     // StartBufferingCachedEntity.
    5075             : 
    5076           3 :     if (WillRedirect(mResponseHead)) {
    5077             :         // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here,
    5078             :         // to avoid event dispatching latency.
    5079           0 :         MOZ_ASSERT(!mCacheInputStream);
    5080           0 :         LOG(("Skipping skip read of cached redirect entity\n"));
    5081           0 :         return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
    5082             :     }
    5083             : 
    5084           3 :     if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !mCachedContentIsPartial) {
    5085           0 :         if (!mApplicationCacheForWrite) {
    5086           0 :             LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
    5087             :                  "load flag\n"));
    5088           0 :             MOZ_ASSERT(!mCacheInputStream);
    5089             :             // TODO: Bug 759040 - We should call HandleAsyncNotModified directly
    5090             :             // here, to avoid event dispatching latency.
    5091           0 :             return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
    5092             :         }
    5093             : 
    5094           0 :         if (!ShouldUpdateOfflineCacheEntry()) {
    5095           0 :             LOG(("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
    5096             :                  "load flag (mApplicationCacheForWrite not null case)\n"));
    5097           0 :             mCacheInputStream.CloseAndRelease();
    5098             :             // TODO: Bug 759040 - We should call HandleAsyncNotModified directly
    5099             :             // here, to avoid event dispatching latency.
    5100           0 :             return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
    5101             :         }
    5102             :     }
    5103             : 
    5104           3 :     MOZ_ASSERT(mCacheInputStream);
    5105           3 :     if (!mCacheInputStream) {
    5106           0 :         NS_ERROR("mCacheInputStream is null but we're expecting to "
    5107             :                         "be able to read from it.");
    5108           0 :         return NS_ERROR_UNEXPECTED;
    5109             :     }
    5110             : 
    5111           6 :     nsCOMPtr<nsIInputStream> inputStream = mCacheInputStream.forget();
    5112             : 
    5113           6 :     rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), inputStream,
    5114           3 :                                    int64_t(-1), int64_t(-1), 0, 0, true);
    5115           3 :     if (NS_FAILED(rv)) {
    5116           0 :         inputStream->Close();
    5117           0 :         return rv;
    5118             :     }
    5119             : 
    5120           3 :     rv = mCachePump->AsyncRead(this, mListenerContext);
    5121           3 :     if (NS_FAILED(rv)) return rv;
    5122             : 
    5123           3 :     if (mTimingEnabled)
    5124           3 :         mCacheReadStart = TimeStamp::Now();
    5125             : 
    5126           3 :     uint32_t suspendCount = mSuspendCount;
    5127           3 :     while (suspendCount--)
    5128           0 :         mCachePump->Suspend();
    5129             : 
    5130           3 :     return NS_OK;
    5131             : }
    5132             : 
    5133             : void
    5134           6 : nsHttpChannel::CloseCacheEntry(bool doomOnFailure)
    5135             : {
    5136           6 :     mCacheInputStream.CloseAndRelease();
    5137             : 
    5138           6 :     if (!mCacheEntry)
    5139           1 :         return;
    5140             : 
    5141           5 :     LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%" PRIx32 " mCacheEntryIsWriteOnly=%x",
    5142             :          this, static_cast<uint32_t>(static_cast<nsresult>(mStatus)), mCacheEntryIsWriteOnly));
    5143             : 
    5144             :     // If we have begun to create or replace a cache entry, and that cache
    5145             :     // entry is not complete and not resumable, then it needs to be doomed.
    5146             :     // Otherwise, CheckCache will make the mistake of thinking that the
    5147             :     // partial cache entry is complete.
    5148             : 
    5149           5 :     bool doom = false;
    5150           5 :     if (mInitedCacheEntry) {
    5151           2 :         MOZ_ASSERT(mResponseHead, "oops");
    5152           4 :         if (NS_FAILED(mStatus) && doomOnFailure &&
    5153           2 :             mCacheEntryIsWriteOnly && !mResponseHead->IsResumable())
    5154           0 :             doom = true;
    5155             :     }
    5156           3 :     else if (mCacheEntryIsWriteOnly)
    5157           0 :         doom = true;
    5158             : 
    5159           5 :     if (doom) {
    5160           0 :         LOG(("  dooming cache entry!!"));
    5161           0 :         mCacheEntry->AsyncDoom(nullptr);
    5162             :     } else {
    5163             :       // Store updated security info, makes cached EV status race less likely
    5164             :       // (see bug 1040086)
    5165           5 :       if (mSecurityInfo)
    5166           0 :           mCacheEntry->SetSecurityInfo(mSecurityInfo);
    5167             :     }
    5168             : 
    5169           5 :     mCachedResponseHead = nullptr;
    5170             : 
    5171           5 :     mCachePump = nullptr;
    5172           5 :     mCacheEntry = nullptr;
    5173           5 :     mCacheEntryIsWriteOnly = false;
    5174           5 :     mInitedCacheEntry = false;
    5175             : }
    5176             : 
    5177             : 
    5178             : void
    5179           0 : nsHttpChannel::CloseOfflineCacheEntry()
    5180             : {
    5181           0 :     if (!mOfflineCacheEntry)
    5182           0 :         return;
    5183             : 
    5184           0 :     LOG(("nsHttpChannel::CloseOfflineCacheEntry [this=%p]", this));
    5185             : 
    5186           0 :     if (NS_FAILED(mStatus)) {
    5187           0 :         mOfflineCacheEntry->AsyncDoom(nullptr);
    5188             :     }
    5189             :     else {
    5190             :         bool succeeded;
    5191           0 :         if (NS_SUCCEEDED(GetRequestSucceeded(&succeeded)) && !succeeded)
    5192           0 :             mOfflineCacheEntry->AsyncDoom(nullptr);
    5193             :     }
    5194             : 
    5195           0 :     mOfflineCacheEntry = nullptr;
    5196             : }
    5197             : 
    5198             : 
    5199             : // Initialize the cache entry for writing.
    5200             : //  - finalize storage policy
    5201             : //  - store security info
    5202             : //  - update expiration time
    5203             : //  - store headers and other meta data
    5204             : nsresult
    5205           2 : nsHttpChannel::InitCacheEntry()
    5206             : {
    5207             :     nsresult rv;
    5208             : 
    5209           2 :     NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_UNEXPECTED);
    5210             :     // if only reading, nothing to be done here.
    5211           2 :     if (mCacheEntryIsReadOnly)
    5212           0 :         return NS_OK;
    5213             : 
    5214             :     // Don't cache the response again if already cached...
    5215           2 :     if (mCachedContentIsValid)
    5216           0 :         return NS_OK;
    5217             : 
    5218           2 :     LOG(("nsHttpChannel::InitCacheEntry [this=%p entry=%p]\n",
    5219             :         this, mCacheEntry.get()));
    5220             : 
    5221           2 :     bool recreate = !mCacheEntryIsWriteOnly;
    5222           2 :     bool dontPersist = mLoadFlags & INHIBIT_PERSISTENT_CACHING;
    5223             : 
    5224           2 :     if (!recreate && dontPersist) {
    5225             :         // If the current entry is persistent but we inhibit peristence
    5226             :         // then force recreation of the entry as memory/only.
    5227           0 :         rv = mCacheEntry->GetPersistent(&recreate);
    5228           0 :         if (NS_FAILED(rv))
    5229           0 :             return rv;
    5230             :     }
    5231             : 
    5232           2 :     if (recreate) {
    5233           0 :         LOG(("  we have a ready entry, but reading it again from the server -> recreating cache entry\n"));
    5234             :         // clean the altData cache and reset this to avoid wrong content length
    5235           0 :         mAvailableCachedAltDataType.Truncate();
    5236             : 
    5237           0 :         nsCOMPtr<nsICacheEntry> currentEntry;
    5238           0 :         currentEntry.swap(mCacheEntry);
    5239           0 :         rv = currentEntry->Recreate(dontPersist, getter_AddRefs(mCacheEntry));
    5240           0 :         if (NS_FAILED(rv)) {
    5241           0 :           LOG(("  recreation failed, the response will not be cached"));
    5242           0 :           return NS_OK;
    5243             :         }
    5244             : 
    5245           0 :         mCacheEntryIsWriteOnly = true;
    5246             :     }
    5247             : 
    5248             :     // Set the expiration time for this cache entry
    5249           2 :     rv = UpdateExpirationTime();
    5250           2 :     if (NS_FAILED(rv)) return rv;
    5251             : 
    5252             :     // mark this weakly framed until a response body is seen
    5253           2 :     mCacheEntry->SetMetaDataElement("strongly-framed", "0");
    5254             : 
    5255           2 :     rv = AddCacheEntryHeaders(mCacheEntry);
    5256           2 :     if (NS_FAILED(rv)) return rv;
    5257             : 
    5258           2 :     mInitedCacheEntry = true;
    5259             : 
    5260             :     // Don't perform the check when writing (doesn't make sense)
    5261           2 :     mConcurrentCacheAccess = 0;
    5262             : 
    5263           2 :     return NS_OK;
    5264             : }
    5265             : 
    5266             : void
    5267           6 : nsHttpChannel::UpdateInhibitPersistentCachingFlag()
    5268             : {
    5269             :     // The no-store directive within the 'Cache-Control:' header indicates
    5270             :     // that we must not store the response in a persistent cache.
    5271           6 :     if (mResponseHead->NoStore())
    5272           0 :         mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
    5273             : 
    5274             :     // Only cache SSL content on disk if the pref is set
    5275             :     bool isHttps;
    5276          12 :     if (!gHttpHandler->IsPersistentHttpsCachingEnabled() &&
    5277           6 :         NS_SUCCEEDED(mURI->SchemeIs("https", &isHttps)) && isHttps) {
    5278           0 :         mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
    5279             :     }
    5280           6 : }
    5281             : 
    5282             : nsresult
    5283           0 : nsHttpChannel::InitOfflineCacheEntry()
    5284             : {
    5285             :     // This function can be called even when we fail to connect (bug 551990)
    5286             : 
    5287           0 :     if (!mOfflineCacheEntry) {
    5288           0 :         return NS_OK;
    5289             :     }
    5290             : 
    5291           0 :     if (!mResponseHead || mResponseHead->NoStore()) {
    5292           0 :         if (mResponseHead && mResponseHead->NoStore()) {
    5293           0 :             mOfflineCacheEntry->AsyncDoom(nullptr);
    5294             :         }
    5295             : 
    5296           0 :         CloseOfflineCacheEntry();
    5297             : 
    5298           0 :         if (mResponseHead && mResponseHead->NoStore()) {
    5299           0 :             return NS_ERROR_NOT_AVAILABLE;
    5300             :         }
    5301             : 
    5302           0 :         return NS_OK;
    5303             :     }
    5304             : 
    5305             :     // This entry's expiration time should match the main entry's expiration
    5306             :     // time.  UpdateExpirationTime() will keep it in sync once the offline
    5307             :     // cache entry has been created.
    5308           0 :     if (mCacheEntry) {
    5309             :         uint32_t expirationTime;
    5310           0 :         nsresult rv = mCacheEntry->GetExpirationTime(&expirationTime);
    5311           0 :         NS_ENSURE_SUCCESS(rv, rv);
    5312             : 
    5313           0 :         mOfflineCacheEntry->SetExpirationTime(expirationTime);
    5314             :     }
    5315             : 
    5316           0 :     return AddCacheEntryHeaders(mOfflineCacheEntry);
    5317             : }
    5318             : 
    5319             : 
    5320             : nsresult
    5321           2 : DoAddCacheEntryHeaders(nsHttpChannel *self,
    5322             :                        nsICacheEntry *entry,
    5323             :                        nsHttpRequestHead *requestHead,
    5324             :                        nsHttpResponseHead *responseHead,
    5325             :                        nsISupports *securityInfo)
    5326             : {
    5327             :     nsresult rv;
    5328             : 
    5329           2 :     LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] begin", self));
    5330             :     // Store secure data in memory only
    5331           2 :     if (securityInfo)
    5332           0 :         entry->SetSecurityInfo(securityInfo);
    5333             : 
    5334             :     // Store the HTTP request method with the cache entry so we can distinguish
    5335             :     // for example GET and HEAD responses.
    5336           4 :     nsAutoCString method;
    5337           2 :     requestHead->Method(method);
    5338           2 :     rv = entry->SetMetaDataElement("request-method", method.get());
    5339           2 :     if (NS_FAILED(rv)) return rv;
    5340             : 
    5341             :     // Store the HTTP authorization scheme used if any...
    5342           2 :     rv = StoreAuthorizationMetaData(entry, requestHead);
    5343           2 :     if (NS_FAILED(rv)) return rv;
    5344             : 
    5345             :     // Iterate over the headers listed in the Vary response header, and
    5346             :     // store the value of the corresponding request header so we can verify
    5347             :     // that it has not varied when we try to re-use the cached response at
    5348             :     // a later time.  Take care to store "Cookie" headers only as hashes
    5349             :     // due to security considerations and the fact that they can be pretty
    5350             :     // large (bug 468426). We take care of "Vary: cookie" in ResponseWouldVary.
    5351             :     //
    5352             :     // NOTE: if "Vary: accept, cookie", then we will store the "accept" header
    5353             :     // in the cache.  we could try to avoid needlessly storing the "accept"
    5354             :     // header in this case, but it doesn't seem worth the extra code to perform
    5355             :     // the check.
    5356             :     {
    5357           4 :         nsAutoCString buf, metaKey;
    5358           2 :         Unused << responseHead->GetHeader(nsHttp::Vary, buf);
    5359           2 :         if (!buf.IsEmpty()) {
    5360           0 :             NS_NAMED_LITERAL_CSTRING(prefix, "request-");
    5361             : 
    5362           0 :             char *bufData = buf.BeginWriting(); // going to munge buf
    5363           0 :             char *token = nsCRT::strtok(bufData, NS_HTTP_HEADER_SEPS, &bufData);
    5364           0 :             while (token) {
    5365           0 :                 LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \
    5366             :                         "processing %s", self, token));
    5367           0 :                 if (*token != '*') {
    5368           0 :                     nsHttpAtom atom = nsHttp::ResolveAtom(token);
    5369           0 :                     nsAutoCString val;
    5370           0 :                     nsAutoCString hash;
    5371           0 :                     if (NS_SUCCEEDED(requestHead->GetHeader(atom, val))) {
    5372             :                         // If cookie-header, store a hash of the value
    5373           0 :                         if (atom == nsHttp::Cookie) {
    5374           0 :                             LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \
    5375             :                                     "cookie-value %s", self, val.get()));
    5376           0 :                             rv = Hash(val.get(), hash);
    5377             :                             // If hash failed, store a string not very likely
    5378             :                             // to be the result of subsequent hashes
    5379           0 :                             if (NS_FAILED(rv)) {
    5380           0 :                                 val = NS_LITERAL_CSTRING("<hash failed>");
    5381             :                             } else {
    5382           0 :                                 val = hash;
    5383             :                             }
    5384             : 
    5385           0 :                             LOG(("   hashed to %s\n", val.get()));
    5386             :                         }
    5387             : 
    5388             :                         // build cache meta data key and set meta data element...
    5389           0 :                         metaKey = prefix + nsDependentCString(token);
    5390           0 :                         entry->SetMetaDataElement(metaKey.get(), val.get());
    5391             :                     } else {
    5392           0 :                         LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] " \
    5393             :                                 "clearing metadata for %s", self, token));
    5394           0 :                         metaKey = prefix + nsDependentCString(token);
    5395           0 :                         entry->SetMetaDataElement(metaKey.get(), nullptr);
    5396             :                     }
    5397             :                 }
    5398           0 :                 token = nsCRT::strtok(bufData, NS_HTTP_HEADER_SEPS, &bufData);
    5399             :             }
    5400             :         }
    5401             :     }
    5402             : 
    5403             :     // Store the received HTTP head with the cache entry as an element of
    5404             :     // the meta data.
    5405           4 :     nsAutoCString head;
    5406           2 :     responseHead->Flatten(head, true);
    5407           2 :     rv = entry->SetMetaDataElement("response-head", head.get());
    5408           2 :     if (NS_FAILED(rv)) return rv;
    5409           2 :     head.Truncate();
    5410           2 :     responseHead->FlattenNetworkOriginalHeaders(head);
    5411           2 :     rv = entry->SetMetaDataElement("original-response-headers", head.get());
    5412           2 :     if (NS_FAILED(rv)) return rv;
    5413             : 
    5414             :     // Indicate we have successfully finished setting metadata on the cache entry.
    5415           2 :     rv = entry->MetaDataReady();
    5416             : 
    5417           2 :     return rv;
    5418             : }
    5419             : 
    5420             : nsresult
    5421           2 : nsHttpChannel::AddCacheEntryHeaders(nsICacheEntry *entry)
    5422             : {
    5423           2 :     return DoAddCacheEntryHeaders(this, entry, &mRequestHead, mResponseHead, mSecurityInfo);
    5424             : }
    5425             : 
    5426             : inline void
    5427           0 : GetAuthType(const char *challenge, nsCString &authType)
    5428             : {
    5429             :     const char *p;
    5430             : 
    5431             :     // get the challenge type
    5432           0 :     if ((p = strchr(challenge, ' ')) != nullptr)
    5433           0 :         authType.Assign(challenge, p - challenge);
    5434             :     else
    5435           0 :         authType.Assign(challenge);
    5436           0 : }
    5437             : 
    5438             : nsresult
    5439           2 : StoreAuthorizationMetaData(nsICacheEntry *entry, nsHttpRequestHead *requestHead)
    5440             : {
    5441             :     // Not applicable to proxy authorization...
    5442           4 :     nsAutoCString val;
    5443           2 :     if (NS_FAILED(requestHead->GetHeader(nsHttp::Authorization, val))) {
    5444           2 :         return NS_OK;
    5445             :     }
    5446             : 
    5447             :     // eg. [Basic realm="wally world"]
    5448           0 :     nsAutoCString buf;
    5449           0 :     GetAuthType(val.get(), buf);
    5450           0 :     return entry->SetMetaDataElement("auth", buf.get());
    5451             : }
    5452             : 
    5453             : // Finalize the cache entry
    5454             : //  - may need to rewrite response headers if any headers changed
    5455             : //  - may need to recalculate the expiration time if any headers changed
    5456             : //  - called only for freshly written cache entries
    5457             : nsresult
    5458           2 : nsHttpChannel::FinalizeCacheEntry()
    5459             : {
    5460           2 :     LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p]\n", this));
    5461             : 
    5462             :     // Don't update this meta-data on 304
    5463           2 :     if (mStronglyFramed && !mCachedContentIsValid && mCacheEntry) {
    5464           1 :         LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p] Is Strongly Framed\n", this));
    5465           1 :         mCacheEntry->SetMetaDataElement("strongly-framed", "1");
    5466             :     }
    5467             : 
    5468           2 :     if (mResponseHead && mResponseHeadersModified) {
    5469             :         // Set the expiration time for this cache entry
    5470           0 :         nsresult rv = UpdateExpirationTime();
    5471           0 :         if (NS_FAILED(rv)) return rv;
    5472             :     }
    5473           2 :     return NS_OK;
    5474             : }
    5475             : 
    5476             : // Open an output stream to the cache entry and insert a listener tee into
    5477             : // the chain of response listeners.
    5478             : nsresult
    5479           2 : nsHttpChannel::InstallCacheListener(int64_t offset)
    5480             : {
    5481             :     nsresult rv;
    5482             : 
    5483           2 :     LOG(("Preparing to write data into the cache [uri=%s]\n", mSpec.get()));
    5484             : 
    5485           2 :     MOZ_ASSERT(mCacheEntry);
    5486           2 :     MOZ_ASSERT(mCacheEntryIsWriteOnly || mCachedContentIsPartial || mRaceCacheWithNetwork);
    5487           2 :     MOZ_ASSERT(mListener);
    5488             : 
    5489           4 :     nsAutoCString contentEncoding, contentType;
    5490           2 :     Unused << mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
    5491           2 :     mResponseHead->ContentType(contentType);
    5492             :     // If the content is compressible and the server has not compressed it,
    5493             :     // mark the cache entry for compression.
    5494           6 :     if (contentEncoding.IsEmpty() &&
    5495           2 :         (contentType.EqualsLiteral(TEXT_HTML) ||
    5496           0 :          contentType.EqualsLiteral(TEXT_PLAIN) ||
    5497           0 :          contentType.EqualsLiteral(TEXT_CSS) ||
    5498           0 :          contentType.EqualsLiteral(TEXT_JAVASCRIPT) ||
    5499           0 :          contentType.EqualsLiteral(TEXT_ECMASCRIPT) ||
    5500           0 :          contentType.EqualsLiteral(TEXT_XML) ||
    5501           0 :          contentType.EqualsLiteral(APPLICATION_JAVASCRIPT) ||
    5502           0 :          contentType.EqualsLiteral(APPLICATION_ECMASCRIPT) ||
    5503           0 :          contentType.EqualsLiteral(APPLICATION_XJAVASCRIPT) ||
    5504           0 :          contentType.EqualsLiteral(APPLICATION_XHTML_XML))) {
    5505           2 :         rv = mCacheEntry->SetMetaDataElement("uncompressed-len", "0");
    5506           2 :         if (NS_FAILED(rv)) {
    5507           0 :             LOG(("unable to mark cache entry for compression"));
    5508             :         }
    5509             :     }
    5510             : 
    5511           2 :     LOG(("Trading cache input stream for output stream [channel=%p]", this));
    5512             : 
    5513             :     // We must close the input stream first because cache entries do not
    5514             :     // correctly handle having an output stream and input streams open at
    5515             :     // the same time.
    5516           2 :     mCacheInputStream.CloseAndRelease();
    5517             : 
    5518           4 :     nsCOMPtr<nsIOutputStream> out;
    5519           2 :     rv = mCacheEntry->OpenOutputStream(offset, getter_AddRefs(out));
    5520           2 :     if (rv == NS_ERROR_NOT_AVAILABLE) {
    5521           0 :         LOG(("  entry doomed, not writing it [channel=%p]", this));
    5522             :         // Entry is already doomed.
    5523             :         // This may happen when expiration time is set to past and the entry
    5524             :         // has been removed by the background eviction logic.
    5525           0 :         return NS_OK;
    5526             :     }
    5527           2 :     if (NS_FAILED(rv)) return rv;
    5528             : 
    5529           2 :     if (mCacheOnlyMetadata) {
    5530           0 :         LOG(("Not storing content, cacheOnlyMetadata set"));
    5531             :         // We must open and then close the output stream of the cache entry.
    5532             :         // This way we indicate the content has been written (despite with zero
    5533             :         // length) and the entry is now in the ready state with "having data".
    5534             : 
    5535           0 :         out->Close();
    5536           0 :         return NS_OK;
    5537             :     }
    5538             : 
    5539             :     // XXX disk cache does not support overlapped i/o yet
    5540             : #if 0
    5541             :     // Mark entry valid inorder to allow simultaneous reading...
    5542             :     rv = mCacheEntry->MarkValid();
    5543             :     if (NS_FAILED(rv)) return rv;
    5544             : #endif
    5545             : 
    5546             :     nsCOMPtr<nsIStreamListenerTee> tee =
    5547           4 :         do_CreateInstance(kStreamListenerTeeCID, &rv);
    5548           2 :     if (NS_FAILED(rv)) return rv;
    5549             : 
    5550           4 :     nsCOMPtr<nsIEventTarget> cacheIOTarget;
    5551           2 :     if (!CacheObserver::UseNewCache()) {
    5552           0 :         nsCOMPtr<nsICacheStorageService> serv(services::GetCacheStorageService());
    5553           0 :         if (!serv) {
    5554           0 :             return NS_ERROR_NOT_AVAILABLE;
    5555             :         }
    5556             : 
    5557           0 :         serv->GetIoTarget(getter_AddRefs(cacheIOTarget));
    5558             :     }
    5559             : 
    5560           2 :     if (!cacheIOTarget) {
    5561           2 :         LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%" PRIx32
    5562             :              " cacheIOTarget=%p",
    5563             :              tee.get(), static_cast<uint32_t>(rv), cacheIOTarget.get()));
    5564           2 :         rv = tee->Init(mListener, out, nullptr);
    5565             :     } else {
    5566           0 :         LOG(("nsHttpChannel::InstallCacheListener async tee %p", tee.get()));
    5567           0 :         rv = tee->InitAsync(mListener, cacheIOTarget, out, nullptr);
    5568             :     }
    5569             : 
    5570           2 :     if (NS_FAILED(rv)) return rv;
    5571           2 :     mListener = tee;
    5572           2 :     return NS_OK;
    5573             : }
    5574             : 
    5575             : nsresult
    5576           0 : nsHttpChannel::InstallOfflineCacheListener(int64_t offset)
    5577             : {
    5578             :     nsresult rv;
    5579             : 
    5580           0 :     LOG(("Preparing to write data into the offline cache [uri=%s]\n",
    5581             :          mSpec.get()));
    5582             : 
    5583           0 :     MOZ_ASSERT(mOfflineCacheEntry);
    5584           0 :     MOZ_ASSERT(mListener);
    5585             : 
    5586           0 :     nsCOMPtr<nsIOutputStream> out;
    5587           0 :     rv = mOfflineCacheEntry->OpenOutputStream(offset, getter_AddRefs(out));
    5588           0 :     if (NS_FAILED(rv)) return rv;
    5589             : 
    5590             :     nsCOMPtr<nsIStreamListenerTee> tee =
    5591           0 :         do_CreateInstance(kStreamListenerTeeCID, &rv);
    5592           0 :     if (NS_FAILED(rv)) return rv;
    5593             : 
    5594           0 :     rv = tee->Init(mListener, out, nullptr);
    5595           0 :     if (NS_FAILED(rv)) return rv;
    5596             : 
    5597           0 :     mListener = tee;
    5598             : 
    5599           0 :     return NS_OK;
    5600             : }
    5601             : 
    5602             : void
    5603           3 : nsHttpChannel::ClearBogusContentEncodingIfNeeded()
    5604             : {
    5605             :     // For .gz files, apache sends both a Content-Type: application/x-gzip
    5606             :     // as well as Content-Encoding: gzip, which is completely wrong.  In
    5607             :     // this case, we choose to ignore the rogue Content-Encoding header. We
    5608             :     // must do this early on so as to prevent it from being seen up stream.
    5609             :     // The same problem exists for Content-Encoding: compress in default
    5610             :     // Apache installs.
    5611           6 :     nsAutoCString contentType;
    5612           3 :     mResponseHead->ContentType(contentType);
    5613           3 :     if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "gzip") && (
    5614           0 :         contentType.EqualsLiteral(APPLICATION_GZIP) ||
    5615           0 :         contentType.EqualsLiteral(APPLICATION_GZIP2) ||
    5616           0 :         contentType.EqualsLiteral(APPLICATION_GZIP3))) {
    5617             :         // clear the Content-Encoding header
    5618           0 :         mResponseHead->ClearHeader(nsHttp::Content_Encoding);
    5619             :     }
    5620           3 :     else if (mResponseHead->HasHeaderValue(nsHttp::Content_Encoding, "compress") && (
    5621           0 :              contentType.EqualsLiteral(APPLICATION_COMPRESS) ||
    5622           0 :              contentType.EqualsLiteral(APPLICATION_COMPRESS2))) {
    5623             :         // clear the Content-Encoding header
    5624           0 :         mResponseHead->ClearHeader(nsHttp::Content_Encoding);
    5625             :     }
    5626           3 : }
    5627             : 
    5628             : //-----------------------------------------------------------------------------
    5629             : // nsHttpChannel <redirect>
    5630             : //-----------------------------------------------------------------------------
    5631             : 
    5632             : nsresult
    5633           0 : nsHttpChannel::SetupReplacementChannel(nsIURI       *newURI,
    5634             :                                        nsIChannel   *newChannel,
    5635             :                                        bool          preserveMethod,
    5636             :                                        uint32_t      redirectFlags)
    5637             : {
    5638           0 :     LOG(("nsHttpChannel::SetupReplacementChannel "
    5639             :          "[this=%p newChannel=%p preserveMethod=%d]",
    5640             :          this, newChannel, preserveMethod));
    5641             : 
    5642             :     nsresult rv =
    5643           0 :       HttpBaseChannel::SetupReplacementChannel(newURI, newChannel,
    5644           0 :                                                preserveMethod, redirectFlags);
    5645           0 :     if (NS_FAILED(rv))
    5646           0 :         return rv;
    5647             : 
    5648           0 :     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
    5649           0 :     if (!httpChannel)
    5650           0 :         return NS_OK; // no other options to set
    5651             : 
    5652             :     // convey the mApplyConversion flag (bug 91862)
    5653           0 :     nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel);
    5654           0 :     if (encodedChannel)
    5655           0 :         encodedChannel->SetApplyConversion(mApplyConversion);
    5656             : 
    5657             :     // transfer the resume information
    5658           0 :     if (mResuming) {
    5659           0 :         nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(newChannel));
    5660           0 :         if (!resumableChannel) {
    5661           0 :             NS_WARNING("Got asked to resume, but redirected to non-resumable channel!");
    5662           0 :             return NS_ERROR_NOT_RESUMABLE;
    5663             :         }
    5664           0 :         resumableChannel->ResumeAt(mStartPos, mEntityID);
    5665             :     }
    5666             : 
    5667           0 :     if (!(redirectFlags & nsIChannelEventSink::REDIRECT_STS_UPGRADE) &&
    5668           0 :         mInterceptCache != INTERCEPTED &&
    5669           0 :         mRedirectMode != nsIHttpChannelInternal::REDIRECT_MODE_MANUAL) {
    5670           0 :       nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL;
    5671           0 :       rv = newChannel->GetLoadFlags(&loadFlags);
    5672           0 :       NS_ENSURE_SUCCESS(rv, rv);
    5673           0 :       loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
    5674           0 :       rv = newChannel->SetLoadFlags(loadFlags);
    5675           0 :       NS_ENSURE_SUCCESS(rv, rv);
    5676             :     }
    5677             : 
    5678           0 :     if (redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
    5679           0 :       nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(newChannel);
    5680           0 :       if (timedChannel) {
    5681           0 :         timedChannel->SetLaunchServiceWorkerStart(mLaunchServiceWorkerStart);
    5682           0 :         timedChannel->SetLaunchServiceWorkerEnd(mLaunchServiceWorkerEnd);
    5683           0 :         timedChannel->SetDispatchFetchEventStart(mDispatchFetchEventStart);
    5684           0 :         timedChannel->SetDispatchFetchEventEnd(mDispatchFetchEventEnd);
    5685           0 :         timedChannel->SetHandleFetchEventStart(mHandleFetchEventStart);
    5686           0 :         timedChannel->SetHandleFetchEventEnd(mHandleFetchEventEnd);
    5687             :       }
    5688             :     }
    5689             : 
    5690           0 :     return NS_OK;
    5691             : }
    5692             : 
    5693             : nsresult
    5694           0 : nsHttpChannel::AsyncProcessRedirection(uint32_t redirectType)
    5695             : {
    5696           0 :     LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n",
    5697             :         this, redirectType));
    5698             : 
    5699           0 :     nsAutoCString location;
    5700             : 
    5701             :     // if a location header was not given, then we can't perform the redirect,
    5702             :     // so just carry on as though this were a normal response.
    5703           0 :     if (NS_FAILED(mResponseHead->GetHeader(nsHttp::Location, location)))
    5704           0 :         return NS_ERROR_FAILURE;
    5705             : 
    5706             :     // make sure non-ASCII characters in the location header are escaped.
    5707           0 :     nsAutoCString locationBuf;
    5708           0 :     if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII, locationBuf))
    5709           0 :         location = locationBuf;
    5710             : 
    5711           0 :     if (mRedirectionLimit == 0) {
    5712           0 :         LOG(("redirection limit reached!\n"));
    5713           0 :         return NS_ERROR_REDIRECT_LOOP;
    5714             :     }
    5715             : 
    5716           0 :     mRedirectType = redirectType;
    5717             : 
    5718           0 :     LOG(("redirecting to: %s [redirection-limit=%u]\n",
    5719             :         location.get(), uint32_t(mRedirectionLimit)));
    5720             : 
    5721           0 :     nsresult rv = CreateNewURI(location.get(), getter_AddRefs(mRedirectURI));
    5722             : 
    5723           0 :     if (NS_FAILED(rv)) {
    5724           0 :         LOG(("Invalid URI for redirect: Location: %s\n", location.get()));
    5725           0 :         return NS_ERROR_CORRUPTED_CONTENT;
    5726             :     }
    5727             : 
    5728           0 :     if (mApplicationCache) {
    5729             :         // if we are redirected to a different origin check if there is a fallback
    5730             :         // cache entry to fall back to. we don't care about file strict
    5731             :         // checking, at least mURI is not a file URI.
    5732           0 :         if (!NS_SecurityCompareURIs(mURI, mRedirectURI, false)) {
    5733           0 :             PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback);
    5734             :             bool waitingForRedirectCallback;
    5735           0 :             Unused << ProcessFallback(&waitingForRedirectCallback);
    5736           0 :             if (waitingForRedirectCallback)
    5737           0 :                 return NS_OK;
    5738           0 :             PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirectionAfterFallback);
    5739             :         }
    5740             :     }
    5741             : 
    5742           0 :     return ContinueProcessRedirectionAfterFallback(NS_OK);
    5743             : }
    5744             : 
    5745             : nsresult
    5746           0 : nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv)
    5747             : {
    5748           0 :     if (NS_SUCCEEDED(rv) && mFallingBack) {
    5749             :         // do not continue with redirect processing, fallback is in
    5750             :         // progress now.
    5751           0 :         return NS_OK;
    5752             :     }
    5753             : 
    5754             :     // Kill the current cache entry if we are redirecting
    5755             :     // back to ourself.
    5756           0 :     bool redirectingBackToSameURI = false;
    5757           0 :     if (mCacheEntry && mCacheEntryIsWriteOnly &&
    5758           0 :         NS_SUCCEEDED(mURI->Equals(mRedirectURI, &redirectingBackToSameURI)) &&
    5759             :         redirectingBackToSameURI)
    5760           0 :             mCacheEntry->AsyncDoom(nullptr);
    5761             : 
    5762           0 :     bool hasRef = false;
    5763           0 :     rv = mRedirectURI->GetHasRef(&hasRef);
    5764             : 
    5765             :     // move the reference of the old location to the new one if the new
    5766             :     // one has none.
    5767           0 :     if (NS_SUCCEEDED(rv) && !hasRef) {
    5768           0 :         nsAutoCString ref;
    5769           0 :         mURI->GetRef(ref);
    5770           0 :         if (!ref.IsEmpty()) {
    5771             :             // NOTE: SetRef will fail if mRedirectURI is immutable
    5772             :             // (e.g. an about: URI)... Oh well.
    5773           0 :             mRedirectURI->SetRef(ref);
    5774             :         }
    5775             :     }
    5776             : 
    5777           0 :     bool rewriteToGET = ShouldRewriteRedirectToGET(mRedirectType,
    5778           0 :                                                    mRequestHead.ParsedMethod());
    5779             : 
    5780             :     // prompt if the method is not safe (such as POST, PUT, DELETE, ...)
    5781           0 :     if (!rewriteToGET && !mRequestHead.IsSafeMethod()) {
    5782           0 :         rv = PromptTempRedirect();
    5783           0 :         if (NS_FAILED(rv)) return rv;
    5784             :     }
    5785             : 
    5786           0 :     nsCOMPtr<nsIIOService> ioService;
    5787           0 :     rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
    5788           0 :     if (NS_FAILED(rv)) return rv;
    5789             : 
    5790             :     uint32_t redirectFlags;
    5791           0 :     if (nsHttp::IsPermanentRedirect(mRedirectType))
    5792           0 :         redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
    5793             :     else
    5794           0 :         redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
    5795             : 
    5796           0 :     nsCOMPtr<nsIChannel> newChannel;
    5797           0 :     nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(mRedirectURI, redirectFlags);
    5798           0 :     rv = NS_NewChannelInternal(getter_AddRefs(newChannel),
    5799             :                                mRedirectURI,
    5800             :                                redirectLoadInfo,
    5801             :                                nullptr, // aLoadGroup
    5802             :                                nullptr, // aCallbacks
    5803             :                                nsIRequest::LOAD_NORMAL,
    5804           0 :                                ioService);
    5805           0 :     NS_ENSURE_SUCCESS(rv, rv);
    5806             : 
    5807           0 :     rv = SetupReplacementChannel(mRedirectURI, newChannel,
    5808           0 :                                  !rewriteToGET, redirectFlags);
    5809           0 :     if (NS_FAILED(rv)) return rv;
    5810             : 
    5811             :     // verify that this is a legal redirect
    5812           0 :     mRedirectChannel = newChannel;
    5813             : 
    5814           0 :     PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
    5815           0 :     rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
    5816             : 
    5817           0 :     if (NS_SUCCEEDED(rv))
    5818           0 :         rv = WaitForRedirectCallback();
    5819             : 
    5820           0 :     if (NS_FAILED(rv)) {
    5821           0 :         AutoRedirectVetoNotifier notifier(this);
    5822           0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
    5823             :     }
    5824             : 
    5825           0 :     return rv;
    5826             : }
    5827             : 
    5828             : nsresult
    5829           0 : nsHttpChannel::ContinueProcessRedirection(nsresult rv)
    5830             : {
    5831           0 :     AutoRedirectVetoNotifier notifier(this);
    5832             : 
    5833           0 :     LOG(("nsHttpChannel::ContinueProcessRedirection [rv=%" PRIx32 ",this=%p]\n",
    5834             :          static_cast<uint32_t>(rv), this));
    5835           0 :     if (NS_FAILED(rv))
    5836           0 :         return rv;
    5837             : 
    5838           0 :     NS_PRECONDITION(mRedirectChannel, "No redirect channel?");
    5839             : 
    5840             :     // Make sure to do this after we received redirect veto answer,
    5841             :     // i.e. after all sinks had been notified
    5842           0 :     mRedirectChannel->SetOriginalURI(mOriginalURI);
    5843             : 
    5844             :     // And now, the deprecated way
    5845           0 :     nsCOMPtr<nsIHttpEventSink> httpEventSink;
    5846           0 :     GetCallback(httpEventSink);
    5847           0 :     if (httpEventSink) {
    5848             :         // NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8
    5849             :         // versions.
    5850           0 :         rv = httpEventSink->OnRedirect(this, mRedirectChannel);
    5851           0 :         if (NS_FAILED(rv))
    5852           0 :             return rv;
    5853             :     }
    5854             :     // XXX we used to talk directly with the script security manager, but that
    5855             :     // should really be handled by the event sink implementation.
    5856             : 
    5857             :     // begin loading the new channel
    5858           0 :     if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
    5859           0 :         MOZ_ASSERT(!mListenerContext, "mListenerContext should be null!");
    5860           0 :         rv = mRedirectChannel->AsyncOpen2(mListener);
    5861             :     }
    5862             :     else {
    5863           0 :         rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
    5864             :     }
    5865           0 :     NS_ENSURE_SUCCESS(rv, rv);
    5866             : 
    5867             :     // close down this channel
    5868           0 :     Cancel(NS_BINDING_REDIRECTED);
    5869             : 
    5870           0 :     notifier.RedirectSucceeded();
    5871             : 
    5872           0 :     ReleaseListeners();
    5873             : 
    5874           0 :     return NS_OK;
    5875             : }
    5876             : 
    5877             : //-----------------------------------------------------------------------------
    5878             : // nsHttpChannel <auth>
    5879             : //-----------------------------------------------------------------------------
    5880             : 
    5881           0 : NS_IMETHODIMP nsHttpChannel::OnAuthAvailable()
    5882             : {
    5883           0 :     LOG(("nsHttpChannel::OnAuthAvailable [this=%p]", this));
    5884             : 
    5885             :     // setting mAuthRetryPending flag and resuming the transaction
    5886             :     // triggers process of throwing away the unauthenticated data already
    5887             :     // coming from the network
    5888           0 :     mAuthRetryPending = true;
    5889           0 :     mProxyAuthPending = false;
    5890           0 :     LOG(("Resuming the transaction, we got credentials from user"));
    5891           0 :     mTransactionPump->Resume();
    5892             : 
    5893           0 :     return NS_OK;
    5894             : }
    5895             : 
    5896           0 : NS_IMETHODIMP nsHttpChannel::OnAuthCancelled(bool userCancel)
    5897             : {
    5898           0 :     LOG(("nsHttpChannel::OnAuthCancelled [this=%p]", this));
    5899             : 
    5900           0 :     if (mTransactionPump) {
    5901             :         // If the channel is trying to authenticate to a proxy and
    5902             :         // that was canceled we cannot show the http response body
    5903             :         // from the 40x as that might mislead the user into thinking
    5904             :         // it was a end host response instead of a proxy reponse.
    5905             :         // This must check explicitly whether a proxy auth was being done
    5906             :         // because we do want to show the content if this is an error from
    5907             :         // the origin server.
    5908           0 :         if (mProxyAuthPending)
    5909           0 :             Cancel(NS_ERROR_PROXY_CONNECTION_REFUSED);
    5910             : 
    5911             :         // ensure call of OnStartRequest of the current listener here,
    5912             :         // it would not be called otherwise at all
    5913           0 :         nsresult rv = CallOnStartRequest();
    5914             : 
    5915             :         // drop mAuthRetryPending flag and resume the transaction
    5916             :         // this resumes load of the unauthenticated content data (which
    5917             :         // may have been canceled if we don't want to show it)
    5918           0 :         mAuthRetryPending = false;
    5919           0 :         LOG(("Resuming the transaction, user cancelled the auth dialog"));
    5920           0 :         mTransactionPump->Resume();
    5921             : 
    5922           0 :         if (NS_FAILED(rv))
    5923           0 :             mTransactionPump->Cancel(rv);
    5924             :     }
    5925             : 
    5926           0 :     mProxyAuthPending = false;
    5927           0 :     return NS_OK;
    5928             : }
    5929             : 
    5930           0 : NS_IMETHODIMP nsHttpChannel::CloseStickyConnection()
    5931             : {
    5932           0 :     LOG(("nsHttpChannel::CloseStickyConnection this=%p", this));
    5933             : 
    5934             :     // Require we are between OnStartRequest and OnStopRequest, because
    5935             :     // what we do here takes effect in OnStopRequest (not reusing the
    5936             :     // connection for next authentication round).
    5937           0 :     if (!mIsPending) {
    5938           0 :         LOG(("  channel not pending"));
    5939           0 :         NS_ERROR("CloseStickyConnection not called before OnStopRequest, won't have any effect");
    5940           0 :         return NS_ERROR_UNEXPECTED;
    5941             :     }
    5942             : 
    5943           0 :     MOZ_ASSERT(mTransaction);
    5944           0 :     if (!mTransaction) {
    5945           0 :         return NS_ERROR_UNEXPECTED;
    5946             :     }
    5947             : 
    5948           0 :     if (!(mCaps & NS_HTTP_STICKY_CONNECTION ||
    5949           0 :           mTransaction->Caps() & NS_HTTP_STICKY_CONNECTION)) {
    5950           0 :         LOG(("  not sticky"));
    5951           0 :         return NS_OK;
    5952             :     }
    5953             : 
    5954           0 :     RefPtr<nsAHttpConnection> conn = mTransaction->GetConnectionReference();
    5955           0 :     if (!conn) {
    5956           0 :         LOG(("  no connection"));
    5957           0 :         return NS_OK;
    5958             :     }
    5959             : 
    5960             :     // This turns the IsPersistent() indicator on the connection to false,
    5961             :     // and makes us throw it away in OnStopRequest.
    5962           0 :     conn->DontReuse();
    5963           0 :     return NS_OK;
    5964             : }
    5965             : 
    5966           0 : NS_IMETHODIMP nsHttpChannel::ConnectionRestartable(bool aRestartable)
    5967             : {
    5968           0 :     LOG(("nsHttpChannel::ConnectionRestartable this=%p, restartable=%d",
    5969             :          this, aRestartable));
    5970           0 :     mAuthConnectionRestartable = aRestartable;
    5971           0 :     return NS_OK;
    5972             : }
    5973             : 
    5974             : //-----------------------------------------------------------------------------
    5975             : // nsHttpChannel::nsISupports
    5976             : //-----------------------------------------------------------------------------
    5977             : 
    5978         321 : NS_IMPL_ADDREF_INHERITED(nsHttpChannel, HttpBaseChannel)
    5979         307 : NS_IMPL_RELEASE_INHERITED(nsHttpChannel, HttpBaseChannel)
    5980             : 
    5981         276 : NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
    5982         276 :     NS_INTERFACE_MAP_ENTRY(nsIRequest)
    5983         270 :     NS_INTERFACE_MAP_ENTRY(nsIChannel)
    5984         218 :     NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
    5985         218 :     NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
    5986         212 :     NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
    5987         199 :     NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
    5988         198 :     NS_INTERFACE_MAP_ENTRY(nsICachingChannel)
    5989         194 :     NS_INTERFACE_MAP_ENTRY(nsIClassOfService)
    5990         193 :     NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
    5991         192 :     NS_INTERFACE_MAP_ENTRY(nsIFormPOSTActionChannel)
    5992         192 :     NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
    5993         185 :     NS_INTERFACE_MAP_ENTRY(nsICacheEntryOpenCallback)
    5994         180 :     NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
    5995         151 :     NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
    5996         151 :     NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
    5997         150 :     NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
    5998         148 :     NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
    5999         142 :     NS_INTERFACE_MAP_ENTRY(nsIInputAvailableCallback)
    6000         142 :     NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
    6001         136 :     NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
    6002         136 :     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
    6003         136 :     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
    6004         129 :     NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
    6005         129 :     NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
    6006         128 :     NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
    6007         127 :     NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
    6008         127 :     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    6009         123 :     NS_INTERFACE_MAP_ENTRY(nsICorsPreflightCallback)
    6010         123 :     NS_INTERFACE_MAP_ENTRY(nsIRaceCacheWithNetwork)
    6011         123 :     NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
    6012         123 :     NS_INTERFACE_MAP_ENTRY(nsIHstsPrimingCallback)
    6013         123 :     NS_INTERFACE_MAP_ENTRY(nsIChannelWithDivertableParentListener)
    6014             :     // we have no macro that covers this case.
    6015         123 :     if (aIID.Equals(NS_GET_IID(nsHttpChannel)) ) {
    6016           6 :         AddRef();
    6017           6 :         *aInstancePtr = this;
    6018           6 :         return NS_OK;
    6019             :     } else
    6020         117 : NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
    6021             : 
    6022             : //-----------------------------------------------------------------------------
    6023             : // nsHttpChannel::nsIRequest
    6024             : //-----------------------------------------------------------------------------
    6025             : 
    6026             : NS_IMETHODIMP
    6027           1 : nsHttpChannel::Cancel(nsresult status)
    6028             : {
    6029           1 :     MOZ_ASSERT(NS_IsMainThread());
    6030             :     // We should never have a pump open while a CORS preflight is in progress.
    6031           1 :     MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
    6032             : 
    6033           1 :     LOG(("nsHttpChannel::Cancel [this=%p status=%" PRIx32 "]\n",
    6034             :          this, static_cast<uint32_t>(status)));
    6035           1 :     if (mCanceled) {
    6036           0 :         LOG(("  ignoring; already canceled\n"));
    6037           0 :         return NS_OK;
    6038             :     }
    6039           1 :     if (mWaitingForRedirectCallback) {
    6040           0 :         LOG(("channel canceled during wait for redirect callback"));
    6041             :     }
    6042           1 :     mCanceled = true;
    6043           1 :     mStatus = status;
    6044           1 :     if (mProxyRequest)
    6045           0 :         mProxyRequest->Cancel(status);
    6046           1 :     CancelNetworkRequest(status);
    6047           1 :     mCacheInputStream.CloseAndRelease();
    6048           1 :     if (mCachePump)
    6049           1 :         mCachePump->Cancel(status);
    6050           1 :     if (mAuthProvider)
    6051           1 :         mAuthProvider->Cancel(status);
    6052           1 :     if (mPreflightChannel)
    6053           0 :         mPreflightChannel->Cancel(status);
    6054           1 :     return NS_OK;
    6055             : }
    6056             : 
    6057             : void
    6058           1 : nsHttpChannel::CancelNetworkRequest(nsresult aStatus)
    6059             : {
    6060           1 :     if (mTransaction) {
    6061           0 :         nsresult rv = gHttpHandler->CancelTransaction(mTransaction, aStatus);
    6062           0 :         if (NS_FAILED(rv)) {
    6063           0 :             LOG(("failed to cancel the transaction\n"));
    6064             :         }
    6065             :     }
    6066           1 :     if (mTransactionPump)
    6067           0 :         mTransactionPump->Cancel(aStatus);
    6068           1 : }
    6069             : 
    6070             : NS_IMETHODIMP
    6071           0 : nsHttpChannel::Suspend()
    6072             : {
    6073           0 :     nsresult rv = SuspendInternal();
    6074             : 
    6075           0 :     nsresult rvParentChannel = NS_OK;
    6076           0 :     if (mParentChannel) {
    6077           0 :       rvParentChannel = mParentChannel->SuspendMessageDiversion();
    6078             :     }
    6079             : 
    6080           0 :     return NS_FAILED(rv) ? rv : rvParentChannel;
    6081             : }
    6082             : 
    6083             : NS_IMETHODIMP
    6084           0 : nsHttpChannel::Resume()
    6085             : {
    6086           0 :     nsresult rv = ResumeInternal();
    6087             : 
    6088           0 :     nsresult rvParentChannel = NS_OK;
    6089           0 :     if (mParentChannel) {
    6090           0 :       rvParentChannel = mParentChannel->ResumeMessageDiversion();
    6091             :     }
    6092             : 
    6093           0 :     return NS_FAILED(rv) ? rv : rvParentChannel;
    6094             : }
    6095             : 
    6096             : //-----------------------------------------------------------------------------
    6097             : // nsHttpChannel::nsIChannel
    6098             : //-----------------------------------------------------------------------------
    6099             : 
    6100             : NS_IMETHODIMP
    6101           4 : nsHttpChannel::GetSecurityInfo(nsISupports **securityInfo)
    6102             : {
    6103           4 :     NS_ENSURE_ARG_POINTER(securityInfo);
    6104           4 :     *securityInfo = mSecurityInfo;
    6105           4 :     NS_IF_ADDREF(*securityInfo);
    6106           4 :     return NS_OK;
    6107             : }
    6108             : 
    6109             : // If any of the functions that AsyncOpen calls returns immediately an error
    6110             : // AsyncAbort(which calls onStart/onStopRequest) does not need to be call.
    6111             : // To be sure that they are not call ReleaseListeners() is called.
    6112             : // If AsyncOpen returns NS_OK, after that point AsyncAbort must be called on
    6113             : // any error.
    6114             : NS_IMETHODIMP
    6115           6 : nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context)
    6116             : {
    6117           6 :     MOZ_ASSERT(!mLoadInfo ||
    6118             :                mLoadInfo->GetSecurityMode() == 0 ||
    6119             :                mLoadInfo->GetInitialSecurityCheckDone() ||
    6120             :                (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
    6121             :                 nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
    6122             :                "security flags in loadInfo but asyncOpen2() not called");
    6123             : 
    6124           6 :     LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
    6125             : 
    6126             : #ifdef MOZ_TASK_TRACER
    6127             :     if (tasktracer::IsStartLogging()) {
    6128             :         uint64_t sourceEventId, parentTaskId;
    6129             :         tasktracer::SourceEventType sourceEventType;
    6130             :         GetCurTraceInfo(&sourceEventId, &parentTaskId, &sourceEventType);
    6131             :         nsCOMPtr<nsIURI> uri;
    6132             :         GetURI(getter_AddRefs(uri));
    6133             :         nsAutoCString urispec;
    6134             :         uri->GetSpec(urispec);
    6135             :         tasktracer::AddLabel("nsHttpChannel::AsyncOpen %s", urispec.get());
    6136             :     }
    6137             : #endif
    6138             : 
    6139           6 :     NS_CompareLoadInfoAndLoadContext(this);
    6140             : 
    6141             : #ifdef DEBUG
    6142           6 :     AssertPrivateBrowsingId();
    6143             : #endif
    6144             : 
    6145           6 :     NS_ENSURE_ARG_POINTER(listener);
    6146           6 :     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
    6147           6 :     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
    6148             : 
    6149             :     nsresult rv;
    6150             : 
    6151           6 :     MOZ_ASSERT(NS_IsMainThread());
    6152             : 
    6153           6 :     if (!gHttpHandler->Active()) {
    6154           0 :         LOG(("  after HTTP shutdown..."));
    6155           0 :         ReleaseListeners();
    6156           0 :         return NS_ERROR_NOT_AVAILABLE;
    6157             :     }
    6158             : 
    6159             :     static bool sRCWNInited = false;
    6160           6 :     if (!sRCWNInited) {
    6161           1 :         sRCWNInited = true;
    6162           1 :         Preferences::AddBoolVarCache(&sRCWNEnabled, "network.http.rcwn.enabled");
    6163           1 :         Preferences::AddUintVarCache(&sRCWNQueueSizeNormal, "network.http.rcwn.cache_queue_normal_threshold");
    6164           1 :         Preferences::AddUintVarCache(&sRCWNQueueSizePriority, "network.http.rcwn.cache_queue_priority_threshold");
    6165           1 :         Preferences::AddUintVarCache(&sRCWNSmallResourceSizeKB, "network.http.rcwn.small_resource_size_kb");
    6166           1 :         Preferences::AddUintVarCache(&sRCWNMaxWaitMs, "network.http.rcwn.max_wait_before_racing_ms");
    6167             :     }
    6168             : 
    6169           6 :     rv = NS_CheckPortSafety(mURI);
    6170           6 :     if (NS_FAILED(rv)) {
    6171           0 :         ReleaseListeners();
    6172           0 :         return rv;
    6173             :     }
    6174             : 
    6175           6 :     if (mInterceptCache != INTERCEPTED && ShouldIntercept()) {
    6176           0 :         mInterceptCache = MAYBE_INTERCEPT;
    6177           0 :         SetCouldBeSynthesized();
    6178             :     }
    6179             : 
    6180             :     // Remember the cookie header that was set, if any
    6181          12 :     nsAutoCString cookieHeader;
    6182           6 :     if (NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Cookie, cookieHeader))) {
    6183           0 :         mUserSetCookieHeader = cookieHeader;
    6184             :     }
    6185             : 
    6186           6 :     AddCookiesToRequest();
    6187             : 
    6188             :     // Set user agent override, do so before OnOpeningRequest notification
    6189             :     // since we want to allow consumers of that notification change or remove
    6190             :     // the User-Agent request header.
    6191           6 :     HttpBaseChannel::SetDocshellUserAgentOverride();
    6192             : 
    6193             :     // After we notify any observers (on-opening-request, loadGroup, etc) we
    6194             :     // must return NS_OK and return any errors asynchronously via
    6195             :     // OnStart/OnStopRequest.  Observers may add a reference to the channel
    6196             :     // and expect to get OnStopRequest so they know when to drop the reference,
    6197             :     // etc.
    6198             : 
    6199             :     // notify "http-on-opening-request" observers, but not if this is a redirect
    6200           6 :     if (!(mLoadFlags & LOAD_REPLACE)) {
    6201           6 :         gHttpHandler->OnOpeningRequest(this);
    6202             :     }
    6203             : 
    6204           6 :     mIsPending = true;
    6205           6 :     mWasOpened = true;
    6206             : 
    6207           6 :     mListener = listener;
    6208           6 :     mListenerContext = context;
    6209             : 
    6210           6 :     if (mLoadGroup)
    6211           1 :         mLoadGroup->AddRequest(this, nullptr);
    6212             : 
    6213             :     // record asyncopen time unconditionally and clear it if we
    6214             :     // don't want it after OnModifyRequest() weighs in. But waiting for
    6215             :     // that to complete would mean we don't include proxy resolution in the
    6216             :     // timing.
    6217           6 :     mAsyncOpenTime = TimeStamp::Now();
    6218             : 
    6219             :     // Remember we have Authorization header set here.  We need to check on it
    6220             :     // just once and early, AsyncOpen is the best place.
    6221           6 :     mCustomAuthHeader = mRequestHead.HasHeader(nsHttp::Authorization);
    6222             : 
    6223             :     // The common case for HTTP channels is to begin proxy resolution and return
    6224             :     // at this point. The only time we know mProxyInfo already is if we're
    6225             :     // proxying a non-http protocol like ftp.
    6226           6 :     if (!mProxyInfo && NS_SUCCEEDED(ResolveProxy())) {
    6227           6 :         return NS_OK;
    6228             :     }
    6229             : 
    6230           0 :     rv = BeginConnect();
    6231           0 :     if (NS_FAILED(rv)) {
    6232           0 :         CloseCacheEntry(false);
    6233           0 :         Unused << AsyncAbort(rv);
    6234             :     }
    6235             : 
    6236           0 :     return NS_OK;
    6237             : }
    6238             : 
    6239             : namespace {
    6240             : 
    6241             : class InitLocalBlockListXpcCallback final : public nsIURIClassifierCallback {
    6242             : public:
    6243             :   using CallbackType = nsHttpChannel::InitLocalBlockListCallback;
    6244             : 
    6245           4 :   explicit InitLocalBlockListXpcCallback(const CallbackType& aCallback)
    6246           4 :     : mCallback(aCallback)
    6247             :   {
    6248           4 :   }
    6249             : 
    6250             :   NS_DECL_ISUPPORTS
    6251             :   NS_DECL_NSIURICLASSIFIERCALLBACK
    6252             : 
    6253             : private:
    6254           4 :   ~InitLocalBlockListXpcCallback() = default;
    6255             : 
    6256             :   CallbackType mCallback;
    6257             : };
    6258             : 
    6259          17 : NS_IMPL_ISUPPORTS(InitLocalBlockListXpcCallback, nsIURIClassifierCallback)
    6260             : 
    6261             : /*virtual*/ nsresult
    6262           1 : InitLocalBlockListXpcCallback::OnClassifyComplete(nsresult aErrorCode, // Only this matters.
    6263             :                                                const nsACString& /*aLists*/,
    6264             :                                                const nsACString& /*aProvider*/,
    6265             :                                                const nsACString& /*aPrefix*/)
    6266             : {
    6267           1 :     bool localBlockList = aErrorCode == NS_ERROR_TRACKING_URI;
    6268           1 :     mCallback(localBlockList);
    6269           1 :     return NS_OK;
    6270             : }
    6271             : 
    6272             : } // end of unnamed namespace/
    6273             : 
    6274             : already_AddRefed<nsChannelClassifier>
    6275           8 : nsHttpChannel::GetOrCreateChannelClassifier()
    6276             : {
    6277           8 :     if (!mChannelClassifier) {
    6278           4 :         mChannelClassifier = new nsChannelClassifier(this);
    6279           4 :         LOG(("nsHttpChannel [%p] created nsChannelClassifier [%p]\n",
    6280             :              this, mChannelClassifier.get()));
    6281             :     }
    6282             : 
    6283          16 :     RefPtr<nsChannelClassifier> classifier = mChannelClassifier;
    6284          16 :     return classifier.forget();
    6285             : }
    6286             : 
    6287             : bool
    6288           4 : nsHttpChannel::InitLocalBlockList(const InitLocalBlockListCallback& aCallback)
    6289             : {
    6290           4 :     mLocalBlocklist = false;
    6291             : 
    6292           4 :     if (!(mLoadFlags & LOAD_CLASSIFY_URI)) {
    6293           0 :         return false;
    6294             :     }
    6295             : 
    6296             :     // Check to see if this principal exists on local blocklists.
    6297             :     RefPtr<nsChannelClassifier> channelClassifier =
    6298           8 :         GetOrCreateChannelClassifier();
    6299             : 
    6300             :     // We skip speculative connections by setting mLocalBlocklist only
    6301             :     // when tracking protection is enabled. Though we could do this for
    6302             :     // both phishing and malware, it is not necessary for correctness,
    6303             :     // since no network events will be received while the
    6304             :     // nsChannelClassifier is in progress. See bug 1122691.
    6305             :     RefPtr<InitLocalBlockListXpcCallback> xpcCallback
    6306           8 :         = new InitLocalBlockListXpcCallback(aCallback);
    6307           4 :     if (NS_FAILED(channelClassifier->CheckIsTrackerWithLocalTable(xpcCallback))) {
    6308           3 :         return false;
    6309             :     }
    6310             : 
    6311           1 :     return true;
    6312             : }
    6313             : 
    6314             : NS_IMETHODIMP
    6315           6 : nsHttpChannel::AsyncOpen2(nsIStreamListener *aListener)
    6316             : {
    6317          12 :   nsCOMPtr<nsIStreamListener> listener = aListener;
    6318           6 :   nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
    6319           6 :   if (NS_WARN_IF(NS_FAILED(rv))) {
    6320           0 :       ReleaseListeners();
    6321           0 :       return rv;
    6322             :   }
    6323           6 :   return AsyncOpen(listener, nullptr);
    6324             : }
    6325             : 
    6326             : // BeginConnect() SHOULD NOT call AsyncAbort(). AsyncAbort will be called by
    6327             : // functions that called BeginConnect if needed. Only AsyncOpen and
    6328             : // OnProxyAvailable ever call BeginConnect.
    6329             : nsresult
    6330           6 : nsHttpChannel::BeginConnect()
    6331             : {
    6332           6 :     LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this));
    6333             :     nsresult rv;
    6334             : 
    6335             :     // Construct connection info object
    6336          12 :     nsAutoCString host;
    6337          12 :     nsAutoCString scheme;
    6338           6 :     int32_t port = -1;
    6339           6 :     bool isHttps = false;
    6340             : 
    6341           6 :     rv = mURI->GetScheme(scheme);
    6342           6 :     if (NS_SUCCEEDED(rv))
    6343           6 :         rv = mURI->SchemeIs("https", &isHttps);
    6344           6 :     if (NS_SUCCEEDED(rv))
    6345           6 :         rv = mURI->GetAsciiHost(host);
    6346           6 :     if (NS_SUCCEEDED(rv))
    6347           6 :         rv = mURI->GetPort(&port);
    6348           6 :     if (NS_SUCCEEDED(rv))
    6349           6 :         mURI->GetUsername(mUsername);
    6350           6 :     if (NS_SUCCEEDED(rv))
    6351           6 :         rv = mURI->GetAsciiSpec(mSpec);
    6352           6 :     if (NS_FAILED(rv)) {
    6353           0 :         return rv;
    6354             :     }
    6355             : 
    6356             :     // Reject the URL if it doesn't specify a host
    6357           6 :     if (host.IsEmpty()) {
    6358           0 :         rv = NS_ERROR_MALFORMED_URI;
    6359           0 :         return rv;
    6360             :     }
    6361           6 :     LOG(("host=%s port=%d\n", host.get(), port));
    6362           6 :     LOG(("uri=%s\n", mSpec.get()));
    6363             : 
    6364          12 :     nsCOMPtr<nsProxyInfo> proxyInfo;
    6365           6 :     if (mProxyInfo)
    6366           0 :         proxyInfo = do_QueryInterface(mProxyInfo);
    6367             : 
    6368           6 :     mRequestHead.SetHTTPS(isHttps);
    6369           6 :     mRequestHead.SetOrigin(scheme, host, port);
    6370             : 
    6371           6 :     SetDoNotTrack();
    6372             : 
    6373          12 :     OriginAttributes originAttributes;
    6374           6 :     NS_GetOriginAttributes(this, originAttributes);
    6375             : 
    6376          12 :     RefPtr<AltSvcMapping> mapping;
    6377          30 :     if (!mConnectionInfo && mAllowAltSvc && // per channel
    6378          18 :         !(mLoadFlags & LOAD_FRESH_CONNECTION) &&
    6379          24 :         (scheme.Equals(NS_LITERAL_CSTRING("http")) ||
    6380          12 :          scheme.Equals(NS_LITERAL_CSTRING("https"))) &&
    6381          30 :         (!proxyInfo || proxyInfo->IsDirect()) &&
    6382          12 :         (mapping = gHttpHandler->GetAltServiceMapping(scheme,
    6383             :                                                       host, port,
    6384           6 :                                                       mPrivateBrowsing,
    6385           6 :                                                       originAttributes))) {
    6386           0 :         LOG(("nsHttpChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n",
    6387             :              this, scheme.get(), mapping->AlternateHost().get(),
    6388             :              mapping->AlternatePort(), mapping->HashKey().get()));
    6389             : 
    6390           0 :         if (!(mLoadFlags & LOAD_ANONYMOUS) && !mPrivateBrowsing) {
    6391           0 :             nsAutoCString altUsedLine(mapping->AlternateHost());
    6392           0 :             bool defaultPort = mapping->AlternatePort() ==
    6393           0 :                 (isHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT);
    6394           0 :             if (!defaultPort) {
    6395           0 :                 altUsedLine.AppendLiteral(":");
    6396           0 :                 altUsedLine.AppendInt(mapping->AlternatePort());
    6397             :             }
    6398           0 :             rv = mRequestHead.SetHeader(nsHttp::Alternate_Service_Used, altUsedLine);
    6399           0 :             MOZ_ASSERT(NS_SUCCEEDED(rv));
    6400             :         }
    6401             : 
    6402             :         nsCOMPtr<nsIConsoleService> consoleService =
    6403           0 :             do_GetService(NS_CONSOLESERVICE_CONTRACTID);
    6404           0 :         if (consoleService) {
    6405           0 :             nsAutoString message(NS_LITERAL_STRING("Alternate Service Mapping found: "));
    6406           0 :             AppendASCIItoUTF16(scheme.get(), message);
    6407           0 :             message.Append(NS_LITERAL_STRING("://"));
    6408           0 :             AppendASCIItoUTF16(host.get(), message);
    6409           0 :             message.Append(NS_LITERAL_STRING(":"));
    6410           0 :             message.AppendInt(port);
    6411           0 :             message.Append(NS_LITERAL_STRING(" to "));
    6412           0 :             AppendASCIItoUTF16(scheme.get(), message);
    6413           0 :             message.Append(NS_LITERAL_STRING("://"));
    6414           0 :             AppendASCIItoUTF16(mapping->AlternateHost().get(), message);
    6415           0 :             message.Append(NS_LITERAL_STRING(":"));
    6416           0 :             message.AppendInt(mapping->AlternatePort());
    6417           0 :             consoleService->LogStringMessage(message.get());
    6418             :         }
    6419             : 
    6420           0 :         LOG(("nsHttpChannel %p Using connection info from altsvc mapping", this));
    6421           0 :         mapping->GetConnectionInfo(getter_AddRefs(mConnectionInfo), proxyInfo, originAttributes);
    6422           0 :         Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, true);
    6423           0 :         Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC_OE, !isHttps);
    6424           6 :     } else if (mConnectionInfo) {
    6425           0 :         LOG(("nsHttpChannel %p Using channel supplied connection info", this));
    6426           0 :         Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, false);
    6427             :     } else {
    6428           6 :         LOG(("nsHttpChannel %p Using default connection info", this));
    6429             : 
    6430           6 :         mConnectionInfo = new nsHttpConnectionInfo(host, port, EmptyCString(), mUsername, proxyInfo,
    6431          12 :                                                    originAttributes, isHttps);
    6432           6 :         Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, false);
    6433             :     }
    6434             : 
    6435             :     // Set network interface id only when it's not empty to avoid
    6436             :     // rebuilding hash key.
    6437           6 :     if (!mNetworkInterfaceId.IsEmpty()) {
    6438           0 :         mConnectionInfo->SetNetworkInterfaceId(mNetworkInterfaceId);
    6439             :     }
    6440             : 
    6441             :     mAuthProvider =
    6442          12 :         do_CreateInstance("@mozilla.org/network/http-channel-auth-provider;1",
    6443           6 :                           &rv);
    6444           6 :     if (NS_SUCCEEDED(rv))
    6445           6 :         rv = mAuthProvider->Init(this);
    6446           6 :     if (NS_FAILED(rv)) {
    6447           0 :         return rv;
    6448             :     }
    6449             : 
    6450             :     // check to see if authorization headers should be included
    6451             :     // mCustomAuthHeader is set in AsyncOpen if we find Authorization header
    6452           6 :     rv = mAuthProvider->AddAuthorizationHeaders(mCustomAuthHeader);
    6453           6 :     if (NS_FAILED(rv)) {
    6454           0 :         LOG(("nsHttpChannel %p AddAuthorizationHeaders failed (%08x)",
    6455             :              this, static_cast<uint32_t>(rv)));
    6456             :     }
    6457             : 
    6458             :     // notify "http-on-modify-request" observers
    6459           6 :     CallOnModifyRequestObservers();
    6460             : 
    6461           6 :     SetLoadGroupUserAgentOverride();
    6462             : 
    6463             :     // Check if request was cancelled during on-modify-request or on-useragent.
    6464           6 :     if (mCanceled) {
    6465           0 :         return mStatus;
    6466             :     }
    6467             : 
    6468           6 :     if (mSuspendCount) {
    6469           0 :         LOG(("Waiting until resume BeginConnect [this=%p]\n", this));
    6470           0 :         MOZ_ASSERT(!mCallOnResume);
    6471           0 :         mCallOnResume = &nsHttpChannel::HandleBeginConnectContinue;
    6472           0 :         return NS_OK;
    6473             :     }
    6474             : 
    6475           6 :     return BeginConnectContinue();
    6476             : }
    6477             : 
    6478             : void
    6479           0 : nsHttpChannel::HandleBeginConnectContinue()
    6480             : {
    6481           0 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
    6482             :     nsresult rv;
    6483             : 
    6484           0 :     if (mSuspendCount) {
    6485           0 :         LOG(("Waiting until resume BeginConnect [this=%p]\n", this));
    6486           0 :         mCallOnResume = &nsHttpChannel::HandleBeginConnectContinue;
    6487           0 :         return;
    6488             :     }
    6489             : 
    6490           0 :     LOG(("nsHttpChannel::HandleBeginConnectContinue [this=%p]\n", this));
    6491           0 :     rv = BeginConnectContinue();
    6492           0 :     if (NS_FAILED(rv)) {
    6493           0 :         CloseCacheEntry(false);
    6494           0 :         Unused << AsyncAbort(rv);
    6495             :     }
    6496             : }
    6497             : 
    6498             : nsresult
    6499           6 : nsHttpChannel::BeginConnectContinue()
    6500             : {
    6501             :     nsresult rv;
    6502             : 
    6503             :     // Check if request was cancelled during suspend AFTER on-modify-request or
    6504             :     // on-useragent.
    6505           6 :     if (mCanceled) {
    6506           0 :         return mStatus;
    6507             :     }
    6508             : 
    6509             :     // Check to see if we should redirect this channel elsewhere by
    6510             :     // nsIHttpChannel.redirectTo API request
    6511           6 :     if (mAPIRedirectToURI) {
    6512           0 :         return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
    6513             :     }
    6514             : 
    6515             :     // If mTimingEnabled flag is not set after OnModifyRequest() then
    6516             :     // clear the already recorded AsyncOpen value for consistency.
    6517           6 :     if (!mTimingEnabled)
    6518           2 :         mAsyncOpenTime = TimeStamp();
    6519             : 
    6520             :     // if this somehow fails we can go on without it
    6521           6 :     Unused << gHttpHandler->AddConnectionHeader(&mRequestHead, mCaps);
    6522             : 
    6523           6 :     if (mLoadFlags & VALIDATE_ALWAYS || BYPASS_LOCAL_CACHE(mLoadFlags))
    6524           1 :         mCaps |= NS_HTTP_REFRESH_DNS;
    6525             : 
    6526             :     // Adjust mCaps according to our request headers:
    6527             :     //  - If "Connection: close" is set as a request header, then do not bother
    6528             :     //    trying to establish a keep-alive connection.
    6529           6 :     if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close"))
    6530           0 :         mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE);
    6531             : 
    6532           6 :     if (gHttpHandler->CriticalRequestPrioritization()) {
    6533           6 :         if (mClassOfService & nsIClassOfService::Leader) {
    6534           2 :             mCaps |= NS_HTTP_LOAD_AS_BLOCKING;
    6535             :         }
    6536           6 :         if (mClassOfService & nsIClassOfService::Unblocked) {
    6537           0 :             mCaps |= NS_HTTP_LOAD_UNBLOCKED;
    6538             :         }
    6539           7 :         if (mClassOfService & nsIClassOfService::UrgentStart &&
    6540           1 :             gHttpHandler->IsUrgentStartEnabled()) {
    6541           1 :             mCaps |= NS_HTTP_URGENT_START;
    6542           1 :             SetPriority(nsISupportsPriority::PRIORITY_HIGHEST);
    6543             :         }
    6544             :     }
    6545             : 
    6546             :     // Force-Reload should reset the persistent connection pool for this host
    6547           6 :     if (mLoadFlags & LOAD_FRESH_CONNECTION) {
    6548             :         // just the initial document resets the whole pool
    6549           0 :         if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
    6550           0 :             gHttpHandler->ConnMgr()->ClearAltServiceMappings();
    6551           0 :             rv = gHttpHandler->ConnMgr()->DoShiftReloadConnectionCleanup(mConnectionInfo);
    6552           0 :             if (NS_FAILED(rv)) {
    6553           0 :                 LOG(("nsHttpChannel::BeginConnect "
    6554             :                      "DoShiftReloadConnectionCleanup failed: %08x [this=%p]",
    6555             :                      static_cast<uint32_t>(rv), this));
    6556             :             }
    6557             :         }
    6558             :     }
    6559             : 
    6560             :     // We may have been cancelled already, either by on-modify-request
    6561             :     // listeners or load group observers; in that case, we should not send the
    6562             :     // request to the server
    6563           6 :     if (mCanceled) {
    6564           0 :         return mStatus;
    6565             :     }
    6566             : 
    6567           6 :     if (!(mLoadFlags & LOAD_CLASSIFY_URI)) {
    6568           2 :         return ContinueBeginConnectWithResult();
    6569             :     }
    6570             : 
    6571             :     // We are about to do a async lookup to check if the URI is a
    6572             :     // tracker. The result will be delivered along with the callback.
    6573             :     // Chances are the lookup is not needed so InitLocalBlockList()
    6574             :     // will return false and then we can BeginConnectActual() right away.
    6575           8 :     RefPtr<nsHttpChannel> self = this;
    6576          29 :     bool willCallback = InitLocalBlockList([self](bool aLocalBlockList) -> void  {
    6577           1 :         self->mLocalBlocklist = aLocalBlockList;
    6578           1 :         nsresult rv = self->BeginConnectActual();
    6579           1 :         if (NS_FAILED(rv)) {
    6580             :             // Since this error is thrown asynchronously so that the caller
    6581             :             // of BeginConnect() will not do clean up for us. We have to do
    6582             :             // it on our own.
    6583           0 :             self->CloseCacheEntry(false);
    6584           0 :             Unused << self->AsyncAbort(rv);
    6585             :         }
    6586           5 :     });
    6587             : 
    6588           4 :     if (!willCallback) {
    6589             :         // We can do BeginConnectActual immediately if mLocalBlockList is initialized
    6590             :         // synchronously. Note that we don't need to handle the failure because
    6591             :         // BeginConnect() will return synchronously and the caller will be responsible
    6592             :         // for handling it.
    6593           3 :         return BeginConnectActual();
    6594             :     }
    6595             : 
    6596           1 :     return NS_OK;
    6597             : }
    6598             : 
    6599             : nsresult
    6600           4 : nsHttpChannel::BeginConnectActual()
    6601             : {
    6602           4 :     if (mCanceled) {
    6603           0 :         return mStatus;
    6604             :     }
    6605             : 
    6606           8 :     if (!mLocalBlocklist && !mConnectionInfo->UsingHttpProxy() &&
    6607           4 :         !(mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE))) {
    6608             :         // Start a DNS lookup very early in case the real open is queued the DNS can
    6609             :         // happen in parallel. Do not do so in the presence of an HTTP proxy as
    6610             :         // all lookups other than for the proxy itself are done by the proxy.
    6611             :         // Also we don't do a lookup if the LOAD_NO_NETWORK_IO or
    6612             :         // LOAD_ONLY_FROM_CACHE flags are set.
    6613             :         //
    6614             :         // We keep the DNS prefetch object around so that we can retrieve
    6615             :         // timing information from it. There is no guarantee that we actually
    6616             :         // use the DNS prefetch data for the real connection, but as we keep
    6617             :         // this data around for 3 minutes by default, this should almost always
    6618             :         // be correct, and even when it isn't, the timing still represents _a_
    6619             :         // valid DNS lookup timing for the site, even if it is not _the_
    6620             :         // timing we used.
    6621           4 :         LOG(("nsHttpChannel::BeginConnect [this=%p] prefetching%s\n",
    6622             :              this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : ""));
    6623           8 :         OriginAttributes originAttributes;
    6624           4 :         NS_GetOriginAttributes(this, originAttributes);
    6625             :         mDNSPrefetch = new nsDNSPrefetch(mURI, originAttributes,
    6626           8 :                                          this, mTimingEnabled);
    6627           4 :         mDNSPrefetch->PrefetchHigh(mCaps & NS_HTTP_REFRESH_DNS);
    6628             :     }
    6629             : 
    6630             :     // mLocalBlocklist is true only if tracking protection is enabled and the
    6631             :     // URI is a tracking domain, it makes no guarantees about phishing or
    6632             :     // malware, so if LOAD_CLASSIFY_URI is true we must call
    6633             :     // nsChannelClassifier to catch phishing and malware URIs.
    6634           4 :     bool callContinueBeginConnect = true;
    6635           4 :     if (!mLocalBlocklist) {
    6636             :         // Here we call ContinueBeginConnectWithResult and not
    6637             :         // ContinueBeginConnect so that in the case of an error we do not start
    6638             :         // channelClassifier.
    6639           4 :         nsresult rv = ContinueBeginConnectWithResult();
    6640           4 :         if (NS_FAILED(rv)) {
    6641           0 :             return rv;
    6642             :         }
    6643           4 :         callContinueBeginConnect = false;
    6644             :     }
    6645             :     // nsChannelClassifier calls ContinueBeginConnect if it has not already
    6646             :     // been called, after optionally cancelling the channel once we have a
    6647             :     // remote verdict. We call a concrete class instead of an nsI* that might
    6648             :     // be overridden.
    6649             :     RefPtr<nsChannelClassifier> channelClassifier =
    6650           8 :         GetOrCreateChannelClassifier();
    6651           4 :     LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
    6652             :          channelClassifier.get(), this));
    6653           4 :     channelClassifier->Start();
    6654           4 :     if (callContinueBeginConnect) {
    6655           0 :         return ContinueBeginConnectWithResult();
    6656             :     }
    6657           4 :     return NS_OK;
    6658             : }
    6659             : 
    6660             : NS_IMETHODIMP
    6661           3 : nsHttpChannel::GetEncodedBodySize(uint64_t *aEncodedBodySize)
    6662             : {
    6663           3 :     if (mCacheEntry && !mCacheEntryIsWriteOnly) {
    6664           2 :         int64_t dataSize = 0;
    6665           2 :         mCacheEntry->GetDataSize(&dataSize);
    6666           2 :         *aEncodedBodySize = dataSize;
    6667             :     } else {
    6668           1 :         *aEncodedBodySize = mLogicalOffset;
    6669             :     }
    6670           3 :     return NS_OK;
    6671             : }
    6672             : 
    6673             : //-----------------------------------------------------------------------------
    6674             : // nsHttpChannel::nsIHttpChannelInternal
    6675             : //-----------------------------------------------------------------------------
    6676             : 
    6677             : NS_IMETHODIMP
    6678           0 : nsHttpChannel::SetupFallbackChannel(const char *aFallbackKey)
    6679             : {
    6680           0 :     ENSURE_CALLED_BEFORE_CONNECT();
    6681             : 
    6682           0 :     LOG(("nsHttpChannel::SetupFallbackChannel [this=%p, key=%s]\n",
    6683             :          this, aFallbackKey));
    6684           0 :     mFallbackChannel = true;
    6685           0 :     mFallbackKey = aFallbackKey;
    6686             : 
    6687           0 :     return NS_OK;
    6688             : }
    6689             : 
    6690             : NS_IMETHODIMP
    6691           0 : nsHttpChannel::ForceIntercepted(uint64_t aInterceptionID)
    6692             : {
    6693           0 :     ENSURE_CALLED_BEFORE_ASYNC_OPEN();
    6694             : 
    6695           0 :     if (NS_WARN_IF(mLoadFlags & LOAD_BYPASS_SERVICE_WORKER)) {
    6696           0 :         return NS_ERROR_NOT_AVAILABLE;
    6697             :     }
    6698             : 
    6699           0 :     MarkIntercepted();
    6700           0 :     mResponseCouldBeSynthesized = true;
    6701           0 :     mInterceptionID = aInterceptionID;
    6702           0 :     return NS_OK;
    6703             : }
    6704             : 
    6705             : NS_IMETHODIMP
    6706           0 : nsHttpChannel::SetChannelIsForDownload(bool aChannelIsForDownload)
    6707             : {
    6708           0 :   if (aChannelIsForDownload) {
    6709           0 :     AddClassFlags(nsIClassOfService::Throttleable);
    6710             :   } else {
    6711           0 :     ClearClassFlags(nsIClassOfService::Throttleable);
    6712             :   }
    6713             : 
    6714           0 :   return HttpBaseChannel::SetChannelIsForDownload(aChannelIsForDownload);
    6715             : }
    6716             : 
    6717             : //-----------------------------------------------------------------------------
    6718             : // nsHttpChannel::nsISupportsPriority
    6719             : //-----------------------------------------------------------------------------
    6720             : 
    6721             : NS_IMETHODIMP
    6722           3 : nsHttpChannel::SetPriority(int32_t value)
    6723             : {
    6724           3 :     int16_t newValue = clamped<int32_t>(value, INT16_MIN, INT16_MAX);
    6725           3 :     if (mPriority == newValue)
    6726           0 :         return NS_OK;
    6727           3 :     mPriority = newValue;
    6728           3 :     if (mTransaction) {
    6729           0 :         nsresult rv = gHttpHandler->RescheduleTransaction(mTransaction, mPriority);
    6730           0 :         if (NS_FAILED(rv)) {
    6731           0 :             LOG(("nsHttpChannel::SetPriority [this=%p] "
    6732             :                  "RescheduleTransaction failed (%08x)", this,
    6733             :                  static_cast<uint32_t>(rv)));
    6734             :         }
    6735             :     }
    6736             : 
    6737             :     // If this channel is the real channel for an e10s channel, notify the
    6738             :     // child side about the priority change as well.
    6739           6 :     nsCOMPtr<nsIParentChannel> parentChannel;
    6740           3 :     NS_QueryNotificationCallbacks(this, parentChannel);
    6741           6 :     RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
    6742           3 :     if (httpParent) {
    6743           1 :         httpParent->DoSendSetPriority(newValue);
    6744             :     }
    6745             : 
    6746           3 :     return NS_OK;
    6747             : }
    6748             : 
    6749             : nsresult
    6750           6 : nsHttpChannel::ContinueBeginConnectWithResult()
    6751             : {
    6752           6 :     LOG(("nsHttpChannel::ContinueBeginConnectWithResult [this=%p]", this));
    6753           6 :     NS_PRECONDITION(!mCallOnResume, "How did that happen?");
    6754             : 
    6755             :     nsresult rv;
    6756             : 
    6757           6 :     if (mSuspendCount) {
    6758           0 :         LOG(("Waiting until resume to do async connect [this=%p]\n", this));
    6759           0 :         mCallOnResume = &nsHttpChannel::ContinueBeginConnect;
    6760           0 :         rv = NS_OK;
    6761           6 :     } else if (mCanceled) {
    6762             :         // We may have been cancelled already, by nsChannelClassifier in that
    6763             :         // case, we should not send the request to the server
    6764           0 :         rv = mStatus;
    6765             :     } else {
    6766           6 :         rv = Connect();
    6767             :     }
    6768             : 
    6769           6 :     LOG(("nsHttpChannel::ContinueBeginConnectWithResult result [this=%p rv=%" PRIx32
    6770             :          " mCanceled=%u]\n",
    6771             :          this, static_cast<uint32_t>(rv), static_cast<bool>(mCanceled)));
    6772           6 :     return rv;
    6773             : }
    6774             : 
    6775             : void
    6776           0 : nsHttpChannel::ContinueBeginConnect()
    6777             : {
    6778           0 :     nsresult rv = ContinueBeginConnectWithResult();
    6779           0 :     if (NS_FAILED(rv)) {
    6780           0 :         CloseCacheEntry(false);
    6781           0 :         Unused << AsyncAbort(rv);
    6782             :     }
    6783           0 : }
    6784             : 
    6785             : //-----------------------------------------------------------------------------
    6786             : // HttpChannel::nsIClassOfService
    6787             : //-----------------------------------------------------------------------------
    6788             : 
    6789             : void
    6790           3 : nsHttpChannel::OnClassOfServiceUpdated()
    6791             : {
    6792           3 :     if (mTransaction) {
    6793           0 :         gHttpHandler->UpdateClassOfServiceOnTransaction(mTransaction, mClassOfService);
    6794             :     }
    6795           3 : }
    6796             : 
    6797             : NS_IMETHODIMP
    6798           3 : nsHttpChannel::SetClassFlags(uint32_t inFlags)
    6799             : {
    6800           3 :     uint32_t previous = mClassOfService;
    6801           3 :     mClassOfService = inFlags;
    6802           3 :     if (previous != mClassOfService) {
    6803           3 :         OnClassOfServiceUpdated();
    6804             :     }
    6805           3 :     return NS_OK;
    6806             : }
    6807             : 
    6808             : NS_IMETHODIMP
    6809           0 : nsHttpChannel::AddClassFlags(uint32_t inFlags)
    6810             : {
    6811           0 :     uint32_t previous = mClassOfService;
    6812           0 :     mClassOfService |= inFlags;
    6813           0 :     if (previous != mClassOfService) {
    6814           0 :         OnClassOfServiceUpdated();
    6815             :     }
    6816           0 :     return NS_OK;
    6817             : }
    6818             : 
    6819             : NS_IMETHODIMP
    6820           0 : nsHttpChannel::ClearClassFlags(uint32_t inFlags)
    6821             : {
    6822           0 :     uint32_t previous = mClassOfService;
    6823           0 :     mClassOfService &= ~inFlags;
    6824           0 :     if (previous != mClassOfService) {
    6825           0 :         OnClassOfServiceUpdated();
    6826             :     }
    6827           0 :     return NS_OK;
    6828             : }
    6829             : 
    6830             : //-----------------------------------------------------------------------------
    6831             : // nsHttpChannel::nsIProtocolProxyCallback
    6832             : //-----------------------------------------------------------------------------
    6833             : 
    6834             : NS_IMETHODIMP
    6835           6 : nsHttpChannel::OnProxyAvailable(nsICancelable *request, nsIChannel *channel,
    6836             :                                 nsIProxyInfo *pi, nsresult status)
    6837             : {
    6838           6 :     LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%" PRIx32
    6839             :          " mStatus=%" PRIx32 "]\n",
    6840             :          this, pi, static_cast<uint32_t>(status),
    6841             :          static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
    6842           6 :     mProxyRequest = nullptr;
    6843             : 
    6844             :     nsresult rv;
    6845             : 
    6846             :     // If status is a failure code, then it means that we failed to resolve
    6847             :     // proxy info.  That is a non-fatal error assuming it wasn't because the
    6848             :     // request was canceled.  We just failover to DIRECT when proxy resolution
    6849             :     // fails (failure can mean that the PAC URL could not be loaded).
    6850             : 
    6851           6 :     if (NS_SUCCEEDED(status))
    6852           6 :         mProxyInfo = pi;
    6853             : 
    6854           6 :     if (!gHttpHandler->Active()) {
    6855           0 :         LOG(("nsHttpChannel::OnProxyAvailable [this=%p] "
    6856             :              "Handler no longer active.\n", this));
    6857           0 :         rv = NS_ERROR_NOT_AVAILABLE;
    6858             :     }
    6859             :     else {
    6860           6 :         rv = BeginConnect();
    6861             :     }
    6862             : 
    6863           6 :     if (NS_FAILED(rv)) {
    6864           0 :         CloseCacheEntry(false);
    6865           0 :         Unused << AsyncAbort(rv);
    6866             :     }
    6867           6 :     return rv;
    6868             : }
    6869             : 
    6870             : //-----------------------------------------------------------------------------
    6871             : // nsHttpChannel::nsIProxiedChannel
    6872             : //-----------------------------------------------------------------------------
    6873             : 
    6874             : NS_IMETHODIMP
    6875          12 : nsHttpChannel::GetProxyInfo(nsIProxyInfo **result)
    6876             : {
    6877          12 :     if (!mConnectionInfo)
    6878           0 :         *result = mProxyInfo;
    6879             :     else
    6880          12 :         *result = mConnectionInfo->ProxyInfo();
    6881          12 :     NS_IF_ADDREF(*result);
    6882          12 :     return NS_OK;
    6883             : }
    6884             : 
    6885             : //-----------------------------------------------------------------------------
    6886             : // nsHttpChannel::nsITimedChannel
    6887             : //-----------------------------------------------------------------------------
    6888             : 
    6889             : NS_IMETHODIMP
    6890           3 : nsHttpChannel::GetDomainLookupStart(TimeStamp* _retval) {
    6891           3 :     if (mTransaction)
    6892           0 :         *_retval = mTransaction->GetDomainLookupStart();
    6893             :     else
    6894           3 :         *_retval = mTransactionTimings.domainLookupStart;
    6895           3 :     return NS_OK;
    6896             : }
    6897             : 
    6898             : NS_IMETHODIMP
    6899           3 : nsHttpChannel::GetDomainLookupEnd(TimeStamp* _retval) {
    6900           3 :     if (mTransaction)
    6901           0 :         *_retval = mTransaction->GetDomainLookupEnd();
    6902             :     else
    6903           3 :         *_retval = mTransactionTimings.domainLookupEnd;
    6904           3 :     return NS_OK;
    6905             : }
    6906             : 
    6907             : NS_IMETHODIMP
    6908           3 : nsHttpChannel::GetConnectStart(TimeStamp* _retval) {
    6909           3 :     if (mTransaction)
    6910           0 :         *_retval = mTransaction->GetConnectStart();
    6911             :     else
    6912           3 :         *_retval = mTransactionTimings.connectStart;
    6913           3 :     return NS_OK;
    6914             : }
    6915             : 
    6916             : NS_IMETHODIMP
    6917           3 : nsHttpChannel::GetConnectEnd(TimeStamp* _retval) {
    6918           3 :     if (mTransaction)
    6919           0 :         *_retval = mTransaction->GetConnectEnd();
    6920             :     else
    6921           3 :         *_retval = mTransactionTimings.connectEnd;
    6922           3 :     return NS_OK;
    6923             : }
    6924             : 
    6925             : NS_IMETHODIMP
    6926           3 : nsHttpChannel::GetRequestStart(TimeStamp* _retval) {
    6927           3 :     if (mTransaction)
    6928           0 :         *_retval = mTransaction->GetRequestStart();
    6929             :     else
    6930           3 :         *_retval = mTransactionTimings.requestStart;
    6931           3 :     return NS_OK;
    6932             : }
    6933             : 
    6934             : NS_IMETHODIMP
    6935           3 : nsHttpChannel::GetResponseStart(TimeStamp* _retval) {
    6936           3 :     if (mTransaction)
    6937           0 :         *_retval = mTransaction->GetResponseStart();
    6938             :     else
    6939           3 :         *_retval = mTransactionTimings.responseStart;
    6940           3 :     return NS_OK;
    6941             : }
    6942             : 
    6943             : NS_IMETHODIMP
    6944           3 : nsHttpChannel::GetResponseEnd(TimeStamp* _retval) {
    6945           3 :     if (mTransaction)
    6946           0 :         *_retval = mTransaction->GetResponseEnd();
    6947             :     else
    6948           3 :         *_retval = mTransactionTimings.responseEnd;
    6949           3 :     return NS_OK;
    6950             : }
    6951             : 
    6952             : //-----------------------------------------------------------------------------
    6953             : // nsHttpChannel::nsIHttpAuthenticableChannel
    6954             : //-----------------------------------------------------------------------------
    6955             : 
    6956             : NS_IMETHODIMP
    6957           6 : nsHttpChannel::GetIsSSL(bool *aIsSSL)
    6958             : {
    6959             :     // this attribute is really misnamed - it wants to know if
    6960             :     // https:// is being used. SSL might be used to cover http://
    6961             :     // in some circumstances (proxies, http/2, etc..)
    6962           6 :     return mURI->SchemeIs("https", aIsSSL);
    6963             : }
    6964             : 
    6965             : NS_IMETHODIMP
    6966           0 : nsHttpChannel::GetProxyMethodIsConnect(bool *aProxyMethodIsConnect)
    6967             : {
    6968           0 :     *aProxyMethodIsConnect = mConnectionInfo->UsingConnect();
    6969           0 :     return NS_OK;
    6970             : }
    6971             : 
    6972             : NS_IMETHODIMP
    6973           0 : nsHttpChannel::GetServerResponseHeader(nsACString &value)
    6974             : {
    6975           0 :     if (!mResponseHead)
    6976           0 :         return NS_ERROR_NOT_AVAILABLE;
    6977           0 :     return mResponseHead->GetHeader(nsHttp::Server, value);
    6978             : }
    6979             : 
    6980             : NS_IMETHODIMP
    6981           0 : nsHttpChannel::GetProxyChallenges(nsACString &value)
    6982             : {
    6983           0 :     if (!mResponseHead)
    6984           0 :         return NS_ERROR_UNEXPECTED;
    6985           0 :     return mResponseHead->GetHeader(nsHttp::Proxy_Authenticate, value);
    6986             : }
    6987             : 
    6988             : NS_IMETHODIMP
    6989           0 : nsHttpChannel::GetWWWChallenges(nsACString &value)
    6990             : {
    6991           0 :     if (!mResponseHead)
    6992           0 :         return NS_ERROR_UNEXPECTED;
    6993           0 :     return mResponseHead->GetHeader(nsHttp::WWW_Authenticate, value);
    6994             : }
    6995             : 
    6996             : NS_IMETHODIMP
    6997           0 : nsHttpChannel::SetProxyCredentials(const nsACString &value)
    6998             : {
    6999           0 :     return mRequestHead.SetHeader(nsHttp::Proxy_Authorization, value);
    7000             : }
    7001             : 
    7002             : NS_IMETHODIMP
    7003           0 : nsHttpChannel::SetWWWCredentials(const nsACString &value)
    7004             : {
    7005             :     // This method is called when various browser initiated authorization
    7006             :     // code sets the credentials.  We need to flag this header as the
    7007             :     // "browser default" so it does not show up in the ServiceWorker
    7008             :     // FetchEvent.  This may actually get called more than once, though,
    7009             :     // so we clear the header first since "default" headers are not
    7010             :     // allowed to overwrite normally.
    7011           0 :     Unused << mRequestHead.ClearHeader(nsHttp::Authorization);
    7012           0 :     return mRequestHead.SetHeader(nsHttp::Authorization, value, false,
    7013           0 :                                   nsHttpHeaderArray::eVarietyRequestDefault);
    7014             : }
    7015             : 
    7016             : //-----------------------------------------------------------------------------
    7017             : // Methods that nsIHttpAuthenticableChannel dupes from other IDLs, which we
    7018             : // get from HttpBaseChannel, must be explicitly forwarded, because C++ sucks.
    7019             : //
    7020             : 
    7021             : NS_IMETHODIMP
    7022          40 : nsHttpChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
    7023             : {
    7024          40 :     return HttpBaseChannel::GetLoadFlags(aLoadFlags);
    7025             : }
    7026             : 
    7027             : NS_IMETHODIMP
    7028          35 : nsHttpChannel::GetURI(nsIURI **aURI)
    7029             : {
    7030          35 :     return HttpBaseChannel::GetURI(aURI);
    7031             : }
    7032             : 
    7033             : NS_IMETHODIMP
    7034          53 : nsHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
    7035             : {
    7036          53 :     return HttpBaseChannel::GetNotificationCallbacks(aCallbacks);
    7037             : }
    7038             : 
    7039             : NS_IMETHODIMP
    7040          18 : nsHttpChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
    7041             : {
    7042          18 :     return HttpBaseChannel::GetLoadGroup(aLoadGroup);
    7043             : }
    7044             : 
    7045             : NS_IMETHODIMP
    7046           1 : nsHttpChannel::GetRequestMethod(nsACString& aMethod)
    7047             : {
    7048           1 :     return HttpBaseChannel::GetRequestMethod(aMethod);
    7049             : }
    7050             : 
    7051             : //-----------------------------------------------------------------------------
    7052             : // nsHttpChannel::nsIRequestObserver
    7053             : //-----------------------------------------------------------------------------
    7054             : 
    7055             : NS_IMETHODIMP
    7056           6 : nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
    7057             : {
    7058             :     nsresult rv;
    7059             : 
    7060          12 :     AUTO_PROFILER_LABEL("nsHttpChannel::OnStartRequest", NETWORK);
    7061             : 
    7062           6 :     if (!(mCanceled || NS_FAILED(mStatus)) && !WRONG_RACING_RESPONSE_SOURCE(request)) {
    7063             :         // capture the request's status, so our consumers will know ASAP of any
    7064             :         // connection failures, etc - bug 93581
    7065             :         nsresult status;
    7066           6 :         request->GetStatus(&status);
    7067           6 :         mStatus = status;
    7068             :     }
    7069             : 
    7070           6 :     LOG(("nsHttpChannel::OnStartRequest [this=%p request=%p status=%" PRIx32 "]\n",
    7071             :          this, request, static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
    7072             : 
    7073           6 :     if (mRaceCacheWithNetwork) {
    7074           0 :         LOG(("  racingNetAndCache - mFirstResponseSource:%d fromCache:%d fromNet:%d\n",
    7075             :              mFirstResponseSource, request == mCachePump, request == mTransactionPump));
    7076           0 :         if (mFirstResponseSource == RESPONSE_PENDING) {
    7077             :             // When the cache wins mFirstResponseSource is set to RESPONSE_FROM_CACHE
    7078             :             // earlier in ReadFromCache, so this must be a response from the network.
    7079           0 :             MOZ_ASSERT(request == mTransactionPump);
    7080           0 :             LOG(("  First response from network\n"));
    7081           0 :             mFirstResponseSource = RESPONSE_FROM_NETWORK;
    7082           0 :             mAvailableCachedAltDataType.Truncate();
    7083           0 :         } else if (WRONG_RACING_RESPONSE_SOURCE(request)) {
    7084           0 :             LOG(("  Early return when racing. This response not needed."));
    7085           0 :             return NS_OK;
    7086             :         }
    7087             :     }
    7088             : 
    7089             :     // Make sure things are what we expect them to be...
    7090           6 :     MOZ_ASSERT(request == mCachePump || request == mTransactionPump,
    7091             :                "Unexpected request");
    7092             : 
    7093           6 :     MOZ_ASSERT(mRaceCacheWithNetwork || !(mTransactionPump && mCachePump) || mCachedContentIsPartial,
    7094             :                "If we have both pumps, the cache content must be partial");
    7095             : 
    7096           6 :     mAfterOnStartRequestBegun = true;
    7097           6 :     mOnStartRequestTimestamp = TimeStamp::Now();
    7098             : 
    7099           6 :     Telemetry::Accumulate(Telemetry::HTTP_ONSTART_SUSPEND_TOTAL_TIME,
    7100           6 :                           mSuspendTotalTime);
    7101             : 
    7102           6 :     if (!mSecurityInfo && !mCachePump && mTransaction) {
    7103             :         // grab the security info from the connection object; the transaction
    7104             :         // is guaranteed to own a reference to the connection.
    7105           3 :         mSecurityInfo = mTransaction->SecurityInfo();
    7106             :     }
    7107             : 
    7108             :     // don't enter this block if we're reading from the cache...
    7109           6 :     if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) {
    7110             :         // mTransactionPump doesn't hit OnInputStreamReady and call this until
    7111             :         // all of the response headers have been acquired, so we can take ownership
    7112             :         // of them from the transaction.
    7113           3 :         mResponseHead = mTransaction->TakeResponseHead();
    7114             :         // the response head may be null if the transaction was cancelled.  in
    7115             :         // which case we just need to call OnStartRequest/OnStopRequest.
    7116           3 :         if (mResponseHead)
    7117           3 :             return ProcessResponse();
    7118             : 
    7119           0 :         NS_WARNING("No response head in OnStartRequest");
    7120             :     }
    7121             : 
    7122             :     // cache file could be deleted on our behalf, it could contain errors or
    7123             :     // it failed to allocate memory, reload from network here.
    7124           3 :     if (mCacheEntry && mCachePump && RECOVER_FROM_CACHE_FILE_ERROR(mStatus)) {
    7125           0 :         LOG(("  cache file error, reloading from server"));
    7126           0 :         mCacheEntry->AsyncDoom(nullptr);
    7127           0 :         rv = StartRedirectChannelToURI(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
    7128           0 :         if (NS_SUCCEEDED(rv))
    7129           0 :             return NS_OK;
    7130             :     }
    7131             : 
    7132             :     // avoid crashing if mListener happens to be null...
    7133           3 :     if (!mListener) {
    7134           0 :         NS_NOTREACHED("mListener is null");
    7135           0 :         return NS_OK;
    7136             :     }
    7137             : 
    7138             :     // before we start any content load, check for redirectTo being called
    7139             :     // this code is executed mainly before we start load from the cache
    7140           3 :     if (mAPIRedirectToURI && !mCanceled) {
    7141           0 :         nsAutoCString redirectToSpec;
    7142           0 :         mAPIRedirectToURI->GetAsciiSpec(redirectToSpec);
    7143           0 :         LOG(("  redirectTo called with uri=%s", redirectToSpec.BeginReading()));
    7144             : 
    7145           0 :         MOZ_ASSERT(!mOnStartRequestCalled);
    7146             : 
    7147           0 :         nsCOMPtr<nsIURI> redirectTo;
    7148           0 :         mAPIRedirectToURI.swap(redirectTo);
    7149             : 
    7150           0 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
    7151           0 :         rv = StartRedirectChannelToURI(redirectTo, nsIChannelEventSink::REDIRECT_TEMPORARY);
    7152           0 :         if (NS_SUCCEEDED(rv)) {
    7153           0 :             return NS_OK;
    7154             :         }
    7155           0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest1);
    7156             :     }
    7157             : 
    7158             :     // Hack: ContinueOnStartRequest1 uses NS_OK to detect successful redirects,
    7159             :     // so we distinguish this codepath (a non-redirect that's processing
    7160             :     // normally) by passing in a bogus error code.
    7161           3 :     return ContinueOnStartRequest1(NS_BINDING_FAILED);
    7162             : }
    7163             : 
    7164             : nsresult
    7165           3 : nsHttpChannel::ContinueOnStartRequest1(nsresult result)
    7166             : {
    7167           3 :     if (NS_SUCCEEDED(result)) {
    7168             :         // Redirect has passed through, we don't want to go on with this
    7169             :         // channel.  It will now be canceled by the redirect handling code
    7170             :         // that called this function.
    7171           0 :         return NS_OK;
    7172             :     }
    7173             : 
    7174             :     // on proxy errors, try to failover
    7175           3 :     if (mConnectionInfo->ProxyInfo() &&
    7176           0 :        (mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
    7177           0 :         mStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
    7178           0 :         mStatus == NS_ERROR_NET_TIMEOUT)) {
    7179             : 
    7180           0 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2);
    7181           0 :         if (NS_SUCCEEDED(ProxyFailover()))
    7182           0 :             return NS_OK;
    7183           0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2);
    7184             :     }
    7185             : 
    7186             :     // Hack: ContinueOnStartRequest2 uses NS_OK to detect successful redirects,
    7187             :     // so we distinguish this codepath (a non-redirect that's processing
    7188             :     // normally) by passing in a bogus error code.
    7189           3 :     return ContinueOnStartRequest2(NS_BINDING_FAILED);
    7190             : }
    7191             : 
    7192             : nsresult
    7193           3 : nsHttpChannel::ContinueOnStartRequest2(nsresult result)
    7194             : {
    7195           3 :     if (NS_SUCCEEDED(result)) {
    7196             :         // Redirect has passed through, we don't want to go on with this
    7197             :         // channel.  It will now be canceled by the redirect handling code
    7198             :         // that called this function.
    7199           0 :         return NS_OK;
    7200             :     }
    7201             : 
    7202             :     // on other request errors, try to fall back
    7203           3 :     if (NS_FAILED(mStatus)) {
    7204           0 :         PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
    7205             :         bool waitingForRedirectCallback;
    7206           0 :         Unused << ProcessFallback(&waitingForRedirectCallback);
    7207           0 :         if (waitingForRedirectCallback)
    7208           0 :             return NS_OK;
    7209           0 :         PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
    7210             :     }
    7211             : 
    7212           3 :     return ContinueOnStartRequest3(NS_OK);
    7213             : }
    7214             : 
    7215             : nsresult
    7216           3 : nsHttpChannel::ContinueOnStartRequest3(nsresult result)
    7217             : {
    7218           3 :     LOG(("nsHttpChannel::ContinueOnStartRequest3 [this=%p]", this));
    7219             : 
    7220           3 :     if (mFallingBack)
    7221           0 :         return NS_OK;
    7222             : 
    7223           3 :     return CallOnStartRequest();
    7224             : }
    7225             : 
    7226             : NS_IMETHODIMP
    7227           6 : nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
    7228             : {
    7229          12 :     AUTO_PROFILER_LABEL("nsHttpChannel::OnStopRequest", NETWORK);
    7230             : 
    7231           6 :     LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%" PRIx32 "]\n",
    7232             :          this, request, static_cast<uint32_t>(status)));
    7233             : 
    7234           6 :     LOG(("OnStopRequest %p requestFromCache: %d mFirstResponseSource: %d\n", this, request == mCachePump, mFirstResponseSource));
    7235             : 
    7236           6 :     MOZ_ASSERT(NS_IsMainThread(),
    7237             :                "OnStopRequest should only be called from the main thread");
    7238             : 
    7239           6 :     if (WRONG_RACING_RESPONSE_SOURCE(request)) {
    7240           0 :         return NS_OK;
    7241             :     }
    7242             : 
    7243           6 :     if (NS_FAILED(status)) {
    7244           2 :         ProcessSecurityReport(status);
    7245             :     }
    7246             : 
    7247             :     // If this load failed because of a security error, it may be because we
    7248             :     // are in a captive portal - trigger an async check to make sure.
    7249           6 :     int32_t nsprError = -1 * NS_ERROR_GET_CODE(status);
    7250           6 :     if (mozilla::psm::IsNSSErrorCode(nsprError)) {
    7251           0 :         gIOService->RecheckCaptivePortal();
    7252             :     }
    7253             : 
    7254           6 :     if (mTimingEnabled && request == mCachePump) {
    7255           3 :         mCacheReadEnd = TimeStamp::Now();
    7256             : 
    7257           3 :         ReportNetVSCacheTelemetry();
    7258             :     }
    7259             : 
    7260             :     // allow content to be cached if it was loaded successfully (bug #482935)
    7261           6 :     bool contentComplete = NS_SUCCEEDED(status);
    7262             : 
    7263             :     // honor the cancelation status even if the underlying transaction completed.
    7264           6 :     if (mCanceled || NS_FAILED(mStatus))
    7265           1 :         status = mStatus;
    7266             : 
    7267           6 :     if (mCachedContentIsPartial) {
    7268           0 :         if (NS_SUCCEEDED(status)) {
    7269             :             // mTransactionPump should be suspended
    7270           0 :             MOZ_ASSERT(request != mTransactionPump,
    7271             :                        "byte-range transaction finished prematurely");
    7272             : 
    7273           0 :             if (request == mCachePump) {
    7274             :                 bool streamDone;
    7275           0 :                 status = OnDoneReadingPartialCacheEntry(&streamDone);
    7276           0 :                 if (NS_SUCCEEDED(status) && !streamDone)
    7277           0 :                     return status;
    7278             :                 // otherwise, fall through and fire OnStopRequest...
    7279             :             }
    7280           0 :             else if (request == mTransactionPump) {
    7281           0 :                 MOZ_ASSERT(mConcurrentCacheAccess);
    7282             :             }
    7283             :             else
    7284           0 :                 NS_NOTREACHED("unexpected request");
    7285             :         }
    7286             :         // Do not to leave the transaction in a suspended state in error cases.
    7287           0 :         if (NS_FAILED(status) && mTransaction) {
    7288           0 :             nsresult rv = gHttpHandler->CancelTransaction(mTransaction, status);
    7289           0 :             if (NS_FAILED(rv)) {
    7290           0 :                 LOG(("  CancelTransaction failed (%08x)",
    7291             :                      static_cast<uint32_t>(rv)));
    7292             :             }
    7293             :         }
    7294             :     }
    7295             : 
    7296          12 :     nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
    7297           6 :     if (conv) {
    7298           0 :         conv->GetDecodedDataLength(&mDecodedBodySize);
    7299             :     }
    7300             : 
    7301           6 :     bool isFromNet = request == mTransactionPump;
    7302             : 
    7303           6 :     if (mTransaction) {
    7304             :         // determine if we should call DoAuthRetry
    7305           3 :         bool authRetry = mAuthRetryPending && NS_SUCCEEDED(status);
    7306           3 :         mStronglyFramed = mTransaction->ResponseIsComplete();
    7307           3 :         LOG(("nsHttpChannel %p has a strongly framed transaction: %d",
    7308             :              this, mStronglyFramed));
    7309             : 
    7310             :         //
    7311             :         // grab references to connection in case we need to retry an
    7312             :         // authentication request over it or use it for an upgrade
    7313             :         // to another protocol.
    7314             :         //
    7315             :         // this code relies on the code in nsHttpTransaction::Close, which
    7316             :         // tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to
    7317             :         // keep the connection around after the transaction is finished.
    7318             :         //
    7319           6 :         RefPtr<nsAHttpConnection> conn;
    7320           3 :         LOG(("  authRetry=%d, sticky conn cap=%d", authRetry, mCaps & NS_HTTP_STICKY_CONNECTION));
    7321             :         // We must check caps for stickinness also on the transaction because it
    7322             :         // might have been updated by the transaction itself during inspection of
    7323             :         // the reposnse headers yet on the socket thread (found connection based
    7324             :         // auth schema).
    7325           3 :         if (authRetry && (mCaps & NS_HTTP_STICKY_CONNECTION ||
    7326           0 :                           mTransaction->Caps() & NS_HTTP_STICKY_CONNECTION)) {
    7327           0 :             conn = mTransaction->GetConnectionReference();
    7328           0 :             LOG(("  transaction %p provides connection %p", mTransaction.get(), conn.get()));
    7329             :             // This is so far a workaround to fix leak when reusing unpersistent
    7330             :             // connection for authentication retry. See bug 459620 comment 4
    7331             :             // for details.
    7332           0 :             if (conn && !conn->IsPersistent()) {
    7333           0 :                 LOG(("  connection is not persistent, not reusing it"));
    7334           0 :                 conn = nullptr;
    7335             :             }
    7336             :         }
    7337             : 
    7338           6 :         RefPtr<nsAHttpConnection> stickyConn;
    7339           3 :         if (mCaps & NS_HTTP_STICKY_CONNECTION) {
    7340           0 :             stickyConn = mTransaction->GetConnectionReference();
    7341             :         }
    7342             : 
    7343           3 :         mTransferSize = mTransaction->GetTransferSize();
    7344             : 
    7345             :         // If we are using the transaction to serve content, we also save the
    7346             :         // time since async open in the cache entry so we can compare telemetry
    7347             :         // between cache and net response.
    7348             :         // Do not store the time of conditional requests because even if we
    7349             :         // fetch the data from the server, the time includes loading of the old
    7350             :         // cache entry which would skew the network load time.
    7351          11 :         if (request == mTransactionPump && mCacheEntry && !mDidReval &&
    7352           4 :             !mCustomConditionalRequest &&
    7353           6 :             !mAsyncOpenTime.IsNull() && !mOnStartRequestTimestamp.IsNull()) {
    7354           1 :             uint64_t onStartTime = (mOnStartRequestTimestamp - mAsyncOpenTime).ToMilliseconds();
    7355           1 :             uint64_t onStopTime = (TimeStamp::Now() - mAsyncOpenTime).ToMilliseconds();
    7356           1 :             Unused << mCacheEntry->SetNetworkTimes(onStartTime, onStopTime);
    7357             :         }
    7358             : 
    7359             :         // at this point, we're done with the transaction
    7360           3 :         mTransactionTimings = mTransaction->Timings();
    7361           3 :         mTransaction = nullptr;
    7362           3 :         mTransactionPump = nullptr;
    7363             : 
    7364             :         // We no longer need the dns prefetch object
    7365           7 :         if (mDNSPrefetch && mDNSPrefetch->TimingsValid()
    7366           0 :             && !mTransactionTimings.requestStart.IsNull()
    7367           0 :             && !mTransactionTimings.connectStart.IsNull()
    7368           3 :             && mDNSPrefetch->EndTimestamp() <= mTransactionTimings.connectStart) {
    7369             :             // We only need the domainLookup timestamps when not using a
    7370             :             // persistent connection, meaning if the endTimestamp < connectStart
    7371           0 :             mTransactionTimings.domainLookupStart =
    7372           0 :                 mDNSPrefetch->StartTimestamp();
    7373           0 :             mTransactionTimings.domainLookupEnd =
    7374           0 :                 mDNSPrefetch->EndTimestamp();
    7375             :         }
    7376           3 :         mDNSPrefetch = nullptr;
    7377             : 
    7378             :         // handle auth retry...
    7379           3 :         if (authRetry) {
    7380           0 :             mAuthRetryPending = false;
    7381           0 :             status = DoAuthRetry(conn);
    7382           0 :             if (NS_SUCCEEDED(status))
    7383           0 :                 return NS_OK;
    7384             :         }
    7385             : 
    7386             :         // If DoAuthRetry failed, or if we have been cancelled since showing
    7387             :         // the auth. dialog, then we need to send OnStartRequest now
    7388           3 :         if (authRetry || (mAuthRetryPending && NS_FAILED(status))) {
    7389           0 :             MOZ_ASSERT(NS_FAILED(status), "should have a failure code here");
    7390             :             // NOTE: since we have a failure status, we can ignore the return
    7391             :             // value from onStartRequest.
    7392           0 :             if (mListener) {
    7393           0 :                 MOZ_ASSERT(!mOnStartRequestCalled,
    7394             :                            "We should not call OnStartRequest twice.");
    7395           0 :                 mListener->OnStartRequest(this, mListenerContext);
    7396           0 :                 mOnStartRequestCalled = true;
    7397             :             } else {
    7398           0 :                 NS_WARNING("OnStartRequest skipped because of null listener");
    7399             :             }
    7400             :         }
    7401             : 
    7402             :         // if this transaction has been replaced, then bail.
    7403           3 :         if (mTransactionReplaced) {
    7404           0 :             LOG(("Transaction replaced\n"));
    7405             :             // This was just the network check for a 304 response.
    7406           0 :             mFirstResponseSource = RESPONSE_PENDING;
    7407           0 :             return NS_OK;
    7408             :         }
    7409             : 
    7410           6 :         if (mUpgradeProtocolCallback && stickyConn &&
    7411           3 :             mResponseHead && mResponseHead->Status() == 101) {
    7412             :             nsresult rv =
    7413           0 :                 gHttpHandler->ConnMgr()->CompleteUpgrade(stickyConn,
    7414           0 :                                                          mUpgradeProtocolCallback);
    7415           0 :             if (NS_FAILED(rv)) {
    7416           0 :                 LOG(("  CompleteUpgrade failed with %08x",
    7417             :                      static_cast<uint32_t>(rv)));
    7418             :             }
    7419             :         }
    7420             :     }
    7421             : 
    7422             :     // HTTP_CHANNEL_DISPOSITION TELEMETRY
    7423             :     enum ChannelDisposition
    7424             :     {
    7425             :         kHttpCanceled = 0,
    7426             :         kHttpDisk = 1,
    7427             :         kHttpNetOK = 2,
    7428             :         kHttpNetEarlyFail = 3,
    7429             :         kHttpNetLateFail = 4,
    7430             :         kHttpsCanceled = 8,
    7431             :         kHttpsDisk = 9,
    7432             :         kHttpsNetOK = 10,
    7433             :         kHttpsNetEarlyFail = 11,
    7434             :         kHttpsNetLateFail = 12
    7435           6 :     } chanDisposition = kHttpCanceled;
    7436             : 
    7437             :     // HTTP 0.9 is more likely to be an error than really 0.9, so count it that way
    7438           6 :     if (mCanceled) {
    7439           1 :         chanDisposition  = kHttpCanceled;
    7440           5 :     } else if (!mUsedNetwork) {
    7441           2 :         chanDisposition = kHttpDisk;
    7442           8 :     } else if (NS_SUCCEEDED(status) &&
    7443           5 :                mResponseHead &&
    7444           2 :                mResponseHead->Version() != NS_HTTP_VERSION_0_9) {
    7445           2 :         chanDisposition = kHttpNetOK;
    7446           1 :     } else if (!mTransferSize) {
    7447           0 :         chanDisposition = kHttpNetEarlyFail;
    7448             :     } else {
    7449           1 :         chanDisposition = kHttpNetLateFail;
    7450             :     }
    7451           6 :     if (IsHTTPS()) {
    7452             :         // shift http to https disposition enums
    7453           0 :         chanDisposition = static_cast<ChannelDisposition>(chanDisposition + kHttpsCanceled);
    7454             :     }
    7455           6 :     LOG(("  nsHttpChannel::OnStopRequest ChannelDisposition %d\n", chanDisposition));
    7456           6 :     Telemetry::Accumulate(Telemetry::HTTP_CHANNEL_DISPOSITION, chanDisposition);
    7457             : 
    7458             :     // if needed, check cache entry has all data we expect
    7459          20 :     if (mCacheEntry && mCachePump &&
    7460           9 :         mConcurrentCacheAccess && contentComplete) {
    7461             :         int64_t size, contentLength;
    7462           0 :         nsresult rv = CheckPartial(mCacheEntry, &size, &contentLength);
    7463           0 :         if (NS_SUCCEEDED(rv)) {
    7464           0 :             if (size == int64_t(-1)) {
    7465             :                 // mayhemer TODO - we have to restart read from cache here at the size offset
    7466           0 :                 MOZ_ASSERT(false);
    7467             :                 LOG(("  cache entry write is still in progress, but we just "
    7468             :                      "finished reading the cache entry"));
    7469             :             }
    7470           0 :             else if (contentLength != int64_t(-1) && contentLength != size) {
    7471           0 :                 LOG(("  concurrent cache entry write has been interrupted"));
    7472           0 :                 mCachedResponseHead = Move(mResponseHead);
    7473             :                 // Ignore zero partial length because we also want to resume when
    7474             :                 // no data at all has been read from the cache.
    7475           0 :                 rv = MaybeSetupByteRangeRequest(size, contentLength, true);
    7476           0 :                 if (NS_SUCCEEDED(rv) && mIsPartialRequest) {
    7477             :                     // Prevent read from cache again
    7478           0 :                     mCachedContentIsValid = 0;
    7479           0 :                     mCachedContentIsPartial = 1;
    7480             : 
    7481             :                     // Perform the range request
    7482           0 :                     rv = ContinueConnect();
    7483           0 :                     if (NS_SUCCEEDED(rv)) {
    7484           0 :                         LOG(("  performing range request"));
    7485           0 :                         mCachePump = nullptr;
    7486           0 :                         return NS_OK;
    7487             :                     } else {
    7488           0 :                         LOG(("  but range request perform failed 0x%08" PRIx32,
    7489             :                              static_cast<uint32_t>(rv)));
    7490           0 :                         status = NS_ERROR_NET_INTERRUPT;
    7491             :                     }
    7492             :                 }
    7493             :                 else {
    7494           0 :                     LOG(("  but range request setup failed rv=0x%08" PRIx32 ", failing load",
    7495             :                          static_cast<uint32_t>(rv)));
    7496             :                 }
    7497             :             }
    7498             :         }
    7499             :     }
    7500             : 
    7501           6 :     mIsPending = false;
    7502           6 :     mStatus = status;
    7503             : 
    7504             :     // perform any final cache operations before we close the cache entry.
    7505           6 :     if (mCacheEntry && mRequestTimeInitialized) {
    7506             :         bool writeAccess;
    7507             :         // New implementation just returns value of the !mCacheEntryIsReadOnly flag passed in.
    7508             :         // Old implementation checks on nsICache::ACCESS_WRITE flag.
    7509           2 :         mCacheEntry->HasWriteAccess(!mCacheEntryIsReadOnly, &writeAccess);
    7510           2 :         if (writeAccess) {
    7511           2 :             nsresult rv = FinalizeCacheEntry();
    7512           2 :             if (NS_FAILED(rv)) {
    7513           0 :                 LOG(("FinalizeCacheEntry failed (%08x)",
    7514             :                      static_cast<uint32_t>(rv)));
    7515             :             }
    7516             :         }
    7517             :     }
    7518             : 
    7519           6 :     ReportRcwnStats(isFromNet);
    7520             : 
    7521             :     // Register entry to the Performance resource timing
    7522           6 :     mozilla::dom::Performance* documentPerformance = GetPerformance();
    7523           6 :     if (documentPerformance) {
    7524           0 :         documentPerformance->AddEntry(this, this);
    7525             :     }
    7526             : 
    7527           6 :     if (mListener) {
    7528           6 :         LOG(("  calling OnStopRequest\n"));
    7529           6 :         MOZ_ASSERT(mOnStartRequestCalled,
    7530             :                    "OnStartRequest should be called before OnStopRequest");
    7531           6 :         MOZ_ASSERT(!mOnStopRequestCalled,
    7532             :                    "We should not call OnStopRequest twice");
    7533           6 :         mListener->OnStopRequest(this, mListenerContext, status);
    7534           6 :         mOnStopRequestCalled = true;
    7535             :     }
    7536             : 
    7537             :     // If a preferred alt-data type was set, this signals the consumer is
    7538             :     // interested in reading and/or writing the alt-data representation.
    7539             :     // We need to hold a reference to the cache entry in case the listener calls
    7540             :     // openAlternativeOutputStream() after CloseCacheEntry() clears mCacheEntry.
    7541           6 :     if (!mPreferredCachedAltDataType.IsEmpty()) {
    7542           0 :         mAltDataCacheEntry = mCacheEntry;
    7543             :     }
    7544             : 
    7545           6 :     CloseCacheEntry(!contentComplete);
    7546             : 
    7547           6 :     if (mOfflineCacheEntry)
    7548           0 :         CloseOfflineCacheEntry();
    7549             : 
    7550           6 :     if (mLoadGroup)
    7551           1 :         mLoadGroup->RemoveRequest(this, nullptr, status);
    7552             : 
    7553             :     // We don't need this info anymore
    7554           6 :     CleanRedirectCacheChainIfNecessary();
    7555             : 
    7556           6 :     ReleaseListeners();
    7557             : 
    7558           6 :     return NS_OK;
    7559             : }
    7560             : 
    7561             : //-----------------------------------------------------------------------------
    7562             : // nsHttpChannel::nsIStreamListener
    7563             : //-----------------------------------------------------------------------------
    7564             : 
    7565           3 : class OnTransportStatusAsyncEvent : public Runnable
    7566             : {
    7567             : public:
    7568           1 :   OnTransportStatusAsyncEvent(nsITransportEventSink* aEventSink,
    7569             :                               nsresult aTransportStatus,
    7570             :                               int64_t aProgress,
    7571             :                               int64_t aProgressMax)
    7572           1 :     : Runnable("net::OnTransportStatusAsyncEvent")
    7573             :     , mEventSink(aEventSink)
    7574             :     , mTransportStatus(aTransportStatus)
    7575             :     , mProgress(aProgress)
    7576           1 :     , mProgressMax(aProgressMax)
    7577             :   {
    7578           1 :     MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be created on main thread");
    7579           1 :     }
    7580             : 
    7581           1 :     NS_IMETHOD Run() override
    7582             :     {
    7583           1 :         MOZ_ASSERT(NS_IsMainThread(), "Should run on main thread");
    7584           1 :         if (mEventSink) {
    7585           2 :             mEventSink->OnTransportStatus(nullptr, mTransportStatus,
    7586           2 :                                           mProgress, mProgressMax);
    7587             :         }
    7588           1 :         return NS_OK;
    7589             :     }
    7590             : private:
    7591             :     nsCOMPtr<nsITransportEventSink> mEventSink;
    7592             :     nsresult mTransportStatus;
    7593             :     int64_t mProgress;
    7594             :     int64_t mProgressMax;
    7595             : };
    7596             : 
    7597             : NS_IMETHODIMP
    7598           5 : nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
    7599             :                                nsIInputStream *input,
    7600             :                                uint64_t offset, uint32_t count)
    7601             : {
    7602             :     nsresult rv;
    7603          10 :     AUTO_PROFILER_LABEL("nsHttpChannel::OnDataAvailable", NETWORK);
    7604             : 
    7605           5 :     LOG(("nsHttpChannel::OnDataAvailable [this=%p request=%p offset=%" PRIu64
    7606             :          " count=%" PRIu32 "]\n",
    7607             :         this, request, offset, count));
    7608             : 
    7609           5 :     LOG(("  requestFromCache: %d mFirstResponseSource: %d\n",
    7610             :         request == mCachePump, mFirstResponseSource));
    7611             : 
    7612             :     // don't send out OnDataAvailable notifications if we've been canceled.
    7613           5 :     if (mCanceled)
    7614           0 :         return mStatus;
    7615             : 
    7616          10 :     if (mAuthRetryPending || WRONG_RACING_RESPONSE_SOURCE(request) ||
    7617           7 :         (request == mTransactionPump && mTransactionReplaced)) {
    7618             :         uint32_t n;
    7619           0 :         return input->ReadSegments(NS_DiscardSegment, nullptr, count, &n);
    7620             :     }
    7621             : 
    7622           5 :     MOZ_ASSERT(mResponseHead, "No response head in ODA!!");
    7623             : 
    7624           5 :     MOZ_ASSERT(!(mCachedContentIsPartial && (request == mTransactionPump)),
    7625             :                "transaction pump not suspended");
    7626             : 
    7627           5 :     mIsReadingFromCache = (request == mCachePump);
    7628             : 
    7629           5 :     if (mListener) {
    7630             :         //
    7631             :         // synthesize transport progress event.  we do this here since we want
    7632             :         // to delay OnProgress events until we start streaming data.  this is
    7633             :         // crucially important since it impacts the lock icon (see bug 240053).
    7634             :         //
    7635             :         nsresult transportStatus;
    7636           5 :         if (request == mCachePump)
    7637           3 :             transportStatus = NS_NET_STATUS_READING;
    7638             :         else
    7639           2 :             transportStatus = NS_NET_STATUS_RECEIVING_FROM;
    7640             : 
    7641             :         // mResponseHead may reference new or cached headers, but either way it
    7642             :         // holds our best estimate of the total content length.  Even in the case
    7643             :         // of a byte range request, the content length stored in the cached
    7644             :         // response headers is what we want to use here.
    7645             : 
    7646           5 :         int64_t progressMax = -1;
    7647           5 :         rv = GetContentLength(&progressMax);
    7648           5 :         if (NS_FAILED(rv)) {
    7649           0 :             NS_WARNING("GetContentLength failed");
    7650             :         }
    7651           5 :         int64_t progress = mLogicalOffset + count;
    7652             : 
    7653           5 :         if ((progress > progressMax) && (progressMax != -1)) {
    7654             :             NS_WARNING("unexpected progress values - "
    7655           0 :                        "is server exceeding content length?");
    7656             :         }
    7657             : 
    7658             :         // make sure params are in range for js
    7659           5 :         if (!InScriptableRange(progressMax)) {
    7660           0 :             progressMax = -1;
    7661             :         }
    7662             : 
    7663           5 :         if (!InScriptableRange(progress)) {
    7664           0 :             progress = -1;
    7665             :         }
    7666             : 
    7667           5 :         if (NS_IsMainThread()) {
    7668           4 :             OnTransportStatus(nullptr, transportStatus, progress, progressMax);
    7669             :         } else {
    7670             :             rv = NS_DispatchToMainThread(
    7671             :                 new OnTransportStatusAsyncEvent(this, transportStatus,
    7672           1 :                                                 progress, progressMax));
    7673           1 :             NS_ENSURE_SUCCESS(rv, rv);
    7674             :         }
    7675             : 
    7676             :         //
    7677             :         // we have to manually keep the logical offset of the stream up-to-date.
    7678             :         // we cannot depend solely on the offset provided, since we may have
    7679             :         // already streamed some data from another source (see, for example,
    7680             :         // OnDoneReadingPartialCacheEntry).
    7681             :         //
    7682           5 :         int64_t offsetBefore = 0;
    7683          10 :         nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(input);
    7684           5 :         if (seekable && NS_FAILED(seekable->Tell(&offsetBefore))) {
    7685           0 :             seekable = nullptr;
    7686             :         }
    7687             : 
    7688          10 :         nsresult rv =  mListener->OnDataAvailable(this,
    7689             :                                                   mListenerContext,
    7690             :                                                   input,
    7691             :                                                   mLogicalOffset,
    7692          10 :                                                   count);
    7693           5 :         if (NS_SUCCEEDED(rv)) {
    7694             :             // by contract mListener must read all of "count" bytes, but
    7695             :             // nsInputStreamPump is tolerant to seekable streams that violate that
    7696             :             // and it will redeliver incompletely read data. So we need to do
    7697             :             // the same thing when updating the progress counter to stay in sync.
    7698             :             int64_t offsetAfter, delta;
    7699           4 :             if (seekable && NS_SUCCEEDED(seekable->Tell(&offsetAfter))) {
    7700           2 :                 delta = offsetAfter - offsetBefore;
    7701           2 :                 if (delta != count) {
    7702           0 :                     count = delta;
    7703             : 
    7704           0 :                     NS_WARNING("Listener OnDataAvailable contract violation");
    7705             :                     nsCOMPtr<nsIConsoleService> consoleService =
    7706           0 :                         do_GetService(NS_CONSOLESERVICE_CONTRACTID);
    7707             :                     nsAutoString message
    7708           0 :                         (NS_LITERAL_STRING(
    7709           0 :                         "http channel Listener OnDataAvailable contract violation"));
    7710           0 :                     if (consoleService) {
    7711           0 :                         consoleService->LogStringMessage(message.get());
    7712             :                     }
    7713             :                 }
    7714             :             }
    7715           4 :             mLogicalOffset += count;
    7716             :         }
    7717             : 
    7718           5 :         return rv;
    7719             :     }
    7720             : 
    7721           0 :     return NS_ERROR_ABORT;
    7722             : }
    7723             : 
    7724             : //-----------------------------------------------------------------------------
    7725             : // nsHttpChannel::nsIThreadRetargetableRequest
    7726             : //-----------------------------------------------------------------------------
    7727             : 
    7728             : NS_IMETHODIMP
    7729           1 : nsHttpChannel::RetargetDeliveryTo(nsIEventTarget* aNewTarget)
    7730             : {
    7731           1 :     MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only");
    7732             : 
    7733           1 :     NS_ENSURE_ARG(aNewTarget);
    7734           1 :     if (aNewTarget->IsOnCurrentThread()) {
    7735           0 :         NS_WARNING("Retargeting delivery to same thread");
    7736           0 :         return NS_OK;
    7737             :     }
    7738           1 :     if (!mTransactionPump && !mCachePump) {
    7739           0 :         LOG(("nsHttpChannel::RetargetDeliveryTo %p %p no pump available\n",
    7740             :              this, aNewTarget));
    7741           0 :         return NS_ERROR_NOT_AVAILABLE;
    7742             :     }
    7743             : 
    7744           1 :     nsresult rv = NS_OK;
    7745             :     // If both cache pump and transaction pump exist, we're probably dealing
    7746             :     // with partially cached content. So, we must be able to retarget both.
    7747           2 :     nsCOMPtr<nsIThreadRetargetableRequest> retargetableCachePump;
    7748           2 :     nsCOMPtr<nsIThreadRetargetableRequest> retargetableTransactionPump;
    7749           1 :     if (mCachePump) {
    7750           1 :         retargetableCachePump = do_QueryObject(mCachePump);
    7751             :         // nsInputStreamPump should implement this interface.
    7752           1 :         MOZ_ASSERT(retargetableCachePump);
    7753           1 :         rv = retargetableCachePump->RetargetDeliveryTo(aNewTarget);
    7754             :     }
    7755           1 :     if (NS_SUCCEEDED(rv) && mTransactionPump) {
    7756           0 :         retargetableTransactionPump = do_QueryObject(mTransactionPump);
    7757             :         // nsInputStreamPump should implement this interface.
    7758           0 :         MOZ_ASSERT(retargetableTransactionPump);
    7759           0 :         rv = retargetableTransactionPump->RetargetDeliveryTo(aNewTarget);
    7760             : 
    7761             :         // If retarget fails for transaction pump, we must restore mCachePump.
    7762           0 :         if (NS_FAILED(rv) && retargetableCachePump) {
    7763           0 :             nsCOMPtr<nsIEventTarget> main = GetMainThreadEventTarget();
    7764           0 :             NS_ENSURE_TRUE(main, NS_ERROR_UNEXPECTED);
    7765           0 :             rv = retargetableCachePump->RetargetDeliveryTo(main);
    7766             :         }
    7767             :     }
    7768           1 :     return rv;
    7769             : }
    7770             : 
    7771             : //-----------------------------------------------------------------------------
    7772             : // nsHttpChannel::nsThreadRetargetableStreamListener
    7773             : //-----------------------------------------------------------------------------
    7774             : 
    7775             : NS_IMETHODIMP
    7776           1 : nsHttpChannel::CheckListenerChain()
    7777             : {
    7778           1 :     NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!");
    7779           1 :     nsresult rv = NS_OK;
    7780             :     nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
    7781           2 :         do_QueryInterface(mListener, &rv);
    7782           1 :     if (retargetableListener) {
    7783           1 :         rv = retargetableListener->CheckListenerChain();
    7784             :     }
    7785           2 :     return rv;
    7786             : }
    7787             : 
    7788             : //-----------------------------------------------------------------------------
    7789             : // nsHttpChannel::nsITransportEventSink
    7790             : //-----------------------------------------------------------------------------
    7791             : 
    7792             : NS_IMETHODIMP
    7793          21 : nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
    7794             :                                  int64_t progress, int64_t progressMax)
    7795             : {
    7796          21 :     MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread only");
    7797             :     // cache the progress sink so we don't have to query for it each time.
    7798          21 :     if (!mProgressSink)
    7799          16 :         GetCallback(mProgressSink);
    7800             : 
    7801          21 :     if (status == NS_NET_STATUS_CONNECTED_TO ||
    7802             :         status == NS_NET_STATUS_WAITING_FOR) {
    7803           6 :         if (mTransaction) {
    7804           6 :             mTransaction->GetNetworkAddresses(mSelfAddr, mPeerAddr);
    7805             :         } else {
    7806             :             nsCOMPtr<nsISocketTransport> socketTransport =
    7807           0 :                 do_QueryInterface(trans);
    7808           0 :             if (socketTransport) {
    7809           0 :                 socketTransport->GetSelfAddr(&mSelfAddr);
    7810           0 :                 socketTransport->GetPeerAddr(&mPeerAddr);
    7811             :             }
    7812             :         }
    7813             :     }
    7814             : 
    7815             :     // block socket status event after Cancel or OnStopRequest has been called.
    7816          21 :     if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending) {
    7817           9 :         LOG(("sending progress%s notification [this=%p status=%" PRIx32
    7818             :              " progress=%" PRId64 "/%" PRId64 "]\n",
    7819             :             (mLoadFlags & LOAD_BACKGROUND)? "" : " and status",
    7820             :              this, static_cast<uint32_t>(status), progress, progressMax));
    7821             : 
    7822           9 :         if (!(mLoadFlags & LOAD_BACKGROUND)) {
    7823          18 :             nsAutoCString host;
    7824           9 :             mURI->GetHost(host);
    7825          18 :             mProgressSink->OnStatus(this, nullptr, status,
    7826          18 :                                     NS_ConvertUTF8toUTF16(host).get());
    7827             :         }
    7828             : 
    7829           9 :         if (progress > 0) {
    7830           4 :             if ((progress > progressMax) && (progressMax != -1)) {
    7831           0 :                 NS_WARNING("unexpected progress values");
    7832             :             }
    7833             : 
    7834             :             // Try to get mProgressSink if it was nulled out during OnStatus.
    7835           4 :             if (!mProgressSink) {
    7836           0 :                 GetCallback(mProgressSink);
    7837             :             }
    7838           4 :             if (mProgressSink) {
    7839           4 :                 mProgressSink->OnProgress(this, nullptr, progress, progressMax);
    7840             :             }
    7841             :         }
    7842             :     }
    7843             : 
    7844          21 :     return NS_OK;
    7845             : }
    7846             : 
    7847             : //-----------------------------------------------------------------------------
    7848             : // nsHttpChannel::nsICacheInfoChannel
    7849             : //-----------------------------------------------------------------------------
    7850             : 
    7851             : NS_IMETHODIMP
    7852           7 : nsHttpChannel::IsFromCache(bool *value)
    7853             : {
    7854           7 :     if (!mIsPending)
    7855           0 :         return NS_ERROR_NOT_AVAILABLE;
    7856             : 
    7857           7 :     if (!mRaceCacheWithNetwork) {
    7858             :         // return false if reading a partial cache entry; the data isn't
    7859             :         // entirely from the cache!
    7860          21 :         *value = (mCachePump || (mLoadFlags & LOAD_ONLY_IF_MODIFIED)) &&
    7861          11 :                   mCachedContentIsValid && !mCachedContentIsPartial;
    7862           7 :         return NS_OK;
    7863             :     }
    7864             : 
    7865             :     // If we are racing network and cache (or skipping the cache)
    7866             :     // we just return the first response source.
    7867           0 :     *value = mFirstResponseSource == RESPONSE_FROM_CACHE;
    7868             : 
    7869           0 :     return NS_OK;
    7870             : }
    7871             : 
    7872             : NS_IMETHODIMP
    7873           3 : nsHttpChannel::GetCacheTokenFetchCount(int32_t *_retval)
    7874             : {
    7875           3 :     NS_ENSURE_ARG_POINTER(_retval);
    7876           6 :     nsCOMPtr<nsICacheEntry> cacheEntry = mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
    7877           3 :     if (!cacheEntry) {
    7878           0 :         return NS_ERROR_NOT_AVAILABLE;
    7879             :     }
    7880             : 
    7881           3 :     return cacheEntry->GetFetchCount(_retval);
    7882             : }
    7883             : 
    7884             : NS_IMETHODIMP
    7885           3 : nsHttpChannel::GetCacheTokenLastFetched(uint32_t *_retval)
    7886             : {
    7887           3 :     NS_ENSURE_ARG_POINTER(_retval);
    7888           6 :     nsCOMPtr<nsICacheEntry> cacheEntry = mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
    7889           3 :     if (!cacheEntry) {
    7890           0 :         return NS_ERROR_NOT_AVAILABLE;
    7891             :     }
    7892             : 
    7893           3 :     return cacheEntry->GetLastFetched(_retval);
    7894             : }
    7895             : 
    7896             : NS_IMETHODIMP
    7897           4 : nsHttpChannel::GetCacheTokenExpirationTime(uint32_t *_retval)
    7898             : {
    7899           4 :     NS_ENSURE_ARG_POINTER(_retval);
    7900           4 :     if (!mCacheEntry)
    7901           0 :         return NS_ERROR_NOT_AVAILABLE;
    7902             : 
    7903           4 :     return mCacheEntry->GetExpirationTime(_retval);
    7904             : }
    7905             : 
    7906             : NS_IMETHODIMP
    7907           3 : nsHttpChannel::GetCacheTokenCachedCharset(nsACString &_retval)
    7908             : {
    7909             :     nsresult rv;
    7910             : 
    7911           3 :     if (!mCacheEntry)
    7912           0 :         return NS_ERROR_NOT_AVAILABLE;
    7913             : 
    7914           6 :     nsXPIDLCString cachedCharset;
    7915           6 :     rv = mCacheEntry->GetMetaDataElement("charset",
    7916           6 :                                          getter_Copies(cachedCharset));
    7917           3 :     if (NS_SUCCEEDED(rv))
    7918           0 :         _retval = cachedCharset;
    7919             : 
    7920           3 :     return rv;
    7921             : }
    7922             : 
    7923             : NS_IMETHODIMP
    7924           0 : nsHttpChannel::SetCacheTokenCachedCharset(const nsACString &aCharset)
    7925             : {
    7926           0 :     if (!mCacheEntry)
    7927           0 :         return NS_ERROR_NOT_AVAILABLE;
    7928             : 
    7929           0 :     return mCacheEntry->SetMetaDataElement("charset",
    7930           0 :                                            PromiseFlatCString(aCharset).get());
    7931             : }
    7932             : 
    7933             : NS_IMETHODIMP
    7934           3 : nsHttpChannel::SetAllowStaleCacheContent(bool aAllowStaleCacheContent)
    7935             : {
    7936           3 :     LOG(("nsHttpChannel::SetAllowStaleCacheContent [this=%p, allow=%d]",
    7937             :          this, aAllowStaleCacheContent));
    7938           3 :     mAllowStaleCacheContent = aAllowStaleCacheContent;
    7939           3 :     return NS_OK;
    7940             : }
    7941             : NS_IMETHODIMP
    7942           0 : nsHttpChannel::GetAllowStaleCacheContent(bool *aAllowStaleCacheContent)
    7943             : {
    7944           0 :     NS_ENSURE_ARG(aAllowStaleCacheContent);
    7945           0 :     *aAllowStaleCacheContent = mAllowStaleCacheContent;
    7946           0 :     return NS_OK;
    7947             : }
    7948             : 
    7949             : NS_IMETHODIMP
    7950           3 : nsHttpChannel::PreferAlternativeDataType(const nsACString & aType)
    7951             : {
    7952           3 :     ENSURE_CALLED_BEFORE_ASYNC_OPEN();
    7953           3 :     mPreferredCachedAltDataType = aType;
    7954           3 :     return NS_OK;
    7955             : }
    7956             : 
    7957             : NS_IMETHODIMP
    7958           3 : nsHttpChannel::GetAlternativeDataType(nsACString & aType)
    7959             : {
    7960             :     // must be called during or after OnStartRequest
    7961           3 :     if (!mAfterOnStartRequestBegun) {
    7962           0 :         return NS_ERROR_NOT_AVAILABLE;
    7963             :     }
    7964           3 :     aType = mAvailableCachedAltDataType;
    7965           3 :     return NS_OK;
    7966             : }
    7967             : 
    7968             : NS_IMETHODIMP
    7969           0 : nsHttpChannel::OpenAlternativeOutputStream(const nsACString & type, nsIOutputStream * *_retval)
    7970             : {
    7971             :     // OnStopRequest will clear mCacheEntry, but we may use mAltDataCacheEntry
    7972             :     // if the consumer called PreferAlternativeDataType()
    7973           0 :     nsCOMPtr<nsICacheEntry> cacheEntry = mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
    7974           0 :     if (!cacheEntry) {
    7975           0 :         return NS_ERROR_NOT_AVAILABLE;
    7976             :     }
    7977           0 :     return cacheEntry->OpenAlternativeOutputStream(type, _retval);
    7978             : }
    7979             : 
    7980             : //-----------------------------------------------------------------------------
    7981             : // nsHttpChannel::nsICachingChannel
    7982             : //-----------------------------------------------------------------------------
    7983             : 
    7984             : NS_IMETHODIMP
    7985           3 : nsHttpChannel::GetCacheToken(nsISupports **token)
    7986             : {
    7987           3 :     NS_ENSURE_ARG_POINTER(token);
    7988           3 :     if (!mCacheEntry)
    7989           0 :         return NS_ERROR_NOT_AVAILABLE;
    7990           3 :     return CallQueryInterface(mCacheEntry, token);
    7991             : }
    7992             : 
    7993             : NS_IMETHODIMP
    7994           0 : nsHttpChannel::SetCacheToken(nsISupports *token)
    7995             : {
    7996           0 :     return NS_ERROR_NOT_IMPLEMENTED;
    7997             : }
    7998             : 
    7999             : NS_IMETHODIMP
    8000           0 : nsHttpChannel::GetOfflineCacheToken(nsISupports **token)
    8001             : {
    8002           0 :     NS_ENSURE_ARG_POINTER(token);
    8003           0 :     if (!mOfflineCacheEntry)
    8004           0 :         return NS_ERROR_NOT_AVAILABLE;
    8005           0 :     return CallQueryInterface(mOfflineCacheEntry, token);
    8006             : }
    8007             : 
    8008             : NS_IMETHODIMP
    8009           0 : nsHttpChannel::SetOfflineCacheToken(nsISupports *token)
    8010             : {
    8011           0 :     return NS_ERROR_NOT_IMPLEMENTED;
    8012             : }
    8013             : 
    8014             : NS_IMETHODIMP
    8015           3 : nsHttpChannel::GetCacheKey(nsISupports **key)
    8016             : {
    8017             :     nsresult rv;
    8018           3 :     NS_ENSURE_ARG_POINTER(key);
    8019             : 
    8020           3 :     LOG(("nsHttpChannel::GetCacheKey [this=%p]\n", this));
    8021             : 
    8022           3 :     *key = nullptr;
    8023             : 
    8024             :     nsCOMPtr<nsISupportsPRUint32> container =
    8025           6 :         do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
    8026             : 
    8027           3 :     if (!container)
    8028           0 :         return NS_ERROR_OUT_OF_MEMORY;
    8029             : 
    8030           3 :     rv = container->SetData(mPostID);
    8031           3 :     if (NS_FAILED(rv)) return rv;
    8032             : 
    8033           3 :     return CallQueryInterface(container.get(), key);
    8034             : }
    8035             : 
    8036             : NS_IMETHODIMP
    8037           3 : nsHttpChannel::SetCacheKey(nsISupports *key)
    8038             : {
    8039             :     nsresult rv;
    8040             : 
    8041           3 :     LOG(("nsHttpChannel::SetCacheKey [this=%p key=%p]\n", this, key));
    8042             : 
    8043           3 :     ENSURE_CALLED_BEFORE_CONNECT();
    8044             : 
    8045           3 :     if (!key)
    8046           0 :         mPostID = 0;
    8047             :     else {
    8048             :         // extract the post id
    8049           6 :         nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(key, &rv);
    8050           3 :         if (NS_FAILED(rv)) return rv;
    8051             : 
    8052           3 :         rv = container->GetData(&mPostID);
    8053           3 :         if (NS_FAILED(rv)) return rv;
    8054             :     }
    8055           3 :     return NS_OK;
    8056             : }
    8057             : 
    8058             : NS_IMETHODIMP
    8059           0 : nsHttpChannel::GetCacheOnlyMetadata(bool *aOnlyMetadata)
    8060             : {
    8061           0 :     NS_ENSURE_ARG(aOnlyMetadata);
    8062           0 :     *aOnlyMetadata = mCacheOnlyMetadata;
    8063           0 :     return NS_OK;
    8064             : }
    8065             : 
    8066             : NS_IMETHODIMP
    8067           0 : nsHttpChannel::SetCacheOnlyMetadata(bool aOnlyMetadata)
    8068             : {
    8069           0 :     LOG(("nsHttpChannel::SetCacheOnlyMetadata [this=%p only-metadata=%d]\n",
    8070             :         this, aOnlyMetadata));
    8071             : 
    8072           0 :     ENSURE_CALLED_BEFORE_ASYNC_OPEN();
    8073             : 
    8074           0 :     mCacheOnlyMetadata = aOnlyMetadata;
    8075           0 :     if (aOnlyMetadata) {
    8076           0 :         mLoadFlags |= LOAD_ONLY_IF_MODIFIED;
    8077             :     }
    8078             : 
    8079           0 :     return NS_OK;
    8080             : }
    8081             : 
    8082             : NS_IMETHODIMP
    8083           0 : nsHttpChannel::GetPin(bool *aPin)
    8084             : {
    8085           0 :     NS_ENSURE_ARG(aPin);
    8086           0 :     *aPin = mPinCacheContent;
    8087           0 :     return NS_OK;
    8088             : }
    8089             : 
    8090             : NS_IMETHODIMP
    8091           0 : nsHttpChannel::SetPin(bool aPin)
    8092             : {
    8093           0 :     LOG(("nsHttpChannel::SetPin [this=%p pin=%d]\n",
    8094             :         this, aPin));
    8095             : 
    8096           0 :     ENSURE_CALLED_BEFORE_CONNECT();
    8097             : 
    8098           0 :     mPinCacheContent = aPin;
    8099           0 :     return NS_OK;
    8100             : }
    8101             : 
    8102             : NS_IMETHODIMP
    8103           0 : nsHttpChannel::ForceCacheEntryValidFor(uint32_t aSecondsToTheFuture)
    8104             : {
    8105           0 :     if (!mCacheEntry) {
    8106           0 :         LOG(("nsHttpChannel::ForceCacheEntryValidFor found no cache entry "
    8107             :              "for this channel [this=%p].", this));
    8108             :     } else {
    8109           0 :         mCacheEntry->ForceValidFor(aSecondsToTheFuture);
    8110             : 
    8111           0 :         nsAutoCString key;
    8112           0 :         mCacheEntry->GetKey(key);
    8113             : 
    8114           0 :         LOG(("nsHttpChannel::ForceCacheEntryValidFor successfully forced valid "
    8115             :              "entry with key %s for %d seconds. [this=%p]", key.get(),
    8116             :              aSecondsToTheFuture, this));
    8117             :     }
    8118             : 
    8119           0 :     return NS_OK;
    8120             : }
    8121             : 
    8122             : //-----------------------------------------------------------------------------
    8123             : // nsHttpChannel::nsIResumableChannel
    8124             : //-----------------------------------------------------------------------------
    8125             : 
    8126             : NS_IMETHODIMP
    8127           0 : nsHttpChannel::ResumeAt(uint64_t aStartPos,
    8128             :                         const nsACString& aEntityID)
    8129             : {
    8130           0 :     LOG(("nsHttpChannel::ResumeAt [this=%p startPos=%" PRIu64 " id='%s']\n",
    8131             :          this, aStartPos, PromiseFlatCString(aEntityID).get()));
    8132           0 :     mEntityID = aEntityID;
    8133           0 :     mStartPos = aStartPos;
    8134           0 :     mResuming = true;
    8135           0 :     return NS_OK;
    8136             : }
    8137             : 
    8138             : nsresult
    8139           0 : nsHttpChannel::DoAuthRetry(nsAHttpConnection *conn)
    8140             : {
    8141           0 :     LOG(("nsHttpChannel::DoAuthRetry [this=%p]\n", this));
    8142             : 
    8143           0 :     MOZ_ASSERT(!mTransaction, "should not have a transaction");
    8144             :     nsresult rv;
    8145             : 
    8146             :     // toggle mIsPending to allow nsIObserver implementations to modify
    8147             :     // the request headers (bug 95044).
    8148           0 :     mIsPending = false;
    8149             : 
    8150             :     // fetch cookies, and add them to the request header.
    8151             :     // the server response could have included cookies that must be sent with
    8152             :     // this authentication attempt (bug 84794).
    8153             :     // TODO: save cookies from auth response and send them here (bug 572151).
    8154           0 :     AddCookiesToRequest();
    8155             : 
    8156             :     // notify "http-on-modify-request" observers
    8157           0 :     CallOnModifyRequestObservers();
    8158             : 
    8159           0 :     mIsPending = true;
    8160             : 
    8161             :     // get rid of the old response headers
    8162           0 :     mResponseHead = nullptr;
    8163             : 
    8164             :     // rewind the upload stream
    8165           0 :     if (mUploadStream) {
    8166           0 :         nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
    8167           0 :         if (seekable)
    8168           0 :             seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
    8169             :     }
    8170             : 
    8171             :     // always set sticky connection flag
    8172           0 :     mCaps |= NS_HTTP_STICKY_CONNECTION;
    8173             :     // and when needed, allow restart regardless the sticky flag
    8174           0 :     if (mAuthConnectionRestartable) {
    8175           0 :         LOG(("  connection made restartable"));
    8176           0 :         mCaps |= NS_HTTP_CONNECTION_RESTARTABLE;
    8177           0 :         mAuthConnectionRestartable = false;
    8178             :     } else {
    8179           0 :         LOG(("  connection made non-restartable"));
    8180           0 :         mCaps &= ~NS_HTTP_CONNECTION_RESTARTABLE;
    8181             :     }
    8182             : 
    8183             :     // and create a new one...
    8184           0 :     rv = SetupTransaction();
    8185           0 :     if (NS_FAILED(rv)) return rv;
    8186             : 
    8187             :     // transfer ownership of connection to transaction
    8188           0 :     if (conn)
    8189           0 :         mTransaction->SetConnection(conn);
    8190             : 
    8191           0 :     rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
    8192           0 :     if (NS_FAILED(rv)) return rv;
    8193             : 
    8194           0 :     rv = mTransactionPump->AsyncRead(this, nullptr);
    8195           0 :     if (NS_FAILED(rv)) return rv;
    8196             : 
    8197           0 :     uint32_t suspendCount = mSuspendCount;
    8198           0 :     while (suspendCount--)
    8199           0 :         mTransactionPump->Suspend();
    8200             : 
    8201           0 :     return NS_OK;
    8202             : }
    8203             : 
    8204             : //-----------------------------------------------------------------------------
    8205             : // nsHttpChannel::nsIApplicationCacheChannel
    8206             : //-----------------------------------------------------------------------------
    8207             : 
    8208             : NS_IMETHODIMP
    8209           0 : nsHttpChannel::GetApplicationCache(nsIApplicationCache **out)
    8210             : {
    8211           0 :     NS_IF_ADDREF(*out = mApplicationCache);
    8212           0 :     return NS_OK;
    8213             : }
    8214             : 
    8215             : NS_IMETHODIMP
    8216           0 : nsHttpChannel::SetApplicationCache(nsIApplicationCache *appCache)
    8217             : {
    8218           0 :     ENSURE_CALLED_BEFORE_CONNECT();
    8219             : 
    8220           0 :     mApplicationCache = appCache;
    8221           0 :     return NS_OK;
    8222             : }
    8223             : 
    8224             : NS_IMETHODIMP
    8225           0 : nsHttpChannel::GetApplicationCacheForWrite(nsIApplicationCache **out)
    8226             : {
    8227           0 :     NS_IF_ADDREF(*out = mApplicationCacheForWrite);
    8228           0 :     return NS_OK;
    8229             : }
    8230             : 
    8231             : NS_IMETHODIMP
    8232           0 : nsHttpChannel::SetApplicationCacheForWrite(nsIApplicationCache *appCache)
    8233             : {
    8234           0 :     ENSURE_CALLED_BEFORE_CONNECT();
    8235             : 
    8236           0 :     mApplicationCacheForWrite = appCache;
    8237           0 :     return NS_OK;
    8238             : }
    8239             : 
    8240             : NS_IMETHODIMP
    8241           4 : nsHttpChannel::GetLoadedFromApplicationCache(bool *aLoadedFromApplicationCache)
    8242             : {
    8243           4 :     *aLoadedFromApplicationCache = mLoadedFromApplicationCache;
    8244           4 :     return NS_OK;
    8245             : }
    8246             : 
    8247             : NS_IMETHODIMP
    8248           0 : nsHttpChannel::GetInheritApplicationCache(bool *aInherit)
    8249             : {
    8250           0 :     *aInherit = mInheritApplicationCache;
    8251           0 :     return NS_OK;
    8252             : }
    8253             : 
    8254             : NS_IMETHODIMP
    8255           3 : nsHttpChannel::SetInheritApplicationCache(bool aInherit)
    8256             : {
    8257           3 :     ENSURE_CALLED_BEFORE_CONNECT();
    8258             : 
    8259           3 :     mInheritApplicationCache = aInherit;
    8260           3 :     return NS_OK;
    8261             : }
    8262             : 
    8263             : NS_IMETHODIMP
    8264           0 : nsHttpChannel::GetChooseApplicationCache(bool *aChoose)
    8265             : {
    8266           0 :     *aChoose = mChooseApplicationCache;
    8267           0 :     return NS_OK;
    8268             : }
    8269             : 
    8270             : NS_IMETHODIMP
    8271           1 : nsHttpChannel::SetChooseApplicationCache(bool aChoose)
    8272             : {
    8273           1 :     ENSURE_CALLED_BEFORE_CONNECT();
    8274             : 
    8275           1 :     mChooseApplicationCache = aChoose;
    8276           1 :     return NS_OK;
    8277             : }
    8278             : 
    8279             : nsHttpChannel::OfflineCacheEntryAsForeignMarker*
    8280           0 : nsHttpChannel::GetOfflineCacheEntryAsForeignMarker()
    8281             : {
    8282           0 :     if (!mApplicationCache)
    8283           0 :         return nullptr;
    8284             : 
    8285           0 :     return new OfflineCacheEntryAsForeignMarker(mApplicationCache, mURI);
    8286             : }
    8287             : 
    8288             : nsresult
    8289           0 : nsHttpChannel::OfflineCacheEntryAsForeignMarker::MarkAsForeign()
    8290             : {
    8291             :     nsresult rv;
    8292             : 
    8293           0 :     nsCOMPtr<nsIURI> noRefURI;
    8294           0 :     rv = mCacheURI->CloneIgnoringRef(getter_AddRefs(noRefURI));
    8295           0 :     NS_ENSURE_SUCCESS(rv, rv);
    8296             : 
    8297           0 :     nsAutoCString spec;
    8298           0 :     rv = noRefURI->GetAsciiSpec(spec);
    8299           0 :     NS_ENSURE_SUCCESS(rv, rv);
    8300             : 
    8301           0 :     return mApplicationCache->MarkEntry(spec,
    8302           0 :                                         nsIApplicationCache::ITEM_FOREIGN);
    8303             : }
    8304             : 
    8305             : NS_IMETHODIMP
    8306           0 : nsHttpChannel::MarkOfflineCacheEntryAsForeign()
    8307             : {
    8308             :     nsresult rv;
    8309             : 
    8310             :     nsAutoPtr<OfflineCacheEntryAsForeignMarker> marker(
    8311           0 :         GetOfflineCacheEntryAsForeignMarker());
    8312             : 
    8313           0 :     if (!marker)
    8314           0 :         return NS_ERROR_NOT_AVAILABLE;
    8315             : 
    8316           0 :     rv = marker->MarkAsForeign();
    8317           0 :     NS_ENSURE_SUCCESS(rv, rv);
    8318             : 
    8319           0 :     return NS_OK;
    8320             : }
    8321             : 
    8322             : //-----------------------------------------------------------------------------
    8323             : // nsHttpChannel::nsIAsyncVerifyRedirectCallback
    8324             : //-----------------------------------------------------------------------------
    8325             : 
    8326             : nsresult
    8327           0 : nsHttpChannel::WaitForRedirectCallback()
    8328             : {
    8329             :     nsresult rv;
    8330           0 :     LOG(("nsHttpChannel::WaitForRedirectCallback [this=%p]\n", this));
    8331             : 
    8332           0 :     if (mTransactionPump) {
    8333           0 :         rv = mTransactionPump->Suspend();
    8334           0 :         NS_ENSURE_SUCCESS(rv, rv);
    8335             :     }
    8336           0 :     if (mCachePump) {
    8337           0 :         rv = mCachePump->Suspend();
    8338           0 :         if (NS_FAILED(rv) && mTransactionPump) {
    8339             : #ifdef DEBUG
    8340             :             nsresult resume =
    8341             : #endif
    8342           0 :             mTransactionPump->Resume();
    8343           0 :             MOZ_ASSERT(NS_SUCCEEDED(resume),
    8344             :                        "Failed to resume transaction pump");
    8345             :         }
    8346           0 :         NS_ENSURE_SUCCESS(rv, rv);
    8347             :     }
    8348             : 
    8349           0 :     mWaitingForRedirectCallback = true;
    8350           0 :     return NS_OK;
    8351             : }
    8352             : 
    8353             : NS_IMETHODIMP
    8354           0 : nsHttpChannel::OnRedirectVerifyCallback(nsresult result)
    8355             : {
    8356           0 :     LOG(("nsHttpChannel::OnRedirectVerifyCallback [this=%p] "
    8357             :          "result=%" PRIx32 " stack=%" PRIuSIZE " mWaitingForRedirectCallback=%u\n",
    8358             :          this, static_cast<uint32_t>(result), mRedirectFuncStack.Length(),
    8359             :          mWaitingForRedirectCallback));
    8360           0 :     MOZ_ASSERT(mWaitingForRedirectCallback,
    8361             :                "Someone forgot to call WaitForRedirectCallback() ?!");
    8362           0 :     mWaitingForRedirectCallback = false;
    8363             : 
    8364           0 :     if (mCanceled && NS_SUCCEEDED(result))
    8365           0 :         result = NS_BINDING_ABORTED;
    8366             : 
    8367           0 :     for (uint32_t i = mRedirectFuncStack.Length(); i > 0;) {
    8368           0 :         --i;
    8369             :         // Pop the last function pushed to the stack
    8370           0 :         nsContinueRedirectionFunc func = mRedirectFuncStack[i];
    8371           0 :         mRedirectFuncStack.RemoveElementAt(mRedirectFuncStack.Length() - 1);
    8372             : 
    8373             :         // Call it with the result we got from the callback or the deeper
    8374             :         // function call.
    8375           0 :         result = (this->*func)(result);
    8376             : 
    8377             :         // If a new function has been pushed to the stack and placed us in the
    8378             :         // waiting state, we need to break the chain and wait for the callback
    8379             :         // again.
    8380           0 :         if (mWaitingForRedirectCallback)
    8381           0 :             break;
    8382             :     }
    8383             : 
    8384           0 :     if (NS_FAILED(result) && !mCanceled) {
    8385             :         // First, cancel this channel if we are in failure state to set mStatus
    8386             :         // and let it be propagated to pumps.
    8387           0 :         Cancel(result);
    8388             :     }
    8389             : 
    8390           0 :     if (!mWaitingForRedirectCallback) {
    8391             :         // We are not waiting for the callback. At this moment we must release
    8392             :         // reference to the redirect target channel, otherwise we may leak.
    8393           0 :         mRedirectChannel = nullptr;
    8394             :     }
    8395             : 
    8396             :     // We always resume the pumps here. If all functions on stack have been
    8397             :     // called we need OnStopRequest to be triggered, and if we broke out of the
    8398             :     // loop above (and are thus waiting for a new callback) the suspension
    8399             :     // count must be balanced in the pumps.
    8400           0 :     if (mTransactionPump)
    8401           0 :         mTransactionPump->Resume();
    8402           0 :     if (mCachePump)
    8403           0 :         mCachePump->Resume();
    8404             : 
    8405           0 :     return result;
    8406             : }
    8407             : 
    8408             : void
    8409           2 : nsHttpChannel::PushRedirectAsyncFunc(nsContinueRedirectionFunc func)
    8410             : {
    8411           2 :     mRedirectFuncStack.AppendElement(func);
    8412           2 : }
    8413             : 
    8414             : void
    8415           2 : nsHttpChannel::PopRedirectAsyncFunc(nsContinueRedirectionFunc func)
    8416             : {
    8417           2 :     MOZ_ASSERT(func == mRedirectFuncStack[mRedirectFuncStack.Length() - 1],
    8418             :                "Trying to pop wrong method from redirect async stack!");
    8419             : 
    8420           2 :     mRedirectFuncStack.TruncateLength(mRedirectFuncStack.Length() - 1);
    8421           2 : }
    8422             : 
    8423             : //-----------------------------------------------------------------------------
    8424             : // nsIDNSListener functions
    8425             : //-----------------------------------------------------------------------------
    8426             : 
    8427             : NS_IMETHODIMP
    8428           0 : nsHttpChannel::OnLookupComplete(nsICancelable *request,
    8429             :                                 nsIDNSRecord  *rec,
    8430             :                                 nsresult       status)
    8431             : {
    8432           0 :     MOZ_ASSERT(NS_IsMainThread(), "Expecting DNS callback on main thread.");
    8433             : 
    8434           0 :     LOG(("nsHttpChannel::OnLookupComplete [this=%p] prefetch complete%s: "
    8435             :          "%s status[0x%" PRIx32 "]\n",
    8436             :          this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "",
    8437             :          NS_SUCCEEDED(status) ? "success" : "failure", static_cast<uint32_t>(status)));
    8438             : 
    8439             :     // We no longer need the dns prefetch object. Note: mDNSPrefetch could be
    8440             :     // validly null if OnStopRequest has already been called.
    8441             :     // We only need the domainLookup timestamps when not loading from cache
    8442           0 :     if (mDNSPrefetch && mDNSPrefetch->TimingsValid() && mTransaction) {
    8443           0 :         TimeStamp connectStart = mTransaction->GetConnectStart();
    8444           0 :         TimeStamp requestStart = mTransaction->GetRequestStart();
    8445             :         // We only set the domainLookup timestamps if we're not using a
    8446             :         // persistent connection.
    8447           0 :         if (requestStart.IsNull() && connectStart.IsNull()) {
    8448           0 :             mTransaction->SetDomainLookupStart(mDNSPrefetch->StartTimestamp());
    8449           0 :             mTransaction->SetDomainLookupEnd(mDNSPrefetch->EndTimestamp());
    8450             :         }
    8451             :     }
    8452           0 :     mDNSPrefetch = nullptr;
    8453             : 
    8454             :     // Unset DNS cache refresh if it was requested,
    8455           0 :     if (mCaps & NS_HTTP_REFRESH_DNS) {
    8456           0 :         mCaps &= ~NS_HTTP_REFRESH_DNS;
    8457           0 :         if (mTransaction) {
    8458           0 :             mTransaction->SetDNSWasRefreshed();
    8459             :         }
    8460             :     }
    8461             : 
    8462           0 :     return NS_OK;
    8463             : }
    8464             : 
    8465             : //-----------------------------------------------------------------------------
    8466             : // nsHttpChannel internal functions
    8467             : //-----------------------------------------------------------------------------
    8468             : 
    8469             : // Creates an URI to the given location using current URI for base and charset
    8470             : nsresult
    8471           0 : nsHttpChannel::CreateNewURI(const char *loc, nsIURI **newURI)
    8472             : {
    8473           0 :     nsCOMPtr<nsIIOService> ioService;
    8474           0 :     nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
    8475           0 :     if (NS_FAILED(rv)) return rv;
    8476             : 
    8477             :     // the new uri should inherit the origin charset of the current uri
    8478           0 :     nsAutoCString originCharset;
    8479           0 :     rv = mURI->GetOriginCharset(originCharset);
    8480           0 :     if (NS_FAILED(rv))
    8481           0 :         originCharset.Truncate();
    8482             : 
    8483           0 :     return ioService->NewURI(nsDependentCString(loc),
    8484             :                              originCharset.get(),
    8485             :                              mURI,
    8486           0 :                              newURI);
    8487             : }
    8488             : 
    8489             : void
    8490           3 : nsHttpChannel::MaybeInvalidateCacheEntryForSubsequentGet()
    8491             : {
    8492             :     // See RFC 2616 section 5.1.1. These are considered valid
    8493             :     // methods which DO NOT invalidate cache-entries for the
    8494             :     // referred resource. POST, PUT and DELETE as well as any
    8495             :     // other method not listed here will potentially invalidate
    8496             :     // any cached copy of the resource
    8497           8 :     if (mRequestHead.IsGet() || mRequestHead.IsOptions() ||
    8498           6 :         mRequestHead.IsHead() || mRequestHead.IsTrace() ||
    8499           1 :         mRequestHead.IsConnect()) {
    8500           2 :         return;
    8501             :     }
    8502             : 
    8503             :     // Invalidate the request-uri.
    8504           1 :     if (LOG_ENABLED()) {
    8505           0 :       nsAutoCString key;
    8506           0 :       mURI->GetAsciiSpec(key);
    8507           0 :       LOG(("MaybeInvalidateCacheEntryForSubsequentGet [this=%p uri=%s]\n",
    8508             :           this, key.get()));
    8509             :     }
    8510             : 
    8511           1 :     DoInvalidateCacheEntry(mURI);
    8512             : 
    8513             :     // Invalidate Location-header if set
    8514           2 :     nsAutoCString location;
    8515           1 :     Unused << mResponseHead->GetHeader(nsHttp::Location, location);
    8516           1 :     if (!location.IsEmpty()) {
    8517           0 :         LOG(("  Location-header=%s\n", location.get()));
    8518           0 :         InvalidateCacheEntryForLocation(location.get());
    8519             :     }
    8520             : 
    8521             :     // Invalidate Content-Location-header if set
    8522           1 :     Unused << mResponseHead->GetHeader(nsHttp::Content_Location, location);
    8523           1 :     if (!location.IsEmpty()) {
    8524           0 :         LOG(("  Content-Location-header=%s\n", location.get()));
    8525           0 :         InvalidateCacheEntryForLocation(location.get());
    8526             :     }
    8527             : }
    8528             : 
    8529             : void
    8530           0 : nsHttpChannel::InvalidateCacheEntryForLocation(const char *location)
    8531             : {
    8532           0 :     nsAutoCString tmpCacheKey, tmpSpec;
    8533           0 :     nsCOMPtr<nsIURI> resultingURI;
    8534           0 :     nsresult rv = CreateNewURI(location, getter_AddRefs(resultingURI));
    8535           0 :     if (NS_SUCCEEDED(rv) && HostPartIsTheSame(resultingURI)) {
    8536           0 :         DoInvalidateCacheEntry(resultingURI);
    8537             :     } else {
    8538           0 :         LOG(("  hosts not matching\n"));
    8539             :     }
    8540           0 : }
    8541             : 
    8542             : void
    8543           1 : nsHttpChannel::DoInvalidateCacheEntry(nsIURI* aURI)
    8544             : {
    8545             :     // NOTE:
    8546             :     // Following comments 24,32 and 33 in bug #327765, we only care about
    8547             :     // the cache in the protocol-handler, not the application cache.
    8548             :     // The logic below deviates from the original logic in OpenCacheEntry on
    8549             :     // one point by using only READ_ONLY access-policy. I think this is safe.
    8550             : 
    8551             :     nsresult rv;
    8552             : 
    8553           2 :     nsAutoCString key;
    8554           1 :     if (LOG_ENABLED()) {
    8555           0 :       aURI->GetAsciiSpec(key);
    8556             :     }
    8557             : 
    8558           1 :     LOG(("DoInvalidateCacheEntry [channel=%p key=%s]", this, key.get()));
    8559             : 
    8560           2 :     nsCOMPtr<nsICacheStorageService> cacheStorageService(services::GetCacheStorageService());
    8561           1 :     rv = cacheStorageService ? NS_OK : NS_ERROR_FAILURE;
    8562             : 
    8563           2 :     nsCOMPtr<nsICacheStorage> cacheStorage;
    8564           1 :     if (NS_SUCCEEDED(rv)) {
    8565           2 :         RefPtr<LoadContextInfo> info = GetLoadContextInfo(this);
    8566           1 :         rv = cacheStorageService->DiskCacheStorage(info, false, getter_AddRefs(cacheStorage));
    8567             :     }
    8568             : 
    8569           1 :     if (NS_SUCCEEDED(rv)) {
    8570           1 :         rv = cacheStorage->AsyncDoomURI(aURI, EmptyCString(), nullptr);
    8571             :     }
    8572             : 
    8573           1 :     LOG(("DoInvalidateCacheEntry [channel=%p key=%s rv=%d]", this, key.get(), int(rv)));
    8574           1 : }
    8575             : 
    8576             : void
    8577           3 : nsHttpChannel::AsyncOnExamineCachedResponse()
    8578             : {
    8579           3 :     gHttpHandler->OnExamineCachedResponse(this);
    8580             : 
    8581           3 : }
    8582             : 
    8583             : void
    8584          10 : nsHttpChannel::UpdateAggregateCallbacks()
    8585             : {
    8586          10 :     if (!mTransaction) {
    8587          10 :         return;
    8588             :     }
    8589           0 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
    8590           0 :     NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
    8591             :                                            GetCurrentThreadEventTarget(),
    8592           0 :                                            getter_AddRefs(callbacks));
    8593           0 :     mTransaction->SetSecurityCallbacks(callbacks);
    8594             : }
    8595             : 
    8596             : NS_IMETHODIMP
    8597           1 : nsHttpChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
    8598             : {
    8599           1 :     MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
    8600             : 
    8601           1 :     nsresult rv = HttpBaseChannel::SetLoadGroup(aLoadGroup);
    8602           1 :     if (NS_SUCCEEDED(rv)) {
    8603           1 :         UpdateAggregateCallbacks();
    8604             :     }
    8605           1 :     return rv;
    8606             : }
    8607             : 
    8608             : NS_IMETHODIMP
    8609           9 : nsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
    8610             : {
    8611           9 :     MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
    8612             : 
    8613           9 :     nsresult rv = HttpBaseChannel::SetNotificationCallbacks(aCallbacks);
    8614           9 :     if (NS_SUCCEEDED(rv)) {
    8615           9 :         UpdateAggregateCallbacks();
    8616             :     }
    8617           9 :     return rv;
    8618             : }
    8619             : 
    8620             : void
    8621           0 : nsHttpChannel::MarkIntercepted()
    8622             : {
    8623           0 :     mInterceptCache = INTERCEPTED;
    8624           0 : }
    8625             : 
    8626             : NS_IMETHODIMP
    8627          11 : nsHttpChannel::GetResponseSynthesized(bool* aSynthesized)
    8628             : {
    8629          11 :     NS_ENSURE_ARG_POINTER(aSynthesized);
    8630          11 :     *aSynthesized = (mInterceptCache == INTERCEPTED);
    8631          11 :     return NS_OK;
    8632             : }
    8633             : 
    8634             : bool
    8635          11 : nsHttpChannel::AwaitingCacheCallbacks()
    8636             : {
    8637          11 :     return mCacheEntriesToWaitFor != 0;
    8638             : }
    8639             : 
    8640             : void
    8641           0 : nsHttpChannel::SetPushedStream(Http2PushedStream *stream)
    8642             : {
    8643           0 :     MOZ_ASSERT(stream);
    8644           0 :     MOZ_ASSERT(!mPushedStream);
    8645           0 :     mPushedStream = stream;
    8646           0 : }
    8647             : 
    8648             : nsresult
    8649           0 : nsHttpChannel::OnPush(const nsACString &url, Http2PushedStream *pushedStream)
    8650             : {
    8651           0 :     MOZ_ASSERT(NS_IsMainThread());
    8652           0 :     LOG(("nsHttpChannel::OnPush [this=%p]\n", this));
    8653             : 
    8654           0 :     MOZ_ASSERT(mCaps & NS_HTTP_ONPUSH_LISTENER);
    8655           0 :     nsCOMPtr<nsIHttpPushListener> pushListener;
    8656           0 :     NS_QueryNotificationCallbacks(mCallbacks,
    8657             :                                   mLoadGroup,
    8658             :                                   NS_GET_IID(nsIHttpPushListener),
    8659           0 :                                   getter_AddRefs(pushListener));
    8660             : 
    8661           0 :     MOZ_ASSERT(pushListener);
    8662           0 :     if (!pushListener) {
    8663           0 :         LOG(("nsHttpChannel::OnPush [this=%p] notification callbacks do not "
    8664             :              "implement nsIHttpPushListener\n", this));
    8665           0 :         return NS_ERROR_UNEXPECTED;
    8666             :     }
    8667             : 
    8668           0 :     nsCOMPtr<nsIURI> pushResource;
    8669             :     nsresult rv;
    8670             : 
    8671             :     // Create a Channel for the Push Resource
    8672           0 :     rv = NS_NewURI(getter_AddRefs(pushResource), url);
    8673           0 :     if (NS_FAILED(rv)) {
    8674           0 :         return NS_ERROR_FAILURE;
    8675             :     }
    8676             : 
    8677           0 :     nsCOMPtr<nsIIOService> ioService;
    8678           0 :     rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
    8679           0 :     NS_ENSURE_SUCCESS(rv, rv);
    8680             : 
    8681           0 :     nsCOMPtr<nsIChannel> pushChannel;
    8682           0 :     rv = NS_NewChannelInternal(getter_AddRefs(pushChannel),
    8683             :                                pushResource,
    8684             :                                mLoadInfo,
    8685             :                                nullptr, // aLoadGroup
    8686             :                                nullptr, // aCallbacks
    8687             :                                nsIRequest::LOAD_NORMAL,
    8688           0 :                                ioService);
    8689           0 :     NS_ENSURE_SUCCESS(rv, rv);
    8690             : 
    8691           0 :     nsCOMPtr<nsIHttpChannel> pushHttpChannel = do_QueryInterface(pushChannel);
    8692           0 :     MOZ_ASSERT(pushHttpChannel);
    8693           0 :     if (!pushHttpChannel) {
    8694           0 :         return NS_ERROR_UNEXPECTED;
    8695             :     }
    8696             : 
    8697           0 :     RefPtr<nsHttpChannel> channel;
    8698           0 :     CallQueryInterface(pushHttpChannel, channel.StartAssignment());
    8699           0 :     MOZ_ASSERT(channel);
    8700           0 :     if (!channel) {
    8701           0 :         return NS_ERROR_UNEXPECTED;
    8702             :     }
    8703             : 
    8704             :     // new channel needs mrqeuesthead and headers from pushedStream
    8705           0 :     channel->mRequestHead.ParseHeaderSet(
    8706           0 :         pushedStream->GetRequestString().BeginWriting());
    8707             : 
    8708           0 :     channel->mLoadGroup = mLoadGroup;
    8709           0 :     channel->mLoadInfo = mLoadInfo;
    8710           0 :     channel->mCallbacks = mCallbacks;
    8711             : 
    8712             :     // Link the pushed stream with the new channel and call listener
    8713           0 :     channel->SetPushedStream(pushedStream);
    8714           0 :     rv = pushListener->OnPush(this, pushHttpChannel);
    8715           0 :     return rv;
    8716             : }
    8717             : 
    8718             : // static
    8719          11 : bool nsHttpChannel::IsRedirectStatus(uint32_t status)
    8720             : {
    8721             :     // 305 disabled as a security measure (see bug 187996).
    8722          11 :     return status == 300 || status == 301 || status == 302 || status == 303 ||
    8723          22 :            status == 307 || status == 308;
    8724             : }
    8725             : 
    8726             : void
    8727           0 : nsHttpChannel::SetCouldBeSynthesized()
    8728             : {
    8729           0 :   MOZ_ASSERT(!BypassServiceWorker());
    8730           0 :   mResponseCouldBeSynthesized = true;
    8731           0 : }
    8732             : 
    8733             : void
    8734           0 : nsHttpChannel::SetConnectionInfo(nsHttpConnectionInfo *aCI)
    8735             : {
    8736           0 :     mConnectionInfo = aCI ? aCI->Clone() : nullptr;
    8737           0 : }
    8738             : 
    8739             : NS_IMETHODIMP
    8740           0 : nsHttpChannel::OnPreflightSucceeded()
    8741             : {
    8742           0 :     MOZ_ASSERT(mRequireCORSPreflight, "Why did a preflight happen?");
    8743           0 :     mIsCorsPreflightDone = 1;
    8744           0 :     mPreflightChannel = nullptr;
    8745             : 
    8746           0 :     return ContinueConnect();
    8747             : }
    8748             : 
    8749             : NS_IMETHODIMP
    8750           0 : nsHttpChannel::OnPreflightFailed(nsresult aError)
    8751             : {
    8752           0 :     MOZ_ASSERT(mRequireCORSPreflight, "Why did a preflight happen?");
    8753           0 :     mIsCorsPreflightDone = 1;
    8754           0 :     mPreflightChannel = nullptr;
    8755             : 
    8756           0 :     CloseCacheEntry(false);
    8757           0 :     Unused << AsyncAbort(aError);
    8758           0 :     return NS_OK;
    8759             : }
    8760             : 
    8761             : //-----------------------------------------------------------------------------
    8762             : // nsIHstsPrimingCallback functions
    8763             : //-----------------------------------------------------------------------------
    8764             : 
    8765             : /*
    8766             :  * May be invoked synchronously if HSTS priming has already been performed
    8767             :  * for the host.
    8768             :  */
    8769             : nsresult
    8770           0 : nsHttpChannel::OnHSTSPrimingSucceeded(bool aCached)
    8771             : {
    8772             :     // If "security.mixed_content.use_hsts" is false, record the result of
    8773             :     // HSTS priming and block or proceed with the load as required by
    8774             :     // mixed-content blocking
    8775           0 :     bool wouldBlock = mLoadInfo->GetMixedContentWouldBlock();
    8776             :     // Clear out the HSTS priming flags on the LoadInfo to simplify the logic in
    8777             :     // TryHSTSPriming()
    8778           0 :     mLoadInfo->ClearHSTSPriming();
    8779             : 
    8780           0 :     if (nsMixedContentBlocker::sUseHSTS) {
    8781             :         // redirect the channel to HTTPS if the pref
    8782             :         // "security.mixed_content.use_hsts" is true
    8783           0 :         LOG(("HSTS Priming succeeded, redirecting to HTTPS [this=%p]", this));
    8784           0 :         Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
    8785             :                 (aCached) ? HSTSPrimingResult::eHSTS_PRIMING_CACHED_DO_UPGRADE :
    8786           0 :                             HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED);
    8787             :         // we have to record this upgrade here since we have already
    8788             :         // been through NS_ShouldSecureUpgrade
    8789           0 :         Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 3);
    8790           0 :         Telemetry::Accumulate(Telemetry::HSTS_UPGRADE_SOURCE, 2);
    8791           0 :         mLoadInfo->SetIsHSTSPrimingUpgrade(true);
    8792           0 :         return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
    8793             :     }
    8794             : 
    8795             :     // preserve the mixed-content-before-hsts order and block if required
    8796           0 :     if (wouldBlock) {
    8797           0 :         LOG(("HSTS Priming succeeded, blocking for mixed-content [this=%p]",
    8798             :                     this));
    8799             :         Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
    8800           0 :                               HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED_BLOCK);
    8801           0 :         CloseCacheEntry(false);
    8802           0 :         return AsyncAbort(NS_ERROR_CONTENT_BLOCKED);
    8803             :     }
    8804             : 
    8805           0 :     LOG(("HSTS Priming succeeded, loading insecure: [this=%p]", this));
    8806             :     Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
    8807           0 :                           HSTSPrimingResult::eHSTS_PRIMING_SUCCEEDED_HTTP);
    8808             : 
    8809             :     // log HTTP_SCHEME_UPGRADE telemetry
    8810           0 :     Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 0);
    8811             : 
    8812           0 :     nsresult rv = ContinueConnect();
    8813           0 :     if (NS_FAILED(rv)) {
    8814           0 :         CloseCacheEntry(false);
    8815           0 :         return AsyncAbort(rv);
    8816             :     }
    8817             : 
    8818           0 :     return NS_OK;
    8819             : }
    8820             : 
    8821             : /*
    8822             :  * May be invoked synchronously if HSTS priming has already been performed
    8823             :  * for the host.
    8824             :  */
    8825             : nsresult
    8826           0 : nsHttpChannel::OnHSTSPrimingFailed(nsresult aError, bool aCached)
    8827             : {
    8828           0 :     bool wouldBlock = mLoadInfo->GetMixedContentWouldBlock();
    8829             :     // Clear out the HSTS priming flags on the LoadInfo to simplify the logic in
    8830             :     // TryHSTSPriming()
    8831           0 :     mLoadInfo->ClearHSTSPriming();
    8832             : 
    8833           0 :     LOG(("HSTS Priming Failed [this=%p], %s the load", this,
    8834             :                 (wouldBlock) ? "blocking" : "allowing"));
    8835           0 :     if (aError == NS_ERROR_HSTS_PRIMING_TIMEOUT) {
    8836             :         // A priming request was sent, but timed out
    8837           0 :         Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
    8838             :                 (wouldBlock) ?  HSTSPrimingResult::eHSTS_PRIMING_TIMEOUT_BLOCK :
    8839           0 :                 HSTSPrimingResult::eHSTS_PRIMING_TIMEOUT_ACCEPT);
    8840           0 :     } else if (aCached) {
    8841             :         // Between the time we marked for priming and started the priming request,
    8842             :         // the host was found to not allow the upgrade, probably from another
    8843             :         // priming request.
    8844           0 :         Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
    8845             :                 (wouldBlock) ?  HSTSPrimingResult::eHSTS_PRIMING_CACHED_BLOCK :
    8846           0 :                 HSTSPrimingResult::eHSTS_PRIMING_CACHED_NO_UPGRADE);
    8847             :     } else {
    8848             :         // A priming request was sent, and no HSTS header was found that allows
    8849             :         // the upgrade.
    8850           0 :         Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_RESULT,
    8851             :                 (wouldBlock) ?  HSTSPrimingResult::eHSTS_PRIMING_FAILED_BLOCK :
    8852           0 :                 HSTSPrimingResult::eHSTS_PRIMING_FAILED_ACCEPT);
    8853             :     }
    8854             : 
    8855             :     // Don't visit again for at least
    8856             :     // security.mixed_content.hsts_priming_cache_timeout seconds.
    8857           0 :     nsISiteSecurityService* sss = gHttpHandler->GetSSService();
    8858           0 :     NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
    8859           0 :     OriginAttributes originAttributes;
    8860           0 :     NS_GetOriginAttributes(this, originAttributes);
    8861           0 :     nsresult rv = sss->CacheNegativeHSTSResult(mURI,
    8862           0 :             nsMixedContentBlocker::sHSTSPrimingCacheTimeout, originAttributes);
    8863           0 :     if (NS_FAILED(rv)) {
    8864           0 :         NS_ERROR("nsISiteSecurityService::CacheNegativeHSTSResult failed");
    8865             :     }
    8866             : 
    8867             :     // If we would block, go ahead and abort with the error provided
    8868           0 :     if (wouldBlock) {
    8869           0 :         CloseCacheEntry(false);
    8870           0 :         return AsyncAbort(aError);
    8871             :     }
    8872             : 
    8873             :     // log HTTP_SCHEME_UPGRADE telemetry
    8874           0 :     Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 0);
    8875             : 
    8876             :     // we can continue the load and the UI has been updated as mixed content
    8877           0 :     rv = ContinueConnect();
    8878           0 :     if (NS_FAILED(rv)) {
    8879           0 :         CloseCacheEntry(false);
    8880           0 :         return AsyncAbort(rv);
    8881             :     }
    8882             : 
    8883           0 :     return NS_OK;
    8884             : }
    8885             : 
    8886             : //-----------------------------------------------------------------------------
    8887             : // AChannelHasDivertableParentChannelAsListener internal functions
    8888             : //-----------------------------------------------------------------------------
    8889             : 
    8890             : NS_IMETHODIMP
    8891           0 : nsHttpChannel::MessageDiversionStarted(ADivertableParentChannel *aParentChannel)
    8892             : {
    8893           0 :   LOG(("nsHttpChannel::MessageDiversionStarted [this=%p]", this));
    8894           0 :   MOZ_ASSERT(!mParentChannel);
    8895           0 :   mParentChannel = aParentChannel;
    8896             :   // If the channel is suspended, propagate that info to the parent's mEventQ.
    8897           0 :   uint32_t suspendCount = mSuspendCount;
    8898           0 :   while (suspendCount--) {
    8899           0 :     mParentChannel->SuspendMessageDiversion();
    8900             :   }
    8901           0 :   return NS_OK;
    8902             : }
    8903             : 
    8904             : NS_IMETHODIMP
    8905           0 : nsHttpChannel::MessageDiversionStop()
    8906             : {
    8907           0 :   LOG(("nsHttpChannel::MessageDiversionStop [this=%p]", this));
    8908           0 :   MOZ_ASSERT(mParentChannel);
    8909           0 :   mParentChannel = nullptr;
    8910           0 :   return NS_OK;
    8911             : }
    8912             : 
    8913             : NS_IMETHODIMP
    8914           0 : nsHttpChannel::SuspendInternal()
    8915             : {
    8916           0 :     NS_ENSURE_TRUE(mIsPending, NS_ERROR_NOT_AVAILABLE);
    8917             : 
    8918           0 :     LOG(("nsHttpChannel::SuspendInternal [this=%p]\n", this));
    8919             : 
    8920           0 :     ++mSuspendCount;
    8921             : 
    8922           0 :     if (mSuspendCount == 1) {
    8923           0 :         mSuspendTimestamp = TimeStamp::NowLoRes();
    8924             :     }
    8925             : 
    8926           0 :     nsresult rvTransaction = NS_OK;
    8927           0 :     if (mTransactionPump) {
    8928           0 :         rvTransaction = mTransactionPump->Suspend();
    8929             :     }
    8930           0 :     nsresult rvCache = NS_OK;
    8931           0 :     if (mCachePump) {
    8932           0 :         rvCache = mCachePump->Suspend();
    8933             :     }
    8934             : 
    8935           0 :     return NS_FAILED(rvTransaction) ? rvTransaction : rvCache;
    8936             : }
    8937             : 
    8938             : NS_IMETHODIMP
    8939           0 : nsHttpChannel::ResumeInternal()
    8940             : {
    8941           0 :     NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
    8942             : 
    8943           0 :     LOG(("nsHttpChannel::ResumeInternal [this=%p]\n", this));
    8944             : 
    8945           0 :     if (--mSuspendCount == 0) {
    8946           0 :         mSuspendTotalTime += (TimeStamp::NowLoRes() - mSuspendTimestamp).
    8947           0 :                                ToMilliseconds();
    8948             : 
    8949           0 :         if (mCallOnResume) {
    8950           0 :             nsresult rv = AsyncCall(mCallOnResume);
    8951           0 :             mCallOnResume = nullptr;
    8952           0 :             NS_ENSURE_SUCCESS(rv, rv);
    8953             :         }
    8954             :     }
    8955             : 
    8956           0 :     nsresult rvTransaction = NS_OK;
    8957           0 :     if (mTransactionPump) {
    8958           0 :         rvTransaction = mTransactionPump->Resume();
    8959             :     }
    8960             : 
    8961           0 :     nsresult rvCache = NS_OK;
    8962           0 :     if (mCachePump) {
    8963           0 :         rvCache = mCachePump->Resume();
    8964             :     }
    8965             : 
    8966           0 :     return NS_FAILED(rvTransaction) ? rvTransaction : rvCache;
    8967             : }
    8968             : 
    8969             : void
    8970           0 : nsHttpChannel::MaybeWarnAboutAppCache()
    8971             : {
    8972             :     // First, accumulate a telemetry ping about appcache usage.
    8973             :     Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,
    8974           0 :                           true);
    8975             : 
    8976             :     // Then, issue a deprecation warning.
    8977           0 :     nsCOMPtr<nsIDeprecationWarner> warner;
    8978           0 :     GetCallback(warner);
    8979           0 :     if (warner) {
    8980           0 :         warner->IssueWarning(nsIDocument::eAppCache, false);
    8981             :     }
    8982           0 : }
    8983             : 
    8984             : void
    8985           6 : nsHttpChannel::SetLoadGroupUserAgentOverride()
    8986             : {
    8987          12 :     nsCOMPtr<nsIURI> uri;
    8988           6 :     GetURI(getter_AddRefs(uri));
    8989          12 :     nsAutoCString uriScheme;
    8990           6 :     if (uri) {
    8991           6 :         uri->GetScheme(uriScheme);
    8992             :     }
    8993             : 
    8994             :     // We don't need a UA for file: protocols.
    8995           6 :     if (uriScheme.EqualsLiteral("file")) {
    8996           0 :         gHttpHandler->OnUserAgentRequest(this);
    8997           0 :         return;
    8998             :     }
    8999             : 
    9000           6 :     nsIRequestContextService* rcsvc = gHttpHandler->GetRequestContextService();
    9001          12 :     nsCOMPtr<nsIRequestContext> rc;
    9002           6 :     if (rcsvc) {
    9003           6 :         rcsvc->GetRequestContext(mRequestContextID,
    9004          12 :                                     getter_AddRefs(rc));
    9005             :     }
    9006             : 
    9007          12 :     nsAutoCString ua;
    9008           6 :     if (nsContentUtils::IsNonSubresourceRequest(this)) {
    9009           1 :         gHttpHandler->OnUserAgentRequest(this);
    9010           1 :         if (rc) {
    9011           1 :             GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
    9012           1 :             rc->SetUserAgentOverride(ua);
    9013             :         }
    9014             :     } else {
    9015           5 :         GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua);
    9016             :         // Don't overwrite the UA if it is already set (eg by an XHR with explicit UA).
    9017           5 :         if (ua.IsEmpty()) {
    9018           0 :             if (rc) {
    9019           0 :                 rc->GetUserAgentOverride(ua);
    9020           0 :                 SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), ua, false);
    9021             :             } else {
    9022           0 :                 gHttpHandler->OnUserAgentRequest(this);
    9023             :             }
    9024             :         }
    9025             :     }
    9026             : }
    9027             : 
    9028             : void
    9029           6 : nsHttpChannel::SetDoNotTrack()
    9030             : {
    9031             :   /**
    9032             :    * 'DoNotTrack' header should be added if 'privacy.donottrackheader.enabled'
    9033             :    * is true or tracking protection is enabled. See bug 1258033.
    9034             :    */
    9035          12 :   nsCOMPtr<nsILoadContext> loadContext;
    9036           6 :   NS_QueryNotificationCallbacks(this, loadContext);
    9037             : 
    9038          12 :   if ((loadContext && loadContext->UseTrackingProtection()) ||
    9039           6 :       nsContentUtils::DoNotTrackEnabled()) {
    9040             :     DebugOnly<nsresult> rv =
    9041           0 :       mRequestHead.SetHeader(nsHttp::DoNotTrack,
    9042           0 :                              NS_LITERAL_CSTRING("1"),
    9043           0 :                              false);
    9044           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    9045             :   }
    9046           6 : }
    9047             : 
    9048             : void
    9049           6 : nsHttpChannel::ReportRcwnStats(bool isFromNet)
    9050             : {
    9051           6 :     if (!sRCWNEnabled) {
    9052           6 :         return;
    9053             :     }
    9054             : 
    9055           0 :     if (isFromNet) {
    9056           0 :         if (mRaceCacheWithNetwork) {
    9057           0 :             gIOService->IncrementNetWonRequestNumber();
    9058           0 :             Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_RACE_NETWORK_WIN, mTransferSize);
    9059           0 :             if (mRaceDelay) {
    9060           0 :                 AccumulateCategorical(Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::NetworkDelayedRace);
    9061             :             } else {
    9062           0 :                 AccumulateCategorical(Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::NetworkRace);
    9063             :             }
    9064             :         } else {
    9065           0 :             Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_NOT_RACE, mTransferSize);
    9066           0 :             AccumulateCategorical(Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::NetworkNoRace);
    9067             :         }
    9068             :     } else {
    9069           0 :         if (mRaceCacheWithNetwork) {
    9070           0 :             gIOService->IncrementCacheWonRequestNumber();
    9071           0 :             Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_RACE_CACHE_WIN, mTransferSize);
    9072           0 :             if (mRaceDelay) {
    9073           0 :                 AccumulateCategorical(Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::CacheDelayedRace);
    9074             :             } else {
    9075           0 :                 AccumulateCategorical(Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::CacheRace);
    9076             :             }
    9077             :         } else {
    9078           0 :             Telemetry::Accumulate(Telemetry::NETWORK_RACE_CACHE_BANDWIDTH_NOT_RACE, mTransferSize);
    9079           0 :             AccumulateCategorical(Telemetry::LABELS_NETWORK_RACE_CACHE_WITH_NETWORK_USAGE_2::CacheNoRace);
    9080             :         }
    9081             :     }
    9082             : 
    9083           0 :     gIOService->IncrementRequestNumber();
    9084             : }
    9085             : 
    9086             : static const size_t kPositiveBucketNumbers = 34;
    9087             : static const int64_t kPositiveBucketLevels[kPositiveBucketNumbers] =
    9088             : {
    9089             :         0, 10, 20, 30, 40, 50, 60, 70, 80, 90,
    9090             :         100, 200, 300, 400, 500, 600, 700, 800, 900, 1000,
    9091             :         2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000,
    9092             :         20000, 30000, 40000, 50000, 60000
    9093             : };
    9094             : 
    9095             : /**
    9096             :  * For space efficiency, we collect finer resolution for small difference
    9097             :  * between net and cache time, coarser for larger.
    9098             :  * Bucket #40 for a tie.
    9099             :  * #41 to #50 indicates cache wins by 1ms to 100ms, split equally.
    9100             :  * #51 to #59 indicates cache wins by 101ms to 1000ms.
    9101             :  * #60 to #68 indicates cache wins by 1s to 10s.
    9102             :  * #69 to #73 indicates cache wins by 11s to 60s.
    9103             :  * #74 indicates cache wins by more than 1 minute.
    9104             :  *
    9105             :  * #39 to #30 indicates network wins by 1ms to 100ms, split equally.
    9106             :  * #29 to #21 indicates network wins by 101ms to 1000ms.
    9107             :  * #20 to #12 indicates network wins by 1s to 10s.
    9108             :  * #11 to #7 indicates network wins by 11s to 60s.
    9109             :  * #6 indicates network wins by more than 1 minute.
    9110             :  *
    9111             :  * Other bucket numbers are reserved.
    9112             :  */
    9113             : inline int64_t
    9114           4 : nsHttpChannel::ComputeTelemetryBucketNumber(int64_t difftime_ms)
    9115             : {
    9116             :         int64_t absBucketIndex =
    9117           4 :                 std::lower_bound(kPositiveBucketLevels,
    9118             :                      kPositiveBucketLevels + kPositiveBucketNumbers,
    9119           8 :                            static_cast<int64_t>(mozilla::Abs(difftime_ms)))
    9120           4 :                 - kPositiveBucketLevels;
    9121             : 
    9122           4 :         return difftime_ms >= 0 ? 40 + absBucketIndex
    9123           4 :                                 : 40 - absBucketIndex;
    9124             : }
    9125             : 
    9126             : void
    9127           3 : nsHttpChannel::ReportNetVSCacheTelemetry()
    9128             : {
    9129             :     nsresult rv;
    9130           3 :     if (!mCacheEntry) {
    9131           1 :         return;
    9132             :     }
    9133             : 
    9134             :     // We only report telemetry if the entry is persistent (on disk)
    9135             :     bool persistent;
    9136           3 :     rv = mCacheEntry->GetPersistent(&persistent);
    9137           3 :     if (NS_FAILED(rv) || !persistent) {
    9138           0 :         return;
    9139             :     }
    9140             : 
    9141           3 :     uint64_t onStartNetTime = 0;
    9142           3 :     if (NS_FAILED(mCacheEntry->GetOnStartTime(&onStartNetTime))) {
    9143           1 :         return;
    9144             :     }
    9145             : 
    9146           2 :     uint64_t onStopNetTime = 0;
    9147           2 :     if (NS_FAILED(mCacheEntry->GetOnStopTime(&onStopNetTime))) {
    9148           0 :         return;
    9149             :     }
    9150             : 
    9151           2 :     uint64_t onStartCacheTime = (mOnStartRequestTimestamp - mAsyncOpenTime).ToMilliseconds();
    9152           2 :     int64_t onStartDiff = onStartNetTime - onStartCacheTime;
    9153           2 :     onStartDiff = ComputeTelemetryBucketNumber(onStartDiff);
    9154             : 
    9155           2 :     uint64_t onStopCacheTime = (mCacheReadEnd - mAsyncOpenTime).ToMilliseconds();
    9156           2 :     int64_t onStopDiff = onStopNetTime - onStopCacheTime;
    9157           2 :     onStopDiff = ComputeTelemetryBucketNumber(onStopDiff);
    9158             : 
    9159           2 :     if (mDidReval) {
    9160           0 :         Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_REVALIDATED_V2, onStartDiff);
    9161           0 :         Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_REVALIDATED_V2, onStopDiff);
    9162             :     } else {
    9163           2 :         Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_NOTREVALIDATED_V2, onStartDiff);
    9164           2 :         Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_NOTREVALIDATED_V2, onStopDiff);
    9165             :     }
    9166             : 
    9167           2 :     if (mDidReval) {
    9168             :         // We don't report revalidated probes as the data would be skewed.
    9169           0 :         return;
    9170             :     }
    9171             : 
    9172           2 :     if (mCacheOpenWithPriority) {
    9173           2 :         if (mCacheQueueSizeWhenOpen < 5) {
    9174           2 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QSMALL_HIGHPRI_V2, onStartDiff);
    9175           2 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QSMALL_HIGHPRI_V2, onStopDiff);
    9176           0 :         } else if (mCacheQueueSizeWhenOpen < 10) {
    9177           0 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QMED_HIGHPRI_V2, onStartDiff);
    9178           0 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QMED_HIGHPRI_V2, onStopDiff);
    9179             :         } else {
    9180           0 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QBIG_HIGHPRI_V2, onStartDiff);
    9181           0 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QBIG_HIGHPRI_V2, onStopDiff);
    9182             :         }
    9183             :     } else { // The limits are higher for normal priority cache queues
    9184           0 :         if (mCacheQueueSizeWhenOpen < 10) {
    9185           0 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QSMALL_NORMALPRI_V2, onStartDiff);
    9186           0 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QSMALL_NORMALPRI_V2, onStopDiff);
    9187           0 :         } else if (mCacheQueueSizeWhenOpen < 50) {
    9188           0 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QMED_NORMALPRI_V2, onStartDiff);
    9189           0 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QMED_NORMALPRI_V2, onStopDiff);
    9190             :         } else {
    9191           0 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTART_QBIG_NORMALPRI_V2, onStartDiff);
    9192           0 :             Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_QBIG_NORMALPRI_V2, onStopDiff);
    9193             :         }
    9194             :     }
    9195             : 
    9196           2 :     uint32_t diskStorageSizeK = 0;
    9197           2 :     rv = mCacheEntry->GetDiskStorageSizeInKB(&diskStorageSizeK);
    9198           2 :     if (NS_FAILED(rv)) {
    9199           0 :         return;
    9200             :     }
    9201             : 
    9202             :     // No significant difference was observed between different sizes for |onStartDiff|
    9203           2 :     if (diskStorageSizeK < 256) {
    9204           2 :         Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_SMALL_V2, onStopDiff);
    9205             :     } else {
    9206           0 :         Telemetry::Accumulate(Telemetry::HTTP_NET_VS_CACHE_ONSTOP_LARGE_V2, onStopDiff);
    9207             :     }
    9208             : }
    9209             : 
    9210             : NS_IMETHODIMP
    9211           0 : nsHttpChannel::Test_delayCacheEntryOpeningBy(int32_t aTimeout)
    9212             : {
    9213           0 :     MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
    9214           0 :     mCacheOpenDelay = aTimeout;
    9215           0 :     return NS_OK;
    9216             : }
    9217             : 
    9218             : NS_IMETHODIMP
    9219           0 : nsHttpChannel::Test_triggerDelayedOpenCacheEntry()
    9220             : {
    9221           0 :     MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
    9222             :     nsresult rv;
    9223           0 :     if (!mCacheOpenDelay) {
    9224             :         // No delay was set.
    9225           0 :         return NS_ERROR_NOT_AVAILABLE;
    9226             :     }
    9227           0 :     if (!mCacheOpenFunc) {
    9228             :         // There should be a runnable.
    9229           0 :         return NS_ERROR_FAILURE;
    9230             :     }
    9231           0 :     if (mCacheOpenTimer) {
    9232           0 :         rv = mCacheOpenTimer->Cancel();
    9233           0 :         if (NS_FAILED(rv)) {
    9234           0 :             return rv;
    9235             :         }
    9236           0 :         mCacheOpenTimer = nullptr;
    9237             :     }
    9238           0 :     mCacheOpenDelay = 0;
    9239             :     // Avoid re-entrancy issues by nulling our mCacheOpenFunc before calling it.
    9240           0 :     std::function<void(nsHttpChannel*)> cacheOpenFunc = nullptr;
    9241           0 :     std::swap(cacheOpenFunc, mCacheOpenFunc);
    9242           0 :     cacheOpenFunc(this);
    9243             : 
    9244           0 :     return NS_OK;
    9245             : }
    9246             : 
    9247             : nsresult
    9248           6 : nsHttpChannel::TriggerNetwork(int32_t aTimeout)
    9249             : {
    9250           6 :     MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
    9251             :     // If a network request has already gone out, there is no point in
    9252             :     // doing this again.
    9253           6 :     LOG(("nsHttpChannel::TriggerNetwork [this=%p]\n", this));
    9254           6 :     if (mNetworkTriggered) {
    9255           0 :         LOG(("  network already triggered. Returning.\n"));
    9256           0 :         return NS_OK;
    9257             :     }
    9258             : 
    9259           6 :     if (!aTimeout) {
    9260           6 :         mNetworkTriggered = true;
    9261           6 :         if (mNetworkTriggerTimer) {
    9262           0 :             mNetworkTriggerTimer->Cancel();
    9263           0 :             mNetworkTriggerTimer = nullptr;
    9264             :         }
    9265             : 
    9266             :         // If we are waiting for a proxy request, that means we can't trigger
    9267             :         // the next step just yet. We need for mConnectionInfo to be non-null
    9268             :         // before we call TryHSTSPriming. OnProxyAvailable will trigger
    9269             :         // BeginConnect, and Connect will call TryHSTSPriming even if it's
    9270             :         // for the cache callbacks.
    9271           6 :         if (mProxyRequest) {
    9272           0 :             LOG(("  proxy request in progress. Delaying network trigger.\n"));
    9273           0 :             mWaitingForProxy = true;
    9274           0 :             return NS_OK;
    9275             :         }
    9276             : 
    9277           6 :         if (mCacheAsyncOpenCalled && !mOnCacheAvailableCalled) {
    9278           0 :             mRaceCacheWithNetwork = true;
    9279             :         }
    9280             : 
    9281           6 :         LOG(("  triggering network\n"));
    9282           6 :         return TryHSTSPriming();
    9283             :     }
    9284             : 
    9285           0 :     LOG(("  setting timer to trigger network: %d ms\n", aTimeout));
    9286           0 :     mNetworkTriggerTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
    9287           0 :     mNetworkTriggerTimer->InitWithCallback(this, aTimeout, nsITimer::TYPE_ONE_SHOT);
    9288           0 :     return NS_OK;
    9289             : }
    9290             : 
    9291             : nsresult
    9292           0 : nsHttpChannel::MaybeRaceCacheWithNetwork()
    9293             : {
    9294             :     // Don't trigger the network if the load flags say so.
    9295           0 :     if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_NO_NETWORK_IO)) {
    9296           0 :         return NS_OK;
    9297             :     }
    9298             : 
    9299             :     // We must not race if the channel has a failure status code.
    9300           0 :     if (NS_FAILED(mStatus)) {
    9301           0 :         return NS_OK;
    9302             :     }
    9303             : 
    9304             :     // If a CORS Preflight is required we must not race.
    9305           0 :     if (mRequireCORSPreflight && !mIsCorsPreflightDone) {
    9306           0 :         return NS_OK;
    9307             :     }
    9308             : 
    9309           0 :     if (CacheFileUtils::CachePerfStats::IsCacheSlow()) {
    9310             :         // If the cache is slow, trigger the network request immediately.
    9311           0 :         mRaceDelay = 0;
    9312             :     } else {
    9313             :         // Give cache a headstart of 3 times the average cache entry open time.
    9314           0 :         mRaceDelay = CacheFileUtils::CachePerfStats::GetAverage(
    9315           0 :                      CacheFileUtils::CachePerfStats::ENTRY_OPEN, true) * 3;
    9316             :         // We use microseconds in CachePerfStats but we need milliseconds
    9317             :         // for TriggerNetwork.
    9318           0 :         mRaceDelay /= 1000;
    9319           0 :         if (mRaceDelay > sRCWNMaxWaitMs) {
    9320           0 :             mRaceDelay = sRCWNMaxWaitMs;
    9321             :         }
    9322             :     }
    9323             : 
    9324           0 :     MOZ_ASSERT(sRCWNEnabled, "The pref must be truned on.");
    9325           0 :     LOG(("nsHttpChannel::MaybeRaceCacheWithNetwork [this=%p, delay=%u]\n",
    9326             :          this, mRaceDelay));
    9327             : 
    9328           0 :     return TriggerNetwork(mRaceDelay);
    9329             : }
    9330             : 
    9331             : NS_IMETHODIMP
    9332           0 : nsHttpChannel::Test_triggerNetwork(int32_t aTimeout)
    9333             : {
    9334           0 :     MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
    9335           0 :     return TriggerNetwork(aTimeout);
    9336             : }
    9337             : 
    9338             : NS_IMETHODIMP
    9339           0 : nsHttpChannel::Notify(nsITimer *aTimer)
    9340             : {
    9341           0 :     RefPtr<nsHttpChannel> self(this);
    9342           0 :     if (aTimer == mCacheOpenTimer) {
    9343           0 :         return Test_triggerDelayedOpenCacheEntry();
    9344           0 :     } else if (aTimer == mNetworkTriggerTimer) {
    9345           0 :         return TriggerNetwork(0);
    9346             :     } else {
    9347           0 :         MOZ_CRASH("Unknown timer");
    9348             :     }
    9349             : 
    9350             :     return NS_OK;
    9351             : }
    9352             : 
    9353             : } // namespace net
    9354             : } // namespace mozilla

Generated by: LCOV version 1.13