tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsHttpChannel.cpp (442007B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set expandtab ts=4 sw=2 sts=2 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/ScopeExit.h"
     13 #include "mozilla/Sprintf.h"
     14 #include "mozilla/ToString.h"
     15 #include "mozilla/dom/nsCSPContext.h"
     16 #include "mozilla/dom/NavigatorLogin.h"
     17 #include "mozilla/glean/AntitrackingMetrics.h"
     18 #include "mozilla/glean/NetwerkMetrics.h"
     19 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
     20 #include "mozilla/net/CaptivePortalService.h"
     21 #include "mozilla/net/CookieServiceParent.h"
     22 #include "mozilla/StoragePrincipalHelper.h"
     23 
     24 #include "nsCOMPtr.h"
     25 #include "nsContentSecurityUtils.h"
     26 #include "nsHttp.h"
     27 #include "nsHttpChannel.h"
     28 #include "nsHttpChannelAuthProvider.h"
     29 #include "nsHttpHandler.h"
     30 #include "nsIStreamConverter.h"
     31 #include "nsString.h"
     32 #include "nsICacheStorageService.h"
     33 #include "nsICacheStorage.h"
     34 #include "nsICacheEntry.h"
     35 #include "nsICookieNotification.h"
     36 #include "nsICryptoHash.h"
     37 #include "nsIEffectiveTLDService.h"
     38 #include "nsIHttpHeaderVisitor.h"
     39 #include "nsINetworkInterceptController.h"
     40 #include "nsIStringBundle.h"
     41 #include "nsIStreamListenerTee.h"
     42 #include "nsISeekableStream.h"
     43 #include "nsIProtocolProxyService2.h"
     44 #include "nsIURLQueryStringStripper.h"
     45 #include "nsIWebTransport.h"
     46 #include "nsCRT.h"
     47 #include "nsMimeTypes.h"
     48 #include "nsNetCID.h"
     49 #include "nsNetUtil.h"
     50 #include "nsIStreamTransportService.h"
     51 #include "prnetdb.h"
     52 #include "nsEscape.h"
     53 #include "nsComponentManagerUtils.h"
     54 #include "nsStreamUtils.h"
     55 #include "nsIOService.h"
     56 #include "nsDNSPrefetch.h"
     57 #include "nsChannelClassifier.h"
     58 #include "nsIRedirectResultListener.h"
     59 #include "mozilla/TimeStamp.h"
     60 #include "nsError.h"
     61 #include "nsPrintfCString.h"
     62 #include "nsQueryObject.h"
     63 #include "nsThreadUtils.h"
     64 #include "nsIConsoleService.h"
     65 #include "nsINetworkErrorLogging.h"
     66 #include "mozilla/AntiTrackingRedirectHeuristic.h"
     67 #include "mozilla/AntiTrackingUtils.h"
     68 #include "mozilla/Attributes.h"
     69 #include "mozilla/BasePrincipal.h"
     70 #include "mozilla/DebugOnly.h"
     71 #include "mozilla/PerfStats.h"
     72 #include "mozilla/ProfilerLabels.h"
     73 #include "mozilla/FlowMarkers.h"
     74 #include "mozilla/Components.h"
     75 #include "mozilla/StaticPrefs_dom.h"
     76 #include "mozilla/StaticPrefs_network.h"
     77 #include "mozilla/StaticPrefs_privacy.h"
     78 #include "mozilla/StaticPrefs_security.h"
     79 #include "sslt.h"
     80 #include "nsCharSeparatedTokenizer.h"
     81 #include "nsContentUtils.h"
     82 #include "nsContentSecurityManager.h"
     83 #include "nsIClassOfService.h"
     84 #include "CookieService.h"
     85 #include "nsIPrincipal.h"
     86 #include "nsIScriptError.h"
     87 #include "nsIScriptSecurityManager.h"
     88 #include "nsITransportSecurityInfo.h"
     89 #include "nsIWebProgressListener.h"
     90 #include "LoadContextInfo.h"
     91 #include "netCore.h"
     92 #include "nsHttpTransaction.h"
     93 #include "nsICancelable.h"
     94 #include "nsIHttpChannelInternal.h"
     95 #include "nsIPrompt.h"
     96 #include "nsInputStreamPump.h"
     97 #include "nsURLHelper.h"
     98 #include "nsISocketTransport.h"
     99 #include "nsIStreamConverterService.h"
    100 #include "nsISiteSecurityService.h"
    101 #include "nsIURIMutator.h"
    102 #include "nsString.h"
    103 #include "nsStringStream.h"
    104 #include "mozilla/dom/PerformanceStorage.h"
    105 #include "mozilla/dom/ReferrerInfo.h"
    106 #include "mozilla/glean/DomSecurityMetrics.h"
    107 #include "mozilla/Telemetry.h"
    108 #include "mozilla/Services.h"
    109 #include "nsISystemInfo.h"
    110 #include "mozilla/Components.h"
    111 #include "AlternateServices.h"
    112 #include "NetworkMarker.h"
    113 #include "nsIDNSRecord.h"
    114 #include "mozilla/dom/Document.h"
    115 #include "nsICompressConvStats.h"
    116 #include "nsCORSListenerProxy.h"
    117 #include "nsISocketProvider.h"
    118 #include "mozilla/extensions/StreamFilterParent.h"
    119 #include "mozilla/net/SFVService.h"
    120 #include "mozilla/NullPrincipal.h"
    121 #include "CacheControlParser.h"
    122 #include "nsMixedContentBlocker.h"
    123 #include "CacheStorageService.h"
    124 #include "HttpChannelParent.h"
    125 #include "HttpTransactionParent.h"
    126 #include "ThirdPartyUtil.h"
    127 #include "InterceptedHttpChannel.h"
    128 #include "../../cache2/CacheFileUtils.h"
    129 #include "nsINetworkLinkService.h"
    130 #include "mozilla/ContentBlockingAllowList.h"
    131 #include "mozilla/dom/ServiceWorkerUtils.h"
    132 #include "mozilla/dom/nsHTTPSOnlyStreamListener.h"
    133 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
    134 #include "mozilla/net/AsyncUrlChannelClassifier.h"
    135 #include "mozilla/net/CookieJarSettings.h"
    136 #include "mozilla/net/NeckoChannelParams.h"
    137 #include "mozilla/net/OpaqueResponseUtils.h"
    138 #include "mozilla/net/UrlClassifierFeatureFactory.h"
    139 #include "mozilla/net/URLPatternGlue.h"
    140 #include "mozilla/net/urlpattern_glue.h"
    141 #include "HttpTrafficAnalyzer.h"
    142 #include "mozilla/net/SocketProcessParent.h"
    143 #include "mozilla/dom/SecFetch.h"
    144 #include "mozilla/dom/WindowGlobalParent.h"
    145 #include "mozilla/net/TRRService.h"
    146 #include "LNAPermissionRequest.h"
    147 #include "nsUnknownDecoder.h"
    148 #ifdef XP_WIN
    149 #  include "HttpWinUtils.h"
    150 #endif
    151 #ifdef XP_MACOSX
    152 #  include "MicrosoftEntraSSOUtils.h"
    153 #endif
    154 #ifdef FUZZING
    155 #  include "mozilla/StaticPrefs_fuzzing.h"
    156 #endif
    157 
    158 #include "mozilla/dom/ReportDeliver.h"
    159 #include "mozilla/dom/ReportingHeader.h"
    160 
    161 namespace mozilla {
    162 
    163 using namespace dom;
    164 
    165 namespace net {
    166 
    167 namespace {
    168 
    169 // True if the local cache should be bypassed when processing a request.
    170 #define BYPASS_LOCAL_CACHE(loadFlags, isPreferCacheLoadOverBypass) \
    171  ((loadFlags) & (nsIRequest::LOAD_BYPASS_CACHE |                  \
    172                  nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE) &&   \
    173   !(((loadFlags) & nsIRequest::LOAD_FROM_CACHE) &&                \
    174     (isPreferCacheLoadOverBypass)))
    175 
    176 #define RECOVER_FROM_CACHE_FILE_ERROR(result) \
    177  ((result) == NS_ERROR_FILE_NOT_FOUND ||     \
    178   (result) == NS_ERROR_FILE_CORRUPTED || (result) == NS_ERROR_OUT_OF_MEMORY)
    179 
    180 #define WRONG_RACING_RESPONSE_SOURCE(req)               \
    181  (mRaceCacheWithNetwork &&                             \
    182   (((mFirstResponseSource == RESPONSE_FROM_CACHE) &&   \
    183     ((req) != mCachePump)) ||                          \
    184    ((mFirstResponseSource == RESPONSE_FROM_NETWORK) && \
    185     ((req) != mTransactionPump))))
    186 
    187 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
    188 
    189 enum ChannelDisposition {
    190  kHttpCanceled = 0,
    191  kHttpDisk = 1,
    192  kHttpNetOK = 2,
    193  kHttpNetEarlyFail = 3,
    194  kHttpNetLateFail = 4,
    195  kHttpsCanceled = 8,
    196  kHttpsDisk = 9,
    197  kHttpsNetOK = 10,
    198  kHttpsNetEarlyFail = 11,
    199  kHttpsNetLateFail = 12
    200 };
    201 
    202 static nsLiteralCString CacheDispositionToTelemetryLabel(
    203    nsICacheInfoChannel::CacheDisposition hitOrMiss) {
    204  switch (hitOrMiss) {
    205    case nsICacheInfoChannel::kCacheUnresolved:
    206      return "Unresolved"_ns;
    207    case nsICacheInfoChannel::kCacheHit:
    208      return "Hit"_ns;
    209    case nsICacheInfoChannel::kCacheHitViaReval:
    210      return "HitViaReval"_ns;
    211    case nsICacheInfoChannel::kCacheMissedViaReval:
    212      return "MissedViaReval"_ns;
    213    case nsICacheInfoChannel::kCacheMissed:
    214      return "Missed"_ns;
    215    case nsICacheInfoChannel::kCacheUnknown:
    216      return "Unknown"_ns;
    217    default:
    218      return "Invalid"_ns;
    219  }
    220 }
    221 
    222 void AccumulateCacheHitTelemetry(
    223    nsICacheInfoChannel::CacheDisposition hitOrMiss, nsIChannel* aChannel) {
    224  nsCString key("UNKNOWN");
    225 
    226  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
    227 
    228  nsAutoCString contentType;
    229  if (NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
    230    if (nsContentUtils::IsJavascriptMIMEType(
    231            NS_ConvertUTF8toUTF16(contentType))) {
    232      key.AssignLiteral("JAVASCRIPT");
    233    } else if (StringBeginsWith(contentType, "text/css"_ns) ||
    234               (loadInfo && loadInfo->GetExternalContentPolicyType() ==
    235                                ExtContentPolicy::TYPE_STYLESHEET)) {
    236      key.AssignLiteral("STYLESHEET");
    237    } else if (StringBeginsWith(contentType, "application/wasm"_ns)) {
    238      key.AssignLiteral("WASM");
    239    } else if (StringBeginsWith(contentType, "image/"_ns)) {
    240      key.AssignLiteral("IMAGE");
    241    } else if (StringBeginsWith(contentType, "video/"_ns)) {
    242      key.AssignLiteral("MEDIA");
    243    } else if (StringBeginsWith(contentType, "audio/"_ns)) {
    244      key.AssignLiteral("MEDIA");
    245    } else if (!StringBeginsWith(contentType,
    246                                 nsLiteralCString(UNKNOWN_CONTENT_TYPE))) {
    247      key.AssignLiteral("OTHER");
    248    }
    249  }
    250 
    251  nsLiteralCString label = CacheDispositionToTelemetryLabel(hitOrMiss);
    252  glean::http::cache_disposition.Get(key, label).Add();
    253  glean::http::cache_disposition.Get("ALL"_ns, label).Add();
    254 }
    255 
    256 // Computes and returns a SHA1 hash of the input buffer. The input buffer
    257 // must be a null-terminated string.
    258 nsresult Hash(const char* buf, nsACString& hash) {
    259  nsresult rv;
    260 
    261  nsCOMPtr<nsICryptoHash> hasher =
    262      do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
    263  NS_ENSURE_SUCCESS(rv, rv);
    264 
    265  rv = hasher->Init(nsICryptoHash::SHA1);
    266  NS_ENSURE_SUCCESS(rv, rv);
    267 
    268  rv = hasher->Update(reinterpret_cast<unsigned const char*>(buf), strlen(buf));
    269  NS_ENSURE_SUCCESS(rv, rv);
    270 
    271  rv = hasher->Finish(true, hash);
    272  NS_ENSURE_SUCCESS(rv, rv);
    273 
    274  return NS_OK;
    275 }
    276 
    277 class CookieVisitor final {
    278 public:
    279  explicit CookieVisitor(nsHttpResponseHead* aResponseHead) {
    280    nsAutoCString cookieHeader;
    281    if (NS_SUCCEEDED(
    282            aResponseHead->GetHeader(nsHttp::Set_Cookie, cookieHeader))) {
    283      for (const auto& cookie : cookieHeader.Split('\n')) {
    284        mCookieHeaders.AppendElement(cookie);
    285      }
    286    }
    287  }
    288 
    289  ~CookieVisitor() = default;
    290 
    291  const nsTArray<nsCString>& CookieHeaders() const { return mCookieHeaders; }
    292 
    293 private:
    294  nsTArray<nsCString> mCookieHeaders;
    295 };
    296 
    297 class CookieObserver final : public nsIObserver,
    298                             public nsSupportsWeakReference {
    299 public:
    300  NS_DECL_ISUPPORTS
    301  NS_DECL_NSIOBSERVER
    302 
    303  static already_AddRefed<CookieObserver> Create(bool aPrivateBrowsing);
    304 
    305  void StealChanges(nsTArray<CookieChange>& aChanges) {
    306    aChanges.SwapElements(mChanges);
    307  }
    308 
    309 private:
    310  CookieObserver() = default;
    311  ~CookieObserver() = default;
    312 
    313  nsTArray<CookieChange> mChanges;
    314 };
    315 
    316 NS_IMPL_ISUPPORTS(CookieObserver, nsIObserver, nsISupportsWeakReference)
    317 
    318 // static
    319 already_AddRefed<CookieObserver> CookieObserver::Create(bool aPrivateBrowsing) {
    320  MOZ_ASSERT(NS_IsMainThread());
    321 
    322  RefPtr<CookieObserver> observer = new CookieObserver();
    323 
    324  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    325  if (NS_WARN_IF(!os)) {
    326    return nullptr;
    327  }
    328 
    329  nsresult rv = os->AddObserver(
    330      observer, aPrivateBrowsing ? "private-cookie-changed" : "cookie-changed",
    331      true);
    332  if (NS_WARN_IF(NS_FAILED(rv))) {
    333    return nullptr;
    334  }
    335 
    336  return observer.forget();
    337 }
    338 
    339 NS_IMETHODIMP
    340 CookieObserver::Observe(nsISupports* aSubject, const char* aTopic,
    341                        const char16_t* aData) {
    342  MOZ_ASSERT(NS_IsMainThread());
    343 
    344  nsCOMPtr<nsICookieNotification> notification = do_QueryInterface(aSubject);
    345  NS_ENSURE_TRUE(notification, NS_ERROR_FAILURE);
    346 
    347  nsCOMPtr<nsICookie> xpcCookie;
    348  nsresult rv = notification->GetCookie(getter_AddRefs(xpcCookie));
    349  if (NS_WARN_IF(NS_FAILED(rv))) {
    350    return rv;
    351  }
    352 
    353  if (!xpcCookie) {
    354    return NS_OK;
    355  }
    356 
    357  const Cookie& cookie = xpcCookie->AsCookie();
    358 
    359  nsICookieNotification::Action action = notification->GetAction();
    360 
    361  switch (action) {
    362    case nsICookieNotification::COOKIE_DELETED:
    363      mChanges.AppendElement(CookieChange{/* added */ false, cookie.ToIPC(),
    364                                          cookie.OriginAttributesRef()});
    365      break;
    366 
    367    case nsICookieNotification::COOKIE_ADDED:
    368      [[fallthrough]];
    369    case nsICookieNotification::COOKIE_CHANGED:
    370      mChanges.AppendElement(CookieChange{/* added */ true, cookie.ToIPC(),
    371                                          cookie.OriginAttributesRef()});
    372      break;
    373 
    374    default:
    375      // We don't care about other actions because none of them can be
    376      // triggered by the Set-Cookie header.
    377      break;
    378  }
    379 
    380  return NS_OK;
    381 }
    382 
    383 void MaybeInitializeCookieProcessingGuard(
    384    nsHttpChannel* aChannel, CookieServiceParent::CookieProcessingGuard& aGuard,
    385    RefPtr<CookieObserver>& aCookieObserver,
    386    RefPtr<HttpChannelParent>& aHttpChannelParent, uint32_t aHttpStatus) {
    387  nsCOMPtr<nsIParentChannel> parentChannel;
    388  NS_QueryNotificationCallbacks(aChannel, parentChannel);
    389  aHttpChannelParent = do_QueryObject(parentChannel);
    390  if (!aHttpChannelParent) {
    391    return;
    392  }
    393 
    394  aCookieObserver = CookieObserver::Create(NS_UsePrivateBrowsing(aChannel));
    395 
    396  PNeckoParent* neckoParent = aHttpChannelParent->Manager();
    397  if (!neckoParent) {
    398    return;
    399  }
    400 
    401  PCookieServiceParent* csParent =
    402      LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
    403  CookieServiceParent* cookieServiceParent =
    404      static_cast<CookieServiceParent*>(csParent);
    405  if (!cookieServiceParent) {
    406    return;
    407  }
    408 
    409  // on redirect we don't want to use processing guard
    410  // because it will prevent cookies from being set in the
    411  // content process that originated the request
    412  if (nsHttpChannel::IsRedirectStatus(aHttpStatus)) {
    413    return;
    414  }
    415 
    416  aGuard.Initialize(cookieServiceParent);
    417 }
    418 
    419 }  // unnamed namespace
    420 
    421 // We only treat 3xx responses as redirects if they have a Location header and
    422 // the status code is in a whitelist.
    423 bool nsHttpChannel::WillRedirect(const nsHttpResponseHead& response) {
    424  return IsRedirectStatus(response.Status()) &&
    425         response.HasHeader(nsHttp::Location);
    426 }
    427 
    428 nsresult StoreAuthorizationMetaData(nsICacheEntry* entry,
    429                                    nsHttpRequestHead* requestHead);
    430 
    431 class MOZ_STACK_CLASS AutoRedirectVetoNotifier {
    432 public:
    433  explicit AutoRedirectVetoNotifier(nsHttpChannel* channel, nsresult& aRv)
    434      : mChannel(channel), mRv(aRv) {
    435    if (mChannel->LoadHasAutoRedirectVetoNotifier()) {
    436      MOZ_CRASH("Nested AutoRedirectVetoNotifier on the stack");
    437      mChannel = nullptr;
    438      return;
    439    }
    440 
    441    mChannel->StoreHasAutoRedirectVetoNotifier(true);
    442  }
    443  ~AutoRedirectVetoNotifier() { ReportRedirectResult(mRv); }
    444  void RedirectSucceeded() { ReportRedirectResult(NS_OK); }
    445 
    446 private:
    447  nsHttpChannel* mChannel;
    448  bool mCalledReport = false;
    449  nsresult& mRv;
    450  void ReportRedirectResult(nsresult aRv);
    451 };
    452 
    453 void AutoRedirectVetoNotifier::ReportRedirectResult(nsresult aRv) {
    454  if (!mChannel) return;
    455 
    456  if (mCalledReport) {
    457    return;
    458  }
    459  mCalledReport = true;
    460 
    461  mChannel->mRedirectChannel = nullptr;
    462 
    463  if (NS_SUCCEEDED(aRv)) {
    464    mChannel->RemoveAsNonTailRequest();
    465  }
    466 
    467  nsCOMPtr<nsIRedirectResultListener> vetoHook;
    468  NS_QueryNotificationCallbacks(mChannel, NS_GET_IID(nsIRedirectResultListener),
    469                                getter_AddRefs(vetoHook));
    470 
    471  nsHttpChannel* channel = mChannel;
    472  mChannel = nullptr;
    473 
    474  if (vetoHook) vetoHook->OnRedirectResult(aRv);
    475 
    476  // Drop after the notification
    477  channel->StoreHasAutoRedirectVetoNotifier(false);
    478 }
    479 
    480 //-----------------------------------------------------------------------------
    481 // nsHttpChannel <public>
    482 //-----------------------------------------------------------------------------
    483 
    484 nsHttpChannel::nsHttpChannel() : HttpAsyncAborter<nsHttpChannel>(this) {
    485  LOG(("Creating nsHttpChannel [this=%p, nsIChannel=%p]\n", this,
    486       static_cast<nsIChannel*>(this)));
    487  mChannelCreationTime = PR_Now();
    488  mChannelCreationTimestamp = TimeStamp::Now();
    489 }
    490 
    491 nsHttpChannel::~nsHttpChannel() {
    492  PROFILER_MARKER("~nsHttpChannel", NETWORK, {}, TerminatingFlowMarker,
    493                  Flow::FromPointer(this));
    494  LOG(("Destroying nsHttpChannel [this=%p, nsIChannel=%p]\n", this,
    495       static_cast<nsIChannel*>(this)));
    496 
    497  if (LOG_ENABLED()) {
    498    nsCString webExtension;
    499    this->GetPropertyAsACString(u"cancelledByExtension"_ns, webExtension);
    500    if (!webExtension.IsEmpty()) {
    501      LOG(("channel [%p] cancelled by extension [id=%s]", this,
    502           webExtension.get()));
    503    }
    504  }
    505 
    506  if (mAuthProvider) {
    507    DebugOnly<nsresult> rv = mAuthProvider->Disconnect(NS_ERROR_ABORT);
    508    MOZ_ASSERT(NS_SUCCEEDED(rv));
    509  }
    510 
    511  ReleaseMainThreadOnlyReferences();
    512  if (gHttpHandler) {
    513    gHttpHandler->RemoveHttpChannel(mChannelId);
    514  }
    515 
    516  if (mDictDecompress && mUsingDictionary) {
    517    mDictDecompress->UseCompleted();
    518  }
    519 }
    520 
    521 void nsHttpChannel::ReleaseMainThreadOnlyReferences() {
    522  if (NS_IsMainThread()) {
    523    // Already on main thread, let dtor to
    524    // take care of releasing references
    525    return;
    526  }
    527 
    528  nsTArray<nsCOMPtr<nsISupports>> arrayToRelease;
    529  arrayToRelease.AppendElement(mAuthProvider.forget());
    530  arrayToRelease.AppendElement(mRedirectChannel.forget());
    531  arrayToRelease.AppendElement(mPreflightChannel.forget());
    532  arrayToRelease.AppendElement(mDNSPrefetch.forget());
    533 
    534  MOZ_DIAGNOSTIC_ASSERT(
    535      !mEarlyHintObserver,
    536      "Early hint observer should have been released in ReleaseListeners()");
    537  arrayToRelease.AppendElement(mEarlyHintObserver.forget());
    538  MOZ_DIAGNOSTIC_ASSERT(
    539      !mChannelClassifier,
    540      "Channel classifier should have been released in ReleaseListeners()");
    541  arrayToRelease.AppendElement(
    542      mChannelClassifier.forget().downcast<nsIURIClassifierCallback>());
    543  MOZ_DIAGNOSTIC_ASSERT(
    544      !mWarningReporter,
    545      "Warning reporter should have been released in ReleaseListeners()");
    546  arrayToRelease.AppendElement(mWarningReporter.forget());
    547 
    548  NS_DispatchToMainThread(new ProxyReleaseRunnable(std::move(arrayToRelease)));
    549 }
    550 
    551 nsresult nsHttpChannel::Init(nsIURI* uri, uint32_t caps, nsProxyInfo* proxyInfo,
    552                             uint32_t proxyResolveFlags, nsIURI* proxyURI,
    553                             uint64_t channelId, nsILoadInfo* aLoadInfo) {
    554  LOG1(("nsHttpChannel::Init [this=%p]\n", this));
    555  nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo, proxyResolveFlags,
    556                                      proxyURI, channelId, aLoadInfo);
    557 
    558  return rv;
    559 }
    560 
    561 nsresult nsHttpChannel::AddSecurityMessage(const nsAString& aMessageTag,
    562                                           const nsAString& aMessageCategory) {
    563  if (mWarningReporter) {
    564    return mWarningReporter->ReportSecurityMessage(aMessageTag,
    565                                                   aMessageCategory);
    566  }
    567  return HttpBaseChannel::AddSecurityMessage(aMessageTag, aMessageCategory);
    568 }
    569 
    570 NS_IMETHODIMP
    571 nsHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage,
    572                                     const nsACString& aCategory,
    573                                     bool aIsWarning) {
    574  if (mWarningReporter) {
    575    return mWarningReporter->LogBlockedCORSRequest(aMessage, aCategory,
    576                                                   aIsWarning);
    577  }
    578  return NS_ERROR_UNEXPECTED;
    579 }
    580 
    581 NS_IMETHODIMP
    582 nsHttpChannel::LogMimeTypeMismatch(const nsACString& aMessageName,
    583                                   bool aWarning, const nsAString& aURL,
    584                                   const nsAString& aContentType) {
    585  if (mWarningReporter) {
    586    return mWarningReporter->LogMimeTypeMismatch(aMessageName, aWarning, aURL,
    587                                                 aContentType);
    588  }
    589  return NS_ERROR_UNEXPECTED;
    590 }
    591 
    592 //-----------------------------------------------------------------------------
    593 // nsHttpChannel <private>
    594 //-----------------------------------------------------------------------------
    595 
    596 void nsHttpChannel::AddStorageAccessHeadersToRequest() {
    597  if (!StaticPrefs::dom_storage_access_enabled() ||
    598      !StaticPrefs::dom_storage_access_headers_enabled()) {
    599    return;
    600  }
    601 
    602  // check if request is eligible for storage-access
    603  uint32_t cookiePolicy = 0;
    604  if (mLoadInfo->GetCookiePolicy(&cookiePolicy) != NS_OK) {
    605    return;
    606  }
    607  if (cookiePolicy != nsILoadInfo::SEC_COOKIES_INCLUDE) {
    608    return;
    609  }
    610 
    611  // check whether we have storage-access permission set in channel
    612  nsILoadInfo::StoragePermissionState storageAccess =
    613      AntiTrackingUtils::GetStoragePermissionStateInParent(this);
    614 
    615  switch (storageAccess) {
    616    case nsILoadInfo::HasStoragePermission:
    617    case nsILoadInfo::StoragePermissionAllowListed:
    618      SetRequestHeader(nsHttp::Sec_Fetch_Storage_Access.val(), "active"_ns,
    619                       false);
    620      break;
    621    case nsILoadInfo::InactiveStoragePermission:
    622      SetRequestHeader(nsHttp::Sec_Fetch_Storage_Access.val(), "inactive"_ns,
    623                       false);
    624      break;
    625    case nsILoadInfo::DisabledStoragePermission:
    626      SetRequestHeader(nsHttp::Sec_Fetch_Storage_Access.val(), "none"_ns,
    627                       false);
    628      break;
    629    case nsILoadInfo::NoStoragePermission:
    630      break;
    631  }
    632 }
    633 
    634 bool nsHttpChannel::StorageAccessReloadedChannel() {
    635  return LoadStorageAccessReloadChannel();
    636 }
    637 
    638 nsresult nsHttpChannel::PrepareToConnect() {
    639  LOG(("nsHttpChannel::PrepareToConnect [this=%p]\n", this));
    640 
    641  // This may be async; the dictionary headers may need to fetch an origin
    642  // dictionary cache entry from disk before adding the headers.  We can
    643  // continue with channel creation, and just block on this being done later
    644  AUTO_PROFILER_FLOW_MARKER("nsHttpHandler::AddAcceptAndDictionaryHeaders",
    645                            NETWORK, Flow::FromPointer(this));
    646  // AddAcceptAndDictionaryHeaders must call this->Suspend before kicking
    647  // off the async operation that can result in calling the lambda (which
    648  // will Resume), to avoid a race condition.
    649  nsresult rv = gHttpHandler->AddAcceptAndDictionaryHeaders(
    650      mURI, mLoadInfo->GetExternalContentPolicyType(), &mRequestHead, IsHTTPS(),
    651      this, nsHttpChannel::StaticSuspend,
    652      [self = RefPtr(this)](bool aNeedsResume, DictionaryCacheEntry* aDict) {
    653        self->mDictDecompress = aDict;
    654        if (aNeedsResume) {
    655          LOG_DICTIONARIES(("Resuming after getting Dictionary headers"));
    656          self->Resume();
    657        }
    658        if (self->mDictDecompress) {
    659          LOG_DICTIONARIES(
    660              ("Added dictionary header for %p, DirectoryCacheEntry %p",
    661               self.get(), aDict));
    662          AUTO_PROFILER_FLOW_MARKER(
    663              "nsHttpHandler::AddAcceptAndDictionaryHeaders Add "
    664              "Available-Dictionary",
    665              NETWORK, Flow::FromPointer(self));
    666          // mDictDecompress is set if we added Available-Dictionary
    667          self->mDictDecompress->InUse();
    668          self->mUsingDictionary = true;
    669          PROFILER_MARKER("Dictionary Prefetch", NETWORK,
    670                          MarkerTiming::IntervalStart(), FlowMarker,
    671                          Flow::FromPointer(self));
    672          // XXX if this fails, retry the connection (we assume that the
    673          // DictionaryCacheEntry has been removed).  Failure should be only in
    674          // weird cases like no storage service.
    675          return NS_SUCCEEDED(self->mDictDecompress->Prefetch(
    676              GetLoadContextInfo(self), self->mShouldSuspendForDictionary,
    677              [self](nsresult aResult) {
    678                // this is called when the prefetch is complete to
    679                // un-Suspend the channel
    680                PROFILER_MARKER("Dictionary Prefetch", NETWORK,
    681                                MarkerTiming::IntervalEnd(), FlowMarker,
    682                                Flow::FromPointer(self));
    683                if (NS_FAILED(aResult)) {
    684                  LOG(
    685                      ("nsHttpChannel::SetupChannelForTransaction [this=%p] "
    686                       "Dictionary prefetch failed: 0x%08" PRIx32,
    687                       self.get(), static_cast<uint32_t>(aResult)));
    688                  if (self->mUsingDictionary) {
    689                    self->mDictDecompress->UseCompleted();
    690                    self->mUsingDictionary = false;
    691                  }
    692                  self->mDictDecompress = nullptr;
    693                  if (self->mSuspendedForDictionary) {
    694                    self->mSuspendedForDictionary = false;
    695                    self->Cancel(aResult);
    696                    self->Resume();
    697                  }
    698                  return;
    699                }
    700                MOZ_ASSERT(self->mDictDecompress->DictionaryReady());
    701                if (self->mSuspendedForDictionary) {
    702                  LOG(
    703                      ("nsHttpChannel::SetupChannelForTransaction [this=%p] "
    704                       "Resuming channel "
    705                       "suspended for Dictionary",
    706                       self.get()));
    707                  self->mSuspendedForDictionary = false;
    708                  self->Resume();
    709                }
    710              }));
    711        }
    712        return true;
    713      });
    714  if (NS_FAILED(rv)) return rv;
    715 
    716  // notify "http-on-modify-request-before-cookies" observers
    717  gHttpHandler->OnModifyRequestBeforeCookies(this);
    718 
    719  if (mStaleRevalidation) {
    720    // This is a revalidating channel.
    721    // The cookies (user set cookies + cookies from the cookeservice) are
    722    // already copied to the request headers when opening this channel in
    723    // PerformBackgroundCacheRevalidationNow().
    724  } else {
    725    AddCookiesToRequest();
    726  }
    727 
    728 #if defined(XP_WIN) || defined(XP_MACOSX)
    729 
    730  auto prefEnabledForCurrentContainer = [&]() {
    731    uint32_t containerId = mLoadInfo->GetOriginAttributes().mUserContextId;
    732    // Make sure that the default container ID is 0
    733    static_assert(nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID == 0);
    734 
    735    nsAutoCString prefName;
    736 #  ifdef XP_WIN
    737    prefName = nsPrintfCString("network.http.windows-sso.container-enabled.%u",
    738                               containerId);
    739 #  endif
    740 
    741 #  ifdef XP_MACOSX
    742    prefName = nsPrintfCString(
    743        "network.http.microsoft-entra-sso.container-enabled.%u", containerId);
    744 #  endif
    745 
    746    bool enabled = false;
    747    Preferences::GetBool(prefName.get(), &enabled);
    748 
    749    LOG(("Pref for %s is %d\n", prefName.get(), enabled));
    750 
    751    return enabled;
    752  };
    753 
    754 #endif  // defined(XP_WIN) || defined(XP_MACOSX)
    755 
    756 #ifdef XP_WIN
    757 
    758  // If Windows 10 SSO is enabled, we potentially add auth
    759  // information to secure top level loads (DOCUMENTs) and iframes
    760  // (SUBDOCUMENTs) that aren't anonymous or private browsing.
    761  if (StaticPrefs::network_http_windows_sso_enabled() &&
    762      mURI->SchemeIs("https") && !(mLoadFlags & LOAD_ANONYMOUS) &&
    763      !mPrivateBrowsing) {
    764    ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType();
    765    if ((type == ExtContentPolicy::TYPE_DOCUMENT ||
    766         type == ExtContentPolicy::TYPE_SUBDOCUMENT) &&
    767        prefEnabledForCurrentContainer()) {
    768      AddWindowsSSO(this);
    769    }
    770  }
    771 #endif
    772 
    773 #ifdef XP_MACOSX
    774 
    775  auto isUriMSAuthority = [&]() {
    776    nsAutoCString endPoint;
    777    nsresult rv = mURI->GetHost(endPoint);
    778    if (!NS_SUCCEEDED(rv)) {
    779      return false;
    780    }
    781    LOG(("endPoint is %s\n", endPoint.get()));
    782 
    783    return gHttpHandler->IsHostMSAuthority(endPoint);
    784  };
    785 
    786  // If macOS SSO is enabled, we potentially add auth
    787  // information to secure top level loads (DOCUMENTs) and iframes
    788  // (SUBDOCUMENTs) that aren't anonymous or private browsing.
    789  if (StaticPrefs::network_http_microsoft_entra_sso_enabled() &&
    790      mURI->SchemeIs("https") && !(mLoadFlags & LOAD_ANONYMOUS) &&
    791      !mPrivateBrowsing) {
    792    ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType();
    793    if ((type == ExtContentPolicy::TYPE_DOCUMENT ||
    794         type == ExtContentPolicy::TYPE_SUBDOCUMENT) &&
    795        prefEnabledForCurrentContainer() && isUriMSAuthority()) {
    796      nsMainThreadPtrHandle<nsHttpChannel> self(
    797          new nsMainThreadPtrHolder<nsHttpChannel>(
    798              "nsHttpChannel::PrepareToConnect::self", this));
    799      auto resultCallback = [self(self)]() {
    800        MOZ_ASSERT(NS_IsMainThread());
    801        nsresult rv = self->ContinuePrepareToConnect();
    802        if (NS_FAILED(rv)) {
    803          self->CloseCacheEntry(false);
    804          (void)self->AsyncAbort(rv);
    805        }
    806      };
    807 
    808      nsresult rv = AddMicrosoftEntraSSO(this, std::move(resultCallback));
    809 
    810      // Returns NS_OK if performRequests is called in MicrosoftEntraSSOUtils
    811      // This temporarily stops the channel setup for the delegate.
    812      if (NS_SUCCEEDED(rv)) {
    813        return rv;
    814      }
    815    }
    816  }
    817 
    818 #endif
    819 
    820  return ContinuePrepareToConnect();
    821 }
    822 
    823 nsresult nsHttpChannel::ContinuePrepareToConnect() {
    824  // notify "http-on-modify-request" observers
    825  CallOnModifyRequestObservers();
    826 
    827  return CallOrWaitForResume(
    828      [](auto* self) { return self->OnBeforeConnect(); });
    829 }
    830 
    831 void nsHttpChannel::HandleContinueCancellingByURLClassifier(
    832    nsresult aErrorCode) {
    833  MOZ_ASSERT(
    834      UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
    835  MOZ_ASSERT(!mCallOnResume, "How did that happen?");
    836 
    837  if (mSuspendCount) {
    838    LOG(
    839        ("Waiting until resume HandleContinueCancellingByURLClassifier "
    840         "[this=%p]\n",
    841         this));
    842    mCallOnResume = [aErrorCode](nsHttpChannel* self) {
    843      self->HandleContinueCancellingByURLClassifier(aErrorCode);
    844      return NS_OK;
    845    };
    846    return;
    847  }
    848 
    849  LOG(("nsHttpChannel::HandleContinueCancellingByURLClassifier [this=%p]\n",
    850       this));
    851  ContinueCancellingByURLClassifier(aErrorCode);
    852 }
    853 
    854 void nsHttpChannel::SetPriorityHeader() {
    855  nsAutoCString userSetPriority;
    856  (void)GetRequestHeader("Priority"_ns, userSetPriority);
    857  if (!userSetPriority.IsEmpty()) {
    858    // If the Priority header is set by the user, do not override it.
    859    return;
    860  }
    861 
    862  uint8_t urgency =
    863      nsHttpHandler::UrgencyFromCoSFlags(mClassOfService.Flags(), mPriority);
    864  bool incremental = mClassOfService.Incremental();
    865 
    866  nsPrintfCString value(
    867      "%s", urgency != 3 ? nsPrintfCString("u=%d", urgency).get() : "");
    868 
    869  if (incremental) {
    870    if (!value.IsEmpty()) {
    871      value.Append(", ");
    872    }
    873    value.Append("i");
    874  }
    875 
    876  if (!value.IsEmpty()) {
    877    SetRequestHeader("Priority"_ns, value, false);
    878  }
    879 }
    880 
    881 nsresult nsHttpChannel::OnBeforeConnect() {
    882  nsresult rv = NS_OK;
    883 
    884  // Check if request was cancelled during suspend AFTER on-modify-request
    885  if (mCanceled) {
    886    return mStatus;
    887  }
    888 
    889  // Check to see if we should redirect this channel elsewhere by
    890  // nsIHttpChannel.redirectTo API request
    891  if (mAPIRedirectTo) {
    892    return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
    893  }
    894 
    895  // Note that we are only setting the "Upgrade-Insecure-Requests" request
    896  // header for *all* navigational requests instead of all requests as
    897  // defined in the spec, see:
    898  // https://www.w3.org/TR/upgrade-insecure-requests/#preference
    899  ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType();
    900 
    901  if (type == ExtContentPolicy::TYPE_DOCUMENT ||
    902      type == ExtContentPolicy::TYPE_SUBDOCUMENT) {
    903    rv = SetRequestHeader("Upgrade-Insecure-Requests"_ns, "1"_ns, false);
    904    NS_ENSURE_SUCCESS(rv, rv);
    905  }
    906 
    907  if (LoadAuthRedirectedChannel()) {
    908    // This channel is a result of a redirect due to auth retry
    909    // We have already checked for HSTS upgarde in the redirecting channel.
    910    // We can safely skip those checks
    911    return ContinueOnBeforeConnect(false, rv);
    912  }
    913 
    914  SecFetch::AddSecFetchHeader(this);
    915 
    916  // Check to see if we should redirect this channel to the unstripped URI. To
    917  // revert the query stripping if the loading channel is in the content
    918  // blocking allow list.
    919  if (ContentBlockingAllowList::Check(this)) {
    920    nsCOMPtr<nsIURI> unstrippedURI;
    921    mLoadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI));
    922 
    923    if (unstrippedURI) {
    924      return AsyncCall(&nsHttpChannel::HandleAsyncRedirectToUnstrippedURI);
    925    }
    926  }
    927 
    928  nsCOMPtr<nsIPrincipal> resultPrincipal;
    929  if (!mURI->SchemeIs("https")) {
    930    nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
    931        this, getter_AddRefs(resultPrincipal));
    932  }
    933 
    934  // Check if we already know about the HSTS status of the host
    935  nsISiteSecurityService* sss = gHttpHandler->GetSSService();
    936  NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
    937  bool isSecureURI;
    938  OriginAttributes originAttributes;
    939  if (!StoragePrincipalHelper::GetOriginAttributesForHSTS(this,
    940                                                          originAttributes)) {
    941    return NS_ERROR_FAILURE;
    942  }
    943  rv = sss->IsSecureURI(mURI, originAttributes, &isSecureURI);
    944  NS_ENSURE_SUCCESS(rv, rv);
    945  // Save that on the loadInfo so it can later be consumed by
    946  // SecurityInfo.sys.mjs
    947  mLoadInfo->SetHstsStatus(isSecureURI);
    948 
    949  RefPtr<mozilla::dom::BrowsingContext> bc;
    950  mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
    951  // If bypassing the cache and we're forced offline
    952  // we can just return the error here.
    953  if (bc && bc->Top()->GetForceOffline() &&
    954      BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) {
    955    return NS_ERROR_OFFLINE;
    956  }
    957 
    958  // At this point it is no longer possible to call
    959  // HttpBaseChannel::UpgradeToSecure.
    960  StoreUpgradableToSecure(false);
    961  bool shouldUpgrade = LoadUpgradeToSecure();
    962  if (mURI->SchemeIs("http")) {
    963    OriginAttributes originAttributes;
    964    if (!StoragePrincipalHelper::GetOriginAttributesForHSTS(this,
    965                                                            originAttributes)) {
    966      return NS_ERROR_FAILURE;
    967    }
    968 
    969    if (!shouldUpgrade) {
    970      // Make sure http channel is released on main thread.
    971      // See bug 1539148 for details.
    972      nsMainThreadPtrHandle<nsHttpChannel> self(
    973          new nsMainThreadPtrHolder<nsHttpChannel>(
    974              "nsHttpChannel::OnBeforeConnect::self", this));
    975      auto resultCallback = [self(self)](bool aResult, nsresult aStatus) {
    976        MOZ_ASSERT(NS_IsMainThread());
    977 
    978        nsresult rv = self->MaybeUseHTTPSRRForUpgrade(aResult, aStatus);
    979        if (NS_FAILED(rv)) {
    980          self->CloseCacheEntry(false);
    981          (void)self->AsyncAbort(rv);
    982        }
    983      };
    984 
    985      bool willCallback = false;
    986      rv = NS_ShouldSecureUpgrade(
    987          mURI, mLoadInfo, resultPrincipal, LoadAllowSTS(), originAttributes,
    988          shouldUpgrade, std::move(resultCallback), willCallback);
    989      // If the request gets upgraded because of the HTTPS-Only mode, but no
    990      // event listener has been registered so far, we want to do that here.
    991      uint32_t httpOnlyStatus = mLoadInfo->GetHttpsOnlyStatus();
    992      if (httpOnlyStatus &
    993          nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED) {
    994        RefPtr<nsHTTPSOnlyStreamListener> httpsOnlyListener =
    995            new nsHTTPSOnlyStreamListener(mListener, mLoadInfo);
    996        mListener = httpsOnlyListener;
    997 
    998        httpOnlyStatus ^=
    999            nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED;
   1000        httpOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED;
   1001        mLoadInfo->SetHttpsOnlyStatus(httpOnlyStatus);
   1002      }
   1003      LOG(
   1004          ("nsHttpChannel::OnBeforeConnect "
   1005           "[this=%p willCallback=%d rv=%" PRIx32 "]\n",
   1006           this, willCallback, static_cast<uint32_t>(rv)));
   1007 
   1008      if (NS_FAILED(rv) || MOZ_UNLIKELY(willCallback)) {
   1009        return rv;
   1010      }
   1011    }
   1012  }
   1013 
   1014  return MaybeUseHTTPSRRForUpgrade(shouldUpgrade, NS_OK);
   1015 }
   1016 
   1017 // Returns true if the network connectivity checker indicated
   1018 // that HTTPS records can be resolved on this network - false otherwise.
   1019 // When TRR is enabled, we always return true, as resolving HTTPS
   1020 // records don't depend on the network.
   1021 static bool canUseHTTPSRRonNetwork(bool& aTRREnabled) {
   1022  // Respect the pref.
   1023  if (StaticPrefs::network_dns_force_use_https_rr()) {
   1024    aTRREnabled = true;
   1025    return true;
   1026  }
   1027 
   1028  aTRREnabled = false;
   1029 
   1030  if (nsCOMPtr<nsIDNSService> dns = mozilla::components::DNS::Service()) {
   1031    nsIDNSService::ResolverMode mode;
   1032    // If the browser is currently using TRR/DoH, then it can
   1033    // definitely resolve HTTPS records.
   1034    if (NS_SUCCEEDED(dns->GetCurrentTrrMode(&mode))) {
   1035      if (mode == nsIDNSService::MODE_TRRFIRST) {
   1036        RefPtr<TRRService> trr = TRRService::Get();
   1037        if (trr && trr->IsConfirmed()) {
   1038          aTRREnabled = true;
   1039        }
   1040      } else if (mode == nsIDNSService::MODE_TRRONLY) {
   1041        aTRREnabled = true;
   1042      }
   1043      if (aTRREnabled) {
   1044        return true;
   1045      }
   1046    }
   1047  }
   1048 
   1049  if (RefPtr<NetworkConnectivityService> ncs =
   1050          NetworkConnectivityService::GetSingleton()) {
   1051    nsINetworkConnectivityService::ConnectivityState state;
   1052    if (NS_SUCCEEDED(ncs->GetDNS_HTTPS(&state)) &&
   1053        state == nsINetworkConnectivityService::NOT_AVAILABLE) {
   1054      return false;
   1055    }
   1056  }
   1057  return true;
   1058 }
   1059 
   1060 nsresult nsHttpChannel::MaybeUseHTTPSRRForUpgrade(bool aShouldUpgrade,
   1061                                                  nsresult aStatus) {
   1062  if (NS_FAILED(aStatus)) {
   1063    return aStatus;
   1064  }
   1065 
   1066  RefPtr<mozilla::dom::BrowsingContext> bc;
   1067  mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
   1068  bool forceOffline = bc && bc->Top()->GetForceOffline();
   1069 
   1070  if (mURI->SchemeIs("https") || aShouldUpgrade || !LoadUseHTTPSSVC() ||
   1071      forceOffline) {
   1072    return ContinueOnBeforeConnect(aShouldUpgrade, aStatus);
   1073  }
   1074 
   1075  auto shouldSkipUpgradeWithHTTPSRR = [&]() -> bool {
   1076    if (mCaps & NS_HTTP_DISALLOW_HTTPS_RR) {
   1077      return true;
   1078    }
   1079 
   1080    // Skip using HTTPS RR to upgrade when this is not a top-level load and the
   1081    // loading principal is http.
   1082    if ((mLoadInfo->GetExternalContentPolicyType() !=
   1083         ExtContentPolicy::TYPE_DOCUMENT) &&
   1084        (mLoadInfo->GetLoadingPrincipal() &&
   1085         mLoadInfo->GetLoadingPrincipal()->SchemeIs("http"))) {
   1086      return true;
   1087    }
   1088 
   1089    // If the network connectivity checker indicates the network is
   1090    // blocking HTTPS requests, then we should skip them so we don't
   1091    // needlessly wait for a timeout.
   1092    bool trrEnabled = false;
   1093    if (!canUseHTTPSRRonNetwork(trrEnabled)) {
   1094      return true;
   1095    }
   1096 
   1097    // Don't block the channel when TRR is not used.
   1098    if (!trrEnabled) {
   1099      return true;
   1100    }
   1101 
   1102    auto dnsStrategy = GetProxyDNSStrategy();
   1103    if (dnsStrategy != ProxyDNSStrategy::ORIGIN) {
   1104      return true;
   1105    }
   1106 
   1107    nsAutoCString uriHost;
   1108    mURI->GetAsciiHost(uriHost);
   1109 
   1110    return gHttpHandler->IsHostExcludedForHTTPSRR(uriHost);
   1111  };
   1112 
   1113  if (shouldSkipUpgradeWithHTTPSRR()) {
   1114    StoreUseHTTPSSVC(false);
   1115    // If the website does not want to use HTTPS RR, we should set
   1116    // NS_HTTP_DISALLOW_HTTPS_RR. This is for avoiding HTTPS RR being used by
   1117    // the transaction.
   1118    DisallowHTTPSRR(mCaps);
   1119    return ContinueOnBeforeConnect(aShouldUpgrade, aStatus);
   1120  }
   1121 
   1122  if (mHTTPSSVCRecord.isSome()) {
   1123    LOG((
   1124        "nsHttpChannel::MaybeUseHTTPSRRForUpgrade [%p] mHTTPSSVCRecord is some",
   1125        this));
   1126    StoreWaitHTTPSSVCRecord(false);
   1127    bool hasHTTPSRR = (mHTTPSSVCRecord.ref() != nullptr);
   1128    return ContinueOnBeforeConnect(hasHTTPSRR, aStatus, hasHTTPSRR);
   1129  }
   1130 
   1131  LOG(("nsHttpChannel::MaybeUseHTTPSRRForUpgrade [%p] wait for HTTPS RR",
   1132       this));
   1133 
   1134  OriginAttributes originAttributes;
   1135  StoragePrincipalHelper::GetOriginAttributesForHTTPSRR(this, originAttributes);
   1136 
   1137  RefPtr<nsDNSPrefetch> resolver =
   1138      new nsDNSPrefetch(mURI, originAttributes, nsIRequest::GetTRRMode());
   1139  nsWeakPtr weakPtrThis(
   1140      do_GetWeakReference(static_cast<nsIHttpChannel*>(this)));
   1141  nsresult rv = resolver->FetchHTTPSSVC(
   1142      mCaps & NS_HTTP_REFRESH_DNS, !LoadUseHTTPSSVC(),
   1143      [weakPtrThis](nsIDNSHTTPSSVCRecord* aRecord) {
   1144        nsCOMPtr<nsIHttpChannel> channel = do_QueryReferent(weakPtrThis);
   1145        RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(channel);
   1146        if (httpChannelImpl) {
   1147          httpChannelImpl->OnHTTPSRRAvailable(aRecord);
   1148        }
   1149      });
   1150  if (NS_FAILED(rv)) {
   1151    LOG(("  FetchHTTPSSVC failed with 0x%08" PRIx32,
   1152         static_cast<uint32_t>(rv)));
   1153    return ContinueOnBeforeConnect(aShouldUpgrade, aStatus);
   1154  }
   1155 
   1156  StoreWaitHTTPSSVCRecord(true);
   1157  return NS_OK;
   1158 }
   1159 
   1160 nsresult nsHttpChannel::ContinueOnBeforeConnect(bool aShouldUpgrade,
   1161                                                nsresult aStatus,
   1162                                                bool aUpgradeWithHTTPSRR) {
   1163  LOG(
   1164      ("nsHttpChannel::ContinueOnBeforeConnect "
   1165       "[this=%p aShouldUpgrade=%d rv=%" PRIx32 "]\n",
   1166       this, aShouldUpgrade, static_cast<uint32_t>(aStatus)));
   1167 
   1168  MOZ_ASSERT(!LoadWaitHTTPSSVCRecord());
   1169 
   1170  if (NS_FAILED(aStatus)) {
   1171    return aStatus;
   1172  }
   1173 
   1174  if (aShouldUpgrade && !mURI->SchemeIs("https")) {
   1175    // only set HTTPS_RR to be responsbile for the upgrade in the loadinfo
   1176    // if it actually was responsible, otherwise the correct flag is
   1177    // already present in the loadinfo.
   1178    if (aUpgradeWithHTTPSRR) {
   1179      mLoadInfo->SetHttpsUpgradeTelemetry(nsILoadInfo::HTTPS_RR);
   1180    }
   1181    return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
   1182  }
   1183 
   1184  // ensure that we are using a valid hostname
   1185  if (!net_IsValidDNSHost(nsDependentCString(mConnectionInfo->Origin()))) {
   1186    return NS_ERROR_UNKNOWN_HOST;
   1187  }
   1188 
   1189  if (mUpgradeProtocolCallback) {
   1190    // Websockets can run over HTTP/2, but other upgrades can't.
   1191    if (mUpgradeProtocol.EqualsLiteral("websocket") &&
   1192        StaticPrefs::network_http_http2_websockets()) {
   1193      // Need to tell the conn manager that we're ok with http/2 even with
   1194      // the allow keepalive bit not set. That bit needs to stay off,
   1195      // though, in case we end up having to fallback to http/1.1 (where
   1196      // we absolutely do want to disable keepalive).
   1197      mCaps |= NS_HTTP_ALLOW_SPDY_WITHOUT_KEEPALIVE;
   1198    } else {
   1199      mCaps |= NS_HTTP_DISALLOW_SPDY;
   1200    }
   1201    // Upgrades cannot use HTTP/3.
   1202    // TODO: When mUpgradeProtocolCallback is not null, we should allow HTTP/3
   1203    // for connect-udp.
   1204    mCaps |= NS_HTTP_DISALLOW_HTTP3;
   1205    // Because NS_HTTP_STICKY_CONNECTION breaks HTTPS RR fallabck mecnahism, we
   1206    // can not use HTTPS RR for upgrade requests.
   1207    DisallowHTTPSRR(mCaps);
   1208  }
   1209 
   1210  if (LoadIsTRRServiceChannel()) {
   1211    mCaps |= NS_HTTP_LARGE_KEEPALIVE;
   1212    DisallowHTTPSRR(mCaps);
   1213  }
   1214 
   1215  if (mTransactionSticky) {
   1216    MOZ_ASSERT(LoadAuthRedirectedChannel());
   1217    // this means this is a redirected channel channel due to auth retry and a
   1218    // connection based auth scheme was used
   1219    // we have a reference to the old-transaction with sticky connection which
   1220    // we need to use
   1221    mCaps |= NS_HTTP_STICKY_CONNECTION;
   1222  }
   1223 
   1224  mCaps |= NS_HTTP_TRR_FLAGS_FROM_MODE(nsIRequest::GetTRRMode());
   1225 
   1226  // Finalize ConnectionInfo flags before SpeculativeConnect
   1227  mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
   1228  mConnectionInfo->SetPrivate(mPrivateBrowsing);
   1229  mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY);
   1230  mConnectionInfo->SetBeConservative((mCaps & NS_HTTP_BE_CONSERVATIVE) ||
   1231                                     LoadBeConservative());
   1232  mConnectionInfo->SetTlsFlags(mTlsFlags);
   1233  mConnectionInfo->SetIsTrrServiceChannel(LoadIsTRRServiceChannel());
   1234  mConnectionInfo->SetTRRMode(nsIRequest::GetTRRMode());
   1235  mConnectionInfo->SetIPv4Disabled(mCaps & NS_HTTP_DISABLE_IPV4);
   1236  mConnectionInfo->SetIPv6Disabled(mCaps & NS_HTTP_DISABLE_IPV6);
   1237  mConnectionInfo->SetAnonymousAllowClientCert(
   1238      (mLoadFlags & LOAD_ANONYMOUS_ALLOW_CLIENT_CERT) != 0);
   1239 
   1240  if (mWebTransportSessionEventListener) {
   1241    nsTArray<RefPtr<nsIWebTransportHash>> aServerCertHashes;
   1242    nsresult rv;
   1243    nsCOMPtr<WebTransportConnectionSettings> wtconSettings =
   1244        do_QueryInterface(mWebTransportSessionEventListener, &rv);
   1245    NS_ENSURE_SUCCESS(rv, rv);
   1246 
   1247    wtconSettings->GetServerCertificateHashes(aServerCertHashes);
   1248    gHttpHandler->ConnMgr()->StoreServerCertHashes(
   1249        mConnectionInfo, gHttpHandler->IsHttp2Excluded(mConnectionInfo),
   1250        !Http3Allowed(), std::move(aServerCertHashes));
   1251  }
   1252 
   1253  if (ShouldIntercept()) {
   1254    return RedirectToInterceptedChannel();
   1255  }
   1256 
   1257  // notify "http-on-before-connect" observers
   1258  gHttpHandler->OnBeforeConnect(this);
   1259 
   1260  return CallOrWaitForResume([](auto* self) { return self->Connect(); });
   1261 }
   1262 
   1263 class MOZ_STACK_CLASS AddResponseHeadersToResponseHead final
   1264    : public nsIHttpHeaderVisitor {
   1265 public:
   1266  explicit AddResponseHeadersToResponseHead(nsHttpResponseHead* aResponseHead)
   1267      : mResponseHead(aResponseHead) {}
   1268 
   1269  NS_IMETHOD VisitHeader(const nsACString& aHeader,
   1270                         const nsACString& aValue) override {
   1271    nsAutoCString headerLine = aHeader + ": "_ns + aValue;
   1272    DebugOnly<nsresult> rv = mResponseHead->ParseHeaderLine(headerLine);
   1273    MOZ_ASSERT(NS_SUCCEEDED(rv));
   1274 
   1275    return NS_OK;
   1276  }
   1277 
   1278  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
   1279 
   1280  // Stub AddRef/Release since this is a stack class.
   1281  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override {
   1282    return ++mRefCnt;
   1283  }
   1284 
   1285  NS_IMETHOD_(MozExternalRefCountType) Release(void) override {
   1286    return --mRefCnt;
   1287  }
   1288 
   1289  virtual ~AddResponseHeadersToResponseHead() {
   1290    MOZ_DIAGNOSTIC_ASSERT(mRefCnt == 0);
   1291  }
   1292 
   1293 private:
   1294  nsHttpResponseHead* mResponseHead;
   1295 
   1296  nsrefcnt mRefCnt = 0;
   1297 };
   1298 
   1299 NS_IMPL_QUERY_INTERFACE(AddResponseHeadersToResponseHead, nsIHttpHeaderVisitor)
   1300 
   1301 nsresult nsHttpChannel::HandleOverrideResponse() {
   1302  // Start building a response with the data from mOverrideResponse.
   1303  mResponseHead = MakeUnique<nsHttpResponseHead>();
   1304 
   1305  // Apply override response status code and status text.
   1306  uint32_t statusCode;
   1307  nsresult rv = mOverrideResponse->GetResponseStatus(&statusCode);
   1308  NS_ENSURE_SUCCESS(rv, rv);
   1309 
   1310  nsAutoCString statusText;
   1311  rv = mOverrideResponse->GetResponseStatusText(statusText);
   1312  NS_ENSURE_SUCCESS(rv, rv);
   1313 
   1314  // Hardcoding protocol HTTP/1.1
   1315  nsPrintfCString line("HTTP/1.1 %u %s", statusCode, statusText.get());
   1316  rv = mResponseHead->ParseStatusLine(line);
   1317  NS_ENSURE_SUCCESS(rv, rv);
   1318 
   1319  // Apply override response headers.
   1320  AddResponseHeadersToResponseHead visitor(mResponseHead.get());
   1321  rv = mOverrideResponse->VisitResponseHeaders(&visitor);
   1322  NS_ENSURE_SUCCESS(rv, rv);
   1323 
   1324  if (WillRedirect(*mResponseHead)) {
   1325    // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here,
   1326    // to avoid event dispatching latency.
   1327    LOG(("Skipping read of overridden response redirect entity\n"));
   1328    return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
   1329  }
   1330 
   1331  // This block parses the cookie header, collects any cookie changes,
   1332  // and sends them to the parent actor.
   1333  {
   1334    RefPtr<HttpChannelParent> httpParent;
   1335    RefPtr<CookieObserver> cookieObserver;
   1336 
   1337    CookieServiceParent::CookieProcessingGuard cookieProcessingGuard;
   1338    MaybeInitializeCookieProcessingGuard(
   1339        this, cookieProcessingGuard, cookieObserver, httpParent, statusCode);
   1340 
   1341    // Handle Set-Cookie headers as if the response was from networking.
   1342    CookieVisitor cookieVisitor(mResponseHead.get());
   1343    SetCookieHeaders(cookieVisitor.CookieHeaders());
   1344 
   1345    if (cookieObserver) {
   1346      nsTArray<CookieChange> cookieChanges;
   1347      cookieObserver->StealChanges(cookieChanges);
   1348 
   1349      if (!cookieChanges.IsEmpty()) {
   1350        MOZ_ASSERT(httpParent);
   1351        httpParent->SetCookieChanges(std::move(cookieChanges));
   1352      }
   1353    }
   1354  }
   1355 
   1356  rv = ProcessSecurityHeaders();
   1357  if (NS_FAILED(rv)) {
   1358    NS_WARNING("ProcessSecurityHeaders failed, continuing load.");
   1359  }
   1360 
   1361  if ((statusCode < 500) && (statusCode != 421)) {
   1362    ProcessAltService();
   1363  }
   1364 
   1365  nsAutoCString body;
   1366  rv = mOverrideResponse->GetResponseBody(body);
   1367  NS_ENSURE_SUCCESS(rv, rv);
   1368 
   1369  nsCOMPtr<nsIInputStream> stringStream;
   1370  rv = NS_NewCStringInputStream(getter_AddRefs(stringStream), body);
   1371  NS_ENSURE_SUCCESS(rv, rv);
   1372  rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), stringStream, 0, 0,
   1373                                 true);
   1374  if (NS_FAILED(rv)) {
   1375    stringStream->Close();
   1376    return rv;
   1377  }
   1378 
   1379  rv = mCachePump->AsyncRead(this);
   1380  if (NS_FAILED(rv)) return rv;
   1381 
   1382  return NS_OK;
   1383 }
   1384 
   1385 nsresult nsHttpChannel::Connect() {
   1386  LOG(("nsHttpChannel::Connect [this=%p]\n", this));
   1387 
   1388  if (mAPIRedirectTo) {
   1389    LOG(("nsHttpChannel::Connect [transparent=%d]\n",
   1390         mAPIRedirectTo->second()));
   1391 
   1392    nsresult rv = StartRedirectChannelToURI(
   1393        mAPIRedirectTo->first(),
   1394        mAPIRedirectTo->second() ? nsIChannelEventSink::REDIRECT_PERMANENT |
   1395                                       nsIChannelEventSink::REDIRECT_TRANSPARENT
   1396                                 : nsIChannelEventSink::REDIRECT_PERMANENT);
   1397    mAPIRedirectTo = Nothing();
   1398    if (NS_SUCCEEDED(rv)) {
   1399      return NS_OK;
   1400    }
   1401    return NS_ERROR_FAILURE;
   1402  }
   1403 
   1404  // If mOverrideResponse is set, bypass the rest of the connection and reply
   1405  // immediately with a response built using the data from mOverrideResponse.
   1406  if (mOverrideResponse) {
   1407    return HandleOverrideResponse();
   1408  }
   1409 
   1410  // Don't allow resuming when cache must be used
   1411  if (LoadResuming() && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
   1412    LOG(("Resuming from cache is not supported yet"));
   1413    return NS_ERROR_DOCUMENT_NOT_CACHED;
   1414  }
   1415 
   1416  // Step 8.18 of HTTP-network-or-cache fetch
   1417  // https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
   1418  nsAutoCString rangeVal;
   1419  if (NS_SUCCEEDED(GetRequestHeader("Range"_ns, rangeVal))) {
   1420    SetRequestHeader("Accept-Encoding"_ns, "identity"_ns, false);
   1421  }
   1422 
   1423  if (mRequestHead.IsPost() || mRequestHead.IsPatch()) {
   1424    // If the post id is already set then this is an attempt to replay
   1425    // a post/patch transaction via the cache.  Otherwise, we need a unique
   1426    // post id for this transaction.
   1427    if (mPostID == 0) {
   1428      mPostID = gHttpHandler->GenerateUniqueID();
   1429    }
   1430 
   1431    if (StaticPrefs::network_http_idempotencyKey_enabled() &&
   1432        !mRequestHead.HasHeader(nsHttp::Idempotency_Key)) {
   1433      // check if we need to add
   1434      // idempotency-key header
   1435      // See Bug 1830022 for more details
   1436      nsAutoCString key;
   1437      gHttpHandler->GenerateIdempotencyKeyForPost(mPostID, mLoadInfo, key);
   1438      MOZ_ALWAYS_SUCCEEDS(
   1439          mRequestHead.SetHeader(nsHttp::Idempotency_Key, key, false));
   1440    }
   1441  }
   1442 
   1443 #ifdef MOZ_WIDGET_ANDROID
   1444  bool val = false;
   1445  if (nsIOService::ShouldAddAdditionalSearchHeaders(mURI, &val)) {
   1446    SetRequestHeader("X-Search-Subdivision"_ns, val ? "1"_ns : "0"_ns, false);
   1447  }
   1448 #endif
   1449 
   1450  bool isTrackingResource = IsThirdPartyTrackingResource();
   1451  LOG(("nsHttpChannel %p tracking resource=%d, cos=%lu, inc=%d", this,
   1452       isTrackingResource, mClassOfService.Flags(),
   1453       mClassOfService.Incremental()));
   1454 
   1455  if (isTrackingResource) {
   1456    AddClassFlags(nsIClassOfService::Tail);
   1457  }
   1458 
   1459  if (WaitingForTailUnblock()) {
   1460    MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock);
   1461    mOnTailUnblock = &nsHttpChannel::ConnectOnTailUnblock;
   1462    return NS_OK;
   1463  }
   1464 
   1465  return ConnectOnTailUnblock();
   1466 }
   1467 
   1468 nsresult nsHttpChannel::ConnectOnTailUnblock() {
   1469  nsresult rv;
   1470 
   1471  LOG(("nsHttpChannel::ConnectOnTailUnblock [this=%p]\n", this));
   1472  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::ConnectOnTailUnblock", NETWORK,
   1473                            Flow::FromPointer(this));
   1474 
   1475  // Consider opening a TCP connection right away.
   1476  SpeculativeConnect();
   1477 
   1478  // open a cache entry for this channel...
   1479  rv = OpenCacheEntry(mURI->SchemeIs("https"));
   1480 
   1481  // do not continue if asyncOpenCacheEntry is in progress
   1482  if (AwaitingCacheCallbacks()) {
   1483    LOG(("nsHttpChannel::Connect %p AwaitingCacheCallbacks forces async\n",
   1484         this));
   1485    MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state");
   1486 
   1487    if (mNetworkTriggered && mWaitingForProxy) {
   1488      // Someone has called TriggerNetwork(), meaning we are racing the
   1489      // network with the cache.
   1490      mWaitingForProxy = false;
   1491      return ContinueConnect();
   1492    }
   1493 
   1494    return NS_OK;
   1495  }
   1496 
   1497  if (NS_FAILED(rv)) {
   1498    LOG(("OpenCacheEntry failed [rv=%" PRIx32 "]\n",
   1499         static_cast<uint32_t>(rv)));
   1500    // if this channel is only allowed to pull from the cache, then
   1501    // we must fail if we were unable to open a cache entry.
   1502    if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
   1503      return NS_ERROR_DOCUMENT_NOT_CACHED;
   1504    }
   1505    // otherwise, let's just proceed without using the cache.
   1506  }
   1507 
   1508  if (mRaceCacheWithNetwork && ((mCacheEntry && !CachedContentIsValid() &&
   1509                                 (mDidReval || LoadCachedContentIsPartial())) ||
   1510                                mIgnoreCacheEntry)) {
   1511    // We won't send the conditional request because the unconditional
   1512    // request was already sent (see bug 1377223).
   1513    glean::network::race_cache_validation
   1514        .EnumGet(glean::network::RaceCacheValidationLabel::eNotsent)
   1515        .Add();
   1516  }
   1517 
   1518  // When racing, if OnCacheEntryAvailable is called before AsyncOpenURI
   1519  // returns, then we may not have started reading from the cache.
   1520  // If the content is valid, we should attempt to do so, as technically the
   1521  // cache has won the race.
   1522  if (mRaceCacheWithNetwork && CachedContentIsValid()) {
   1523    (void)ReadFromCache();
   1524  }
   1525 
   1526  return TriggerNetwork();
   1527 }
   1528 
   1529 nsresult nsHttpChannel::ContinueConnect() {
   1530  // If we need to start a CORS preflight, do it now!
   1531  // Note that it is important to do this before the early returns below.
   1532  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::ContinueConnect", NETWORK,
   1533                            Flow::FromPointer(this));
   1534  if (!LoadIsCorsPreflightDone() && LoadRequireCORSPreflight()) {
   1535    MOZ_ASSERT(!mPreflightChannel);
   1536    nsresult rv = nsCORSListenerProxy::StartCORSPreflight(
   1537        this, this, mUnsafeHeaders, getter_AddRefs(mPreflightChannel));
   1538    return rv;
   1539  }
   1540 
   1541  MOZ_RELEASE_ASSERT(!LoadRequireCORSPreflight() || LoadIsCorsPreflightDone(),
   1542                     "CORS preflight must have been finished by the time we "
   1543                     "do the rest of ContinueConnect");
   1544 
   1545  RefPtr<mozilla::dom::BrowsingContext> bc;
   1546  mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
   1547 
   1548  // we may or may not have a cache entry at this point
   1549  if (mCacheEntry) {
   1550    // read straight from the cache if possible...
   1551    if (CachedContentIsValid()) {
   1552      // If we're forced offline, and set to bypass the cache, return offline.
   1553      if (bc && bc->Top()->GetForceOffline() &&
   1554          BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) {
   1555        return NS_ERROR_OFFLINE;
   1556      }
   1557 
   1558      nsRunnableMethod<nsHttpChannel>* event = nullptr;
   1559      nsresult rv;
   1560      if (!LoadCachedContentIsPartial()) {
   1561        rv = AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse, &event);
   1562        if (NS_FAILED(rv)) {
   1563          LOG(("  AsyncCall failed (%08x)", static_cast<uint32_t>(rv)));
   1564        }
   1565      }
   1566      rv = ReadFromCache();
   1567      if (NS_FAILED(rv) && event) {
   1568        event->Revoke();
   1569      }
   1570 
   1571      AccumulateCacheHitTelemetry(kCacheHit, this);
   1572      mCacheDisposition = kCacheHit;
   1573 
   1574      return rv;
   1575    }
   1576    if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
   1577      // the cache contains the requested resource, but it must be
   1578      // validated before we can reuse it.  since we are not allowed
   1579      // to hit the net, there's nothing more to do.  the document
   1580      // is effectively not in the cache.
   1581      LOG(("  !CachedContentIsValid() && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
   1582      return NS_ERROR_DOCUMENT_NOT_CACHED;
   1583    }
   1584  } else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
   1585    LOG(("  !mCacheEntry && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
   1586    return NS_ERROR_DOCUMENT_NOT_CACHED;
   1587  }
   1588 
   1589  if (mLoadFlags & LOAD_NO_NETWORK_IO) {
   1590    LOG(("  mLoadFlags & LOAD_NO_NETWORK_IO"));
   1591    return NS_ERROR_DOCUMENT_NOT_CACHED;
   1592  }
   1593 
   1594  // We're about to hit the network. Don't if we're forced offline.
   1595  if (bc && bc->Top()->GetForceOffline()) {
   1596    return NS_ERROR_OFFLINE;
   1597  }
   1598 
   1599  // hit the net...
   1600  nsresult rv = DoConnect(mTransactionSticky);
   1601  mTransactionSticky = nullptr;
   1602  return rv;
   1603 }
   1604 
   1605 nsresult nsHttpChannel::DoConnect(HttpTransactionShell* aTransWithStickyConn) {
   1606  LOG(("nsHttpChannel::DoConnect [this=%p]\n", this));
   1607 
   1608  if (!mDNSBlockingPromise.IsEmpty()) {
   1609    LOG(("  waiting for DNS prefetch"));
   1610 
   1611    // Transaction is passed only from auth retry for which we will definitely
   1612    // not block on DNS to alter the origin server name for IP; it has already
   1613    // been done.
   1614    MOZ_ASSERT(!aTransWithStickyConn);
   1615    MOZ_ASSERT(mDNSBlockingThenable);
   1616 
   1617    nsCOMPtr<nsISerialEventTarget> target(do_GetMainThread());
   1618    RefPtr<nsHttpChannel> self(this);
   1619    mDNSBlockingThenable->Then(
   1620        target, __func__,
   1621        [self](const nsCOMPtr<nsIDNSRecord>& aRec) {
   1622          nsresult rv = self->DoConnectActual(nullptr);
   1623          if (NS_FAILED(rv)) {
   1624            self->CloseCacheEntry(false);
   1625            (void)self->AsyncAbort(rv);
   1626          }
   1627        },
   1628        [self](nsresult err) {
   1629          self->CloseCacheEntry(false);
   1630          (void)self->AsyncAbort(err);
   1631        });
   1632 
   1633    // The connection will continue when the promise is resolved in
   1634    // OnLookupComplete.
   1635    return NS_OK;
   1636  }
   1637 
   1638  return DoConnectActual(aTransWithStickyConn);
   1639 }
   1640 
   1641 nsresult nsHttpChannel::DoConnectActual(
   1642    HttpTransactionShell* aTransWithStickyConn) {
   1643  LOG(("nsHttpChannel::DoConnectActual [this=%p, aTransWithStickyConn=%p]\n",
   1644       this, aTransWithStickyConn));
   1645 
   1646  nsresult rv = SetupChannelForTransaction();
   1647  if (NS_FAILED(rv)) {
   1648    return rv;
   1649  }
   1650 
   1651  return CallOrWaitForResume(
   1652      [trans = RefPtr(aTransWithStickyConn)](auto* self) {
   1653        return self->DispatchTransaction(trans);
   1654      });
   1655 }
   1656 
   1657 nsresult nsHttpChannel::DispatchTransaction(
   1658    HttpTransactionShell* aTransWithStickyConn) {
   1659  LOG(("nsHttpChannel::DispatchTransaction [this=%p, aTransWithStickyConn=%p]",
   1660       this, aTransWithStickyConn));
   1661  nsresult rv = InitTransaction();
   1662  if (NS_FAILED(rv)) {
   1663    return rv;
   1664  }
   1665  if (aTransWithStickyConn) {
   1666    rv = gHttpHandler->InitiateTransactionWithStickyConn(
   1667        mTransaction, mPriority, aTransWithStickyConn);
   1668  } else {
   1669    rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
   1670  }
   1671 
   1672  if (NS_FAILED(rv)) {
   1673    return rv;
   1674  }
   1675 
   1676  rv = mTransaction->AsyncRead(this, getter_AddRefs(mTransactionPump));
   1677  if (NS_FAILED(rv)) {
   1678    return rv;
   1679  }
   1680 
   1681  uint32_t suspendCount = mSuspendCount;
   1682  if (LoadAsyncResumePending()) {
   1683    LOG(
   1684        ("  Suspend()'ing transaction pump once because of async resume pending"
   1685         ", sc=%u, pump=%p, this=%p",
   1686         suspendCount, mTransactionPump.get(), this));
   1687    ++suspendCount;
   1688  }
   1689  while (suspendCount--) {
   1690    mTransactionPump->Suspend();
   1691  }
   1692 
   1693  return NS_OK;
   1694 }
   1695 
   1696 void nsHttpChannel::SpeculativeConnect() {
   1697  // Before we take the latency hit of dealing with the cache, try and
   1698  // get the TCP (and SSL) handshakes going so they can overlap.
   1699 
   1700  // don't speculate if we are offline, when doing http upgrade (i.e.
   1701  // websockets bootstrap), or if we can't do keep-alive (because then we
   1702  // couldn't reuse the speculative connection anyhow).
   1703  RefPtr<mozilla::dom::BrowsingContext> bc;
   1704  mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
   1705 
   1706  if (gIOService->IsOffline() || mUpgradeProtocolCallback ||
   1707      !(mCaps & NS_HTTP_ALLOW_KEEPALIVE) ||
   1708      (bc && bc->Top()->GetForceOffline())) {
   1709    return;
   1710  }
   1711 
   1712  // LOAD_ONLY_FROM_CACHE and LOAD_NO_NETWORK_IO must not hit network.
   1713  // LOAD_FROM_CACHE is unlikely to hit network, so skip preconnects for it.
   1714  if (mLoadFlags &
   1715      (LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE | LOAD_NO_NETWORK_IO)) {
   1716    return;
   1717  }
   1718 
   1719  if (LoadAllowStaleCacheContent()) {
   1720    return;
   1721  }
   1722 
   1723  nsCOMPtr<nsIInterfaceRequestor> callbacks;
   1724  NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
   1725                                         getter_AddRefs(callbacks));
   1726  if (!callbacks) return;
   1727  bool httpsRRAllowed = !(mCaps & NS_HTTP_DISALLOW_HTTPS_RR);
   1728  (void)gHttpHandler->MaybeSpeculativeConnectWithHTTPSRR(
   1729      mConnectionInfo, callbacks,
   1730      mCaps & (NS_HTTP_DISALLOW_SPDY | NS_HTTP_TRR_MODE_MASK |
   1731               NS_HTTP_DISABLE_IPV4 | NS_HTTP_DISABLE_IPV6 |
   1732               NS_HTTP_DISALLOW_HTTP3 | NS_HTTP_REFRESH_DNS),
   1733      nsHttpHandler::EchConfigEnabled() && httpsRRAllowed);
   1734 }
   1735 
   1736 void nsHttpChannel::DoNotifyListenerCleanup() {
   1737  // We don't need this info anymore
   1738  CleanRedirectCacheChainIfNecessary();
   1739 }
   1740 
   1741 void nsHttpChannel::ReleaseListeners() {
   1742  HttpBaseChannel::ReleaseListeners();
   1743  mChannelClassifier = nullptr;
   1744  mWarningReporter = nullptr;
   1745  mEarlyHintObserver = nullptr;
   1746  mWebTransportSessionEventListener = nullptr;
   1747 
   1748  for (StreamFilterRequest& request : mStreamFilterRequests) {
   1749    request.mPromise->Reject(false, __func__);
   1750  }
   1751  mStreamFilterRequests.Clear();
   1752 }
   1753 
   1754 void nsHttpChannel::DoAsyncAbort(nsresult aStatus) {
   1755  (void)AsyncAbort(aStatus);
   1756 }
   1757 
   1758 void nsHttpChannel::HandleAsyncRedirect() {
   1759  MOZ_ASSERT(!mCallOnResume, "How did that happen?");
   1760 
   1761  if (mSuspendCount) {
   1762    LOG(("Waiting until resume to do async redirect [this=%p]\n", this));
   1763    mCallOnResume = [](nsHttpChannel* self) {
   1764      self->HandleAsyncRedirect();
   1765      return NS_OK;
   1766    };
   1767    return;
   1768  }
   1769 
   1770  nsresult rv = NS_OK;
   1771 
   1772  LOG(("nsHttpChannel::HandleAsyncRedirect [this=%p]\n", this));
   1773 
   1774  // since this event is handled asynchronously, it is possible that this
   1775  // channel could have been canceled, in which case there would be no point
   1776  // in processing the redirect.
   1777  if (NS_SUCCEEDED(mStatus)) {
   1778    PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
   1779    rv = AsyncProcessRedirection(mResponseHead->Status());
   1780    if (NS_FAILED(rv)) {
   1781      PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
   1782      // TODO: if !DoNotRender3xxBody(), render redirect body instead.
   1783      // But first we need to cache 3xx bodies (bug 748510)
   1784      rv = ContinueHandleAsyncRedirect(rv);
   1785      MOZ_ASSERT(NS_SUCCEEDED(rv));
   1786    }
   1787  } else {
   1788    rv = ContinueHandleAsyncRedirect(mStatus);
   1789    MOZ_ASSERT(NS_SUCCEEDED(rv));
   1790  }
   1791 }
   1792 
   1793 nsresult nsHttpChannel::ContinueHandleAsyncRedirect(nsresult rv) {
   1794  if (NS_FAILED(rv)) {
   1795    // If AsyncProcessRedirection fails, then we have to send out the
   1796    // OnStart/OnStop notifications.
   1797    LOG(("ContinueHandleAsyncRedirect got failure result [rv=%" PRIx32 "]\n",
   1798         static_cast<uint32_t>(rv)));
   1799 
   1800    bool redirectsEnabled = !mLoadInfo->GetDontFollowRedirects();
   1801 
   1802    if (redirectsEnabled) {
   1803      // TODO: stop failing original channel if redirect vetoed?
   1804      mStatus = rv;
   1805 
   1806      DoNotifyListener();
   1807 
   1808      // Blow away cache entry if we couldn't process the redirect
   1809      // for some reason (the cache entry might be corrupt).
   1810      if (mCacheEntry) {
   1811        mCacheEntry->AsyncDoom(nullptr);
   1812      }
   1813    } else {
   1814      DoNotifyListener();
   1815    }
   1816  }
   1817 
   1818  CloseCacheEntry(true);
   1819 
   1820  StoreIsPending(false);
   1821 
   1822  if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
   1823 
   1824  return NS_OK;
   1825 }
   1826 
   1827 void nsHttpChannel::HandleAsyncNotModified() {
   1828  MOZ_ASSERT(!mCallOnResume, "How did that happen?");
   1829 
   1830  if (mSuspendCount) {
   1831    LOG(("Waiting until resume to do async not-modified [this=%p]\n", this));
   1832    mCallOnResume = [](nsHttpChannel* self) {
   1833      self->HandleAsyncNotModified();
   1834      return NS_OK;
   1835    };
   1836    return;
   1837  }
   1838 
   1839  LOG(("nsHttpChannel::HandleAsyncNotModified [this=%p]\n", this));
   1840 
   1841  DoNotifyListener();
   1842 
   1843  CloseCacheEntry(false);
   1844 
   1845  StoreIsPending(false);
   1846 
   1847  if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
   1848 }
   1849 
   1850 nsresult nsHttpChannel::SetupChannelForTransaction() {
   1851  LOG((
   1852      "nsHttpChannel::SetupChannelForTransaction [this=%p, cos=%lu, inc=%d "
   1853      "prio=%d]\n",
   1854      this, mClassOfService.Flags(), mClassOfService.Incremental(), mPriority));
   1855 
   1856  NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED);
   1857 
   1858  nsresult rv;
   1859 
   1860  mozilla::MutexAutoLock lock(mRCWNLock);
   1861 
   1862  if (StaticPrefs::network_http_priority_header_enabled()) {
   1863    SetPriorityHeader();
   1864  }
   1865 
   1866  // If we're racing cache with network, conditional or byte range header
   1867  // could be added in OnCacheEntryCheck. We cannot send conditional request
   1868  // without having the entry, so we need to remove the headers here and
   1869  // ignore the cache entry in OnCacheEntryAvailable.
   1870  if (mRaceCacheWithNetwork && AwaitingCacheCallbacks()) {
   1871    if (mDidReval) {
   1872      LOG(("  Removing conditional request headers"));
   1873      UntieValidationRequest();
   1874      mDidReval = false;
   1875      mIgnoreCacheEntry = true;
   1876    }
   1877 
   1878    if (LoadCachedContentIsPartial()) {
   1879      LOG(("  Removing byte range request headers"));
   1880      UntieByteRangeRequest();
   1881      StoreCachedContentIsPartial(false);
   1882      mIgnoreCacheEntry = true;
   1883    }
   1884 
   1885    if (mIgnoreCacheEntry) {
   1886      mAvailableCachedAltDataType.Truncate();
   1887      StoreDeliveringAltData(false);
   1888      mAltDataLength = -1;
   1889      mCacheInputStream.CloseAndRelease();
   1890    }
   1891  }
   1892 
   1893  StoreUsedNetwork(1);
   1894 
   1895  if (!LoadAllowSpdy()) {
   1896    mCaps |= NS_HTTP_DISALLOW_SPDY;
   1897  }
   1898  if (!LoadAllowHttp3()) {
   1899    mCaps |= NS_HTTP_DISALLOW_HTTP3;
   1900  }
   1901  if (LoadBeConservative()) {
   1902    mCaps |= NS_HTTP_BE_CONSERVATIVE;
   1903  }
   1904 
   1905  if (mLoadFlags & LOAD_ANONYMOUS_ALLOW_CLIENT_CERT) {
   1906    mCaps |= NS_HTTP_LOAD_ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT;
   1907  }
   1908 
   1909  if (nsContentUtils::ShouldResistFingerprinting(this,
   1910                                                 RFPTarget::HttpUserAgent)) {
   1911    mCaps |= NS_HTTP_USE_RFP;
   1912  }
   1913 
   1914  // Use the URI path if not proxying (transparent proxying such as proxy
   1915  // CONNECT does not count here). Also figure out what HTTP version to use.
   1916  nsAutoCString buf, path;
   1917  nsCString* requestURI;
   1918 
   1919  // This is the normal e2e H1 path syntax "/index.html"
   1920  rv = mURI->GetPathQueryRef(path);
   1921  if (NS_FAILED(rv)) {
   1922    return rv;
   1923  }
   1924 
   1925  // path may contain UTF-8 characters, so ensure that they're escaped.
   1926  if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII | esc_Spaces,
   1927                   buf)) {
   1928    requestURI = &buf;
   1929  } else {
   1930    requestURI = &path;
   1931  }
   1932 
   1933  // trim off the #ref portion if any...
   1934  int32_t ref1 = requestURI->FindChar('#');
   1935  if (ref1 != kNotFound) {
   1936    requestURI->SetLength(ref1);
   1937  }
   1938 
   1939  if (mConnectionInfo->UsingConnect() || !mConnectionInfo->UsingHttpProxy()) {
   1940    mRequestHead.SetVersion(gHttpHandler->HttpVersion());
   1941  } else {
   1942    mRequestHead.SetPath(*requestURI);
   1943 
   1944    // RequestURI should be the absolute uri H1 proxy syntax
   1945    // "http://foo/index.html" so we will overwrite the relative version in
   1946    // requestURI
   1947    rv = mURI->GetUserPass(buf);
   1948    if (NS_FAILED(rv)) return rv;
   1949    if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) ||
   1950                           strncmp(mSpec.get(), "https:", 6) == 0)) {
   1951      nsCOMPtr<nsIURI> tempURI = nsIOService::CreateExposableURI(mURI);
   1952      rv = tempURI->GetAsciiSpec(path);
   1953      if (NS_FAILED(rv)) return rv;
   1954      requestURI = &path;
   1955    } else {
   1956      requestURI = &mSpec;
   1957    }
   1958 
   1959    // trim off the #ref portion if any...
   1960    int32_t ref2 = requestURI->FindChar('#');
   1961    if (ref2 != kNotFound) {
   1962      requestURI->SetLength(ref2);
   1963    }
   1964 
   1965    mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion());
   1966  }
   1967 
   1968  mRequestHead.SetRequestURI(*requestURI);
   1969 
   1970  // set the request time for cache expiration calculations
   1971  mRequestTime = NowInSeconds();
   1972  StoreRequestTimeInitialized(true);
   1973 
   1974  // if doing a reload, force end-to-end
   1975  if (mLoadFlags & LOAD_BYPASS_CACHE) {
   1976    // We need to send 'Pragma:no-cache' to inhibit proxy caching even if
   1977    // no proxy is configured since we might be talking with a transparent
   1978    // proxy, i.e. one that operates at the network level.  See bug #14772.
   1979    // But we should not touch Pragma if Cache-Control is already set
   1980    // (https://fetch.spec.whatwg.org/#ref-for-concept-request-cache-mode%E2%91%A3)
   1981    if (!mRequestHead.HasHeader(nsHttp::Pragma)) {
   1982      rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
   1983      MOZ_ASSERT(NS_SUCCEEDED(rv));
   1984    }
   1985    // If we're configured to speak HTTP/1.1 then also send 'Cache-control:
   1986    // no-cache'. But likewise don't touch Cache-Control if it's already set.
   1987    if (mRequestHead.Version() >= HttpVersion::v1_1 &&
   1988        !mRequestHead.HasHeader(nsHttp::Cache_Control)) {
   1989      rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true);
   1990      MOZ_ASSERT(NS_SUCCEEDED(rv));
   1991    }
   1992  } else if (mLoadFlags & VALIDATE_ALWAYS) {
   1993    // We need to send 'Cache-Control: max-age=0' to force each cache along
   1994    // the path to the origin server to revalidate its own entry, if any,
   1995    // with the next cache or server.  See bug #84847.
   1996    //
   1997    // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache'
   1998    //
   1999    // But don't send the headers if they're already set:
   2000    // https://fetch.spec.whatwg.org/#ref-for-concept-request-cache-mode%E2%91%A2
   2001    if (mRequestHead.Version() >= HttpVersion::v1_1) {
   2002      if (!mRequestHead.HasHeader(nsHttp::Cache_Control)) {
   2003        rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "max-age=0",
   2004                                        true);
   2005      }
   2006    } else {
   2007      if (!mRequestHead.HasHeader(nsHttp::Pragma)) {
   2008        rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true);
   2009      }
   2010    }
   2011    MOZ_ASSERT(NS_SUCCEEDED(rv));
   2012  }
   2013 
   2014  if (LoadResuming()) {
   2015    char byteRange[32];
   2016    SprintfLiteral(byteRange, "bytes=%" PRIu64 "-", mStartPos);
   2017    rv = mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(byteRange));
   2018    MOZ_ASSERT(NS_SUCCEEDED(rv));
   2019 
   2020    if (!mEntityID.IsEmpty()) {
   2021      // Also, we want an error if this resource changed in the meantime
   2022      // Format of the entity id is: escaped_etag/size/lastmod
   2023      nsCString::const_iterator start, end, slash;
   2024      mEntityID.BeginReading(start);
   2025      mEntityID.EndReading(end);
   2026      mEntityID.BeginReading(slash);
   2027 
   2028      if (FindCharInReadable('/', slash, end)) {
   2029        nsAutoCString ifMatch;
   2030        rv = mRequestHead.SetHeader(
   2031            nsHttp::If_Match,
   2032            NS_UnescapeURL(Substring(start, slash), 0, ifMatch));
   2033        MOZ_ASSERT(NS_SUCCEEDED(rv));
   2034 
   2035        ++slash;  // Incrementing, so that searching for '/' won't find
   2036                  // the same slash again
   2037      }
   2038 
   2039      if (FindCharInReadable('/', slash, end)) {
   2040        rv = mRequestHead.SetHeader(nsHttp::If_Unmodified_Since,
   2041                                    Substring(++slash, end));
   2042        MOZ_ASSERT(NS_SUCCEEDED(rv));
   2043      }
   2044    }
   2045  }
   2046 
   2047  // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.
   2048  if (mLoadFlags & LOAD_ANONYMOUS) mCaps |= NS_HTTP_LOAD_ANONYMOUS;
   2049 
   2050  if (mUpgradeProtocolCallback) {
   2051    rv = mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false);
   2052    MOZ_ASSERT(NS_SUCCEEDED(rv));
   2053    rv = mRequestHead.SetHeaderOnce(nsHttp::Connection, nsHttp::Upgrade.get(),
   2054                                    false);
   2055    MOZ_ASSERT(NS_SUCCEEDED(rv));
   2056    mCaps |= NS_HTTP_STICKY_CONNECTION;
   2057    mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
   2058  }
   2059 
   2060  if (mWebTransportSessionEventListener) {
   2061    mCaps |= NS_HTTP_STICKY_CONNECTION;
   2062  }
   2063 
   2064  return NS_OK;
   2065 }
   2066 
   2067 // Updates mLNAPermission members based on existing permission and tracking
   2068 // flags in load info
   2069 LNAPermission nsHttpChannel::UpdateLocalNetworkAccessPermissions(
   2070    const nsACString& aPermissionType) {
   2071  // We should arrive at this point after LNA has been detected at the
   2072  // transaction layer and has errored
   2073 
   2074  MOZ_ASSERT(aPermissionType == LOCAL_HOST_PERMISSION_KEY ||
   2075             aPermissionType == LOCAL_NETWORK_PERMISSION_KEY);
   2076  LNAPermission userPerms = aPermissionType == LOCAL_HOST_PERMISSION_KEY
   2077                                ? mLNAPermission.mLocalHostPermission
   2078                                : mLNAPermission.mLocalNetworkPermission;
   2079 
   2080  if (NS_WARN_IF(userPerms != LNAPermission::Pending)) {
   2081    // Unexpected condition, we should not hit this case
   2082    MOZ_ASSERT(false,
   2083               "UpdateLocalNetworkAccessPermissions called with non-pending "
   2084               "permission");
   2085    return userPerms;
   2086  }
   2087 
   2088  MOZ_ASSERT(mLoadInfo->TriggeringPrincipal(), "need triggering principal");
   2089 
   2090  // Skip LNA checks if the triggering principal and target are same origin
   2091  // Note: This could be a case where there is a network change or device
   2092  // migration to a private or corporate network
   2093  bool isSameOrigin = false;
   2094  nsresult rv =
   2095      mLoadInfo->TriggeringPrincipal()->IsSameOrigin(mURI, &isSameOrigin);
   2096  if (NS_SUCCEEDED(rv) && isSameOrigin) {
   2097    userPerms = LNAPermission::Granted;
   2098    return userPerms;
   2099  }
   2100 
   2101  // Skip LNA checks if captive portal is active
   2102  nsCOMPtr<nsICaptivePortalService> cps = CaptivePortalService::GetSingleton();
   2103  if (cps) {
   2104    int32_t state = cps->State();
   2105    if (state == nsICaptivePortalService::LOCKED_PORTAL &&
   2106        aPermissionType == LOCAL_NETWORK_PERMISSION_KEY) {
   2107      userPerms = LNAPermission::Granted;
   2108      return userPerms;
   2109    }
   2110  }
   2111 
   2112  // Step 1. Check for  Existing Allow or Deny permission
   2113  if (nsContentUtils::IsExactSitePermAllow(mLoadInfo->TriggeringPrincipal(),
   2114                                           aPermissionType)) {
   2115    userPerms = LNAPermission::Granted;
   2116    return userPerms;
   2117  }
   2118 
   2119  if (nsContentUtils::IsExactSitePermDeny(mLoadInfo->TriggeringPrincipal(),
   2120                                          aPermissionType)) {
   2121    userPerms = LNAPermission::Denied;
   2122    return userPerms;
   2123  }
   2124 
   2125  // Step 2.If this is from third Party Tracker, just block
   2126  uint32_t flags = 0;
   2127  using CF = nsIClassifiedChannel::ClassificationFlags;
   2128  if (StaticPrefs::network_lna_block_trackers() &&
   2129      NS_SUCCEEDED(
   2130          mLoadInfo->GetTriggeringThirdPartyClassificationFlags(&flags)) &&
   2131      (flags & (CF::CLASSIFIED_ANY_BASIC_TRACKING |
   2132                CF::CLASSIFIED_ANY_SOCIAL_TRACKING)) != 0) {
   2133    userPerms = LNAPermission::Denied;
   2134    return userPerms;
   2135  }
   2136 
   2137  // Step 3
   2138  // could not determine the permission, lets prompt user
   2139  // for permission if lna blocking is enabled
   2140  if (StaticPrefs::network_lna_blocking()) {
   2141    return userPerms;
   2142  }
   2143 
   2144  // we dont have reasons to block the request so we allow this LNA request
   2145  // Ideally we should not hit this case once the feature is fully shipped
   2146  userPerms = LNAPermission::Granted;
   2147  return userPerms;
   2148 }
   2149 
   2150 nsresult nsHttpChannel::InitTransaction() {
   2151  nsresult rv;
   2152  // create wrapper for this channel's notification callbacks
   2153  nsCOMPtr<nsIInterfaceRequestor> callbacks;
   2154  NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
   2155                                         getter_AddRefs(callbacks));
   2156 
   2157  // create the transaction object
   2158  if (nsIOService::UseSocketProcess()) {
   2159    if (NS_WARN_IF(!gIOService->SocketProcessReady())) {
   2160      return NS_ERROR_NOT_AVAILABLE;
   2161    }
   2162    RefPtr<SocketProcessParent> socketProcess =
   2163        SocketProcessParent::GetSingleton();
   2164    if (!socketProcess->CanSend()) {
   2165      return NS_ERROR_NOT_AVAILABLE;
   2166    }
   2167 
   2168    nsCOMPtr<nsIParentChannel> parentChannel;
   2169    NS_QueryNotificationCallbacks(this, parentChannel);
   2170    RefPtr<DocumentLoadListener> documentChannelParent =
   2171        do_QueryObject(parentChannel);
   2172    // See HttpTransactionChild::CanSendODAToContentProcessDirectly() and
   2173    // nsHttpChannel::CallOnStartRequest() for the reason why we need to know if
   2174    // this is a document load. We only send ODA directly to child process for
   2175    // non document loads.
   2176    RefPtr<HttpTransactionParent> transParent =
   2177        new HttpTransactionParent(!!documentChannelParent);
   2178    LOG1(("nsHttpChannel %p created HttpTransactionParent %p\n", this,
   2179          transParent.get()));
   2180 
   2181    // Since OnStopRequest could be sent to child process from socket process
   2182    // directly, we need to store these two values in HttpTransactionChild and
   2183    // forward to child process until HttpTransactionChild::OnStopRequest is
   2184    // called.
   2185    transParent->SetRedirectTimestamp(mRedirectStartTimeStamp,
   2186                                      mRedirectEndTimeStamp);
   2187 
   2188    if (socketProcess) {
   2189      MOZ_ALWAYS_TRUE(
   2190          socketProcess->SendPHttpTransactionConstructor(transParent));
   2191    }
   2192    mTransaction = transParent;
   2193  } else {
   2194    mTransaction = new nsHttpTransaction();
   2195    LOG1(("nsHttpChannel %p created nsHttpTransaction %p\n", this,
   2196          mTransaction.get()));
   2197  }
   2198 
   2199  // Save the mapping of channel id and the channel. We need this mapping for
   2200  // nsIHttpActivityObserver.
   2201  gHttpHandler->AddHttpChannel(mChannelId, ToSupports(this));
   2202 
   2203  EnsureBrowserId();
   2204  EnsureRequestContext();
   2205 
   2206  HttpTrafficCategory category = CreateTrafficCategory();
   2207  std::function<void(TransactionObserverResult&&)> observer;
   2208  if (mTransactionObserver) {
   2209    observer = [transactionObserver{std::move(mTransactionObserver)}](
   2210                   TransactionObserverResult&& aResult) {
   2211      transactionObserver->Complete(aResult.versionOk(), aResult.authOk(),
   2212                                    aResult.closeReason());
   2213    };
   2214  }
   2215  mTransaction->SetIsForWebTransport(!!mWebTransportSessionEventListener);
   2216 
   2217  RefPtr<mozilla::dom::BrowsingContext> bc;
   2218  mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
   2219 
   2220  nsILoadInfo::IPAddressSpace parentAddressSpace =
   2221      nsILoadInfo::IPAddressSpace::Unknown;
   2222  if (!bc) {
   2223    parentAddressSpace = mLoadInfo->GetParentIpAddressSpace();
   2224  } else {
   2225    parentAddressSpace = bc->GetCurrentIPAddressSpace();
   2226  }
   2227 
   2228  // Check if this is a top-level navigation load and grant LNA permissions
   2229  // to skip local network access verification for navigational loads
   2230  if (mLoadInfo && StaticPrefs::network_lna_allow_top_level_navigation()) {
   2231    ExtContentPolicyType contentPolicyType =
   2232        mLoadInfo->GetExternalContentPolicyType();
   2233    if (contentPolicyType == ExtContentPolicy::TYPE_DOCUMENT) {
   2234      // Grant permissions for top-level navigation loads
   2235      mLNAPermission.mLocalHostPermission = LNAPermission::Granted;
   2236      mLNAPermission.mLocalNetworkPermission = LNAPermission::Granted;
   2237    }
   2238  }
   2239 
   2240  // Grant LNA permissions for captive portal tabs to allow them to access
   2241  // local network resources without prompting the user
   2242  if (bc && bc->GetIsCaptivePortalTab()) {
   2243    mLNAPermission.mLocalNetworkPermission = LNAPermission::Granted;
   2244  }
   2245 
   2246  rv = mTransaction->Init(
   2247      mCaps, mConnectionInfo, &mRequestHead, mUploadStream, mReqContentLength,
   2248      LoadUploadStreamHasHeaders(), GetCurrentSerialEventTarget(), callbacks,
   2249      this, mBrowserId, category, mRequestContext, mClassOfService,
   2250      mInitialRwin, LoadResponseTimeoutEnabled(), mChannelId,
   2251      std::move(observer), parentAddressSpace, mLNAPermission);
   2252  if (NS_FAILED(rv)) {
   2253    mTransaction = nullptr;
   2254    return rv;
   2255  }
   2256 
   2257  return rv;
   2258 }
   2259 
   2260 HttpTrafficCategory nsHttpChannel::CreateTrafficCategory() {
   2261  MOZ_ASSERT(!mFirstPartyClassificationFlags ||
   2262             !mThirdPartyClassificationFlags);
   2263 
   2264  if (!StaticPrefs::network_traffic_analyzer_enabled()) {
   2265    return HttpTrafficCategory::eInvalid;
   2266  }
   2267 
   2268  HttpTrafficAnalyzer::ClassOfService cos;
   2269  {
   2270    if ((mClassOfService.Flags() & nsIClassOfService::Leader) &&
   2271        mLoadInfo->GetExternalContentPolicyType() ==
   2272            ExtContentPolicy::TYPE_SCRIPT) {
   2273      cos = HttpTrafficAnalyzer::ClassOfService::eLeader;
   2274    } else if (mLoadFlags & nsIRequest::LOAD_BACKGROUND) {
   2275      cos = HttpTrafficAnalyzer::ClassOfService::eBackground;
   2276    } else {
   2277      cos = HttpTrafficAnalyzer::ClassOfService::eOther;
   2278    }
   2279  }
   2280 
   2281  bool isThirdParty = AntiTrackingUtils::IsThirdPartyChannel(this);
   2282 
   2283  HttpTrafficAnalyzer::TrackingClassification tc;
   2284  {
   2285    uint32_t flags = isThirdParty ? mThirdPartyClassificationFlags
   2286                                  : mFirstPartyClassificationFlags;
   2287 
   2288    using CF = nsIClassifiedChannel::ClassificationFlags;
   2289    using TC = HttpTrafficAnalyzer::TrackingClassification;
   2290 
   2291    if (flags & CF::CLASSIFIED_TRACKING_CONTENT) {
   2292      tc = TC::eContent;
   2293    } else if (flags & CF::CLASSIFIED_FINGERPRINTING_CONTENT) {
   2294      tc = TC::eFingerprinting;
   2295    } else if (flags & CF::CLASSIFIED_ANY_BASIC_TRACKING) {
   2296      tc = TC::eBasic;
   2297    } else {
   2298      tc = TC::eNone;
   2299    }
   2300  }
   2301 
   2302  bool isSystemPrincipal =
   2303      mLoadInfo->GetLoadingPrincipal() &&
   2304      mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal();
   2305  return HttpTrafficAnalyzer::CreateTrafficCategory(
   2306      NS_UsePrivateBrowsing(this), isSystemPrincipal, isThirdParty, cos, tc);
   2307 }
   2308 
   2309 void nsHttpChannel::SetCachedContentType() {
   2310  if (!mResponseHead) {
   2311    return;
   2312  }
   2313 
   2314  nsAutoCString contentTypeStr;
   2315  mResponseHead->ContentType(contentTypeStr);
   2316 
   2317  uint8_t contentType = nsICacheEntry::CONTENT_TYPE_OTHER;
   2318  if (nsContentUtils::IsJavascriptMIMEType(
   2319          NS_ConvertUTF8toUTF16(contentTypeStr))) {
   2320    contentType = nsICacheEntry::CONTENT_TYPE_JAVASCRIPT;
   2321  } else if (StringBeginsWith(contentTypeStr, "text/css"_ns) ||
   2322             (mLoadInfo->GetExternalContentPolicyType() ==
   2323              ExtContentPolicy::TYPE_STYLESHEET)) {
   2324    contentType = nsICacheEntry::CONTENT_TYPE_STYLESHEET;
   2325  } else if (StringBeginsWith(contentTypeStr, "application/wasm"_ns)) {
   2326    contentType = nsICacheEntry::CONTENT_TYPE_WASM;
   2327  } else if (StringBeginsWith(contentTypeStr, "image/"_ns)) {
   2328    contentType = nsICacheEntry::CONTENT_TYPE_IMAGE;
   2329  } else if (StringBeginsWith(contentTypeStr, "video/"_ns)) {
   2330    contentType = nsICacheEntry::CONTENT_TYPE_MEDIA;
   2331  } else if (StringBeginsWith(contentTypeStr, "audio/"_ns)) {
   2332    contentType = nsICacheEntry::CONTENT_TYPE_MEDIA;
   2333  }
   2334 
   2335  mCacheEntry->SetContentType(contentType);
   2336 }
   2337 
   2338 nsresult nsHttpChannel::CallOnStartRequest() {
   2339  LOG(("nsHttpChannel::CallOnStartRequest [this=%p]", this));
   2340 
   2341  MOZ_RELEASE_ASSERT(!LoadRequireCORSPreflight() || LoadIsCorsPreflightDone(),
   2342                     "CORS preflight must have been finished by the time we "
   2343                     "call OnStartRequest");
   2344 
   2345  MOZ_RELEASE_ASSERT(mCanceled || LoadProcessCrossOriginSecurityHeadersCalled(),
   2346                     "Security headers need to have been processed before "
   2347                     "calling CallOnStartRequest");
   2348 
   2349  mEarlyHintObserver = nullptr;
   2350 
   2351  if (StaticPrefs::network_http_network_error_logging_enabled() &&
   2352      mResponseHead && mResponseHead->HasHeader(nsHttp::NEL) &&
   2353      LoadUsedNetwork()) {
   2354    // If the policy gets cleared, but this response is still in the cache,
   2355    // we probably don't want to reinstall the policy when the entry is
   2356    // reloaded. That means it's important to only call RegisterPolicy when
   2357    // LoadUsedNetwork() is true.
   2358    if (nsCOMPtr<nsINetworkErrorLogging> nel =
   2359            components::NetworkErrorLogging::Service()) {
   2360      nel->RegisterPolicy(this);
   2361    }
   2362  }
   2363 
   2364  if (LoadOnStartRequestCalled()) {
   2365    // This can only happen when a range request loading rest of the data
   2366    // after interrupted concurrent cache read asynchronously failed, e.g.
   2367    // the response range bytes are not as expected or this channel has
   2368    // been externally canceled.
   2369    //
   2370    // It's legal to bypass CallOnStartRequest for that case since we've
   2371    // already called OnStartRequest on our listener and also added all
   2372    // content converters before.
   2373    MOZ_ASSERT(LoadConcurrentCacheAccess());
   2374    LOG(("CallOnStartRequest already invoked before"));
   2375    return mStatus;
   2376  }
   2377 
   2378  // Ensure mListener->OnStartRequest will be invoked before exiting
   2379  // this function.
   2380  auto onStartGuard = MakeScopeExit([&] {
   2381    LOG(
   2382        ("  calling mListener->OnStartRequest by ScopeExit [this=%p, "
   2383         "listener=%p]\n",
   2384         this, mListener.get()));
   2385    MOZ_ASSERT(!LoadOnStartRequestCalled());
   2386 
   2387    if (mListener) {
   2388      nsCOMPtr<nsIStreamListener> deleteProtector(mListener);
   2389      StoreOnStartRequestCalled(true);
   2390      deleteProtector->OnStartRequest(this);
   2391    }
   2392    StoreOnStartRequestCalled(true);
   2393  });
   2394 
   2395  nsresult rv = ValidateMIMEType();
   2396  // Since ODA and OnStopRequest could be sent from socket process directly, we
   2397  // need to update the channel status before calling mListener->OnStartRequest.
   2398  // This is the only way to let child process discard the already received ODA
   2399  // messages.
   2400  if (NS_FAILED(rv)) {
   2401    mStatus = rv;
   2402    return mStatus;
   2403  }
   2404 
   2405  // EnsureOpaqueResponseIsAllowed and EnsureOpauqeResponseIsAllowedAfterSniff
   2406  // are the checks for Opaque Response Blocking to ensure that we block as many
   2407  // cross-origin responses with CORS headers as possible that are not either
   2408  // Javascript or media to avoid leaking their contents through side channels.
   2409  OpaqueResponse opaqueResponse =
   2410      PerformOpaqueResponseSafelistCheckBeforeSniff();
   2411  if (opaqueResponse == OpaqueResponse::Block) {
   2412    SetChannelBlockedByOpaqueResponse();
   2413    CancelWithReason(NS_BINDING_ABORTED,
   2414                     "OpaqueResponseBlocker::BlockResponse"_ns);
   2415    return NS_BINDING_ABORTED;
   2416  }
   2417 
   2418  // We must ensure that any dcb/dcz content is decompressed in the parent
   2419  // process, and removed from the Content-Encoding before it's sent down
   2420  // to the content process.  In most (all?) cases, this should have occurred
   2421  // before this.
   2422  if (mResponseHead && XRE_IsParentProcess() && !LoadHasAppliedConversion()) {
   2423    nsAutoCString contentEncoding;
   2424    (void)mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
   2425    // Note: doesn't handle multiple compressors: "dcb, gzip" or
   2426    // "gzip, dcb" (etc)
   2427    if (contentEncoding.Equals("dcb") || contentEncoding.Equals("dcz")) {
   2428      LOG_DICTIONARIES(
   2429          ("Still had %s encoding at CallOnStartRequest, converting",
   2430           contentEncoding.get()));
   2431      nsCOMPtr<nsIStreamListener> listener;
   2432      MOZ_DIAGNOSTIC_ASSERT(LoadHasAppliedConversion() == false);
   2433      // Insist we install a converter.  This should never be the case, but
   2434      // if somehow it is, we need a converter; content can't be allowed
   2435      // to see dcb/dcz since it can't convert them
   2436      StoreHasAppliedConversion(false);
   2437      rv = DoApplyContentConversions(mListener, getter_AddRefs(listener),
   2438                                     nullptr);
   2439      if (NS_FAILED(rv)) {
   2440        return rv;
   2441      }
   2442      if (listener) {
   2443        MOZ_ASSERT(!LoadDataSentToChildProcess(),
   2444                   "DataSentToChildProcess being true means ODAs are sent to "
   2445                   "the child process directly. We MUST NOT apply content "
   2446                   "converter in this case.");
   2447        mListener = listener;
   2448        mCompressListener = listener;
   2449 
   2450        StoreHasAppliedConversion(true);
   2451      }
   2452    }
   2453  }
   2454 
   2455  // Allow consumers to override our content type
   2456  if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
   2457    // NOTE: We can have both a txn pump and a cache pump when the cache
   2458    // content is partial. In that case, we need to read from the cache,
   2459    // because that's the one that has the initial contents. If that fails
   2460    // then give the transaction pump a shot.
   2461 
   2462    nsIChannel* thisChannel = static_cast<nsIChannel*>(this);
   2463 
   2464    bool typeSniffersCalled = false;
   2465    if (mCachePump) {
   2466      typeSniffersCalled =
   2467          NS_SUCCEEDED(mCachePump->PeekStream(CallTypeSniffers, thisChannel));
   2468    }
   2469 
   2470    if (!typeSniffersCalled && mTransactionPump) {
   2471      RefPtr<nsInputStreamPump> pump = do_QueryObject(mTransactionPump);
   2472      if (pump) {
   2473        pump->PeekStream(CallTypeSniffers, thisChannel);
   2474      } else {
   2475        MOZ_ASSERT(nsIOService::UseSocketProcess());
   2476        RefPtr<HttpTransactionParent> trans = do_QueryObject(mTransactionPump);
   2477        MOZ_ASSERT(trans);
   2478        trans->SetSniffedTypeToChannel(CallTypeSniffers, thisChannel);
   2479      }
   2480    }
   2481  }
   2482 
   2483  // Note that the code below should be synced with the code in
   2484  // HttpTransactionChild::CanSendODAToContentProcessDirectly(). We MUST make
   2485  // sure HttpTransactionChild::CanSendODAToContentProcessDirectly() returns
   2486  // false when a stream converter is applied.
   2487  bool unknownDecoderStarted = false;
   2488  if (mResponseHead && !mResponseHead->HasContentType()) {
   2489    MOZ_ASSERT(mConnectionInfo, "Should have connection info here");
   2490    if (!mContentTypeHint.IsEmpty()) {
   2491      mResponseHead->SetContentType(mContentTypeHint);
   2492    } else if (mResponseHead->Version() == HttpVersion::v0_9 &&
   2493               mConnectionInfo->OriginPort() !=
   2494                   mConnectionInfo->DefaultPort()) {
   2495      mResponseHead->SetContentType(nsLiteralCString(TEXT_PLAIN));
   2496    } else {
   2497      // Uh-oh.  We had better find out what type we are!
   2498      mListener = new nsUnknownDecoder(mListener);
   2499      unknownDecoderStarted = true;
   2500    }
   2501  }
   2502 
   2503  // If unknownDecoder is not going to be launched, call
   2504  // EnsureOpaqueResponseIsAllowedAfterSniff immediately.
   2505  if (!unknownDecoderStarted) {
   2506    if (opaqueResponse == OpaqueResponse::SniffCompressed) {
   2507      mListener = new nsCompressedAudioVideoImageDetector(
   2508          mListener, &HttpBaseChannel::CallTypeSniffers);
   2509    } else if (opaqueResponse == OpaqueResponse::Sniff) {
   2510      MOZ_DIAGNOSTIC_ASSERT(mORB);
   2511      nsresult rv = mORB->EnsureOpaqueResponseIsAllowedAfterSniff(this);
   2512 
   2513      if (NS_FAILED(rv)) {
   2514        return rv;
   2515      }
   2516    }
   2517  }
   2518 
   2519  // If the content is multipart/x-mixed-replace, we'll insert a MIME decoder
   2520  // in the pipeline to handle the content and pass it along to our
   2521  // original listener. nsUnknownDecoder doesn't support detecting this type,
   2522  // so we only need to insert this using the response header's mime type.
   2523  //
   2524  // We only do this for unwrapped document loads, since we might want to send
   2525  // parts to the external protocol handler without leaving the parent process.
   2526  bool mustRunStreamFilterInParent = false;
   2527  nsCOMPtr<nsIParentChannel> parentChannel;
   2528  NS_QueryNotificationCallbacks(this, parentChannel);
   2529  RefPtr<DocumentLoadListener> docListener = do_QueryObject(parentChannel);
   2530  if (mResponseHead && docListener && docListener->GetChannel() == this) {
   2531    nsAutoCString contentType;
   2532    mResponseHead->ContentType(contentType);
   2533 
   2534    if (contentType.Equals("multipart/x-mixed-replace"_ns)) {
   2535      nsCOMPtr<nsIStreamConverterService> convServ(
   2536          mozilla::components::StreamConverter::Service(&rv));
   2537      if (NS_SUCCEEDED(rv)) {
   2538        nsCOMPtr<nsIStreamListener> toListener(mListener);
   2539        nsCOMPtr<nsIStreamListener> fromListener;
   2540 
   2541        rv = convServ->AsyncConvertData("multipart/x-mixed-replace", "*/*",
   2542                                        toListener, nullptr,
   2543                                        getter_AddRefs(fromListener));
   2544        if (NS_SUCCEEDED(rv)) {
   2545          mListener = fromListener;
   2546          mustRunStreamFilterInParent = true;
   2547        }
   2548      }
   2549    }
   2550  }
   2551 
   2552  // If we installed a multipart converter, then we need to add StreamFilter
   2553  // object before it, so that extensions see the un-parsed original stream.
   2554  // We may want to add an option for extensions to opt-in to proper multipart
   2555  // handling.
   2556  // If not, then pass the StreamFilter promise on to DocumentLoadListener,
   2557  // where it'll be added in the content process.
   2558  for (StreamFilterRequest& request : mStreamFilterRequests) {
   2559    if (mustRunStreamFilterInParent) {
   2560      mozilla::ipc::Endpoint<extensions::PStreamFilterParent> parent;
   2561      mozilla::ipc::Endpoint<extensions::PStreamFilterChild> child;
   2562      nsresult rv = extensions::PStreamFilter::CreateEndpoints(&parent, &child);
   2563      if (NS_FAILED(rv)) {
   2564        request.mPromise->Reject(false, __func__);
   2565      } else {
   2566        extensions::StreamFilterParent::Attach(this, std::move(parent));
   2567        request.mPromise->Resolve(std::move(child), __func__);
   2568      }
   2569    } else {
   2570      if (docListener) {
   2571        docListener->AttachStreamFilter()->ChainTo(request.mPromise.forget(),
   2572                                                   __func__);
   2573      } else {
   2574        request.mPromise->Reject(false, __func__);
   2575      }
   2576    }
   2577    request.mPromise = nullptr;
   2578  }
   2579  mStreamFilterRequests.Clear();
   2580  StoreTracingEnabled(false);
   2581 
   2582  if (mResponseHead && !mResponseHead->HasContentCharset()) {
   2583    mResponseHead->SetContentCharset(mContentCharsetHint);
   2584  }
   2585 
   2586  if (mCacheEntry && LoadCacheEntryIsWriteOnly()) {
   2587    SetCachedContentType();
   2588  }
   2589 
   2590  LOG(("  calling mListener->OnStartRequest [this=%p, listener=%p]\n", this,
   2591       mListener.get()));
   2592 
   2593  // About to call OnStartRequest, dismiss the guard object.
   2594  onStartGuard.release();
   2595 
   2596  if (mListener) {
   2597    MOZ_ASSERT(!LoadOnStartRequestCalled(),
   2598               "We should not call OsStartRequest twice");
   2599    nsCOMPtr<nsIStreamListener> deleteProtector(mListener);
   2600    StoreOnStartRequestCalled(true);
   2601    rv = deleteProtector->OnStartRequest(this);
   2602    if (NS_FAILED(rv)) return rv;
   2603  } else {
   2604    NS_WARNING("OnStartRequest skipped because of null listener");
   2605    StoreOnStartRequestCalled(true);
   2606  }
   2607 
   2608  // Install stream converter if required.
   2609  // Normally, we expect the listener to disable content conversion during
   2610  // OnStartRequest if it wants to handle it itself (which is common case with
   2611  // HttpChannelParent, disabling so that it can be done in the content
   2612  // process). If we've installed an nsUnknownDecoder, then we won't yet have
   2613  // called OnStartRequest on the final listener (that happens after we send
   2614  // OnDataAvailable to the nsUnknownDecoder), so it can't yet have disabled
   2615  // content conversion.
   2616  // In that case, assume that the listener will disable content conversion,
   2617  // unless it's specifically told us that it won't.
   2618  if (!unknownDecoderStarted || LoadListenerRequiresContentConversion()) {
   2619    nsCOMPtr<nsIStreamListener> listener;
   2620    rv =
   2621        DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr);
   2622    if (NS_FAILED(rv)) {
   2623      return rv;
   2624    }
   2625    if (listener) {
   2626      MOZ_ASSERT(!LoadDataSentToChildProcess(),
   2627                 "DataSentToChildProcess being true means ODAs are sent to "
   2628                 "the child process directly. We MUST NOT apply content "
   2629                 "converter in this case.");
   2630      mListener = listener;
   2631      mCompressListener = listener;
   2632 
   2633      StoreHasAppliedConversion(true);
   2634    }
   2635  }
   2636 
   2637  // if this channel is for a download, close off access to the cache.
   2638  if (mCacheEntry && LoadChannelIsForDownload()) {
   2639    mCacheEntry->AsyncDoom(nullptr);
   2640 
   2641    // We must keep the cache entry in case of partial request.
   2642    // Concurrent access is the same, we need the entry in
   2643    // OnStopRequest.
   2644    // We also need the cache entry when racing cache with network to find
   2645    // out what is the source of the data.
   2646    if (!LoadCachedContentIsPartial() && !LoadConcurrentCacheAccess() &&
   2647        !(mRaceCacheWithNetwork &&
   2648          mFirstResponseSource == RESPONSE_FROM_CACHE)) {
   2649      CloseCacheEntry(false);
   2650    }
   2651  }
   2652 
   2653  return NS_OK;
   2654 }
   2655 
   2656 NS_IMETHODIMP nsHttpChannel::GetHttpProxyConnectResponseCode(
   2657    int32_t* aResponseCode) {
   2658  NS_ENSURE_ARG_POINTER(aResponseCode);
   2659 
   2660  if (mConnectionInfo && mConnectionInfo->UsingConnect()) {
   2661    *aResponseCode = mProxyConnectResponseCode;
   2662  } else {
   2663    *aResponseCode = -1;
   2664  }
   2665  return NS_OK;
   2666 }
   2667 
   2668 nsresult nsHttpChannel::ProcessFailedProxyConnect(uint32_t httpStatus) {
   2669  // Failure to set up a proxy tunnel via CONNECT means one of the following:
   2670  // 1) Proxy wants authorization, or forbids.
   2671  // 2) DNS at proxy couldn't resolve target URL.
   2672  // 3) Proxy connection to target failed or timed out.
   2673  // 4) Eve intercepted our CONNECT, and is replying with malicious HTML.
   2674  //
   2675  // Our current architecture would parse the proxy's response content with
   2676  // the permission of the target URL.  Given #4, we must avoid rendering the
   2677  // body of the reply, and instead give the user a (hopefully helpful)
   2678  // boilerplate error page, based on just the HTTP status of the reply.
   2679 
   2680  MOZ_ASSERT(mConnectionInfo->UsingConnect(),
   2681             "proxy connect failed but not using CONNECT?");
   2682  nsresult rv = HttpProxyResponseToErrorCode(httpStatus);
   2683  LOG(("Cancelling failed proxy CONNECT [this=%p httpStatus=%u]\n", this,
   2684       httpStatus));
   2685 
   2686  // Make sure the connection is thrown away as it can be in a bad state
   2687  // and the proxy may just hang on the next request.
   2688  MOZ_ASSERT(mTransaction);
   2689  mTransaction->DontReuseConnection();
   2690 
   2691  Cancel(rv);
   2692  {
   2693    nsresult rv = CallOnStartRequest();
   2694    if (NS_FAILED(rv)) {
   2695      LOG(("CallOnStartRequest failed [this=%p httpStatus=%u rv=%08x]\n", this,
   2696           httpStatus, static_cast<uint32_t>(rv)));
   2697    }
   2698  }
   2699  return rv;
   2700 }
   2701 
   2702 static void GetSTSConsoleErrorTag(uint32_t failureResult,
   2703                                  nsAString& consoleErrorTag) {
   2704  switch (failureResult) {
   2705    case nsISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER:
   2706      consoleErrorTag = u"STSCouldNotParseHeader"_ns;
   2707      break;
   2708    case nsISiteSecurityService::ERROR_NO_MAX_AGE:
   2709      consoleErrorTag = u"STSNoMaxAge"_ns;
   2710      break;
   2711    case nsISiteSecurityService::ERROR_MULTIPLE_MAX_AGES:
   2712      consoleErrorTag = u"STSMultipleMaxAges"_ns;
   2713      break;
   2714    case nsISiteSecurityService::ERROR_INVALID_MAX_AGE:
   2715      consoleErrorTag = u"STSInvalidMaxAge"_ns;
   2716      break;
   2717    case nsISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS:
   2718      consoleErrorTag = u"STSMultipleIncludeSubdomains"_ns;
   2719      break;
   2720    case nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS:
   2721      consoleErrorTag = u"STSInvalidIncludeSubdomains"_ns;
   2722      break;
   2723    case nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE:
   2724      consoleErrorTag = u"STSCouldNotSaveState"_ns;
   2725      break;
   2726    default:
   2727      consoleErrorTag = u"STSUnknownError"_ns;
   2728      break;
   2729  }
   2730 }
   2731 
   2732 /**
   2733 * Process an HTTP Strict Transport Security (HSTS) header.
   2734 */
   2735 nsresult nsHttpChannel::ProcessHSTSHeader(nsITransportSecurityInfo* aSecInfo) {
   2736  nsHttpAtom atom(nsHttp::ResolveAtom("Strict-Transport-Security"_ns));
   2737 
   2738  nsAutoCString securityHeader;
   2739  nsresult rv = mResponseHead->GetHeader(atom, securityHeader);
   2740  if (rv == NS_ERROR_NOT_AVAILABLE) {
   2741    LOG(("nsHttpChannel: No %s header, continuing load.\n", atom.get()));
   2742    return NS_OK;
   2743  }
   2744  if (NS_WARN_IF(NS_FAILED(rv))) {
   2745    return rv;
   2746  }
   2747 
   2748  if (!aSecInfo) {
   2749    LOG(("nsHttpChannel::ProcessHSTSHeader: no securityInfo?"));
   2750    return NS_ERROR_INVALID_ARG;
   2751  }
   2752  nsITransportSecurityInfo::OverridableErrorCategory overridableErrorCategory;
   2753  rv = aSecInfo->GetOverridableErrorCategory(&overridableErrorCategory);
   2754  if (NS_WARN_IF(NS_FAILED(rv))) {
   2755    return rv;
   2756  }
   2757  if (overridableErrorCategory !=
   2758      nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET) {
   2759    LOG(
   2760        ("nsHttpChannel::ProcessHSTSHeader: untrustworthy connection - not "
   2761         "processing header"));
   2762    return NS_ERROR_FAILURE;
   2763  }
   2764 
   2765  nsISiteSecurityService* sss = gHttpHandler->GetSSService();
   2766  NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
   2767 
   2768  OriginAttributes originAttributes;
   2769  if (NS_WARN_IF(!StoragePrincipalHelper::GetOriginAttributesForHSTS(
   2770          this, originAttributes))) {
   2771    return NS_ERROR_FAILURE;
   2772  }
   2773 
   2774  uint32_t failureResult;
   2775  rv = sss->ProcessHeader(mURI, securityHeader, originAttributes, nullptr,
   2776                          nullptr, &failureResult);
   2777  if (NS_FAILED(rv)) {
   2778    nsAutoString consoleErrorCategory(u"Invalid HSTS Headers"_ns);
   2779    nsAutoString consoleErrorTag;
   2780    GetSTSConsoleErrorTag(failureResult, consoleErrorTag);
   2781    (void)AddSecurityMessage(consoleErrorTag, consoleErrorCategory);
   2782    LOG(("nsHttpChannel: Failed to parse %s header, continuing load.\n",
   2783         atom.get()));
   2784  }
   2785  return NS_OK;
   2786 }
   2787 
   2788 /**
   2789 * Decide whether or not to remember Strict-Transport-Security, and whether
   2790 * or not to enforce channel integrity.
   2791 *
   2792 * @return NS_ERROR_FAILURE if there's security information missing even though
   2793 *             it's an HTTPS connection.
   2794 */
   2795 nsresult nsHttpChannel::ProcessSecurityHeaders() {
   2796  // If this channel is not loading securely, STS or PKP doesn't do anything.
   2797  // In the case of HSTS, the upgrade to HTTPS takes place earlier in the
   2798  // channel load process.
   2799  if (!mURI->SchemeIs("https")) {
   2800    return NS_OK;
   2801  }
   2802 
   2803  if (IsBrowsingContextDiscarded()) {
   2804    return NS_OK;
   2805  }
   2806 
   2807  nsAutoCString asciiHost;
   2808  nsresult rv = mURI->GetAsciiHost(asciiHost);
   2809  NS_ENSURE_SUCCESS(rv, NS_OK);
   2810 
   2811  // If the channel is not a hostname, but rather an IP, do not process STS
   2812  // or PKP headers
   2813  if (HostIsIPLiteral(asciiHost)) {
   2814    return NS_OK;
   2815  }
   2816 
   2817  // mSecurityInfo may not always be present, and if it's not then it is okay
   2818  // to just disregard any security headers since we know nothing about the
   2819  // security of the connection.
   2820  NS_ENSURE_TRUE(mSecurityInfo, NS_OK);
   2821 
   2822  // Only process HSTS headers for first-party loads. This prevents a
   2823  // proliferation of useless HSTS state for partitioned third parties.
   2824  if (!mLoadInfo->GetIsThirdPartyContextToTopWindow()) {
   2825    rv = ProcessHSTSHeader(mSecurityInfo);
   2826    NS_ENSURE_SUCCESS(rv, rv);
   2827  }
   2828 
   2829  return NS_OK;
   2830 }
   2831 
   2832 bool nsHttpChannel::IsHTTPS() { return mURI->SchemeIs("https"); }
   2833 
   2834 void nsHttpChannel::ProcessSSLInformation() {
   2835  // If this is HTTPS, record any use of RSA so that Key Exchange Algorithm
   2836  // can be whitelisted for TLS False Start in future sessions. We could
   2837  // do the same for DH but its rarity doesn't justify the lookup.
   2838 
   2839  if (mCanceled || NS_FAILED(mStatus) || !mSecurityInfo || !IsHTTPS() ||
   2840      mPrivateBrowsing) {
   2841    return;
   2842  }
   2843 
   2844  if (!mSecurityInfo) {
   2845    return;
   2846  }
   2847 
   2848  uint32_t state;
   2849  if (NS_SUCCEEDED(mSecurityInfo->GetSecurityState(&state)) &&
   2850      (state & nsIWebProgressListener::STATE_IS_BROKEN)) {
   2851    // Send weak crypto warnings to the web console
   2852    if (state & nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) {
   2853      nsString consoleErrorTag = u"WeakCipherSuiteWarning"_ns;
   2854      nsString consoleErrorCategory = u"SSL"_ns;
   2855      (void)AddSecurityMessage(consoleErrorTag, consoleErrorCategory);
   2856    }
   2857  }
   2858 
   2859  uint16_t tlsVersion;
   2860  nsresult rv = mSecurityInfo->GetProtocolVersion(&tlsVersion);
   2861  if (NS_SUCCEEDED(rv) &&
   2862      tlsVersion != nsITransportSecurityInfo::TLS_VERSION_1_2 &&
   2863      tlsVersion != nsITransportSecurityInfo::TLS_VERSION_1_3) {
   2864    nsString consoleErrorTag = u"DeprecatedTLSVersion2"_ns;
   2865    nsString consoleErrorCategory = u"TLS"_ns;
   2866    (void)AddSecurityMessage(consoleErrorTag, consoleErrorCategory);
   2867  }
   2868 }
   2869 
   2870 void nsHttpChannel::ProcessAltService(nsHttpConnectionInfo* aTransConnInfo) {
   2871  // e.g. Alt-Svc: h2=":443"; ma=60
   2872  // e.g. Alt-Svc: h2="otherhost:443"
   2873  // Alt-Svc       = 1#( alternative *( OWS ";" OWS parameter ) )
   2874  // alternative   = protocol-id "=" alt-authority
   2875  // protocol-id   = token ; percent-encoded ALPN protocol identifier
   2876  // alt-authority = quoted-string ;  containing [ uri-host ] ":" port
   2877 
   2878  if (!LoadAllowAltSvc()) {  // per channel opt out
   2879    return;
   2880  }
   2881 
   2882  if (mWebTransportSessionEventListener) {
   2883    return;
   2884  }
   2885 
   2886  if (!gHttpHandler->AllowAltSvc() || (mCaps & NS_HTTP_DISALLOW_SPDY)) {
   2887    return;
   2888  }
   2889 
   2890  if (IsBrowsingContextDiscarded()) {
   2891    return;
   2892  }
   2893 
   2894  nsAutoCString scheme;
   2895  mURI->GetScheme(scheme);
   2896  bool isHttp = scheme.EqualsLiteral("http");
   2897  if (!isHttp && !scheme.EqualsLiteral("https")) {
   2898    return;
   2899  }
   2900 
   2901  nsAutoCString altSvc;
   2902  (void)mResponseHead->GetHeader(nsHttp::Alternate_Service, altSvc);
   2903  if (altSvc.IsEmpty()) {
   2904    return;
   2905  }
   2906 
   2907  if (!nsHttp::IsReasonableHeaderValue(altSvc)) {
   2908    LOG(("Alt-Svc Response Header seems unreasonable - skipping\n"));
   2909    return;
   2910  }
   2911 
   2912  nsAutoCString originHost;
   2913  int32_t originPort = 80;
   2914  mURI->GetPort(&originPort);
   2915  if (NS_FAILED(mURI->GetAsciiHost(originHost))) {
   2916    return;
   2917  }
   2918 
   2919  nsCOMPtr<nsIInterfaceRequestor> callbacks;
   2920  nsCOMPtr<nsProxyInfo> proxyInfo;
   2921  NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
   2922                                         getter_AddRefs(callbacks));
   2923 
   2924  if (mProxyInfo) {
   2925    proxyInfo = do_QueryInterface(mProxyInfo);
   2926  }
   2927 
   2928  OriginAttributes originAttributes;
   2929  // Regular principal in case we have a proxy.
   2930  if (proxyInfo &&
   2931      !StaticPrefs::privacy_partition_network_state_connection_with_proxy()) {
   2932    StoragePrincipalHelper::GetOriginAttributes(
   2933        this, originAttributes, StoragePrincipalHelper::eRegularPrincipal);
   2934  } else {
   2935    StoragePrincipalHelper::GetOriginAttributesForNetworkState(
   2936        this, originAttributes);
   2937  }
   2938 
   2939  AltSvcMapping::ProcessHeader(altSvc, scheme, originHost, originPort,
   2940                               mUsername, mPrivateBrowsing, callbacks,
   2941                               proxyInfo, mCaps & NS_HTTP_DISALLOW_SPDY,
   2942                               originAttributes, aTransConnInfo);
   2943 }
   2944 
   2945 nsresult nsHttpChannel::ProcessResponse(nsHttpConnectionInfo* aConnInfo) {
   2946  uint32_t httpStatus = mResponseHead->Status();
   2947 
   2948  LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n", this,
   2949       httpStatus));
   2950 
   2951  // Gather data on whether the transaction and page (if this is
   2952  // the initial page load) is being loaded with SSL.
   2953  glean::http::transaction_is_ssl
   2954      .EnumGet(static_cast<glean::http::TransactionIsSslLabel>(
   2955          mConnectionInfo->EndToEndSSL()))
   2956      .Add();
   2957  if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
   2958    glean::http::pageload_is_ssl
   2959        .EnumGet(static_cast<glean::http::PageloadIsSslLabel>(
   2960            mConnectionInfo->EndToEndSSL()))
   2961        .Add();
   2962  }
   2963 
   2964  if (Telemetry::CanRecordPrereleaseData()) {
   2965    // Gather data on various response status to monitor any increased frequency
   2966    // of auth failures due to Bug 1896350
   2967    switch (httpStatus) {
   2968      case 200:
   2969        mozilla::glean::networking::http_response_status_code.Get("200_ok"_ns)
   2970            .Add(1);
   2971        break;
   2972      case 301:
   2973        mozilla::glean::networking::http_response_status_code
   2974            .Get("301_moved_permanently"_ns)
   2975            .Add(1);
   2976        break;
   2977      case 302:
   2978        mozilla::glean::networking::http_response_status_code
   2979            .Get("302_found"_ns)
   2980            .Add(1);
   2981        break;
   2982      case 304:
   2983        mozilla::glean::networking::http_response_status_code
   2984            .Get("304_not_modified"_ns)
   2985            .Add(1);
   2986        break;
   2987      case 307:
   2988        mozilla::glean::networking::http_response_status_code
   2989            .Get("307_temporary_redirect"_ns)
   2990            .Add(1);
   2991        break;
   2992      case 308:
   2993        mozilla::glean::networking::http_response_status_code
   2994            .Get("308_permanent_redirect"_ns)
   2995            .Add(1);
   2996        break;
   2997      case 400:
   2998        mozilla::glean::networking::http_response_status_code
   2999            .Get("400_bad_request"_ns)
   3000            .Add(1);
   3001        break;
   3002      case 401:
   3003        mozilla::glean::networking::http_response_status_code
   3004            .Get("401_unauthorized"_ns)
   3005            .Add(1);
   3006        break;
   3007      case 403:
   3008        mozilla::glean::networking::http_response_status_code
   3009            .Get("403_forbidden"_ns)
   3010            .Add(1);
   3011        break;
   3012      case 404:
   3013        mozilla::glean::networking::http_response_status_code
   3014            .Get("404_not_found"_ns)
   3015            .Add(1);
   3016        break;
   3017      case 421:
   3018        mozilla::glean::networking::http_response_status_code
   3019            .Get("421_misdirected_request"_ns)
   3020            .Add(1);
   3021        break;
   3022      case 425:
   3023        mozilla::glean::networking::http_response_status_code
   3024            .Get("425_too_early"_ns)
   3025            .Add(1);
   3026        break;
   3027      case 429:
   3028        mozilla::glean::networking::http_response_status_code
   3029            .Get("429_too_many_requests"_ns)
   3030            .Add(1);
   3031        break;
   3032      case 500:
   3033        mozilla::glean::networking::http_response_status_code
   3034            .Get("other_5xx"_ns)
   3035            .Add(1);
   3036        break;
   3037      default:
   3038        if (httpStatus >= 400 && httpStatus < 500) {
   3039          mozilla::glean::networking::http_response_status_code
   3040              .Get("other_4xx"_ns)
   3041              .Add(1);
   3042        } else if (httpStatus > 500) {
   3043          mozilla::glean::networking::http_response_status_code
   3044              .Get("other_5xx"_ns)
   3045              .Add(1);
   3046        } else {
   3047          mozilla::glean::networking::http_response_status_code.Get("other"_ns)
   3048              .Add(1);
   3049        }
   3050        break;
   3051    }
   3052  }
   3053 
   3054  // We use GetReferringPage because mReferrerInfo may not be set at all(this is
   3055  // especially useful in xpcshell tests, where we don't have an actual pageload
   3056  // to get a referrer from).
   3057  // Only allow 407 (authentication required) to continue
   3058  if (mTransaction && mTransaction->ProxyConnectFailed() && httpStatus != 407) {
   3059    return ProcessFailedProxyConnect(httpStatus);
   3060  }
   3061 
   3062  MOZ_ASSERT(!CachedContentIsValid() || mRaceCacheWithNetwork,
   3063             "We should not be hitting the network if we have valid cached "
   3064             "content unless we are racing the network and cache");
   3065 
   3066  ProcessSSLInformation();
   3067 
   3068  // notify "http-on-examine-response" observers
   3069  gHttpHandler->OnExamineResponse(this);
   3070 
   3071  return ContinueProcessResponse1(aConnInfo);
   3072 }
   3073 
   3074 void nsHttpChannel::AsyncContinueProcessResponse(
   3075    nsHttpConnectionInfo* aConnInfo) {
   3076  nsresult rv;
   3077  rv = ContinueProcessResponse1(aConnInfo);
   3078  if (NS_FAILED(rv)) {
   3079    // A synchronous failure here would normally be passed as the return
   3080    // value from OnStartRequest, which would in turn cancel the request.
   3081    // If we're continuing asynchronously, we need to cancel the request
   3082    // ourselves.
   3083    (void)Cancel(rv);
   3084  }
   3085 }
   3086 
   3087 nsresult nsHttpChannel::ContinueProcessResponse1(
   3088    nsHttpConnectionInfo* aConnInfo) {
   3089  MOZ_ASSERT(!mCallOnResume, "How did that happen?");
   3090  nsresult rv = NS_OK;
   3091 
   3092  if (mSuspendCount) {
   3093    LOG(("Waiting until resume to finish processing response [this=%p]\n",
   3094         this));
   3095    mCallOnResume = [connInfo = RefPtr{aConnInfo}](nsHttpChannel* self) {
   3096      self->AsyncContinueProcessResponse(connInfo);
   3097      return NS_OK;
   3098    };
   3099    return NS_OK;
   3100  }
   3101 
   3102  // Check if request was cancelled during http-on-examine-response.
   3103  if (mCanceled) {
   3104    return CallOnStartRequest();
   3105  }
   3106 
   3107  uint32_t httpStatus = mResponseHead->Status();
   3108 
   3109  // STS, Cookies and Alt-Service should not be handled on proxy failure.
   3110  // If proxy CONNECT response needs to complete, wait to process connection
   3111  // for Strict-Transport-Security.
   3112  if (!(mTransaction && mTransaction->ProxyConnectFailed()) &&
   3113      (httpStatus != 407)) {
   3114    // This block parses the cookie header, collects any cookie changes,
   3115    // and sends them to the parent actor.
   3116    {
   3117      RefPtr<CookieObserver> cookieObserver;
   3118      RefPtr<HttpChannelParent> httpParent;
   3119      CookieServiceParent::CookieProcessingGuard cookieProcessingGuard;
   3120 
   3121      if (!LoadOnStartRequestCalled()) {
   3122        // This can only happen when a range request is created again in
   3123        // nsHttpChannel::ContinueOnStopRequest. If OnStartRequest is already
   3124        // called, we shouldn't call SetCookieHeaders.
   3125 
   3126        MaybeInitializeCookieProcessingGuard(this, cookieProcessingGuard,
   3127                                             cookieObserver, httpParent,
   3128                                             httpStatus);
   3129      }
   3130 
   3131      CookieVisitor cookieVisitor(mResponseHead.get());
   3132      SetCookieHeaders(cookieVisitor.CookieHeaders());
   3133 
   3134      if (cookieObserver) {
   3135        nsTArray<CookieChange> cookieChanges;
   3136        cookieObserver->StealChanges(cookieChanges);
   3137 
   3138        if (!cookieChanges.IsEmpty()) {
   3139          MOZ_ASSERT(httpParent);
   3140          httpParent->SetCookieChanges(std::move(cookieChanges));
   3141        }
   3142      }
   3143    }
   3144 
   3145    // Given a successful connection, process any STS or PKP data that's
   3146    // relevant.
   3147    nsresult rv = ProcessSecurityHeaders();
   3148    if (NS_FAILED(rv)) {
   3149      NS_WARNING("ProcessSTSHeader failed, continuing load.");
   3150    }
   3151 
   3152    if ((httpStatus < 500) && (httpStatus != 421)) {
   3153      ProcessAltService(aConnInfo);
   3154    }
   3155  }
   3156 
   3157  if (LoadConcurrentCacheAccess() && LoadCachedContentIsPartial() &&
   3158      httpStatus != 206) {
   3159    LOG(
   3160        ("  only expecting 206 when doing partial request during "
   3161         "interrupted cache concurrent read"));
   3162    return NS_ERROR_CORRUPTED_CONTENT;
   3163  }
   3164 
   3165  if (httpStatus != 401 && httpStatus != 407) {
   3166    // reset the authentication's current continuation state because ourvr
   3167    // last authentication attempt has been completed successfully
   3168    MOZ_DIAGNOSTIC_ASSERT(mAuthProvider);
   3169    rv = mAuthProvider ? mAuthProvider->Disconnect(NS_ERROR_ABORT)
   3170                       : NS_ERROR_UNEXPECTED;
   3171    if (NS_FAILED(rv)) {
   3172      LOG(("  Disconnect failed (%08x)", static_cast<uint32_t>(rv)));
   3173    }
   3174    mAuthProvider = nullptr;
   3175    LOG(("  continuation state has been reset"));
   3176  }
   3177 
   3178  gHttpHandler->OnAfterExamineResponse(this);
   3179 
   3180  // No process switch needed, continue as normal.
   3181  return ContinueProcessResponse2(rv);
   3182 }
   3183 
   3184 nsresult nsHttpChannel::ContinueProcessResponse2(nsresult rv) {
   3185  if (mSuspendCount) {
   3186    LOG(("Waiting until resume to finish processing response [this=%p]\n",
   3187         this));
   3188    mCallOnResume = [rv](nsHttpChannel* self) {
   3189      (void)self->ContinueProcessResponse2(rv);
   3190      return NS_OK;
   3191    };
   3192    return NS_OK;
   3193  }
   3194 
   3195  if (NS_FAILED(rv) && !mCanceled) {
   3196    // The process switch failed, cancel this channel.
   3197    Cancel(rv);
   3198    return CallOnStartRequest();
   3199  }
   3200 
   3201  if (mAPIRedirectTo && !mCanceled) {
   3202    MOZ_ASSERT(!LoadOnStartRequestCalled());
   3203 
   3204    PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);
   3205    rv = StartRedirectChannelToURI(
   3206        mAPIRedirectTo->first(),
   3207        mAPIRedirectTo->second() ? nsIChannelEventSink::REDIRECT_TEMPORARY |
   3208                                       nsIChannelEventSink::REDIRECT_TRANSPARENT
   3209                                 : nsIChannelEventSink::REDIRECT_TEMPORARY);
   3210    mAPIRedirectTo = Nothing();
   3211    if (NS_SUCCEEDED(rv)) {
   3212      return NS_OK;
   3213    }
   3214    PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3);
   3215  }
   3216 
   3217  // Hack: ContinueProcessResponse3 uses NS_OK to detect successful
   3218  // redirects, so we distinguish this codepath (a non-redirect that's
   3219  // processing normally) by passing in a bogus error code.
   3220  return ContinueProcessResponse3(NS_BINDING_FAILED);
   3221 }
   3222 
   3223 nsresult nsHttpChannel::ContinueProcessResponse3(nsresult rv) {
   3224  LOG(("nsHttpChannel::ContinueProcessResponse3 [this=%p, rv=%" PRIx32 "]",
   3225       this, static_cast<uint32_t>(rv)));
   3226 
   3227  if (NS_SUCCEEDED(rv)) {
   3228    // redirectTo() has passed through, we don't want to go on with
   3229    // this channel.  It will now be canceled by the redirect handling
   3230    // code that called this function.
   3231    return NS_OK;
   3232  }
   3233 
   3234  rv = NS_OK;
   3235 
   3236  uint32_t httpStatus = mResponseHead->Status();
   3237  bool transactionRestarted = mTransaction->TakeRestartedState();
   3238 
   3239  // handle different server response categories.  Note that we handle
   3240  // caching or not caching of error pages in
   3241  // nsHttpResponseHead::MustValidate; if you change this switch, update that
   3242  // one
   3243  switch (httpStatus) {
   3244    case 200:
   3245    case 203:
   3246      // Per RFC 2616, 14.35.2, "A server MAY ignore the Range header".
   3247      // So if a server does that and sends 200 instead of 206 that we
   3248      // expect, notify our caller.
   3249      // However, if we wanted to start from the beginning, let it go through
   3250      if (LoadResuming() && mStartPos != 0) {
   3251        LOG(("Server ignored our Range header, cancelling [this=%p]\n", this));
   3252        Cancel(NS_ERROR_NOT_RESUMABLE);
   3253        rv = CallOnStartRequest();
   3254        break;
   3255      }
   3256      // these can normally be cached
   3257      rv = ProcessNormal();
   3258      MaybeInvalidateCacheEntryForSubsequentGet();
   3259      break;
   3260    case 206:
   3261      if (LoadCachedContentIsPartial()) {  // an internal byte range request...
   3262        auto func = [](auto* self, nsresult aRv) {
   3263          return self->ContinueProcessResponseAfterPartialContent(aRv);
   3264        };
   3265        rv = ProcessPartialContent(func);
   3266        // Directly call ContinueProcessResponseAfterPartialContent if channel
   3267        // is not suspended or ProcessPartialContent throws.
   3268        if (!mSuspendCount || NS_FAILED(rv)) {
   3269          return ContinueProcessResponseAfterPartialContent(rv);
   3270        }
   3271        return NS_OK;
   3272      } else {
   3273        mCacheInputStream.CloseAndRelease();
   3274        rv = ProcessNormal();
   3275      }
   3276      break;
   3277    case 301:
   3278    case 302:
   3279    case 303:
   3280    case 307:
   3281    case 308:
   3282 #if 0
   3283    case 305: // disabled as a security measure (see bug 187996).
   3284 #endif
   3285      // RFC 9110: 303 responses are cacheable, but only with explicit
   3286      // freshness information (max-age or Expires). Without these directives,
   3287      // 303 should not be cached.
   3288      if (httpStatus == 303) {
   3289        uint32_t freshnessLifetime = 0;
   3290        bool hasFreshness =
   3291            (NS_SUCCEEDED(
   3292                 mResponseHead->ComputeFreshnessLifetime(&freshnessLifetime)) &&
   3293             freshnessLifetime > 0) ||
   3294            mResponseHead->HasHeader(nsHttp::Expires);
   3295        if (mResponseHead->NoStore() || mResponseHead->NoCache() ||
   3296            !hasFreshness) {
   3297          CloseCacheEntry(false);
   3298        }
   3299      }
   3300      // don't store the response body for redirects
   3301      MaybeInvalidateCacheEntryForSubsequentGet();
   3302      PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse4);
   3303      rv = AsyncProcessRedirection(httpStatus);
   3304      if (NS_FAILED(rv)) {
   3305        PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse4);
   3306        LOG(("AsyncProcessRedirection failed [rv=%" PRIx32 "]\n",
   3307             static_cast<uint32_t>(rv)));
   3308        // don't cache failed redirect responses.
   3309        if (mCacheEntry) mCacheEntry->AsyncDoom(nullptr);
   3310        if (DoNotRender3xxBody(rv)) {
   3311          mStatus = rv;
   3312          DoNotifyListener();
   3313        } else {
   3314          rv = ContinueProcessResponse4(rv);
   3315        }
   3316      }
   3317      break;
   3318    case 304:
   3319      if (!ShouldBypassProcessNotModified()) {
   3320        auto func = [](auto* self, nsresult aRv) {
   3321          return self->ContinueProcessResponseAfterNotModified(aRv);
   3322        };
   3323        rv = ProcessNotModified(func);
   3324        // Directly call ContinueProcessResponseAfterNotModified if channel
   3325        // is not suspended or ProcessNotModified throws.
   3326        if (!mSuspendCount || NS_FAILED(rv)) {
   3327          return ContinueProcessResponseAfterNotModified(rv);
   3328        }
   3329        return NS_OK;
   3330      }
   3331 
   3332      // Don't cache uninformative 304
   3333      if (LoadCustomConditionalRequest()) {
   3334        CloseCacheEntry(false);
   3335      }
   3336 
   3337      if (ShouldBypassProcessNotModified() || NS_FAILED(rv)) {
   3338        rv = ProcessNormal();
   3339      }
   3340      break;
   3341    case 401:
   3342    case 407:
   3343      if (MOZ_UNLIKELY(httpStatus == 407 && transactionRestarted)) {
   3344        // The transaction has been internally restarted.  We want to
   3345        // authenticate to the proxy again, so reuse either cached credentials
   3346        // or use default credentials for NTLM/Negotiate.  This prevents
   3347        // considering the previously used credentials as invalid.
   3348        MOZ_DIAGNOSTIC_ASSERT(mAuthProvider);
   3349        if (!mAuthProvider) {
   3350          mStatus = NS_ERROR_UNEXPECTED;
   3351          return ProcessNormal();
   3352        }
   3353        mAuthProvider->ClearProxyIdent();
   3354      }
   3355      if (!LoadAuthRedirectedChannel() &&
   3356          MOZ_UNLIKELY(LoadCustomAuthHeader()) && httpStatus == 401) {
   3357        // When a custom auth header fails, we don't want to try
   3358        // any cached credentials, nor we want to ask the user.
   3359        // It's up to the consumer to re-try w/o setting a custom
   3360        // auth header if cached credentials should be attempted.
   3361        rv = NS_ERROR_FAILURE;
   3362      } else if (httpStatus == 401 &&
   3363                 StaticPrefs::
   3364                     network_auth_supress_auth_prompt_for_XFO_failures() &&
   3365                 !nsContentSecurityUtils::CheckCSPFrameAncestorAndXFO(this)) {
   3366        // CSP Frame Ancestor and X-Frame-Options check has failed
   3367        // Do not prompt http auth - Bug 1629307
   3368        rv = NS_ERROR_FAILURE;
   3369      } else {
   3370        MOZ_DIAGNOSTIC_ASSERT(mAuthProvider);
   3371        rv = mAuthProvider
   3372                 ? mAuthProvider->ProcessAuthentication(
   3373                       httpStatus, mConnectionInfo->EndToEndSSL() &&
   3374                                       mTransaction &&
   3375                                       mTransaction->ProxyConnectFailed())
   3376                 : NS_ERROR_UNEXPECTED;
   3377      }
   3378      if (rv == NS_ERROR_IN_PROGRESS) {
   3379        // authentication prompt has been invoked and result
   3380        // is expected asynchronously
   3381        mIsAuthChannel = true;
   3382        mAuthRetryPending = true;
   3383        if (httpStatus == 407 ||
   3384            (mTransaction && mTransaction->ProxyConnectFailed())) {
   3385          StoreProxyAuthPending(true);
   3386        }
   3387 
   3388        // suspend the transaction pump to stop receiving the
   3389        // unauthenticated content data. We will throw that data
   3390        // away when user provides credentials or resume the pump
   3391        // when user refuses to authenticate.
   3392        LOG(
   3393            ("Suspending the transaction, asynchronously prompting for "
   3394             "credentials"));
   3395        Suspend();
   3396 
   3397 #ifdef DEBUG
   3398        // This is for test purposes only. See bug 1683176 for details.
   3399        gHttpHandler->OnTransactionSuspendedDueToAuthentication(this);
   3400 #endif
   3401        rv = NS_OK;
   3402      } else if (NS_FAILED(rv)) {
   3403        LOG(("ProcessAuthentication failed [rv=%" PRIx32 "]\n",
   3404             static_cast<uint32_t>(rv)));
   3405        if (mTransaction && mTransaction->ProxyConnectFailed()) {
   3406          return ProcessFailedProxyConnect(httpStatus);
   3407        }
   3408        if (rv == NS_ERROR_BASIC_HTTP_AUTH_DISABLED) {
   3409          mStatus = rv;
   3410        }
   3411        rv = ProcessNormal();
   3412      } else {
   3413        mIsAuthChannel = true;
   3414        mAuthRetryPending = true;
   3415        if (StaticPrefs::network_auth_use_redirect_for_retries()) {
   3416          if (NS_SUCCEEDED(RedirectToNewChannelForAuthRetry())) {
   3417            return NS_OK;
   3418          }
   3419          mAuthRetryPending = false;
   3420          rv = ProcessNormal();
   3421        }
   3422      }
   3423      break;
   3424 
   3425    case 408:
   3426    case 425:
   3427    case 429:
   3428      // Do not cache 408, 425 and 429.
   3429      CloseCacheEntry(false);
   3430      [[fallthrough]];  // process normally
   3431    default:
   3432      rv = ProcessNormal();
   3433      MaybeInvalidateCacheEntryForSubsequentGet();
   3434      break;
   3435  }
   3436 
   3437  UpdateCacheDisposition(false, false);
   3438  return rv;
   3439 }
   3440 
   3441 nsresult nsHttpChannel::ContinueProcessResponseAfterPartialContent(
   3442    nsresult aRv) {
   3443  LOG(
   3444      ("nsHttpChannel::ContinueProcessResponseAfterPartialContent "
   3445       "[this=%p, rv=%" PRIx32 "]",
   3446       this, static_cast<uint32_t>(aRv)));
   3447 
   3448  UpdateCacheDisposition(false, NS_SUCCEEDED(aRv));
   3449  return aRv;
   3450 }
   3451 
   3452 nsresult nsHttpChannel::ContinueProcessResponseAfterNotModified(nsresult aRv) {
   3453  LOG(
   3454      ("nsHttpChannel::ContinueProcessResponseAfterNotModified "
   3455       "[this=%p, rv=%" PRIx32 "]",
   3456       this, static_cast<uint32_t>(aRv)));
   3457 
   3458  if (NS_SUCCEEDED(aRv)) {
   3459    StoreTransactionReplaced(true);
   3460    UpdateCacheDisposition(true, false);
   3461    return NS_OK;
   3462  }
   3463 
   3464  LOG(("ProcessNotModified failed [rv=%" PRIx32 "]\n",
   3465       static_cast<uint32_t>(aRv)));
   3466 
   3467  // We cannot read from the cache entry, it might be in an
   3468  // incosistent state.  Doom it and redirect the channel
   3469  // to the same URI to reload from the network.
   3470  mCacheInputStream.CloseAndRelease();
   3471  if (mCacheEntry) {
   3472    mCacheEntry->AsyncDoom(nullptr);
   3473    mCacheEntry = nullptr;
   3474  }
   3475 
   3476  nsresult rv =
   3477      StartRedirectChannelToURI(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
   3478  if (NS_SUCCEEDED(rv)) {
   3479    return NS_OK;
   3480  }
   3481 
   3482  // Don't cache uninformative 304
   3483  if (LoadCustomConditionalRequest()) {
   3484    CloseCacheEntry(false);
   3485  }
   3486 
   3487  if (ShouldBypassProcessNotModified() || NS_FAILED(rv)) {
   3488    rv = ProcessNormal();
   3489  }
   3490 
   3491  UpdateCacheDisposition(false, false);
   3492  return rv;
   3493 }
   3494 
   3495 static void ReportHttpResponseVersion(HttpVersion version) {
   3496  if (Telemetry::CanRecordPrereleaseData()) {
   3497    glean::http::response_version.AccumulateSingleSample(
   3498        static_cast<uint32_t>(version));
   3499  }
   3500  mozilla::glean::networking::http_response_version
   3501      .Get(HttpVersionToTelemetryLabel(version))
   3502      .Add(1);
   3503 }
   3504 
   3505 void nsHttpChannel::UpdateCacheDisposition(bool aSuccessfulReval,
   3506                                           bool aPartialContentUsed) {
   3507  if (mRaceDelay && !mRaceCacheWithNetwork &&
   3508      (LoadCachedContentIsPartial() || mDidReval)) {
   3509    if (aSuccessfulReval || aPartialContentUsed) {
   3510      glean::network::race_cache_validation
   3511          .EnumGet(glean::network::RaceCacheValidationLabel::eCachedcontentused)
   3512          .Add();
   3513    } else {
   3514      glean::network::race_cache_validation
   3515          .EnumGet(
   3516              glean::network::RaceCacheValidationLabel::eCachedcontentnotused)
   3517          .Add();
   3518    }
   3519  }
   3520 
   3521  PROFILER_MARKER_TEXT(
   3522      "CacheDisposition", NETWORK, {},
   3523      nsPrintfCString(
   3524          !mDidReval ? "Missed"
   3525                     : (aSuccessfulReval ? "HitViaReval" : "MissedViaReval")));
   3526  CacheDisposition cacheDisposition;
   3527  if (!mDidReval) {
   3528    cacheDisposition = kCacheMissed;
   3529  } else if (aSuccessfulReval) {
   3530    cacheDisposition = kCacheHitViaReval;
   3531  } else {
   3532    cacheDisposition = kCacheMissedViaReval;
   3533  }
   3534  mCacheDisposition = cacheDisposition;
   3535 
   3536  if (Telemetry::CanRecordPrereleaseData()) {
   3537    AccumulateCacheHitTelemetry(cacheDisposition, this);
   3538  }
   3539 
   3540  ReportHttpResponseVersion(mResponseHead->Version());
   3541 }
   3542 
   3543 // Only used for redirects (3XX responses)
   3544 nsresult nsHttpChannel::ContinueProcessResponse4(nsresult rv) {
   3545  bool doNotRender = DoNotRender3xxBody(rv);
   3546 
   3547  if (rv == NS_ERROR_DOM_BAD_URI && mRedirectURI &&
   3548      !net::SchemeIsHttpOrHttps(mRedirectURI)) {
   3549    // This was a blocked attempt to redirect and subvert the system by
   3550    // redirecting to another protocol (perhaps javascript:)
   3551    // In that case we want to throw an error instead of displaying the
   3552    // non-redirected response body.
   3553    LOG(("ContinueProcessResponse4 detected rejected Non-HTTP Redirection"));
   3554    doNotRender = true;
   3555    rv = NS_ERROR_CORRUPTED_CONTENT;
   3556  }
   3557 
   3558  if (doNotRender) {
   3559    Cancel(rv);
   3560    DoNotifyListener();
   3561    return rv;
   3562  }
   3563 
   3564  if (NS_SUCCEEDED(rv)) {
   3565    UpdateInhibitPersistentCachingFlag();
   3566 
   3567    MaybeCreateCacheEntryWhenRCWN();
   3568 
   3569    rv = InitCacheEntry();
   3570    if (NS_FAILED(rv)) {
   3571      LOG(
   3572          ("ContinueProcessResponse4 "
   3573           "failed to init cache entry [rv=%x]\n",
   3574           static_cast<uint32_t>(rv)));
   3575    }
   3576    CloseCacheEntry(false);
   3577    return NS_OK;
   3578  }
   3579 
   3580  LOG(("ContinueProcessResponse4 got failure result [rv=%" PRIx32 "]\n",
   3581       static_cast<uint32_t>(rv)));
   3582  if (mTransaction && mTransaction->ProxyConnectFailed()) {
   3583    return ProcessFailedProxyConnect(mRedirectType);
   3584  }
   3585  return ProcessNormal();
   3586 }
   3587 
   3588 nsresult nsHttpChannel::ProcessNormal() {
   3589  LOG(("nsHttpChannel::ProcessNormal [this=%p]\n", this));
   3590 
   3591  return ContinueProcessNormal(NS_OK);
   3592 }
   3593 
   3594 nsresult nsHttpChannel::ContinueProcessNormal(nsresult rv) {
   3595  LOG(("nsHttpChannel::ContinueProcessNormal [this=%p]", this));
   3596 
   3597  if (NS_FAILED(rv)) {
   3598    // Fill the failure status here, we have failed to fall back, thus we
   3599    // have to report our status as failed.
   3600    mStatus = rv;
   3601    DoNotifyListener();
   3602    return rv;
   3603  }
   3604 
   3605  rv = ProcessCrossOriginSecurityHeaders();
   3606  if (NS_FAILED(rv)) {
   3607    mStatus = rv;
   3608    HandleAsyncAbort();
   3609    return rv;
   3610  }
   3611 
   3612  // if we're here, then any byte-range requests failed to result in a partial
   3613  // response.  we must clear this flag to prevent BufferPartialContent from
   3614  // being called inside our OnDataAvailable (see bug 136678).
   3615  StoreCachedContentIsPartial(false);
   3616 
   3617  UpdateInhibitPersistentCachingFlag();
   3618 
   3619  MaybeCreateCacheEntryWhenRCWN();
   3620 
   3621  // this must be called before firing OnStartRequest, since http clients,
   3622  // such as imagelib, expect our cache entry to already have the correct
   3623  // expiration time (bug 87710).
   3624  if (mCacheEntry) {
   3625    rv = InitCacheEntry();
   3626    if (NS_FAILED(rv)) CloseCacheEntry(true);
   3627  }
   3628 
   3629  // We may need to install the cache listener before CallonStartRequest,
   3630  // since InstallCacheListener can modify the Content-Encoding to remove
   3631  // dcb/dcz (and perhaps others), and CallOnStartRequest() copies
   3632  // Content-Encoding to send to the content process.  If this doesn't
   3633  // install a listener (because this isn't a dictionary or
   3634  // dictionary-compressed), call it after CallOnStartRequest so that we
   3635  // save the compressed data in the cache, and run the decompressor in the
   3636  // content process.
   3637  bool isDictionaryCompressed = false;
   3638  nsAutoCString contentEncoding;
   3639  (void)mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
   3640  // Note: doesn't handle dcb, gzip or gzip, dcb (etc)
   3641  if (contentEncoding.Equals("dcb") || contentEncoding.Equals("dcz")) {
   3642    isDictionaryCompressed = true;
   3643  }
   3644 
   3645  if (mCacheEntry && !LoadCacheEntryIsReadOnly()) {
   3646    // XXX We may want to consider recompressing any dcb/dcz files to save space
   3647    // and improve hitrate.  Downside is CPU use, complexity and perhaps delay,
   3648    // maybe.
   3649    nsAutoCString dictionary;
   3650    if (StaticPrefs::network_http_dictionaries_enable() && IsHTTPS()) {
   3651      (void)mResponseHead->GetHeader(nsHttp::Use_As_Dictionary, dictionary);
   3652      if (!dictionary.IsEmpty()) {
   3653        if (!ParseDictionary(mCacheEntry, mResponseHead.get(), true)) {
   3654          LOG_DICTIONARIES(("Failed to parse use-as-dictionary"));
   3655        } else {
   3656          MOZ_ASSERT(mDictSaving);
   3657 
   3658          // We need to record the hash as we save it
   3659          mCacheEntry->SetDictionary(mDictSaving);
   3660        }
   3661      }
   3662    }
   3663 
   3664    if (isDictionaryCompressed || mDictSaving) {
   3665      LOG(("Decompressing before saving into cache [channel=%p]", this));
   3666      rv = DoInstallCacheListener(isDictionaryCompressed, &dictionary, 0);
   3667    }
   3668  } else {
   3669    if (isDictionaryCompressed) {
   3670      // We still need to decompress in the parent if it's dcb or dcz even if
   3671      // not saving to the cache
   3672      LOG_DICTIONARIES(
   3673          ("Removing Content-Encoding %s for %p", contentEncoding.get(), this));
   3674      nsCOMPtr<nsIStreamListener> listener;
   3675      // otherwise we won't convert in the parent process
   3676      // XXX may be redundant, but safe
   3677      SetApplyConversion(true);
   3678      rv = DoApplyContentConversionsInternal(
   3679          mListener, getter_AddRefs(listener), true, nullptr);
   3680      if (NS_FAILED(rv)) {
   3681        return rv;
   3682      }
   3683      if (listener) {
   3684        LOG_DICTIONARIES(("Installed nsHTTPCompressConv %p without cache tee",
   3685                          listener.get()));
   3686        mListener = listener;
   3687        mCompressListener = listener;
   3688        StoreHasAppliedConversion(true);
   3689      } else {
   3690        LOG_DICTIONARIES(("Didn't install decompressor without cache tee"));
   3691      }
   3692    }
   3693  }  // else we'll call InstallCacheListener after CallOnStartRequest
   3694 
   3695  // Check that the server sent us what we were asking for
   3696  if (LoadResuming()) {
   3697    // Create an entity id from the response
   3698    nsAutoCString id;
   3699    rv = GetEntityID(id);
   3700    if (NS_FAILED(rv)) {
   3701      // If creating an entity id is not possible -> error
   3702      Cancel(NS_ERROR_NOT_RESUMABLE);
   3703    } else if (mResponseHead->Status() != 206 &&
   3704               mResponseHead->Status() != 200) {
   3705      // Probably 404 Not Found, 412 Precondition Failed or
   3706      // 416 Invalid Range -> error
   3707      LOG(("Unexpected response status while resuming, aborting [this=%p]\n",
   3708           this));
   3709      Cancel(NS_ERROR_ENTITY_CHANGED);
   3710    }
   3711    // If we were passed an entity id, verify it's equal to the server's
   3712    else if (!mEntityID.IsEmpty()) {
   3713      if (!mEntityID.Equals(id)) {
   3714        LOG(("Entity mismatch, expected '%s', got '%s', aborting [this=%p]",
   3715             mEntityID.get(), id.get(), this));
   3716        Cancel(NS_ERROR_ENTITY_CHANGED);
   3717      }
   3718    }
   3719  }
   3720 
   3721  // If we don't have the entire dictionary yet, Suspend() the channel
   3722  // until the dictionary is in-memory.
   3723  if (mDictDecompress && mUsingDictionary && mShouldSuspendForDictionary &&
   3724      !mDictDecompress->DictionaryReady()) {
   3725    LOG(
   3726        ("nsHttpChannel::ContinueProcessNormal [this=%p] Suspending the "
   3727         "transaction, waiting for dictionary",
   3728         this));
   3729    Suspend();
   3730    mSuspendedForDictionary = true;
   3731  }
   3732 
   3733  rv = CallOnStartRequest();
   3734  if (NS_FAILED(rv)) return rv;
   3735 
   3736  // If we didn't install cache listeners to decompress above
   3737  // install the cache listener now (so they'll get compressed data)
   3738  if (!isDictionaryCompressed && !mDictSaving) {
   3739    // install cache listener if we still have a cache entry open
   3740    if (mCacheEntry && !LoadCacheEntryIsReadOnly()) {
   3741      rv = InstallCacheListener();
   3742      if (NS_FAILED(rv)) return rv;
   3743    }
   3744  }
   3745  return NS_OK;
   3746 }
   3747 
   3748 nsresult nsHttpChannel::PromptTempRedirect() {
   3749  if (!gHttpHandler->PromptTempRedirect()) {
   3750    return NS_OK;
   3751  }
   3752  nsresult rv;
   3753  nsCOMPtr<nsIStringBundleService> bundleService;
   3754  bundleService = mozilla::components::StringBundle::Service(&rv);
   3755  if (NS_FAILED(rv)) return rv;
   3756 
   3757  nsCOMPtr<nsIStringBundle> stringBundle;
   3758  rv =
   3759      bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(stringBundle));
   3760  if (NS_FAILED(rv)) return rv;
   3761 
   3762  nsAutoString messageString;
   3763  rv = stringBundle->GetStringFromName("RepostFormData", messageString);
   3764  if (NS_SUCCEEDED(rv)) {
   3765    bool repost = false;
   3766 
   3767    nsCOMPtr<nsIPrompt> prompt;
   3768    GetCallback(prompt);
   3769    if (!prompt) return NS_ERROR_NO_INTERFACE;
   3770 
   3771    prompt->Confirm(nullptr, messageString.get(), &repost);
   3772    if (!repost) return NS_ERROR_FAILURE;
   3773  }
   3774 
   3775  return rv;
   3776 }
   3777 
   3778 nsresult nsHttpChannel::ProxyFailover() {
   3779  LOG(("nsHttpChannel::ProxyFailover [this=%p]\n", this));
   3780 
   3781  nsresult rv;
   3782 
   3783  nsCOMPtr<nsIProtocolProxyService> pps;
   3784  pps = mozilla::components::ProtocolProxy::Service(&rv);
   3785  if (NS_FAILED(rv)) return rv;
   3786 
   3787  nsCOMPtr<nsIProxyInfo> pi;
   3788  rv = pps->GetFailoverForProxy(mConnectionInfo->ProxyInfo(), mURI, mStatus,
   3789                                getter_AddRefs(pi));
   3790 #ifdef MOZ_PROXY_DIRECT_FAILOVER
   3791  if (NS_FAILED(rv)) {
   3792    if (!StaticPrefs::network_proxy_failover_direct()) {
   3793      return rv;
   3794    }
   3795    // If this request used a failed proxy and there is no failover available,
   3796    // fallback to DIRECT connections for conservative requests.
   3797    if (LoadBeConservative()) {
   3798      rv = pps->NewProxyInfo("direct"_ns, ""_ns, 0, ""_ns, ""_ns, 0, UINT32_MAX,
   3799                             nullptr, getter_AddRefs(pi));
   3800    }
   3801 #endif
   3802    if (NS_FAILED(rv)) {
   3803      return rv;
   3804    }
   3805 #ifdef MOZ_PROXY_DIRECT_FAILOVER
   3806  }
   3807 #endif
   3808 
   3809  // XXXbz so where does this codepath remove us from the loadgroup,
   3810  // exactly?
   3811  return AsyncDoReplaceWithProxy(pi);
   3812 }
   3813 
   3814 void nsHttpChannel::SetHTTPSSVCRecord(
   3815    already_AddRefed<nsIDNSHTTPSSVCRecord>&& aRecord) {
   3816  LOG(("nsHttpChannel::SetHTTPSSVCRecord [this=%p]\n", this));
   3817  nsCOMPtr<nsIDNSHTTPSSVCRecord> record = aRecord;
   3818  MOZ_ASSERT(!mHTTPSSVCRecord);
   3819  mHTTPSSVCRecord.emplace(std::move(record));
   3820 }
   3821 
   3822 void nsHttpChannel::HandleAsyncRedirectChannelToHttps() {
   3823  MOZ_ASSERT(!mCallOnResume, "How did that happen?");
   3824 
   3825  if (mSuspendCount) {
   3826    LOG(("Waiting until resume to do async redirect to https [this=%p]\n",
   3827         this));
   3828    mCallOnResume = [](nsHttpChannel* self) {
   3829      self->HandleAsyncRedirectChannelToHttps();
   3830      return NS_OK;
   3831    };
   3832    return;
   3833  }
   3834 
   3835  nsresult rv = StartRedirectChannelToHttps();
   3836  if (NS_FAILED(rv)) {
   3837    rv = ContinueAsyncRedirectChannelToURI(rv);
   3838    if (NS_FAILED(rv)) {
   3839      LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n",
   3840           static_cast<uint32_t>(rv), this));
   3841    }
   3842  }
   3843 }
   3844 
   3845 nsresult nsHttpChannel::StartRedirectChannelToHttps() {
   3846  LOG(("nsHttpChannel::HandleAsyncRedirectChannelToHttps() [STS]\n"));
   3847 
   3848  nsCOMPtr<nsIURI> upgradedURI;
   3849  nsresult rv = NS_GetSecureUpgradedURI(mURI, getter_AddRefs(upgradedURI));
   3850  NS_ENSURE_SUCCESS(rv, rv);
   3851 
   3852  return StartRedirectChannelToURI(
   3853      upgradedURI, nsIChannelEventSink::REDIRECT_PERMANENT |
   3854                       nsIChannelEventSink::REDIRECT_STS_UPGRADE);
   3855 }
   3856 
   3857 void nsHttpChannel::HandleAsyncAPIRedirect() {
   3858  MOZ_ASSERT(!mCallOnResume, "How did that happen?");
   3859  MOZ_ASSERT(mAPIRedirectTo, "How did that happen?");
   3860  MOZ_ASSERT(mAPIRedirectTo->first(), "How did that happen?");
   3861 
   3862  if (mSuspendCount) {
   3863    LOG(("Waiting until resume to do async API redirect [this=%p]\n", this));
   3864    mCallOnResume = [](nsHttpChannel* self) {
   3865      self->HandleAsyncAPIRedirect();
   3866      return NS_OK;
   3867    };
   3868    return;
   3869  }
   3870 
   3871  nsresult rv = StartRedirectChannelToURI(
   3872      mAPIRedirectTo->first(),
   3873      mAPIRedirectTo->second() ? nsIChannelEventSink::REDIRECT_PERMANENT |
   3874                                     nsIChannelEventSink::REDIRECT_TRANSPARENT
   3875                               : nsIChannelEventSink::REDIRECT_PERMANENT);
   3876  if (NS_FAILED(rv)) {
   3877    rv = ContinueAsyncRedirectChannelToURI(rv);
   3878    if (NS_FAILED(rv)) {
   3879      LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n",
   3880           static_cast<uint32_t>(rv), this));
   3881    }
   3882  }
   3883 }
   3884 
   3885 void nsHttpChannel::HandleAsyncRedirectToUnstrippedURI() {
   3886  MOZ_ASSERT(!mCallOnResume, "How did that happen?");
   3887 
   3888  if (mSuspendCount) {
   3889    LOG(
   3890        ("Waiting until resume to do async redirect to unstripped URI "
   3891         "[this=%p]\n",
   3892         this));
   3893    mCallOnResume = [](nsHttpChannel* self) {
   3894      self->HandleAsyncRedirectToUnstrippedURI();
   3895      return NS_OK;
   3896    };
   3897    return;
   3898  }
   3899 
   3900  nsCOMPtr<nsIURI> unstrippedURI;
   3901  mLoadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI));
   3902 
   3903  // Clear the unstripped URI from the loadInfo before starting redirect in case
   3904  // endless redirect.
   3905  mLoadInfo->SetUnstrippedURI(nullptr);
   3906 
   3907  nsresult rv = StartRedirectChannelToURI(
   3908      unstrippedURI, nsIChannelEventSink::REDIRECT_PERMANENT);
   3909 
   3910  if (NS_FAILED(rv)) {
   3911    rv = ContinueAsyncRedirectChannelToURI(rv);
   3912    if (NS_FAILED(rv)) {
   3913      LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n",
   3914           static_cast<uint32_t>(rv), this));
   3915    }
   3916  }
   3917 }
   3918 nsresult nsHttpChannel::RedirectToNewChannelForAuthRetry() {
   3919  LOG(("nsHttpChannel::RedirectToNewChannelForAuthRetry %p", this));
   3920  nsresult rv = NS_OK;
   3921 
   3922  nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect(
   3923      mURI, nsIChannelEventSink::REDIRECT_INTERNAL |
   3924                nsIChannelEventSink::REDIRECT_AUTH_RETRY);
   3925 
   3926  nsCOMPtr<nsIIOService> ioService;
   3927 
   3928  rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
   3929  NS_ENSURE_SUCCESS(rv, rv);
   3930 
   3931  nsCOMPtr<nsIChannel> newChannel;
   3932  rv = gHttpHandler->NewProxiedChannel(mURI, mProxyInfo, mProxyResolveFlags,
   3933                                       mProxyURI, mLoadInfo,
   3934                                       getter_AddRefs(newChannel));
   3935 
   3936  NS_ENSURE_SUCCESS(rv, rv);
   3937 
   3938  rv = SetupReplacementChannel(mURI, newChannel, true,
   3939                               nsIChannelEventSink::REDIRECT_INTERNAL |
   3940                                   nsIChannelEventSink::REDIRECT_AUTH_RETRY);
   3941  NS_ENSURE_SUCCESS(rv, rv);
   3942 
   3943  // rewind the upload stream
   3944  if (mUploadStream) {
   3945    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
   3946    nsresult rv = NS_ERROR_NO_INTERFACE;
   3947    if (seekable) {
   3948      rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
   3949    }
   3950 
   3951    // This should not normally happen, but it's possible that big memory
   3952    // blobs originating in the other process can't be rewinded.
   3953    // In that case we just fail the request, otherwise the content length
   3954    // will not match and this load will never complete.
   3955    NS_ENSURE_SUCCESS(rv, rv);
   3956  }
   3957 
   3958  RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(newChannel);
   3959 
   3960  MOZ_ASSERT(mAuthProvider);
   3961  httpChannelImpl->mAuthProvider = std::move(mAuthProvider);
   3962 
   3963  httpChannelImpl->mProxyInfo = mProxyInfo;
   3964 
   3965  if ((mCaps & NS_HTTP_STICKY_CONNECTION) ||
   3966      mTransaction->HasStickyConnection()) {
   3967    mConnectionInfo = mTransaction->GetConnInfo();
   3968 
   3969    httpChannelImpl->mTransactionSticky = mTransaction;
   3970 
   3971    if (mTransaction->Http2Disabled()) {
   3972      httpChannelImpl->mCaps |= NS_HTTP_DISALLOW_SPDY;
   3973    }
   3974    if (mTransaction->Http3Disabled()) {
   3975      httpChannelImpl->mCaps |= NS_HTTP_DISALLOW_HTTP3;
   3976    }
   3977  }
   3978  // always set sticky connection flag
   3979  httpChannelImpl->mCaps |= NS_HTTP_STICKY_CONNECTION;
   3980 
   3981  if (LoadAuthConnectionRestartable()) {
   3982    httpChannelImpl->mCaps |= NS_HTTP_CONNECTION_RESTARTABLE;
   3983  } else {
   3984    httpChannelImpl->mCaps &= ~NS_HTTP_CONNECTION_RESTARTABLE;
   3985  }
   3986 
   3987  MOZ_ASSERT(mConnectionInfo);
   3988  httpChannelImpl->mConnectionInfo = mConnectionInfo->Clone();
   3989 
   3990  // we need to store the state to skip unnecessary checks in the new channel
   3991  httpChannelImpl->StoreAuthRedirectedChannel(true);
   3992 
   3993  // We must copy proxy and auth header to the new channel.
   3994  // Although the new channel can populate auth headers from auth cache, we
   3995  // would still like to use the auth headers generated in this channel. The
   3996  // main reason for doing this is that certain connection-based/stateful auth
   3997  // schemes like NTLM will fail when we try generate the credentials more than
   3998  // the number of times the server has presented us the challenge due to the
   3999  // usage of nonce in generating the credentials Copying the auth header will
   4000  // bypass generation of the credentials
   4001  nsAutoCString authVal;
   4002  if (NS_SUCCEEDED(GetRequestHeader("Proxy-Authorization"_ns, authVal))) {
   4003    httpChannelImpl->SetRequestHeader("Proxy-Authorization"_ns, authVal, false);
   4004  }
   4005  if (NS_SUCCEEDED(GetRequestHeader("Authorization"_ns, authVal))) {
   4006    httpChannelImpl->SetRequestHeader("Authorization"_ns, authVal, false);
   4007  }
   4008 
   4009  httpChannelImpl->SetBlockAuthPrompt(LoadBlockAuthPrompt());
   4010  mRedirectChannel = newChannel;
   4011 
   4012  rv = gHttpHandler->AsyncOnChannelRedirect(
   4013      this, newChannel,
   4014      nsIChannelEventSink::REDIRECT_INTERNAL |
   4015          nsIChannelEventSink::REDIRECT_AUTH_RETRY);
   4016 
   4017  if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
   4018 
   4019  // redirected channel will be opened after we receive the OnStopRequest
   4020 
   4021  if (NS_FAILED(rv)) {
   4022    AutoRedirectVetoNotifier notifier(this, rv);
   4023    mRedirectChannel = nullptr;
   4024  }
   4025 
   4026  return rv;
   4027 }
   4028 
   4029 nsresult nsHttpChannel::StartRedirectChannelToURI(nsIURI* upgradedURI,
   4030                                                  uint32_t flags) {
   4031  return StartRedirectChannelToURI(upgradedURI, flags, [](nsIChannel*) {});
   4032 }
   4033 
   4034 nsresult nsHttpChannel::StartRedirectChannelToURI(
   4035    nsIURI* upgradedURI, uint32_t flags,
   4036    std::function<void(nsIChannel*)>&& aCallback) {
   4037  nsresult rv = NS_OK;
   4038  LOG(("nsHttpChannel::StartRedirectChannelToURI()\n"));
   4039 
   4040  nsCOMPtr<nsIChannel> newChannel;
   4041  nsCOMPtr<nsILoadInfo> redirectLoadInfo =
   4042      CloneLoadInfoForRedirect(upgradedURI, flags);
   4043 
   4044  nsCOMPtr<nsIIOService> ioService;
   4045  rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
   4046  NS_ENSURE_SUCCESS(rv, rv);
   4047 
   4048  rv = NS_NewChannelInternal(getter_AddRefs(newChannel), upgradedURI,
   4049                             redirectLoadInfo,
   4050                             nullptr,  // PerformanceStorage
   4051                             nullptr,  // aLoadGroup
   4052                             nullptr,  // aCallbacks
   4053                             nsIRequest::LOAD_NORMAL, ioService);
   4054  NS_ENSURE_SUCCESS(rv, rv);
   4055 
   4056  rv = SetupReplacementChannel(upgradedURI, newChannel, true, flags);
   4057  NS_ENSURE_SUCCESS(rv, rv);
   4058 
   4059  if (mHTTPSSVCRecord) {
   4060    RefPtr<nsHttpChannel> httpChan = do_QueryObject(newChannel);
   4061    nsCOMPtr<nsIDNSHTTPSSVCRecord> rec = mHTTPSSVCRecord.ref();
   4062    if (httpChan && rec) {
   4063      httpChan->SetHTTPSSVCRecord(rec.forget());
   4064    }
   4065  }
   4066 
   4067  // Inform consumers about this fake redirect
   4068  mRedirectChannel = newChannel;
   4069 
   4070  aCallback(newChannel);
   4071 
   4072  PushRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
   4073  rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
   4074 
   4075  if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
   4076 
   4077  if (NS_FAILED(rv)) {
   4078    AutoRedirectVetoNotifier notifier(this, rv);
   4079 
   4080    /* Remove the async call to ContinueAsyncRedirectChannelToURI().
   4081     * It is called directly by our callers upon return (to clean up
   4082     * the failed redirect). */
   4083    PopRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
   4084  }
   4085 
   4086  return rv;
   4087 }
   4088 
   4089 nsresult nsHttpChannel::ContinueAsyncRedirectChannelToURI(nsresult rv) {
   4090  LOG(("nsHttpChannel::ContinueAsyncRedirectChannelToURI [this=%p]", this));
   4091 
   4092  // Since we handle mAPIRedirectTo uri also after on-examine-response handler
   4093  // rather drop it here to avoid any redirect loops, even just hypothetical.
   4094  mAPIRedirectTo = Nothing();
   4095 
   4096  if (NS_SUCCEEDED(rv)) {
   4097    rv = OpenRedirectChannel(rv);
   4098  }
   4099 
   4100  if (NS_FAILED(rv)) {
   4101    // Cancel the channel here, the update to https had been vetoed
   4102    // but from the security reasons we have to discard the whole channel
   4103    // load.
   4104    Cancel(rv);
   4105  }
   4106 
   4107  if (mLoadGroup) {
   4108    mLoadGroup->RemoveRequest(this, nullptr, mStatus);
   4109  }
   4110 
   4111  if (NS_FAILED(rv) && !mCachePump && !mTransactionPump) {
   4112    // We have to manually notify the listener because there is not any pump
   4113    // that would call our OnStart/StopRequest after resume from waiting for
   4114    // the redirect callback.
   4115    DoNotifyListener();
   4116  }
   4117 
   4118  return rv;
   4119 }
   4120 
   4121 nsresult nsHttpChannel::OpenRedirectChannel(nsresult rv) {
   4122  AutoRedirectVetoNotifier notifier(this, rv);
   4123 
   4124  if (NS_FAILED(rv)) return rv;
   4125 
   4126  if (!mRedirectChannel) {
   4127    LOG((
   4128        "nsHttpChannel::OpenRedirectChannel unexpected null redirect channel"));
   4129    return NS_ERROR_FAILURE;
   4130  }
   4131 
   4132  // Make sure to do this after we received redirect veto answer,
   4133  // i.e. after all sinks had been notified
   4134  mRedirectChannel->SetOriginalURI(mOriginalURI);
   4135 
   4136  // open new channel
   4137  rv = mRedirectChannel->AsyncOpen(mListener);
   4138 
   4139  NS_ENSURE_SUCCESS(rv, rv);
   4140 
   4141  mStatus = NS_BINDING_REDIRECTED;
   4142 
   4143  notifier.RedirectSucceeded();
   4144 
   4145  ReleaseListeners();
   4146 
   4147  return NS_OK;
   4148 }
   4149 
   4150 nsresult nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi) {
   4151  LOG(("nsHttpChannel::AsyncDoReplaceWithProxy [this=%p pi=%p]", this, pi));
   4152  nsresult rv;
   4153 
   4154  nsCOMPtr<nsIChannel> newChannel;
   4155  rv = gHttpHandler->NewProxiedChannel(mURI, pi, mProxyResolveFlags, mProxyURI,
   4156                                       mLoadInfo, getter_AddRefs(newChannel));
   4157  if (NS_FAILED(rv)) return rv;
   4158 
   4159  uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL;
   4160 
   4161  rv = SetupReplacementChannel(mURI, newChannel, true, flags);
   4162  if (NS_FAILED(rv)) return rv;
   4163 
   4164  // Inform consumers about this fake redirect
   4165  mRedirectChannel = newChannel;
   4166 
   4167  PushRedirectAsyncFunc(&nsHttpChannel::OpenRedirectChannel);
   4168  rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags);
   4169 
   4170  if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
   4171 
   4172  if (NS_FAILED(rv)) {
   4173    AutoRedirectVetoNotifier notifier(this, rv);
   4174    PopRedirectAsyncFunc(&nsHttpChannel::OpenRedirectChannel);
   4175  }
   4176 
   4177  return rv;
   4178 }
   4179 
   4180 nsresult nsHttpChannel::ResolveProxy() {
   4181  LOG(("nsHttpChannel::ResolveProxy [this=%p]\n", this));
   4182 
   4183  nsresult rv;
   4184 
   4185  nsCOMPtr<nsIProtocolProxyService> pps;
   4186  pps = mozilla::components::ProtocolProxy::Service(&rv);
   4187  if (NS_FAILED(rv)) return rv;
   4188 
   4189  // using the nsIProtocolProxyService2 allows a minor performance
   4190  // optimization, but if an add-on has only provided the original interface
   4191  // then it is ok to use that version.
   4192  nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps);
   4193  if (pps2) {
   4194    rv = pps2->AsyncResolve2(this, mProxyResolveFlags, this, nullptr,
   4195                             getter_AddRefs(mProxyRequest));
   4196  } else {
   4197    rv = pps->AsyncResolve(static_cast<nsIChannel*>(this), mProxyResolveFlags,
   4198                           this, nullptr, getter_AddRefs(mProxyRequest));
   4199  }
   4200 
   4201  return rv;
   4202 }
   4203 
   4204 bool nsHttpChannel::ResponseWouldVary(nsICacheEntry* entry) {
   4205  nsresult rv;
   4206  nsAutoCString buf, metaKey;
   4207  (void)mCachedResponseHead->GetHeader(nsHttp::Vary, buf);
   4208 
   4209  constexpr auto prefix = "request-"_ns;
   4210 
   4211  // enumerate the elements of the Vary header...
   4212  for (const nsACString& token :
   4213       nsCCharSeparatedTokenizer(buf, NS_HTTP_HEADER_SEP).ToRange()) {
   4214    LOG(
   4215        ("nsHttpChannel::ResponseWouldVary [channel=%p] "
   4216         "processing %s\n",
   4217         this, nsPromiseFlatCString(token).get()));
   4218    //
   4219    // if "*", then assume response would vary.  technically speaking,
   4220    // "Vary: header, *" is not permitted, but we allow it anyways.
   4221    //
   4222    // We hash values of cookie-headers for the following reasons:
   4223    //
   4224    //   1- cookies can be very large in size
   4225    //
   4226    //   2- cookies may contain sensitive information.  (for parity with
   4227    //      out policy of not storing Set-cookie headers in the cache
   4228    //      meta data, we likewise do not want to store cookie headers
   4229    //      here.)
   4230    //
   4231    if (token.EqualsLiteral("*")) {
   4232      return true;  // if we encounter this, just get out of here
   4233    }
   4234 
   4235    // build cache meta data key...
   4236    metaKey = prefix + token;
   4237 
   4238    // check the last value of the given request header to see if it has
   4239    // since changed.  if so, then indeed the cached response is invalid.
   4240    nsCString lastVal;
   4241    entry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal));
   4242    LOG(
   4243        ("nsHttpChannel::ResponseWouldVary [channel=%p] "
   4244         "stored value = \"%s\"\n",
   4245         this, lastVal.get()));
   4246 
   4247    // Look for value of "Cookie" in the request headers
   4248    nsHttpAtom atom = nsHttp::ResolveAtom(token);
   4249    nsAutoCString newVal;
   4250    bool hasHeader = NS_SUCCEEDED(mRequestHead.GetHeader(atom, newVal));
   4251    if (!lastVal.IsEmpty()) {
   4252      // value for this header in cache, but no value in request
   4253      if (!hasHeader) {
   4254        return true;  // yes - response would vary
   4255      }
   4256 
   4257      // If this is a cookie-header, stored metadata is not
   4258      // the value itself but the hash. So we also hash the
   4259      // outgoing value here in order to compare the hashes
   4260      nsAutoCString hash;
   4261      if (atom == nsHttp::Cookie) {
   4262        rv = Hash(newVal.get(), hash);
   4263        // If hash failed, be conservative (the cached hash
   4264        // exists at this point) and claim response would vary
   4265        if (NS_FAILED(rv)) return true;
   4266        newVal = hash;
   4267 
   4268        LOG(
   4269            ("nsHttpChannel::ResponseWouldVary [this=%p] "
   4270             "set-cookie value hashed to %s\n",
   4271             this, newVal.get()));
   4272      }
   4273 
   4274      if (!newVal.Equals(lastVal)) {
   4275        return true;  // yes, response would vary
   4276      }
   4277 
   4278    } else if (hasHeader) {  // old value is empty, but newVal is set
   4279      return true;
   4280    }
   4281  }
   4282 
   4283  return false;
   4284 }
   4285 
   4286 // Remove an entry from Vary header, if it exists
   4287 void RemoveFromVary(nsHttpResponseHead* aResponseHead,
   4288                    const nsACString& aRemove) {
   4289  nsAutoCString buf;
   4290  (void)aResponseHead->GetHeader(nsHttp::Vary, buf);
   4291 
   4292  bool remove = false;
   4293  for (const nsACString& token :
   4294       nsCCharSeparatedTokenizer(buf, NS_HTTP_HEADER_SEP).ToRange()) {
   4295    if (token.Equals(aRemove)) {
   4296      // Need to build a new string without aRemove
   4297      remove = true;
   4298      break;
   4299    }
   4300  }
   4301  if (!remove) {
   4302    return;
   4303  }
   4304  nsAutoCString newValue;
   4305  for (const nsACString& token :
   4306       nsCCharSeparatedTokenizer(buf, NS_HTTP_HEADER_SEP).ToRange()) {
   4307    if (!token.Equals(aRemove)) {
   4308      if (!newValue.IsEmpty()) {
   4309        newValue += ","_ns;
   4310      }
   4311      newValue += token;
   4312    }
   4313  }
   4314  LOG(("RemoveFromVary %s removed, new value -> %s",
   4315       PromiseFlatCString(aRemove).get(), newValue.get()));
   4316  (void)aResponseHead->SetHeaderOverride(nsHttp::Vary, newValue);
   4317 }
   4318 
   4319 // We need to have an implementation of this function just so that we can keep
   4320 // all references to mCallOnResume of type nsHttpChannel:  it's not OK in C++
   4321 // to set a member function ptr to  a base class function.
   4322 void nsHttpChannel::HandleAsyncAbort() {
   4323  HttpAsyncAborter<nsHttpChannel>::HandleAsyncAbort();
   4324 }
   4325 
   4326 //-----------------------------------------------------------------------------
   4327 // nsHttpChannel <byte-range>
   4328 //-----------------------------------------------------------------------------
   4329 
   4330 bool nsHttpChannel::IsResumable(int64_t partialLen, int64_t contentLength,
   4331                                bool ignoreMissingPartialLen) const {
   4332  bool hasContentEncoding =
   4333      mCachedResponseHead->HasHeader(nsHttp::Content_Encoding);
   4334 
   4335  nsAutoCString etag;
   4336  (void)mCachedResponseHead->GetHeader(nsHttp::ETag, etag);
   4337  bool hasWeakEtag = !etag.IsEmpty() && StringBeginsWith(etag, "W/"_ns);
   4338 
   4339  return (partialLen < contentLength) &&
   4340         (partialLen > 0 || ignoreMissingPartialLen) && !hasContentEncoding &&
   4341         !hasWeakEtag && mCachedResponseHead->IsResumable() &&
   4342         !LoadCustomConditionalRequest() && !mCachedResponseHead->NoStore();
   4343 }
   4344 
   4345 nsresult nsHttpChannel::MaybeSetupByteRangeRequest(
   4346    int64_t partialLen, int64_t contentLength, bool ignoreMissingPartialLen) {
   4347  // Be pesimistic
   4348  StoreIsPartialRequest(false);
   4349 
   4350  if (!IsResumable(partialLen, contentLength, ignoreMissingPartialLen)) {
   4351    return NS_ERROR_NOT_RESUMABLE;
   4352  }
   4353 
   4354  // looks like a partial entry we can reuse; add If-Range
   4355  // and Range headers.
   4356  nsresult rv = SetupByteRangeRequest(partialLen);
   4357  if (NS_FAILED(rv)) {
   4358    // Make the request unconditional again.
   4359    UntieByteRangeRequest();
   4360  }
   4361 
   4362  return rv;
   4363 }
   4364 
   4365 nsresult nsHttpChannel::SetupByteRangeRequest(int64_t partialLen) {
   4366  // cached content has been found to be partial, add necessary request
   4367  // headers to complete cache entry.
   4368 
   4369  // use strongest validator available...
   4370  nsAutoCString val;
   4371  (void)mCachedResponseHead->GetHeader(nsHttp::ETag, val);
   4372  if (val.IsEmpty()) {
   4373    (void)mCachedResponseHead->GetHeader(nsHttp::Last_Modified, val);
   4374  }
   4375  if (val.IsEmpty()) {
   4376    // if we hit this code it means mCachedResponseHead->IsResumable() is
   4377    // either broken or not being called.
   4378    MOZ_ASSERT_UNREACHABLE("no cache validator");
   4379    StoreIsPartialRequest(false);
   4380    return NS_ERROR_FAILURE;
   4381  }
   4382 
   4383  char buf[64];
   4384  SprintfLiteral(buf, "bytes=%" PRId64 "-", partialLen);
   4385 
   4386  DebugOnly<nsresult> rv{};
   4387  rv = mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(buf));
   4388  MOZ_ASSERT(NS_SUCCEEDED(rv));
   4389  rv = mRequestHead.SetHeader(nsHttp::If_Range, val);
   4390  MOZ_ASSERT(NS_SUCCEEDED(rv));
   4391  StoreIsPartialRequest(true);
   4392 
   4393  return NS_OK;
   4394 }
   4395 
   4396 void nsHttpChannel::UntieByteRangeRequest() {
   4397  DebugOnly<nsresult> rv{};
   4398  rv = mRequestHead.ClearHeader(nsHttp::Range);
   4399  MOZ_ASSERT(NS_SUCCEEDED(rv));
   4400  rv = mRequestHead.ClearHeader(nsHttp::If_Range);
   4401  MOZ_ASSERT(NS_SUCCEEDED(rv));
   4402 }
   4403 
   4404 nsresult nsHttpChannel::ProcessPartialContent(
   4405    const std::function<nsresult(nsHttpChannel*, nsresult)>&
   4406        aContinueProcessResponseFunc) {
   4407  // ok, we've just received a 206
   4408  //
   4409  // we need to stream whatever data is in the cache out first, and then
   4410  // pick up whatever data is on the wire, writing it into the cache.
   4411 
   4412  LOG(("nsHttpChannel::ProcessPartialContent [this=%p]\n", this));
   4413 
   4414  NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED);
   4415  NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED);
   4416 
   4417  // Check if the content-encoding we now got is different from the one we
   4418  // got before
   4419  nsAutoCString contentEncoding, cachedContentEncoding;
   4420  // It is possible that there is not such headers
   4421  (void)mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
   4422  (void)mCachedResponseHead->GetHeader(nsHttp::Content_Encoding,
   4423                                       cachedContentEncoding);
   4424  if (nsCRT::strcasecmp(contentEncoding.get(), cachedContentEncoding.get()) !=
   4425      0) {
   4426    Cancel(NS_ERROR_INVALID_CONTENT_ENCODING);
   4427    return CallOnStartRequest();
   4428  }
   4429 
   4430  nsresult rv;
   4431 
   4432  int64_t cachedContentLength = mCachedResponseHead->ContentLength();
   4433  int64_t entitySize = mResponseHead->TotalEntitySize();
   4434 
   4435  nsAutoCString contentRange;
   4436  (void)mResponseHead->GetHeader(nsHttp::Content_Range, contentRange);
   4437  LOG(
   4438      ("nsHttpChannel::ProcessPartialContent [this=%p trans=%p] "
   4439       "original content-length %" PRId64 ", entity-size %" PRId64
   4440       ", content-range %s\n",
   4441       this, mTransaction.get(), cachedContentLength, entitySize,
   4442       contentRange.get()));
   4443 
   4444  if ((entitySize >= 0) && (cachedContentLength >= 0) &&
   4445      (entitySize != cachedContentLength)) {
   4446    LOG(
   4447        ("nsHttpChannel::ProcessPartialContent [this=%p] "
   4448         "206 has different total entity size than the content length "
   4449         "of the original partially cached entity.\n",
   4450         this));
   4451 
   4452    mCacheEntry->AsyncDoom(nullptr);
   4453    Cancel(NS_ERROR_CORRUPTED_CONTENT);
   4454    return CallOnStartRequest();
   4455  }
   4456 
   4457  if (LoadConcurrentCacheAccess()) {
   4458    // We started to read cached data sooner than its write has been done.
   4459    // But the concurrent write has not finished completely, so we had to
   4460    // do a range request.  Now let the content coming from the network
   4461    // be presented to consumers and also stored to the cache entry.
   4462 
   4463    rv = InstallCacheListener(mLogicalOffset);
   4464    if (NS_FAILED(rv)) return rv;
   4465  } else {
   4466    // suspend the current transaction
   4467    rv = mTransactionPump->Suspend();
   4468    if (NS_FAILED(rv)) return rv;
   4469  }
   4470 
   4471  // merge any new headers with the cached response headers
   4472  mCachedResponseHead->UpdateHeaders(mResponseHead.get());
   4473 
   4474  // update the cached response head
   4475  nsAutoCString head;
   4476  mCachedResponseHead->Flatten(head, true);
   4477  rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
   4478  if (NS_FAILED(rv)) return rv;
   4479 
   4480  // make the cached response be the current response
   4481  mResponseHead = std::move(mCachedResponseHead);
   4482 
   4483  UpdateInhibitPersistentCachingFlag();
   4484 
   4485  rv = UpdateExpirationTime();
   4486  if (NS_FAILED(rv)) return rv;
   4487 
   4488  // notify observers interested in looking at a response that has been
   4489  // merged with any cached headers (http-on-examine-merged-response).
   4490  gHttpHandler->OnExamineMergedResponse(this);
   4491 
   4492  if (LoadConcurrentCacheAccess()) {
   4493    StoreCachedContentIsPartial(false);
   4494    // Leave the ConcurrentCacheAccess flag set, we want to use it
   4495    // to prevent duplicate OnStartRequest call on the target listener
   4496    // in case this channel is canceled before it gets its OnStartRequest
   4497    // from the http transaction.
   4498    return rv;
   4499  }
   4500 
   4501  // Now we continue reading the network response.
   4502  // the cached content is valid, although incomplete.
   4503  StoreCachedContentIsValid(CachedContentValidity::Valid);
   4504  return CallOrWaitForResume([aContinueProcessResponseFunc](auto* self) {
   4505    nsresult rv = self->ReadFromCache();
   4506    return aContinueProcessResponseFunc(self, rv);
   4507  });
   4508 }
   4509 
   4510 nsresult nsHttpChannel::OnDoneReadingPartialCacheEntry(bool* streamDone) {
   4511  nsresult rv;
   4512 
   4513  LOG(("nsHttpChannel::OnDoneReadingPartialCacheEntry [this=%p]", this));
   4514 
   4515  // by default, assume we would have streamed all data or failed...
   4516  *streamDone = true;
   4517 
   4518  // setup cache listener to append to cache entry
   4519  int64_t size;
   4520  rv = mCacheEntry->GetDataSize(&size);
   4521  if (NS_FAILED(rv)) return rv;
   4522 
   4523  rv = InstallCacheListener(size);
   4524  if (NS_FAILED(rv)) return rv;
   4525 
   4526  // Entry is valid, do it now, after the output stream has been opened,
   4527  // otherwise when done earlier, pending readers would consider the cache
   4528  // entry still as partial (CacheEntry::GetDataSize would return the partial
   4529  // data size) and consumers would do the conditional request again.
   4530  rv = mCacheEntry->SetValid();
   4531  if (NS_FAILED(rv)) return rv;
   4532 
   4533  // need to track the logical offset of the data being sent to our listener
   4534  mLogicalOffset = size;
   4535 
   4536  // we're now completing the cached content, so we can clear this flag.
   4537  // this puts us in the state of a regular download.
   4538  StoreCachedContentIsPartial(false);
   4539  // The cache input stream pump is finished, we do not need it any more.
   4540  // (see bug 1313923)
   4541  mCachePump = nullptr;
   4542 
   4543  // resume the transaction if it exists, otherwise the pipe contained the
   4544  // remaining part of the document and we've now streamed all of the data.
   4545  if (mTransactionPump) {
   4546    rv = mTransactionPump->Resume();
   4547    if (NS_SUCCEEDED(rv)) *streamDone = false;
   4548  } else {
   4549    MOZ_ASSERT_UNREACHABLE("no transaction");
   4550  }
   4551  return rv;
   4552 }
   4553 
   4554 //-----------------------------------------------------------------------------
   4555 // nsHttpChannel <cache>
   4556 //-----------------------------------------------------------------------------
   4557 
   4558 bool nsHttpChannel::ShouldBypassProcessNotModified() {
   4559  if (LoadCustomConditionalRequest()) {
   4560    LOG(("Bypassing ProcessNotModified due to custom conditional headers"));
   4561    return true;
   4562  }
   4563 
   4564  if (!mDidReval) {
   4565    LOG(
   4566        ("Server returned a 304 response even though we did not send a "
   4567         "conditional request"));
   4568    return true;
   4569  }
   4570 
   4571  return false;
   4572 }
   4573 
   4574 void nsHttpChannel::MaybeGenerateNELReport() {
   4575  if (!StaticPrefs::network_http_network_error_logging_enabled()) {
   4576    return;
   4577  }
   4578 
   4579  nsCOMPtr<nsINetworkErrorReport> report;
   4580  if (nsCOMPtr<nsINetworkErrorLogging> nel =
   4581          components::NetworkErrorLogging::Service()) {
   4582    nel->GenerateNELReport(this, getter_AddRefs(report));
   4583  }
   4584 
   4585  mReportedNEL = true;
   4586 
   4587  // https://www.w3.org/TR/2023/WD-network-error-logging-20231005/#deliver-a-network-report
   4588  // 4. Generate a network report given these parameters:
   4589  // type: network-error
   4590  // url
   4591  // user_agent
   4592  // body
   4593 
   4594  if (!report) {
   4595    return;
   4596  }
   4597 
   4598  nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
   4599  if (!ssm) {
   4600    return;
   4601  }
   4602 
   4603  nsCOMPtr<nsIPrincipal> channelPrincipal;
   4604  ssm->GetChannelResultPrincipal(this, getter_AddRefs(channelPrincipal));
   4605  if (!channelPrincipal) {
   4606    return;
   4607  }
   4608 
   4609  nsAutoCString body;
   4610  nsAutoString group;
   4611  nsAutoString url;
   4612 
   4613  report->GetBody(body);
   4614  report->GetGroup(group);
   4615  report->GetUrl(url);
   4616 
   4617  nsAutoCString endpointURL;
   4618  ReportingHeader::GetEndpointForReportIncludeSubdomains(
   4619      group, channelPrincipal, /* includeSubdomains */ true, endpointURL);
   4620  if (endpointURL.IsEmpty()) {
   4621    return;
   4622  }
   4623 
   4624  ReportDeliver::ReportData data;
   4625  data.mType = u"network-error"_ns;
   4626  data.mGroupName = group;
   4627  data.mURL = url;
   4628  data.mFailures = 0;
   4629  data.mCreationTime = TimeStamp::Now();
   4630 
   4631  data.mPrincipal = channelPrincipal;
   4632  data.mEndpointURL = endpointURL;
   4633  data.mReportBodyJSON = body;
   4634  nsAutoCString userAgent;
   4635  // XXX(valentin): Should this be the potentially user set value of the header
   4636  // or the current value of user_agent from http handler?
   4637  (void)mRequestHead.GetHeader(nsHttp::User_Agent, userAgent);
   4638  data.mUserAgent = NS_ConvertUTF8toUTF16(userAgent);
   4639 
   4640  // Enqueue the report to be delivered by the reporting API
   4641  ReportDeliver::Fetch(data);
   4642 }
   4643 
   4644 nsresult nsHttpChannel::ProcessNotModified(
   4645    const std::function<nsresult(nsHttpChannel*, nsresult)>&
   4646        aContinueProcessResponseFunc) {
   4647  nsresult rv;
   4648 
   4649  LOG(("nsHttpChannel::ProcessNotModified [this=%p]\n", this));
   4650 
   4651  // Assert ShouldBypassProcessNotModified() has been checked before call to
   4652  // ProcessNotModified().
   4653  MOZ_ASSERT(!ShouldBypassProcessNotModified());
   4654 
   4655  MOZ_ASSERT(mCachedResponseHead);
   4656  MOZ_ASSERT(mCacheEntry);
   4657  NS_ENSURE_TRUE(mCachedResponseHead && mCacheEntry, NS_ERROR_UNEXPECTED);
   4658 
   4659  // If the 304 response contains a Last-Modified different than the
   4660  // one in our cache that is pretty suspicious and is, in at least the
   4661  // case of bug 716840, a sign of the server having previously corrupted
   4662  // our cache with a bad response. Take the minor step here of just dooming
   4663  // that cache entry so there is a fighting chance of getting things on the
   4664  // right track.
   4665 
   4666  nsAutoCString lastModifiedCached;
   4667  nsAutoCString lastModified304;
   4668 
   4669  rv =
   4670      mCachedResponseHead->GetHeader(nsHttp::Last_Modified, lastModifiedCached);
   4671  if (NS_SUCCEEDED(rv)) {
   4672    rv = mResponseHead->GetHeader(nsHttp::Last_Modified, lastModified304);
   4673  }
   4674 
   4675  if (NS_SUCCEEDED(rv) && !lastModified304.Equals(lastModifiedCached)) {
   4676    LOG(
   4677        ("Cache Entry and 304 Last-Modified Headers Do Not Match "
   4678         "[%s] and [%s]\n",
   4679         lastModifiedCached.get(), lastModified304.get()));
   4680 
   4681    mCacheEntry->AsyncDoom(nullptr);
   4682    glean::http::cache_lm_inconsistent
   4683        .EnumGet(glean::http::CacheLmInconsistentLabel::eTrue)
   4684        .Add();
   4685  }
   4686 
   4687  // merge any new headers with the cached response headers
   4688  mCachedResponseHead->UpdateHeaders(mResponseHead.get());
   4689 
   4690  // update the cached response head
   4691  nsAutoCString head;
   4692  mCachedResponseHead->Flatten(head, true);
   4693  rv = mCacheEntry->SetMetaDataElement("response-head", head.get());
   4694  if (NS_FAILED(rv)) return rv;
   4695 
   4696  if (LoadUsedNetwork() && !mReportedNEL) {
   4697    MaybeGenerateNELReport();
   4698  }
   4699 
   4700  // make the cached response be the current response
   4701  mResponseHead = std::move(mCachedResponseHead);
   4702 
   4703  UpdateInhibitPersistentCachingFlag();
   4704 
   4705  rv = UpdateExpirationTime();
   4706  if (NS_FAILED(rv)) return rv;
   4707 
   4708  rv = AddCacheEntryHeaders(mCacheEntry, false);
   4709  if (NS_FAILED(rv)) return rv;
   4710 
   4711  // notify observers interested in looking at a reponse that has been
   4712  // merged with any cached headers
   4713  gHttpHandler->OnExamineMergedResponse(this);
   4714 
   4715  StoreCachedContentIsValid(CachedContentValidity::Valid);
   4716 
   4717  // Tell other consumers the entry is OK to use
   4718  rv = mCacheEntry->SetValid();
   4719  if (NS_FAILED(rv)) return rv;
   4720 
   4721  return CallOrWaitForResume([aContinueProcessResponseFunc](auto* self) {
   4722    nsresult rv = self->ReadFromCache();
   4723    return aContinueProcessResponseFunc(self, rv);
   4724  });
   4725 }
   4726 
   4727 // Determines if a request is a byte range request for a subrange,
   4728 // i.e. is a byte range request, but not a 0- byte range request.
   4729 static bool IsSubRangeRequest(nsHttpRequestHead& aRequestHead) {
   4730  nsAutoCString byteRange;
   4731  if (NS_FAILED(aRequestHead.GetHeader(nsHttp::Range, byteRange))) {
   4732    return false;
   4733  }
   4734 
   4735  if (byteRange.EqualsLiteral("bytes=0-")) {
   4736 #ifndef ANDROID
   4737    glean::network::byte_range_request.Get("cacheable"_ns).Add(1);
   4738 #endif
   4739    return false;
   4740  }
   4741 #ifndef ANDROID
   4742  glean::network::byte_range_request.Get("not_cacheable"_ns).Add(1);
   4743 #endif
   4744  return true;
   4745 }
   4746 
   4747 nsresult nsHttpChannel::OpenCacheEntry(bool isHttps) {
   4748  // Drop this flag here
   4749  StoreConcurrentCacheAccess(0);
   4750 
   4751  LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this));
   4752 
   4753  // make sure we're not abusing this function
   4754  MOZ_ASSERT(!mCacheEntry, "cache entry already open");
   4755  if (!mRequestHead.IsGet() && !mRequestHead.IsHead() &&
   4756      !mRequestHead.IsPost() && !mRequestHead.IsPatch()) {
   4757    // don't use the cache for other types of requests
   4758    return NS_OK;
   4759  }
   4760 
   4761  MOZ_ASSERT_IF(mRequestHead.IsPost() || mRequestHead.IsPatch(), mPostID > 0);
   4762 
   4763  return OpenCacheEntryInternal(isHttps);
   4764 }
   4765 
   4766 #ifdef XP_WIN
   4767 static mozilla::Maybe<bool> sHasSSD;
   4768 #endif
   4769 
   4770 static bool RCWNEnabled() {
   4771  // State table for RCWN logic (race_with_non_ssd is Windows only):
   4772  // network.http.rcwn.enabled | Device  | race_with_non_ssd | Result
   4773  // true                      | any     | any               | Enabled
   4774  // false                     | SSD     | any               | Disabled
   4775  // false                     | non-SSD | true              | Enabled
   4776  // false                     | non-SSD | false             | Disabled
   4777 
   4778  bool rcwnEnabled = StaticPrefs::network_http_rcwn_enabled();
   4779 #ifdef XP_WIN
   4780  if (!rcwnEnabled) {
   4781    if (sHasSSD.isNothing()) {
   4782      bool hasSSD = true;
   4783      nsCOMPtr<nsIPropertyBag2> sysInfo =
   4784          mozilla::components::SystemInfo::Service();
   4785      if (NS_SUCCEEDED(sysInfo->GetPropertyAsBool(u"hasSSD"_ns, &hasSSD))) {
   4786        sHasSSD = Some(hasSSD);
   4787      } else {
   4788        // Failed to detect, assume SSD (conservative default)
   4789        sHasSSD = Some(true);
   4790      }
   4791    }
   4792    if (sHasSSD.isSome() && !sHasSSD.value()) {
   4793      // For non-SSD devices, check the non-SSD-specific preference
   4794      rcwnEnabled = StaticPrefs::network_http_rcwn_race_with_non_ssd();
   4795    }
   4796  }
   4797 #endif
   4798  return rcwnEnabled;
   4799 }
   4800 
   4801 nsresult nsHttpChannel::OpenCacheEntryInternal(bool isHttps) {
   4802  nsresult rv;
   4803 
   4804  if (LoadResuming()) {
   4805    // We don't support caching for requests initiated
   4806    // via nsIResumableChannel.
   4807    return NS_OK;
   4808  }
   4809 
   4810  // Don't cache byte range requests which are subranges, only cache 0-
   4811  // byte range requests.
   4812  if (IsSubRangeRequest(mRequestHead)) {
   4813    return NS_OK;
   4814  }
   4815 
   4816  // Handle correctly WaitForCacheEntry
   4817  AutoCacheWaitFlags waitFlags(this);
   4818 
   4819  nsAutoCString cacheKey;
   4820 
   4821  nsCOMPtr<nsICacheStorageService> cacheStorageService(
   4822      components::CacheStorage::Service());
   4823  if (!cacheStorageService) {
   4824    return NS_ERROR_NOT_AVAILABLE;
   4825  }
   4826 
   4827  nsCOMPtr<nsICacheStorage> cacheStorage;
   4828  mCacheEntryURI = mURI;
   4829 
   4830  RefPtr<LoadContextInfo> info = GetLoadContextInfo(this);
   4831  if (!info) {
   4832    return NS_ERROR_FAILURE;
   4833  }
   4834 
   4835  uint32_t cacheEntryOpenFlags;
   4836  bool offline = gIOService->IsOffline();
   4837 
   4838  RefPtr<mozilla::dom::BrowsingContext> bc;
   4839  mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
   4840 
   4841  bool maybeRCWN = false;
   4842 
   4843  nsAutoCString cacheControlRequestHeader;
   4844  (void)mRequestHead.GetHeader(nsHttp::Cache_Control,
   4845                               cacheControlRequestHeader);
   4846  CacheControlParser cacheControlRequest(cacheControlRequestHeader);
   4847  if (cacheControlRequest.NoStore()) {
   4848    return NS_OK;
   4849  }
   4850 
   4851  bool forceOffline = bc && bc->Top()->GetForceOffline();
   4852  if (offline || (mLoadFlags & INHIBIT_CACHING) || forceOffline) {
   4853    if (BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass()) &&
   4854        !offline && !forceOffline) {
   4855      return NS_OK;
   4856    }
   4857    cacheEntryOpenFlags = nsICacheStorage::OPEN_READONLY;
   4858    StoreCacheEntryIsReadOnly(true);
   4859  } else if (BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) {
   4860    cacheEntryOpenFlags = nsICacheStorage::OPEN_TRUNCATE;
   4861  } else {
   4862    cacheEntryOpenFlags =
   4863        nsICacheStorage::OPEN_NORMALLY | nsICacheStorage::CHECK_MULTITHREADED;
   4864  }
   4865 
   4866  // Remember the request is a custom conditional request so that we can
   4867  // process any 304 response correctly.
   4868  StoreCustomConditionalRequest(
   4869      mRequestHead.HasHeader(nsHttp::If_Modified_Since) ||
   4870      mRequestHead.HasHeader(nsHttp::If_None_Match) ||
   4871      mRequestHead.HasHeader(nsHttp::If_Unmodified_Since) ||
   4872      mRequestHead.HasHeader(nsHttp::If_Match) ||
   4873      mRequestHead.HasHeader(nsHttp::If_Range));
   4874 
   4875  if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) {
   4876    rv = cacheStorageService->MemoryCacheStorage(
   4877        info,  // ? choose app cache as well...
   4878        getter_AddRefs(cacheStorage));
   4879  } else if (LoadPinCacheContent()) {
   4880    rv = cacheStorageService->PinningCacheStorage(info,
   4881                                                  getter_AddRefs(cacheStorage));
   4882  } else {
   4883    // Try to race only if we use disk cache storage
   4884    maybeRCWN = mRequestHead.IsSafeMethod();
   4885    rv = cacheStorageService->DiskCacheStorage(info,
   4886                                               getter_AddRefs(cacheStorage));
   4887  }
   4888  NS_ENSURE_SUCCESS(rv, rv);
   4889 
   4890  if ((mClassOfService.Flags() & nsIClassOfService::Leader) ||
   4891      (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI)) {
   4892    cacheEntryOpenFlags |= nsICacheStorage::OPEN_PRIORITY;
   4893  }
   4894 
   4895  // Only for backward compatibility with the old cache back end.
   4896  // When removed, remove the flags and related code snippets.
   4897  if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
   4898    cacheEntryOpenFlags |= nsICacheStorage::OPEN_BYPASS_IF_BUSY;
   4899  }
   4900 
   4901  if (mPostID) {
   4902    mCacheIdExtension.Append(nsPrintfCString("%d", mPostID));
   4903  }
   4904  if (LoadIsTRRServiceChannel()) {
   4905    mCacheIdExtension.Append("TRR");
   4906  }
   4907  if (mRequestHead.IsHead()) {
   4908    mCacheIdExtension.Append("HEAD");
   4909  }
   4910  bool isThirdParty = false;
   4911  if (StaticPrefs::network_fetch_cache_partition_cross_origin() &&
   4912      (NS_FAILED(mLoadInfo->TriggeringPrincipal()->IsThirdPartyChannel(
   4913           this, &isThirdParty)) ||
   4914       isThirdParty) &&
   4915      (mLoadInfo->InternalContentPolicyType() == nsIContentPolicy::TYPE_FETCH ||
   4916       mLoadInfo->InternalContentPolicyType() ==
   4917           nsIContentPolicy::TYPE_XMLHTTPREQUEST ||
   4918       mLoadInfo->InternalContentPolicyType() ==
   4919           nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC ||
   4920       mLoadInfo->InternalContentPolicyType() ==
   4921           nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC)) {
   4922    mCacheIdExtension.Append("FETCH");
   4923  }
   4924 
   4925  mCacheOpenWithPriority = cacheEntryOpenFlags & nsICacheStorage::OPEN_PRIORITY;
   4926  mCacheQueueSizeWhenOpen =
   4927      CacheStorageService::CacheQueueSize(mCacheOpenWithPriority);
   4928 
   4929  // If the browser is set to offline, or it doesn't have any active network
   4930  // interfaces then don't race, as it's unlikely the network would win :)
   4931  if (NS_IsOffline()) {
   4932    maybeRCWN = false;
   4933  }
   4934 
   4935  if ((mNetworkTriggerDelay || RCWNEnabled()) && maybeRCWN && mAllowRCWN) {
   4936    bool hasAltData = false;
   4937    uint32_t sizeInKb = 0;
   4938    rv = cacheStorage->GetCacheIndexEntryAttrs(
   4939        mCacheEntryURI, mCacheIdExtension, &hasAltData, &sizeInKb);
   4940 
   4941    // We will attempt to race the network vs the cache if we've found
   4942    // this entry in the cache index, and it has appropriate attributes
   4943    // (doesn't have alt-data, and has a small size)
   4944    if (NS_SUCCEEDED(rv) && !hasAltData &&
   4945        sizeInKb < StaticPrefs::network_http_rcwn_small_resource_size_kb()) {
   4946      MaybeRaceCacheWithNetwork();
   4947    }
   4948  }
   4949 
   4950  if (!mCacheOpenDelay) {
   4951    MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread");
   4952    if (mNetworkTriggered) {
   4953      mRaceCacheWithNetwork = RCWNEnabled();
   4954    }
   4955    rv = cacheStorage->AsyncOpenURI(mCacheEntryURI, mCacheIdExtension,
   4956                                    cacheEntryOpenFlags, this);
   4957  } else {
   4958    // We pass `this` explicitly as a parameter due to the raw pointer
   4959    // to refcounted object in lambda analysis.
   4960    mCacheOpenFunc = [cacheEntryOpenFlags,
   4961                      cacheStorage](nsHttpChannel* self) -> void {
   4962      MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread");
   4963      cacheStorage->AsyncOpenURI(self->mCacheEntryURI, self->mCacheIdExtension,
   4964                                 cacheEntryOpenFlags, self);
   4965    };
   4966 
   4967    // calls nsHttpChannel::Notify after `mCacheOpenDelay` milliseconds
   4968    auto callback = MakeRefPtr<TimerCallback>(this);
   4969    NS_NewTimerWithCallback(getter_AddRefs(mCacheOpenTimer), callback,
   4970                            mCacheOpenDelay, nsITimer::TYPE_ONE_SHOT);
   4971  }
   4972  NS_ENSURE_SUCCESS(rv, rv);
   4973 
   4974  waitFlags.Keep(WAIT_FOR_CACHE_ENTRY);
   4975 
   4976  return NS_OK;
   4977 }
   4978 
   4979 nsresult nsHttpChannel::CheckPartial(nsICacheEntry* aEntry, int64_t* aSize,
   4980                                     int64_t* aContentLength) {
   4981  return nsHttp::CheckPartial(
   4982      aEntry, aSize, aContentLength,
   4983      mCachedResponseHead ? mCachedResponseHead.get() : mResponseHead.get());
   4984 }
   4985 
   4986 void nsHttpChannel::UntieValidationRequest() {
   4987  DebugOnly<nsresult> rv{};
   4988  // Make the request unconditional again.
   4989  rv = mRequestHead.ClearHeader(nsHttp::If_Modified_Since);
   4990  MOZ_ASSERT(NS_SUCCEEDED(rv));
   4991  rv = mRequestHead.ClearHeader(nsHttp::If_None_Match);
   4992  MOZ_ASSERT(NS_SUCCEEDED(rv));
   4993  rv = mRequestHead.ClearHeader(nsHttp::ETag);
   4994  MOZ_ASSERT(NS_SUCCEEDED(rv));
   4995 }
   4996 
   4997 NS_IMETHODIMP
   4998 nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, uint32_t* aResult) {
   4999  nsresult rv = NS_OK;
   5000 
   5001  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnCacheEntryCheck", NETWORK,
   5002                            Flow::FromPointer(this));
   5003  LOG(("nsHttpChannel::OnCacheEntryCheck enter [channel=%p entry=%p]", this,
   5004       entry));
   5005 
   5006  mozilla::MutexAutoLock lock(mRCWNLock);
   5007 
   5008  if (mRaceCacheWithNetwork && mFirstResponseSource == RESPONSE_FROM_NETWORK) {
   5009    LOG(
   5010        ("Not using cached response because we've already got one from the "
   5011         "network %p",
   5012         this));
   5013    *aResult = ENTRY_NOT_WANTED;
   5014 
   5015    // Net-win indicates that mOnStartRequestTimestamp is from net.
   5016    TimeDuration savedTime = (TimeStamp::Now() - mOnStartRequestTimestamp);
   5017    glean::network::race_cache_with_network_saved_time.AccumulateRawDuration(
   5018        savedTime);
   5019    PROFILER_MARKER_TEXT("RCWN", NETWORK, {},
   5020                         nsPrintfCString("Network won by %" PRId64 "ms",
   5021                                         int64_t(savedTime.ToMilliseconds())));
   5022    return NS_OK;
   5023  }
   5024  if (mRaceCacheWithNetwork && mFirstResponseSource == RESPONSE_PENDING) {
   5025    mOnCacheEntryCheckTimestamp = TimeStamp::Now();
   5026  }
   5027 
   5028  nsAutoCString cacheControlRequestHeader;
   5029  (void)mRequestHead.GetHeader(nsHttp::Cache_Control,
   5030                               cacheControlRequestHeader);
   5031  CacheControlParser cacheControlRequest(cacheControlRequestHeader);
   5032 
   5033  if (cacheControlRequest.NoStore()) {
   5034    LOG(
   5035        ("Not using cached response based on no-store request cache "
   5036         "directive\n"));
   5037    *aResult = ENTRY_NOT_WANTED;
   5038    return NS_OK;
   5039  }
   5040 
   5041  // Be pessimistic: assume the cache entry has no useful data.
   5042  *aResult = ENTRY_WANTED;
   5043  StoreCachedContentIsValid(CachedContentValidity::Invalid);
   5044 
   5045  nsCString buf;
   5046 
   5047  // Get the method that was used to generate the cached response
   5048  rv = entry->GetMetaDataElement("request-method", getter_Copies(buf));
   5049  NS_ENSURE_SUCCESS(rv, rv);
   5050 
   5051  bool methodWasHead = buf.EqualsLiteral("HEAD");
   5052  bool methodWasGet = buf.EqualsLiteral("GET");
   5053 
   5054  if (methodWasHead) {
   5055    // The cached response does not contain an entity.  We can only reuse
   5056    // the response if the current request is also HEAD.
   5057    if (!mRequestHead.IsHead()) {
   5058      *aResult = ENTRY_NOT_WANTED;
   5059      return NS_OK;
   5060    }
   5061  }
   5062  buf.Adopt(nullptr);
   5063 
   5064  // We'll need this value in later computations...
   5065  uint32_t lastModifiedTime;
   5066  rv = entry->GetLastModified(&lastModifiedTime);
   5067  NS_ENSURE_SUCCESS(rv, rv);
   5068 
   5069  // Determine if this is the first time that this cache entry
   5070  // has been accessed during this session.
   5071  bool fromPreviousSession =
   5072      (gHttpHandler->SessionStartTime() > lastModifiedTime);
   5073 
   5074  // Get the cached HTTP response headers
   5075  mCachedResponseHead = MakeUnique<nsHttpResponseHead>();
   5076 
   5077  rv = nsHttp::GetHttpResponseHeadFromCacheEntry(entry,
   5078                                                 mCachedResponseHead.get());
   5079  NS_ENSURE_SUCCESS(rv, rv);
   5080 
   5081  bool isCachedRedirect = WillRedirect(*mCachedResponseHead);
   5082 
   5083  // Do not return 304 responses from the cache, and also do not return
   5084  // any other non-redirect 3xx responses from the cache (see bug 759043).
   5085  NS_ENSURE_TRUE((mCachedResponseHead->Status() / 100 != 3) || isCachedRedirect,
   5086                 NS_ERROR_ABORT);
   5087 
   5088  if (mCachedResponseHead->NoStore() && LoadCacheEntryIsReadOnly()) {
   5089    // This prevents loading no-store responses when navigating back
   5090    // while the browser is set to work offline.
   5091    LOG(("  entry loading as read-only but is no-store, set INHIBIT_CACHING"));
   5092    mLoadFlags |= nsIRequest::INHIBIT_CACHING;
   5093  }
   5094 
   5095  // Don't bother to validate items that are read-only,
   5096  // unless they are read-only because of INHIBIT_CACHING
   5097  if ((LoadCacheEntryIsReadOnly() &&
   5098       !(mLoadFlags & nsIRequest::INHIBIT_CACHING))) {
   5099    int64_t size, contentLength;
   5100    rv = CheckPartial(entry, &size, &contentLength);
   5101    NS_ENSURE_SUCCESS(rv, rv);
   5102 
   5103    if (contentLength != int64_t(-1) && contentLength != size) {
   5104      *aResult = ENTRY_NOT_WANTED;
   5105      return NS_OK;
   5106    }
   5107 
   5108    rv = OpenCacheInputStream(entry, true);
   5109    if (NS_SUCCEEDED(rv)) {
   5110      StoreCachedContentIsValid(CachedContentValidity::Valid);
   5111    }
   5112    return rv;
   5113  }
   5114 
   5115  bool wantCompleteEntry = false;
   5116 
   5117  if (!methodWasHead && !isCachedRedirect) {
   5118    // If the cached content-length is set and it does not match the data
   5119    // size of the cached content, then the cached response is partial...
   5120    // either we need to issue a byte range request or we need to refetch
   5121    // the entire document.
   5122    //
   5123    // We exclude redirects from this check because we (usually) strip the
   5124    // entity when we store the cache entry, and even if we didn't, we
   5125    // always ignore a cached redirect's entity anyway. See bug 759043.
   5126    int64_t size, contentLength;
   5127    rv = CheckPartial(entry, &size, &contentLength);
   5128    NS_ENSURE_SUCCESS(rv, rv);
   5129 
   5130    if (size == int64_t(-1)) {
   5131      LOG(("  write is in progress"));
   5132      if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) {
   5133        LOG(
   5134            ("  not interested in the entry, "
   5135             "LOAD_BYPASS_LOCAL_CACHE_IF_BUSY specified"));
   5136 
   5137        *aResult = ENTRY_NOT_WANTED;
   5138        return NS_OK;
   5139      }
   5140 
   5141      // Ignore !(size > 0) from the resumability condition
   5142      if (!IsResumable(size, contentLength, true)) {
   5143        if (IsNavigation()) {
   5144          LOG(
   5145              ("  bypassing wait for the entry, "
   5146               "this is a navigational load"));
   5147          *aResult = ENTRY_NOT_WANTED;
   5148          return NS_OK;
   5149        }
   5150 
   5151        LOG(
   5152            ("  wait for entry completion, "
   5153             "response is not resumable"));
   5154 
   5155        wantCompleteEntry = true;
   5156      } else {
   5157        StoreConcurrentCacheAccess(1);
   5158      }
   5159    } else if (contentLength != int64_t(-1) && contentLength != size) {
   5160      LOG(
   5161          ("Cached data size does not match the Content-Length header "
   5162           "[content-length=%" PRId64 " size=%" PRId64 "]\n",
   5163           contentLength, size));
   5164 
   5165      rv = MaybeSetupByteRangeRequest(size, contentLength);
   5166      StoreCachedContentIsPartial(NS_SUCCEEDED(rv) && LoadIsPartialRequest());
   5167      if (LoadCachedContentIsPartial()) {
   5168        rv = OpenCacheInputStream(entry, false);
   5169        if (NS_FAILED(rv)) {
   5170          UntieByteRangeRequest();
   5171          return rv;
   5172        }
   5173 
   5174        *aResult = ENTRY_NEEDS_REVALIDATION;
   5175        return NS_OK;
   5176      }
   5177 
   5178      if (size == 0 && LoadCacheOnlyMetadata()) {
   5179        // Don't break cache entry load when the entry's data size
   5180        // is 0 and CacheOnlyMetadata flag is set. In that case we
   5181        // want to proceed since the LOAD_ONLY_IF_MODIFIED flag is
   5182        // also set.
   5183        MOZ_ASSERT(mLoadFlags & LOAD_ONLY_IF_MODIFIED);
   5184      } else {
   5185        return rv;
   5186      }
   5187    }
   5188  }
   5189 
   5190  bool isHttps = mURI->SchemeIs("https");
   5191 
   5192  bool doValidation = false;
   5193  bool doBackgroundValidation = false;
   5194  bool canAddImsHeader = true;
   5195 
   5196  bool isForcedValid = false;
   5197  entry->GetIsForcedValid(&isForcedValid);
   5198 
   5199  bool weaklyFramed, isImmutable;
   5200  nsHttp::DetermineFramingAndImmutability(entry, mCachedResponseHead.get(),
   5201                                          isHttps, &weaklyFramed, &isImmutable);
   5202 
   5203  // Cached entry is not the entity we request (see bug #633743)
   5204  if (ResponseWouldVary(entry)) {
   5205    LOG(("Validating based on Vary headers returning TRUE\n"));
   5206    canAddImsHeader = false;
   5207    doValidation = true;
   5208  } else {
   5209    if (mCachedResponseHead->ExpiresInPast() ||
   5210        mCachedResponseHead->MustValidateIfExpired()) {
   5211    }
   5212    doValidation = nsHttp::ValidationRequired(
   5213        isForcedValid, mCachedResponseHead.get(), mLoadFlags,
   5214        LoadAllowStaleCacheContent(), LoadForceValidateCacheContent(),
   5215        isImmutable, LoadCustomConditionalRequest(), mRequestHead, entry,
   5216        cacheControlRequest, fromPreviousSession, &doBackgroundValidation);
   5217  }
   5218 
   5219  nsAutoCString requestedETag;
   5220  if (!doValidation &&
   5221      NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::If_Match, requestedETag)) &&
   5222      (methodWasGet || methodWasHead)) {
   5223    nsAutoCString cachedETag;
   5224    (void)mCachedResponseHead->GetHeader(nsHttp::ETag, cachedETag);
   5225    if (!cachedETag.IsEmpty() && (StringBeginsWith(cachedETag, "W/"_ns) ||
   5226                                  !requestedETag.Equals(cachedETag))) {
   5227      // User has defined If-Match header, if the cached entry is not
   5228      // matching the provided header value or the cached ETag is weak,
   5229      // force validation.
   5230      doValidation = true;
   5231    }
   5232  }
   5233 
   5234  // Previous error should not be propagated.
   5235  rv = NS_OK;
   5236 
   5237  if (!doValidation) {
   5238    //
   5239    // Check the authorization headers used to generate the cache entry.
   5240    // We must validate the cache entry if:
   5241    //
   5242    // 1) the cache entry was generated prior to this session w/
   5243    //    credentials (see bug 103402).
   5244    // 2) the cache entry was generated w/o credentials, but would now
   5245    //    require credentials (see bug 96705).
   5246    //
   5247    // NOTE: this does not apply to proxy authentication.
   5248    //
   5249    entry->GetMetaDataElement("auth", getter_Copies(buf));
   5250    doValidation =
   5251        (fromPreviousSession && !buf.IsEmpty()) ||
   5252        (buf.IsEmpty() && mRequestHead.HasHeader(nsHttp::Authorization));
   5253  }
   5254 
   5255  // Bug #561276: We maintain a chain of cache-keys which returns cached
   5256  // 3xx-responses (redirects) in order to detect cycles. If a cycle is
   5257  // found, ignore the cached response and hit the net. Otherwise, use
   5258  // the cached response and add the cache-key to the chain. Note that
   5259  // a limited number of redirects (cached or not) is allowed and is
   5260  // enforced independently of this mechanism
   5261  if (!doValidation && isCachedRedirect) {
   5262    nsAutoCString cacheKey;
   5263    rv = GenerateCacheKey(mPostID, cacheKey);
   5264    MOZ_ASSERT(NS_SUCCEEDED(rv));
   5265 
   5266    auto redirectedCachekeys = mRedirectedCachekeys.Lock();
   5267    auto& ref = redirectedCachekeys.ref();
   5268    if (!ref) {
   5269      ref = MakeUnique<nsTArray<nsCString>>();
   5270    } else if (ref->Contains(cacheKey)) {
   5271      doValidation = true;
   5272    }
   5273 
   5274    LOG(("Redirection-chain %s key %s\n",
   5275         doValidation ? "contains" : "does not contain", cacheKey.get()));
   5276 
   5277    // Append cacheKey if not in the chain already
   5278    if (!doValidation) {
   5279      ref->AppendElement(cacheKey);
   5280    }
   5281  }
   5282 
   5283  StoreCachedContentIsValid(!doValidation ? CachedContentValidity::Valid
   5284                                          : CachedContentValidity::Invalid);
   5285 
   5286  if (isForcedValid) {
   5287    // Telemetry value is only useful if this was a prefetched item
   5288    if (!doValidation) {
   5289      // Could have gotten to a funky state with some of the if chain above
   5290      // and in nsHttp::ValidationRequired. Make sure we get it right here.
   5291      entry->MarkForcedValidUse();
   5292    }
   5293  }
   5294 
   5295  if (doValidation) {
   5296    //
   5297    // now, we are definitely going to issue a HTTP request to the server.
   5298    // make it conditional if possible.
   5299    //
   5300    // do not attempt to validate no-store content, since servers will not
   5301    // expect it to be cached.  (we only keep it in our cache for the
   5302    // purposes of back/forward, etc.)
   5303    //
   5304    // the request method MUST be either GET or HEAD (see bug 175641) and
   5305    // the cached response code must be < 400
   5306    //
   5307    // the cached content must not be weakly framed
   5308    //
   5309    // do not override conditional headers when consumer has defined its own
   5310    if (!mCachedResponseHead->NoStore() &&
   5311        (mRequestHead.IsGet() || mRequestHead.IsHead()) &&
   5312        !LoadCustomConditionalRequest() && !weaklyFramed &&
   5313        (mCachedResponseHead->Status() < 400)) {
   5314      if (LoadConcurrentCacheAccess()) {
   5315        // In case of concurrent read and also validation request we
   5316        // must wait for the current writer to close the output stream
   5317        // first.  Otherwise, when the writer's job would have been interrupted
   5318        // before all the data were downloaded, we'd have to do a range request
   5319        // which would be a second request in line during this channel's
   5320        // life-time.  nsHttpChannel is not designed to do that, so rather
   5321        // turn off concurrent read and wait for entry's completion.
   5322        // Then only re-validation or range-re-validation request will go out.
   5323        StoreConcurrentCacheAccess(0);
   5324        // This will cause that OnCacheEntryCheck is called again with the same
   5325        // entry after the writer is done.
   5326        wantCompleteEntry = true;
   5327      } else {
   5328        nsAutoCString val;
   5329        // Add If-Modified-Since header if a Last-Modified was given
   5330        // and we are allowed to do this (see bugs 510359 and 269303)
   5331        if (canAddImsHeader) {
   5332          (void)mCachedResponseHead->GetHeader(nsHttp::Last_Modified, val);
   5333          if (!val.IsEmpty()) {
   5334            rv = mRequestHead.SetHeader(nsHttp::If_Modified_Since, val);
   5335            MOZ_ASSERT(NS_SUCCEEDED(rv));
   5336          }
   5337        }
   5338        // Add If-None-Match header if an ETag was given in the response
   5339        (void)mCachedResponseHead->GetHeader(nsHttp::ETag, val);
   5340        if (!val.IsEmpty()) {
   5341          rv = mRequestHead.SetHeader(nsHttp::If_None_Match, val);
   5342          MOZ_ASSERT(NS_SUCCEEDED(rv));
   5343        }
   5344        mDidReval = true;
   5345      }
   5346    }
   5347  }
   5348 
   5349  bool valid = CachedContentIsValid();
   5350  if (valid || mDidReval) {
   5351    rv = OpenCacheInputStream(entry, valid);
   5352    if (NS_FAILED(rv)) {
   5353      // If we can't get the entity then we have to act as though we
   5354      // don't have the cache entry.
   5355      if (mDidReval) {
   5356        UntieValidationRequest();
   5357        mDidReval = false;
   5358      }
   5359      StoreCachedContentIsValid(CachedContentValidity::Invalid);
   5360    }
   5361  }
   5362 
   5363  if (mDidReval) {
   5364    *aResult = ENTRY_NEEDS_REVALIDATION;
   5365  } else if (wantCompleteEntry) {
   5366    *aResult = RECHECK_AFTER_WRITE_FINISHED;
   5367  } else {
   5368    *aResult = ENTRY_WANTED;
   5369 
   5370    if (doBackgroundValidation) {
   5371      PerformBackgroundCacheRevalidation();
   5372    }
   5373  }
   5374 
   5375  LOG(
   5376      ("nsHTTPChannel::OnCacheEntryCheck exit [this=%p doValidation=%d "
   5377       "result=%d]\n",
   5378       this, doValidation, *aResult));
   5379  return rv;
   5380 }
   5381 
   5382 NS_IMETHODIMP
   5383 nsHttpChannel::OnCacheEntryAvailable(nsICacheEntry* entry, bool aNew,
   5384                                     nsresult status) {
   5385  MOZ_ASSERT(NS_IsMainThread());
   5386 
   5387  nsresult rv;
   5388 
   5389  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnCacheEntryAvailable", NETWORK,
   5390                            Flow::FromPointer(this));
   5391  LOG(
   5392      ("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p "
   5393       "new=%d status=%" PRIx32 "] for %s",
   5394       this, entry, aNew, static_cast<uint32_t>(status), mSpec.get()));
   5395 
   5396  // if the channel's already fired onStopRequest, then we should ignore
   5397  // this event.
   5398  if (!LoadIsPending()) {
   5399    mCacheInputStream.CloseAndRelease();
   5400    return NS_OK;
   5401  }
   5402 
   5403  rv = OnCacheEntryAvailableInternal(entry, aNew, status);
   5404  if (NS_FAILED(rv)) {
   5405    CloseCacheEntry(false);
   5406    if (mRaceCacheWithNetwork && mNetworkTriggered &&
   5407        mFirstResponseSource != RESPONSE_FROM_CACHE) {
   5408      // Ignore the error if we're racing cache with network and the cache
   5409      // didn't win, The network part will handle cancelation or any other
   5410      // error. Otherwise we could end up calling the listener twice, see
   5411      // bug 1397593.
   5412      LOG(
   5413          ("  not calling AsyncAbort() because we're racing cache with "
   5414           "network"));
   5415    } else {
   5416      (void)AsyncAbort(rv);
   5417    }
   5418  }
   5419 
   5420  return NS_OK;
   5421 }
   5422 
   5423 nsresult nsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntry* entry,
   5424                                                      bool aNew,
   5425                                                      nsresult status) {
   5426  nsresult rv;
   5427 
   5428  if (mCanceled) {
   5429    LOG(("channel was canceled [this=%p status=%" PRIx32 "]\n", this,
   5430         static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
   5431    return mStatus;
   5432  }
   5433 
   5434  if (mIgnoreCacheEntry) {
   5435    if (!entry || aNew) {
   5436      // We use this flag later to decide whether to report
   5437      // LABELS_NETWORK_RACE_CACHE_VALIDATION::NotSent. We didn't have
   5438      // an usable entry, so drop the flag.
   5439      mIgnoreCacheEntry = false;
   5440    }
   5441    entry = nullptr;
   5442    status = NS_ERROR_NOT_AVAILABLE;
   5443  }
   5444 
   5445  rv = OnNormalCacheEntryAvailable(entry, aNew, status);
   5446 
   5447  if (NS_FAILED(rv) && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
   5448    return NS_ERROR_DOCUMENT_NOT_CACHED;
   5449  }
   5450 
   5451  if (NS_FAILED(rv)) {
   5452    return rv;
   5453  }
   5454 
   5455  // We may be waiting for more callbacks...
   5456  if (AwaitingCacheCallbacks()) {
   5457    return NS_OK;
   5458  }
   5459 
   5460  bool valid = CachedContentIsValid();
   5461  if (mRaceCacheWithNetwork &&
   5462      ((mCacheEntry && !valid && (mDidReval || LoadCachedContentIsPartial())) ||
   5463       mIgnoreCacheEntry)) {
   5464    // We won't send the conditional request because the unconditional
   5465    // request was already sent (see bug 1377223).
   5466    glean::network::race_cache_validation
   5467        .EnumGet(glean::network::RaceCacheValidationLabel::eNotsent)
   5468        .Add();
   5469  }
   5470 
   5471  if (mRaceCacheWithNetwork && valid) {
   5472    (void)ReadFromCache();
   5473  }
   5474 
   5475  return TriggerNetwork();
   5476 }
   5477 
   5478 nsresult nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntry* aEntry,
   5479                                                    bool aNew,
   5480                                                    nsresult aEntryStatus) {
   5481  StoreWaitForCacheEntry(LoadWaitForCacheEntry() & ~WAIT_FOR_CACHE_ENTRY);
   5482 
   5483  if (NS_FAILED(aEntryStatus) || aNew) {
   5484    // Make sure this flag is dropped.  It may happen the entry is doomed
   5485    // between OnCacheEntryCheck and OnCacheEntryAvailable.
   5486    StoreCachedContentIsValid(CachedContentValidity::Invalid);
   5487 
   5488    // From the same reason remove any conditional headers added
   5489    // in OnCacheEntryCheck.
   5490    if (mDidReval) {
   5491      LOG(("  Removing conditional request headers"));
   5492      UntieValidationRequest();
   5493      mDidReval = false;
   5494    }
   5495 
   5496    if (LoadCachedContentIsPartial()) {
   5497      LOG(("  Removing byte range request headers"));
   5498      UntieByteRangeRequest();
   5499      StoreCachedContentIsPartial(false);
   5500    }
   5501 
   5502    if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
   5503      // if this channel is only allowed to pull from the cache, then
   5504      // we must fail if we were unable to open a cache entry for read.
   5505      return NS_ERROR_DOCUMENT_NOT_CACHED;
   5506    }
   5507  }
   5508 
   5509  if (NS_SUCCEEDED(aEntryStatus)) {
   5510    mCacheEntry = aEntry;
   5511    StoreCacheEntryIsWriteOnly(aNew);
   5512 
   5513    if (!aNew && !mAsyncOpenTime.IsNull()) {
   5514      // We use microseconds for IO operations. For consistency let's use
   5515      // microseconds here too.
   5516      uint32_t duration = (TimeStamp::Now() - mAsyncOpenTime).ToMicroseconds();
   5517      bool isSlow = false;
   5518      if ((mCacheOpenWithPriority &&
   5519           mCacheQueueSizeWhenOpen >=
   5520               StaticPrefs::
   5521                   network_http_rcwn_cache_queue_priority_threshold()) ||
   5522          (!mCacheOpenWithPriority &&
   5523           mCacheQueueSizeWhenOpen >=
   5524               StaticPrefs::network_http_rcwn_cache_queue_normal_threshold())) {
   5525        isSlow = true;
   5526      }
   5527      CacheFileUtils::CachePerfStats::AddValue(
   5528          CacheFileUtils::CachePerfStats::ENTRY_OPEN, duration, isSlow);
   5529    }
   5530  }
   5531 
   5532  return NS_OK;
   5533 }
   5534 
   5535 // Generates the proper cache-key for this instance of nsHttpChannel
   5536 nsresult nsHttpChannel::GenerateCacheKey(uint32_t postID,
   5537                                         nsACString& cacheKey) {
   5538  AssembleCacheKey(mSpec.get(), postID, cacheKey);
   5539  return NS_OK;
   5540 }
   5541 
   5542 // Assembles a cache-key from the given pieces of information and |mLoadFlags|
   5543 void nsHttpChannel::AssembleCacheKey(const char* spec, uint32_t postID,
   5544                                     nsACString& cacheKey) {
   5545  cacheKey.Truncate();
   5546 
   5547  if (mLoadFlags & LOAD_ANONYMOUS) {
   5548    cacheKey.AssignLiteral("anon&");
   5549  }
   5550 
   5551  if (postID) {
   5552    char buf[32];
   5553    SprintfLiteral(buf, "id=%x&", postID);
   5554    cacheKey.Append(buf);
   5555  }
   5556 
   5557  if (!cacheKey.IsEmpty()) {
   5558    cacheKey.AppendLiteral("uri=");
   5559  }
   5560 
   5561  // Strip any trailing #ref from the URL before using it as the key
   5562  const char* p = strchr(spec, '#');
   5563  if (p) {
   5564    cacheKey.Append(spec, p - spec);
   5565  } else {
   5566    cacheKey.Append(spec);
   5567  }
   5568 }
   5569 
   5570 nsresult DoUpdateExpirationTime(nsHttpChannel* aSelf,
   5571                                nsICacheEntry* aCacheEntry,
   5572                                nsHttpResponseHead* aResponseHead,
   5573                                uint32_t& aExpirationTime) {
   5574  MOZ_ASSERT(aExpirationTime == 0);
   5575  NS_ENSURE_TRUE(aResponseHead, NS_ERROR_FAILURE);
   5576 
   5577  nsresult rv;
   5578 
   5579  if (!aResponseHead->MustValidate()) {
   5580    // For stale-while-revalidate we use expiration time as the absolute base
   5581    // for calculation of the stale window absolute end time.  Hence, when the
   5582    // entry may be served w/o revalidation, we need a non-zero value for the
   5583    // expiration time.  Let's set it to |now|, which basicly means "expired",
   5584    // same as when set to 0.
   5585    uint32_t now = NowInSeconds();
   5586    aExpirationTime = now;
   5587 
   5588    uint32_t freshnessLifetime = 0;
   5589 
   5590    rv = aResponseHead->ComputeFreshnessLifetime(&freshnessLifetime);
   5591    if (NS_FAILED(rv)) return rv;
   5592 
   5593    if (freshnessLifetime > 0) {
   5594      uint32_t currentAge = 0;
   5595 
   5596      rv = aResponseHead->ComputeCurrentAge(now, aSelf->GetRequestTime(),
   5597                                            &currentAge);
   5598      if (NS_FAILED(rv)) return rv;
   5599 
   5600      LOG(("freshnessLifetime = %u, currentAge = %u\n", freshnessLifetime,
   5601           currentAge));
   5602 
   5603      if (freshnessLifetime > currentAge) {
   5604        uint32_t timeRemaining = freshnessLifetime - currentAge;
   5605        // be careful... now + timeRemaining may overflow
   5606        if (now + timeRemaining < now) {
   5607          aExpirationTime = uint32_t(-1);
   5608        } else {
   5609          aExpirationTime = now + timeRemaining;
   5610        }
   5611      }
   5612    }
   5613  }
   5614 
   5615  rv = aCacheEntry->SetExpirationTime(aExpirationTime);
   5616  NS_ENSURE_SUCCESS(rv, rv);
   5617 
   5618  return rv;
   5619 }
   5620 
   5621 // UpdateExpirationTime is called when a new response comes in from the server.
   5622 // It updates the stored response-time and sets the expiration time on the
   5623 // cache entry.
   5624 //
   5625 // From section 13.2.4 of RFC2616, we compute expiration time as follows:
   5626 //
   5627 //    timeRemaining = freshnessLifetime - currentAge
   5628 //    expirationTime = now + timeRemaining
   5629 //
   5630 nsresult nsHttpChannel::UpdateExpirationTime() {
   5631  uint32_t expirationTime = 0;
   5632  nsresult rv = DoUpdateExpirationTime(this, mCacheEntry, mResponseHead.get(),
   5633                                       expirationTime);
   5634  NS_ENSURE_SUCCESS(rv, rv);
   5635 
   5636  return NS_OK;
   5637 }
   5638 
   5639 nsresult nsHttpChannel::OpenCacheInputStream(nsICacheEntry* cacheEntry,
   5640                                             bool startBuffering) {
   5641  nsresult rv;
   5642 
   5643  if (mURI->SchemeIs("https")) {
   5644    rv = cacheEntry->GetSecurityInfo(getter_AddRefs(mCachedSecurityInfo));
   5645    if (NS_FAILED(rv)) {
   5646      LOG(("failed to parse security-info [channel=%p, entry=%p]", this,
   5647           cacheEntry));
   5648      NS_WARNING("failed to parse security-info");
   5649      cacheEntry->AsyncDoom(nullptr);
   5650      return rv;
   5651    }
   5652 
   5653    MOZ_ASSERT(mCachedSecurityInfo);
   5654    if (!mCachedSecurityInfo) {
   5655      LOG(
   5656          ("mCacheEntry->GetSecurityInfo returned success but did not "
   5657           "return the security info [channel=%p, entry=%p]",
   5658           this, cacheEntry));
   5659      cacheEntry->AsyncDoom(nullptr);
   5660      return NS_ERROR_UNEXPECTED;  // XXX error code
   5661    }
   5662  }
   5663 
   5664  // Keep the conditions below in sync with the conditions in ReadFromCache.
   5665 
   5666  rv = NS_OK;
   5667 
   5668  if (WillRedirect(*mCachedResponseHead)) {
   5669    // Do not even try to read the entity for a redirect because we do not
   5670    // return an entity to the application when we process redirects.
   5671    LOG(("Will skip read of cached redirect entity\n"));
   5672    return NS_OK;
   5673  }
   5674 
   5675  if ((mLoadFlags & nsICachingChannel::LOAD_ONLY_IF_MODIFIED) &&
   5676      !LoadCachedContentIsPartial()) {
   5677    // For LOAD_ONLY_IF_MODIFIED, we usually don't have to deal with the
   5678    // cached entity.
   5679    LOG(
   5680        ("Will skip read from cache based on LOAD_ONLY_IF_MODIFIED "
   5681         "load flag\n"));
   5682    return NS_OK;
   5683  }
   5684 
   5685  // Open an input stream for the entity, so that the call to OpenInputStream
   5686  // happens off the main thread.
   5687  nsCOMPtr<nsIInputStream> stream;
   5688 
   5689  // If an alternate representation was requested, try to open the alt
   5690  // input stream.
   5691  // If the entry has a "is-from-child" metadata, then only open the altdata
   5692  // stream if the consumer is also from child.
   5693  bool altDataFromChild = false;
   5694  {
   5695    nsCString value;
   5696    rv = cacheEntry->GetMetaDataElement("alt-data-from-child",
   5697                                        getter_Copies(value));
   5698    altDataFromChild = !value.IsEmpty();
   5699  }
   5700 
   5701  nsAutoCString altDataType;
   5702  (void)cacheEntry->GetAltDataType(altDataType);
   5703 
   5704  nsAutoCString contentType;
   5705  mCachedResponseHead->ContentType(contentType);
   5706 
   5707  bool foundAltData = false;
   5708  bool deliverAltData = true;
   5709  if (!LoadDisableAltDataCache() && !altDataType.IsEmpty() &&
   5710      !mPreferredCachedAltDataTypes.IsEmpty() &&
   5711      altDataFromChild == LoadAltDataForChild()) {
   5712    for (auto& pref : mPreferredCachedAltDataTypes) {
   5713      if (pref.type() == altDataType &&
   5714          (pref.contentType().IsEmpty() || pref.contentType() == contentType)) {
   5715        foundAltData = true;
   5716        deliverAltData =
   5717            pref.deliverAltData() ==
   5718            nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::ASYNC;
   5719        break;
   5720      }
   5721    }
   5722  }
   5723 
   5724  nsCOMPtr<nsIInputStream> altData;
   5725  int64_t altDataSize = -1;
   5726  if (foundAltData) {
   5727    rv = cacheEntry->OpenAlternativeInputStream(altDataType,
   5728                                                getter_AddRefs(altData));
   5729    if (NS_SUCCEEDED(rv)) {
   5730      // We have succeeded.
   5731      mAvailableCachedAltDataType = altDataType;
   5732      StoreDeliveringAltData(deliverAltData);
   5733 
   5734      // Set the correct data size on the channel.
   5735      (void)cacheEntry->GetAltDataSize(&altDataSize);
   5736      mAltDataLength = altDataSize;
   5737 
   5738      LOG(("Opened alt-data input stream [type=%s, size=%" PRId64
   5739           ", deliverAltData=%d]",
   5740           altDataType.get(), mAltDataLength, deliverAltData));
   5741 
   5742      if (deliverAltData) {
   5743        stream = altData;
   5744      }
   5745    }
   5746  }
   5747 
   5748  if (!stream) {
   5749    rv = cacheEntry->OpenInputStream(0, getter_AddRefs(stream));
   5750  }
   5751 
   5752  if (NS_FAILED(rv)) {
   5753    LOG(
   5754        ("Failed to open cache input stream [channel=%p, "
   5755         "mCacheEntry=%p]",
   5756         this, cacheEntry));
   5757    return rv;
   5758  }
   5759 
   5760  if (startBuffering) {
   5761    bool nonBlocking;
   5762    rv = stream->IsNonBlocking(&nonBlocking);
   5763    if (NS_SUCCEEDED(rv) && nonBlocking) startBuffering = false;
   5764  }
   5765 
   5766  if (!startBuffering) {
   5767    // Bypass wrapping the input stream for the new cache back-end since
   5768    // nsIStreamTransportService expects a blocking stream.  Preloading of
   5769    // the data must be done on the level of the cache backend, internally.
   5770    //
   5771    // We do not connect the stream to the stream transport service if we
   5772    // have to validate the entry with the server. If we did, we would get
   5773    // into a race condition between the stream transport service reading
   5774    // the existing contents and the opening of the cache entry's output
   5775    // stream to write the new contents in the case where we get a non-304
   5776    // response.
   5777    LOG(
   5778        ("Opened cache input stream without buffering [channel=%p, "
   5779         "mCacheEntry=%p, stream=%p]",
   5780         this, cacheEntry, stream.get()));
   5781    mCacheInputStream.takeOver(stream);
   5782    return rv;
   5783  }
   5784 
   5785  // Have the stream transport service start reading the entity on one of its
   5786  // background threads.
   5787 
   5788  nsCOMPtr<nsITransport> transport;
   5789  nsCOMPtr<nsIInputStream> wrapper;
   5790 
   5791  nsCOMPtr<nsIStreamTransportService> sts(
   5792      components::StreamTransport::Service());
   5793  rv = sts ? NS_OK : NS_ERROR_NOT_AVAILABLE;
   5794  if (NS_SUCCEEDED(rv)) {
   5795    rv = sts->CreateInputTransport(stream, true, getter_AddRefs(transport));
   5796  }
   5797  if (NS_SUCCEEDED(rv)) {
   5798    rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(wrapper));
   5799  }
   5800  if (NS_SUCCEEDED(rv)) {
   5801    LOG(
   5802        ("Opened cache input stream [channel=%p, wrapper=%p, "
   5803         "transport=%p, stream=%p]",
   5804         this, wrapper.get(), transport.get(), stream.get()));
   5805  } else {
   5806    LOG(
   5807        ("Failed to open cache input stream [channel=%p, "
   5808         "wrapper=%p, transport=%p, stream=%p]",
   5809         this, wrapper.get(), transport.get(), stream.get()));
   5810 
   5811    stream->Close();
   5812    return rv;
   5813  }
   5814 
   5815  mCacheInputStream.takeOver(wrapper);
   5816 
   5817  return NS_OK;
   5818 }
   5819 
   5820 // Actually process the cached response that we started to handle in CheckCache
   5821 // and/or StartBufferingCachedEntity.
   5822 nsresult nsHttpChannel::ReadFromCache(void) {
   5823  NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE);
   5824  NS_ENSURE_TRUE(CachedContentIsValid(), NS_ERROR_FAILURE);
   5825  NS_ENSURE_TRUE(!mCachePump, NS_OK);  // already opened
   5826 
   5827  LOG(
   5828      ("nsHttpChannel::ReadFromCache [this=%p] "
   5829       "Using cached copy of: %s\n",
   5830       this, mSpec.get()));
   5831 
   5832  // When racing the cache with the network with a timer, and we get data from
   5833  // the cache, we should prevent the timer from triggering a network request.
   5834  if (mNetworkTriggerTimer) {
   5835    mNetworkTriggerTimer->Cancel();
   5836    mNetworkTriggerTimer = nullptr;
   5837  }
   5838 
   5839  if (mRaceCacheWithNetwork) {
   5840    MOZ_ASSERT(mFirstResponseSource != RESPONSE_FROM_CACHE);
   5841    if (mFirstResponseSource == RESPONSE_PENDING) {
   5842      LOG(("First response from cache"));
   5843      PROFILER_MARKER_TEXT(
   5844          "RCWN", NETWORK, {},
   5845          nsPrintfCString("Cache won for %s (%p)", mSpec.get(), this));
   5846      mFirstResponseSource = RESPONSE_FROM_CACHE;
   5847 
   5848      // Cancel the transaction because we will serve the request from the cache
   5849      CancelNetworkRequest(NS_BINDING_ABORTED);
   5850      if (mTransactionPump && mSuspendCount) {
   5851        uint32_t suspendCount = mSuspendCount;
   5852        while (suspendCount--) {
   5853          mTransactionPump->Resume();
   5854        }
   5855      }
   5856      mTransaction = nullptr;
   5857      mTransactionPump = nullptr;
   5858    } else {
   5859      MOZ_ASSERT(mFirstResponseSource == RESPONSE_FROM_NETWORK);
   5860      LOG(
   5861          ("Skipping read from cache because first response was from "
   5862           "network\n"));
   5863 
   5864      if (!mOnCacheEntryCheckTimestamp.IsNull()) {
   5865        TimeStamp currentTime = TimeStamp::Now();
   5866        TimeDuration savedTime = currentTime - mOnStartRequestTimestamp;
   5867        glean::network::race_cache_with_network_saved_time
   5868            .AccumulateRawDuration(savedTime);
   5869 
   5870        PROFILER_MARKER_TEXT(
   5871            "RCWN", NETWORK, {},
   5872            nsPrintfCString("Network won by %" PRId64 "ms for %s",
   5873                            int64_t(savedTime.ToMilliseconds()), mSpec.get()));
   5874        TimeDuration diffTime = currentTime - mOnCacheEntryCheckTimestamp;
   5875        glean::network::race_cache_with_network_ocec_on_start_diff
   5876            .AccumulateRawDuration(diffTime);
   5877      }
   5878      return NS_OK;
   5879    }
   5880  }
   5881 
   5882  if (mCachedResponseHead) mResponseHead = std::move(mCachedResponseHead);
   5883 
   5884  UpdateInhibitPersistentCachingFlag();
   5885 
   5886  // if we don't already have security info, try to get it from the cache
   5887  // entry. there are two cases to consider here: 1) we are just reading
   5888  // from the cache, or 2) this may be due to a 304 not modified response,
   5889  // in which case we could have security info from a socket transport.
   5890  if (!mSecurityInfo) mSecurityInfo = mCachedSecurityInfo;
   5891 
   5892  nsresult rv;
   5893 
   5894  // Keep the conditions below in sync with the conditions in
   5895  // StartBufferingCachedEntity.
   5896 
   5897  if (WillRedirect(*mResponseHead)) {
   5898    // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here,
   5899    // to avoid event dispatching latency.
   5900    MOZ_ASSERT(!mCacheInputStream);
   5901    LOG(("Skipping skip read of cached redirect entity\n"));
   5902    return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
   5903  }
   5904 
   5905  if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !LoadCachedContentIsPartial()) {
   5906    LOG(
   5907        ("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED "
   5908         "load flag\n"));
   5909    MOZ_ASSERT(!mCacheInputStream);
   5910    // TODO: Bug 759040 - We should call HandleAsyncNotModified directly
   5911    // here, to avoid event dispatching latency.
   5912    return AsyncCall(&nsHttpChannel::HandleAsyncNotModified);
   5913  }
   5914 
   5915  MOZ_ASSERT(mCacheInputStream);
   5916  if (!mCacheInputStream) {
   5917    NS_ERROR(
   5918        "mCacheInputStream is null but we're expecting to "
   5919        "be able to read from it.");
   5920    return NS_ERROR_UNEXPECTED;
   5921  }
   5922 
   5923  nsCOMPtr<nsIInputStream> inputStream = mCacheInputStream.forget();
   5924 
   5925  rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), inputStream, 0, 0,
   5926                                 true);
   5927  if (NS_FAILED(rv)) {
   5928    inputStream->Close();
   5929    return rv;
   5930  }
   5931 
   5932  rv = mCachePump->AsyncRead(this);
   5933  if (NS_FAILED(rv)) return rv;
   5934 
   5935  uint32_t suspendCount = mSuspendCount;
   5936  if (LoadAsyncResumePending()) {
   5937    LOG(
   5938        ("  Suspend()'ing cache pump once because of async resume pending"
   5939         ", sc=%u, pump=%p, this=%p",
   5940         suspendCount, mCachePump.get(), this));
   5941    ++suspendCount;
   5942  }
   5943  while (suspendCount--) {
   5944    mCachePump->Suspend();
   5945  }
   5946 
   5947  return NS_OK;
   5948 }
   5949 
   5950 void nsHttpChannel::CloseCacheEntry(bool doomOnFailure) {
   5951  mCacheInputStream.CloseAndRelease();
   5952 
   5953  if (!mCacheEntry) return;
   5954 
   5955  LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%" PRIx32
   5956       " CacheEntryIsWriteOnly=%x",
   5957       this, static_cast<uint32_t>(static_cast<nsresult>(mStatus)),
   5958       LoadCacheEntryIsWriteOnly()));
   5959 
   5960  // If we have begun to create or replace a cache entry, and that cache
   5961  // entry is not complete and not resumable, then it needs to be doomed.
   5962  // Otherwise, CheckCache will make the mistake of thinking that the
   5963  // partial cache entry is complete.
   5964 
   5965  bool doom = false;
   5966  if (LoadInitedCacheEntry()) {
   5967    MOZ_ASSERT(mResponseHead, "oops");
   5968    if (NS_FAILED(mStatus) && doomOnFailure && LoadCacheEntryIsWriteOnly() &&
   5969        !mResponseHead->IsResumable()) {
   5970      doom = true;
   5971    }
   5972  } else if (LoadCacheEntryIsWriteOnly()) {
   5973    doom = true;
   5974  }
   5975 
   5976  if (doom) {
   5977    LOG(("  dooming cache entry!!"));
   5978    mCacheEntry->AsyncDoom(nullptr);
   5979  } else {
   5980    // Store updated security info, makes cached EV status race less likely
   5981    // (see bug 1040086)
   5982    if (mSecurityInfo) {
   5983      mCacheEntry->SetSecurityInfo(mSecurityInfo);
   5984    }
   5985  }
   5986 
   5987  mCachedResponseHead = nullptr;
   5988 
   5989  mCachePump = nullptr;
   5990  // This releases the entry for other consumers to use.
   5991  // We call Dismiss() in case someone still keeps a reference
   5992  // to this entry handle.
   5993  mCacheEntry->Dismiss();
   5994  mCacheEntry = nullptr;
   5995  StoreCacheEntryIsWriteOnly(false);
   5996  StoreInitedCacheEntry(false);
   5997 }
   5998 
   5999 void nsHttpChannel::MaybeCreateCacheEntryWhenRCWN() {
   6000  mozilla::MutexAutoLock lock(mRCWNLock);
   6001 
   6002  // Create cache entry for writing only when we're racing cache with network
   6003  // and we don't have the entry because network won.
   6004  if (mCacheEntry || !mRaceCacheWithNetwork ||
   6005      mFirstResponseSource != RESPONSE_FROM_NETWORK ||
   6006      LoadCacheEntryIsReadOnly()) {
   6007    return;
   6008  }
   6009 
   6010  LOG(("nsHttpChannel::MaybeCreateCacheEntryWhenRCWN [this=%p]", this));
   6011 
   6012  nsCOMPtr<nsICacheStorageService> cacheStorageService(
   6013      components::CacheStorage::Service());
   6014  if (!cacheStorageService) {
   6015    return;
   6016  }
   6017 
   6018  nsCOMPtr<nsICacheStorage> cacheStorage;
   6019  RefPtr<LoadContextInfo> info = GetLoadContextInfo(this);
   6020  (void)cacheStorageService->DiskCacheStorage(info,
   6021                                              getter_AddRefs(cacheStorage));
   6022  if (!cacheStorage) {
   6023    return;
   6024  }
   6025 
   6026  (void)cacheStorage->OpenTruncate(mCacheEntryURI, mCacheIdExtension,
   6027                                   getter_AddRefs(mCacheEntry));
   6028 
   6029  LOG(("  created entry %p", mCacheEntry.get()));
   6030 
   6031  if (AwaitingCacheCallbacks()) {
   6032    // Setting mIgnoreCacheEntry to true ensures that we won't close this
   6033    // write-only entry in OnCacheEntryAvailable() if this method was called
   6034    // after OnCacheEntryCheck().
   6035    mIgnoreCacheEntry = true;
   6036  }
   6037 
   6038  mAvailableCachedAltDataType.Truncate();
   6039  StoreDeliveringAltData(false);
   6040  mAltDataLength = -1;
   6041  mCacheInputStream.CloseAndRelease();
   6042  StoreCachedContentIsValid(CachedContentValidity::Invalid);
   6043 }
   6044 
   6045 // Initialize the cache entry for writing.
   6046 //  - finalize storage policy
   6047 //  - store security info
   6048 //  - update expiration time
   6049 //  - store headers and other meta data
   6050 nsresult nsHttpChannel::InitCacheEntry() {
   6051  nsresult rv;
   6052 
   6053  NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_UNEXPECTED);
   6054  // if only reading, nothing to be done here.
   6055  if (LoadCacheEntryIsReadOnly()) return NS_OK;
   6056 
   6057  // Don't cache the response again if already cached...
   6058  if (CachedContentIsValid()) return NS_OK;
   6059 
   6060  LOG(("nsHttpChannel::InitCacheEntry [this=%p entry=%p]\n", this,
   6061       mCacheEntry.get()));
   6062 
   6063  bool recreate = !LoadCacheEntryIsWriteOnly();
   6064  bool dontPersist = mLoadFlags & INHIBIT_PERSISTENT_CACHING;
   6065 
   6066  if (!recreate && dontPersist) {
   6067    // If the current entry is persistent but we inhibit peristence
   6068    // then force recreation of the entry as memory/only.
   6069    rv = mCacheEntry->GetPersistent(&recreate);
   6070    if (NS_FAILED(rv)) return rv;
   6071  }
   6072 
   6073  if (recreate) {
   6074    LOG(
   6075        ("  we have a ready entry, but reading it again from the server -> "
   6076         "recreating cache entry\n"));
   6077    // clean the altData cache and reset this to avoid wrong content length
   6078    mAvailableCachedAltDataType.Truncate();
   6079    StoreDeliveringAltData(false);
   6080 
   6081    nsCOMPtr<nsICacheEntry> currentEntry;
   6082    currentEntry.swap(mCacheEntry);
   6083    rv = currentEntry->Recreate(dontPersist, getter_AddRefs(mCacheEntry));
   6084    if (NS_FAILED(rv)) {
   6085      LOG(("  recreation failed, the response will not be cached"));
   6086      return NS_OK;
   6087    }
   6088 
   6089    StoreCacheEntryIsWriteOnly(true);
   6090  }
   6091 
   6092  // Set the expiration time for this cache entry
   6093  rv = UpdateExpirationTime();
   6094  if (NS_FAILED(rv)) return rv;
   6095 
   6096  // mark this weakly framed until a response body is seen
   6097  mCacheEntry->SetMetaDataElement("strongly-framed", "0");
   6098 
   6099  rv = AddCacheEntryHeaders(mCacheEntry, false);
   6100  if (NS_FAILED(rv)) return rv;
   6101 
   6102  StoreInitedCacheEntry(true);
   6103 
   6104  // Don't perform the check when writing (doesn't make sense)
   6105  StoreConcurrentCacheAccess(0);
   6106 
   6107  return NS_OK;
   6108 }
   6109 
   6110 void nsHttpChannel::UpdateInhibitPersistentCachingFlag() {
   6111  // The no-store directive within the 'Cache-Control:' header indicates
   6112  // that we must not store the response in a persistent cache.
   6113  if (mResponseHead->NoStore()) {
   6114    mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
   6115    return;
   6116  }
   6117 
   6118  if (!StaticPrefs::network_cache_persist_permanent_redirects_http() &&
   6119      mURI->SchemeIs("http") &&
   6120      nsHttp::IsPermanentRedirect(mResponseHead->Status())) {
   6121    mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
   6122    return;
   6123  }
   6124 
   6125  // Only cache SSL content on disk if the pref is set
   6126  if (!gHttpHandler->IsPersistentHttpsCachingEnabled() &&
   6127      mURI->SchemeIs("https")) {
   6128    mLoadFlags |= INHIBIT_PERSISTENT_CACHING;
   6129  }
   6130 }
   6131 
   6132 nsresult DoAddCacheEntryHeaders(nsHttpChannel* self, nsICacheEntry* entry,
   6133                                nsHttpRequestHead* requestHead,
   6134                                nsHttpResponseHead* responseHead,
   6135                                nsITransportSecurityInfo* securityInfo,
   6136                                bool aModified) {
   6137  nsresult rv;
   6138 
   6139  LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] begin", self));
   6140  // Store secure data in memory only
   6141  if (securityInfo) {
   6142    entry->SetSecurityInfo(securityInfo);
   6143  }
   6144 
   6145  // Note: if aModified == false, then we're processing a 304 Not Modified,
   6146  // and we *shouldn't* have any change to the Dictionary (and won't be
   6147  // replacing the DictionaryEntry, though if the Match/Match-dest/Id/Type
   6148  // changed, we may need to rewrite it. XXX?
   6149 
   6150  // Store the HTTP request method with the cache entry so we can distinguish
   6151  // for example GET and HEAD responses.
   6152  nsAutoCString method;
   6153  requestHead->Method(method);
   6154  rv = entry->SetMetaDataElement("request-method", method.get());
   6155  if (NS_FAILED(rv)) return rv;
   6156 
   6157  // Store the HTTP authorization scheme used if any...
   6158  rv = StoreAuthorizationMetaData(entry, requestHead);
   6159  if (NS_FAILED(rv)) return rv;
   6160 
   6161  rv = self->UpdateCacheEntryHeaders(entry, nullptr);
   6162  return rv;
   6163 }
   6164 
   6165 nsresult nsHttpChannel::AddCacheEntryHeaders(nsICacheEntry* entry,
   6166                                             bool aModified) {
   6167  return DoAddCacheEntryHeaders(this, entry, &mRequestHead, mResponseHead.get(),
   6168                                mSecurityInfo, aModified);
   6169 }
   6170 
   6171 nsresult nsHttpChannel::UpdateCacheEntryHeaders(nsICacheEntry* entry,
   6172                                                const nsHttpAtom* aAtom) {
   6173  nsresult rv = NS_OK;
   6174 
   6175  // Iterate over the headers listed in the Vary response header, and
   6176  // store the value of the corresponding request header so we can verify
   6177  // that it has not varied when we try to re-use the cached response at
   6178  // a later time.  Take care to store "Cookie" headers only as hashes
   6179  // due to security considerations and the fact that they can be pretty
   6180  // large (bug 468426). We take care of "Vary: cookie" in ResponseWouldVary.
   6181  //
   6182  // NOTE: if "Vary: accept, cookie", then we will store the "accept" header
   6183  // in the cache.  we could try to avoid needlessly storing the "accept"
   6184  // header in this case, but it doesn't seem worth the extra code to perform
   6185  // the check.
   6186  {
   6187    nsAutoCString buf, metaKey;
   6188    (void)mResponseHead->GetHeader(nsHttp::Vary, buf);
   6189 
   6190    constexpr auto prefix = "request-"_ns;
   6191 
   6192    for (const nsACString& token :
   6193         nsCCharSeparatedTokenizer(buf, NS_HTTP_HEADER_SEP).ToRange()) {
   6194      LOG(
   6195          ("nsHttpChannel::ProcessVaryCacheEntryHeaders [this=%p] "
   6196           "processing %s",
   6197           this, nsPromiseFlatCString(token).get()));
   6198      if (!token.EqualsLiteral("*")) {
   6199        nsHttpAtom atom = nsHttp::ResolveAtom(token);
   6200        if (!aAtom || atom == *aAtom) {
   6201          nsAutoCString val;
   6202          nsAutoCString hash;
   6203          if (NS_SUCCEEDED(mRequestHead.GetHeader(atom, val))) {
   6204            // If cookie-header, store a hash of the value
   6205            if (atom == nsHttp::Cookie) {
   6206              LOG(
   6207                  ("nsHttpChannel::ProcessVaryCacheEntryHeaders [this=%p] "
   6208                   "cookie-value %s",
   6209                   this, val.get()));
   6210              rv = Hash(val.get(), hash);
   6211              // If hash failed, store a string not very likely
   6212              // to be the result of subsequent hashes
   6213              if (NS_FAILED(rv)) {
   6214                val = "<hash failed>"_ns;
   6215              } else {
   6216                val = hash;
   6217              }
   6218 
   6219              LOG(("   hashed to %s\n", val.get()));
   6220            }
   6221 
   6222            // build cache meta data key and set meta data element...
   6223            metaKey = prefix + token;
   6224            entry->SetMetaDataElement(metaKey.get(), val.get());
   6225          } else {
   6226            LOG(
   6227                ("nsHttpChannel::ProcessVaryCacheEntryHeaders [this=%p] "
   6228                 "clearing metadata for %s",
   6229                 this, nsPromiseFlatCString(token).get()));
   6230            metaKey = prefix + token;
   6231            entry->SetMetaDataElement(metaKey.get(), nullptr);
   6232          }
   6233        }
   6234      }
   6235    }
   6236  }
   6237  if (NS_FAILED(rv)) {
   6238    return rv;
   6239  }
   6240  // Store the received HTTP head with the cache entry as an element of
   6241  // the meta data.
   6242  nsAutoCString head;
   6243  mResponseHead->Flatten(head, true);
   6244  rv = entry->SetMetaDataElement("response-head", head.get());
   6245  if (NS_FAILED(rv)) return rv;
   6246  head.Truncate();
   6247  mResponseHead->FlattenNetworkOriginalHeaders(head);
   6248  rv = entry->SetMetaDataElement("original-response-headers", head.get());
   6249  if (NS_FAILED(rv)) return rv;
   6250 
   6251  // Indicate we have successfully finished setting metadata on the cache
   6252  // entry.
   6253  return entry->MetaDataReady();
   6254 }
   6255 
   6256 bool nsHttpChannel::ParseDictionary(nsICacheEntry* aEntry,
   6257                                    nsHttpResponseHead* aResponseHead,
   6258                                    bool aModified) {
   6259  nsAutoCString val;
   6260  if (NS_SUCCEEDED(aResponseHead->GetHeader(nsHttp::Use_As_Dictionary, val))) {
   6261    nsAutoCStringN<128> matchVal;
   6262    nsAutoCStringN<64> matchIdVal;
   6263    nsTArray<nsCString> matchDestItems;
   6264    nsAutoCString typeVal;
   6265 
   6266    if (!NS_ParseUseAsDictionary(val, matchVal, matchIdVal, matchDestItems,
   6267                                 typeVal)) {
   6268      return false;
   6269    }
   6270 
   6271    nsCString key;
   6272    nsresult rv;
   6273    if (NS_FAILED(rv = aEntry->GetKey(key))) {
   6274      return false;
   6275    }
   6276 
   6277    // Verify if the matchVal has regexp groups.  If so, reject it
   6278    UrlpPattern pattern;
   6279    UrlpOptions options{};
   6280    if (!urlp_parse_pattern_from_string(&matchVal, &mSpec, options, &pattern)) {
   6281      LOG_DICTIONARIES(
   6282          ("Failed to parse dictionary pattern %s", matchVal.get()));
   6283      return false;
   6284    }
   6285    if (urlp_get_has_regexp_groups(pattern)) {
   6286      LOG_DICTIONARIES(("Pattern %s has regexp groups", matchVal.get()));
   6287      return false;
   6288    }
   6289 
   6290    nsCString hash;
   6291    // Available now for use
   6292    RefPtr<DictionaryCache> dicts(DictionaryCache::GetInstance());
   6293    LOG_DICTIONARIES(
   6294        ("Adding DictionaryCache entry for %s: key %s, matchval %s, id=%s, "
   6295         "match-dest[0]=%s, type=%s",
   6296         mSpec.get(), key.get(), matchVal.get(), matchIdVal.get(),
   6297         matchDestItems.Length() > 0 ? matchDestItems[0].get() : "<none>",
   6298         typeVal.get()));
   6299 
   6300    uint32_t expTime = 0;
   6301    (void)GetCacheTokenExpirationTime(&expTime);
   6302 
   6303    dicts->AddEntry(mURI, key, matchVal, matchDestItems, matchIdVal, Some(hash),
   6304                    aModified, expTime, getter_AddRefs(mDictSaving));
   6305    // If this was 304 Not Modified, then we don't need the dictionary data
   6306    // (though we may update the dictionary entry if the match/id/etc changed).
   6307    // If this is 304, mDictSaving will be cleared by AddEntry.
   6308    if (mDictSaving) {
   6309      if (mDictSaving->ShouldSuspendUntilCacheRead()) {
   6310        LOG_DICTIONARIES(("Suspending %p to wait for cache read", this));
   6311        mTransactionPump->Suspend();
   6312        mDictSaving->CallbackOnCacheRead([self = RefPtr(this)](nsresult) {
   6313          LOG_DICTIONARIES(("Resuming %p after cache read", self.get()));
   6314          self->Resume();
   6315        });
   6316      }
   6317    }
   6318    return true;
   6319  }
   6320  return true;  // succeeded, no use-as-dictionary
   6321 }
   6322 
   6323 inline void GetAuthType(const char* challenge, nsCString& authType) {
   6324  const char* p;
   6325 
   6326  // get the challenge type
   6327  if ((p = strchr(challenge, ' ')) != nullptr) {
   6328    authType.Assign(challenge, p - challenge);
   6329  } else {
   6330    authType.Assign(challenge);
   6331  }
   6332 }
   6333 
   6334 nsresult StoreAuthorizationMetaData(nsICacheEntry* entry,
   6335                                    nsHttpRequestHead* requestHead) {
   6336  // Not applicable to proxy authorization...
   6337  nsAutoCString val;
   6338  if (NS_FAILED(requestHead->GetHeader(nsHttp::Authorization, val))) {
   6339    return NS_OK;
   6340  }
   6341 
   6342  // eg. [Basic realm="wally world"]
   6343  nsAutoCString buf;
   6344  GetAuthType(val.get(), buf);
   6345  return entry->SetMetaDataElement("auth", buf.get());
   6346 }
   6347 
   6348 // Finalize the cache entry
   6349 //  - may need to rewrite response headers if any headers changed
   6350 //  - may need to recalculate the expiration time if any headers changed
   6351 //  - called only for freshly written cache entries
   6352 nsresult nsHttpChannel::FinalizeCacheEntry() {
   6353  LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p]\n", this));
   6354 
   6355  // Don't update this meta-data on 304
   6356  if (LoadStronglyFramed() && !CachedContentIsValid() && mCacheEntry) {
   6357    LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p] Is Strongly Framed\n",
   6358         this));
   6359    mCacheEntry->SetMetaDataElement("strongly-framed", "1");
   6360  }
   6361 
   6362  if (mResponseHead && LoadResponseHeadersModified()) {
   6363    // Set the expiration time for this cache entry
   6364    nsresult rv = UpdateExpirationTime();
   6365    if (NS_FAILED(rv)) return rv;
   6366  }
   6367  return NS_OK;
   6368 }
   6369 
   6370 nsresult nsHttpChannel::InstallCacheListener(int64_t offset) {
   6371  return DoInstallCacheListener(false, nullptr, offset);
   6372 }
   6373 
   6374 // Open an output stream to the cache entry and insert a listener tee into
   6375 // the chain of response listeners, so the data will go the cache and the
   6376 // normal listener chain, which often will eventually include a
   6377 // decompressor. If the Content-Encoding is dcb or dcz, we'll include a
   6378 // decompressor *before* the tee, so the cache will see decompressed data
   6379 // (we can't decompress dcb/dcz when reading from the cache).  Also, if an
   6380 // entry is being used as a dictionary (Use-As-Dictionary), we want the data
   6381 // to in the cache to be decompressed, so we should install a decompressor
   6382 // before the tee as well.
   6383 nsresult nsHttpChannel::DoInstallCacheListener(bool aIsDictionaryCompressed,
   6384                                               nsACString* aDictionary,
   6385                                               int64_t offset) {
   6386  nsresult rv;
   6387 
   6388  LOG(("Preparing to write data into the cache [uri=%s]\n", mSpec.get()));
   6389 
   6390  MOZ_ASSERT(mCacheEntry);
   6391  MOZ_ASSERT(LoadCacheEntryIsWriteOnly() || LoadCachedContentIsPartial() ||
   6392             mRaceCacheWithNetwork);
   6393  MOZ_ASSERT(mListener);
   6394 
   6395  LOG(("Trading cache input stream for output stream [channel=%p]", this));
   6396 
   6397  // We must close the input stream first because cache entries do not
   6398  // correctly handle having an output stream and input streams open at
   6399  // the same time.
   6400  mCacheInputStream.CloseAndRelease();
   6401 
   6402  int64_t predictedSize = mResponseHead->TotalEntitySize();
   6403  if (predictedSize != -1) {
   6404    predictedSize -= offset;
   6405  }
   6406 
   6407  nsCOMPtr<nsIOutputStream> out;
   6408  rv =
   6409      mCacheEntry->OpenOutputStream(offset, predictedSize, getter_AddRefs(out));
   6410  if (rv == NS_ERROR_NOT_AVAILABLE) {
   6411    LOG(("  entry doomed, not writing it [channel=%p]", this));
   6412    // Entry is already doomed.
   6413    // This may happen when expiration time is set to past and the entry
   6414    // has been removed by the background eviction logic.
   6415    return NS_OK;
   6416  }
   6417  if (rv == NS_ERROR_FILE_TOO_BIG) {
   6418    LOG(("  entry would exceed max allowed size, not writing it [channel=%p]",
   6419         this));
   6420    mCacheEntry->AsyncDoom(nullptr);
   6421    return NS_OK;
   6422  }
   6423  if (NS_FAILED(rv)) return rv;
   6424 
   6425  if (LoadCacheOnlyMetadata()) {
   6426    LOG(("Not storing content, cacheOnlyMetadata set"));
   6427    // We must open and then close the output stream of the cache entry.
   6428    // This way we indicate the content has been written (despite with zero
   6429    // length) and the entry is now in the ready state with "having data".
   6430 
   6431    out->Close();
   6432    return NS_OK;
   6433  }
   6434 
   6435  // XXX disk cache does not support overlapped i/o yet
   6436 #if 0
   6437    // Mark entry valid inorder to allow simultaneous reading...
   6438    rv = mCacheEntry->MarkValid();
   6439    if (NS_FAILED(rv)) return rv;
   6440 #endif
   6441 
   6442  nsCOMPtr<nsIStreamListenerTee> tee =
   6443      do_CreateInstance(kStreamListenerTeeCID, &rv);
   6444  if (NS_FAILED(rv)) return rv;
   6445 
   6446  rv = tee->Init(mListener, out, nullptr);
   6447  LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%" PRIx32, tee.get(),
   6448       static_cast<uint32_t>(rv)));
   6449  if (NS_FAILED(rv)) return rv;
   6450  mListener = tee;
   6451 
   6452  mWritingToCache = true;
   6453  // If this is Use-As-Dictionary we need to be able to read it quickly for
   6454  // dictionary use, OR if it's encoded in dcb or dcz (using a dictionary),
   6455  // we must decompress it before storing since we won't have the dictionary
   6456  // when we go to read it out later.
   6457  // In this case, we hook an nsHTTPCompressConv instance in before the tee
   6458  // since we don't want to have to decompress it here and again in the content
   6459  // process (if it's not dcb/dcz); if it is dcb/dcz we must decompress it
   6460  // before the content process gets to see it
   6461  // XXX We could recompress this with e.g. gzip to save space and improve
   6462  // hitrate, at the cost of some CPU.
   6463 
   6464  // Note: this doesn't handle cases like "dcb, gzip" or (worse?) "gzip, dcb".
   6465  // We could in theory handle them.
   6466  if (aDictionary || aIsDictionaryCompressed) {
   6467    nsCOMPtr<nsIStreamListener> listener;
   6468    // otherwise we won't convert in the parent process
   6469    SetApplyConversion(true);
   6470    rv = DoApplyContentConversionsInternal(mListener, getter_AddRefs(listener),
   6471                                           true, nullptr);
   6472    if (NS_FAILED(rv)) {
   6473      return rv;
   6474    }
   6475    // Remove Available-Dictionary from Vary header if present.  This
   6476    // avoids us refusing to match on a future load, for example if this
   6477    // dictionary was decoded from an earlier version using a dictionary
   6478    // (i.e. the update jquery to new version using the old version as a
   6479    // dictionary; no future load will use that old version).
   6480 
   6481    // XXX It would be slightly more efficient to remove all at once
   6482    // instead of sequentially by passing an array of strings
   6483    RemoveFromVary(mResponseHead.get(), "available-dictionary"_ns);
   6484    RemoveFromVary(mResponseHead.get(), "accept-encoding"_ns);
   6485 
   6486    if (listener) {
   6487      LOG_DICTIONARIES(
   6488          ("Installed nsHTTPCompressConv %p before tee", listener.get()));
   6489      mListener = listener;
   6490      mCompressListener = listener;
   6491      StoreHasAppliedConversion(true);
   6492 
   6493    } else {
   6494      LOG_DICTIONARIES(("Didn't install decompressor before tee"));
   6495    }
   6496    // We may have modified Content-Encoding; make sure cache metadata
   6497    // reflects that.  Pass nullptr so we pick up the Vary updates above
   6498    rv = UpdateCacheEntryHeaders(mCacheEntry, nullptr);
   6499    if (NS_FAILED(rv)) {
   6500      mCacheEntry->AsyncDoom(nullptr);
   6501      return rv;
   6502    }
   6503  }
   6504 
   6505  return NS_OK;
   6506 }
   6507 
   6508 //-----------------------------------------------------------------------------
   6509 // nsHttpChannel <redirect>
   6510 //-----------------------------------------------------------------------------
   6511 
   6512 nsresult nsHttpChannel::SetupReplacementChannel(nsIURI* newURI,
   6513                                                nsIChannel* newChannel,
   6514                                                bool preserveMethod,
   6515                                                uint32_t redirectFlags) {
   6516  LOG(
   6517      ("nsHttpChannel::SetupReplacementChannel "
   6518       "[this=%p newChannel=%p preserveMethod=%d]",
   6519       this, newChannel, preserveMethod));
   6520 
   6521  if (!mEndMarkerAdded && profiler_thread_is_being_profiled_for_markers()) {
   6522    mEndMarkerAdded = true;
   6523 
   6524    nsAutoCString requestMethod;
   6525    GetRequestMethod(requestMethod);
   6526 
   6527    int32_t priority = PRIORITY_NORMAL;
   6528    GetPriority(&priority);
   6529 
   6530    TimingStruct timings;
   6531    if (mTransaction) {
   6532      timings = mTransaction->Timings();
   6533    }
   6534 
   6535    uint64_t size = 0;
   6536    GetEncodedBodySize(&size);
   6537 
   6538    nsAutoCString contentType;
   6539    mozilla::Maybe<mozilla::net::HttpVersion> httpVersion = Nothing();
   6540    mozilla::Maybe<uint32_t> responseStatus = Nothing();
   6541    if (mResponseHead) {
   6542      mResponseHead->ContentType(contentType);
   6543      httpVersion = Some(mResponseHead->Version());
   6544      responseStatus = Some(mResponseHead->Status());
   6545    }
   6546 
   6547    RefPtr<nsIIdentChannel> newIdentChannel = do_QueryObject(newChannel);
   6548    uint64_t channelId = 0;
   6549    if (newIdentChannel) {
   6550      channelId = newIdentChannel->ChannelId();
   6551    }
   6552    profiler_add_network_marker(
   6553        mURI, requestMethod, priority, mChannelId,
   6554        NetworkLoadType::LOAD_REDIRECT, mLastStatusReported, TimeStamp::Now(),
   6555        size, mCacheDisposition, mLoadInfo->GetInnerWindowID(),
   6556        mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus,
   6557        &timings, std::move(mSource), httpVersion, responseStatus,
   6558        Some(nsDependentCString(contentType.get())), newURI, redirectFlags,
   6559        channelId);
   6560  }
   6561 
   6562  nsresult rv = HttpBaseChannel::SetupReplacementChannel(
   6563      newURI, newChannel, preserveMethod, redirectFlags);
   6564  if (NS_FAILED(rv)) return rv;
   6565 
   6566  nsAutoCString uriHost;
   6567  mURI->GetAsciiHost(uriHost);
   6568  // disable https-rr when encountering a downgrade from https to http.
   6569  // If the host would have https-rr dns-entries, it would be misconfigured
   6570  // due to giving us mixed signals:
   6571  //   1. the signal to upgrade all http requests to https,
   6572  //   2. but also downgrading to http on https via redirects.
   6573  // Add to exclude list for that reason
   6574  if (!gHttpHandler->IsHostExcludedForHTTPSRR(uriHost) &&
   6575      nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
   6576          mURI, newURI, mLoadInfo,
   6577          {nsHTTPSOnlyUtils::UpgradeDowngradeEndlessLoopOptions::
   6578               EnforceForHTTPSRR})) {
   6579    // Add the host to a excluded list because:
   6580    // 1. We don't need to do the same check again.
   6581    // 2. Other subresources in the same host will be also excluded.
   6582    gHttpHandler->ExcludeHTTPSRRHost(uriHost);
   6583    LOG(("[%p] skip HTTPS upgrade for host [%s]", this, uriHost.get()));
   6584  }
   6585 
   6586  rv = CheckRedirectLimit(newURI, redirectFlags);
   6587  NS_ENSURE_SUCCESS(rv, rv);
   6588 
   6589  // pass on the early hint observer to be able to process `103 Early Hints`
   6590  // responses after cross origin redirects
   6591  if (mEarlyHintObserver) {
   6592    if (RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(newChannel)) {
   6593      httpChannelImpl->SetEarlyHintObserver(mEarlyHintObserver);
   6594    }
   6595    mEarlyHintObserver = nullptr;
   6596  }
   6597 
   6598  // We don't support redirection for WebTransport for now.
   6599  mWebTransportSessionEventListener = nullptr;
   6600 
   6601  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
   6602  if (!httpChannel) return NS_OK;  // no other options to set
   6603 
   6604  // convey the ApplyConversion flag (bug 91862)
   6605  nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel);
   6606  if (encodedChannel) encodedChannel->SetApplyConversion(LoadApplyConversion());
   6607 
   6608  // transfer the resume information
   6609  if (LoadResuming()) {
   6610    nsCOMPtr<nsIResumableChannel> resumableChannel(
   6611        do_QueryInterface(newChannel));
   6612    if (!resumableChannel) {
   6613      NS_WARNING(
   6614          "Got asked to resume, but redirected to non-resumable channel!");
   6615      return NS_ERROR_NOT_RESUMABLE;
   6616    }
   6617    resumableChannel->ResumeAt(mStartPos, mEntityID);
   6618  }
   6619 
   6620  nsCOMPtr<nsIHttpChannelInternal> internalChannel =
   6621      do_QueryInterface(newChannel, &rv);
   6622  if (NS_SUCCEEDED(rv)) {
   6623    TimeStamp timestamp;
   6624    rv = GetNavigationStartTimeStamp(&timestamp);
   6625    if (NS_WARN_IF(NS_FAILED(rv))) {
   6626      return rv;
   6627    }
   6628    if (timestamp) {
   6629      (void)internalChannel->SetNavigationStartTimeStamp(timestamp);
   6630    }
   6631  }
   6632 
   6633  return NS_OK;
   6634 }
   6635 
   6636 nsresult nsHttpChannel::AsyncProcessRedirection(uint32_t redirectType) {
   6637  LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n", this,
   6638       redirectType));
   6639 
   6640  nsresult rv = ProcessCrossOriginSecurityHeaders();
   6641  if (NS_FAILED(rv)) {
   6642    mStatus = rv;
   6643    HandleAsyncAbort();
   6644    return rv;
   6645  }
   6646 
   6647  nsAutoCString location;
   6648 
   6649  // if a location header was not given, then we can't perform the redirect,
   6650  // so just carry on as though this were a normal response.
   6651  if (NS_FAILED(mResponseHead->GetHeader(nsHttp::Location, location))) {
   6652    return NS_ERROR_FAILURE;
   6653  }
   6654 
   6655  // If we were told to not follow redirects automatically, then again
   6656  // carry on as though this were a normal response.
   6657  if (mLoadInfo->GetDontFollowRedirects()) {
   6658    return NS_ERROR_FAILURE;
   6659  }
   6660 
   6661  // make sure non-ASCII characters in the location header are escaped.
   6662  nsAutoCString locationBuf;
   6663  if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces,
   6664                   locationBuf)) {
   6665    location = locationBuf;
   6666  }
   6667 
   6668  mRedirectType = redirectType;
   6669 
   6670  LOG(("redirecting to: %s [redirection-limit=%u]\n", location.get(),
   6671       uint32_t(mRedirectionLimit)));
   6672 
   6673  rv = CreateNewURI(location.get(), getter_AddRefs(mRedirectURI));
   6674 
   6675  if (NS_FAILED(rv)) {
   6676    LOG(("Invalid URI for redirect: Location: %s\n", location.get()));
   6677    return NS_ERROR_CORRUPTED_CONTENT;
   6678  }
   6679 
   6680  if (!StaticPrefs::network_allow_redirect_to_data() &&
   6681      !mLoadInfo->GetAllowInsecureRedirectToDataURI() &&
   6682      mRedirectURI->SchemeIs("data")) {
   6683    LOG(("Invalid data URI for redirect!"));
   6684    nsContentSecurityManager::ReportBlockedDataURI(mRedirectURI, mLoadInfo,
   6685                                                   true);
   6686    return NS_ERROR_DOM_BAD_URI;
   6687  }
   6688 
   6689  // Perform the URL query string stripping for redirects. We will only strip
   6690  // the query string if it is redirecting to a third-party URI in the top
   6691  // level.
   6692  if (StaticPrefs::privacy_query_stripping_redirect()) {
   6693    ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
   6694    bool isThirdPartyRedirectURI = true;
   6695    thirdPartyUtil->IsThirdPartyURI(mURI, mRedirectURI,
   6696                                    &isThirdPartyRedirectURI);
   6697    if (isThirdPartyRedirectURI && mLoadInfo->GetExternalContentPolicyType() ==
   6698                                       ExtContentPolicy::TYPE_DOCUMENT) {
   6699      glean::contentblocking::query_stripping_count
   6700          .EnumGet(glean::contentblocking::QueryStrippingCountLabel::eRedirect)
   6701          .Add();
   6702 
   6703      nsCOMPtr<nsIPrincipal> prin;
   6704      ContentBlockingAllowList::RecomputePrincipal(
   6705          mRedirectURI, mLoadInfo->GetOriginAttributes(), getter_AddRefs(prin));
   6706 
   6707      bool isRedirectURIInAllowList = false;
   6708      if (prin) {
   6709        ContentBlockingAllowList::Check(prin, mPrivateBrowsing,
   6710                                        isRedirectURIInAllowList);
   6711      }
   6712 
   6713      if (!isRedirectURIInAllowList) {
   6714        nsCOMPtr<nsIURI> strippedURI;
   6715 
   6716        nsCOMPtr<nsIURLQueryStringStripper> queryStripper;
   6717        queryStripper =
   6718            mozilla::components::URLQueryStringStripper::Service(&rv);
   6719        NS_ENSURE_SUCCESS(rv, rv);
   6720 
   6721        uint32_t numStripped;
   6722 
   6723        rv = queryStripper->Strip(mRedirectURI, mPrivateBrowsing,
   6724                                  getter_AddRefs(strippedURI), &numStripped);
   6725        NS_ENSURE_SUCCESS(rv, rv);
   6726 
   6727        if (numStripped) {
   6728          mUnstrippedRedirectURI = mRedirectURI;
   6729          mRedirectURI = strippedURI;
   6730 
   6731          // Record telemetry, but only if we stripped any query params.
   6732          glean::contentblocking::query_stripping_count
   6733              .EnumGet(glean::contentblocking::QueryStrippingCountLabel::
   6734                           eStripforredirect)
   6735              .Add();
   6736          glean::contentblocking::query_stripping_param_count
   6737              .AccumulateSingleSample(numStripped);
   6738        }
   6739      }
   6740    }
   6741  }
   6742 
   6743  // if we have a Set-Login header, we should try to handle it here
   6744  nsAutoCString setLogin;
   6745  if (NS_SUCCEEDED(mResponseHead->GetHeader(nsHttp::Set_Login, setLogin))) {
   6746    bool isDocument = mLoadInfo->GetExternalContentPolicyType() ==
   6747                      ExtContentPolicy::TYPE_DOCUMENT;
   6748    if (isDocument) {
   6749      auto ssm = nsContentUtils::GetSecurityManager();
   6750      if (ssm) {
   6751        nsCOMPtr<nsIPrincipal> documentPrincipal;
   6752        nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
   6753            this, getter_AddRefs(documentPrincipal));
   6754        dom::NavigatorLogin::SetLoginStatus(documentPrincipal, setLogin);
   6755      }
   6756    } else {
   6757      bool inThirdPartyContext = mLoadInfo->GetIsInThirdPartyContext();
   6758      nsIPrincipal* loadingPrincipal = mLoadInfo->GetLoadingPrincipal();
   6759      if (loadingPrincipal) {
   6760        bool isSameOriginToLoadingPrincipal =
   6761            loadingPrincipal->IsSameOrigin(mURI);
   6762        if (!inThirdPartyContext && isSameOriginToLoadingPrincipal) {
   6763          dom::NavigatorLogin::SetLoginStatus(loadingPrincipal, setLogin);
   6764        }
   6765      }
   6766    }
   6767  }
   6768 
   6769  if (NS_WARN_IF(!mRedirectURI)) {
   6770    LOG(("Invalid redirect URI after performaing query string stripping"));
   6771    return NS_ERROR_FAILURE;
   6772  }
   6773 
   6774  return ContinueProcessRedirectionAfterFallback(NS_OK);
   6775 }
   6776 
   6777 nsresult nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv) {
   6778  // Kill the current cache entry if we are redirecting
   6779  // back to ourself.
   6780  bool redirectingBackToSameURI = false;
   6781  if (mCacheEntry && LoadCacheEntryIsWriteOnly() &&
   6782      NS_SUCCEEDED(mURI->Equals(mRedirectURI, &redirectingBackToSameURI)) &&
   6783      redirectingBackToSameURI) {
   6784    mCacheEntry->AsyncDoom(nullptr);
   6785  }
   6786 
   6787  // move the reference of the old location to the new one if the new
   6788  // one has none.
   6789  PropagateReferenceIfNeeded(mURI, mRedirectURI);
   6790 
   6791  bool rewriteToGET =
   6792      ShouldRewriteRedirectToGET(mRedirectType, mRequestHead.ParsedMethod());
   6793 
   6794  // prompt if the method is not safe (such as POST, PUT, DELETE, ...)
   6795  if (!rewriteToGET && !mRequestHead.IsSafeMethod()) {
   6796    rv = PromptTempRedirect();
   6797    if (NS_FAILED(rv)) return rv;
   6798  }
   6799 
   6800  uint32_t redirectFlags;
   6801  if (nsHttp::IsPermanentRedirect(mRedirectType)) {
   6802    redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
   6803  } else {
   6804    redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
   6805  }
   6806 
   6807  nsCOMPtr<nsIIOService> ioService;
   6808  rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
   6809  if (NS_FAILED(rv)) return rv;
   6810 
   6811  nsCOMPtr<nsIChannel> newChannel;
   6812  nsCOMPtr<nsILoadInfo> redirectLoadInfo =
   6813      CloneLoadInfoForRedirect(mRedirectURI, redirectFlags);
   6814 
   6815  // Propagate the unstripped redirect URI.
   6816  redirectLoadInfo->SetUnstrippedURI(mUnstrippedRedirectURI);
   6817 
   6818  rv = NS_NewChannelInternal(getter_AddRefs(newChannel), mRedirectURI,
   6819                             redirectLoadInfo,
   6820                             nullptr,  // PerformanceStorage
   6821                             nullptr,  // aLoadGroup
   6822                             nullptr,  // aCallbacks
   6823                             nsIRequest::LOAD_NORMAL, ioService);
   6824  // If this fails, it usually means that the URI was invalid. Treat this as if
   6825  // it were a CreateNewURI failure.
   6826  if (NS_WARN_IF(NS_FAILED(rv))) {
   6827    return NS_ERROR_CORRUPTED_CONTENT;
   6828  }
   6829 
   6830  rv = SetupReplacementChannel(mRedirectURI, newChannel, !rewriteToGET,
   6831                               redirectFlags);
   6832  if (NS_FAILED(rv)) return rv;
   6833 
   6834  // verify that this is a legal redirect
   6835  mRedirectChannel = newChannel;
   6836 
   6837  PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
   6838  rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags);
   6839 
   6840  if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback();
   6841 
   6842  if (NS_FAILED(rv)) {
   6843    AutoRedirectVetoNotifier notifier(this, rv);
   6844    PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection);
   6845  }
   6846 
   6847  return rv;
   6848 }
   6849 
   6850 nsresult nsHttpChannel::ContinueProcessRedirection(nsresult rv) {
   6851  AutoRedirectVetoNotifier notifier(this, rv);
   6852 
   6853  LOG(("nsHttpChannel::ContinueProcessRedirection [rv=%" PRIx32 ",this=%p]\n",
   6854       static_cast<uint32_t>(rv), this));
   6855  if (NS_FAILED(rv)) return rv;
   6856 
   6857  MOZ_ASSERT(mRedirectChannel, "No redirect channel?");
   6858 
   6859  // Make sure to do this after we received redirect veto answer,
   6860  // i.e. after all sinks had been notified
   6861  mRedirectChannel->SetOriginalURI(mOriginalURI);
   6862 
   6863  // XXX we used to talk directly with the script security manager, but that
   6864  // should really be handled by the event sink implementation.
   6865 
   6866  // begin loading the new channel
   6867  rv = mRedirectChannel->AsyncOpen(mListener);
   6868  LOG(("  new channel AsyncOpen returned %" PRIX32, static_cast<uint32_t>(rv)));
   6869  NS_ENSURE_SUCCESS(rv, rv);
   6870 
   6871  // close down this channel
   6872  Cancel(NS_BINDING_REDIRECTED);
   6873 
   6874  notifier.RedirectSucceeded();
   6875 
   6876  ReleaseListeners();
   6877 
   6878  return NS_OK;
   6879 }
   6880 
   6881 //-----------------------------------------------------------------------------
   6882 // nsHttpChannel <auth>
   6883 //-----------------------------------------------------------------------------
   6884 
   6885 NS_IMETHODIMP nsHttpChannel::OnAuthAvailable() {
   6886  LOG(("nsHttpChannel::OnAuthAvailable [this=%p]", this));
   6887 
   6888  // setting mAuthRetryPending flag and resuming the transaction
   6889  // triggers process of throwing away the unauthenticated data already
   6890  // coming from the network
   6891  mIsAuthChannel = true;
   6892  mAuthRetryPending = true;
   6893  StoreProxyAuthPending(false);
   6894  LOG(("Resuming the transaction, we got credentials from user"));
   6895  if (mTransactionPump) {
   6896    Resume();
   6897  }
   6898 
   6899  if (StaticPrefs::network_auth_use_redirect_for_retries()) {
   6900    return CallOrWaitForResume(
   6901        [](auto* self) { return self->RedirectToNewChannelForAuthRetry(); });
   6902  }
   6903 
   6904  return NS_OK;
   6905 }
   6906 
   6907 NS_IMETHODIMP nsHttpChannel::OnAuthCancelled(bool userCancel) {
   6908  LOG(("nsHttpChannel::OnAuthCancelled [this=%p]", this));
   6909  MOZ_ASSERT(mAuthRetryPending, "OnAuthCancelled should not be called twice");
   6910 
   6911  if (mTransactionPump) {
   6912    // If the channel is trying to authenticate to a proxy and
   6913    // that was canceled we cannot show the http response body
   6914    // from the 40x as that might mislead the user into thinking
   6915    // it was a end host response instead of a proxy reponse.
   6916    // This must check explicitly whether a proxy auth was being done
   6917    // because we do want to show the content if this is an error from
   6918    // the origin server.
   6919    if (LoadProxyAuthPending()) Cancel(NS_ERROR_PROXY_CONNECTION_REFUSED);
   6920 
   6921    // Make sure to process security headers before calling CallOnStartRequest.
   6922    nsresult rv = ProcessCrossOriginSecurityHeaders();
   6923    if (NS_FAILED(rv)) {
   6924      mStatus = rv;
   6925      HandleAsyncAbort();
   6926      return rv;
   6927    }
   6928 
   6929    // ensure call of OnStartRequest of the current listener here,
   6930    // it would not be called otherwise at all
   6931    rv = CallOnStartRequest();
   6932 
   6933    // drop mAuthRetryPending flag and resume the transaction
   6934    // this resumes load of the unauthenticated content data (which
   6935    // may have been canceled if we don't want to show it)
   6936    mAuthRetryPending = false;
   6937    LOG(("Resuming the transaction, user cancelled the auth dialog"));
   6938    Resume();
   6939 
   6940    if (NS_FAILED(rv)) mTransactionPump->Cancel(rv);
   6941  }
   6942 
   6943  StoreProxyAuthPending(false);
   6944  return NS_OK;
   6945 }
   6946 
   6947 NS_IMETHODIMP nsHttpChannel::CloseStickyConnection() {
   6948  LOG(("nsHttpChannel::CloseStickyConnection this=%p", this));
   6949 
   6950  // Require we are between OnStartRequest and OnStopRequest, because
   6951  // what we do here takes effect in OnStopRequest (not reusing the
   6952  // connection for next authentication round).
   6953  if (!LoadIsPending()) {
   6954    LOG(("  channel not pending"));
   6955    NS_ERROR(
   6956        "CloseStickyConnection not called before OnStopRequest, won't have any "
   6957        "effect");
   6958    return NS_ERROR_UNEXPECTED;
   6959  }
   6960 
   6961  MOZ_ASSERT(mTransaction);
   6962  if (!mTransaction) {
   6963    return NS_ERROR_UNEXPECTED;
   6964  }
   6965 
   6966  if (!(mCaps & NS_HTTP_STICKY_CONNECTION ||
   6967        mTransaction->HasStickyConnection())) {
   6968    LOG(("  not sticky"));
   6969    return NS_OK;
   6970  }
   6971 
   6972  mTransaction->DontReuseConnection();
   6973  return NS_OK;
   6974 }
   6975 
   6976 NS_IMETHODIMP nsHttpChannel::ConnectionRestartable(bool aRestartable) {
   6977  LOG(("nsHttpChannel::ConnectionRestartable this=%p, restartable=%d", this,
   6978       aRestartable));
   6979  StoreAuthConnectionRestartable(aRestartable);
   6980  return NS_OK;
   6981 }
   6982 
   6983 //-----------------------------------------------------------------------------
   6984 // nsHttpChannel::nsISupports
   6985 //-----------------------------------------------------------------------------
   6986 
   6987 NS_IMPL_ADDREF_INHERITED(nsHttpChannel, HttpBaseChannel)
   6988 NS_IMPL_RELEASE_INHERITED(nsHttpChannel, HttpBaseChannel)
   6989 
   6990 NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
   6991  NS_INTERFACE_MAP_ENTRY(nsIRequest)
   6992  NS_INTERFACE_MAP_ENTRY(nsIChannel)
   6993  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   6994  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   6995  NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
   6996  NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel)
   6997  NS_INTERFACE_MAP_ENTRY(nsICachingChannel)
   6998  NS_INTERFACE_MAP_ENTRY(nsIClassOfService)
   6999  NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
   7000  NS_INTERFACE_MAP_ENTRY(nsIFormPOSTActionChannel)
   7001  NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
   7002  NS_INTERFACE_MAP_ENTRY(nsICacheEntryOpenCallback)
   7003  NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
   7004  NS_INTERFACE_MAP_ENTRY(nsIResumableChannel)
   7005  NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
   7006  NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
   7007  NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
   7008  NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
   7009  NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
   7010  NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
   7011  NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
   7012  NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
   7013  NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
   7014  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   7015  NS_INTERFACE_MAP_ENTRY(nsICorsPreflightCallback)
   7016  NS_INTERFACE_MAP_ENTRY(nsIRaceCacheWithNetwork)
   7017  NS_INTERFACE_MAP_ENTRY(nsIRequestTailUnblockCallback)
   7018  NS_INTERFACE_MAP_ENTRY_CONCRETE(nsHttpChannel)
   7019  NS_INTERFACE_MAP_ENTRY(nsIEarlyHintObserver)
   7020 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
   7021 
   7022 //-----------------------------------------------------------------------------
   7023 // nsHttpChannel::nsIRequest
   7024 //-----------------------------------------------------------------------------
   7025 
   7026 NS_IMETHODIMP nsHttpChannel::SetCanceledReason(const nsACString& aReason) {
   7027  return SetCanceledReasonImpl(aReason);
   7028 }
   7029 
   7030 NS_IMETHODIMP nsHttpChannel::GetCanceledReason(nsACString& aReason) {
   7031  return GetCanceledReasonImpl(aReason);
   7032 }
   7033 
   7034 NS_IMETHODIMP
   7035 nsHttpChannel::CancelWithReason(nsresult aStatus, const nsACString& aReason) {
   7036  return CancelWithReasonImpl(aStatus, aReason);
   7037 }
   7038 
   7039 NS_IMETHODIMP
   7040 nsHttpChannel::Cancel(nsresult status) {
   7041  MOZ_ASSERT(NS_IsMainThread());
   7042  // We should never have a pump open while a CORS preflight is in progress.
   7043  MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
   7044 #ifdef DEBUG
   7045  // We want to perform this check only when the chanel is being cancelled the
   7046  // first time with a URL classifier blocking error code.  If mStatus is
   7047  // already set to such an error code then Cancel() may be called for some
   7048  // other reason, for example because we've received notification about our
   7049  // parent process side channel being canceled, in which case we cannot expect
   7050  // that CancelByURLClassifier() would have handled this case.
   7051  if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status) &&
   7052      !UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(mStatus)) {
   7053    MOZ_CRASH_UNSAFE_PRINTF("Blocking classifier error %" PRIx32
   7054                            " need to be handled by CancelByURLClassifier()",
   7055                            static_cast<uint32_t>(status));
   7056  }
   7057 #endif
   7058 
   7059  LOG(("nsHttpChannel::Cancel [this=%p status=%" PRIx32 ", reason=%s]\n", this,
   7060       static_cast<uint32_t>(status), mCanceledReason.get()));
   7061  PROFILER_MARKER("nsHttpChannel::Cancel", NETWORK, {}, FlowMarker,
   7062                  Flow::FromPointer(this));
   7063 
   7064  MOZ_ASSERT_IF(!(mConnectionInfo && mConnectionInfo->UsingConnect()) &&
   7065                    NS_SUCCEEDED(mStatus),
   7066                !AllowedErrorForTransactionRetry(status));
   7067 
   7068  mEarlyHintObserver = nullptr;
   7069  mWebTransportSessionEventListener = nullptr;
   7070 
   7071  if (mCanceled) {
   7072    LOG(("  ignoring; already canceled\n"));
   7073    return NS_OK;
   7074  }
   7075 
   7076  LogCallingScriptLocation(this);
   7077 
   7078  if (LoadWaitingForRedirectCallback()) {
   7079    LOG(("channel canceled during wait for redirect callback"));
   7080  }
   7081 
   7082  return CancelInternal(status);
   7083 }
   7084 
   7085 NS_IMETHODIMP
   7086 nsHttpChannel::CancelByURLClassifier(nsresult aErrorCode) {
   7087  MOZ_ASSERT(
   7088      UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
   7089  MOZ_ASSERT(NS_IsMainThread());
   7090  // We should never have a pump open while a CORS preflight is in progress.
   7091  MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
   7092 
   7093  LOG(("nsHttpChannel::CancelByURLClassifier [this=%p]\n", this));
   7094 
   7095  if (mCanceled) {
   7096    LOG(("  ignoring; already canceled\n"));
   7097    return NS_OK;
   7098  }
   7099 
   7100  // We are being canceled by the channel classifier because of tracking
   7101  // protection, but we haven't yet had a chance to dispatch the
   7102  // "http-on-modify-request" notifications yet (this would normally be
   7103  // done in PrepareToConnect()).  So do that now, before proceeding to
   7104  // cancel.
   7105  //
   7106  // Note that running these observers can itself result in the channel
   7107  // being canceled.  In that case, we accept that cancelation code as
   7108  // the cause of the cancelation, as if the classification of the channel
   7109  // would have occurred past this point!
   7110 
   7111  // notify "http-on-modify-request" observers
   7112  CallOnModifyRequestObservers();
   7113 
   7114  // Check if request was cancelled during on-modify-request
   7115  if (mCanceled) {
   7116    return mStatus;
   7117  }
   7118 
   7119  if (mSuspendCount) {
   7120    LOG(("Waiting until resume in Cancel [this=%p]\n", this));
   7121    MOZ_ASSERT(!mCallOnResume);
   7122    StoreChannelClassifierCancellationPending(1);
   7123    mCallOnResume = [aErrorCode](nsHttpChannel* self) {
   7124      self->HandleContinueCancellingByURLClassifier(aErrorCode);
   7125      return NS_OK;
   7126    };
   7127    return NS_OK;
   7128  }
   7129 
   7130  // Check to see if we should redirect this channel elsewhere by
   7131  // nsIHttpChannel.redirectTo API request
   7132  if (mAPIRedirectTo) {
   7133    StoreChannelClassifierCancellationPending(1);
   7134    return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
   7135  }
   7136 
   7137  return CancelInternal(aErrorCode);
   7138 }
   7139 
   7140 void nsHttpChannel::ContinueCancellingByURLClassifier(nsresult aErrorCode) {
   7141  MOZ_ASSERT(
   7142      UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
   7143  MOZ_ASSERT(NS_IsMainThread());
   7144  // We should never have a pump open while a CORS preflight is in progress.
   7145  MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
   7146 
   7147  LOG(("nsHttpChannel::ContinueCancellingByURLClassifier [this=%p]\n", this));
   7148  if (mCanceled) {
   7149    LOG(("  ignoring; already canceled\n"));
   7150    return;
   7151  }
   7152 
   7153  // Check to see if we should redirect this channel elsewhere by
   7154  // nsIHttpChannel.redirectTo API request
   7155  if (mAPIRedirectTo) {
   7156    (void)AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
   7157    return;
   7158  }
   7159 
   7160  (void)CancelInternal(aErrorCode);
   7161 }
   7162 
   7163 nsresult nsHttpChannel::CancelInternal(nsresult status) {
   7164  LOG(("nsHttpChannel::CancelInternal [this=%p]\n", this));
   7165  bool channelClassifierCancellationPending =
   7166      !!LoadChannelClassifierCancellationPending();
   7167  if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) {
   7168    StoreChannelClassifierCancellationPending(0);
   7169  }
   7170 
   7171  mEarlyHintObserver = nullptr;
   7172  mWebTransportSessionEventListener = nullptr;
   7173  mCanceled = true;
   7174  mStatus = NS_FAILED(status) ? status : NS_ERROR_ABORT;
   7175 
   7176  // If we're waiting for LNA permission result and the channel is being
   7177  // cancelled, we need to call OnPermissionPromptResult with permission denied
   7178  // to resume the channel properly
   7179  if (mWaitingForLNAPermission) {
   7180    LOG(
   7181        ("nsHttpChannel::CancelInternal [this=%p] cancelling while waiting for "
   7182         "LNA permission, denying permission",
   7183         this));
   7184    // Determine permission key dynamically from transaction
   7185    const nsACString& permissionKey =
   7186        (mTransaction && mTransaction->GetTargetIPAddressSpace() ==
   7187                             nsILoadInfo::IPAddressSpace::Local)
   7188            ? LOCAL_HOST_PERMISSION_KEY
   7189            : LOCAL_NETWORK_PERMISSION_KEY;
   7190    OnPermissionPromptResult(false, permissionKey);
   7191    return NS_OK;
   7192  }
   7193 
   7194  if (LoadUsedNetwork() && !mReportedNEL) {
   7195    MaybeGenerateNELReport();
   7196  }
   7197 
   7198  // We don't want the content process to see any header values
   7199  // when the request is blocked by ORB
   7200  if (mChannelBlockedByOpaqueResponse && mCachedOpaqueResponseBlockingPref) {
   7201    mResponseHead->ClearHeaders();
   7202  }
   7203 
   7204  if (mLastStatusReported && !mEndMarkerAdded &&
   7205      profiler_thread_is_being_profiled_for_markers()) {
   7206    // These do allocations/frees/etc; avoid if not active
   7207    // mLastStatusReported can be null if Cancel is called before we added the
   7208    // start marker.
   7209    mEndMarkerAdded = true;
   7210 
   7211    nsAutoCString requestMethod;
   7212    GetRequestMethod(requestMethod);
   7213 
   7214    int32_t priority = PRIORITY_NORMAL;
   7215    GetPriority(&priority);
   7216 
   7217    uint64_t size = 0;
   7218    GetEncodedBodySize(&size);
   7219 
   7220    profiler_add_network_marker(
   7221        mURI, requestMethod, priority, mChannelId, NetworkLoadType::LOAD_CANCEL,
   7222        mLastStatusReported, TimeStamp::Now(), size, mCacheDisposition,
   7223        mLoadInfo->GetInnerWindowID(),
   7224        mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus,
   7225        &mTransactionTimings, std::move(mSource));
   7226  }
   7227 
   7228  // If we don't have mTransactionPump and mCachePump, we need to call
   7229  // AsyncAbort to make sure this channel's listener got notified.
   7230  bool needAsyncAbort = !mTransactionPump && !mCachePump;
   7231 
   7232  if (mProxyRequest) mProxyRequest->Cancel(status);
   7233  CancelNetworkRequest(status);
   7234  mCacheInputStream.CloseAndRelease();
   7235  if (mCachePump) mCachePump->Cancel(status);
   7236  if (mAuthProvider) mAuthProvider->Cancel(status);
   7237  if (mPreflightChannel) mPreflightChannel->Cancel(status);
   7238  if (mRequestContext && mOnTailUnblock) {
   7239    mOnTailUnblock = nullptr;
   7240    mRequestContext->CancelTailedRequest(this);
   7241    CloseCacheEntry(false);
   7242    needAsyncAbort = false;
   7243    (void)AsyncAbort(status);
   7244  } else if (channelClassifierCancellationPending) {
   7245    // If mCallOnResume is not null here, it's set in
   7246    // nsHttpChannel::CancelByURLClassifier. We can override mCallOnResume since
   7247    // mCanceled is true and nsHttpChannel::ContinueCancellingByURLClassifier
   7248    // does nothing.
   7249    if (mCallOnResume) {
   7250      mCallOnResume = nullptr;
   7251    }
   7252    // If we're coming from an asynchronous path when canceling a channel due
   7253    // to safe-browsing protection, we need to AsyncAbort the channel now.
   7254    needAsyncAbort = false;
   7255    (void)AsyncAbort(status);
   7256  }
   7257 
   7258  // If we already have mCallOnResume, AsyncAbort will be called in
   7259  // ResumeInternal.
   7260  if (needAsyncAbort && !mCallOnResume && !mSuspendCount) {
   7261    LOG(("nsHttpChannel::CancelInternal do AsyncAbort [this=%p]\n", this));
   7262    CloseCacheEntry(false);
   7263    (void)AsyncAbort(status);
   7264  }
   7265  return NS_OK;
   7266 }
   7267 
   7268 void nsHttpChannel::CancelNetworkRequest(nsresult aStatus) {
   7269  if (mTransaction) {
   7270    nsresult rv = gHttpHandler->CancelTransaction(mTransaction, aStatus);
   7271    if (NS_FAILED(rv)) {
   7272      LOG(("failed to cancel the transaction\n"));
   7273    }
   7274  }
   7275  if (mTransactionPump) mTransactionPump->Cancel(aStatus);
   7276 
   7277  mEarlyHintObserver = nullptr;
   7278  mWebTransportSessionEventListener = nullptr;
   7279 }
   7280 
   7281 NS_IMETHODIMP
   7282 nsHttpChannel::Suspend() {
   7283  MOZ_ASSERT(NS_IsMainThread());
   7284  NS_ENSURE_TRUE(LoadIsPending(), NS_ERROR_NOT_AVAILABLE);
   7285 
   7286  PROFILER_MARKER("nsHttpChannel::Suspend", NETWORK, {}, FlowMarker,
   7287                  Flow::FromPointer(this));
   7288  LOG(("nsHttpChannel::SuspendInternal [this=%p]\n", this));
   7289  LogCallingScriptLocation(this);
   7290 
   7291  ++mSuspendCount;
   7292 
   7293  if (mSuspendCount == 1) {
   7294    mSuspendTimestamp = TimeStamp::NowLoRes();
   7295 
   7296    // Start timer to detect if we're suspended too long
   7297    // This helps unblock waiting cache listeners when a channel is suspended
   7298    // for an unreasonably long time.
   7299    uint32_t delay = StaticPrefs::network_cache_suspended_writer_delay_ms();
   7300    if (!mSuspendTimer && delay) {
   7301      mSuspendTimer = NS_NewTimer();
   7302    }
   7303 
   7304    if (mSuspendTimer && delay) {
   7305      RefPtr<TimerCallback> timerCallback = new TimerCallback(this);
   7306      mSuspendTimer->InitWithCallback(timerCallback, delay,
   7307                                      nsITimer::TYPE_ONE_SHOT);
   7308      LOG(("  started suspend timer, will fire in %dms", delay));
   7309    }
   7310  }
   7311 
   7312  nsresult rvTransaction = NS_OK;
   7313  if (mTransactionPump) {
   7314    rvTransaction = mTransactionPump->Suspend();
   7315  }
   7316  nsresult rvCache = NS_OK;
   7317  if (mCachePump) {
   7318    rvCache = mCachePump->Suspend();
   7319  }
   7320 
   7321  return NS_FAILED(rvTransaction) ? rvTransaction : rvCache;
   7322 }
   7323 
   7324 // static
   7325 void nsHttpChannel::StaticSuspend(nsHttpChannel* aChan) { aChan->Suspend(); }
   7326 
   7327 NS_IMETHODIMP
   7328 nsHttpChannel::Resume() {
   7329  MOZ_ASSERT(NS_IsMainThread());
   7330  NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
   7331 
   7332  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::Resume", NETWORK,
   7333                            Flow::FromPointer(this));
   7334  LOG(("nsHttpChannel::ResumeInternal [this=%p]\n", this));
   7335  LogCallingScriptLocation(this);
   7336 
   7337  if (--mSuspendCount == 0) {
   7338    mSuspendTotalTime += TimeStamp::NowLoRes() - mSuspendTimestamp;
   7339 
   7340    // Cancel suspend timer since we're resuming
   7341    if (mSuspendTimer) {
   7342      mSuspendTimer->Cancel();
   7343      LOG(("  cancelled suspend timer"));
   7344    }
   7345 
   7346    // Reset bypass flag since the writer is resuming
   7347    if (mBypassCacheWriterSet && mCacheEntry) {
   7348      mCacheEntry->SetBypassWriterLock(false);
   7349      mBypassCacheWriterSet = false;
   7350      LOG(("  reset bypass writer lock flag"));
   7351    }
   7352 
   7353    if (mCallOnResume) {
   7354      // Resume the interrupted procedure first, then resume
   7355      // the pump to continue process the input stream.
   7356      // Any newly created pump MUST be suspended to prevent calling
   7357      // its OnStartRequest before OnStopRequest of any pre-existing
   7358      // pump.  AsyncResumePending ensures that.
   7359      MOZ_ASSERT(!LoadAsyncResumePending());
   7360      StoreAsyncResumePending(1);
   7361 
   7362      std::function<nsresult(nsHttpChannel*)> callOnResume = nullptr;
   7363      std::swap(callOnResume, mCallOnResume);
   7364 
   7365      RefPtr<nsHttpChannel> self(this);
   7366      nsCOMPtr<nsIRequest> transactionPump = mTransactionPump;
   7367      RefPtr<nsInputStreamPump> cachePump = mCachePump;
   7368 
   7369      nsresult rv = NS_DispatchToCurrentThread(NS_NewRunnableFunction(
   7370          "nsHttpChannel::CallOnResume",
   7371          [callOnResume{std::move(callOnResume)}, self{std::move(self)},
   7372           transactionPump{std::move(transactionPump)},
   7373           cachePump{std::move(cachePump)}]() {
   7374            MOZ_ASSERT(self->LoadAsyncResumePending());
   7375            nsresult rv = self->CallOrWaitForResume(callOnResume);
   7376            if (NS_FAILED(rv)) {
   7377              self->CloseCacheEntry(false);
   7378              (void)self->AsyncAbort(rv);
   7379            }
   7380            MOZ_ASSERT(self->LoadAsyncResumePending());
   7381 
   7382            self->StoreAsyncResumePending(0);
   7383 
   7384            // And now actually resume the previously existing pumps.
   7385            if (transactionPump) {
   7386              LOG(
   7387                  ("nsHttpChannel::CallOnResume resuming previous transaction "
   7388                   "pump %p, this=%p",
   7389                   transactionPump.get(), self.get()));
   7390              transactionPump->Resume();
   7391            }
   7392            if (cachePump) {
   7393              LOG(
   7394                  ("nsHttpChannel::CallOnResume resuming previous cache pump "
   7395                   "%p, this=%p",
   7396                   cachePump.get(), self.get()));
   7397              cachePump->Resume();
   7398            }
   7399 
   7400            // Any newly created pumps were suspended once because of
   7401            // AsyncResumePending. Problem is that the stream listener
   7402            // notification is already pending in the queue right now, because
   7403            // AsyncRead doesn't (regardless if called after Suspend) respect
   7404            // the suspend coutner and the right order would not be preserved.
   7405            // Hence, we do another dispatch round to actually Resume after
   7406            // the notification from the original pump.
   7407            if (transactionPump != self->mTransactionPump &&
   7408                self->mTransactionPump) {
   7409              LOG(
   7410                  ("nsHttpChannel::CallOnResume async-resuming new "
   7411                   "transaction "
   7412                   "pump %p, this=%p",
   7413                   self->mTransactionPump.get(), self.get()));
   7414 
   7415              nsCOMPtr<nsIRequest> pump = self->mTransactionPump;
   7416              NS_DispatchToCurrentThread(NS_NewRunnableFunction(
   7417                  "nsHttpChannel::CallOnResume new transaction",
   7418                  [pump{std::move(pump)}]() { pump->Resume(); }));
   7419            }
   7420            if (cachePump != self->mCachePump && self->mCachePump) {
   7421              LOG(
   7422                  ("nsHttpChannel::CallOnResume async-resuming new cache pump "
   7423                   "%p, this=%p",
   7424                   self->mCachePump.get(), self.get()));
   7425 
   7426              RefPtr<nsInputStreamPump> pump = self->mCachePump;
   7427              NS_DispatchToCurrentThread(NS_NewRunnableFunction(
   7428                  "nsHttpChannel::CallOnResume new pump",
   7429                  [pump{std::move(pump)}]() { pump->Resume(); }));
   7430            }
   7431          }));
   7432      NS_ENSURE_SUCCESS(rv, rv);
   7433      return rv;
   7434    }
   7435  }
   7436 
   7437  nsresult rvTransaction = NS_OK;
   7438  if (mTransactionPump) {
   7439    rvTransaction = mTransactionPump->Resume();
   7440  }
   7441 
   7442  nsresult rvCache = NS_OK;
   7443  if (mCachePump) {
   7444    rvCache = mCachePump->Resume();
   7445  }
   7446 
   7447  return NS_FAILED(rvTransaction) ? rvTransaction : rvCache;
   7448 }
   7449 
   7450 //-----------------------------------------------------------------------------
   7451 // nsHttpChannel::nsIChannel
   7452 //-----------------------------------------------------------------------------
   7453 
   7454 NS_IMETHODIMP
   7455 nsHttpChannel::GetSecurityInfo(nsITransportSecurityInfo** securityInfo) {
   7456  NS_ENSURE_ARG_POINTER(securityInfo);
   7457  *securityInfo = do_AddRef(mSecurityInfo).take();
   7458  return NS_OK;
   7459 }
   7460 
   7461 // If any of the functions that AsyncOpen calls returns immediately an error
   7462 // AsyncAbort(which calls onStart/onStopRequest) does not need to be call.
   7463 // To be sure that they are not call ReleaseListeners() is called.
   7464 // If AsyncOpen returns NS_OK, after that point AsyncAbort must be called on
   7465 // any error.
   7466 NS_IMETHODIMP
   7467 nsHttpChannel::AsyncOpen(nsIStreamListener* aListener) {
   7468  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::AsyncOpen", NETWORK,
   7469                            Flow::FromPointer(this));
   7470  nsCOMPtr<nsIStreamListener> listener = aListener;
   7471  nsresult rv =
   7472      nsContentSecurityManager::doContentSecurityCheck(this, listener);
   7473  if (NS_WARN_IF(NS_FAILED(rv))) {
   7474    ReleaseListeners();
   7475    return rv;
   7476  }
   7477 
   7478  MOZ_ASSERT(
   7479      mLoadInfo->GetSecurityMode() == 0 ||
   7480          mLoadInfo->GetInitialSecurityCheckDone() ||
   7481          (mLoadInfo->GetSecurityMode() ==
   7482               nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
   7483           mLoadInfo->GetLoadingPrincipal() &&
   7484           mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
   7485      "security flags in loadInfo but doContentSecurityCheck() not called");
   7486 
   7487  LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
   7488  mOpenerCallingScriptLocation = CallingScriptLocationString();
   7489  LogCallingScriptLocation(this, mOpenerCallingScriptLocation);
   7490  NS_CompareLoadInfoAndLoadContext(this);
   7491 
   7492 #ifdef DEBUG
   7493  AssertPrivateBrowsingId();
   7494 #endif
   7495 
   7496  NS_ENSURE_ARG_POINTER(listener);
   7497  NS_ENSURE_TRUE(!LoadIsPending(), NS_ERROR_IN_PROGRESS);
   7498  NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_ALREADY_OPENED);
   7499 
   7500  if (mCanceled) {
   7501    ReleaseListeners();
   7502    return NS_FAILED(mStatus) ? mStatus : NS_ERROR_FAILURE;
   7503  }
   7504 
   7505  if (MaybeWaitForUploadStreamNormalization(listener, nullptr)) {
   7506    return NS_OK;
   7507  }
   7508 
   7509  MOZ_ASSERT(NS_IsMainThread());
   7510 
   7511  if (!gHttpHandler->Active()) {
   7512    LOG(("  after HTTP shutdown..."));
   7513    ReleaseListeners();
   7514    return NS_ERROR_NOT_AVAILABLE;
   7515  }
   7516 
   7517  rv = NS_CheckPortSafety(mURI);
   7518  if (NS_FAILED(rv)) {
   7519    ReleaseListeners();
   7520    return rv;
   7521  }
   7522 
   7523  // If no one called SetLoadGroup or SetNotificationCallbacks, the private
   7524  // state has not been updated on PrivateBrowsingChannel (which we derive
   7525  // from) Same if the loadinfo has changed since the creation of the channel.
   7526  // Hence, we have to call UpdatePrivateBrowsing() here
   7527  UpdatePrivateBrowsing();
   7528 
   7529  AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(this);
   7530 
   7531  // Recalculate the default userAgent header after the AntiTrackingInfo gets
   7532  // updated because we can only know whether the site is exempted from
   7533  // fingerprinting protection after we have the AntiTracking Info.
   7534  //
   7535  // Note that we don't recalculate the header if it has been modified since the
   7536  // channel was created because we want to preserve the modified header.
   7537  if (!LoadIsUserAgentHeaderModified()) {
   7538    rv = mRequestHead.SetHeader(
   7539        nsHttp::User_Agent,
   7540        gHttpHandler->UserAgent(nsContentUtils::ShouldResistFingerprinting(
   7541            this, RFPTarget::HttpUserAgent)),
   7542        false, nsHttpHeaderArray::eVarietyRequestEnforceDefault);
   7543    MOZ_ASSERT(NS_SUCCEEDED(rv));
   7544  }
   7545 
   7546  if (WaitingForTailUnblock()) {
   7547    // This channel is marked as Tail and is part of a request context
   7548    // that has positive number of non-tailed requestst, hence this channel
   7549    // has been put to a queue.
   7550    // When tail is unblocked, OnTailUnblock on this channel will be called
   7551    // to continue AsyncOpen.
   7552    mListener = listener;
   7553    MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock);
   7554    mOnTailUnblock = &nsHttpChannel::AsyncOpenOnTailUnblock;
   7555 
   7556    LOG(("  put on hold until tail is unblocked"));
   7557    return NS_OK;
   7558  }
   7559 
   7560  // Remember the cookie header that was set, if any
   7561  nsAutoCString cookieHeader;
   7562  if (NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Cookie, cookieHeader))) {
   7563    // if this is a cache revalidaing channel (mIsStaleRevalidation), then this
   7564    // represents both user cookies and cookies from cookieService
   7565    mUserSetCookieHeader = cookieHeader;
   7566  }
   7567 
   7568  // Set user agent override, do so before OnOpeningRequest notification
   7569  // since we want to allow consumers of that notification change or remove
   7570  // the User-Agent request header.
   7571  HttpBaseChannel::SetDocshellUserAgentOverride();
   7572 
   7573  // After we notify any observers (on-opening-request, loadGroup, etc) we
   7574  // must return NS_OK and return any errors asynchronously via
   7575  // OnStart/OnStopRequest.  Observers may add a reference to the channel
   7576  // and expect to get OnStopRequest so they know when to drop the reference,
   7577  // etc.
   7578 
   7579  // notify "http-on-opening-request" observers, but not if this is a redirect
   7580  if (!(mLoadFlags & LOAD_REPLACE)) {
   7581    gHttpHandler->OnOpeningRequest(this);
   7582  }
   7583 
   7584  StoreIsPending(true);
   7585  StoreWasOpened(true);
   7586 
   7587  mListener = listener;
   7588 
   7589  if (nsIOService::UseSocketProcess() &&
   7590      !gIOService->IsSocketProcessLaunchComplete()) {
   7591    RefPtr<nsHttpChannel> self = this;
   7592    gIOService->CallOrWaitForSocketProcess(
   7593        [self]() { self->AsyncOpenFinal(TimeStamp::Now()); });
   7594    return NS_OK;
   7595  }
   7596 
   7597  AsyncOpenFinal(TimeStamp::Now());
   7598 
   7599  return NS_OK;
   7600 }
   7601 
   7602 void nsHttpChannel::AsyncOpenFinal(TimeStamp aTimeStamp) {
   7603  // We save this timestamp from outside of the if block in case we enable the
   7604  // profiler after AsyncOpen().
   7605  mLastStatusReported = TimeStamp::Now();
   7606  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::AsyncOpenFinal", NETWORK,
   7607                            Flow::FromPointer(this));
   7608  if (profiler_thread_is_being_profiled_for_markers()) {
   7609    nsAutoCString requestMethod;
   7610    GetRequestMethod(requestMethod);
   7611 
   7612    profiler_add_network_marker(
   7613        mURI, requestMethod, mPriority, mChannelId, NetworkLoadType::LOAD_START,
   7614        mChannelCreationTimestamp, mLastStatusReported, 0, mCacheDisposition,
   7615        mLoadInfo->GetInnerWindowID(),
   7616        mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus);
   7617  }
   7618 
   7619  // Added due to PauseTask/DelayHttpChannel
   7620  if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr);
   7621 
   7622  // record asyncopen time unconditionally and clear it if we
   7623  // don't want it after OnModifyRequest() weighs in. But waiting for
   7624  // that to complete would mean we don't include proxy resolution in the
   7625  // timing.
   7626  if (!LoadAsyncOpenTimeOverriden()) {
   7627    mAsyncOpenTime = aTimeStamp;
   7628  }
   7629 
   7630  // Remember we have Authorization header set here.  We need to check on it
   7631  // just once and early, AsyncOpen is the best place.
   7632  StoreCustomAuthHeader(mRequestHead.HasHeader(nsHttp::Authorization));
   7633 
   7634  bool willCallback = false;
   7635  // We are about to do an async lookup to check if the URI is a tracker. If
   7636  // yes, this channel will be canceled by channel classifier.  Chances are the
   7637  // lookup is not needed so CheckIsTrackerWithLocalTable() will return an
   7638  // error and then we can MaybeResolveProxyAndBeginConnect() right away.
   7639  // We skip the check in case this is an internal redirected channel
   7640  if (!LoadAuthRedirectedChannel() &&
   7641      NS_ShouldClassifyChannel(this, ClassifyType::ETP)) {
   7642    RefPtr<nsHttpChannel> self = this;
   7643    willCallback = NS_SUCCEEDED(
   7644        AsyncUrlChannelClassifier::CheckChannel(this, [self]() -> void {
   7645          nsCOMPtr<nsIURI> uri;
   7646          self->GetURI(getter_AddRefs(uri));
   7647          MOZ_ASSERT(uri);
   7648 
   7649          // Finish the AntiTracking Heuristic before
   7650          // MaybeResolveProxyAndBeginConnect().
   7651          FinishAntiTrackingRedirectHeuristic(self, uri);
   7652 
   7653          self->MaybeResolveProxyAndBeginConnect();
   7654        }));
   7655  }
   7656 
   7657  if (!willCallback) {
   7658    // We can do MaybeResolveProxyAndBeginConnect immediately if
   7659    // CheckIsTrackerWithLocalTable is failed. Note that we don't need to
   7660    // handle the failure because BeginConnect() will return synchronously and
   7661    // the caller will be responsible for handling it.
   7662    MaybeResolveProxyAndBeginConnect();
   7663  }
   7664 }
   7665 
   7666 void nsHttpChannel::MaybeResolveProxyAndBeginConnect() {
   7667  nsresult rv;
   7668 
   7669  // The common case for HTTP channels is to begin proxy resolution and return
   7670  // at this point. The only time we know mProxyInfo already is if we're
   7671  // proxying a non-http protocol like ftp. We don't need to discover proxy
   7672  // settings if we are never going to make a network connection.
   7673  if (!mProxyInfo &&
   7674      !(mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_NO_NETWORK_IO)) &&
   7675      !BypassProxy() && NS_SUCCEEDED(ResolveProxy())) {
   7676    return;
   7677  }
   7678 
   7679  if (!gHttpHandler->Active()) {
   7680    LOG(
   7681        ("nsHttpChannel::MaybeResolveProxyAndBeginConnect [this=%p] "
   7682         "Handler no longer active.\n",
   7683         this));
   7684    rv = NS_ERROR_NOT_AVAILABLE;
   7685  } else {
   7686    rv = BeginConnect();
   7687  }
   7688  if (NS_FAILED(rv)) {
   7689    CloseCacheEntry(false);
   7690    (void)AsyncAbort(rv);
   7691  }
   7692 }
   7693 
   7694 nsresult nsHttpChannel::AsyncOpenOnTailUnblock() {
   7695  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::AsyncOpenOnTailUnblock", NETWORK,
   7696                            Flow::FromPointer(this));
   7697  return AsyncOpen(mListener);
   7698 }
   7699 
   7700 already_AddRefed<nsChannelClassifier>
   7701 nsHttpChannel::GetOrCreateChannelClassifier() {
   7702  if (!mChannelClassifier) {
   7703    mChannelClassifier = new nsChannelClassifier(this);
   7704    LOG(("nsHttpChannel [%p] created nsChannelClassifier [%p]\n", this,
   7705         mChannelClassifier.get()));
   7706  }
   7707 
   7708  RefPtr<nsChannelClassifier> classifier = mChannelClassifier;
   7709  return classifier.forget();
   7710 }
   7711 
   7712 ProxyDNSStrategy nsHttpChannel::GetProxyDNSStrategy() {
   7713  // When network_dns_force_use_https_rr is true, return DNS_PREFETCH_ORIGIN.
   7714  // This ensures that we always perform HTTPS RR query.
   7715  nsCOMPtr<nsProxyInfo> proxyInfo(static_cast<nsProxyInfo*>(mProxyInfo.get()));
   7716  if (!proxyInfo || StaticPrefs::network_dns_force_use_https_rr()) {
   7717    return ProxyDNSStrategy::ORIGIN;
   7718  }
   7719 
   7720  // If the proxy is not to perform name resolution itself.
   7721  return GetProxyDNSStrategyHelper(proxyInfo->Type(), proxyInfo->Flags());
   7722 }
   7723 
   7724 // BeginConnect() SHOULD NOT call AsyncAbort(). AsyncAbort will be called by
   7725 // functions that called BeginConnect if needed. Only
   7726 // MaybeResolveProxyAndBeginConnect and OnProxyAvailable ever call
   7727 // BeginConnect.
   7728 nsresult nsHttpChannel::BeginConnect() {
   7729  LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this));
   7730 
   7731  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::BeginConnect", NETWORK,
   7732                            Flow::FromPointer(this));
   7733  nsresult rv;
   7734 
   7735  // It is the caller's responsibility to not call us late in shutdown.
   7736  MOZ_ASSERT(gHttpHandler->Active());
   7737 
   7738  // Construct connection info object
   7739  nsAutoCString host;
   7740  nsAutoCString scheme;
   7741  int32_t port = -1;
   7742  bool isHttps = mURI->SchemeIs("https");
   7743 
   7744  rv = mURI->GetScheme(scheme);
   7745  if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiHost(host);
   7746  if (NS_SUCCEEDED(rv)) rv = mURI->GetPort(&port);
   7747  if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiSpec(mSpec);
   7748  if (NS_FAILED(rv)) {
   7749    return rv;
   7750  }
   7751 
   7752  // Just a warning here because some nsIURIs do not implement this method.
   7753  (void)NS_WARN_IF(NS_FAILED(mURI->GetUsername(mUsername)));
   7754 
   7755  // Reject the URL if it doesn't specify a host
   7756  if (host.IsEmpty()) {
   7757    rv = NS_ERROR_MALFORMED_URI;
   7758    return rv;
   7759  }
   7760  LOG(("host=%s port=%d\n", host.get(), port));
   7761  LOG(("uri=%s\n", mSpec.get()));
   7762 
   7763  nsCOMPtr<nsProxyInfo> proxyInfo;
   7764  if (mProxyInfo) proxyInfo = do_QueryInterface(mProxyInfo);
   7765 
   7766  if (mCaps & NS_HTTP_CONNECT_ONLY) {
   7767    if (!proxyInfo) {
   7768      LOG(("return failure: no proxy for connect-only channel\n"));
   7769      return NS_ERROR_FAILURE;
   7770    }
   7771 
   7772    if (!proxyInfo->IsHTTP() && !proxyInfo->IsHTTPS()) {
   7773      LOG(("return failure: non-http proxy for connect-only channel\n"));
   7774      return NS_ERROR_FAILURE;
   7775    }
   7776  }
   7777 
   7778  mRequestHead.SetHTTPS(isHttps);
   7779  mRequestHead.SetOrigin(scheme, host, port);
   7780 
   7781  AddStorageAccessHeadersToRequest();
   7782  SetOriginHeader();
   7783  SetDoNotTrack();
   7784  SetGlobalPrivacyControl();
   7785 
   7786  OriginAttributes originAttributes;
   7787  // Regular principal in case we have a proxy.
   7788  if (proxyInfo &&
   7789      !StaticPrefs::privacy_partition_network_state_connection_with_proxy()) {
   7790    StoragePrincipalHelper::GetOriginAttributes(
   7791        this, originAttributes, StoragePrincipalHelper::eRegularPrincipal);
   7792  } else {
   7793    StoragePrincipalHelper::GetOriginAttributesForNetworkState(
   7794        this, originAttributes);
   7795  }
   7796 
   7797  // Adjust mCaps according to our request headers:
   7798  //  - If "Connection: close" is set as a request header, then do not bother
   7799  //    trying to establish a keep-alive connection.
   7800  if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close")) {
   7801    mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE);
   7802    StoreAllowHttp3(false);
   7803  }
   7804 
   7805  gHttpHandler->MaybeAddAltSvcForTesting(mURI, mUsername, mPrivateBrowsing,
   7806                                         mCallbacks, originAttributes);
   7807 
   7808  RefPtr<nsHttpConnectionInfo> connInfo;
   7809 #ifdef FUZZING
   7810  if (StaticPrefs::fuzzing_necko_http3()) {
   7811    connInfo =
   7812        new nsHttpConnectionInfo(host, port, "h3"_ns, mUsername, proxyInfo,
   7813                                 originAttributes, host, port, true);
   7814  } else {
   7815 #endif
   7816    if (mWebTransportSessionEventListener) {
   7817      connInfo =
   7818          new nsHttpConnectionInfo(host, port, "h3"_ns, mUsername, proxyInfo,
   7819                                   originAttributes, isHttps, true, true);
   7820      bool dedicated = true;
   7821      nsresult rv;
   7822      nsCOMPtr<WebTransportConnectionSettings> wtconSettings =
   7823          do_QueryInterface(mWebTransportSessionEventListener, &rv);
   7824      NS_ENSURE_SUCCESS(rv, rv);
   7825      nsIWebTransport::HTTPVersion httpVersion;
   7826      (void)wtconSettings->GetHttpVersion(&httpVersion);
   7827      if (httpVersion == nsIWebTransport::HTTPVersion::h2) {
   7828        connInfo =
   7829            new nsHttpConnectionInfo(host, port, "h2"_ns, mUsername, proxyInfo,
   7830                                     originAttributes, isHttps, false, true);
   7831      } else {
   7832        connInfo =
   7833            new nsHttpConnectionInfo(host, port, "h3"_ns, mUsername, proxyInfo,
   7834                                     originAttributes, isHttps, true, true);
   7835      }
   7836      wtconSettings->GetDedicated(&dedicated);
   7837      if (dedicated) {
   7838        connInfo->SetWebTransportId(
   7839            nsHttpConnectionInfo::GenerateNewWebTransportId());
   7840      }
   7841    } else {
   7842      connInfo = new nsHttpConnectionInfo(host, port, ""_ns, mUsername,
   7843                                          proxyInfo, originAttributes, isHttps);
   7844    }
   7845 #ifdef FUZZING
   7846  }
   7847 #endif
   7848 
   7849  bool http2Allowed = !gHttpHandler->IsHttp2Excluded(connInfo);
   7850 
   7851  bool http3Allowed = Http3Allowed();
   7852  if (!http3Allowed) {
   7853    mCaps |= NS_HTTP_DISALLOW_HTTP3;
   7854  }
   7855 
   7856  RefPtr<AltSvcMapping> mapping;
   7857  if (!mConnectionInfo && LoadAllowAltSvc() &&  // per channel
   7858      !mWebTransportSessionEventListener && (http2Allowed || http3Allowed) &&
   7859      !(mLoadFlags & LOAD_FRESH_CONNECTION) &&
   7860      AltSvcMapping::AcceptableProxy(proxyInfo) &&
   7861      (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) &&
   7862      (mapping = gHttpHandler->GetAltServiceMapping(
   7863           scheme, host, port, mPrivateBrowsing, originAttributes, http2Allowed,
   7864           http3Allowed))) {
   7865    LOG(("nsHttpChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n", this,
   7866         scheme.get(), mapping->AlternateHost().get(), mapping->AlternatePort(),
   7867         mapping->HashKey().get()));
   7868 
   7869    if (!(mLoadFlags & LOAD_ANONYMOUS) && !mPrivateBrowsing) {
   7870      nsAutoCString altUsedLine(mapping->AlternateHost());
   7871      bool defaultPort =
   7872          mapping->AlternatePort() ==
   7873          (isHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT);
   7874      if (!defaultPort) {
   7875        altUsedLine.AppendLiteral(":");
   7876        altUsedLine.AppendInt(mapping->AlternatePort());
   7877      }
   7878      // Like what we did for 'Authorization' header, we need to do the same for
   7879      // 'Alt-Used' for avoiding this header being shown in the ServiceWorker
   7880      // FetchEvent.
   7881      (void)mRequestHead.ClearHeader(nsHttp::Alternate_Service_Used);
   7882      rv = mRequestHead.SetHeader(nsHttp::Alternate_Service_Used, altUsedLine,
   7883                                  false,
   7884                                  nsHttpHeaderArray::eVarietyRequestDefault);
   7885      MOZ_ASSERT(NS_SUCCEEDED(rv));
   7886    }
   7887 
   7888    nsCOMPtr<nsIConsoleService> consoleService;
   7889    consoleService = mozilla::components::Console::Service();
   7890    if (consoleService && !host.Equals(mapping->AlternateHost())) {
   7891      nsAutoString message(u"Alternate Service Mapping found: "_ns);
   7892      AppendASCIItoUTF16(scheme, message);
   7893      message.AppendLiteral(u"://");
   7894      AppendASCIItoUTF16(host, message);
   7895      message.AppendLiteral(u":");
   7896      message.AppendInt(port);
   7897      message.AppendLiteral(u" to ");
   7898      AppendASCIItoUTF16(scheme, message);
   7899      message.AppendLiteral(u"://");
   7900      AppendASCIItoUTF16(mapping->AlternateHost(), message);
   7901      message.AppendLiteral(u":");
   7902      message.AppendInt(mapping->AlternatePort());
   7903      consoleService->LogStringMessage(message.get());
   7904    }
   7905 
   7906    LOG(("nsHttpChannel %p Using connection info from altsvc mapping", this));
   7907    mapping->GetConnectionInfo(getter_AddRefs(mConnectionInfo), proxyInfo,
   7908                               originAttributes);
   7909    glean::http::transaction_use_altsvc
   7910        .EnumGet(glean::http::TransactionUseAltsvcLabel::eTrue)
   7911        .Add();
   7912    if (mConnectionInfo->IsHttp3() &&
   7913        StaticPrefs::
   7914            network_http_http3_force_use_alt_svc_mapping_for_testing()) {
   7915      mCaps |= NS_HTTP_DISALLOW_SPDY;
   7916    }
   7917  } else if (mConnectionInfo) {
   7918    LOG(("nsHttpChannel %p Using channel supplied connection info", this));
   7919    glean::http::transaction_use_altsvc
   7920        .EnumGet(glean::http::TransactionUseAltsvcLabel::eFalse)
   7921        .Add();
   7922  } else {
   7923    LOG(("nsHttpChannel %p Using default connection info", this));
   7924 
   7925    mConnectionInfo = connInfo;
   7926    glean::http::transaction_use_altsvc
   7927        .EnumGet(glean::http::TransactionUseAltsvcLabel::eFalse)
   7928        .Add();
   7929  }
   7930 
   7931  bool trrEnabled = false;
   7932  auto dnsStrategy = GetProxyDNSStrategy();
   7933  bool httpsRRAllowed =
   7934      !LoadBeConservative() && !(mCaps & NS_HTTP_BE_CONSERVATIVE) &&
   7935      !(mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal() &&
   7936        mLoadInfo->GetExternalContentPolicyType() !=
   7937            ExtContentPolicy::TYPE_DOCUMENT) &&
   7938      dnsStrategy == ProxyDNSStrategy::ORIGIN &&
   7939      !mConnectionInfo->UsingConnect() && canUseHTTPSRRonNetwork(trrEnabled) &&
   7940      StaticPrefs::network_dns_use_https_rr_as_altsvc();
   7941  if (!httpsRRAllowed) {
   7942    DisallowHTTPSRR(mCaps);
   7943  } else if (trrEnabled) {
   7944    if (nsIRequest::GetTRRMode() != nsIRequest::TRR_DISABLED_MODE) {
   7945      mCaps |= NS_HTTP_FORCE_WAIT_HTTP_RR;
   7946    }
   7947  }
   7948  // No need to lookup HTTPSSVC record if mHTTPSSVCRecord already contains a
   7949  // value.
   7950  StoreUseHTTPSSVC(StaticPrefs::network_dns_upgrade_with_https_rr() &&
   7951                   httpsRRAllowed && mHTTPSSVCRecord.isNothing());
   7952 
   7953  // Need to re-ask the handler, since mConnectionInfo may not be the connInfo
   7954  // we used earlier
   7955  if (!mConnectionInfo->IsHttp3() &&
   7956      gHttpHandler->IsHttp2Excluded(mConnectionInfo)) {
   7957    StoreAllowSpdy(0);
   7958    mCaps |= NS_HTTP_DISALLOW_SPDY;
   7959    mConnectionInfo->SetNoSpdy(true);
   7960  }
   7961 
   7962  // We can be passed with the auth provider if this channel was
   7963  // a result of redirect due to auth retry
   7964  if (!mAuthProvider) {
   7965    mAuthProvider = new nsHttpChannelAuthProvider();
   7966  }
   7967 
   7968  rv = mAuthProvider->Init(this);
   7969  if (NS_FAILED(rv)) {
   7970    return rv;
   7971  }
   7972 
   7973  // check to see if authorization headers should be included
   7974  // CustomAuthHeader is set in AsyncOpen if we find Authorization header
   7975  rv = mAuthProvider->AddAuthorizationHeaders(LoadCustomAuthHeader());
   7976  if (NS_FAILED(rv)) {
   7977    LOG(("nsHttpChannel %p AddAuthorizationHeaders failed (%08x)", this,
   7978         static_cast<uint32_t>(rv)));
   7979  }
   7980 
   7981  // if this somehow fails we can go on without it
   7982  (void)gHttpHandler->AddConnectionHeader(&mRequestHead, mCaps);
   7983 
   7984  if (!LoadIsTRRServiceChannel() &&
   7985      ((mLoadFlags & LOAD_FRESH_CONNECTION) ||
   7986       (!StaticPrefs::network_dns_only_refresh_on_fresh_connection() &&
   7987        (mLoadFlags & VALIDATE_ALWAYS ||
   7988         BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass()))))) {
   7989    mCaps |= NS_HTTP_REFRESH_DNS;
   7990  }
   7991 
   7992  if (gHttpHandler->CriticalRequestPrioritization()) {
   7993    if (mClassOfService.Flags() & nsIClassOfService::Leader) {
   7994      mCaps |= NS_HTTP_LOAD_AS_BLOCKING;
   7995    }
   7996    if (mClassOfService.Flags() & nsIClassOfService::Unblocked) {
   7997      mCaps |= NS_HTTP_LOAD_UNBLOCKED;
   7998    }
   7999    if (mClassOfService.Flags() & nsIClassOfService::UrgentStart &&
   8000        gHttpHandler->IsUrgentStartEnabled()) {
   8001      mCaps |= NS_HTTP_URGENT_START;
   8002      SetPriority(nsISupportsPriority::PRIORITY_HIGHEST);
   8003    }
   8004  }
   8005 
   8006  // Force-Reload should reset the persistent connection pool for this host
   8007  if (mLoadFlags & LOAD_FRESH_CONNECTION) {
   8008    // just the initial document resets the whole pool
   8009    if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
   8010      gHttpHandler->AltServiceCache()->ClearAltServiceMappings();
   8011      rv = gHttpHandler->DoShiftReloadConnectionCleanupWithConnInfo(
   8012          mConnectionInfo);
   8013      if (NS_FAILED(rv)) {
   8014        LOG((
   8015            "nsHttpChannel::BeginConnect "
   8016            "DoShiftReloadConnectionCleanupWithConnInfo failed: %08x [this=%p]",
   8017            static_cast<uint32_t>(rv), this));
   8018      }
   8019    }
   8020  }
   8021 
   8022  // We may have been cancelled already, either by on-modify-request
   8023  // listeners or load group observers; in that case, we should not send the
   8024  // request to the server
   8025  if (mCanceled) {
   8026    return mStatus;
   8027  }
   8028  // skip classifier checks if this channel was the result of internal auth
   8029  // redirect
   8030  bool shouldBeClassifiedForTracker =
   8031      !LoadAuthRedirectedChannel() &&
   8032      NS_ShouldClassifyChannel(this, ClassifyType::ETP);
   8033 
   8034  if (shouldBeClassifiedForTracker) {
   8035    if (LoadChannelClassifierCancellationPending()) {
   8036      LOG(
   8037          ("Waiting for safe-browsing protection cancellation in BeginConnect "
   8038           "[this=%p]\n",
   8039           this));
   8040      return NS_OK;
   8041    }
   8042 
   8043    ReEvaluateReferrerAfterTrackingStatusIsKnown();
   8044  }
   8045 
   8046  MaybeStartDNSPrefetch();
   8047 
   8048  // Update whether the channel is on the third-party cookie blocking exception
   8049  // list.
   8050  CookieService::Update3PCBExceptionInfo(this);
   8051 
   8052  rv = CallOrWaitForResume(
   8053      [](nsHttpChannel* self) { return self->PrepareToConnect(); });
   8054  if (NS_FAILED(rv)) {
   8055    return rv;
   8056  }
   8057 
   8058  bool shouldBeClassifiedForSafeBrowsing =
   8059      NS_ShouldClassifyChannel(this, ClassifyType::SafeBrowsing);
   8060 
   8061  if (shouldBeClassifiedForSafeBrowsing) {
   8062    // Start nsChannelClassifier to catch phishing and malware URIs.
   8063    RefPtr<nsChannelClassifier> channelClassifier =
   8064        GetOrCreateChannelClassifier();
   8065    LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
   8066         channelClassifier.get(), this));
   8067    channelClassifier->Start();
   8068  }
   8069 
   8070  return NS_OK;
   8071 }
   8072 
   8073 void nsHttpChannel::MaybeStartDNSPrefetch() {
   8074  // Start a DNS lookup very early in case the real open is queued the DNS can
   8075  // happen in parallel. Do not do so in the presence of an HTTP proxy as
   8076  // all lookups other than for the proxy itself are done by the proxy.
   8077  // Also we don't do a lookup if the LOAD_NO_NETWORK_IO or
   8078  // LOAD_ONLY_FROM_CACHE flags are set.
   8079  //
   8080  // We keep the DNS prefetch object around so that we can retrieve
   8081  // timing information from it. There is no guarantee that we actually
   8082  // use the DNS prefetch data for the real connection, but as we keep
   8083  // this data around for 3 minutes by default, this should almost always
   8084  // be correct, and even when it isn't, the timing still represents _a_
   8085  // valid DNS lookup timing for the site, even if it is not _the_
   8086  // timing we used.
   8087  if ((mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE)) ||
   8088      LoadAuthRedirectedChannel()) {
   8089    return;
   8090  }
   8091 
   8092  auto dnsStrategy = GetProxyDNSStrategy();
   8093 
   8094  LOG(
   8095      ("nsHttpChannel::MaybeStartDNSPrefetch [this=%p, strategy=%u] "
   8096       "prefetching%s\n",
   8097       this, static_cast<uint32_t>(dnsStrategy),
   8098       mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : ""));
   8099 
   8100  if (dnsStrategy == ProxyDNSStrategy::ORIGIN) {
   8101    OriginAttributes originAttributes;
   8102    StoragePrincipalHelper::GetOriginAttributesForNetworkState(
   8103        this, originAttributes);
   8104 
   8105    mDNSPrefetch = new nsDNSPrefetch(mURI, originAttributes,
   8106                                     nsIRequest::GetTRRMode(), this, true);
   8107    nsIDNSService::DNSFlags dnsFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS;
   8108    if (mCaps & NS_HTTP_REFRESH_DNS) {
   8109      dnsFlags |= nsIDNSService::RESOLVE_BYPASS_CACHE;
   8110    }
   8111 
   8112    (void)mDNSPrefetch->PrefetchHigh(dnsFlags);
   8113 
   8114    bool unused;
   8115    if (StaticPrefs::network_dns_use_https_rr_as_altsvc() && !mHTTPSSVCRecord &&
   8116        !(mCaps & NS_HTTP_DISALLOW_HTTPS_RR) &&
   8117        canUseHTTPSRRonNetwork(unused)) {
   8118      MOZ_ASSERT(!mHTTPSSVCRecord);
   8119 
   8120      OriginAttributes originAttributes;
   8121      StoragePrincipalHelper::GetOriginAttributesForHTTPSRR(this,
   8122                                                            originAttributes);
   8123 
   8124      RefPtr<nsDNSPrefetch> resolver =
   8125          new nsDNSPrefetch(mURI, originAttributes, nsIRequest::GetTRRMode());
   8126      (void)resolver->FetchHTTPSSVC(mCaps & NS_HTTP_REFRESH_DNS, true,
   8127                                    [](nsIDNSHTTPSSVCRecord*) {
   8128                                      // Do nothing. This is a DNS prefetch.
   8129                                    });
   8130    }
   8131  }
   8132 }
   8133 
   8134 NS_IMETHODIMP
   8135 nsHttpChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) {
   8136  if (mCacheEntry && !LoadCacheEntryIsWriteOnly()) {
   8137    int64_t dataSize = 0;
   8138    mCacheEntry->GetDataSize(&dataSize);
   8139    *aEncodedBodySize = dataSize;
   8140  } else {
   8141    *aEncodedBodySize = mLogicalOffset;
   8142  }
   8143  return NS_OK;
   8144 }
   8145 
   8146 NS_IMETHODIMP
   8147 nsHttpChannel::GetDecompressDictionary(DictionaryCacheEntry** aDictionary) {
   8148  *aDictionary = do_AddRef(mDictDecompress).take();
   8149  return NS_OK;
   8150 }
   8151 
   8152 NS_IMETHODIMP
   8153 nsHttpChannel::SetDecompressDictionary(DictionaryCacheEntry* aDictionary) {
   8154  if (!aDictionary) {
   8155    if (mDictDecompress && mUsingDictionary) {
   8156      mDictDecompress->UseCompleted();
   8157    }
   8158    mUsingDictionary = false;
   8159  } else {
   8160    MOZ_ASSERT(!mDictDecompress);
   8161    aDictionary->InUse();
   8162    mUsingDictionary = true;
   8163  }
   8164  mDictDecompress = aDictionary;
   8165  return NS_OK;
   8166 }
   8167 
   8168 //-----------------------------------------------------------------------------
   8169 // nsHttpChannel::nsIHttpChannelInternal
   8170 //-----------------------------------------------------------------------------
   8171 
   8172 NS_IMETHODIMP
   8173 nsHttpChannel::GetIsAuthChannel(bool* aIsAuthChannel) {
   8174  *aIsAuthChannel = mIsAuthChannel;
   8175  return NS_OK;
   8176 }
   8177 
   8178 NS_IMETHODIMP
   8179 nsHttpChannel::SetChannelIsForDownload(bool aChannelIsForDownload) {
   8180  if (aChannelIsForDownload) {
   8181    AddClassFlags(nsIClassOfService::Throttleable);
   8182  } else {
   8183    ClearClassFlags(nsIClassOfService::Throttleable);
   8184  }
   8185 
   8186  return HttpBaseChannel::SetChannelIsForDownload(aChannelIsForDownload);
   8187 }
   8188 
   8189 base::ProcessId nsHttpChannel::ProcessId() {
   8190  nsCOMPtr<nsIParentChannel> parentChannel;
   8191  NS_QueryNotificationCallbacks(this, parentChannel);
   8192  if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel)) {
   8193    return httpParent->OtherPid();
   8194  }
   8195  if (RefPtr<DocumentLoadListener> docParent = do_QueryObject(parentChannel)) {
   8196    return docParent->OtherPid();
   8197  }
   8198  return base::GetCurrentProcId();
   8199 }
   8200 
   8201 auto nsHttpChannel::AttachStreamFilter() -> RefPtr<ChildEndpointPromise> {
   8202  LOG(("nsHttpChannel::AttachStreamFilter [this=%p]", this));
   8203  MOZ_ASSERT(!LoadOnStartRequestCalled());
   8204 
   8205  if (!ProcessId()) {
   8206    return ChildEndpointPromise::CreateAndReject(false, __func__);
   8207  }
   8208 
   8209  nsCOMPtr<nsIParentChannel> parentChannel;
   8210  NS_QueryNotificationCallbacks(this, parentChannel);
   8211 
   8212  // If our listener is a DocumentLoadListener, then we might handle
   8213  // multi-part responses here in the parent process. The current extension
   8214  // API doesn't understand the parsed multipart format, so we defer responding
   8215  // here until CallOnStartRequest, and attach the StreamFilter before the
   8216  // multipart handler (in the parent process!) if applicable.
   8217  if (RefPtr<DocumentLoadListener> docParent = do_QueryObject(parentChannel)) {
   8218    StreamFilterRequest* request = mStreamFilterRequests.AppendElement();
   8219    request->mPromise = new ChildEndpointPromise::Private(__func__);
   8220    return request->mPromise;
   8221  }
   8222 
   8223  mozilla::ipc::Endpoint<extensions::PStreamFilterParent> parent;
   8224  mozilla::ipc::Endpoint<extensions::PStreamFilterChild> child;
   8225  nsresult rv = extensions::PStreamFilter::CreateEndpoints(&parent, &child);
   8226  if (NS_FAILED(rv)) {
   8227    return ChildEndpointPromise::CreateAndReject(false, __func__);
   8228  }
   8229 
   8230  if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel)) {
   8231    return httpParent->AttachStreamFilter(std::move(parent), std::move(child));
   8232  }
   8233 
   8234  extensions::StreamFilterParent::Attach(this, std::move(parent));
   8235  return ChildEndpointPromise::CreateAndResolve(std::move(child), __func__);
   8236 }
   8237 
   8238 NS_IMETHODIMP
   8239 nsHttpChannel::GetNavigationStartTimeStamp(TimeStamp* aTimeStamp) {
   8240  LOG(("nsHttpChannel::GetNavigationStartTimeStamp [this=%p]", this));
   8241  MOZ_ASSERT(aTimeStamp);
   8242  *aTimeStamp = mNavigationStartTimeStamp;
   8243  return NS_OK;
   8244 }
   8245 
   8246 NS_IMETHODIMP
   8247 nsHttpChannel::SetNavigationStartTimeStamp(TimeStamp aTimeStamp) {
   8248  LOG(("nsHttpChannel::SetNavigationStartTimeStamp [this=%p]", this));
   8249  mNavigationStartTimeStamp = aTimeStamp;
   8250  return NS_OK;
   8251 }
   8252 
   8253 //-----------------------------------------------------------------------------
   8254 // nsHttpChannel::nsISupportsPriority
   8255 //-----------------------------------------------------------------------------
   8256 
   8257 NS_IMETHODIMP
   8258 nsHttpChannel::SetPriority(int32_t value) {
   8259  int16_t newValue = std::clamp<int32_t>(value, INT16_MIN, INT16_MAX);
   8260  if (mPriority == newValue) return NS_OK;
   8261 
   8262  LOG(("nsHttpChannel::SetPriority %p p=%d", this, newValue));
   8263 
   8264  mPriority = newValue;
   8265  if (mTransaction) {
   8266    nsresult rv = gHttpHandler->RescheduleTransaction(mTransaction, mPriority);
   8267    if (NS_FAILED(rv)) {
   8268      LOG(
   8269          ("nsHttpChannel::SetPriority [this=%p] "
   8270           "RescheduleTransaction failed (%08x)",
   8271           this, static_cast<uint32_t>(rv)));
   8272    }
   8273  }
   8274 
   8275  // If this channel is the real channel for an e10s channel, notify the
   8276  // child side about the priority change as well.
   8277  nsCOMPtr<nsIParentChannel> parentChannel;
   8278  NS_QueryNotificationCallbacks(this, parentChannel);
   8279  RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
   8280  if (httpParent) {
   8281    httpParent->DoSendSetPriority(newValue);
   8282  }
   8283 
   8284  return NS_OK;
   8285 }
   8286 
   8287 //-----------------------------------------------------------------------------
   8288 // HttpChannel::nsIClassOfService
   8289 //-----------------------------------------------------------------------------
   8290 
   8291 void nsHttpChannel::OnClassOfServiceUpdated() {
   8292  LOG(("nsHttpChannel::OnClassOfServiceUpdated this=%p, cos=%lu, inc=%d", this,
   8293       mClassOfService.Flags(), mClassOfService.Incremental()));
   8294 
   8295  if (mTransaction) {
   8296    gHttpHandler->UpdateClassOfServiceOnTransaction(mTransaction,
   8297                                                    mClassOfService);
   8298  }
   8299  if (EligibleForTailing()) {
   8300    RemoveAsNonTailRequest();
   8301  } else {
   8302    AddAsNonTailRequest();
   8303  }
   8304 }
   8305 
   8306 NS_IMETHODIMP
   8307 nsHttpChannel::SetClassFlags(uint32_t inFlags) {
   8308  uint32_t previous = mClassOfService.Flags();
   8309  mClassOfService.SetFlags(inFlags);
   8310  if (previous != mClassOfService.Flags()) {
   8311    OnClassOfServiceUpdated();
   8312  }
   8313  return NS_OK;
   8314 }
   8315 
   8316 NS_IMETHODIMP
   8317 nsHttpChannel::AddClassFlags(uint32_t inFlags) {
   8318  uint32_t previous = mClassOfService.Flags();
   8319  mClassOfService.SetFlags(inFlags | mClassOfService.Flags());
   8320  if (previous != mClassOfService.Flags()) {
   8321    OnClassOfServiceUpdated();
   8322  }
   8323  return NS_OK;
   8324 }
   8325 
   8326 NS_IMETHODIMP
   8327 nsHttpChannel::ClearClassFlags(uint32_t inFlags) {
   8328  uint32_t previous = mClassOfService.Flags();
   8329  mClassOfService.SetFlags(~inFlags & mClassOfService.Flags());
   8330  if (previous != mClassOfService.Flags()) {
   8331    OnClassOfServiceUpdated();
   8332  }
   8333  return NS_OK;
   8334 }
   8335 
   8336 NS_IMETHODIMP
   8337 nsHttpChannel::SetClassOfService(ClassOfService cos) {
   8338  ClassOfService previous = mClassOfService;
   8339  mClassOfService = cos;
   8340  if (previous != mClassOfService) {
   8341    OnClassOfServiceUpdated();
   8342  }
   8343  return NS_OK;
   8344 }
   8345 
   8346 NS_IMETHODIMP
   8347 nsHttpChannel::SetIncremental(bool incremental) {
   8348  bool previous = mClassOfService.Incremental();
   8349  mClassOfService.SetIncremental(incremental);
   8350  if (previous != mClassOfService.Incremental()) {
   8351    OnClassOfServiceUpdated();
   8352  }
   8353  return NS_OK;
   8354 }
   8355 
   8356 //-----------------------------------------------------------------------------
   8357 // nsHttpChannel::nsIProtocolProxyCallback
   8358 //-----------------------------------------------------------------------------
   8359 
   8360 NS_IMETHODIMP
   8361 nsHttpChannel::OnProxyAvailable(nsICancelable* request, nsIChannel* channel,
   8362                                nsIProxyInfo* pi, nsresult status) {
   8363  LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%" PRIx32
   8364       " mStatus=%" PRIx32 "]\n",
   8365       this, pi, static_cast<uint32_t>(status),
   8366       static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
   8367  mProxyRequest = nullptr;
   8368 
   8369  nsresult rv;
   8370 
   8371  // If status is a failure code, then it means that we failed to resolve
   8372  // proxy info.  That is a non-fatal error assuming it wasn't because the
   8373  // request was canceled.  We just failover to DIRECT when proxy resolution
   8374  // fails (failure can mean that the PAC URL could not be loaded).
   8375 
   8376  if (NS_SUCCEEDED(status)) {
   8377    mProxyInfo = pi;
   8378 
   8379    if (mProxyInfo) {
   8380      nsAutoCStringN<8> type;
   8381      mProxyInfo->GetType(type);
   8382      uint32_t flags = 0;
   8383      mProxyInfo->GetFlags(&flags);
   8384 
   8385      if (type.EqualsLiteral("socks")) {
   8386        if (flags & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
   8387          glean::networking::proxy_info_type
   8388              .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks5h)
   8389              .Add(1);
   8390        } else {
   8391          glean::networking::proxy_info_type
   8392              .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks5)
   8393              .Add(1);
   8394        }
   8395      } else if (type.EqualsLiteral("socks4")) {
   8396        if (flags & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) {
   8397          glean::networking::proxy_info_type
   8398              .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks4a)
   8399              .Add(1);
   8400        } else {
   8401          glean::networking::proxy_info_type
   8402              .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks4)
   8403              .Add(1);
   8404        }
   8405      } else if (type.EqualsLiteral("http")) {
   8406        glean::networking::proxy_info_type
   8407            .EnumGet(glean::networking::ProxyInfoTypeLabel::eHttp)
   8408            .Add(1);
   8409      } else if (type.EqualsLiteral("https")) {
   8410        glean::networking::proxy_info_type
   8411            .EnumGet(glean::networking::ProxyInfoTypeLabel::eHttps)
   8412            .Add(1);
   8413      } else if (type.EqualsLiteral("direct")) {
   8414        glean::networking::proxy_info_type
   8415            .EnumGet(glean::networking::ProxyInfoTypeLabel::eDirect)
   8416            .Add(1);
   8417      } else {
   8418        glean::networking::proxy_info_type
   8419            .EnumGet(glean::networking::ProxyInfoTypeLabel::eUnknown)
   8420            .Add(1);
   8421      }
   8422    }
   8423  }
   8424 
   8425  if (!gHttpHandler->Active()) {
   8426    LOG(
   8427        ("nsHttpChannel::OnProxyAvailable [this=%p] "
   8428         "Handler no longer active.\n",
   8429         this));
   8430    rv = NS_ERROR_NOT_AVAILABLE;
   8431  } else {
   8432    rv = BeginConnect();
   8433  }
   8434 
   8435  if (NS_FAILED(rv)) {
   8436    CloseCacheEntry(false);
   8437    (void)AsyncAbort(rv);
   8438  }
   8439  return rv;
   8440 }
   8441 
   8442 //-----------------------------------------------------------------------------
   8443 // nsHttpChannel::nsIProxiedChannel
   8444 //-----------------------------------------------------------------------------
   8445 
   8446 NS_IMETHODIMP
   8447 nsHttpChannel::GetProxyInfo(nsIProxyInfo** result) {
   8448  if (!mConnectionInfo) {
   8449    *result = do_AddRef(mProxyInfo).take();
   8450  } else {
   8451    *result = do_AddRef(mConnectionInfo->ProxyInfo()).take();
   8452  }
   8453  return NS_OK;
   8454 }
   8455 
   8456 //-----------------------------------------------------------------------------
   8457 // nsHttpChannel::nsITimedChannel
   8458 //-----------------------------------------------------------------------------
   8459 
   8460 NS_IMETHODIMP
   8461 nsHttpChannel::GetDomainLookupStart(TimeStamp* _retval) {
   8462  if (mTransaction) {
   8463    *_retval = mTransaction->GetDomainLookupStart();
   8464  } else {
   8465    *_retval = mTransactionTimings.domainLookupStart;
   8466  }
   8467  return NS_OK;
   8468 }
   8469 
   8470 NS_IMETHODIMP
   8471 nsHttpChannel::GetDomainLookupEnd(TimeStamp* _retval) {
   8472  if (mTransaction) {
   8473    *_retval = mTransaction->GetDomainLookupEnd();
   8474  } else {
   8475    *_retval = mTransactionTimings.domainLookupEnd;
   8476  }
   8477  return NS_OK;
   8478 }
   8479 
   8480 NS_IMETHODIMP
   8481 nsHttpChannel::GetConnectStart(TimeStamp* _retval) {
   8482  if (mTransaction) {
   8483    *_retval = mTransaction->GetConnectStart();
   8484  } else {
   8485    *_retval = mTransactionTimings.connectStart;
   8486  }
   8487  return NS_OK;
   8488 }
   8489 
   8490 NS_IMETHODIMP
   8491 nsHttpChannel::GetTcpConnectEnd(TimeStamp* _retval) {
   8492  if (mTransaction) {
   8493    *_retval = mTransaction->GetTcpConnectEnd();
   8494  } else {
   8495    *_retval = mTransactionTimings.tcpConnectEnd;
   8496  }
   8497  return NS_OK;
   8498 }
   8499 
   8500 NS_IMETHODIMP
   8501 nsHttpChannel::GetSecureConnectionStart(TimeStamp* _retval) {
   8502  if (mTransaction) {
   8503    *_retval = mTransaction->GetSecureConnectionStart();
   8504  } else {
   8505    *_retval = mTransactionTimings.secureConnectionStart;
   8506  }
   8507  return NS_OK;
   8508 }
   8509 
   8510 NS_IMETHODIMP
   8511 nsHttpChannel::GetConnectEnd(TimeStamp* _retval) {
   8512  if (mTransaction) {
   8513    *_retval = mTransaction->GetConnectEnd();
   8514  } else {
   8515    *_retval = mTransactionTimings.connectEnd;
   8516  }
   8517  return NS_OK;
   8518 }
   8519 
   8520 NS_IMETHODIMP
   8521 nsHttpChannel::GetRequestStart(TimeStamp* _retval) {
   8522  if (mTransaction) {
   8523    *_retval = mTransaction->GetRequestStart();
   8524  } else {
   8525    *_retval = mTransactionTimings.requestStart;
   8526  }
   8527  return NS_OK;
   8528 }
   8529 
   8530 NS_IMETHODIMP
   8531 nsHttpChannel::GetResponseStart(TimeStamp* _retval) {
   8532  if (mTransaction) {
   8533    *_retval = mTransaction->GetResponseStart();
   8534  } else {
   8535    *_retval = mTransactionTimings.responseStart;
   8536  }
   8537  return NS_OK;
   8538 }
   8539 
   8540 NS_IMETHODIMP
   8541 nsHttpChannel::GetResponseEnd(TimeStamp* _retval) {
   8542  if (mTransaction) {
   8543    *_retval = mTransaction->GetResponseEnd();
   8544  } else {
   8545    *_retval = mTransactionTimings.responseEnd;
   8546  }
   8547  return NS_OK;
   8548 }
   8549 
   8550 NS_IMETHODIMP
   8551 nsHttpChannel::GetTransactionPending(TimeStamp* _retval) {
   8552  if (mTransaction) {
   8553    *_retval = mTransaction->GetPendingTime();
   8554  } else {
   8555    *_retval = mTransactionTimings.transactionPending;
   8556  }
   8557  return NS_OK;
   8558 }
   8559 
   8560 //-----------------------------------------------------------------------------
   8561 // nsHttpChannel::nsIHttpAuthenticableChannel
   8562 //-----------------------------------------------------------------------------
   8563 
   8564 NS_IMETHODIMP
   8565 nsHttpChannel::GetIsSSL(bool* aIsSSL) {
   8566  // this attribute is really misnamed - it wants to know if
   8567  // https:// is being used. SSL might be used to cover http://
   8568  // in some circumstances (proxies, http/2, etc..)
   8569  return mURI->SchemeIs("https", aIsSSL);
   8570 }
   8571 
   8572 NS_IMETHODIMP
   8573 nsHttpChannel::GetProxyMethodIsConnect(bool* aProxyMethodIsConnect) {
   8574  *aProxyMethodIsConnect = mConnectionInfo->UsingConnect();
   8575  return NS_OK;
   8576 }
   8577 
   8578 NS_IMETHODIMP
   8579 nsHttpChannel::GetServerResponseHeader(nsACString& value) {
   8580  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;
   8581  return mResponseHead->GetHeader(nsHttp::Server, value);
   8582 }
   8583 
   8584 NS_IMETHODIMP
   8585 nsHttpChannel::GetProxyChallenges(nsACString& value) {
   8586  if (!mResponseHead) return NS_ERROR_UNEXPECTED;
   8587  return mResponseHead->GetHeader(nsHttp::Proxy_Authenticate, value);
   8588 }
   8589 
   8590 NS_IMETHODIMP
   8591 nsHttpChannel::GetWWWChallenges(nsACString& value) {
   8592  if (!mResponseHead) return NS_ERROR_UNEXPECTED;
   8593  return mResponseHead->GetHeader(nsHttp::WWW_Authenticate, value);
   8594 }
   8595 
   8596 NS_IMETHODIMP
   8597 nsHttpChannel::SetProxyCredentials(const nsACString& value) {
   8598  return mRequestHead.SetHeader(nsHttp::Proxy_Authorization, value);
   8599 }
   8600 
   8601 NS_IMETHODIMP
   8602 nsHttpChannel::SetWWWCredentials(const nsACString& value) {
   8603  // This method is called when various browser initiated authorization
   8604  // code sets the credentials.  We need to flag this header as the
   8605  // "browser default" so it does not show up in the ServiceWorker
   8606  // FetchEvent.  This may actually get called more than once, though,
   8607  // so we clear the header first since "default" headers are not
   8608  // allowed to overwrite normally.
   8609  (void)mRequestHead.ClearHeader(nsHttp::Authorization);
   8610  return mRequestHead.SetHeader(nsHttp::Authorization, value, false,
   8611                                nsHttpHeaderArray::eVarietyRequestDefault);
   8612 }
   8613 
   8614 //-----------------------------------------------------------------------------
   8615 // Methods that nsIHttpAuthenticableChannel dupes from other IDLs, which we
   8616 // get from HttpBaseChannel, must be explicitly forwarded, because C++ sucks.
   8617 //-----------------------------------------------------------------------------
   8618 
   8619 NS_IMETHODIMP
   8620 nsHttpChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
   8621  return HttpBaseChannel::GetLoadFlags(aLoadFlags);
   8622 }
   8623 
   8624 NS_IMETHODIMP
   8625 nsHttpChannel::GetURI(nsIURI** aURI) { return HttpBaseChannel::GetURI(aURI); }
   8626 
   8627 NS_IMETHODIMP
   8628 nsHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
   8629  return HttpBaseChannel::GetNotificationCallbacks(aCallbacks);
   8630 }
   8631 
   8632 NS_IMETHODIMP
   8633 nsHttpChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
   8634  return HttpBaseChannel::GetLoadGroup(aLoadGroup);
   8635 }
   8636 
   8637 NS_IMETHODIMP
   8638 nsHttpChannel::GetRequestMethod(nsACString& aMethod) {
   8639  return HttpBaseChannel::GetRequestMethod(aMethod);
   8640 }
   8641 
   8642 //-----------------------------------------------------------------------------
   8643 // nsHttpChannel::nsIRequestObserver
   8644 //-----------------------------------------------------------------------------
   8645 
   8646 void nsHttpChannel::RecordOnStartTelemetry(nsresult aStatus,
   8647                                           bool aIsNavigation) {
   8648  glean::http::channel_onstart_success
   8649      .EnumGet(static_cast<glean::http::ChannelOnstartSuccessLabel>(
   8650          NS_SUCCEEDED(aStatus)))
   8651      .Add();
   8652 
   8653  mozilla::glean::networking::http_channel_onstart_status
   8654      .Get(NS_SUCCEEDED(aStatus) ? "successful"_ns : "fail"_ns)
   8655      .Add(1);
   8656 
   8657  if (mTransaction) {
   8658    glean::networking::http3_channel_onstart_success
   8659        .Get((mTransaction->IsHttp3Used()) ? "http3"_ns : "no_http3"_ns,
   8660             NS_SUCCEEDED(aStatus) ? "true"_ns : "false"_ns)
   8661        .Add();
   8662  }
   8663 
   8664  enum class HttpOnStartState : uint32_t {
   8665    Success = 0,
   8666    DNSError = 1,
   8667    Others = 2,
   8668  };
   8669 
   8670  if (TRRService::Get() && TRRService::Get()->IsConfirmed()) {
   8671    // Note this telemetry probe is not working when DNS resolution is done in
   8672    // the socket process.
   8673    HttpOnStartState state = HttpOnStartState::Others;
   8674    if (NS_SUCCEEDED(aStatus)) {
   8675      state = HttpOnStartState::Success;
   8676    } else if (aStatus == NS_ERROR_UNKNOWN_HOST ||
   8677               aStatus == NS_ERROR_UNKNOWN_PROXY_HOST) {
   8678      state = HttpOnStartState::DNSError;
   8679    }
   8680 
   8681    if (aIsNavigation) {
   8682      glean::http::channel_page_onstart_success_trr
   8683          .Get(TRRService::ProviderKey())
   8684          .AccumulateSingleSample(static_cast<uint32_t>(state));
   8685    } else {
   8686      glean::http::channel_sub_onstart_success_trr
   8687          .Get(TRRService::ProviderKey())
   8688          .AccumulateSingleSample(static_cast<uint32_t>(state));
   8689    }
   8690  }
   8691 
   8692  if (nsIOService::UseSocketProcess() && mTransaction) {
   8693    const TimeStamp now = TimeStamp::Now();
   8694    TimeStamp responseEnd = mTransaction->GetResponseEnd();
   8695    if (!responseEnd.IsNull()) {
   8696      PerfStats::RecordMeasurement(PerfStats::Metric::ResponseEndSocketToParent,
   8697                                   now - responseEnd);
   8698    }
   8699 
   8700    mOnStartRequestStartTime = mTransaction->GetOnStartRequestStartTime();
   8701    if (!mOnStartRequestStartTime.IsNull()) {
   8702      PerfStats::RecordMeasurement(
   8703          PerfStats::Metric::OnStartRequestSocketToParent,
   8704          now - mOnStartRequestStartTime);
   8705    }
   8706  } else {
   8707    mOnStartRequestStartTime = TimeStamp::Now();
   8708  }
   8709 }
   8710 
   8711 static bool hasConnectivity() {
   8712  if (RefPtr<NetworkConnectivityService> ncs =
   8713          NetworkConnectivityService::GetSingleton()) {
   8714    nsINetworkConnectivityService::ConnectivityState state;
   8715    if (NS_SUCCEEDED(ncs->GetIPv4(&state)) &&
   8716        state == nsINetworkConnectivityService::OK) {
   8717      return true;
   8718    }
   8719    // We should also check for IPv6 connectivity here, but since
   8720    // quite a few mozilla domains don't have IPv6 records yet,
   8721    // we might incorrectly fallback to a backup domain on
   8722    // IPv6 only networks.
   8723    // When bug 1665605 gets fixed we can also return true when only IPv6 is OK.
   8724  }
   8725 
   8726  return false;
   8727 };
   8728 
   8729 static already_AddRefed<nsIURI> GetFallbackURI(nsIURI* aURI) {
   8730  nsresult rv;
   8731  nsAutoCString host;
   8732  aURI->GetHost(host);
   8733  nsCOMPtr<nsIURI> backupURI;
   8734 
   8735  nsAutoCString fallbackDomain;
   8736  if (!gIOService->GetFallbackDomain(host, fallbackDomain)) {
   8737    return nullptr;
   8738  }
   8739 
   8740  rv = NS_MutateURI(aURI).SetHost(fallbackDomain).Finalize(backupURI);
   8741  if (NS_FAILED(rv)) {
   8742    return nullptr;
   8743  }
   8744 
   8745  return backupURI.forget();
   8746 }
   8747 
   8748 // static
   8749 nsHttpChannel::EssentialDomainCategory
   8750 nsHttpChannel::GetEssentialDomainCategory(nsCString& domain) {
   8751  if (StringEndsWith(domain, ".addons.mozilla.org"_ns)) {
   8752    return EssentialDomainCategory::SubAddonsMozillaOrg;
   8753  }
   8754  if (domain == "addons.mozilla.org"_ns) {
   8755    return EssentialDomainCategory::AddonsMozillaOrg;
   8756  }
   8757  if (domain == "aus5.mozilla.org"_ns) {
   8758    return EssentialDomainCategory::Aus5MozillaOrg;
   8759  }
   8760  if (domain == "firefox.settings.services.mozilla.com"_ns) {
   8761    return EssentialDomainCategory::RemoteSettings;
   8762  }
   8763  if (domain == "incoming.telemetry.mozilla.com"_ns) {
   8764    return EssentialDomainCategory::Telemetry;
   8765  }
   8766  return EssentialDomainCategory::Other;
   8767 }
   8768 
   8769 // Helper function to send LNA access info to child process for console logging
   8770 // The child process has access to CallingScriptLocationString() which requires
   8771 // JS context
   8772 static void ReportLNAAccessToConsole(nsHttpChannel* aChannel,
   8773                                     const char* aMessageName,
   8774                                     const nsACString& aPromptAction = ""_ns) {
   8775  // Send IPC to child process to log to console with script location
   8776  nsCOMPtr<nsIParentChannel> parentChannel;
   8777  NS_QueryNotificationCallbacks(aChannel, parentChannel);
   8778  if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel)) {
   8779    NetAddr peerAddr = aChannel->GetPeerAddr();
   8780 
   8781    // Fetch top-level site URI in parent process and pass via IPC.
   8782    // We need to fetch this here because with Fission (site isolation),
   8783    // cross-site iframes run in separate content processes and cannot
   8784    // access the top-level document in a different process.
   8785    nsAutoCString topLevelSite;
   8786    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   8787    if (loadInfo) {
   8788      RefPtr<mozilla::dom::BrowsingContext> bc;
   8789      loadInfo->GetBrowsingContext(getter_AddRefs(bc));
   8790      if (bc && bc->Top() && bc->Top()->Canonical()) {
   8791        RefPtr<mozilla::dom::WindowGlobalParent> topWindowGlobal =
   8792            bc->Top()->Canonical()->GetCurrentWindowGlobal();
   8793        if (topWindowGlobal) {
   8794          nsIPrincipal* topPrincipal = topWindowGlobal->DocumentPrincipal();
   8795          if (topPrincipal) {
   8796            nsCOMPtr<nsIURI> topURI = topPrincipal->GetURI();
   8797            if (topURI) {
   8798              (void)topURI->GetSpec(topLevelSite);
   8799            }
   8800          }
   8801        }
   8802      }
   8803    }
   8804 
   8805    httpParent->DoSendReportLNAToConsole(peerAddr, nsCString(aMessageName),
   8806                                         nsCString(aPromptAction),
   8807                                         topLevelSite);
   8808  }
   8809 }
   8810 
   8811 nsresult nsHttpChannel::ProcessLNAActions() {
   8812  if (!mTransaction) {
   8813    // this could happen with rcwn enabled.
   8814    // We have hit network and have detected LNA, meanwhile cache won and reset
   8815    // the transaction in ReadFromCache
   8816    return NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED;
   8817  }
   8818  // Suspend to block any notification to the channel.
   8819  // This will get resumed in
   8820  // nsHttpChannel::OnPermissionPromptResult
   8821  UpdateCurrentIpAddressSpace();
   8822  mWaitingForLNAPermission = true;
   8823  Suspend();
   8824  auto permissionKey = mTransaction->GetTargetIPAddressSpace() ==
   8825                               nsILoadInfo::IPAddressSpace::Local
   8826                           ? LOCAL_HOST_PERMISSION_KEY
   8827                           : LOCAL_NETWORK_PERMISSION_KEY;
   8828  LNAPermission permissionUpdateResult =
   8829      UpdateLocalNetworkAccessPermissions(permissionKey);
   8830 
   8831  if (LNAPermission::Granted == permissionUpdateResult) {
   8832    // permission granted (auto-allow via permanent permission)
   8833    mLNAPromptAction.AssignLiteral("auto_allow");
   8834    return OnPermissionPromptResult(true, permissionKey);
   8835  }
   8836 
   8837  if (LNAPermission::Denied == permissionUpdateResult) {
   8838    // permission denied (auto-deny via permanent permission)
   8839    mLNAPromptAction.AssignLiteral("auto_deny");
   8840    return OnPermissionPromptResult(false, permissionKey);
   8841  }
   8842 
   8843  // If we get here, we don't have any permission to access the local
   8844  // host/network. We need to prompt the user for action
   8845  auto permissionPromptCallback =
   8846      [self = RefPtr{this}](bool aPermissionGranted, const nsACString& aType,
   8847                            bool aPromptShown) -> void {
   8848    // Set mLNAPromptAction based on whether prompt was shown and user response
   8849    if (aPromptShown) {
   8850      if (aPermissionGranted) {
   8851        self->mLNAPromptAction.AssignLiteral("prompt_allow");
   8852      } else {
   8853        self->mLNAPromptAction.AssignLiteral("prompt_deny");
   8854      }
   8855    } else {
   8856      if (aPermissionGranted) {
   8857        self->mLNAPromptAction.AssignLiteral("auto_allow");
   8858      } else {
   8859        self->mLNAPromptAction.AssignLiteral("auto_deny");
   8860      }
   8861    }
   8862    self->OnPermissionPromptResult(aPermissionGranted, aType);
   8863  };
   8864 
   8865  RefPtr<LNAPermissionRequest> request = new LNAPermissionRequest(
   8866      std::move(permissionPromptCallback), mLoadInfo, permissionKey);
   8867 
   8868  // Log to console before requesting permission
   8869  ReportLNAAccessToConsole(this, "LocalNetworkAccessPermissionRequired");
   8870 
   8871  // This invokes callback nsHttpChannel::OnPermissionPromptResult
   8872  // synchronously if the permission is already granted or denied
   8873  // if permission is not available we prompt the user and in that case
   8874  // nsHttpChannel::OnPermissionPromptResult is invoked asynchronously once
   8875  // the user responds to the prompt
   8876  return request->RequestPermission();
   8877 }
   8878 
   8879 void nsHttpChannel::UpdateCurrentIpAddressSpace() {
   8880  if (!mTransaction) {
   8881    return;
   8882  }
   8883 
   8884  if (mPeerAddr.GetIpAddressSpace() == nsILoadInfo::IPAddressSpace::Unknown) {
   8885    // fetch peer address from transaction
   8886    bool isTrr;
   8887    bool echConfigUsed;
   8888    mTransaction->GetNetworkAddresses(mSelfAddr, mPeerAddr, isTrr,
   8889                                      mEffectiveTRRMode, mTRRSkipReason,
   8890                                      echConfigUsed);
   8891  }
   8892 
   8893  nsILoadInfo::IPAddressSpace docAddressSpace = mPeerAddr.GetIpAddressSpace();
   8894  mLoadInfo->SetIpAddressSpace(docAddressSpace);
   8895  ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType();
   8896  if (type == ExtContentPolicy::TYPE_DOCUMENT ||
   8897      type == ExtContentPolicy::TYPE_SUBDOCUMENT) {
   8898    RefPtr<mozilla::dom::BrowsingContext> bc;
   8899    mLoadInfo->GetTargetBrowsingContext(getter_AddRefs(bc));
   8900    if (bc) {
   8901      bc->SetCurrentIPAddressSpace(docAddressSpace);
   8902    }
   8903 
   8904    if (mCacheEntry) {
   8905      // store the ipaddr information into the cache metadata entry
   8906      if (mPeerAddr.GetIpAddressSpace() !=
   8907          nsILoadInfo::IPAddressSpace::Unknown) {
   8908        uint16_t port;
   8909        mPeerAddr.GetPort(&port);
   8910        mCacheEntry->SetMetaDataElement("peer-ip-address",
   8911                                        mPeerAddr.ToString().get());
   8912        mCacheEntry->SetMetaDataElement("peer-port", ToString(port).c_str());
   8913      }
   8914    }
   8915  }
   8916 }
   8917 
   8918 NS_IMETHODIMP
   8919 nsHttpChannel::OnStartRequest(nsIRequest* request) {
   8920  nsresult rv;
   8921 
   8922  MOZ_ASSERT(LoadRequestObserversCalled());
   8923 
   8924  AUTO_PROFILER_LABEL("nsHttpChannel::OnStartRequest", NETWORK);
   8925  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnStartRequest", NETWORK,
   8926                            Flow::FromPointer(this));
   8927 
   8928  if (!(mCanceled || NS_FAILED(mStatus)) &&
   8929      !WRONG_RACING_RESPONSE_SOURCE(request)) {
   8930    // capture the request's status, so our consumers will know ASAP of any
   8931    // connection failures, etc - bug 93581
   8932    nsresult status;
   8933    request->GetStatus(&status);
   8934    mStatus = status;
   8935  }
   8936 
   8937  if (mStatus == NS_ERROR_NON_LOCAL_CONNECTION_REFUSED) {
   8938    MOZ_CRASH_UNSAFE(nsPrintfCString("Attempting to connect to non-local "
   8939                                     "address! opener is [%s], uri is "
   8940                                     "[%s]",
   8941                                     mOpenerCallingScriptLocation
   8942                                         ? mOpenerCallingScriptLocation->get()
   8943                                         : "unknown",
   8944                                     mURI->GetSpecOrDefault().get())
   8945                         .get());
   8946  }
   8947 
   8948  if (mStatus == NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED) {
   8949    return ProcessLNAActions();
   8950  }
   8951 
   8952  LOG(("nsHttpChannel::OnStartRequest [this=%p request=%p status=%" PRIx32
   8953       "]\n",
   8954       this, request, static_cast<uint32_t>(static_cast<nsresult>(mStatus))));
   8955 
   8956  RecordOnStartTelemetry(mStatus, IsNavigation());
   8957 
   8958  if (mRaceCacheWithNetwork) {
   8959    LOG(
   8960        ("  racingNetAndCache - mFirstResponseSource:%d fromCache:%d "
   8961         "fromNet:%d\n",
   8962         static_cast<int32_t>(mFirstResponseSource), request == mCachePump,
   8963         request == mTransactionPump));
   8964    if (mFirstResponseSource == RESPONSE_PENDING) {
   8965      // When the cache wins mFirstResponseSource is set to
   8966      // RESPONSE_FROM_CACHE earlier in ReadFromCache, so this must be a
   8967      // response from the network.
   8968      MOZ_ASSERT(request == mTransactionPump);
   8969      LOG(("  First response from network\n"));
   8970      {
   8971        // Race condition with OnCacheEntryCheck, which is not limited
   8972        // to main thread.
   8973        mozilla::MutexAutoLock lock(mRCWNLock);
   8974        mFirstResponseSource = RESPONSE_FROM_NETWORK;
   8975        // If we haven't gotten any response from the cache, we've won the
   8976        // race.  Any response from the case means it's a cache 'win', even if
   8977        // the response was "we don't have an entry" or "we have an entry but
   8978        // it's expired/invalid".
   8979        if (LoadCachedContentIsValid() == CachedContentValidity::Unset) {
   8980          StoreNetworkWonRace(1);
   8981        }
   8982        mOnStartRequestTimestamp = TimeStamp::Now();
   8983        PROFILER_MARKER_TEXT(
   8984            "RCWN", NETWORK, {},
   8985            nsPrintfCString("Network won on StartRequest valid=%d for %s - %p",
   8986                            LoadCachedContentIsValid(), mSpec.get(), this));
   8987 
   8988        // Conditional or byte range header could be added in
   8989        // OnCacheEntryCheck. We need to remove them because the
   8990        // request might be sent again due to auth retry and we must
   8991        // not send these headers without having the entry.
   8992        if (mDidReval) {
   8993          LOG(("  Removing conditional request headers"));
   8994          UntieValidationRequest();
   8995          mDidReval = false;
   8996        }
   8997        if (LoadCachedContentIsPartial()) {
   8998          LOG(("  Removing byte range request headers"));
   8999          UntieByteRangeRequest();
   9000          StoreCachedContentIsPartial(false);
   9001        }
   9002      }
   9003      mAvailableCachedAltDataType.Truncate();
   9004      StoreDeliveringAltData(false);
   9005    } else if (WRONG_RACING_RESPONSE_SOURCE(request)) {
   9006      LOG(("  Early return when racing. This response not needed."));
   9007      return NS_OK;
   9008    } else {
   9009      PROFILER_MARKER_TEXT(
   9010          "RCWN", NETWORK, {},
   9011          nsPrintfCString("Cache won on StartRequest valid=%d for %s - %p",
   9012                          LoadCachedContentIsValid(), mSpec.get(), this));
   9013    }
   9014  }
   9015 
   9016  // Make sure things are what we expect them to be...
   9017  MOZ_ASSERT(request == mCachePump || request == mTransactionPump,
   9018             "Unexpected request");
   9019 
   9020  MOZ_ASSERT(mRaceCacheWithNetwork || !(mTransactionPump && mCachePump) ||
   9021                 LoadCachedContentIsPartial() || LoadTransactionReplaced(),
   9022             "If we have both pumps, we're racing cache with network, the cache"
   9023             " content is partial, or the cache entry was revalidated and "
   9024             "OnStopRequest was not called yet for the transaction pump.");
   9025 
   9026  StoreAfterOnStartRequestBegun(true);
   9027  if (mOnStartRequestTimestamp.IsNull()) {
   9028    mOnStartRequestTimestamp = TimeStamp::Now();
   9029  }
   9030 
   9031  mozilla::glean::networking::http_onstart_suspend_total_time
   9032      .AccumulateRawDuration(mSuspendTotalTime);
   9033 
   9034  if (mTransaction) {
   9035    mProxyConnectResponseCode = mTransaction->GetProxyConnectResponseCode();
   9036    if (request == mTransactionPump) {
   9037      StoreDataSentToChildProcess(mTransaction->DataSentToChildProcess());
   9038    }
   9039 
   9040    if (!mSecurityInfo && !mCachePump) {
   9041      // grab the security info from the connection object; the transaction
   9042      // is guaranteed to own a reference to the connection.
   9043      mSecurityInfo = mTransaction->SecurityInfo();
   9044    }
   9045 
   9046    uint32_t stage = mTransaction->HTTPSSVCReceivedStage();
   9047    if (!LoadHTTPSSVCTelemetryReported() && stage != HTTPSSVC_NOT_USED) {
   9048      glean::http::dns_httpssvc_record_receiving_stage.AccumulateSingleSample(
   9049          stage);
   9050    }
   9051 
   9052    if (HTTPS_RR_IS_USED(stage)) {
   9053      nsAutoCString suffix(LoadEchConfigUsed() ? "_ech_used" : "");
   9054      // Determine the result string based on the status.
   9055      nsAutoCString result(NS_SUCCEEDED(mStatus) ? "success" : "failure");
   9056      result.Append(suffix);
   9057 
   9058      mozilla::glean::networking::http_channel_onstart_success_https_rr
   9059          .Get(result)
   9060          .Add(1);
   9061      StoreHasHTTPSRR(true);
   9062    }
   9063 
   9064    StoreLoadedBySocketProcess(mTransaction->AsHttpTransactionParent() !=
   9065                               nullptr);
   9066 
   9067    bool isTrr;
   9068    bool echConfigUsed;
   9069    mTransaction->GetNetworkAddresses(mSelfAddr, mPeerAddr, isTrr,
   9070                                      mEffectiveTRRMode, mTRRSkipReason,
   9071                                      echConfigUsed);
   9072    // update IP AddressSpace for non-proxy connections and tests
   9073    // We need to update the browsing context for tests as browser tests
   9074    // uses local proxy to connect to
   9075    // external domains
   9076    if (!mProxyInfo || xpc::IsInAutomation()) {
   9077      // If this is main document load or iframe store the IP Address space in
   9078      // the browsing context
   9079      UpdateCurrentIpAddressSpace();
   9080    }
   9081 
   9082    StoreResolvedByTRR(isTrr);
   9083    StoreEchConfigUsed(echConfigUsed);
   9084  } else {  // !mTransaction
   9085    MaybeUpdateDocumentIPAddressSpaceFromCache();
   9086  }
   9087 
   9088  if (!mCanceled && mTransaction &&
   9089      mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal()) {
   9090    // We have to report telemetry before we actually attempt to redirect to
   9091    // the fallback domain because doing so will change mStatus
   9092    ReportSystemChannelTelemetry(mStatus);
   9093  }
   9094 
   9095  // don't enter this block if we're reading from the cache...
   9096  if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) {
   9097    // mTransactionPump doesn't hit OnInputStreamReady and call this until
   9098    // all of the response headers have been acquired, so we can take
   9099    // ownership of them from the transaction.
   9100    RefPtr<nsHttpConnectionInfo> connInfo;
   9101    mResponseHead =
   9102        mTransaction->TakeResponseHeadAndConnInfo(getter_AddRefs(connInfo));
   9103    mSupportsHTTP3 = mTransaction->GetSupportsHTTP3();
   9104    // the response head may be null if the transaction was cancelled.  in
   9105    // which case we just need to call OnStartRequest/OnStopRequest.
   9106    if (mResponseHead) {
   9107      if (AntiTrackingUtils::ProcessStorageAccessHeadersShouldRetry(this)) {
   9108        // force reload. Doom cache to avoid redirect loop
   9109        if (mCacheEntry) {
   9110          mCacheEntry->AsyncDoom(nullptr);
   9111        }
   9112 
   9113        auto storeAllowStorageAccess = [&](nsIChannel* aRedirectedChannel) {
   9114          RefPtr<nsHttpChannel> httpChan = do_QueryObject(aRedirectedChannel);
   9115          if (httpChan) {
   9116            httpChan->StoreStorageAccessReloadChannel(true);
   9117          }
   9118        };
   9119 
   9120        rv = StartRedirectChannelToURI(mURI,
   9121                                       nsIChannelEventSink::REDIRECT_INTERNAL,
   9122                                       storeAllowStorageAccess);
   9123        if (NS_FAILED(rv)) {
   9124          Cancel(rv);
   9125          return CallOnStartRequest();
   9126        }
   9127        return NS_OK;
   9128      }
   9129      return ProcessResponse(connInfo);
   9130    }
   9131 
   9132    NS_WARNING("No response head in OnStartRequest");
   9133  }
   9134 
   9135  // cache file could be deleted on our behalf, it could contain errors or
   9136  // it failed to allocate memory, reload from network here.
   9137  if (mCacheEntry && mCachePump && RECOVER_FROM_CACHE_FILE_ERROR(mStatus)) {
   9138    LOG(("  cache file error, reloading from server"));
   9139    mCacheEntry->AsyncDoom(nullptr);
   9140    rv =
   9141        StartRedirectChannelToURI(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
   9142    if (NS_SUCCEEDED(rv)) return NS_OK;
   9143  }
   9144 
   9145  // If this is a system principal request to an essential domain and we
   9146  // currently have connectivity, then check if there's a fallback domain we
   9147  // can use to retry. If so we redirect to the fallback domain.
   9148  if (NS_FAILED(mStatus) && !mCanceled &&
   9149      mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal()) {
   9150    if (StaticPrefs::network_essential_domains_fallback() &&
   9151        hasConnectivity()) {
   9152      auto passDomainCategory = [&](nsIChannel* aRedirectedChannel) {
   9153        RefPtr<nsHttpChannel> httpChan = do_QueryObject(aRedirectedChannel);
   9154        if (httpChan) {
   9155          nsAutoCString host;
   9156          mURI->GetHost(host);
   9157          httpChan->mEssentialDomainCategory =
   9158              Some(GetEssentialDomainCategory(host));
   9159        }
   9160      };
   9161 
   9162      if (nsCOMPtr<nsIURI> fallbackURI = GetFallbackURI(mURI)) {
   9163        rv = StartRedirectChannelToURI(fallbackURI,
   9164                                       nsIChannelEventSink::REDIRECT_INTERNAL,
   9165                                       passDomainCategory);
   9166        if (NS_SUCCEEDED(rv)) {
   9167          nsCOMPtr<nsIObserverService> obsService =
   9168              services::GetObserverService();
   9169          if (obsService)
   9170            obsService->NotifyObservers(static_cast<nsIHttpChannel*>(this),
   9171                                        "httpchannel-fallback", nullptr);
   9172          return NS_OK;
   9173        }
   9174      }
   9175    }
   9176  }
   9177 
   9178  // avoid crashing if mListener happens to be null...
   9179  if (!mListener) {
   9180    MOZ_ASSERT_UNREACHABLE("mListener is null");
   9181    return NS_OK;
   9182  }
   9183 
   9184  rv = ProcessCrossOriginSecurityHeaders();
   9185  if (NS_FAILED(rv)) {
   9186    mStatus = rv;
   9187    HandleAsyncAbort();
   9188    return rv;
   9189  }
   9190 
   9191  // No process change is needed, so continue on to ContinueOnStartRequest1.
   9192  return ContinueOnStartRequest1(rv);
   9193 }
   9194 
   9195 void nsHttpChannel::MaybeUpdateDocumentIPAddressSpaceFromCache() {
   9196  MOZ_ASSERT(mLoadInfo);
   9197  ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType();
   9198 
   9199  // Update the IPAddressSpace in the BrowsingContext only for main or sub
   9200  // document
   9201  if (type != ExtContentPolicy::TYPE_DOCUMENT &&
   9202      type != ExtContentPolicy::TYPE_SUBDOCUMENT) {
   9203    return;
   9204  }
   9205 
   9206  RefPtr<mozilla::dom::BrowsingContext> bc;
   9207  mLoadInfo->GetTargetBrowsingContext(getter_AddRefs(bc));
   9208 
   9209  if (!bc || !mCacheEntry) {
   9210    return;
   9211  }
   9212 
   9213  nsAutoCString ipAddrStr, portStr;
   9214  mCacheEntry->GetMetaDataElement("peer-ip-address", getter_Copies(ipAddrStr));
   9215  mCacheEntry->GetMetaDataElement("peer-port", getter_Copies(portStr));
   9216 
   9217  nsresult rv;
   9218  uint32_t port = portStr.ToInteger(&rv);
   9219 
   9220  if (!ipAddrStr.IsEmpty() && NS_SUCCEEDED(rv)) {
   9221    NetAddr ipAddr;
   9222    rv = ipAddr.InitFromString(ipAddrStr, port);
   9223    NS_ENSURE_SUCCESS_VOID(rv);
   9224    bc->SetCurrentIPAddressSpace(ipAddr.GetIpAddressSpace());
   9225  }
   9226 }
   9227 
   9228 nsresult nsHttpChannel::OnPermissionPromptResult(bool aGranted,
   9229                                                 const nsACString& aType) {
   9230  mWaitingForLNAPermission = false;
   9231 
   9232  if (aGranted) {
   9233    LOG(
   9234        ("nsHttpChannel::OnPermissionPromptResult [this=%p] "
   9235         "LNAPermissionRequest "
   9236         "granted",
   9237         this));
   9238    // we need to cache this data as permission manager is updated async and
   9239    // might not be reflected immediately
   9240    if (aType == LOCAL_HOST_PERMISSION_KEY) {
   9241      mLNAPermission.mLocalHostPermission = LNAPermission::Granted;
   9242    }
   9243 
   9244    if (aType == LOCAL_NETWORK_PERMISSION_KEY) {
   9245      mLNAPermission.mLocalNetworkPermission = LNAPermission::Granted;
   9246    }
   9247    // reset the transaction
   9248    mTransaction = nullptr;
   9249 
   9250    // resets streams and listener. Ensures we dont get any more callbacks to
   9251    // nsHttpChannel from the pumps
   9252    RefPtr<nsInputStreamPump> pump = do_QueryObject(mTransactionPump);
   9253    if (pump) {
   9254      pump->Reset();
   9255    }
   9256    mTransactionPump = nullptr;
   9257 
   9258    // reset the status as we are going to replay the transaction
   9259    mStatus = nsresult::NS_OK;
   9260 
   9261    // allow notifications for the channel
   9262    Resume();
   9263    // replay the transaction with permisions granted
   9264    return CallOrWaitForResume(
   9265        [](auto* self) -> nsresult { return self->DoConnect(nullptr); });
   9266  }
   9267 
   9268  // permission denied
   9269  // resume the transaction pump, we should get the OnStopRequest Notification
   9270  LOG(
   9271      ("nsHttpChannel::OnPermissionPromptResult [this=%p] "
   9272       "LNAPermissionRequest "
   9273       "denied",
   9274       this));
   9275 
   9276  Resume();
   9277 
   9278  if (aType == LOCAL_HOST_PERMISSION_KEY) {
   9279    mLNAPermission.mLocalHostPermission = LNAPermission::Denied;
   9280  }
   9281 
   9282  if (aType == LOCAL_NETWORK_PERMISSION_KEY) {
   9283    mLNAPermission.mLocalNetworkPermission = LNAPermission::Denied;
   9284  }
   9285 
   9286  if (!mSuspendCount) {
   9287    return ContinueOnStartRequest1(
   9288        nsresult::NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED);
   9289  }
   9290  // channel is suspended state. We will continue when the channel is
   9291  // resumed
   9292  return CallOrWaitForResume([](auto* self) {
   9293    return self->ContinueOnStartRequest1(
   9294        nsresult::NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED);
   9295  });
   9296 }
   9297 
   9298 nsresult nsHttpChannel::ContinueOnStartRequest1(nsresult result) {
   9299  nsresult rv;
   9300 
   9301  // if process selection failed, cancel this load.
   9302  if (NS_FAILED(result) && !mCanceled) {
   9303    Cancel(result);
   9304    return CallOnStartRequest();
   9305  }
   9306 
   9307  // before we start any content load, check for redirectTo being called
   9308  // this code is executed mainly before we start load from the cache
   9309  if (mAPIRedirectTo && !mCanceled) {
   9310    nsAutoCString redirectToSpec;
   9311    mAPIRedirectTo->first()->GetAsciiSpec(redirectToSpec);
   9312    LOG(("  redirectTo called with uri=%s", redirectToSpec.BeginReading()));
   9313 
   9314    MOZ_ASSERT(!LoadOnStartRequestCalled());
   9315    PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2);
   9316    rv = StartRedirectChannelToURI(
   9317        mAPIRedirectTo->first(),
   9318        mAPIRedirectTo->second() ? nsIChannelEventSink::REDIRECT_TEMPORARY |
   9319                                       nsIChannelEventSink::REDIRECT_TRANSPARENT
   9320                                 : nsIChannelEventSink::REDIRECT_TEMPORARY);
   9321    mAPIRedirectTo = Nothing();
   9322    if (NS_SUCCEEDED(rv)) {
   9323      return NS_OK;
   9324    }
   9325    PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2);
   9326  }
   9327 
   9328  // Hack: ContinueOnStartRequest2 uses NS_OK to detect successful redirects,
   9329  // so we distinguish this codepath (a non-redirect that's processing
   9330  // normally) by passing in a bogus error code.
   9331  return ContinueOnStartRequest2(NS_BINDING_FAILED);
   9332 }
   9333 
   9334 nsresult nsHttpChannel::ContinueOnStartRequest2(nsresult result) {
   9335  if (NS_SUCCEEDED(result)) {
   9336    // Redirect has passed through, we don't want to go on with this
   9337    // channel.  It will now be canceled by the redirect handling code
   9338    // that called this function.
   9339    return NS_OK;
   9340  }
   9341 
   9342  // on proxy errors, try to failover
   9343  if (mConnectionInfo->ProxyInfo() &&
   9344      (mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED ||
   9345       mStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
   9346       mStatus == NS_ERROR_NET_TIMEOUT || mStatus == NS_ERROR_NET_RESET)) {
   9347    PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
   9348    if (NS_SUCCEEDED(ProxyFailover())) {
   9349      mProxyConnectResponseCode = 0;
   9350      return NS_OK;
   9351    }
   9352    PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3);
   9353  }
   9354 
   9355  // Hack: ContinueOnStartRequest3 uses NS_OK to detect successful redirects,
   9356  // so we distinguish this codepath (a non-redirect that's processing
   9357  // normally) by passing in a bogus error code.
   9358  return ContinueOnStartRequest3(NS_BINDING_FAILED);
   9359 }
   9360 
   9361 nsresult nsHttpChannel::ContinueOnStartRequest3(nsresult result) {
   9362  if (NS_SUCCEEDED(result)) {
   9363    // Redirect has passed through, we don't want to go on with this
   9364    // channel.  It will now be canceled by the redirect handling code
   9365    // that called this function.
   9366    return NS_OK;
   9367  }
   9368 
   9369  return CallOnStartRequest();
   9370 }
   9371 
   9372 static void ReportHTTPSRRTelemetry(
   9373    const Maybe<nsCOMPtr<nsIDNSHTTPSSVCRecord>>& aMaybeRecord) {
   9374  bool hasHTTPSRR = aMaybeRecord && (aMaybeRecord.ref() != nullptr);
   9375  if (!hasHTTPSRR) {
   9376    mozilla::glean::networking::https_rr_presented.Get("none"_ns).Add(1);
   9377    return;
   9378  }
   9379 
   9380  const nsCOMPtr<nsIDNSHTTPSSVCRecord>& record = aMaybeRecord.ref();
   9381  nsCOMPtr<nsISVCBRecord> svcbRecord;
   9382  if (NS_SUCCEEDED(record->GetServiceModeRecord(false, false,
   9383                                                getter_AddRefs(svcbRecord)))) {
   9384    MOZ_ASSERT(svcbRecord);
   9385 
   9386    Maybe<std::tuple<nsCString, SupportedAlpnRank>> alpn =
   9387        svcbRecord->GetAlpn();
   9388    bool isHttp3 = alpn ? IsHttp3(std::get<1>(*alpn)) : false;
   9389    mozilla::glean::networking::https_rr_presented
   9390        .Get(isHttp3 ? "presented_with_http3"_ns : "presented"_ns)
   9391        .Add(1);
   9392  }
   9393 }
   9394 
   9395 static void RecordHttpChanDispositionGlean(ChannelDisposition chanDisposition) {
   9396  switch (chanDisposition) {
   9397    case kHttpCanceled:
   9398      mozilla::glean::networking::http_channel_disposition
   9399          .Get("http_cancelled"_ns)
   9400          .Add(1);
   9401      break;
   9402    case kHttpDisk:
   9403      mozilla::glean::networking::http_channel_disposition.Get("http_disk"_ns)
   9404          .Add(1);
   9405      break;
   9406    case kHttpNetOK:
   9407      mozilla::glean::networking::http_channel_disposition.Get("http_net_ok"_ns)
   9408          .Add(1);
   9409      break;
   9410    case kHttpNetEarlyFail:
   9411      mozilla::glean::networking::http_channel_disposition
   9412          .Get("http_net_early_fail"_ns)
   9413          .Add(1);
   9414      break;
   9415    case kHttpNetLateFail:
   9416      mozilla::glean::networking::http_channel_disposition
   9417          .Get("http_net_late_fail"_ns)
   9418          .Add(1);
   9419      break;
   9420    case kHttpsCanceled:
   9421      mozilla::glean::networking::http_channel_disposition
   9422          .Get("https_cancelled"_ns)
   9423          .Add(1);
   9424      break;
   9425    case kHttpsDisk:
   9426      mozilla::glean::networking::http_channel_disposition.Get("http_disk"_ns)
   9427          .Add(1);
   9428      break;
   9429    case kHttpsNetOK:
   9430      mozilla::glean::networking::http_channel_disposition
   9431          .Get("https_net_ok"_ns)
   9432          .Add(1);
   9433      break;
   9434    case kHttpsNetEarlyFail:
   9435      mozilla::glean::networking::http_channel_disposition
   9436          .Get("https_net_early_fail"_ns)
   9437          .Add(1);
   9438      break;
   9439    case kHttpsNetLateFail:
   9440      mozilla::glean::networking::http_channel_disposition
   9441          .Get("https_net_late_fail"_ns)
   9442          .Add(1);
   9443      break;
   9444    default:
   9445      MOZ_ASSERT_UNREACHABLE("Unknown value for chanDisposition");
   9446  }
   9447 }
   9448 
   9449 static nsLiteralCString HttpChanDispositionToTelemetryLabel(
   9450    Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE upgradeChanDisposition) {
   9451  if (upgradeChanDisposition ==
   9452      Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::cancel) {
   9453    return "cancel"_ns;
   9454  }
   9455  if (upgradeChanDisposition ==
   9456      Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::disk) {
   9457    return "disk"_ns;
   9458  }
   9459  if (upgradeChanDisposition ==
   9460      Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netOk) {
   9461    return "net_ok"_ns;
   9462  }
   9463  if (upgradeChanDisposition ==
   9464      Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netEarlyFail) {
   9465    return "net_early_fail"_ns;
   9466  }
   9467  if (upgradeChanDisposition ==
   9468      Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netLateFail) {
   9469    return "net_late_fail"_ns;
   9470  }
   9471 
   9472  MOZ_ASSERT_UNREACHABLE("Unknown value for upgradeChanDecomposition");
   9473  return "other"_ns;
   9474 }
   9475 
   9476 nsresult nsHttpChannel::LogConsoleError(const char* aTag) {
   9477  nsCOMPtr<nsIConsoleService> console(mozilla::components::Console::Service());
   9478  NS_ENSURE_TRUE(console, NS_ERROR_OUT_OF_MEMORY);
   9479 
   9480  nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo();
   9481  NS_ENSURE_TRUE(console, NS_ERROR_OUT_OF_MEMORY);
   9482  uint64_t innerWindowID = loadInfo->GetInnerWindowID();
   9483 
   9484  nsAutoString errorText;
   9485  nsresult rv = nsContentUtils::GetLocalizedString(
   9486      nsContentUtils::eNECKO_PROPERTIES, aTag, errorText);
   9487  NS_ENSURE_SUCCESS(rv, rv);
   9488 
   9489  nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
   9490  NS_ENSURE_TRUE(error, NS_ERROR_OUT_OF_MEMORY);
   9491 
   9492  rv =
   9493      error->InitWithSourceURI(errorText, mURI, 0, 0, nsIScriptError::errorFlag,
   9494                               "Invalid HTTP Status Lines"_ns, innerWindowID);
   9495  NS_ENSURE_SUCCESS(rv, rv);
   9496  console->LogMessage(error);
   9497  return NS_OK;
   9498 }
   9499 
   9500 static void RecordHTTPSUpgradeTelemetry(nsIURI* aURI, nsILoadInfo* aLoadInfo) {
   9501  // we record https telemetry only for top-level loads
   9502  if (aLoadInfo->GetExternalContentPolicyType() !=
   9503      ExtContentPolicy::TYPE_DOCUMENT) {
   9504    return;
   9505  }
   9506 
   9507  // exempt loopback addresses because we only want to record telemetry
   9508  // for actual web requests
   9509  if (nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI)) {
   9510    return;
   9511  }
   9512 
   9513  // todo: for now we don't record form submissions, only
   9514  // top-level document loads. Once Bug 1720500 is fixed, we can
   9515  // consider recording form submissions too.
   9516  if (aLoadInfo->GetIsFormSubmission()) {
   9517    return;
   9518  }
   9519 
   9520  bool isHTTPS = aURI->SchemeIs("https");
   9521 
   9522  nsILoadInfo::HTTPSUpgradeTelemetryType httpsTelemetry =
   9523      aLoadInfo->GetHttpsUpgradeTelemetry();
   9524  switch (httpsTelemetry) {
   9525    case nsILoadInfo::NOT_INITIALIZED: {
   9526      if (isHTTPS) {
   9527        // Bug 1912222: We should never encounter NOT_INITIALIZED values,
   9528        // though we still want to know whether those loads are HTTPS
   9529        // or not. Eventually we'll want to remove this if-clause
   9530        // again and only report "not_initialized".
   9531        mozilla::glean::networking::http_to_https_upgrade_reason
   9532            .Get("not_initialized_https"_ns)
   9533            .Add(1);
   9534        return;
   9535      }
   9536      mozilla::glean::networking::http_to_https_upgrade_reason
   9537          .Get("not_initialized"_ns)
   9538          .Add(1);
   9539      break;
   9540    }
   9541    case nsILoadInfo::NO_UPGRADE: {
   9542      if (isHTTPS) {
   9543        // Bug 1912222: We should rearely encounter NO_UPGRADE values, though
   9544        // we still want to ensure those are not HTTPS. Eventually we'll want
   9545        // to remove this if-clause again and only report "no_upgrade".
   9546        mozilla::glean::networking::http_to_https_upgrade_reason
   9547            .Get("no_upgrade_https"_ns)
   9548            .Add(1);
   9549        return;
   9550      }
   9551      mozilla::glean::networking::http_to_https_upgrade_reason
   9552          .Get("no_upgrade"_ns)
   9553          .Add(1);
   9554      break;
   9555    }
   9556    case nsILoadInfo::ALREADY_HTTPS:
   9557      mozilla::glean::networking::http_to_https_upgrade_reason
   9558          .Get("already_https"_ns)
   9559          .Add(1);
   9560      break;
   9561    case nsILoadInfo::HSTS:
   9562      mozilla::glean::networking::http_to_https_upgrade_reason.Get("hsts"_ns)
   9563          .Add(1);
   9564      break;
   9565    case nsILoadInfo::HTTPS_ONLY_UPGRADE:
   9566      mozilla::glean::networking::http_to_https_upgrade_reason
   9567          .Get("https_only_upgrade"_ns)
   9568          .Add(1);
   9569      break;
   9570    case nsILoadInfo::HTTPS_ONLY_UPGRADE_DOWNGRADE:
   9571      mozilla::glean::networking::http_to_https_upgrade_reason
   9572          .Get("https_only_upgrade_downgrade"_ns)
   9573          .Add(1);
   9574      break;
   9575    case nsILoadInfo::HTTPS_FIRST_UPGRADE:
   9576      mozilla::glean::networking::http_to_https_upgrade_reason
   9577          .Get("https_first_upgrade"_ns)
   9578          .Add(1);
   9579      break;
   9580    case nsILoadInfo::HTTPS_FIRST_UPGRADE_DOWNGRADE:
   9581      mozilla::glean::networking::http_to_https_upgrade_reason
   9582          .Get("https_first_upgrade_downgrade"_ns)
   9583          .Add(1);
   9584      break;
   9585    case nsILoadInfo::HTTPS_FIRST_SCHEMELESS_UPGRADE:
   9586      mozilla::glean::networking::http_to_https_upgrade_reason
   9587          .Get("https_first_schemeless_upgrade"_ns)
   9588          .Add(1);
   9589      break;
   9590    case nsILoadInfo::HTTPS_FIRST_SCHEMELESS_UPGRADE_DOWNGRADE:
   9591      mozilla::glean::networking::http_to_https_upgrade_reason
   9592          .Get("https_first_schemeless_upgrade_downgrade"_ns)
   9593          .Add(1);
   9594      break;
   9595    case nsILoadInfo::CSP_UIR:
   9596      mozilla::glean::networking::http_to_https_upgrade_reason.Get("csp_uir"_ns)
   9597          .Add(1);
   9598      break;
   9599    case nsILoadInfo::HTTPS_RR:
   9600      mozilla::glean::networking::http_to_https_upgrade_reason
   9601          .Get("https_rr"_ns)
   9602          .Add(1);
   9603      break;
   9604    case nsILoadInfo::WEB_EXTENSION_UPGRADE:
   9605      mozilla::glean::networking::http_to_https_upgrade_reason
   9606          .Get("web_extension_upgrade"_ns)
   9607          .Add(1);
   9608      break;
   9609    case nsILoadInfo::UPGRADE_EXCEPTION:
   9610      mozilla::glean::networking::http_to_https_upgrade_reason
   9611          .Get("upgrade_exception"_ns)
   9612          .Add(1);
   9613      break;
   9614    case nsILoadInfo::SKIP_HTTPS_UPGRADE:
   9615      mozilla::glean::networking::http_to_https_upgrade_reason
   9616          .Get("skip_upgrade"_ns)
   9617          .Add(1);
   9618      break;
   9619    default:
   9620      MOZ_ASSERT(false, "what telemetry flag is set to end up here?");
   9621  }
   9622 }
   9623 
   9624 static void RecordIPAddressSpaceTelemetry(bool aLoadSuccess, nsIURI* aURI,
   9625                                          nsILoadInfo* aLoadInfo,
   9626                                          NetAddr& aPeerAddr) {
   9627  // if the load was not successful, then there is nothing to record here
   9628  if (!aLoadSuccess) {
   9629    return;
   9630  }
   9631 
   9632  // we record https telemetry only for top-level loads
   9633  if (aLoadInfo->GetExternalContentPolicyType() !=
   9634      ExtContentPolicy::TYPE_DOCUMENT) {
   9635    return;
   9636  }
   9637 
   9638  if (aURI->SchemeIs("https")) {
   9639    mozilla::glean::networking::https_http_or_local.Get("load_is_https"_ns)
   9640        .Add(1);
   9641    return;
   9642  }
   9643 
   9644  if (aURI->SchemeIs("http")) {
   9645    if (aPeerAddr.IsIPAddrLocal() || aPeerAddr.IsLoopbackAddr()) {
   9646      mozilla::glean::networking::https_http_or_local
   9647          .Get("load_is_http_for_local_domain"_ns)
   9648          .Add(1);
   9649    } else {
   9650      mozilla::glean::networking::https_http_or_local.Get("load_is_http"_ns)
   9651          .Add(1);
   9652    }
   9653    return;
   9654  }
   9655 }
   9656 
   9657 static void RecordLNATelemetry(nsHttpChannel* aChannel, bool aLoadSuccess) {
   9658  // Extract data from channel
   9659  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   9660  nsCOMPtr<nsIURI> uri;
   9661  aChannel->GetURI(getter_AddRefs(uri));
   9662  NetAddr peerAddr = aChannel->GetPeerAddr();
   9663  const nsACString& promptAction = aChannel->GetLNAPromptAction();
   9664 
   9665  if (!loadInfo || !uri) {
   9666    return;
   9667  }
   9668 
   9669  RefPtr<mozilla::dom::BrowsingContext> bc;
   9670  loadInfo->GetBrowsingContext(getter_AddRefs(bc));
   9671 
   9672  nsILoadInfo::IPAddressSpace parentAddressSpace =
   9673      nsILoadInfo::IPAddressSpace::Unknown;
   9674  if (!bc) {
   9675    parentAddressSpace = loadInfo->GetParentIpAddressSpace();
   9676  } else {
   9677    parentAddressSpace = bc->GetCurrentIPAddressSpace();
   9678  }
   9679 
   9680  // Early return if NOT LNA - don't record telemetry or log
   9681  if (!mozilla::net::IsLocalOrPrivateNetworkAccess(
   9682          parentAddressSpace, loadInfo->GetIpAddressSpace())) {
   9683    return;
   9684  }
   9685 
   9686  if (aLoadSuccess) {
   9687    mozilla::glean::networking::local_network_access.Get("success"_ns).Add(1);
   9688  } else {
   9689    mozilla::glean::networking::local_network_access.Get("failure"_ns).Add(1);
   9690  }
   9691 
   9692  uint16_t port = 0;
   9693  if (NS_SUCCEEDED(peerAddr.GetPort(&port))) {
   9694    mozilla::glean::networking::local_network_access_port
   9695        .AccumulateSingleSample(port);
   9696  }
   9697 
   9698  // label format is <parentAddressSpace>_to_<targetAddressSpace>_<scheme>
   9699  // At this point we are sure that the request is a LNA,
   9700  // Hence we can safely assume few conditions to construct the label
   9701  nsAutoCString glean_lna_label;
   9702  if (parentAddressSpace == nsILoadInfo::IPAddressSpace::Public) {
   9703    glean_lna_label.Append("public_to_"_ns);
   9704  } else {
   9705    glean_lna_label.Append("private_to_"_ns);
   9706  }
   9707  if (loadInfo->GetIpAddressSpace() == nsILoadInfo::IPAddressSpace::Private) {
   9708    glean_lna_label.Append("private_"_ns);
   9709  } else {
   9710    glean_lna_label.Append("local_"_ns);
   9711  }
   9712  if (uri->SchemeIs("https")) {
   9713    glean_lna_label.Append("https"_ns);
   9714  } else {
   9715    glean_lna_label.Append("http"_ns);
   9716  }
   9717 
   9718  mozilla::glean::networking::local_network_access.Get(glean_lna_label).Add(1);
   9719 
   9720  // Get top-level site (main window principal) - TLD+1
   9721  nsAutoCString topLevelSite;
   9722  if (bc && bc->Top()) {
   9723    if (bc->Top()->Canonical()) {
   9724      RefPtr<mozilla::dom::WindowGlobalParent> topWindowGlobal =
   9725          bc->Top()->Canonical()->GetCurrentWindowGlobal();
   9726      if (topWindowGlobal) {
   9727        nsCOMPtr<nsIPrincipal> topPrincipal =
   9728            topWindowGlobal->DocumentPrincipal();
   9729        if (topPrincipal) {
   9730          (void)topPrincipal->GetBaseDomain(topLevelSite);
   9731        }
   9732      }
   9733    }
   9734  }
   9735 
   9736  // Get initiator (triggering principal) - TLD+1
   9737  nsAutoCString initiator;
   9738  nsCOMPtr<nsIPrincipal> triggeringPrincipal;
   9739  loadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal));
   9740  if (triggeringPrincipal) {
   9741    (void)triggeringPrincipal->GetBaseDomain(initiator);
   9742  }
   9743 
   9744  bool isSecureContext =
   9745      triggeringPrincipal
   9746          ? triggeringPrincipal->GetIsOriginPotentiallyTrustworthy()
   9747          : false;
   9748 
   9749  // Get target host - TLD+1
   9750  nsCOMPtr<nsIEffectiveTLDService> eTLDService =
   9751      mozilla::components::EffectiveTLD::Service();
   9752  nsAutoCString targetHost;
   9753  if (eTLDService) {
   9754    (void)eTLDService->GetBaseDomain(uri, 0, targetHost);
   9755  }
   9756 
   9757  // Get target IP address
   9758  nsCString targetIp = peerAddr.ToString();
   9759 
   9760  // Determine protocol from LoadInfo
   9761  nsAutoCString protocol;
   9762  ExtContentPolicyType contentType = loadInfo->GetExternalContentPolicyType();
   9763  switch (contentType) {
   9764    case ExtContentPolicyType::TYPE_WEBSOCKET:
   9765      protocol.AssignLiteral("websocket");
   9766      break;
   9767    case ExtContentPolicyType::TYPE_WEB_TRANSPORT:
   9768      protocol.AssignLiteral("webtransport");
   9769      break;
   9770    case ExtContentPolicyType::TYPE_FETCH:
   9771      protocol.AssignLiteral("fetch");
   9772      break;
   9773    case ExtContentPolicyType::TYPE_XMLHTTPREQUEST:
   9774      protocol.AssignLiteral("xhr");
   9775      break;
   9776    default:
   9777      if (uri->SchemeIs("https")) {
   9778        protocol.AssignLiteral("https");
   9779      } else {
   9780        protocol.AssignLiteral("http");
   9781      }
   9782      break;
   9783  }
   9784 
   9785  // Record the event
   9786  glean::networking::LocalNetworkAccessConnectionExtra extra = {
   9787      .initiator = initiator.IsEmpty() ? Nothing() : Some(initiator),
   9788      .isSecureContext = Some(isSecureContext),
   9789      .loadSuccess = Some(aLoadSuccess),
   9790      .promptAction =
   9791          promptAction.IsEmpty() ? Nothing() : Some(nsCString(promptAction)),
   9792      .protocol = Some(protocol),
   9793      .targetHost = targetHost.IsEmpty() ? Nothing() : Some(targetHost),
   9794      .targetIp = Some(targetIp),
   9795      .targetPort = Some(port),
   9796      .topLevelSite = topLevelSite.IsEmpty() ? Nothing() : Some(topLevelSite),
   9797 
   9798  };
   9799  glean::networking::local_network_access_connection.Record(Some(extra));
   9800 
   9801  ReportLNAAccessToConsole(aChannel, "LocalNetworkAccessDetected",
   9802                           promptAction);
   9803 }
   9804 
   9805 NS_IMETHODIMP
   9806 nsHttpChannel::OnStopRequest(nsIRequest* request, nsresult status) {
   9807  MOZ_ASSERT(!mAsyncOpenTime.IsNull());
   9808  AUTO_PROFILER_LABEL("nsHttpChannel::OnStopRequest", NETWORK);
   9809  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnStopRequest", NETWORK,
   9810                            Flow::FromPointer(this));
   9811 
   9812  LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%" PRIx32 "]\n",
   9813       this, request, static_cast<uint32_t>(status)));
   9814 
   9815  LOG(("OnStopRequest %p requestFromCache: %d mFirstResponseSource: %d\n", this,
   9816       request == mCachePump, static_cast<int32_t>(mFirstResponseSource)));
   9817 
   9818  MOZ_ASSERT(NS_IsMainThread(),
   9819             "OnStopRequest should only be called from the main thread");
   9820 
   9821  if (mStatus == NS_ERROR_PARSING_HTTP_STATUS_LINE) {
   9822    (void)LogConsoleError("InvalidHTTPResponseStatusLine");
   9823  }
   9824 
   9825  if (WRONG_RACING_RESPONSE_SOURCE(request)) {
   9826    return NS_OK;
   9827  }
   9828 
   9829  // It's possible that LoadUseHTTPSSVC() is false, but we already have
   9830  // mHTTPSSVCRecord.
   9831  if (LoadUseHTTPSSVC() || mHTTPSSVCRecord) {
   9832    ReportHTTPSRRTelemetry(mHTTPSSVCRecord);
   9833  }
   9834 
   9835  // If this load failed because of a security error, it may be because we
   9836  // are in a captive portal - trigger an async check to make sure.
   9837  int32_t nsprError = -1 * NS_ERROR_GET_CODE(status);
   9838  if (mozilla::psm::IsNSSErrorCode(nsprError) && IsHTTPS()) {
   9839    gIOService->RecheckCaptivePortal();
   9840  }
   9841 
   9842  if (request == mCachePump) {
   9843    mCacheReadEnd = TimeStamp::Now();
   9844  }
   9845 
   9846  // allow content to be cached if it was loaded successfully (bug #482935)
   9847  bool contentComplete = NS_SUCCEEDED(status);
   9848 
   9849  // honor the cancelation status even if the underlying transaction
   9850  // completed.
   9851  if (mCanceled || NS_FAILED(mStatus)) status = mStatus;
   9852 
   9853  if (LoadCachedContentIsPartial()) {
   9854    if (NS_SUCCEEDED(status)) {
   9855      // mTransactionPump should be suspended
   9856      MOZ_ASSERT(request != mTransactionPump,
   9857                 "byte-range transaction finished prematurely");
   9858 
   9859      if (request == mCachePump) {
   9860        bool streamDone;
   9861        status = OnDoneReadingPartialCacheEntry(&streamDone);
   9862        if (NS_SUCCEEDED(status) && !streamDone) return status;
   9863        // otherwise, fall through and fire OnStopRequest...
   9864      } else if (request == mTransactionPump) {
   9865        MOZ_ASSERT(LoadConcurrentCacheAccess());
   9866      } else {
   9867        MOZ_ASSERT_UNREACHABLE("unexpected request");
   9868      }
   9869    }
   9870    // Do not to leave the transaction in a suspended state in error cases.
   9871    if (NS_FAILED(status) && mTransaction) {
   9872      nsresult rv = gHttpHandler->CancelTransaction(mTransaction, status);
   9873      if (NS_FAILED(rv)) {
   9874        LOG(("  CancelTransaction failed (%08x)", static_cast<uint32_t>(rv)));
   9875      }
   9876    }
   9877  }
   9878 
   9879  nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener);
   9880  if (conv) {
   9881    conv->GetDecodedDataLength(&mDecodedBodySize);
   9882  }
   9883 
   9884  bool isFromNet = request == mTransactionPump;
   9885 
   9886  if (mTransaction) {
   9887    // determine if we should call DoAuthRetry
   9888    bool authRetry = (mAuthRetryPending && NS_SUCCEEDED(status) &&
   9889                      // we should only auth retry in this channel if are not
   9890                      // redirecting a new channel for authentication retries
   9891                      !StaticPrefs::network_auth_use_redirect_for_retries());
   9892 
   9893    StoreStronglyFramed(mTransaction->ResponseIsComplete());
   9894    LOG(("nsHttpChannel %p has a strongly framed transaction: %d", this,
   9895         LoadStronglyFramed()));
   9896 
   9897    // Save the reference of |mTransaction| to |transactionWithStickyConn|
   9898    // when it has a sticky connection.
   9899    // In the case we need to retry an authentication request, we need to
   9900    // reuse the connection of |transactionWithStickyConn|.
   9901    RefPtr<HttpTransactionShell> transactionWithStickyConn;
   9902    if (mCaps & NS_HTTP_STICKY_CONNECTION ||
   9903        mTransaction->HasStickyConnection()) {
   9904      transactionWithStickyConn = mTransaction;
   9905      // Make sure we use the updated caps and connection info from transaction.
   9906      // We read these values when the transaction is already closed, so there
   9907      // should be no race.
   9908      if (mTransaction->Http2Disabled()) {
   9909        mCaps |= NS_HTTP_DISALLOW_SPDY;
   9910      }
   9911      if (mTransaction->Http3Disabled()) {
   9912        mCaps |= NS_HTTP_DISALLOW_HTTP3;
   9913      }
   9914      mConnectionInfo = mTransaction->GetConnInfo();
   9915      LOG(("  transaction %p has sticky connection",
   9916           transactionWithStickyConn.get()));
   9917    }
   9918 
   9919    // this code relies on the code in nsHttpTransaction::Close, which
   9920    // tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to
   9921    // keep the connection around after the transaction is finished.
   9922    //
   9923    LOG(("  mAuthRetryPending=%d, status=%" PRIx32 ", sticky conn cap=%d",
   9924         static_cast<bool>(mAuthRetryPending), static_cast<uint32_t>(status),
   9925         mCaps & NS_HTTP_STICKY_CONNECTION));
   9926    // We must check caps for stickinness also on the transaction because it
   9927    // might have been updated by the transaction itself during inspection of
   9928    // the reposnse headers yet on the socket thread (found connection based
   9929    // auth schema).
   9930 
   9931    if ((NS_FAILED(status)) && transactionWithStickyConn) {
   9932      // Close (don't reuse) the sticky connection if this channel has been
   9933      // cancelled. There are proxy servers known to get confused when we send
   9934      // a new request over such a half-stated connection.
   9935      if (!LoadAuthConnectionRestartable()) {
   9936        LOG(("  not reusing a half-authenticated sticky connection"));
   9937        transactionWithStickyConn->DontReuseConnection();
   9938      }
   9939    }
   9940 
   9941    if (mCaps & NS_HTTP_STICKY_CONNECTION) {
   9942      mTransaction->SetH2WSConnRefTaken();
   9943    }
   9944 
   9945    mTransferSize = mTransaction->GetTransferSize();
   9946    mRequestSize = mTransaction->GetRequestSize();
   9947 
   9948    RecordHTTPSUpgradeTelemetry(mURI, mLoadInfo);
   9949 
   9950    RecordIPAddressSpaceTelemetry(NS_SUCCEEDED(mStatus), mURI, mLoadInfo,
   9951                                  mPeerAddr);
   9952    RecordLNATelemetry(this, NS_SUCCEEDED(mStatus));
   9953 
   9954    uint32_t flags;
   9955    if (mStatus == NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED &&
   9956        StaticPrefs::network_lna_block_trackers() &&
   9957        NS_SUCCEEDED(
   9958            mLoadInfo->GetTriggeringThirdPartyClassificationFlags(&flags)) &&
   9959        flags != 0) {
   9960      mozilla::glean::networking::local_network_blocked_tracker.Add(1);
   9961    }
   9962 
   9963    // If we are using the transaction to serve content, we also save the
   9964    // time since async open in the cache entry so we can compare telemetry
   9965    // between cache and net response.
   9966    // Do not store the time of conditional requests because even if we
   9967    // fetch the data from the server, the time includes loading of the old
   9968    // cache entry which would skew the network load time.
   9969    if (request == mTransactionPump && mCacheEntry && !mDidReval &&
   9970        !LoadCustomConditionalRequest() && !mAsyncOpenTime.IsNull() &&
   9971        !mOnStartRequestTimestamp.IsNull()) {
   9972      uint64_t onStartTime =
   9973          (mOnStartRequestTimestamp - mAsyncOpenTime).ToMilliseconds();
   9974      uint64_t onStopTime =
   9975          (TimeStamp::Now() - mAsyncOpenTime).ToMilliseconds();
   9976      (void)mCacheEntry->SetNetworkTimes(onStartTime, onStopTime);
   9977    }
   9978 
   9979    mResponseTrailers = mTransaction->TakeResponseTrailers();
   9980 
   9981    if (nsIOService::UseSocketProcess() && mTransaction) {
   9982      mOnStopRequestStartTime = mTransaction->GetOnStopRequestStartTime();
   9983      if (!mOnStopRequestStartTime.IsNull()) {
   9984        PerfStats::RecordMeasurement(
   9985            PerfStats::Metric::OnStopRequestSocketToParent,
   9986            TimeStamp::Now() - mOnStopRequestStartTime);
   9987      }
   9988    } else {
   9989      mOnStopRequestStartTime = TimeStamp::Now();
   9990    }
   9991 
   9992    // at this point, we're done with the transaction
   9993    mTransactionTimings = mTransaction->Timings();
   9994    mTransaction = nullptr;
   9995    mTransactionPump = nullptr;
   9996 
   9997    // We no longer need the dns prefetch object
   9998    if (mDNSPrefetch && mDNSPrefetch->TimingsValid() &&
   9999        !mTransactionTimings.requestStart.IsNull() &&
  10000        !mTransactionTimings.connectStart.IsNull() &&
  10001        mDNSPrefetch->EndTimestamp() <= mTransactionTimings.connectStart) {
  10002      // We only need the domainLookup timestamps when not using a
  10003      // persistent connection, meaning if the endTimestamp < connectStart
  10004      mTransactionTimings.domainLookupStart = mDNSPrefetch->StartTimestamp();
  10005      mTransactionTimings.domainLookupEnd = mDNSPrefetch->EndTimestamp();
  10006    }
  10007    mDNSPrefetch = nullptr;
  10008 
  10009    // handle auth retry...
  10010    if (authRetry) {
  10011      mAuthRetryPending = false;
  10012      auto continueOSR = [authRetry, isFromNet, contentComplete,
  10013                          transactionWithStickyConn](auto* self,
  10014                                                     nsresult aStatus) {
  10015        return self->ContinueOnStopRequestAfterAuthRetry(
  10016            aStatus, authRetry, isFromNet, contentComplete,
  10017            transactionWithStickyConn);
  10018      };
  10019      status = DoAuthRetry(transactionWithStickyConn, continueOSR);
  10020      if (NS_SUCCEEDED(status)) {
  10021        return NS_OK;
  10022      }
  10023    }
  10024    return ContinueOnStopRequestAfterAuthRetry(status, authRetry, isFromNet,
  10025                                               contentComplete,
  10026                                               transactionWithStickyConn);
  10027  }
  10028 
  10029  return ContinueOnStopRequest(status, isFromNet, contentComplete);
  10030 }
  10031 
  10032 nsresult nsHttpChannel::ContinueOnStopRequestAfterAuthRetry(
  10033    nsresult aStatus, bool aAuthRetry, bool aIsFromNet, bool aContentComplete,
  10034    HttpTransactionShell* aTransWithStickyConn) {
  10035  LOG(
  10036      ("nsHttpChannel::ContinueOnStopRequestAfterAuthRetry "
  10037       "[this=%p, aStatus=%" PRIx32
  10038       " aAuthRetry=%d, aIsFromNet=%d, aTransWithStickyConn=%p]\n",
  10039       this, static_cast<uint32_t>(aStatus), aAuthRetry, aIsFromNet,
  10040       aTransWithStickyConn));
  10041 
  10042  if (aAuthRetry && NS_SUCCEEDED(aStatus)) {
  10043    return NS_OK;
  10044  }
  10045 
  10046  // If DoAuthRetry failed, or if we have been cancelled since showing
  10047  // the auth. dialog, then we need to send OnStartRequest now
  10048  if (aAuthRetry || (mAuthRetryPending && NS_FAILED(aStatus))) {
  10049    MOZ_ASSERT(NS_FAILED(aStatus), "should have a failure code here");
  10050    // NOTE: since we have a failure status, we can ignore the return
  10051    // value from onStartRequest.
  10052    LOG(("  calling mListener->OnStartRequest [this=%p, listener=%p]\n", this,
  10053         mListener.get()));
  10054    if (mListener) {
  10055      MOZ_ASSERT(!LoadOnStartRequestCalled(),
  10056                 "We should not call OnStartRequest twice.");
  10057      if (!LoadOnStartRequestCalled()) {
  10058        nsCOMPtr<nsIStreamListener> listener(mListener);
  10059        StoreOnStartRequestCalled(true);
  10060        listener->OnStartRequest(this);
  10061      }
  10062    } else {
  10063      StoreOnStartRequestCalled(true);
  10064      NS_WARNING("OnStartRequest skipped because of null listener");
  10065    }
  10066    mAuthRetryPending = false;
  10067  }
  10068 
  10069  // if this transaction has been replaced, then bail.
  10070  if (LoadTransactionReplaced()) {
  10071    LOG(("Transaction replaced\n"));
  10072    // This was just the network check for a 304 response.
  10073    mFirstResponseSource = RESPONSE_PENDING;
  10074    return NS_OK;
  10075  }
  10076 
  10077  bool upgradeWebsocket = mUpgradeProtocolCallback && aTransWithStickyConn &&
  10078                          mResponseHead &&
  10079                          ((mResponseHead->Status() == 101 &&
  10080                            mResponseHead->Version() == HttpVersion::v1_1) ||
  10081                           (mResponseHead->Status() == 200 &&
  10082                            mResponseHead->Version() == HttpVersion::v2_0));
  10083 
  10084  bool upgradeConnect = mUpgradeProtocolCallback && aTransWithStickyConn &&
  10085                        (mCaps & NS_HTTP_CONNECT_ONLY) && mResponseHead &&
  10086                        mResponseHead->Status() == 200;
  10087 
  10088  if (upgradeWebsocket || upgradeConnect) {
  10089    if (nsIOService::UseSocketProcess() && upgradeConnect) {
  10090      // TODO: Support connection upgrade for socket process in bug 1632809.
  10091      (void)mUpgradeProtocolCallback->OnUpgradeFailed(NS_ERROR_NOT_IMPLEMENTED);
  10092      return ContinueOnStopRequest(aStatus, aIsFromNet, aContentComplete);
  10093    }
  10094 
  10095    nsresult rv = gHttpHandler->CompleteUpgrade(aTransWithStickyConn,
  10096                                                mUpgradeProtocolCallback);
  10097    mUpgradeProtocolCallback = nullptr;
  10098    if (NS_FAILED(rv)) {
  10099      LOG(("  CompleteUpgrade failed with %" PRIx32,
  10100           static_cast<uint32_t>(rv)));
  10101 
  10102      // This ensures that WebSocketChannel::OnStopRequest will be
  10103      // called with an error so the session is properly aborted.
  10104      aStatus = rv;
  10105    }
  10106  }
  10107 
  10108  return ContinueOnStopRequest(aStatus, aIsFromNet, aContentComplete);
  10109 }
  10110 
  10111 nsresult nsHttpChannel::ContinueOnStopRequest(nsresult aStatus, bool aIsFromNet,
  10112                                              bool aContentComplete) {
  10113  LOG(
  10114      ("nsHttpChannel::ContinueOnStopRequest "
  10115       "[this=%p aStatus=%" PRIx32 ", aIsFromNet=%d]\n",
  10116       this, static_cast<uint32_t>(aStatus), aIsFromNet));
  10117 
  10118  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::ContinueOnStopRequest", NETWORK,
  10119                            Flow::FromPointer(this));
  10120  // HTTP_CHANNEL_DISPOSITION TELEMETRY
  10121  ChannelDisposition chanDisposition = kHttpCanceled;
  10122  // HTTP_CHANNEL_DISPOSITION_UPGRADE TELEMETRY
  10123  Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE upgradeChanDisposition =
  10124      Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::cancel;
  10125 
  10126  // HTTP 0.9 is more likely to be an error than really 0.9, so count it that
  10127  // way
  10128  if (mCanceled) {
  10129    chanDisposition = kHttpCanceled;
  10130    upgradeChanDisposition =
  10131        Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::cancel;
  10132  } else if (!LoadUsedNetwork() ||
  10133             (mRaceCacheWithNetwork &&
  10134              mFirstResponseSource == RESPONSE_FROM_CACHE)) {
  10135    chanDisposition = kHttpDisk;
  10136    upgradeChanDisposition =
  10137        Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::disk;
  10138  } else if (NS_SUCCEEDED(aStatus) && mResponseHead &&
  10139             mResponseHead->Version() != HttpVersion::v0_9) {
  10140    chanDisposition = kHttpNetOK;
  10141    upgradeChanDisposition =
  10142        Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netOk;
  10143  } else if (!mTransferSize) {
  10144    chanDisposition = kHttpNetEarlyFail;
  10145    upgradeChanDisposition =
  10146        Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netEarlyFail;
  10147  } else {
  10148    chanDisposition = kHttpNetLateFail;
  10149    upgradeChanDisposition =
  10150        Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netLateFail;
  10151  }
  10152  // Browser upgrading only happens on HTTPS pages for mixed passive content
  10153  // when upgrading is enabled.
  10154  nsCString upgradeKey;
  10155  nsLiteralCString upgradeChanDispositionLabel =
  10156      HttpChanDispositionToTelemetryLabel(upgradeChanDisposition);
  10157  if (IsHTTPS()) {
  10158    // Browser upgrading is disabled and the content is already HTTPS
  10159    upgradeKey = "disabledNoReason"_ns;
  10160    // Checks "security.mixed_content.upgrade_display_content" is true
  10161    if (StaticPrefs::security_mixed_content_upgrade_display_content()) {
  10162      if (mLoadInfo->GetBrowserUpgradeInsecureRequests()) {
  10163        // HTTP content the browser has upgraded to HTTPS
  10164        mozilla::glean::networking::http_channel_disposition_enabled_upgrade
  10165            .Get(upgradeChanDispositionLabel)
  10166            .Add(1);
  10167        upgradeKey = "enabledUpgrade"_ns;
  10168      } else {
  10169        // Content wasn't upgraded but is already HTTPS
  10170        mozilla::glean::networking::http_channel_disposition_enabled_no_reason
  10171            .Get(upgradeChanDispositionLabel)
  10172            .Add(1);
  10173        upgradeKey = "enabledNoReason"_ns;
  10174      }
  10175    } else {
  10176      mozilla::glean::networking::http_channel_disposition_disabled_no_reason
  10177          .Get(upgradeChanDispositionLabel)
  10178          .Add(1);
  10179    }
  10180    // shift http to https disposition enums
  10181    chanDisposition =
  10182        static_cast<ChannelDisposition>(chanDisposition + kHttpsCanceled);
  10183  } else if (mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
  10184    // HTTP content the browser would upgrade to HTTPS if upgrading was
  10185    // enabled
  10186    mozilla::glean::networking::http_channel_disposition_disabled_upgrade
  10187        .Get(upgradeChanDispositionLabel)
  10188        .Add(1);
  10189    upgradeKey = "disabledUpgrade"_ns;
  10190  } else if (StaticPrefs::security_mixed_content_upgrade_display_content()) {
  10191    // HTTP content that wouldn't upgrade
  10192    mozilla::glean::networking::http_channel_disposition_enabled_wont
  10193        .Get(upgradeChanDispositionLabel)
  10194        .Add(1);
  10195    upgradeKey = "enabledWont"_ns;
  10196  } else {
  10197    mozilla::glean::networking::http_channel_disposition_disabled_wont
  10198        .Get(upgradeChanDispositionLabel)
  10199        .Add(1);
  10200    upgradeKey = "disabledWont"_ns;
  10201  }
  10202 
  10203  glean::networking::http_channel_disposition_upgrade
  10204      .Get(upgradeKey, upgradeChanDispositionLabel)
  10205      .Add();
  10206 
  10207  LOG(("  nsHttpChannel::OnStopRequest ChannelDisposition %d\n",
  10208       chanDisposition));
  10209  glean::http::channel_disposition.AccumulateSingleSample(chanDisposition);
  10210  RecordHttpChanDispositionGlean(chanDisposition);
  10211 
  10212  // Collect specific telemetry for measuring image, video, audio
  10213  // success/failure rates in regular browsing mode and when auto upgrading of
  10214  // subresources is enabled. Note that we only evaluate actual image types, not
  10215  // favicons.
  10216  nsContentPolicyType internalLoadType;
  10217  mLoadInfo->GetInternalContentPolicyType(&internalLoadType);
  10218  bool statusIsSuccess = NS_SUCCEEDED(aStatus);
  10219  if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_IMAGE ||
  10220      internalLoadType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD) {
  10221    if (mLoadInfo->GetBrowserDidUpgradeInsecureRequests()) {
  10222      glean::mixed_content::images
  10223          .EnumGet(statusIsSuccess
  10224                       ? glean::mixed_content::ImagesLabel::eImgupsuccess
  10225                       : glean::mixed_content::ImagesLabel::eImgupfailure)
  10226          .Add();
  10227    } else {
  10228      glean::mixed_content::images
  10229          .EnumGet(statusIsSuccess
  10230                       ? glean::mixed_content::ImagesLabel::eImgnoupsuccess
  10231                       : glean::mixed_content::ImagesLabel::eImgnoupfailure)
  10232          .Add();
  10233    }
  10234  }
  10235  if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_VIDEO) {
  10236    if (mLoadInfo->GetBrowserDidUpgradeInsecureRequests()) {
  10237      glean::mixed_content::video
  10238          .EnumGet(statusIsSuccess
  10239                       ? glean::mixed_content::VideoLabel::eVideoupsuccess
  10240                       : glean::mixed_content::VideoLabel::eVideoupfailure)
  10241          .Add();
  10242    } else {
  10243      glean::mixed_content::video
  10244          .EnumGet(statusIsSuccess
  10245                       ? glean::mixed_content::VideoLabel::eVideonoupsuccess
  10246                       : glean::mixed_content::VideoLabel::eVideonoupfailure)
  10247          .Add();
  10248    }
  10249  }
  10250  if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_AUDIO) {
  10251    if (mLoadInfo->GetBrowserDidUpgradeInsecureRequests()) {
  10252      glean::mixed_content::audio
  10253          .EnumGet(statusIsSuccess
  10254                       ? glean::mixed_content::AudioLabel::eAudioupsuccess
  10255                       : glean::mixed_content::AudioLabel::eAudioupfailure)
  10256          .Add();
  10257    } else {
  10258      glean::mixed_content::audio
  10259          .EnumGet(statusIsSuccess
  10260                       ? glean::mixed_content::AudioLabel::eAudionoupsuccess
  10261                       : glean::mixed_content::AudioLabel::eAudionoupfailure)
  10262          .Add();
  10263    }
  10264  }
  10265 
  10266  // if needed, check cache entry has all data we expect
  10267  if (mCacheEntry && mCachePump && LoadConcurrentCacheAccess() &&
  10268      aContentComplete) {
  10269    int64_t size, contentLength;
  10270    nsresult rv = CheckPartial(mCacheEntry, &size, &contentLength);
  10271    if (NS_SUCCEEDED(rv)) {
  10272      if (size == int64_t(-1)) {
  10273        // mayhemer TODO - we have to restart read from cache here at the size
  10274        // offset
  10275        MOZ_ASSERT(false);
  10276        LOG(
  10277            ("  cache entry write is still in progress, but we just "
  10278             "finished reading the cache entry"));
  10279      } else if (contentLength != int64_t(-1) && contentLength != size) {
  10280        LOG(("  concurrent cache entry write has been interrupted"));
  10281        mCachedResponseHead = std::move(mResponseHead);
  10282        // Ignore zero partial length because we also want to resume when
  10283        // no data at all has been read from the cache.
  10284        rv = MaybeSetupByteRangeRequest(size, contentLength, true);
  10285        if (NS_SUCCEEDED(rv) && LoadIsPartialRequest()) {
  10286          // Prevent read from cache again
  10287          StoreCachedContentIsValid(CachedContentValidity::Invalid);
  10288          StoreCachedContentIsPartial(1);
  10289 
  10290          // We are about to perform a different network request.
  10291          // We must set mRaceCacheWithNetwork to false because otherwise
  10292          // we would ignore the network response thinking we didn't need it.
  10293          mRaceCacheWithNetwork = false;
  10294 
  10295          // Perform the range request
  10296          rv = ContinueConnect();
  10297          if (NS_SUCCEEDED(rv)) {
  10298            LOG(("  performing range request"));
  10299            mCachePump = nullptr;
  10300            return NS_OK;
  10301          }
  10302          LOG(("  but range request perform failed 0x%08" PRIx32,
  10303               static_cast<uint32_t>(rv)));
  10304          aStatus = NS_ERROR_NET_INTERRUPT;
  10305        } else {
  10306          LOG(("  but range request setup failed rv=0x%08" PRIx32
  10307               ", failing load",
  10308               static_cast<uint32_t>(rv)));
  10309        }
  10310      }
  10311    }
  10312  }
  10313 
  10314  StoreIsPending(false);
  10315  mStatus = aStatus;
  10316 
  10317  // perform any final cache operations before we close the cache entry.
  10318  if (mCacheEntry && LoadRequestTimeInitialized()) {
  10319    // New implementation just returns value of the !LoadCacheEntryIsReadOnly()
  10320    // flag passed in. Old implementation checks on nsICache::ACCESS_WRITE
  10321    // flag.
  10322 
  10323    // Assume that write access is granted
  10324    if (!LoadCacheEntryIsReadOnly()) {
  10325      nsresult rv = FinalizeCacheEntry();
  10326      if (NS_FAILED(rv)) {
  10327        LOG(("FinalizeCacheEntry failed (%08x)", static_cast<uint32_t>(rv)));
  10328      }
  10329    }
  10330  }
  10331 
  10332  ReportRcwnStats(aIsFromNet);
  10333 
  10334  // Register entry to the PerformanceStorage resource timing
  10335  MaybeReportTimingData();
  10336 
  10337  MaybeFlushConsoleReports();
  10338 
  10339  if (!mEndMarkerAdded && profiler_thread_is_being_profiled_for_markers()) {
  10340    // These do allocations/frees/etc; avoid if not active
  10341    mEndMarkerAdded = true;
  10342 
  10343    nsAutoCString requestMethod;
  10344    GetRequestMethod(requestMethod);
  10345 
  10346    int32_t priority = PRIORITY_NORMAL;
  10347    GetPriority(&priority);
  10348 
  10349    uint64_t size = 0;
  10350    GetEncodedBodySize(&size);
  10351 
  10352    nsAutoCString contentType;
  10353    mozilla::Maybe<mozilla::net::HttpVersion> httpVersion = Nothing();
  10354    mozilla::Maybe<uint32_t> responseStatus = Nothing();
  10355    if (mResponseHead) {
  10356      mResponseHead->ContentType(contentType);
  10357      httpVersion = Some(mResponseHead->Version());
  10358      responseStatus = Some(mResponseHead->Status());
  10359    }
  10360 
  10361    profiler_add_network_marker(
  10362        mURI, requestMethod, priority, mChannelId, NetworkLoadType::LOAD_STOP,
  10363        mLastStatusReported, TimeStamp::Now(), size, mCacheDisposition,
  10364        mLoadInfo->GetInnerWindowID(),
  10365        mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus,
  10366        &mTransactionTimings, std::move(mSource), httpVersion, responseStatus,
  10367        Some(nsDependentCString(contentType.get())));
  10368  }
  10369 
  10370  if (mAuthRetryPending &&
  10371      StaticPrefs::network_auth_use_redirect_for_retries()) {
  10372    nsresult rv = OpenRedirectChannel(aStatus);
  10373    LOG(("Opening redirect channel for auth retry %x",
  10374         static_cast<uint32_t>(rv)));
  10375    if (NS_FAILED(rv)) {
  10376      if (mListener) {
  10377        MOZ_ASSERT(!LoadOnStartRequestCalled(),
  10378                   "We should not call OnStartRequest twice.");
  10379        if (!LoadOnStartRequestCalled()) {
  10380          nsCOMPtr<nsIStreamListener> listener(mListener);
  10381          StoreOnStartRequestCalled(true);
  10382          listener->OnStartRequest(this);
  10383        }
  10384      } else {
  10385        StoreOnStartRequestCalled(true);
  10386        NS_WARNING("OnStartRequest skipped because of null listener");
  10387      }
  10388    }
  10389    mAuthRetryPending = false;
  10390  }
  10391 
  10392  if (LoadUsedNetwork() && !mReportedNEL) {
  10393    MaybeGenerateNELReport();
  10394  }
  10395 
  10396  // notify "http-on-before-stop-request" observers
  10397  gHttpHandler->OnBeforeStopRequest(this);
  10398 
  10399  if (mListener) {
  10400    LOG(("nsHttpChannel %p calling OnStopRequest\n", this));
  10401    MOZ_ASSERT(LoadOnStartRequestCalled(),
  10402               "OnStartRequest should be called before OnStopRequest");
  10403    MOZ_ASSERT(!LoadOnStopRequestCalled(),
  10404               "We should not call OnStopRequest twice");
  10405    StoreOnStopRequestCalled(true);
  10406    mListener->OnStopRequest(this, aStatus);
  10407  }
  10408  StoreOnStopRequestCalled(true);
  10409 
  10410  // The prefetch needs to be released on the main thread
  10411  mDNSPrefetch = nullptr;
  10412 
  10413  mRedirectChannel = nullptr;
  10414 
  10415  // notify "http-on-stop-request" observers
  10416  gHttpHandler->OnStopRequest(this);
  10417 
  10418  RemoveAsNonTailRequest();
  10419 
  10420  if (mChannelBlockedByOpaqueResponse && mCachedOpaqueResponseBlockingPref) {
  10421    mResponseHead->ClearHeaders();
  10422  }
  10423  // If a preferred alt-data type was set, this signals the consumer is
  10424  // interested in reading and/or writing the alt-data representation.
  10425  // We need to hold a reference to the cache entry in case the listener calls
  10426  // openAlternativeOutputStream() after CloseCacheEntry() clears mCacheEntry.
  10427  if (!mPreferredCachedAltDataTypes.IsEmpty()) {
  10428    mAltDataCacheEntry = mCacheEntry;
  10429  }
  10430 
  10431  CloseCacheEntry(!aContentComplete);
  10432  mWritingToCache = false;
  10433 
  10434  if (mLoadGroup) {
  10435    mLoadGroup->RemoveRequest(this, nullptr, aStatus);
  10436  }
  10437 
  10438  // We don't need this info anymore
  10439  CleanRedirectCacheChainIfNecessary();
  10440 
  10441  ReleaseListeners();
  10442 
  10443  // Release mUploadStream to free some memory sooner.
  10444  // We release this in background thread to avoid blocking I/O operations on
  10445  // main thread See Bug 1940224
  10446  (void)NS_DispatchBackgroundTask(NS_NewRunnableFunction(
  10447      "release HttpBaseChannel::mUploadStream",
  10448      [uploadStream = std::move(mUploadStream)]() { (void)uploadStream; }));
  10449 
  10450  return NS_OK;
  10451 }
  10452 
  10453 //-----------------------------------------------------------------------------
  10454 // nsHttpChannel::nsIStreamListener
  10455 //-----------------------------------------------------------------------------
  10456 
  10457 class OnTransportStatusAsyncEvent : public Runnable {
  10458 public:
  10459  OnTransportStatusAsyncEvent(nsITransportEventSink* aEventSink,
  10460                              nsresult aTransportStatus, int64_t aProgress,
  10461                              int64_t aProgressMax)
  10462      : Runnable("net::OnTransportStatusAsyncEvent"),
  10463        mEventSink(aEventSink),
  10464        mTransportStatus(aTransportStatus),
  10465        mProgress(aProgress),
  10466        mProgressMax(aProgressMax) {
  10467    MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be created on main thread");
  10468  }
  10469 
  10470  NS_IMETHOD Run() override {
  10471    MOZ_ASSERT(NS_IsMainThread(), "Should run on main thread");
  10472    if (mEventSink) {
  10473      mEventSink->OnTransportStatus(nullptr, mTransportStatus, mProgress,
  10474                                    mProgressMax);
  10475    }
  10476    return NS_OK;
  10477  }
  10478 
  10479 private:
  10480  nsCOMPtr<nsITransportEventSink> mEventSink;
  10481  nsresult mTransportStatus;
  10482  int64_t mProgress;
  10483  int64_t mProgressMax;
  10484 };
  10485 
  10486 NS_IMETHODIMP
  10487 nsHttpChannel::OnDataAvailable(nsIRequest* request, nsIInputStream* input,
  10488                               uint64_t offset, uint32_t count) {
  10489  nsresult rv;
  10490  AUTO_PROFILER_LABEL("nsHttpChannel::OnDataAvailable", NETWORK);
  10491  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnDataAvailable", NETWORK,
  10492                            Flow::FromPointer(this));
  10493 
  10494  LOG(("nsHttpChannel::OnDataAvailable [this=%p request=%p offset=%" PRIu64
  10495       " count=%" PRIu32 "]\n",
  10496       this, request, offset, count));
  10497 
  10498  LOG(("  requestFromCache: %d mFirstResponseSource: %d\n",
  10499       request == mCachePump, static_cast<int32_t>(mFirstResponseSource)));
  10500 
  10501  // don't send out OnDataAvailable notifications if we've been canceled.
  10502  if (mCanceled) return mStatus;
  10503 
  10504  if (mAuthRetryPending || WRONG_RACING_RESPONSE_SOURCE(request) ||
  10505      (request == mTransactionPump && LoadTransactionReplaced())) {
  10506    uint32_t n;
  10507    return input->ReadSegments(NS_DiscardSegment, nullptr, count, &n);
  10508  }
  10509 
  10510  MOZ_ASSERT(mResponseHead, "No response head in ODA!!");
  10511 
  10512  MOZ_ASSERT(!(LoadCachedContentIsPartial() && (request == mTransactionPump)),
  10513             "transaction pump not suspended");
  10514 
  10515  mIsReadingFromCache = (request == mCachePump);
  10516 
  10517  if (mListener) {
  10518    //
  10519    // synthesize transport progress event.  we do this here since we want
  10520    // to delay OnProgress events until we start streaming data.  this is
  10521    // crucially important since it impacts the lock icon (see bug 240053).
  10522    //
  10523    nsresult transportStatus;
  10524    if (request == mCachePump) {
  10525      transportStatus = NS_NET_STATUS_READING;
  10526    } else {
  10527      transportStatus = NS_NET_STATUS_RECEIVING_FROM;
  10528    }
  10529 
  10530    // mResponseHead may reference new or cached headers, but either way it
  10531    // holds our best estimate of the total content length.  Even in the case
  10532    // of a byte range request, the content length stored in the cached
  10533    // response headers is what we want to use here.
  10534 
  10535    int64_t progressMax = -1;
  10536    rv = GetContentLength(&progressMax);
  10537    if (NS_FAILED(rv)) {
  10538      NS_WARNING("GetContentLength failed");
  10539    }
  10540    int64_t progress = mLogicalOffset + count;
  10541 
  10542    if ((progress > progressMax) && (progressMax != -1)) {
  10543      NS_WARNING(
  10544          "unexpected progress values - "
  10545          "is server exceeding content length?");
  10546    }
  10547 
  10548    // make sure params are in range for js
  10549    if (!InScriptableRange(progressMax)) {
  10550      progressMax = -1;
  10551    }
  10552 
  10553    if (!InScriptableRange(progress)) {
  10554      progress = -1;
  10555    }
  10556 
  10557    if (NS_IsMainThread()) {
  10558      OnTransportStatus(nullptr, transportStatus, progress, progressMax);
  10559    } else {
  10560      rv = NS_DispatchToMainThread(new OnTransportStatusAsyncEvent(
  10561          this, transportStatus, progress, progressMax));
  10562      NS_ENSURE_SUCCESS(rv, rv);
  10563    }
  10564 
  10565    //
  10566    // we have to manually keep the logical offset of the stream up-to-date.
  10567    // we cannot depend solely on the offset provided, since we may have
  10568    // already streamed some data from another source (see, for example,
  10569    // OnDoneReadingPartialCacheEntry).
  10570    //
  10571    int64_t offsetBefore = 0;
  10572    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(input);
  10573    if (seekable && NS_FAILED(seekable->Tell(&offsetBefore))) {
  10574      seekable = nullptr;
  10575    }
  10576 
  10577    if (nsIOService::UseSocketProcess() && mTransaction) {
  10578      mOnDataAvailableStartTime = mTransaction->GetDataAvailableStartTime();
  10579      if (!mOnDataAvailableStartTime.IsNull()) {
  10580        PerfStats::RecordMeasurement(
  10581            PerfStats::Metric::OnDataAvailableSocketToParent,
  10582            TimeStamp::Now() - mOnDataAvailableStartTime);
  10583      }
  10584    } else {
  10585      mOnDataAvailableStartTime = TimeStamp::Now();
  10586    }
  10587    nsresult rv =
  10588        mListener->OnDataAvailable(this, input, mLogicalOffset, count);
  10589    if (NS_SUCCEEDED(rv)) {
  10590      // by contract mListener must read all of "count" bytes, but
  10591      // nsInputStreamPump is tolerant to seekable streams that violate that
  10592      // and it will redeliver incompletely read data. So we need to do
  10593      // the same thing when updating the progress counter to stay in sync.
  10594      int64_t offsetAfter, delta;
  10595      if (seekable && NS_SUCCEEDED(seekable->Tell(&offsetAfter))) {
  10596        delta = offsetAfter - offsetBefore;
  10597        if (delta != count) {
  10598          count = delta;
  10599 
  10600          NS_WARNING("Listener OnDataAvailable contract violation");
  10601          nsCOMPtr<nsIConsoleService> consoleService;
  10602          consoleService = mozilla::components::Console::Service();
  10603          nsAutoString message(nsLiteralString(
  10604              u"http channel Listener OnDataAvailable contract violation"));
  10605          if (consoleService) {
  10606            consoleService->LogStringMessage(message.get());
  10607          }
  10608        }
  10609      }
  10610      mLogicalOffset += count;
  10611    }
  10612 
  10613    return rv;
  10614  }
  10615 
  10616  return NS_ERROR_ABORT;
  10617 }
  10618 
  10619 //-----------------------------------------------------------------------------
  10620 // nsHttpChannel::nsIThreadRetargetableRequest
  10621 //-----------------------------------------------------------------------------
  10622 
  10623 NS_IMETHODIMP
  10624 nsHttpChannel::RetargetDeliveryTo(nsISerialEventTarget* aNewTarget) {
  10625  MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only");
  10626 
  10627  NS_ENSURE_ARG(aNewTarget);
  10628  if (aNewTarget->IsOnCurrentThread()) {
  10629    NS_WARNING("Retargeting delivery to same thread");
  10630    return NS_OK;
  10631  }
  10632  if (!mTransactionPump && !mCachePump) {
  10633    LOG(("nsHttpChannel::RetargetDeliveryTo %p %p no pump available\n", this,
  10634         aNewTarget));
  10635    return NS_ERROR_NOT_AVAILABLE;
  10636  }
  10637 
  10638  nsresult rv = NS_OK;
  10639  // If both cache pump and transaction pump exist, we're probably dealing
  10640  // with partially cached content. So, we must be able to retarget both.
  10641  nsCOMPtr<nsIThreadRetargetableRequest> retargetableCachePump;
  10642  nsCOMPtr<nsIThreadRetargetableRequest> retargetableTransactionPump;
  10643  if (mCachePump) {
  10644    retargetableCachePump = do_QueryObject(mCachePump);
  10645    // nsInputStreamPump should implement this interface.
  10646    MOZ_ASSERT(retargetableCachePump);
  10647    rv = retargetableCachePump->RetargetDeliveryTo(aNewTarget);
  10648  }
  10649  if (NS_SUCCEEDED(rv) && mTransactionPump) {
  10650    retargetableTransactionPump = do_QueryObject(mTransactionPump);
  10651    // nsInputStreamPump should implement this interface.
  10652    MOZ_ASSERT(retargetableTransactionPump);
  10653    rv = retargetableTransactionPump->RetargetDeliveryTo(aNewTarget);
  10654 
  10655    // If retarget fails for transaction pump, we must restore mCachePump.
  10656    if (NS_FAILED(rv) && retargetableCachePump) {
  10657      nsCOMPtr<nsISerialEventTarget> main = GetMainThreadSerialEventTarget();
  10658      NS_ENSURE_TRUE(main, NS_ERROR_UNEXPECTED);
  10659      rv = retargetableCachePump->RetargetDeliveryTo(main);
  10660    }
  10661  }
  10662  return rv;
  10663 }
  10664 
  10665 NS_IMETHODIMP
  10666 nsHttpChannel::GetDeliveryTarget(nsISerialEventTarget** aEventTarget) {
  10667  if (mCachePump) {
  10668    return mCachePump->GetDeliveryTarget(aEventTarget);
  10669  }
  10670  if (mTransactionPump) {
  10671    nsCOMPtr<nsIThreadRetargetableRequest> request =
  10672        do_QueryInterface(mTransactionPump);
  10673    return request->GetDeliveryTarget(aEventTarget);
  10674  }
  10675  return NS_ERROR_NOT_AVAILABLE;
  10676 }
  10677 
  10678 //-----------------------------------------------------------------------------
  10679 // nsHttpChannel::nsThreadRetargetableStreamListener
  10680 //-----------------------------------------------------------------------------
  10681 
  10682 NS_IMETHODIMP
  10683 nsHttpChannel::CheckListenerChain() {
  10684  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!");
  10685  nsresult rv = NS_OK;
  10686  nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
  10687      do_QueryInterface(mListener, &rv);
  10688  if (retargetableListener) {
  10689    rv = retargetableListener->CheckListenerChain();
  10690  }
  10691  return rv;
  10692 }
  10693 
  10694 NS_IMETHODIMP
  10695 nsHttpChannel::OnDataFinished(nsresult aStatus) {
  10696  nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
  10697      do_QueryInterface(mListener);
  10698 
  10699  if (listener) {
  10700    return listener->OnDataFinished(aStatus);
  10701  }
  10702 
  10703  return NS_OK;
  10704 }
  10705 
  10706 //-----------------------------------------------------------------------------
  10707 // nsHttpChannel::nsITransportEventSink
  10708 //-----------------------------------------------------------------------------
  10709 
  10710 NS_IMETHODIMP
  10711 nsHttpChannel::OnTransportStatus(nsITransport* trans, nsresult status,
  10712                                 int64_t progress, int64_t progressMax) {
  10713  MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread only");
  10714  // cache the progress sink so we don't have to query for it each time.
  10715  if (!mProgressSink) GetCallback(mProgressSink);
  10716 
  10717  mLastTransportStatus = status;
  10718  if (status == NS_NET_STATUS_CONNECTED_TO ||
  10719      status == NS_NET_STATUS_WAITING_FOR) {
  10720    bool isTrr = false;
  10721    bool echConfigUsed = false;
  10722    if (mTransaction) {
  10723      mTransaction->GetNetworkAddresses(mSelfAddr, mPeerAddr, isTrr,
  10724                                        mEffectiveTRRMode, mTRRSkipReason,
  10725                                        echConfigUsed);
  10726 
  10727    } else {
  10728      nsCOMPtr<nsISocketTransport> socketTransport = do_QueryInterface(trans);
  10729      if (socketTransport) {
  10730        socketTransport->GetPeerAddr(&mPeerAddr);
  10731        socketTransport->GetSelfAddr(&mSelfAddr);
  10732        socketTransport->ResolvedByTRR(&isTrr);
  10733        socketTransport->GetEffectiveTRRMode(&mEffectiveTRRMode);
  10734        socketTransport->GetEchConfigUsed(&echConfigUsed);
  10735      }
  10736    }
  10737 
  10738    StoreResolvedByTRR(isTrr);
  10739    StoreEchConfigUsed(echConfigUsed);
  10740  }
  10741 
  10742  // block socket status event after Cancel or OnStopRequest has been called.
  10743  if (mProgressSink && NS_SUCCEEDED(mStatus) && LoadIsPending()) {
  10744    LOG(("sending progress%s notification [this=%p status=%" PRIx32
  10745         " progress=%" PRId64 "/%" PRId64 "]\n",
  10746         (mLoadFlags & LOAD_BACKGROUND) ? "" : " and status", this,
  10747         static_cast<uint32_t>(status), progress, progressMax));
  10748 
  10749    nsAutoCString host;
  10750    mURI->GetHost(host);
  10751    if (!(mLoadFlags & LOAD_BACKGROUND)) {
  10752      mProgressSink->OnStatus(this, status, NS_ConvertUTF8toUTF16(host).get());
  10753    } else {
  10754      nsCOMPtr<nsIParentChannel> parentChannel;
  10755      NS_QueryNotificationCallbacks(this, parentChannel);
  10756      // If the event sink is |HttpChannelParent|, we have to send status
  10757      // events to it even if LOAD_BACKGROUND is set. |HttpChannelParent|
  10758      // needs to be aware of whether the status is
  10759      // |NS_NET_STATUS_RECEIVING_FROM| or |NS_NET_STATUS_READING|.
  10760      // LOAD_BACKGROUND is checked again in |HttpChannelChild|, so the final
  10761      // consumer won't get this event.
  10762      if (SameCOMIdentity(parentChannel, mProgressSink)) {
  10763        mProgressSink->OnStatus(this, status,
  10764                                NS_ConvertUTF8toUTF16(host).get());
  10765      }
  10766    }
  10767 
  10768    if (progress > 0) {
  10769      if ((progress > progressMax) && (progressMax != -1)) {
  10770        NS_WARNING("unexpected progress values");
  10771      }
  10772 
  10773      // Try to get mProgressSink if it was nulled out during OnStatus.
  10774      if (!mProgressSink) {
  10775        GetCallback(mProgressSink);
  10776      }
  10777      if (mProgressSink) {
  10778        mProgressSink->OnProgress(this, progress, progressMax);
  10779      }
  10780    }
  10781  }
  10782 
  10783  return NS_OK;
  10784 }
  10785 
  10786 //-----------------------------------------------------------------------------
  10787 // nsHttpChannel::nsICacheInfoChannel
  10788 //-----------------------------------------------------------------------------
  10789 
  10790 NS_IMETHODIMP
  10791 nsHttpChannel::IsFromCache(bool* value) {
  10792  if (!LoadIsPending()) return NS_ERROR_NOT_AVAILABLE;
  10793 
  10794  if (!mRaceCacheWithNetwork) {
  10795    // return false if reading a partial cache entry; the data isn't
  10796    // entirely from the cache!
  10797    *value = (mCachePump || (mLoadFlags & LOAD_ONLY_IF_MODIFIED)) &&
  10798             CachedContentIsValid() && !LoadCachedContentIsPartial();
  10799    return NS_OK;
  10800  }
  10801 
  10802  // If we are racing network and cache (or skipping the cache)
  10803  // we just return the first response source.
  10804  *value = mFirstResponseSource == RESPONSE_FROM_CACHE;
  10805 
  10806  return NS_OK;
  10807 }
  10808 
  10809 NS_IMETHODIMP
  10810 nsHttpChannel::HasCacheEntry(bool* value) {
  10811  *value = !!mCacheEntry;
  10812  return NS_OK;
  10813 }
  10814 
  10815 NS_IMETHODIMP
  10816 nsHttpChannel::GetCacheEntryId(uint64_t* aCacheEntryId) {
  10817  if (!mCacheEntry || NS_FAILED(mCacheEntry->GetCacheEntryId(aCacheEntryId))) {
  10818    return NS_ERROR_NOT_AVAILABLE;
  10819  }
  10820 
  10821  return NS_OK;
  10822 }
  10823 
  10824 NS_IMETHODIMP
  10825 nsHttpChannel::GetCacheTokenFetchCount(uint32_t* _retval) {
  10826  NS_ENSURE_ARG_POINTER(_retval);
  10827  nsCOMPtr<nsICacheEntry> cacheEntry =
  10828      mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
  10829  if (!cacheEntry) {
  10830    return NS_ERROR_NOT_AVAILABLE;
  10831  }
  10832 
  10833  return cacheEntry->GetFetchCount(_retval);
  10834 }
  10835 
  10836 NS_IMETHODIMP
  10837 nsHttpChannel::GetCacheTokenExpirationTime(uint32_t* _retval) {
  10838  NS_ENSURE_ARG_POINTER(_retval);
  10839  if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
  10840 
  10841  return mCacheEntry->GetExpirationTime(_retval);
  10842 }
  10843 
  10844 NS_IMETHODIMP
  10845 nsHttpChannel::SetAllowStaleCacheContent(bool aAllowStaleCacheContent) {
  10846  LOG(("nsHttpChannel::SetAllowStaleCacheContent [this=%p, allow=%d]", this,
  10847       aAllowStaleCacheContent));
  10848  StoreAllowStaleCacheContent(aAllowStaleCacheContent);
  10849  return NS_OK;
  10850 }
  10851 NS_IMETHODIMP
  10852 nsHttpChannel::GetAllowStaleCacheContent(bool* aAllowStaleCacheContent) {
  10853  NS_ENSURE_ARG(aAllowStaleCacheContent);
  10854  *aAllowStaleCacheContent = LoadAllowStaleCacheContent();
  10855  return NS_OK;
  10856 }
  10857 
  10858 NS_IMETHODIMP
  10859 nsHttpChannel::SetForceValidateCacheContent(bool aForceValidateCacheContent) {
  10860  LOG(("nsHttpChannel::SetForceValidateCacheContent [this=%p, allow=%d]", this,
  10861       aForceValidateCacheContent));
  10862  StoreForceValidateCacheContent(aForceValidateCacheContent);
  10863  return NS_OK;
  10864 }
  10865 NS_IMETHODIMP
  10866 nsHttpChannel::GetForceValidateCacheContent(bool* aForceValidateCacheContent) {
  10867  NS_ENSURE_ARG(aForceValidateCacheContent);
  10868  *aForceValidateCacheContent = LoadForceValidateCacheContent();
  10869  return NS_OK;
  10870 }
  10871 
  10872 NS_IMETHODIMP
  10873 nsHttpChannel::SetPreferCacheLoadOverBypass(bool aPreferCacheLoadOverBypass) {
  10874  StorePreferCacheLoadOverBypass(aPreferCacheLoadOverBypass);
  10875  return NS_OK;
  10876 }
  10877 NS_IMETHODIMP
  10878 nsHttpChannel::GetPreferCacheLoadOverBypass(bool* aPreferCacheLoadOverBypass) {
  10879  NS_ENSURE_ARG(aPreferCacheLoadOverBypass);
  10880  *aPreferCacheLoadOverBypass = LoadPreferCacheLoadOverBypass();
  10881  return NS_OK;
  10882 }
  10883 
  10884 NS_IMETHODIMP
  10885 nsHttpChannel::PreferAlternativeDataType(
  10886    const nsACString& aType, const nsACString& aContentType,
  10887    PreferredAlternativeDataDeliveryType aDeliverAltData) {
  10888  ENSURE_CALLED_BEFORE_ASYNC_OPEN();
  10889  mPreferredCachedAltDataTypes.AppendElement(PreferredAlternativeDataTypeParams(
  10890      nsCString(aType), nsCString(aContentType), aDeliverAltData));
  10891  return NS_OK;
  10892 }
  10893 
  10894 const nsTArray<PreferredAlternativeDataTypeParams>&
  10895 nsHttpChannel::PreferredAlternativeDataTypes() {
  10896  return mPreferredCachedAltDataTypes;
  10897 }
  10898 
  10899 NS_IMETHODIMP
  10900 nsHttpChannel::GetAlternativeDataType(nsACString& aType) {
  10901  // must be called during or after OnStartRequest
  10902  if (!LoadAfterOnStartRequestBegun()) {
  10903    return NS_ERROR_NOT_AVAILABLE;
  10904  }
  10905  aType = mAvailableCachedAltDataType;
  10906  return NS_OK;
  10907 }
  10908 
  10909 class CacheEntryWriteHandle : public nsICacheEntryWriteHandle {
  10910  virtual ~CacheEntryWriteHandle() = default;
  10911 
  10912 public:
  10913  NS_DECL_ISUPPORTS
  10914  NS_DECL_NSICACHEENTRYWRITEHANDLE
  10915 
  10916  explicit CacheEntryWriteHandle(nsICacheEntry* aCacheEntry)
  10917      : mCacheEntry(aCacheEntry) {
  10918    MOZ_ASSERT(mCacheEntry);
  10919  }
  10920 
  10921 private:
  10922  nsCOMPtr<nsICacheEntry> mCacheEntry;
  10923 };
  10924 
  10925 NS_IMPL_ADDREF(CacheEntryWriteHandle)
  10926 NS_IMPL_RELEASE(CacheEntryWriteHandle)
  10927 NS_INTERFACE_MAP_BEGIN(CacheEntryWriteHandle)
  10928  NS_INTERFACE_MAP_ENTRY(nsISupports)
  10929  NS_INTERFACE_MAP_ENTRY(nsICacheEntryWriteHandle)
  10930 NS_INTERFACE_MAP_END
  10931 
  10932 NS_IMETHODIMP
  10933 CacheEntryWriteHandle::OpenAlternativeOutputStream(
  10934    const nsACString& type, int64_t predictedSize,
  10935    nsIAsyncOutputStream** _retval) {
  10936  nsresult rv =
  10937      mCacheEntry->OpenAlternativeOutputStream(type, predictedSize, _retval);
  10938  if (NS_SUCCEEDED(rv)) {
  10939    mCacheEntry->SetMetaDataElement("alt-data-from-child", nullptr);
  10940  }
  10941  return rv;
  10942 }
  10943 
  10944 NS_IMETHODIMP
  10945 nsHttpChannel::GetCacheEntryWriteHandle(nsICacheEntryWriteHandle** _retval) {
  10946  nsCOMPtr<nsICacheEntry> cacheEntry =
  10947      mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
  10948  if (!cacheEntry) {
  10949    return NS_ERROR_NOT_AVAILABLE;
  10950  }
  10951 
  10952  nsCOMPtr<nsICacheEntryWriteHandle> handle =
  10953      new CacheEntryWriteHandle(cacheEntry);
  10954  handle.forget(_retval);
  10955  return NS_OK;
  10956 }
  10957 
  10958 NS_IMETHODIMP
  10959 nsHttpChannel::OpenAlternativeOutputStream(const nsACString& type,
  10960                                           int64_t predictedSize,
  10961                                           nsIAsyncOutputStream** _retval) {
  10962  // OnStopRequest will clear mCacheEntry, but we may use mAltDataCacheEntry
  10963  // if the consumer called PreferAlternativeDataType()
  10964  nsCOMPtr<nsICacheEntry> cacheEntry =
  10965      mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
  10966  if (!cacheEntry) {
  10967    return NS_ERROR_NOT_AVAILABLE;
  10968  }
  10969  nsresult rv =
  10970      cacheEntry->OpenAlternativeOutputStream(type, predictedSize, _retval);
  10971  if (NS_SUCCEEDED(rv)) {
  10972    // Clear this metadata flag in case it exists.
  10973    // The caller of this method may set it again.
  10974    cacheEntry->SetMetaDataElement("alt-data-from-child", nullptr);
  10975  }
  10976  return rv;
  10977 }
  10978 
  10979 NS_IMETHODIMP
  10980 nsHttpChannel::GetOriginalInputStream(nsIInputStreamReceiver* aReceiver) {
  10981  if (aReceiver == nullptr) {
  10982    return NS_ERROR_INVALID_ARG;
  10983  }
  10984  nsCOMPtr<nsIInputStream> inputStream;
  10985 
  10986  nsCOMPtr<nsICacheEntry> cacheEntry =
  10987      mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
  10988  if (cacheEntry) {
  10989    cacheEntry->OpenInputStream(0, getter_AddRefs(inputStream));
  10990  }
  10991  aReceiver->OnInputStreamReady(inputStream);
  10992  return NS_OK;
  10993 }
  10994 
  10995 NS_IMETHODIMP
  10996 nsHttpChannel::GetAlternativeDataInputStream(nsIInputStream** aInputStream) {
  10997  NS_ENSURE_ARG_POINTER(aInputStream);
  10998 
  10999  *aInputStream = nullptr;
  11000 
  11001  nsCOMPtr<nsICacheEntry> cacheEntry =
  11002      mCacheEntry ? mCacheEntry : mAltDataCacheEntry;
  11003  if (!mAvailableCachedAltDataType.IsEmpty() && cacheEntry) {
  11004    nsresult rv = cacheEntry->OpenAlternativeInputStream(
  11005        mAvailableCachedAltDataType, aInputStream);
  11006    NS_ENSURE_SUCCESS(rv, rv);
  11007  }
  11008  return NS_OK;
  11009 }
  11010 
  11011 //-----------------------------------------------------------------------------
  11012 // nsHttpChannel::nsICachingChannel
  11013 //-----------------------------------------------------------------------------
  11014 
  11015 NS_IMETHODIMP
  11016 nsHttpChannel::IsRacing(bool* aIsRacing) {
  11017  if (!LoadAfterOnStartRequestBegun()) {
  11018    return NS_ERROR_NOT_AVAILABLE;
  11019  }
  11020  *aIsRacing = mRaceCacheWithNetwork;
  11021  return NS_OK;
  11022 }
  11023 
  11024 NS_IMETHODIMP
  11025 nsHttpChannel::GetCacheToken(nsISupports** token) {
  11026  NS_ENSURE_ARG_POINTER(token);
  11027  if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE;
  11028  return CallQueryInterface(mCacheEntry, token);
  11029 }
  11030 
  11031 NS_IMETHODIMP
  11032 nsHttpChannel::SetCacheToken(nsISupports* token) {
  11033  return NS_ERROR_NOT_IMPLEMENTED;
  11034 }
  11035 
  11036 NS_IMETHODIMP
  11037 nsHttpChannel::GetCacheKey(uint32_t* key) {
  11038  NS_ENSURE_ARG_POINTER(key);
  11039 
  11040  LOG(("nsHttpChannel::GetCacheKey [this=%p]\n", this));
  11041 
  11042  *key = mPostID;
  11043  return NS_OK;
  11044 }
  11045 
  11046 NS_IMETHODIMP
  11047 nsHttpChannel::SetCacheKey(uint32_t key) {
  11048  LOG(("nsHttpChannel::SetCacheKey [this=%p key=%u]\n", this, key));
  11049 
  11050  ENSURE_CALLED_BEFORE_CONNECT();
  11051 
  11052  mPostID = key;
  11053  return NS_OK;
  11054 }
  11055 
  11056 NS_IMETHODIMP
  11057 nsHttpChannel::GetCacheOnlyMetadata(bool* aOnlyMetadata) {
  11058  NS_ENSURE_ARG(aOnlyMetadata);
  11059  *aOnlyMetadata = LoadCacheOnlyMetadata();
  11060  return NS_OK;
  11061 }
  11062 
  11063 NS_IMETHODIMP
  11064 nsHttpChannel::SetCacheOnlyMetadata(bool aOnlyMetadata) {
  11065  LOG(("nsHttpChannel::SetCacheOnlyMetadata [this=%p only-metadata=%d]\n", this,
  11066       aOnlyMetadata));
  11067 
  11068  ENSURE_CALLED_BEFORE_ASYNC_OPEN();
  11069 
  11070  StoreCacheOnlyMetadata(aOnlyMetadata);
  11071  if (aOnlyMetadata) {
  11072    mLoadFlags |= LOAD_ONLY_IF_MODIFIED;
  11073  }
  11074 
  11075  return NS_OK;
  11076 }
  11077 
  11078 NS_IMETHODIMP
  11079 nsHttpChannel::GetPin(bool* aPin) {
  11080  NS_ENSURE_ARG(aPin);
  11081  *aPin = LoadPinCacheContent();
  11082  return NS_OK;
  11083 }
  11084 
  11085 NS_IMETHODIMP
  11086 nsHttpChannel::SetPin(bool aPin) {
  11087  LOG(("nsHttpChannel::SetPin [this=%p pin=%d]\n", this, aPin));
  11088 
  11089  ENSURE_CALLED_BEFORE_CONNECT();
  11090 
  11091  StorePinCacheContent(aPin);
  11092  return NS_OK;
  11093 }
  11094 
  11095 NS_IMETHODIMP
  11096 nsHttpChannel::ForceCacheEntryValidFor(uint32_t aSecondsToTheFuture) {
  11097  if (!mCacheEntry) {
  11098    LOG(
  11099        ("nsHttpChannel::ForceCacheEntryValidFor found no cache entry "
  11100         "for this channel [this=%p].",
  11101         this));
  11102  } else {
  11103    mCacheEntry->ForceValidFor(aSecondsToTheFuture);
  11104 
  11105    nsAutoCString key;
  11106    mCacheEntry->GetKey(key);
  11107 
  11108    LOG(
  11109        ("nsHttpChannel::ForceCacheEntryValidFor successfully forced valid "
  11110         "entry with key %s for %d seconds. [this=%p]",
  11111         key.get(), aSecondsToTheFuture, this));
  11112  }
  11113 
  11114  return NS_OK;
  11115 }
  11116 
  11117 //-----------------------------------------------------------------------------
  11118 // nsHttpChannel::nsIResumableChannel
  11119 //-----------------------------------------------------------------------------
  11120 
  11121 NS_IMETHODIMP
  11122 nsHttpChannel::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID) {
  11123  LOG(("nsHttpChannel::ResumeAt [this=%p startPos=%" PRIu64 " id='%s']\n", this,
  11124       aStartPos, PromiseFlatCString(aEntityID).get()));
  11125  mEntityID = aEntityID;
  11126  mStartPos = aStartPos;
  11127  StoreResuming(true);
  11128  return NS_OK;
  11129 }
  11130 
  11131 nsresult nsHttpChannel::DoAuthRetry(
  11132    HttpTransactionShell* aTransWithStickyConn,
  11133    const std::function<nsresult(nsHttpChannel*, nsresult)>&
  11134        aContinueOnStopRequestFunc) {
  11135  LOG(("nsHttpChannel::DoAuthRetry [this=%p, aTransWithStickyConn=%p]\n", this,
  11136       aTransWithStickyConn));
  11137 
  11138  MOZ_ASSERT(!mTransaction, "should not have a transaction");
  11139 
  11140  // Note that we don't have to toggle |IsPending| anymore. See the reasons
  11141  // below.
  11142  // 1. We can't suspend the channel during "http-on-modify-request"
  11143  // when |IsPending| is false.
  11144  // 2. We don't check |IsPending| in SetRequestHeader now.
  11145 
  11146  // Reset RequestObserversCalled because we've probably called the request
  11147  // observers once already.
  11148  StoreRequestObserversCalled(false);
  11149 
  11150  // fetch cookies, and add them to the request header.
  11151  // the server response could have included cookies that must be sent with
  11152  // this authentication attempt (bug 84794).
  11153  // TODO: save cookies from auth response and send them here (bug 572151).
  11154  AddCookiesToRequest();
  11155 
  11156  // notify "http-on-modify-request" observers
  11157  CallOnModifyRequestObservers();
  11158 
  11159  RefPtr<HttpTransactionShell> trans(aTransWithStickyConn);
  11160  return CallOrWaitForResume(
  11161      [trans{std::move(trans)}, aContinueOnStopRequestFunc](auto* self) {
  11162        return self->ContinueDoAuthRetry(trans, aContinueOnStopRequestFunc);
  11163      });
  11164 }
  11165 
  11166 nsresult nsHttpChannel::ContinueDoAuthRetry(
  11167    HttpTransactionShell* aTransWithStickyConn,
  11168    const std::function<nsresult(nsHttpChannel*, nsresult)>&
  11169        aContinueOnStopRequestFunc) {
  11170  LOG(("nsHttpChannel::ContinueDoAuthRetry [this=%p]\n", this));
  11171  StoreIsPending(true);
  11172 
  11173  // get rid of the old response headers
  11174  mResponseHead = nullptr;
  11175 
  11176  // rewind the upload stream
  11177  if (mUploadStream) {
  11178    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
  11179    nsresult rv = NS_ERROR_NO_INTERFACE;
  11180    if (seekable) {
  11181      rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
  11182    }
  11183 
  11184    // This should not normally happen, but it's possible that big memory
  11185    // blobs originating in the other process can't be rewinded.
  11186    // In that case we just fail the request, otherwise the content length
  11187    // will not match and this load will never complete.
  11188    NS_ENSURE_SUCCESS(rv, rv);
  11189  }
  11190 
  11191  // always set sticky connection flag
  11192  mCaps |= NS_HTTP_STICKY_CONNECTION;
  11193  // and when needed, allow restart regardless the sticky flag
  11194  if (LoadAuthConnectionRestartable()) {
  11195    LOG(("  connection made restartable"));
  11196    mCaps |= NS_HTTP_CONNECTION_RESTARTABLE;
  11197    StoreAuthConnectionRestartable(false);
  11198  } else {
  11199    LOG(("  connection made non-restartable"));
  11200    mCaps &= ~NS_HTTP_CONNECTION_RESTARTABLE;
  11201  }
  11202 
  11203  // notify "http-on-before-connect" observers
  11204  gHttpHandler->OnBeforeConnect(this);
  11205 
  11206  RefPtr<HttpTransactionShell> trans(aTransWithStickyConn);
  11207  return CallOrWaitForResume(
  11208      [trans{std::move(trans)}, aContinueOnStopRequestFunc](auto* self) {
  11209        nsresult rv = self->DoConnect(trans);
  11210        return aContinueOnStopRequestFunc(self, rv);
  11211      });
  11212 }
  11213 
  11214 //-----------------------------------------------------------------------------
  11215 // nsHttpChannel::nsIAsyncVerifyRedirectCallback
  11216 //-----------------------------------------------------------------------------
  11217 
  11218 nsresult nsHttpChannel::WaitForRedirectCallback() {
  11219  nsresult rv;
  11220  LOG(("nsHttpChannel::WaitForRedirectCallback [this=%p]\n", this));
  11221 
  11222  if (mTransactionPump) {
  11223    rv = mTransactionPump->Suspend();
  11224    NS_ENSURE_SUCCESS(rv, rv);
  11225  }
  11226  if (mCachePump) {
  11227    rv = mCachePump->Suspend();
  11228    if (NS_FAILED(rv) && mTransactionPump) {
  11229 #ifdef DEBUG
  11230      nsresult resume =
  11231 #endif
  11232          mTransactionPump->Resume();
  11233      MOZ_ASSERT(NS_SUCCEEDED(resume), "Failed to resume transaction pump");
  11234    }
  11235    NS_ENSURE_SUCCESS(rv, rv);
  11236  }
  11237 
  11238  StoreWaitingForRedirectCallback(true);
  11239  return NS_OK;
  11240 }
  11241 
  11242 NS_IMETHODIMP
  11243 nsHttpChannel::OnRedirectVerifyCallback(nsresult result) {
  11244  LOG(
  11245      ("nsHttpChannel::OnRedirectVerifyCallback [this=%p] "
  11246       "result=%" PRIx32 " stack=%zu WaitingForRedirectCallback=%u\n",
  11247       this, static_cast<uint32_t>(result), mRedirectFuncStack.Length(),
  11248       LoadWaitingForRedirectCallback()));
  11249  MOZ_ASSERT(LoadWaitingForRedirectCallback(),
  11250             "Someone forgot to call WaitForRedirectCallback() ?!");
  11251  StoreWaitingForRedirectCallback(false);
  11252 
  11253  if (mCanceled && NS_SUCCEEDED(result)) result = NS_BINDING_ABORTED;
  11254 
  11255  for (uint32_t i = mRedirectFuncStack.Length(); i > 0;) {
  11256    --i;
  11257    // Pop the last function pushed to the stack
  11258    nsContinueRedirectionFunc func = mRedirectFuncStack.PopLastElement();
  11259 
  11260    // Call it with the result we got from the callback or the deeper
  11261    // function call.
  11262    result = (this->*func)(result);
  11263 
  11264    // If a new function has been pushed to the stack and placed us in the
  11265    // waiting state, we need to break the chain and wait for the callback
  11266    // again.
  11267    if (LoadWaitingForRedirectCallback()) break;
  11268  }
  11269 
  11270  if (NS_FAILED(result) && !mCanceled) {
  11271    // First, cancel this channel if we are in failure state to set mStatus
  11272    // and let it be propagated to pumps.
  11273    Cancel(result);
  11274  }
  11275 
  11276  if (!LoadWaitingForRedirectCallback()) {
  11277    // We are not waiting for the callback. At this moment we must release
  11278    // reference to the redirect target channel, otherwise we may leak.
  11279    // However, if we are redirecting due to auth retries, we open the
  11280    // redirected channel after OnStopRequest. In that case we should not
  11281    // release the reference
  11282    if (!StaticPrefs::network_auth_use_redirect_for_retries() ||
  11283        !mAuthRetryPending) {
  11284      mRedirectChannel = nullptr;
  11285    }
  11286  }
  11287 
  11288  // We always resume the pumps here. If all functions on stack have been
  11289  // called we need OnStopRequest to be triggered, and if we broke out of the
  11290  // loop above (and are thus waiting for a new callback) the suspension
  11291  // count must be balanced in the pumps.
  11292  if (mTransactionPump) mTransactionPump->Resume();
  11293  if (mCachePump) mCachePump->Resume();
  11294 
  11295  return result;
  11296 }
  11297 
  11298 void nsHttpChannel::PushRedirectAsyncFunc(nsContinueRedirectionFunc func) {
  11299  mRedirectFuncStack.AppendElement(func);
  11300 }
  11301 
  11302 void nsHttpChannel::PopRedirectAsyncFunc(nsContinueRedirectionFunc func) {
  11303  MOZ_ASSERT(func == mRedirectFuncStack.LastElement(),
  11304             "Trying to pop wrong method from redirect async stack!");
  11305 
  11306  mRedirectFuncStack.RemoveLastElement();
  11307 }
  11308 
  11309 //-----------------------------------------------------------------------------
  11310 // nsIDNSListener functions
  11311 //-----------------------------------------------------------------------------
  11312 
  11313 NS_IMETHODIMP
  11314 nsHttpChannel::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec,
  11315                                nsresult status) {
  11316  MOZ_ASSERT(NS_IsMainThread(), "Expecting DNS callback on main thread.");
  11317 
  11318  LOG(
  11319      ("nsHttpChannel::OnLookupComplete [this=%p] prefetch complete%s: "
  11320       "%s status[0x%" PRIx32 "]\n",
  11321       this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "",
  11322       NS_SUCCEEDED(status) ? "success" : "failure",
  11323       static_cast<uint32_t>(status)));
  11324 
  11325  // Unset DNS cache refresh if it was requested,
  11326  if (mCaps & NS_HTTP_REFRESH_DNS) {
  11327    mCaps &= ~NS_HTTP_REFRESH_DNS;
  11328    if (mTransaction) {
  11329      mTransaction->SetDNSWasRefreshed();
  11330    }
  11331  }
  11332 
  11333  if (!mDNSBlockingPromise.IsEmpty()) {
  11334    if (NS_SUCCEEDED(status)) {
  11335      nsCOMPtr<nsIDNSRecord> record(rec);
  11336      mDNSBlockingPromise.Resolve(record, __func__);
  11337    } else {
  11338      mDNSBlockingPromise.Reject(status, __func__);
  11339    }
  11340  }
  11341 
  11342  return NS_OK;
  11343 }
  11344 
  11345 void nsHttpChannel::OnHTTPSRRAvailable(nsIDNSHTTPSSVCRecord* aRecord) {
  11346  MOZ_ASSERT(NS_IsMainThread(), "Expecting DNS callback on main thread.");
  11347 
  11348  LOG(("nsHttpChannel::OnHTTPSRRAvailable [this=%p, aRecord=%p]\n", this,
  11349       aRecord));
  11350 
  11351  if (mHTTPSSVCRecord) {
  11352    MOZ_ASSERT(false, "OnHTTPSRRAvailable called twice!");
  11353    return;
  11354  }
  11355 
  11356  nsCOMPtr<nsIDNSHTTPSSVCRecord> record = aRecord;
  11357  mHTTPSSVCRecord.emplace(std::move(record));
  11358  const nsCOMPtr<nsIDNSHTTPSSVCRecord>& httprr = mHTTPSSVCRecord.ref();
  11359 
  11360  if (LoadWaitHTTPSSVCRecord()) {
  11361    MOZ_ASSERT(mURI->SchemeIs("http"));
  11362 
  11363    StoreWaitHTTPSSVCRecord(false);
  11364    nsresult rv = ContinueOnBeforeConnect(!!httprr, mStatus, !!httprr);
  11365    if (NS_FAILED(rv)) {
  11366      CloseCacheEntry(false);
  11367      (void)AsyncAbort(rv);
  11368    }
  11369  } else {
  11370    // This channel is not canceled and the transaction is not created.
  11371    if (httprr && NS_SUCCEEDED(mStatus) && !mTransaction &&
  11372        (mFirstResponseSource != RESPONSE_FROM_CACHE)) {
  11373      bool hasIPAddress = false;
  11374      (void)httprr->GetHasIPAddresses(&hasIPAddress);
  11375      glean::http::dns_httpssvc_record_receiving_stage.AccumulateSingleSample(
  11376          hasIPAddress ? HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_0
  11377                       : HTTPSSVC_WITHOUT_IPHINT_RECEIVED_STAGE_0);
  11378      StoreHTTPSSVCTelemetryReported(true);
  11379    }
  11380  }
  11381 }
  11382 
  11383 //-----------------------------------------------------------------------------
  11384 // nsHttpChannel internal functions
  11385 //-----------------------------------------------------------------------------
  11386 
  11387 // Creates an URI to the given location using current URI for base and charset
  11388 nsresult nsHttpChannel::CreateNewURI(const char* loc, nsIURI** newURI) {
  11389  nsCOMPtr<nsIIOService> ioService;
  11390  nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService));
  11391  if (NS_FAILED(rv)) return rv;
  11392 
  11393  return ioService->NewURI(nsDependentCString(loc), nullptr, mURI, newURI);
  11394 }
  11395 
  11396 void nsHttpChannel::MaybeInvalidateCacheEntryForSubsequentGet() {
  11397  // See RFC 2616 section 5.1.1. These are considered valid
  11398  // methods which DO NOT invalidate cache-entries for the
  11399  // referred resource. POST, PUT and DELETE as well as any
  11400  // other method not listed here will potentially invalidate
  11401  // any cached copy of the resource
  11402  if (mRequestHead.IsGet() || mRequestHead.IsOptions() ||
  11403      mRequestHead.IsHead() || mRequestHead.IsTrace() ||
  11404      mRequestHead.IsConnect()) {
  11405    return;
  11406  }
  11407 
  11408  // Invalidate the request-uri.
  11409  if (LOG_ENABLED()) {
  11410    nsAutoCString key;
  11411    mURI->GetAsciiSpec(key);
  11412    LOG(("MaybeInvalidateCacheEntryForSubsequentGet [this=%p uri=%s]\n", this,
  11413         key.get()));
  11414  }
  11415 
  11416  DoInvalidateCacheEntry(mURI);
  11417 
  11418  // Invalidate Location-header if set
  11419  nsAutoCString location;
  11420  (void)mResponseHead->GetHeader(nsHttp::Location, location);
  11421  if (!location.IsEmpty()) {
  11422    LOG(("  Location-header=%s\n", location.get()));
  11423    InvalidateCacheEntryForLocation(location.get());
  11424  }
  11425 
  11426  // Invalidate Content-Location-header if set
  11427  (void)mResponseHead->GetHeader(nsHttp::Content_Location, location);
  11428  if (!location.IsEmpty()) {
  11429    LOG(("  Content-Location-header=%s\n", location.get()));
  11430    InvalidateCacheEntryForLocation(location.get());
  11431  }
  11432 }
  11433 
  11434 void nsHttpChannel::InvalidateCacheEntryForLocation(const char* location) {
  11435  nsAutoCString tmpCacheKey, tmpSpec;
  11436  nsCOMPtr<nsIURI> resultingURI;
  11437  nsresult rv = CreateNewURI(location, getter_AddRefs(resultingURI));
  11438  if (NS_SUCCEEDED(rv) && HostPartIsTheSame(resultingURI)) {
  11439    DoInvalidateCacheEntry(resultingURI);
  11440  } else {
  11441    LOG(("  hosts not matching\n"));
  11442  }
  11443 }
  11444 
  11445 void nsHttpChannel::DoInvalidateCacheEntry(nsIURI* aURI) {
  11446  // NOTE:
  11447  // Following comments 24,32 and 33 in bug #327765, we only care about
  11448  // the cache in the protocol-handler.
  11449  // The logic below deviates from the original logic in OpenCacheEntry on
  11450  // one point by using only READ_ONLY access-policy. I think this is safe.
  11451 
  11452  nsresult rv;
  11453 
  11454  nsAutoCString key;
  11455  if (LOG_ENABLED()) {
  11456    aURI->GetAsciiSpec(key);
  11457  }
  11458 
  11459  LOG(("DoInvalidateCacheEntry [channel=%p key=%s]", this, key.get()));
  11460 
  11461  nsCOMPtr<nsICacheStorageService> cacheStorageService(
  11462      components::CacheStorage::Service());
  11463  rv = cacheStorageService ? NS_OK : NS_ERROR_FAILURE;
  11464 
  11465  nsCOMPtr<nsICacheStorage> cacheStorage;
  11466  if (NS_SUCCEEDED(rv)) {
  11467    RefPtr<LoadContextInfo> info = GetLoadContextInfo(this);
  11468    rv = cacheStorageService->DiskCacheStorage(info,
  11469                                               getter_AddRefs(cacheStorage));
  11470  }
  11471 
  11472  if (NS_SUCCEEDED(rv)) {
  11473    rv = cacheStorage->AsyncDoomURI(aURI, ""_ns, nullptr);
  11474  }
  11475 
  11476  LOG(("DoInvalidateCacheEntry [channel=%p key=%s rv=%d]", this, key.get(),
  11477       int(rv)));
  11478 }
  11479 
  11480 void nsHttpChannel::AsyncOnExamineCachedResponse() {
  11481  gHttpHandler->OnExamineCachedResponse(this);
  11482 }
  11483 
  11484 void nsHttpChannel::UpdateAggregateCallbacks() {
  11485  if (!mTransaction) {
  11486    return;
  11487  }
  11488  nsCOMPtr<nsIInterfaceRequestor> callbacks;
  11489  NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
  11490                                         GetCurrentSerialEventTarget(),
  11491                                         getter_AddRefs(callbacks));
  11492  mTransaction->SetSecurityCallbacks(callbacks);
  11493 }
  11494 
  11495 NS_IMETHODIMP
  11496 nsHttpChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
  11497  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
  11498 
  11499  nsresult rv = HttpBaseChannel::SetLoadGroup(aLoadGroup);
  11500  if (NS_SUCCEEDED(rv)) {
  11501    UpdateAggregateCallbacks();
  11502  }
  11503  return rv;
  11504 }
  11505 
  11506 NS_IMETHODIMP
  11507 nsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
  11508  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread.");
  11509 
  11510  nsresult rv = HttpBaseChannel::SetNotificationCallbacks(aCallbacks);
  11511  if (NS_SUCCEEDED(rv)) {
  11512    UpdateAggregateCallbacks();
  11513  }
  11514  return rv;
  11515 }
  11516 
  11517 bool nsHttpChannel::AwaitingCacheCallbacks() {
  11518  return LoadWaitForCacheEntry() != 0;
  11519 }
  11520 
  11521 // static
  11522 bool nsHttpChannel::IsRedirectStatus(uint32_t status) {
  11523  // 305 disabled as a security measure (see bug 187996).
  11524  return status == 301 || status == 302 || status == 303 || status == 307 ||
  11525         status == 308;
  11526 }
  11527 
  11528 void nsHttpChannel::SetCouldBeSynthesized() {
  11529  MOZ_ASSERT(!BypassServiceWorker());
  11530  StoreResponseCouldBeSynthesized(true);
  11531 }
  11532 
  11533 NS_IMETHODIMP
  11534 nsHttpChannel::OnPreflightSucceeded() {
  11535  MOZ_ASSERT(LoadRequireCORSPreflight(), "Why did a preflight happen?");
  11536  StoreIsCorsPreflightDone(1);
  11537  mPreflightChannel = nullptr;
  11538 
  11539  return ContinueConnect();
  11540 }
  11541 
  11542 NS_IMETHODIMP
  11543 nsHttpChannel::OnPreflightFailed(nsresult aError) {
  11544  MOZ_ASSERT(LoadRequireCORSPreflight(), "Why did a preflight happen?");
  11545  StoreIsCorsPreflightDone(1);
  11546  mPreflightChannel = nullptr;
  11547 
  11548  CloseCacheEntry(false);
  11549  (void)AsyncAbort(aError);
  11550  return NS_OK;
  11551 }
  11552 
  11553 nsresult nsHttpChannel::CallOrWaitForResume(
  11554    const std::function<nsresult(nsHttpChannel*)>& aFunc) {
  11555  if (mCanceled) {
  11556    MOZ_ASSERT(NS_FAILED(mStatus));
  11557    return mStatus;
  11558  }
  11559 
  11560  if (mSuspendCount) {
  11561    LOG(("Waiting until resume [this=%p]\n", this));
  11562    MOZ_ASSERT(!mCallOnResume);
  11563    mCallOnResume = aFunc;
  11564    return NS_OK;
  11565  }
  11566 
  11567  return aFunc(this);
  11568 }
  11569 
  11570 // This is loosely based on:
  11571 // https://fetch.spec.whatwg.org/#serializing-a-request-origin
  11572 static bool HasNullRequestOrigin(nsHttpChannel* aChannel, nsIURI* aURI,
  11573                                 bool isAddonRequest) {
  11574  // Step 1. If request has a redirect-tainted origin, then return "null".
  11575  if (aChannel->HasRedirectTaintedOrigin()) {
  11576    if (StaticPrefs::network_http_origin_redirectTainted()) {
  11577      return true;
  11578    }
  11579  }
  11580 
  11581  // Non-standard: Only allow HTTP and HTTPS origins.
  11582  if (!ReferrerInfo::IsReferrerSchemeAllowed(aURI)) {
  11583    // And moz-extension: for add-on initiated requests.
  11584    if (!aURI->SchemeIs("moz-extension") || !isAddonRequest) {
  11585      return true;
  11586    }
  11587  }
  11588 
  11589  // Non-standard: Hide onion URLs.
  11590  if (StaticPrefs::network_http_referer_hideOnionSource()) {
  11591    nsAutoCString host;
  11592    if (NS_SUCCEEDED(aURI->GetAsciiHost(host)) &&
  11593        StringEndsWith(host, ".onion"_ns)) {
  11594      return ReferrerInfo::IsCrossOriginRequest(aChannel);
  11595    }
  11596  }
  11597 
  11598  // Step 2. Return request’s origin, serialized.
  11599  return false;
  11600 }
  11601 
  11602 // Step 8.12. of HTTP-network-or-cache fetch
  11603 //
  11604 // https://fetch.spec.whatwg.org/#append-a-request-origin-header
  11605 void nsHttpChannel::SetOriginHeader() {
  11606  auto* triggeringPrincipal =
  11607      BasePrincipal::Cast(mLoadInfo->TriggeringPrincipal());
  11608 
  11609  if (triggeringPrincipal->IsSystemPrincipal()) {
  11610    // We can't infer an Origin header from the system principal,
  11611    // this means system requests use whatever Origin header was specified.
  11612    return;
  11613  }
  11614  bool isAddonRequest = triggeringPrincipal->AddonPolicy() ||
  11615                        triggeringPrincipal->ContentScriptAddonPolicy();
  11616 
  11617  // Non-standard: Handle already existing Origin header.
  11618  nsAutoCString existingHeader;
  11619  (void)mRequestHead.GetHeader(nsHttp::Origin, existingHeader);
  11620  if (!existingHeader.IsEmpty()) {
  11621    LOG(("nsHttpChannel::SetOriginHeader Origin header already present"));
  11622    auto const shouldNullifyOriginHeader =
  11623        [&existingHeader, isAddonRequest](nsHttpChannel* aChannel) {
  11624          nsCOMPtr<nsIURI> uri;
  11625          nsresult rv = NS_NewURI(getter_AddRefs(uri), existingHeader);
  11626          if (NS_FAILED(rv)) {
  11627            return false;
  11628          }
  11629 
  11630          if (HasNullRequestOrigin(aChannel, uri, isAddonRequest)) {
  11631            return true;
  11632          }
  11633 
  11634          nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo();
  11635          if (info->GetTainting() == mozilla::LoadTainting::CORS) {
  11636            return false;
  11637          }
  11638 
  11639          return ReferrerInfo::ShouldSetNullOriginHeader(aChannel, uri);
  11640        };
  11641 
  11642    if (!existingHeader.EqualsLiteral("null") &&
  11643        shouldNullifyOriginHeader(this)) {
  11644      LOG(("nsHttpChannel::SetOriginHeader null Origin by Referrer-Policy"));
  11645      MOZ_ALWAYS_SUCCEEDS(
  11646          mRequestHead.SetHeader(nsHttp::Origin, "null"_ns, false /* merge */));
  11647    }
  11648    return;
  11649  }
  11650 
  11651  if (StaticPrefs::network_http_sendOriginHeader() == 0) {
  11652    // Custom user setting: 0 means never send Origin header.
  11653    return;
  11654  }
  11655 
  11656  // Step 2. Let serializedOrigin be the result of byte-serializing a request
  11657  // origin with request.
  11658  nsAutoCString serializedOrigin;
  11659  nsCOMPtr<nsIURI> uri;
  11660  {
  11661    if (NS_FAILED(triggeringPrincipal->GetURI(getter_AddRefs(uri)))) {
  11662      return;
  11663    }
  11664 
  11665    if (!uri) {
  11666      if (isAddonRequest) {
  11667        // For add-on compatibility prefer sending no header at all
  11668        // instead of `Origin: null`.
  11669        return;
  11670      }
  11671 
  11672      // Otherwise use "null" when the triggeringPrincipal's URI is nullptr.
  11673      serializedOrigin.AssignLiteral("null");
  11674    } else if (HasNullRequestOrigin(this, uri, isAddonRequest)) {
  11675      serializedOrigin.AssignLiteral("null");
  11676    } else {
  11677      nsContentUtils::GetWebExposedOriginSerialization(uri, serializedOrigin);
  11678    }
  11679  }
  11680 
  11681  // Step 3. If request’s response tainting is "cors" or request’s mode is
  11682  // "websocket", then append (`Origin`, serializedOrigin) to request’s header
  11683  // list.
  11684  //
  11685  // Note: We don't handle "websocket" here (yet?).
  11686  if (mLoadInfo->GetTainting() == mozilla::LoadTainting::CORS) {
  11687    MOZ_ALWAYS_SUCCEEDS(mRequestHead.SetHeader(nsHttp::Origin, serializedOrigin,
  11688                                               false /* merge */));
  11689    return;
  11690  }
  11691 
  11692  // Step 4. Otherwise, if request’s method is neither `GET` nor `HEAD`, then:
  11693  if (mRequestHead.IsGet() || mRequestHead.IsHead()) {
  11694    // Modified Step 4 in case storage-access-headers are enabled
  11695    if (!StaticPrefs::dom_storage_access_enabled() ||
  11696        !StaticPrefs::dom_storage_access_headers_enabled()) {
  11697      // proceed with unmodified fetch spec
  11698      return;
  11699    } else {
  11700      // the result of getting `Sec-Fetch-Storage-Access` from request’s header
  11701      // list is "inactive"
  11702      nsAutoCString storageAccess;
  11703      nsresult rv =
  11704          GetRequestHeader("Sec-Fetch-Storage-Access"_ns, storageAccess);
  11705      if (NS_FAILED(rv) || !storageAccess.EqualsLiteral("inactive")) {
  11706        return;
  11707      }
  11708    }
  11709  }
  11710 
  11711  if (!serializedOrigin.EqualsLiteral("null")) {
  11712    // Step 4.1. (Implemented by ReferrerInfo::ShouldSetNullOriginHeader)
  11713    if (ReferrerInfo::ShouldSetNullOriginHeader(this, uri)) {
  11714      serializedOrigin.AssignLiteral("null");
  11715    } else if (StaticPrefs::network_http_sendOriginHeader() == 1) {
  11716      // Non-standard: Restrict Origin to same-origin loads if requested by user
  11717      nsAutoCString currentOrigin;
  11718      nsContentUtils::GetWebExposedOriginSerialization(mURI, currentOrigin);
  11719      if (!serializedOrigin.EqualsIgnoreCase(currentOrigin.get())) {
  11720        // Origin header suppressed by user setting.
  11721        serializedOrigin.AssignLiteral("null");
  11722      }
  11723    }
  11724  }
  11725 
  11726  // Step 4.2. Append (`Origin`, serializedOrigin) to request’s header list.
  11727  MOZ_ALWAYS_SUCCEEDS(mRequestHead.SetHeader(nsHttp::Origin, serializedOrigin,
  11728                                             false /* merge */));
  11729 }
  11730 
  11731 void nsHttpChannel::SetDoNotTrack() {
  11732  /**
  11733   * 'DoNotTrack' header should be added if 'privacy.donottrackheader.enabled'
  11734   * is true.
  11735   */
  11736  if (StaticPrefs::privacy_donottrackheader_enabled()) {
  11737    DebugOnly<nsresult> rv =
  11738        mRequestHead.SetHeader(nsHttp::DoNotTrack, "1"_ns, false);
  11739    MOZ_ASSERT(NS_SUCCEEDED(rv));
  11740  }
  11741 }
  11742 
  11743 void nsHttpChannel::SetGlobalPrivacyControl() {
  11744  MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
  11745 
  11746  if (StaticPrefs::privacy_globalprivacycontrol_functionality_enabled() &&
  11747      (StaticPrefs::privacy_globalprivacycontrol_enabled() ||
  11748       (StaticPrefs::privacy_globalprivacycontrol_pbmode_enabled() &&
  11749        NS_UsePrivateBrowsing(this)))) {
  11750    // Send the header with a value of 1 to indicate opting-out
  11751    DebugOnly<nsresult> rv =
  11752        mRequestHead.SetHeader(nsHttp::GlobalPrivacyControl, "1"_ns, false);
  11753  }
  11754 }
  11755 
  11756 void nsHttpChannel::ReportRcwnStats(bool isFromNet) {
  11757  if (!StaticPrefs::network_http_rcwn_enabled()) {
  11758    return;
  11759  }
  11760 
  11761  if (isFromNet) {
  11762    // Race was won by the network if:
  11763    // * we hadn't gotten a response from the cache yet when we started pumping
  11764    //   data from the network, whether or not the cache entry would have
  11765    //   been valid or not.
  11766    // * If we did get a response from the cache first, but it wasn't valid or
  11767    // just
  11768    //   indicated there was no entry, then it's not a network RCWN win
  11769    // * Note that we consider it a Network win even if an invalid/expired cache
  11770    //   response comes in after the network response.
  11771 
  11772    if (mRaceCacheWithNetwork && LoadNetworkWonRace()) {
  11773      PROFILER_MARKER_TEXT(
  11774          "RCWN", NETWORK, {},
  11775          nsPrintfCString("Network won valid = %d, channel %p, URI %s",
  11776                          LoadCachedContentIsValid(), this, mSpec.get()));
  11777      gIOService->IncrementNetWonRequestNumber();
  11778      glean::network::race_cache_bandwidth_race_network_win.Accumulate(
  11779          mTransferSize);
  11780    } else {
  11781      PROFILER_MARKER_TEXT(
  11782          "RCWN", NETWORK, {},
  11783          nsPrintfCString(
  11784              "Cache won or was replaced, valid = %d, channel %p, URI %s",
  11785              LoadCachedContentIsValid(), this, mSpec.get()));
  11786    }
  11787  } else {
  11788    if (mRaceCacheWithNetwork || mRaceDelay) {
  11789      PROFILER_MARKER_TEXT(
  11790          "RCWN", NETWORK, {},
  11791          nsPrintfCString("Cache won valid=%d, channel %p, URI %s",
  11792                          LoadCachedContentIsValid(), this, mSpec.get()));
  11793      gIOService->IncrementCacheWonRequestNumber();
  11794      glean::network::race_cache_bandwidth_race_cache_win.Accumulate(
  11795          mTransferSize);
  11796    }
  11797  }
  11798 
  11799  gIOService->IncrementRequestNumber();
  11800 }
  11801 
  11802 void nsHttpChannel::ReportSystemChannelTelemetry(nsresult status) {
  11803  // Use status and httpStatus to determine
  11804  // if it was successful, and if we had connectivity / offline in this time
  11805  nsAutoCString domain;
  11806  mOriginalURI->GetHost(domain);
  11807 
  11808  if (!LoadUsedNetwork()) {
  11809    // We're not really interested in any cached requests.
  11810    return;
  11811  }
  11812 
  11813  if (!StringEndsWith(domain, ".mozilla.org"_ns) &&
  11814      !StringEndsWith(domain, ".mozilla.com"_ns) &&
  11815      mEssentialDomainCategory.isNothing()) {
  11816    return;
  11817  }
  11818 
  11819  nsAutoCString label("ok"_ns);
  11820  if (NS_FAILED(status)) {
  11821    if (mCanceled) {
  11822      // The request was cancelled.
  11823      label = "cancel"_ns;
  11824    } else if (NS_IsOffline()) {
  11825      // The error occured while all interfaces are offline
  11826      label = "offline"_ns;
  11827    } else if (!hasConnectivity()) {
  11828      // The error occured while the browser didn't have connectivity
  11829      label = "connectivity"_ns;
  11830    } else if (status == NS_ERROR_UNKNOWN_HOST) {
  11831      // The failure was a DNS error
  11832      label = "dns"_ns;
  11833    } else if (NS_ERROR_GET_MODULE(status) == NS_ERROR_MODULE_SECURITY) {
  11834      // The error was due to TLS
  11835      label = "tls_fail"_ns;
  11836    } else if (status == NS_ERROR_NET_RESET) {
  11837      label = "reset"_ns;
  11838    } else if (status == NS_ERROR_NET_TIMEOUT) {
  11839      label = "timeout"_ns;
  11840    } else if (status == NS_ERROR_CONNECTION_REFUSED) {
  11841      label = "refused"_ns;
  11842    } else if (status == NS_ERROR_NET_PARTIAL_TRANSFER) {
  11843      label = "partial"_ns;
  11844    } else {
  11845      // Unspecified error. If this bucket is too big we might add other labels.
  11846      label = "other"_ns;
  11847    }
  11848  } else if (mResponseHead && mResponseHead->Status() / 100 != 2) {
  11849    // There was no channel error, but the server responded with a non-2XX
  11850    // status code.
  11851    label = "http_status";
  11852  }
  11853 
  11854  // This is the first time failure channel
  11855  if (mEssentialDomainCategory.isNothing()) {
  11856    auto category = GetEssentialDomainCategory(domain);
  11857    switch (category) {
  11858      case EssentialDomainCategory::SubAddonsMozillaOrg: {
  11859        mozilla::glean::network::system_channel_addonversion_status.Get(label)
  11860            .Add(1);
  11861        return;
  11862      }
  11863      case EssentialDomainCategory::AddonsMozillaOrg: {
  11864        mozilla::glean::network::system_channel_addon_status.Get(label).Add(1);
  11865        return;
  11866      }
  11867      case EssentialDomainCategory::Aus5MozillaOrg: {
  11868        mozilla::glean::network::system_channel_update_status.Get(label).Add(1);
  11869        return;
  11870      }
  11871      case EssentialDomainCategory::RemoteSettings: {
  11872        mozilla::glean::network::system_channel_remote_settings_status
  11873            .Get(label)
  11874            .Add(1);
  11875        return;
  11876      }
  11877      case EssentialDomainCategory::Telemetry: {
  11878        mozilla::glean::network::system_channel_telemetry_status.Get(label).Add(
  11879            1);
  11880        return;
  11881      }
  11882      default: {
  11883        // Not one of the probes we recorded earlier.
  11884        mozilla::glean::network::system_channel_other_status.Get(label).Add(1);
  11885        return;
  11886      }
  11887    }
  11888  }
  11889 
  11890  // This is a retry.
  11891  switch (mEssentialDomainCategory.ref()) {
  11892    case EssentialDomainCategory::SubAddonsMozillaOrg: {
  11893      mozilla::glean::network::retried_system_channel_addonversion_status
  11894          .Get(label)
  11895          .Add(1);
  11896      return;
  11897    }
  11898    case EssentialDomainCategory::AddonsMozillaOrg: {
  11899      mozilla::glean::network::retried_system_channel_addon_status.Get(label)
  11900          .Add(1);
  11901      return;
  11902    }
  11903    case EssentialDomainCategory::Aus5MozillaOrg: {
  11904      mozilla::glean::network::retried_system_channel_update_status.Get(label)
  11905          .Add(1);
  11906      return;
  11907    }
  11908    case EssentialDomainCategory::RemoteSettings: {
  11909      mozilla::glean::network::retried_system_channel_remote_settings_status
  11910          .Get(label)
  11911          .Add(1);
  11912      return;
  11913    }
  11914    case EssentialDomainCategory::Telemetry: {
  11915      mozilla::glean::network::retried_system_channel_telemetry_status
  11916          .Get(label)
  11917          .Add(1);
  11918      return;
  11919    }
  11920    default: {
  11921      // Not one of the probes we recorded earlier.
  11922      mozilla::glean::network::retried_system_channel_other_status.Get(label)
  11923          .Add(1);
  11924      return;
  11925    }
  11926  }
  11927 }
  11928 
  11929 NS_IMETHODIMP
  11930 nsHttpChannel::GetAllowRacing(bool* aAllowRacing) {
  11931  *aAllowRacing = mAllowRCWN;
  11932  return NS_OK;
  11933 }
  11934 
  11935 NS_IMETHODIMP
  11936 nsHttpChannel::SetAllowRacing(bool aAllowRacing) {
  11937  mAllowRCWN = aAllowRacing;
  11938  return NS_OK;
  11939 }
  11940 
  11941 NS_IMETHODIMP
  11942 nsHttpChannel::Test_delayCacheEntryOpeningBy(int32_t aTimeout) {
  11943  LOG(("nsHttpChannel::Test_delayCacheEntryOpeningBy this=%p timeout=%d", this,
  11944       aTimeout));
  11945  MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
  11946  mRaceCacheWithNetwork = true;
  11947  mCacheOpenDelay = aTimeout;
  11948  if (mCacheOpenTimer) {
  11949    mCacheOpenTimer->SetDelay(aTimeout);
  11950  }
  11951  return NS_OK;
  11952 }
  11953 
  11954 NS_IMETHODIMP
  11955 nsHttpChannel::Test_triggerDelayedOpenCacheEntry() {
  11956  LOG(("nsHttpChannel::Test_triggerDelayedOpenCacheEntry this=%p", this));
  11957  MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
  11958  nsresult rv;
  11959  if (!mCacheOpenDelay) {
  11960    // No delay was set.
  11961    return NS_ERROR_NOT_AVAILABLE;
  11962  }
  11963  if (!mCacheOpenFunc) {
  11964    // There should be a runnable.
  11965    return NS_ERROR_FAILURE;
  11966  }
  11967  if (mCacheOpenTimer) {
  11968    rv = mCacheOpenTimer->Cancel();
  11969    if (NS_FAILED(rv)) {
  11970      return rv;
  11971    }
  11972    mCacheOpenTimer = nullptr;
  11973  }
  11974  mCacheOpenDelay = 0;
  11975  // Avoid re-entrancy issues by nulling our mCacheOpenFunc before calling it.
  11976  std::function<void(nsHttpChannel*)> cacheOpenFunc = nullptr;
  11977  std::swap(cacheOpenFunc, mCacheOpenFunc);
  11978  cacheOpenFunc(this);
  11979 
  11980  return NS_OK;
  11981 }
  11982 
  11983 nsresult nsHttpChannel::TriggerNetworkWithDelay(uint32_t aDelay) {
  11984  MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
  11985 
  11986  LOG(("nsHttpChannel::TriggerNetworkWithDelay [this=%p, delay=%u]\n", this,
  11987       aDelay));
  11988 
  11989  if (mCanceled) {
  11990    LOG(("  channel was canceled.\n"));
  11991    return mStatus;
  11992  }
  11993 
  11994  // If a network request has already gone out, there is no point in
  11995  // doing this again.
  11996  if (mNetworkTriggered) {
  11997    LOG(("  network already triggered. Returning.\n"));
  11998    return NS_OK;
  11999  }
  12000 
  12001  if (mNetworkTriggerDelay) {
  12002    aDelay = mNetworkTriggerDelay;
  12003  }
  12004 
  12005  if (!aDelay) {
  12006    // We cannot call TriggerNetwork() directly here, because it would
  12007    // cause performance regression in tp6 tests, see bug 1398847.
  12008    return NS_DispatchToMainThread(
  12009        NewRunnableMethod("net::nsHttpChannel::TriggerNetworkWithDelay", this,
  12010                          &nsHttpChannel::TriggerNetwork),
  12011        NS_DISPATCH_NORMAL);
  12012  }
  12013 
  12014  MOZ_ASSERT(!mNetworkTriggerTimer);
  12015  mNetworkTriggerTimer = NS_NewTimer();
  12016  auto callback = MakeRefPtr<TimerCallback>(this);
  12017  LOG(("Creating new networkTriggertimer for delay"));
  12018  mNetworkTriggerTimer->InitWithCallback(callback, aDelay,
  12019                                         nsITimer::TYPE_ONE_SHOT);
  12020  return NS_OK;
  12021 }
  12022 
  12023 nsresult nsHttpChannel::TriggerNetwork() {
  12024  MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
  12025 
  12026  LOG(("nsHttpChannel::TriggerNetwork [this=%p]\n", this));
  12027 
  12028  if (mCanceled) {
  12029    LOG(("  channel was canceled.\n"));
  12030    return mStatus;
  12031  }
  12032 
  12033  // If a network request has already gone out, there is no point in
  12034  // doing this again.
  12035  if (mNetworkTriggered) {
  12036    LOG(("  network already triggered. Returning.\n"));
  12037    return NS_OK;
  12038  }
  12039 
  12040  mNetworkTriggered = true;
  12041  if (mNetworkTriggerTimer) {
  12042    mNetworkTriggerTimer->Cancel();
  12043    mNetworkTriggerTimer = nullptr;
  12044  }
  12045 
  12046  // If we are waiting for a proxy request, that means we can't trigger
  12047  // the next step just yet. We need for mConnectionInfo to be non-null
  12048  // before we call ContinueConnect. OnProxyAvailable will trigger
  12049  // BeginConnect, and Connect will call ContinueConnect even if it's
  12050  // for the cache callbacks.
  12051  if (mProxyRequest) {
  12052    LOG(("  proxy request in progress. Delaying network trigger.\n"));
  12053    mWaitingForProxy = true;
  12054    return NS_OK;
  12055  }
  12056 
  12057  // If |mCacheOpenFunc| is assigned, we're delaying opening the entry to
  12058  // simulate racing. Although cache entry opening hasn't started yet, we're
  12059  // actually racing, so we must set mRaceCacheWithNetwork to true now.
  12060  mRaceCacheWithNetwork =
  12061      AwaitingCacheCallbacks() &&
  12062      (mCacheOpenFunc || StaticPrefs::network_http_rcwn_enabled());
  12063 
  12064  LOG(("  triggering network rcwn=%d\n", bool(mRaceCacheWithNetwork)));
  12065  return ContinueConnect();
  12066 }
  12067 
  12068 nsresult nsHttpChannel::OnSuspendTimeout() {
  12069  MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
  12070 
  12071  LOG(("nsHttpChannel::OnSuspendTimeout [this=%p]\n", this));
  12072 
  12073  // If we're still suspended and have a cache entry, enable bypass mode
  12074  // This allows any waiting or future listeners to continue
  12075  if (mSuspendCount > 0 && mCacheEntry) {
  12076    LOG(("  suspend timeout: bypassing writer lock"));
  12077    mCacheEntry->SetBypassWriterLock(true);
  12078    mBypassCacheWriterSet = true;
  12079  }
  12080 
  12081  return NS_OK;
  12082 }
  12083 
  12084 void nsHttpChannel::MaybeRaceCacheWithNetwork() {
  12085  nsresult rv;
  12086 
  12087  nsCOMPtr<nsINetworkLinkService> netLinkSvc;
  12088  netLinkSvc = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
  12089  if (NS_FAILED(rv)) {
  12090    return;
  12091  }
  12092 
  12093  uint32_t linkType;
  12094  rv = netLinkSvc->GetLinkType(&linkType);
  12095  if (NS_FAILED(rv)) {
  12096    return;
  12097  }
  12098 
  12099  if (!(linkType == nsINetworkLinkService::LINK_TYPE_ETHERNET ||
  12100 #ifndef MOZ_WIDGET_ANDROID
  12101        // On Android we don't assume an unknown link type is unmetered
  12102        linkType == nsINetworkLinkService::LINK_TYPE_UNKNOWN ||
  12103 #endif
  12104        linkType == nsINetworkLinkService::LINK_TYPE_USB ||
  12105        linkType == nsINetworkLinkService::LINK_TYPE_WIFI)) {
  12106    return;
  12107  }
  12108 
  12109  // Don't trigger the network if the load flags say so.
  12110  if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_NO_NETWORK_IO)) {
  12111    return;
  12112  }
  12113 
  12114  // We must not race if the channel has a failure status code.
  12115  if (NS_FAILED(mStatus)) {
  12116    return;
  12117  }
  12118 
  12119  // If a CORS Preflight is required we must not race.
  12120  if (LoadRequireCORSPreflight() && !LoadIsCorsPreflightDone()) {
  12121    return;
  12122  }
  12123 
  12124  if (CacheFileUtils::CachePerfStats::IsCacheSlow()) {
  12125    // If the cache is slow, trigger the network request immediately.
  12126    mRaceDelay = 0;
  12127  } else {
  12128    // Give cache a headstart of 3 times the average cache entry open time.
  12129    mRaceDelay = CacheFileUtils::CachePerfStats::GetAverage(
  12130                     CacheFileUtils::CachePerfStats::ENTRY_OPEN, true) *
  12131                 3;
  12132    // We use microseconds in CachePerfStats but we need milliseconds
  12133    // for TriggerNetwork.
  12134    mRaceDelay /= 1000;
  12135  }
  12136 
  12137  mRaceDelay = std::clamp<uint32_t>(
  12138      mRaceDelay, StaticPrefs::network_http_rcwn_min_wait_before_racing_ms(),
  12139      StaticPrefs::network_http_rcwn_max_wait_before_racing_ms());
  12140 
  12141  MOZ_ASSERT(StaticPrefs::network_http_rcwn_enabled() || mNetworkTriggerDelay,
  12142             "The pref must be turned on.");
  12143  LOG(("nsHttpChannel::MaybeRaceCacheWithNetwork [this=%p, delay=%u]\n", this,
  12144       mRaceDelay));
  12145 
  12146  TriggerNetworkWithDelay(mRaceDelay);
  12147 }
  12148 
  12149 NS_IMETHODIMP
  12150 nsHttpChannel::Test_triggerNetwork(int32_t aTimeout) {
  12151  LOG(("nsHttpChannel::Test_triggerNetwork this=%p timeout=%d", this,
  12152       aTimeout));
  12153  MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
  12154 
  12155  // We set the trigger delay to the specified timeout.
  12156  mRaceCacheWithNetwork = true;
  12157  mNetworkTriggerDelay = aTimeout;
  12158 
  12159  // If we already have a timer, set the delay/
  12160  if (mNetworkTriggerTimer) {
  12161    // If the timeout is 0 and there is a timer, we can trigger
  12162    // the network immediately.
  12163    MOZ_ASSERT(LoadWasOpened(), "Must have been opened before");
  12164    if (!aTimeout) {
  12165      return TriggerNetwork();
  12166    }
  12167    mNetworkTriggerTimer->SetDelay(aTimeout);
  12168  }
  12169  return NS_OK;
  12170 }
  12171 
  12172 nsHttpChannel::TimerCallback::TimerCallback(nsHttpChannel* aChannel)
  12173    : mChannel(aChannel) {}
  12174 
  12175 NS_IMPL_ISUPPORTS(nsHttpChannel::TimerCallback, nsITimerCallback, nsINamed)
  12176 
  12177 NS_IMETHODIMP
  12178 nsHttpChannel::TimerCallback::GetName(nsACString& aName) {
  12179  aName.AssignLiteral("nsHttpChannel");
  12180  return NS_OK;
  12181 }
  12182 
  12183 NS_IMETHODIMP
  12184 nsHttpChannel::TimerCallback::Notify(nsITimer* aTimer) {
  12185  if (aTimer == mChannel->mCacheOpenTimer) {
  12186    return mChannel->Test_triggerDelayedOpenCacheEntry();
  12187  }
  12188  if (aTimer == mChannel->mNetworkTriggerTimer) {
  12189    return mChannel->TriggerNetwork();
  12190  }
  12191  if (aTimer == mChannel->mSuspendTimer) {
  12192    return mChannel->OnSuspendTimeout();
  12193  }
  12194  MOZ_CRASH("Unknown timer");
  12195 
  12196  return NS_OK;
  12197 }
  12198 
  12199 bool nsHttpChannel::EligibleForTailing() {
  12200  if (!(mClassOfService.Flags() & nsIClassOfService::Tail)) {
  12201    return false;
  12202  }
  12203 
  12204  if (mClassOfService.Flags() &
  12205      (nsIClassOfService::UrgentStart | nsIClassOfService::Leader |
  12206       nsIClassOfService::TailForbidden)) {
  12207    return false;
  12208  }
  12209 
  12210  if (mClassOfService.Flags() & nsIClassOfService::Unblocked &&
  12211      !(mClassOfService.Flags() & nsIClassOfService::TailAllowed)) {
  12212    return false;
  12213  }
  12214 
  12215  if (IsNavigation()) {
  12216    return false;
  12217  }
  12218 
  12219  return true;
  12220 }
  12221 
  12222 bool nsHttpChannel::WaitingForTailUnblock() {
  12223  nsresult rv;
  12224 
  12225  if (!gHttpHandler->IsTailBlockingEnabled()) {
  12226    LOG(("nsHttpChannel %p tail-blocking disabled", this));
  12227    return false;
  12228  }
  12229 
  12230  if (!EligibleForTailing()) {
  12231    LOG(("nsHttpChannel %p not eligible for tail-blocking", this));
  12232    AddAsNonTailRequest();
  12233    return false;
  12234  }
  12235 
  12236  if (!EnsureRequestContext()) {
  12237    LOG(("nsHttpChannel %p no request context", this));
  12238    return false;
  12239  }
  12240 
  12241  LOG(("nsHttpChannel::WaitingForTailUnblock this=%p, rc=%p", this,
  12242       mRequestContext.get()));
  12243 
  12244  bool blocked;
  12245  rv = mRequestContext->IsContextTailBlocked(this, &blocked);
  12246  if (NS_FAILED(rv)) {
  12247    return false;
  12248  }
  12249 
  12250  LOG(("  blocked=%d", blocked));
  12251 
  12252  return blocked;
  12253 }
  12254 
  12255 //-----------------------------------------------------------------------------
  12256 // nsHttpChannel::nsIRequestTailUnblockCallback
  12257 //-----------------------------------------------------------------------------
  12258 
  12259 // Must be implemented in the leaf class because we don't have
  12260 // AsyncAbort in HttpBaseChannel.
  12261 NS_IMETHODIMP
  12262 nsHttpChannel::OnTailUnblock(nsresult rv) {
  12263  LOG(("nsHttpChannel::OnTailUnblock this=%p rv=%" PRIx32 " rc=%p", this,
  12264       static_cast<uint32_t>(rv), mRequestContext.get()));
  12265  AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnTailUnblock", NETWORK,
  12266                            Flow::FromPointer(this));
  12267  MOZ_RELEASE_ASSERT(mOnTailUnblock);
  12268 
  12269  if (NS_FAILED(mStatus)) {
  12270    rv = mStatus;
  12271  }
  12272 
  12273  if (NS_SUCCEEDED(rv)) {
  12274    auto callback = mOnTailUnblock;
  12275    mOnTailUnblock = nullptr;
  12276    rv = (this->*callback)();
  12277  }
  12278 
  12279  if (NS_FAILED(rv)) {
  12280    CloseCacheEntry(false);
  12281    return AsyncAbort(rv);
  12282  }
  12283 
  12284  return NS_OK;
  12285 }
  12286 
  12287 void nsHttpChannel::SetWarningReporter(
  12288    HttpChannelSecurityWarningReporter* aReporter) {
  12289  LOG(("nsHttpChannel [this=%p] SetWarningReporter [%p]", this, aReporter));
  12290  mWarningReporter = aReporter;
  12291 }
  12292 
  12293 HttpChannelSecurityWarningReporter* nsHttpChannel::GetWarningReporter() {
  12294  LOG(("nsHttpChannel [this=%p] GetWarningReporter [%p]", this,
  12295       mWarningReporter.get()));
  12296  return mWarningReporter.get();
  12297 }
  12298 
  12299 // The specification for ORB is currently being written:
  12300 // https://whatpr.org/fetch/1442.html#orb-algorithm
  12301 // The `opaque-response-safelist check` is implemented in:
  12302 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckBeforeSniff`
  12303 // * `nsHttpChannel::DisableIsOpaqueResponseAllowedAfterSniffCheck`
  12304 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckAfterSniff`
  12305 // * `OpaqueResponseBlocker::ValidateJavaScript`
  12306 //
  12307 // Should only be called by nsMediaSniffer::GetMIMETypeFromContent and
  12308 // imageLoader::GetMIMETypeFromContent when the content type can be
  12309 // recognized by these sniffers.
  12310 void nsHttpChannel::DisableIsOpaqueResponseAllowedAfterSniffCheck(
  12311    SnifferType aType) {
  12312  // https://whatpr.org/fetch/1442.html#orb-algorithm
  12313  // This method covers steps, 8 and 10.
  12314  MOZ_ASSERT(XRE_IsParentProcess());
  12315 
  12316  if (NeedOpaqueResponseAllowedCheckAfterSniff()) {
  12317    MOZ_ASSERT(mCachedOpaqueResponseBlockingPref);
  12318 
  12319    // If the sniffer type is media and the request comes from a media element,
  12320    // we would like to check:
  12321    // - Whether the information provided by the media element shows it's an
  12322    // initial request.
  12323    // - Whether the response's status is either 200 or 206.
  12324    //
  12325    // If any of the results is false, then we set
  12326    // mBlockOpaqueResponseAfterSniff to true and block the response later.
  12327    if (aType == SnifferType::Media) {
  12328      // Step 8
  12329      MOZ_ASSERT(mLoadInfo);
  12330 
  12331      bool isMediaRequest;
  12332      mLoadInfo->GetIsMediaRequest(&isMediaRequest);
  12333      if (isMediaRequest) {
  12334        bool isInitialRequest;
  12335        mLoadInfo->GetIsMediaInitialRequest(&isInitialRequest);
  12336        MOZ_ASSERT(isInitialRequest);
  12337 
  12338        if (!isInitialRequest) {
  12339          // Step 8.1
  12340          BlockOpaqueResponseAfterSniff(
  12341              u"media request after sniffing, but not initial request"_ns,
  12342              OpaqueResponseBlockedTelemetryReason::eMediaNotInitial);
  12343          return;
  12344        }
  12345 
  12346        if (mResponseHead->Status() != 200 && mResponseHead->Status() != 206) {
  12347          // Step 8.2
  12348          BlockOpaqueResponseAfterSniff(
  12349              u"media request's response status is neither 200 nor 206"_ns,
  12350              OpaqueResponseBlockedTelemetryReason::eMediaIncorrectResp);
  12351          return;
  12352        }
  12353      }
  12354    }
  12355 
  12356    // Step 8.3 if `aType == SnifferType::Media`
  12357    // Step 9 can be skipped, only `HTMLMediaElement` ever sets isMediaRequest.
  12358    // Step 10 if `aType == SnifferType::Image`
  12359    AllowOpaqueResponseAfterSniff();
  12360  }
  12361 }
  12362 
  12363 namespace {
  12364 
  12365 class CopyNonDefaultHeaderVisitor final : public nsIHttpHeaderVisitor {
  12366  nsCOMPtr<nsIHttpChannel> mTarget;
  12367 
  12368  ~CopyNonDefaultHeaderVisitor() = default;
  12369 
  12370  NS_IMETHOD
  12371  VisitHeader(const nsACString& aHeader, const nsACString& aValue) override {
  12372    if (aValue.IsEmpty()) {
  12373      return mTarget->SetEmptyRequestHeader(aHeader);
  12374    }
  12375    return mTarget->SetRequestHeader(aHeader, aValue, false /* merge */);
  12376  }
  12377 
  12378 public:
  12379  explicit CopyNonDefaultHeaderVisitor(nsIHttpChannel* aTarget)
  12380      : mTarget(aTarget) {
  12381    MOZ_DIAGNOSTIC_ASSERT(mTarget);
  12382  }
  12383 
  12384  NS_DECL_ISUPPORTS
  12385 };
  12386 
  12387 NS_IMPL_ISUPPORTS(CopyNonDefaultHeaderVisitor, nsIHttpHeaderVisitor)
  12388 
  12389 }  // anonymous namespace
  12390 
  12391 nsresult nsHttpChannel::RedirectToInterceptedChannel() {
  12392  nsCOMPtr<nsINetworkInterceptController> controller;
  12393  GetCallback(controller);
  12394 
  12395  RefPtr<InterceptedHttpChannel> intercepted =
  12396      InterceptedHttpChannel::CreateForInterception(
  12397          mChannelCreationTime, mChannelCreationTimestamp, mAsyncOpenTime);
  12398 
  12399  nsCOMPtr<nsILoadInfo> redirectLoadInfo =
  12400      CloneLoadInfoForRedirect(mURI, nsIChannelEventSink::REDIRECT_INTERNAL);
  12401 
  12402  nsresult rv = intercepted->Init(
  12403      mURI, mCaps, static_cast<nsProxyInfo*>(mProxyInfo.get()),
  12404      mProxyResolveFlags, mProxyURI, mChannelId, redirectLoadInfo);
  12405 
  12406  rv = SetupReplacementChannel(mURI, intercepted, true,
  12407                               nsIChannelEventSink::REDIRECT_INTERNAL);
  12408  NS_ENSURE_SUCCESS(rv, rv);
  12409 
  12410  // Some APIs, like fetch(), allow content to set non-standard headers.
  12411  // Normally these APIs are responsible for copying these headers across
  12412  // redirects.  In the e10s parent-side intercept case, though, we currently
  12413  // "hide" the internal redirect to the InterceptedHttpChannel.  So the
  12414  // fetch() API does not have the opportunity to move headers over.
  12415  // Therefore, we do it automatically here.
  12416  //
  12417  // Once child-side interception is removed and the internal redirect no
  12418  // longer needs to be "hidden", then this header copying code can be
  12419  // removed.
  12420  nsCOMPtr<nsIHttpHeaderVisitor> visitor =
  12421      new CopyNonDefaultHeaderVisitor(intercepted);
  12422  rv = VisitNonDefaultRequestHeaders(visitor);
  12423  NS_ENSURE_SUCCESS(rv, rv);
  12424 
  12425  mRedirectChannel = intercepted;
  12426 
  12427  PushRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
  12428 
  12429  rv = gHttpHandler->AsyncOnChannelRedirect(
  12430      this, intercepted, nsIChannelEventSink::REDIRECT_INTERNAL);
  12431 
  12432  if (NS_SUCCEEDED(rv)) {
  12433    rv = WaitForRedirectCallback();
  12434  }
  12435 
  12436  if (NS_FAILED(rv)) {
  12437    AutoRedirectVetoNotifier notifier(this, rv);
  12438 
  12439    PopRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI);
  12440  }
  12441 
  12442  return rv;
  12443 }
  12444 
  12445 void nsHttpChannel::ReEvaluateReferrerAfterTrackingStatusIsKnown() {
  12446  nsCOMPtr<nsICookieJarSettings> cjs;
  12447  if (mLoadInfo) {
  12448    (void)mLoadInfo->GetCookieJarSettings(getter_AddRefs(cjs));
  12449  }
  12450  if (!cjs) {
  12451    cjs = net::CookieJarSettings::Create(mLoadInfo->GetLoadingPrincipal());
  12452  }
  12453  if (cjs->GetRejectThirdPartyContexts()) {
  12454    bool isPrivate = mLoadInfo->GetOriginAttributes().IsPrivateBrowsing();
  12455    // If our referrer has been set before, and our referrer policy is unset
  12456    // (default policy) if we thought the channel wasn't a third-party
  12457    // tracking channel, we may need to set our referrer with referrer policy
  12458    // once again to ensure our defaults properly take effect now.
  12459    if (mReferrerInfo) {
  12460      ReferrerInfo* referrerInfo =
  12461          static_cast<ReferrerInfo*>(mReferrerInfo.get());
  12462 
  12463      if (referrerInfo->IsPolicyOverrided() &&
  12464          referrerInfo->ReferrerPolicy() ==
  12465              ReferrerInfo::GetDefaultReferrerPolicy(nullptr, nullptr,
  12466                                                     isPrivate)) {
  12467        nsCOMPtr<nsIReferrerInfo> newReferrerInfo =
  12468            referrerInfo->CloneWithNewPolicy(
  12469                ReferrerInfo::GetDefaultReferrerPolicy(this, mURI, isPrivate));
  12470        // The arguments passed to SetReferrerInfoInternal here should mirror
  12471        // the arguments passed in
  12472        // HttpChannelChild::RecvOverrideReferrerInfoDuringBeginConnect().
  12473        SetReferrerInfoInternal(newReferrerInfo, false, true, true);
  12474 
  12475        nsCOMPtr<nsIParentChannel> parentChannel;
  12476        NS_QueryNotificationCallbacks(this, parentChannel);
  12477        RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel);
  12478        if (httpParent) {
  12479          httpParent->OverrideReferrerInfoDuringBeginConnect(newReferrerInfo);
  12480        }
  12481      }
  12482    }
  12483  }
  12484 }
  12485 
  12486 namespace {
  12487 
  12488 class BackgroundRevalidatingListener : public nsIStreamListener {
  12489  NS_DECL_ISUPPORTS
  12490 
  12491  NS_DECL_NSISTREAMLISTENER
  12492  NS_DECL_NSIREQUESTOBSERVER
  12493 
  12494 private:
  12495  virtual ~BackgroundRevalidatingListener() = default;
  12496 };
  12497 
  12498 NS_IMETHODIMP
  12499 BackgroundRevalidatingListener::OnStartRequest(nsIRequest* request) {
  12500  return NS_OK;
  12501 }
  12502 
  12503 NS_IMETHODIMP
  12504 BackgroundRevalidatingListener::OnDataAvailable(nsIRequest* request,
  12505                                                nsIInputStream* input,
  12506                                                uint64_t offset,
  12507                                                uint32_t count) {
  12508  uint32_t bytesRead = 0;
  12509  return input->ReadSegments(NS_DiscardSegment, nullptr, count, &bytesRead);
  12510 }
  12511 
  12512 NS_IMETHODIMP
  12513 BackgroundRevalidatingListener::OnStopRequest(nsIRequest* request,
  12514                                              nsresult status) {
  12515  if (NS_FAILED(status)) {
  12516    return status;
  12517  }
  12518 
  12519  nsCOMPtr<nsIHttpChannel> channel(do_QueryInterface(request));
  12520  if (gHttpHandler) {
  12521    gHttpHandler->OnBackgroundRevalidation(channel);
  12522  }
  12523  return NS_OK;
  12524 }
  12525 
  12526 NS_IMPL_ISUPPORTS(BackgroundRevalidatingListener, nsIStreamListener,
  12527                  nsIRequestObserver)
  12528 
  12529 }  // namespace
  12530 
  12531 void nsHttpChannel::PerformBackgroundCacheRevalidation() {
  12532  if (!StaticPrefs::network_http_stale_while_revalidate_enabled()) {
  12533    return;
  12534  }
  12535 
  12536  // This is a channel doing a revalidation. It shouldn't do it again.
  12537  if (mStaleRevalidation) {
  12538    return;
  12539  }
  12540 
  12541  LOG(("nsHttpChannel::PerformBackgroundCacheRevalidation %p", this));
  12542 
  12543  (void)NS_DispatchToMainThreadQueue(
  12544      NewIdleRunnableMethod(
  12545          "nsHttpChannel::PerformBackgroundCacheRevalidation", this,
  12546          &nsHttpChannel::PerformBackgroundCacheRevalidationNow),
  12547      EventQueuePriority::Idle);
  12548 }
  12549 
  12550 void nsHttpChannel::PerformBackgroundCacheRevalidationNow() {
  12551  LOG(("nsHttpChannel::PerformBackgroundCacheRevalidationNow %p", this));
  12552 
  12553  MOZ_ASSERT(NS_IsMainThread());
  12554 
  12555  nsresult rv;
  12556 
  12557  nsLoadFlags loadFlags = mLoadFlags | LOAD_ONLY_IF_MODIFIED | VALIDATE_ALWAYS |
  12558                          LOAD_BACKGROUND | LOAD_BYPASS_SERVICE_WORKER;
  12559 
  12560  nsCOMPtr<nsIChannel> validatingChannel;
  12561  rv = NS_NewChannelInternal(getter_AddRefs(validatingChannel), mURI, mLoadInfo,
  12562                             nullptr /* performance storage */, mLoadGroup,
  12563                             mCallbacks, loadFlags);
  12564  if (NS_FAILED(rv)) {
  12565    LOG(("  failed to created the channel, rv=0x%08x",
  12566         static_cast<uint32_t>(rv)));
  12567    return;
  12568  }
  12569 
  12570  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(validatingChannel));
  12571  MOZ_ASSERT(httpChannel);
  12572  nsCOMPtr<nsIHttpHeaderVisitor> visitor =
  12573      new CopyNonDefaultHeaderVisitor(httpChannel);
  12574  rv = VisitNonDefaultRequestHeaders(visitor);
  12575  if (NS_FAILED(rv)) {
  12576    LOG(("failed to copy headers to the validating channel, rv=0x%08x",
  12577         static_cast<uint32_t>(rv)));
  12578    return;
  12579  }
  12580 
  12581  nsCOMPtr<nsISupportsPriority> priority(do_QueryInterface(validatingChannel));
  12582  if (priority) {
  12583    priority->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
  12584  }
  12585 
  12586  nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(validatingChannel));
  12587  if (cos) {
  12588    cos->AddClassFlags(nsIClassOfService::Tail);
  12589  }
  12590 
  12591  RefPtr<nsHttpChannel> httpChan = do_QueryObject(validatingChannel);
  12592  if (httpChan) {
  12593    httpChan->mStaleRevalidation = true;
  12594  }
  12595 
  12596  RefPtr<BackgroundRevalidatingListener> listener =
  12597      new BackgroundRevalidatingListener();
  12598  rv = validatingChannel->AsyncOpen(listener);
  12599  if (NS_FAILED(rv)) {
  12600    LOG(("  failed to open the channel, rv=0x%08x", static_cast<uint32_t>(rv)));
  12601    return;
  12602  }
  12603 
  12604  LOG(("  %p is re-validating with a new channel %p", this,
  12605       validatingChannel.get()));
  12606 }
  12607 
  12608 NS_IMETHODIMP
  12609 nsHttpChannel::SetEarlyHintObserver(nsIEarlyHintObserver* aObserver) {
  12610  mEarlyHintObserver = aObserver;
  12611  return NS_OK;
  12612 }
  12613 
  12614 NS_IMETHODIMP
  12615 nsHttpChannel::EarlyHint(const nsACString& aLinkHeader,
  12616                         const nsACString& aReferrerPolicy,
  12617                         const nsACString& aCspHeader) {
  12618  LOG(("nsHttpChannel::EarlyHint.\n"));
  12619 
  12620  if (mEarlyHintObserver && nsContentUtils::ComputeIsSecureContext(this)) {
  12621    LOG(("nsHttpChannel::EarlyHint propagated.\n"));
  12622    mEarlyHintObserver->EarlyHint(aLinkHeader, aReferrerPolicy, aCspHeader);
  12623  }
  12624  return NS_OK;
  12625 }
  12626 
  12627 NS_IMETHODIMP nsHttpChannel::SetResponseOverride(
  12628    nsIReplacedHttpResponse* aReplacedHttpResponse) {
  12629  mOverrideResponse = new nsMainThreadPtrHolder<nsIReplacedHttpResponse>(
  12630      "nsIReplacedHttpResponse", aReplacedHttpResponse);
  12631 
  12632  if (LoadRequireCORSPreflight()) {
  12633    // Bug 1986615, Bug 1940738, responses provided via setResponseOverride will
  12634    // be handled before the preflight can be sent, flag it as done.
  12635    StoreIsCorsPreflightDone(true);
  12636  }
  12637 
  12638  return NS_OK;
  12639 }
  12640 
  12641 NS_IMETHODIMP nsHttpChannel::SetResponseStatus(uint32_t aStatus,
  12642                                               const nsACString& aStatusText) {
  12643  if (!mResponseHead) {
  12644    return NS_ERROR_NOT_AVAILABLE;
  12645  }
  12646 
  12647  nsAutoCString statusText(aStatusText);
  12648  nsAutoCString protocolVersion(
  12649      nsHttp::GetProtocolVersion(mResponseHead->Version()));
  12650  ToUpperCase(protocolVersion);
  12651 
  12652  nsPrintfCString line("%s %u %s", protocolVersion.get(), aStatus,
  12653                       statusText.get());
  12654 
  12655  return mResponseHead->ParseStatusLine(line);
  12656 }
  12657 
  12658 NS_IMETHODIMP nsHttpChannel::SetWebTransportSessionEventListener(
  12659    WebTransportSessionEventListener* aListener) {
  12660  mWebTransportSessionEventListener = aListener;
  12661  return NS_OK;
  12662 }
  12663 
  12664 already_AddRefed<WebTransportSessionEventListener>
  12665 nsHttpChannel::GetWebTransportSessionEventListener() {
  12666  RefPtr<WebTransportSessionEventListener> wt =
  12667      mWebTransportSessionEventListener;
  12668  return wt.forget();
  12669 }
  12670 
  12671 NS_IMETHODIMP nsHttpChannel::GetLastTransportStatus(
  12672    nsresult* aLastTransportStatus) {
  12673  *aLastTransportStatus = mLastTransportStatus;
  12674  return NS_OK;
  12675 }
  12676 
  12677 NS_IMETHODIMP
  12678 nsHttpChannel::GetCacheDisposition(CacheDisposition* aDisposition) {
  12679  if (!aDisposition) {
  12680    return NS_ERROR_INVALID_ARG;
  12681  }
  12682  *aDisposition = mCacheDisposition;
  12683  return NS_OK;
  12684 }
  12685 
  12686 }  // namespace net
  12687 }  // namespace mozilla