Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  nsHttpChannel.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set expandtab ts=4 sw=2 sts=2 cin: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


// HttpLog.h should generally be included first
#include "HttpLog.h"

#include <inttypes.h>

#include "mozilla/ScopeExit.h"
#include "mozilla/Sprintf.h"
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/glean/AntitrackingMetrics.h"
#include "mozilla/glean/NetwerkMetrics.h"
#include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
#include "mozilla/StoragePrincipalHelper.h"

#include "nsCOMPtr.h"
#include "nsContentSecurityUtils.h"
#include "nsHttp.h"
#include "nsHttpChannel.h"
#include "nsHttpChannelAuthProvider.h"
#include "nsHttpHandler.h"
#include "nsIStreamConverter.h"
#include "nsString.h"
#include "nsICacheStorageService.h"
#include "nsICacheStorage.h"
#include "nsICacheEntry.h"
#include "nsICryptoHash.h"
#include "nsIEffectiveTLDService.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsINetworkInterceptController.h"
#include "nsIStringBundle.h"
#include "nsIStreamListenerTee.h"
#include "nsISeekableStream.h"
#include "nsIProtocolProxyService2.h"
#include "nsIURLQueryStringStripper.h"
#include "nsIWebTransport.h"
#include "nsCRT.h"
#include "nsMimeTypes.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsIStreamTransportService.h"
#include "prnetdb.h"
#include "nsEscape.h"
#include "nsComponentManagerUtils.h"
#include "nsStreamUtils.h"
#include "nsIOService.h"
#include "nsDNSPrefetch.h"
#include "nsChannelClassifier.h"
#include "nsIRedirectResultListener.h"
#include "mozilla/TimeStamp.h"
#include "nsError.h"
#include "nsPrintfCString.h"
#include "nsQueryObject.h"
#include "nsThreadUtils.h"
#include "nsIConsoleService.h"
#include "nsINetworkErrorLogging.h"
#include "mozilla/AntiTrackingRedirectHeuristic.h"
#include "mozilla/AntiTrackingUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/PerfStats.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/Components.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_privacy.h"
#include "mozilla/StaticPrefs_security.h"
#include "sslt.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsContentSecurityManager.h"
#include "nsIClassOfService.h"
#include "CookieService.h"
#include "nsIPrincipal.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsITransportSecurityInfo.h"
#include "nsIWebProgressListener.h"
#include "LoadContextInfo.h"
#include "netCore.h"
#include "nsHttpTransaction.h"
#include "nsICancelable.h"
#include "nsIHttpChannelInternal.h"
#include "nsIPrompt.h"
#include "nsInputStreamPump.h"
#include "nsURLHelper.h"
#include "nsISocketTransport.h"
#include "nsIStreamConverterService.h"
#include "nsISiteSecurityService.h"
#include "nsString.h"
#include "nsStringStream.h"
#include "mozilla/dom/PerformanceStorage.h"
#include "mozilla/dom/ReferrerInfo.h"
#include "mozilla/Telemetry.h"
#include "AlternateServices.h"
#include "NetworkMarker.h"
#include "nsIHttpPushListener.h"
#include "nsIDNSRecord.h"
#include "mozilla/dom/Document.h"
#include "nsICompressConvStats.h"
#include "nsCORSListenerProxy.h"
#include "nsISocketProvider.h"
#include "mozilla/extensions/StreamFilterParent.h"
#include "mozilla/net/Predictor.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/NullPrincipal.h"
#include "CacheControlParser.h"
#include "nsMixedContentBlocker.h"
#include "CacheStorageService.h"
#include "HttpChannelParent.h"
#include "HttpTransactionParent.h"
#include "ThirdPartyUtil.h"
#include "InterceptedHttpChannel.h"
#include "../../cache2/CacheFileUtils.h"
#include "nsINetworkLinkService.h"
#include "mozilla/ContentBlockingAllowList.h"
#include "mozilla/dom/ServiceWorkerUtils.h"
#include "mozilla/dom/nsHTTPSOnlyStreamListener.h"
#include "mozilla/dom/nsHTTPSOnlyUtils.h"
#include "mozilla/net/AsyncUrlChannelClassifier.h"
#include "mozilla/net/CookieJarSettings.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "mozilla/net/OpaqueResponseUtils.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "HttpTrafficAnalyzer.h"
#include "mozilla/net/SocketProcessParent.h"
#include "mozilla/dom/SecFetch.h"
#include "mozilla/net/TRRService.h"
#include "nsUnknownDecoder.h"
#ifdef XP_WIN
#  include "HttpWinUtils.h"
#endif
#ifdef XP_MACOSX
#  include "MicrosoftEntraSSOUtils.h"
#endif
#ifdef FUZZING
#  include "mozilla/StaticPrefs_fuzzing.h"
#endif

namespace mozilla {

using namespace dom;

namespace net {

namespace {

// True if the local cache should be bypassed when processing a request.
#define BYPASS_LOCAL_CACHE(loadFlags, isPreferCacheLoadOverBypass) \
  ((loadFlags) & (nsIRequest::LOAD_BYPASS_CACHE |                  \
                  nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE) &&   \
   !(((loadFlags) & nsIRequest::LOAD_FROM_CACHE) &&                \
     (isPreferCacheLoadOverBypass)))

#define RECOVER_FROM_CACHE_FILE_ERROR(result) \
  ((result) == NS_ERROR_FILE_NOT_FOUND ||     \
   (result) == NS_ERROR_FILE_CORRUPTED || (result) == NS_ERROR_OUT_OF_MEMORY)

#define WRONG_RACING_RESPONSE_SOURCE(req)               \
  (mRaceCacheWithNetwork &&                             \
   (((mFirstResponseSource == RESPONSE_FROM_CACHE) &&   \
     ((req) != mCachePump)) ||                          \
    ((mFirstResponseSource == RESPONSE_FROM_NETWORK) && \
     ((req) != mTransactionPump))))

static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);

enum ChannelDisposition {
  kHttpCanceled = 0,
  kHttpDisk = 1,
  kHttpNetOK = 2,
  kHttpNetEarlyFail = 3,
  kHttpNetLateFail = 4,
  kHttpsCanceled = 8,
  kHttpsDisk = 9,
  kHttpsNetOK = 10,
  kHttpsNetEarlyFail = 11,
  kHttpsNetLateFail = 12
};

void AccumulateCacheHitTelemetry(CacheDisposition hitOrMiss,
                                 nsIChannel* aChannel) {
  nsCString key("UNKNOWN");

  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();

  nsAutoCString contentType;
  if (NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
    if (nsContentUtils::IsJavascriptMIMEType(
            NS_ConvertUTF8toUTF16(contentType))) {
      key.AssignLiteral("JAVASCRIPT");
    } else if (StringBeginsWith(contentType, "text/css"_ns) ||
               (loadInfo && loadInfo->GetExternalContentPolicyType() ==
                                ExtContentPolicy::TYPE_STYLESHEET)) {
      key.AssignLiteral("STYLESHEET");
    } else if (StringBeginsWith(contentType, "application/wasm"_ns)) {
      key.AssignLiteral("WASM");
    } else if (StringBeginsWith(contentType, "image/"_ns)) {
      key.AssignLiteral("IMAGE");
    } else if (StringBeginsWith(contentType, "video/"_ns)) {
      key.AssignLiteral("MEDIA");
    } else if (StringBeginsWith(contentType, "audio/"_ns)) {
      key.AssignLiteral("MEDIA");
    } else if (!StringBeginsWith(contentType,
                                 nsLiteralCString(UNKNOWN_CONTENT_TYPE))) {
      key.AssignLiteral("OTHER");
    }
  }

  Telemetry::LABELS_HTTP_CACHE_DISPOSITION_3 label =
      Telemetry::LABELS_HTTP_CACHE_DISPOSITION_3::Unresolved;
  switch (hitOrMiss) {
    case kCacheUnresolved:
      label = Telemetry::LABELS_HTTP_CACHE_DISPOSITION_3::Unresolved;
      break;
    case kCacheHit:
      label = Telemetry::LABELS_HTTP_CACHE_DISPOSITION_3::Hit;
      break;
    case kCacheHitViaReval:
      label = Telemetry::LABELS_HTTP_CACHE_DISPOSITION_3::HitViaReval;
      break;
    case kCacheMissedViaReval:
      label = Telemetry::LABELS_HTTP_CACHE_DISPOSITION_3::MissedViaReval;
      break;
    case kCacheMissed:
      label = Telemetry::LABELS_HTTP_CACHE_DISPOSITION_3::Missed;
      break;
    case kCacheUnknown:
      label = Telemetry::LABELS_HTTP_CACHE_DISPOSITION_3::Unknown;
      break;
  }

  Telemetry::AccumulateCategoricalKeyed(key, label);
  Telemetry::AccumulateCategoricalKeyed("ALL"_ns, label);
}

// Computes and returns a SHA1 hash of the input buffer. The input buffer
// must be a null-terminated string.
nsresult Hash(const char* buf, nsACString& hash) {
  nsresult rv;

  nsCOMPtr<nsICryptoHash> hasher =
      do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = hasher->Init(nsICryptoHash::SHA1);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = hasher->Update(reinterpret_cast<unsigned const char*>(buf), strlen(buf));
  NS_ENSURE_SUCCESS(rv, rv);

  rv = hasher->Finish(true, hash);
  NS_ENSURE_SUCCESS(rv, rv);

  return NS_OK;
}

class CookieVisitor final {
 public:
  explicit CookieVisitor(nsHttpResponseHead* aResponseHead) {
    nsAutoCString cookieHeader;
    if (NS_SUCCEEDED(
            aResponseHead->GetHeader(nsHttp::Set_Cookie, cookieHeader))) {
      for (const auto& cookie : cookieHeader.Split('\n')) {
        mCookieHeaders.AppendElement(cookie);
      }
    }
  }

  ~CookieVisitor() = default;

  const nsTArray<nsCString>& CookieHeaders() const { return mCookieHeaders; }

 private:
  nsTArray<nsCString> mCookieHeaders;
};
}  // unnamed namespace

// We only treat 3xx responses as redirects if they have a Location header and
// the status code is in a whitelist.
bool nsHttpChannel::WillRedirect(const nsHttpResponseHead& response) {
  return IsRedirectStatus(response.Status()) &&
         response.HasHeader(nsHttp::Location);
}

nsresult StoreAuthorizationMetaData(nsICacheEntry* entry,
                                    nsHttpRequestHead* requestHead);

class MOZ_STACK_CLASS AutoRedirectVetoNotifier {
 public:
  explicit AutoRedirectVetoNotifier(nsHttpChannel* channel, nsresult& aRv)
      : mChannel(channel), mRv(aRv) {
    if (mChannel->LoadHasAutoRedirectVetoNotifier()) {
      MOZ_CRASH("Nested AutoRedirectVetoNotifier on the stack");
      mChannel = nullptr;
      return;
    }

    mChannel->StoreHasAutoRedirectVetoNotifier(true);
  }
  ~AutoRedirectVetoNotifier() { ReportRedirectResult(mRv); }
  void RedirectSucceeded() { ReportRedirectResult(NS_OK); }

 private:
  nsHttpChannel* mChannel;
  bool mCalledReport = false;
  nsresult& mRv;
  void ReportRedirectResult(nsresult aRv);
};

void AutoRedirectVetoNotifier::ReportRedirectResult(nsresult aRv) {
  if (!mChannel) return;

  if (mCalledReport) {
    return;
  }
  mCalledReport = true;

  mChannel->mRedirectChannel = nullptr;

  if (NS_SUCCEEDED(aRv)) {
    mChannel->RemoveAsNonTailRequest();
  }

  nsCOMPtr<nsIRedirectResultListener> vetoHook;
  NS_QueryNotificationCallbacks(mChannel, NS_GET_IID(nsIRedirectResultListener),
                                getter_AddRefs(vetoHook));

  nsHttpChannel* channel = mChannel;
  mChannel = nullptr;

  if (vetoHook) vetoHook->OnRedirectResult(aRv);

  // Drop after the notification
  channel->StoreHasAutoRedirectVetoNotifier(false);
}

//-----------------------------------------------------------------------------
// nsHttpChannel <public>
//-----------------------------------------------------------------------------

nsHttpChannel::nsHttpChannel() : HttpAsyncAborter<nsHttpChannel>(this) {
  LOG(("Creating nsHttpChannel [this=%p, nsIChannel=%p]\n"this,
       static_cast<nsIChannel*>(this)));
  mChannelCreationTime = PR_Now();
  mChannelCreationTimestamp = TimeStamp::Now();
}

nsHttpChannel::~nsHttpChannel() {
  LOG(("Destroying nsHttpChannel [this=%p, nsIChannel=%p]\n"this,
       static_cast<nsIChannel*>(this)));

  if (LOG_ENABLED()) {
    nsCString webExtension;
    this->GetPropertyAsACString(u"cancelledByExtension"_ns, webExtension);
    if (!webExtension.IsEmpty()) {
      LOG(("channel [%p] cancelled by extension [id=%s]"this,
           webExtension.get()));
    }
  }

  if (mAuthProvider) {
    DebugOnly<nsresult> rv = mAuthProvider->Disconnect(NS_ERROR_ABORT);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
  }

  ReleaseMainThreadOnlyReferences();
  if (gHttpHandler) {
    gHttpHandler->RemoveHttpChannel(mChannelId);
  }
}

void nsHttpChannel::ReleaseMainThreadOnlyReferences() {
  if (NS_IsMainThread()) {
    // Already on main thread, let dtor to
    // take care of releasing references
    return;
  }

  nsTArray<nsCOMPtr<nsISupports>> arrayToRelease;
  arrayToRelease.AppendElement(mAuthProvider.forget());
  arrayToRelease.AppendElement(mRedirectChannel.forget());
  arrayToRelease.AppendElement(mPreflightChannel.forget());
  arrayToRelease.AppendElement(mDNSPrefetch.forget());

  MOZ_DIAGNOSTIC_ASSERT(
      !mEarlyHintObserver,
      "Early hint observer should have been released in ReleaseListeners()");
  arrayToRelease.AppendElement(mEarlyHintObserver.forget());
  MOZ_DIAGNOSTIC_ASSERT(
      !mChannelClassifier,
      "Channel classifier should have been released in ReleaseListeners()");
  arrayToRelease.AppendElement(
      mChannelClassifier.forget().downcast<nsIURIClassifierCallback>());
  MOZ_DIAGNOSTIC_ASSERT(
      !mWarningReporter,
      "Warning reporter should have been released in ReleaseListeners()");
  arrayToRelease.AppendElement(mWarningReporter.forget());

  NS_DispatchToMainThread(new ProxyReleaseRunnable(std::move(arrayToRelease)));
}

nsresult nsHttpChannel::Init(nsIURI* uri, uint32_t caps, nsProxyInfo* proxyInfo,
                             uint32_t proxyResolveFlags, nsIURI* proxyURI,
                             uint64_t channelId,
                             ExtContentPolicyType aContentPolicyType,
                             nsILoadInfo* aLoadInfo) {
  nsresult rv =
      HttpBaseChannel::Init(uri, caps, proxyInfo, proxyResolveFlags, proxyURI,
                            channelId, aContentPolicyType, aLoadInfo);
  if (NS_FAILED(rv)) return rv;

  LOG1(("nsHttpChannel::Init [this=%p]\n"this));

  return rv;
}

nsresult nsHttpChannel::AddSecurityMessage(const nsAString& aMessageTag,
                                           const nsAString& aMessageCategory) {
  if (mWarningReporter) {
    return mWarningReporter->ReportSecurityMessage(aMessageTag,
                                                   aMessageCategory);
  }
  return HttpBaseChannel::AddSecurityMessage(aMessageTag, aMessageCategory);
}

NS_IMETHODIMP
nsHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage,
                                     const nsACString& aCategory,
                                     bool aIsWarning) {
  if (mWarningReporter) {
    return mWarningReporter->LogBlockedCORSRequest(aMessage, aCategory,
                                                   aIsWarning);
  }
  return NS_ERROR_UNEXPECTED;
}

NS_IMETHODIMP
nsHttpChannel::LogMimeTypeMismatch(const nsACString& aMessageName,
                                   bool aWarning, const nsAString& aURL,
                                   const nsAString& aContentType) {
  if (mWarningReporter) {
    return mWarningReporter->LogMimeTypeMismatch(aMessageName, aWarning, aURL,
                                                 aContentType);
  }
  return NS_ERROR_UNEXPECTED;
}

//-----------------------------------------------------------------------------
// nsHttpChannel <private>
//-----------------------------------------------------------------------------

nsresult nsHttpChannel::PrepareToConnect() {
  LOG(("nsHttpChannel::PrepareToConnect [this=%p]\n"this));

  // notify "http-on-modify-request-before-cookies" observers
  gHttpHandler->OnModifyRequestBeforeCookies(this);

  AddCookiesToRequest();

#if defined(XP_WIN) || defined(XP_MACOSX)

  auto prefEnabledForCurrentContainer = [&]() {
    uint32_t containerId = mLoadInfo->GetOriginAttributes().mUserContextId;
    // Make sure that the default container ID is 0
    static_assert(nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID == 0);

    nsAutoCString prefName;
#  ifdef XP_WIN
    prefName = nsPrintfCString("network.http.windows-sso.container-enabled.%u",
                               containerId);
#  endif

#  ifdef XP_MACOSX
    prefName = nsPrintfCString(
        "network.http.microsoft-entra-sso.container-enabled.%u", containerId);
#  endif

    bool enabled = false;
    Preferences::GetBool(prefName.get(), &enabled);

    LOG(("Pref for %s is %d\n", prefName.get(), enabled));

    return enabled;
  };

#endif  // defined(XP_WIN) || defined(XP_MACOSX)

#ifdef XP_WIN

  // If Windows 10 SSO is enabled, we potentially add auth
  // information to secure top level loads (DOCUMENTs) and iframes
  // (SUBDOCUMENTs) that aren't anonymous or private browsing.
  if (StaticPrefs::network_http_windows_sso_enabled() &&
      mURI->SchemeIs("https") && !(mLoadFlags & LOAD_ANONYMOUS) &&
      !mPrivateBrowsing) {
    ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType();
    if ((type == ExtContentPolicy::TYPE_DOCUMENT ||
         type == ExtContentPolicy::TYPE_SUBDOCUMENT) &&
        prefEnabledForCurrentContainer()) {
      AddWindowsSSO(this);
    }
  }
#endif

#ifdef XP_MACOSX

  auto isUriMSAuthority = [&]() {
    nsAutoCString endPoint;
    nsresult rv = mURI->GetHost(endPoint);
    if (!NS_SUCCEEDED(rv)) {
      return false;
    }
    LOG(("endPoint is %s\n", endPoint.get()));

    return gHttpHandler->IsHostMSAuthority(endPoint);
  };

  // If macOS SSO is enabled, we potentially add auth
  // information to secure top level loads (DOCUMENTs) and iframes
  // (SUBDOCUMENTs) that aren't anonymous or private browsing.
  if (StaticPrefs::network_http_microsoft_entra_sso_enabled() &&
      mURI->SchemeIs("https") && !(mLoadFlags & LOAD_ANONYMOUS) &&
      !mPrivateBrowsing) {
    ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType();
    if ((type == ExtContentPolicy::TYPE_DOCUMENT ||
         type == ExtContentPolicy::TYPE_SUBDOCUMENT) &&
        prefEnabledForCurrentContainer() && isUriMSAuthority()) {
      nsMainThreadPtrHandle<nsHttpChannel> self(
          new nsMainThreadPtrHolder<nsHttpChannel>(
              "nsHttpChannel::PrepareToConnect::self"this));
      auto resultCallback = [self(self)]() {
        MOZ_ASSERT(NS_IsMainThread());
        nsresult rv = self->ContinuePrepareToConnect();
        if (NS_FAILED(rv)) {
          self->CloseCacheEntry(false);
          Unused << self->AsyncAbort(rv);
        }
      };

      nsresult rv = AddMicrosoftEntraSSO(this, std::move(resultCallback));

      // Returns NS_OK if performRequests is called in MicrosoftEntraSSOUtils
      // This temporarily stops the channel setup for the delegate.
      if (NS_SUCCEEDED(rv)) {
        return rv;
      }
    }
  }

#endif

  return ContinuePrepareToConnect();
}

nsresult nsHttpChannel::ContinuePrepareToConnect() {
  // notify "http-on-modify-request" observers
  CallOnModifyRequestObservers();

  return CallOrWaitForResume(
      [](auto* self) { return self->OnBeforeConnect(); });
}

void nsHttpChannel::HandleContinueCancellingByURLClassifier(
    nsresult aErrorCode) {
  MOZ_ASSERT(
      UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode));
  MOZ_ASSERT(!mCallOnResume, "How did that happen?");

  if (mSuspendCount) {
    LOG(
        ("Waiting until resume HandleContinueCancellingByURLClassifier "
         "[this=%p]\n",
         this));
    mCallOnResume = [aErrorCode](nsHttpChannel* self) {
      self->HandleContinueCancellingByURLClassifier(aErrorCode);
      return NS_OK;
    };
    return;
  }

  LOG(("nsHttpChannel::HandleContinueCancellingByURLClassifier [this=%p]\n",
       this));
  ContinueCancellingByURLClassifier(aErrorCode);
}

void nsHttpChannel::SetPriorityHeader() {
  nsAutoCString userSetPriority;
  Unused << GetRequestHeader("Priority"_ns, userSetPriority);
  if (!userSetPriority.IsEmpty()) {
    // If the Priority header is set by the user, do not override it.
    return;
  }

  uint8_t urgency =
      nsHttpHandler::UrgencyFromCoSFlags(mClassOfService.Flags(), mPriority);
  bool incremental = mClassOfService.Incremental();

  nsPrintfCString value(
      "%s", urgency != 3 ? nsPrintfCString("u=%d", urgency).get() : "");

  if (incremental) {
    if (!value.IsEmpty()) {
      value.Append(", ");
    }
    value.Append("i");
  }

  if (!value.IsEmpty()) {
    SetRequestHeader("Priority"_ns, value, false);
  }
}

nsresult nsHttpChannel::OnBeforeConnect() {
  nsresult rv = NS_OK;

  // Check if request was cancelled during suspend AFTER on-modify-request
  if (mCanceled) {
    return mStatus;
  }

  // Check to see if we should redirect this channel elsewhere by
  // nsIHttpChannel.redirectTo API request
  if (mAPIRedirectTo) {
    return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
  }

  // Note that we are only setting the "Upgrade-Insecure-Requests" request
  // header for *all* navigational requests instead of all requests as
  // defined in the spec, see:
  // https://www.w3.org/TR/upgrade-insecure-requests/#preference
  ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType();

  if (type == ExtContentPolicy::TYPE_DOCUMENT ||
      type == ExtContentPolicy::TYPE_SUBDOCUMENT) {
    rv = SetRequestHeader("Upgrade-Insecure-Requests"_ns, "1"_ns, false);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  if (LoadAuthRedirectedChannel()) {
    // This channel is a result of a redirect due to auth retry
    // We have already checked for HSTS upgarde in the redirecting channel.
    // We can safely skip those checks
    return ContinueOnBeforeConnect(false, rv);
  }

  SecFetch::AddSecFetchHeader(this);

  // Check to see if we should redirect this channel to the unstripped URI. To
  // revert the query stripping if the loading channel is in the content
  // blocking allow list.
  if (ContentBlockingAllowList::Check(this)) {
    nsCOMPtr<nsIURI> unstrippedURI;
    mLoadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI));

    if (unstrippedURI) {
      return AsyncCall(&nsHttpChannel::HandleAsyncRedirectToUnstrippedURI);
    }
  }

  nsCOMPtr<nsIPrincipal> resultPrincipal;
  if (!mURI->SchemeIs("https")) {
    nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
        this, getter_AddRefs(resultPrincipal));
  }

  // Check if we already know about the HSTS status of the host
  nsISiteSecurityService* sss = gHttpHandler->GetSSService();
  NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY);
  bool isSecureURI;
  OriginAttributes originAttributes;
  if (!StoragePrincipalHelper::GetOriginAttributesForHSTS(this,
                                                          originAttributes)) {
    return NS_ERROR_FAILURE;
  }
  rv = sss->IsSecureURI(mURI, originAttributes, &isSecureURI);
  NS_ENSURE_SUCCESS(rv, rv);
  // Save that on the loadInfo so it can later be consumed by
  // SecurityInfo.sys.mjs
  mLoadInfo->SetHstsStatus(isSecureURI);

  RefPtr<mozilla::dom::BrowsingContext> bc;
  mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
  // If bypassing the cache and we're forced offline
  // we can just return the error here.
  if (bc && bc->Top()->GetForceOffline() &&
      BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) {
    return NS_ERROR_OFFLINE;
  }

  // At this point it is no longer possible to call
  // HttpBaseChannel::UpgradeToSecure.
  StoreUpgradableToSecure(false);
  bool shouldUpgrade = LoadUpgradeToSecure();
  if (mURI->SchemeIs("http")) {
    OriginAttributes originAttributes;
    if (!StoragePrincipalHelper::GetOriginAttributesForHSTS(this,
                                                            originAttributes)) {
      return NS_ERROR_FAILURE;
    }

    if (!shouldUpgrade) {
      // Make sure http channel is released on main thread.
      // See bug 1539148 for details.
      nsMainThreadPtrHandle<nsHttpChannel> self(
          new nsMainThreadPtrHolder<nsHttpChannel>(
              "nsHttpChannel::OnBeforeConnect::self"this));
      auto resultCallback = [self(self)](bool aResult, nsresult aStatus) {
        MOZ_ASSERT(NS_IsMainThread());

        nsresult rv = self->MaybeUseHTTPSRRForUpgrade(aResult, aStatus);
        if (NS_FAILED(rv)) {
          self->CloseCacheEntry(false);
          Unused << self->AsyncAbort(rv);
        }
      };

      bool willCallback = false;
      rv = NS_ShouldSecureUpgrade(
          mURI, mLoadInfo, resultPrincipal, LoadAllowSTS(), originAttributes,
          shouldUpgrade, std::move(resultCallback), willCallback);
      // If the request gets upgraded because of the HTTPS-Only mode, but no
      // event listener has been registered so far, we want to do that here.
      uint32_t httpOnlyStatus = mLoadInfo->GetHttpsOnlyStatus();
      if (httpOnlyStatus &
          nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED) {
        RefPtr<nsHTTPSOnlyStreamListener> httpsOnlyListener =
            new nsHTTPSOnlyStreamListener(mListener, mLoadInfo);
        mListener = httpsOnlyListener;

        httpOnlyStatus ^=
            nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED;
        httpOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED;
        mLoadInfo->SetHttpsOnlyStatus(httpOnlyStatus);
      }
      LOG(
          ("nsHttpChannel::OnBeforeConnect "
           "[this=%p willCallback=%d rv=%" PRIx32 "]\n",
           this, willCallback, static_cast<uint32_t>(rv)));

      if (NS_FAILED(rv) || MOZ_UNLIKELY(willCallback)) {
        return rv;
      }
    }
  }

  return MaybeUseHTTPSRRForUpgrade(shouldUpgrade, NS_OK);
}

// Returns true if the network connectivity checker indicated
// that HTTPS records can be resolved on this network - false otherwise.
// When TRR is enabled, we always return true, as resolving HTTPS
// records don't depend on the network.
static bool canUseHTTPSRRonNetwork(bool& aTRREnabled) {
  // Respect the pref.
  if (StaticPrefs::network_dns_force_use_https_rr()) {
    aTRREnabled = true;
    return true;
  }

  aTRREnabled = false;

  if (nsCOMPtr<nsIDNSService> dns = mozilla::components::DNS::Service()) {
    nsIDNSService::ResolverMode mode;
    // If the browser is currently using TRR/DoH, then it can
    // definitely resolve HTTPS records.
    if (NS_SUCCEEDED(dns->GetCurrentTrrMode(&mode))) {
      if (mode == nsIDNSService::MODE_TRRFIRST) {
        RefPtr<TRRService> trr = TRRService::Get();
        if (trr && trr->IsConfirmed()) {
          aTRREnabled = true;
        }
      } else if (mode == nsIDNSService::MODE_TRRONLY) {
        aTRREnabled = true;
      }
      if (aTRREnabled) {
        return true;
      }
    }
  }

  if (RefPtr<NetworkConnectivityService> ncs =
          NetworkConnectivityService::GetSingleton()) {
    nsINetworkConnectivityService::ConnectivityState state;
    if (NS_SUCCEEDED(ncs->GetDNS_HTTPS(&state)) &&
        state == nsINetworkConnectivityService::NOT_AVAILABLE) {
      return false;
    }
  }
  return true;
}

nsresult nsHttpChannel::MaybeUseHTTPSRRForUpgrade(bool aShouldUpgrade,
                                                  nsresult aStatus) {
  if (NS_FAILED(aStatus)) {
    return aStatus;
  }

  RefPtr<mozilla::dom::BrowsingContext> bc;
  mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));
  bool forceOffline = bc && bc->Top()->GetForceOffline();

  if (mURI->SchemeIs("https") || aShouldUpgrade || !LoadUseHTTPSSVC() ||
      forceOffline) {
    return ContinueOnBeforeConnect(aShouldUpgrade, aStatus);
  }

  auto shouldSkipUpgradeWithHTTPSRR = [&]() -> bool {
    if (mCaps & NS_HTTP_DISALLOW_HTTPS_RR) {
      return true;
    }

    // Skip using HTTPS RR to upgrade when this is not a top-level load and the
    // loading principal is http.
    if ((mLoadInfo->GetExternalContentPolicyType() !=
         ExtContentPolicy::TYPE_DOCUMENT) &&
        (mLoadInfo->GetLoadingPrincipal() &&
         mLoadInfo->GetLoadingPrincipal()->SchemeIs("http"))) {
      return true;
    }

    // If the network connectivity checker indicates the network is
    // blocking HTTPS requests, then we should skip them so we don't
    // needlessly wait for a timeout.
    bool trrEnabled = false;
    if (!canUseHTTPSRRonNetwork(trrEnabled)) {
      return true;
    }

    // Don't block the channel when TRR is not used.
    if (!trrEnabled) {
      return true;
    }

    auto dnsStrategy = GetProxyDNSStrategy();
    if (dnsStrategy != ProxyDNSStrategy::ORIGIN) {
      return true;
    }

    nsAutoCString uriHost;
    mURI->GetAsciiHost(uriHost);

    return gHttpHandler->IsHostExcludedForHTTPSRR(uriHost);
  };

  if (shouldSkipUpgradeWithHTTPSRR()) {
    StoreUseHTTPSSVC(false);
    // If the website does not want to use HTTPS RR, we should set
    // NS_HTTP_DISALLOW_HTTPS_RR. This is for avoiding HTTPS RR being used by
    // the transaction.
    DisallowHTTPSRR(mCaps);
    return ContinueOnBeforeConnect(aShouldUpgrade, aStatus);
  }

  if (mHTTPSSVCRecord.isSome()) {
    LOG((
        "nsHttpChannel::MaybeUseHTTPSRRForUpgrade [%p] mHTTPSSVCRecord is some",
        this));
    StoreWaitHTTPSSVCRecord(false);
    bool hasHTTPSRR = (mHTTPSSVCRecord.ref() != nullptr);
    return ContinueOnBeforeConnect(hasHTTPSRR, aStatus, hasHTTPSRR);
  }

  LOG(("nsHttpChannel::MaybeUseHTTPSRRForUpgrade [%p] wait for HTTPS RR",
       this));

  OriginAttributes originAttributes;
  StoragePrincipalHelper::GetOriginAttributesForHTTPSRR(this, originAttributes);

  RefPtr<nsDNSPrefetch> resolver =
      new nsDNSPrefetch(mURI, originAttributes, nsIRequest::GetTRRMode());
  nsWeakPtr weakPtrThis(
      do_GetWeakReference(static_cast<nsIHttpChannel*>(this)));
  nsresult rv = resolver->FetchHTTPSSVC(
      mCaps & NS_HTTP_REFRESH_DNS, !LoadUseHTTPSSVC(),
      [weakPtrThis](nsIDNSHTTPSSVCRecord* aRecord) {
        nsCOMPtr<nsIHttpChannel> channel = do_QueryReferent(weakPtrThis);
        RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(channel);
        if (httpChannelImpl) {
          httpChannelImpl->OnHTTPSRRAvailable(aRecord);
        }
      });
  if (NS_FAILED(rv)) {
    LOG((" FetchHTTPSSVC failed with 0x%08" PRIx32,
         static_cast<uint32_t>(rv)));
    return ContinueOnBeforeConnect(aShouldUpgrade, aStatus);
  }

  StoreWaitHTTPSSVCRecord(true);
  return NS_OK;
}

nsresult nsHttpChannel::ContinueOnBeforeConnect(bool aShouldUpgrade,
                                                nsresult aStatus,
                                                bool aUpgradeWithHTTPSRR) {
  LOG(
      ("nsHttpChannel::ContinueOnBeforeConnect "
       "[this=%p aShouldUpgrade=%d rv=%" PRIx32 "]\n",
       this, aShouldUpgrade, static_cast<uint32_t>(aStatus)));

  MOZ_ASSERT(!LoadWaitHTTPSSVCRecord());

  if (NS_FAILED(aStatus)) {
    return aStatus;
  }

  if (aShouldUpgrade && !mURI->SchemeIs("https")) {
    // only set HTTPS_RR to be responsbile for the upgrade in the loadinfo
    // if it actually was responsible, otherwise the correct flag is
    // already present in the loadinfo.
    if (aUpgradeWithHTTPSRR) {
      mLoadInfo->SetHttpsUpgradeTelemetry(nsILoadInfo::HTTPS_RR);
    }
    return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
  }

  // ensure that we are using a valid hostname
  if (!net_IsValidDNSHost(nsDependentCString(mConnectionInfo->Origin()))) {
    return NS_ERROR_UNKNOWN_HOST;
  }

  if (mUpgradeProtocolCallback) {
    // Websockets can run over HTTP/2, but other upgrades can't.
    if (mUpgradeProtocol.EqualsLiteral("websocket") &&
        StaticPrefs::network_http_http2_websockets()) {
      // Need to tell the conn manager that we're ok with http/2 even with
      // the allow keepalive bit not set. That bit needs to stay off,
      // though, in case we end up having to fallback to http/1.1 (where
      // we absolutely do want to disable keepalive).
      mCaps |= NS_HTTP_ALLOW_SPDY_WITHOUT_KEEPALIVE;
    } else {
      mCaps |= NS_HTTP_DISALLOW_SPDY;
    }
    // Upgrades cannot use HTTP/3.
    mCaps |= NS_HTTP_DISALLOW_HTTP3;
    // Because NS_HTTP_STICKY_CONNECTION breaks HTTPS RR fallabck mecnahism, we
    // can not use HTTPS RR for upgrade requests.
    DisallowHTTPSRR(mCaps);
  }

  if (LoadIsTRRServiceChannel()) {
    mCaps |= NS_HTTP_LARGE_KEEPALIVE;
    DisallowHTTPSRR(mCaps);
  }

  if (mTransactionSticky) {
    MOZ_ASSERT(LoadAuthRedirectedChannel());
    // this means this is a redirected channel channel due to auth retry and a
    // connection based auth scheme was used
    // we have a reference to the old-transaction with sticky connection which
    // we need to use
    mCaps |= NS_HTTP_STICKY_CONNECTION;
  }

  mCaps |= NS_HTTP_TRR_FLAGS_FROM_MODE(nsIRequest::GetTRRMode());

  // Finalize ConnectionInfo flags before SpeculativeConnect
  mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
  mConnectionInfo->SetPrivate(mPrivateBrowsing);
  mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY);
  mConnectionInfo->SetBeConservative((mCaps & NS_HTTP_BE_CONSERVATIVE) ||
                                     LoadBeConservative());
  mConnectionInfo->SetTlsFlags(mTlsFlags);
  mConnectionInfo->SetIsTrrServiceChannel(LoadIsTRRServiceChannel());
  mConnectionInfo->SetTRRMode(nsIRequest::GetTRRMode());
  mConnectionInfo->SetIPv4Disabled(mCaps & NS_HTTP_DISABLE_IPV4);
  mConnectionInfo->SetIPv6Disabled(mCaps & NS_HTTP_DISABLE_IPV6);
  mConnectionInfo->SetAnonymousAllowClientCert(
      (mLoadFlags & LOAD_ANONYMOUS_ALLOW_CLIENT_CERT) != 0);

  if (mWebTransportSessionEventListener) {
    nsTArray<RefPtr<nsIWebTransportHash>> aServerCertHashes;
    nsresult rv;
    nsCOMPtr<WebTransportConnectionSettings> wtconSettings =
        do_QueryInterface(mWebTransportSessionEventListener, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    wtconSettings->GetServerCertificateHashes(aServerCertHashes);
    gHttpHandler->ConnMgr()->StoreServerCertHashes(
        mConnectionInfo, gHttpHandler->IsHttp2Excluded(mConnectionInfo),
        !Http3Allowed(), std::move(aServerCertHashes));
  }

  if (ShouldIntercept()) {
    return RedirectToInterceptedChannel();
  }

  // notify "http-on-before-connect" observers
  gHttpHandler->OnBeforeConnect(this);

  return CallOrWaitForResume([](auto* self) { return self->Connect(); });
}

class MOZ_STACK_CLASS AddResponseHeadersToResponseHead final
    : public nsIHttpHeaderVisitor {
 public:
  explicit AddResponseHeadersToResponseHead(nsHttpResponseHead* aResponseHead)
      : mResponseHead(aResponseHead) {}

  NS_IMETHOD VisitHeader(const nsACString& aHeader,
                         const nsACString& aValue) override {
    nsAutoCString headerLine = aHeader + ": "_ns + aValue;
    DebugOnly<nsresult> rv = mResponseHead->ParseHeaderLine(headerLine);
    MOZ_ASSERT(NS_SUCCEEDED(rv));

    return NS_OK;
  }

  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;

  // Stub AddRef/Release since this is a stack class.
  NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override {
    return ++mRefCnt;
  }

  NS_IMETHOD_(MozExternalRefCountType) Release(void) override {
    return --mRefCnt;
  }

  virtual ~AddResponseHeadersToResponseHead() {
    MOZ_DIAGNOSTIC_ASSERT(mRefCnt == 0);
  }

 private:
  nsHttpResponseHead* mResponseHead;

  nsrefcnt mRefCnt = 0;
};

NS_IMPL_QUERY_INTERFACE(AddResponseHeadersToResponseHead, nsIHttpHeaderVisitor)

nsresult nsHttpChannel::HandleOverrideResponse() {
  // Start building a response with the data from mOverrideResponse.
  mResponseHead = MakeUnique<nsHttpResponseHead>();

  // Apply override response status code and status text.
  uint32_t statusCode;
  nsresult rv = mOverrideResponse->GetResponseStatus(&statusCode);
  NS_ENSURE_SUCCESS(rv, rv);

  nsAutoCString statusText;
  rv = mOverrideResponse->GetResponseStatusText(statusText);
  NS_ENSURE_SUCCESS(rv, rv);

  // Hardcoding protocol HTTP/1.1
  nsPrintfCString line("HTTP/1.1 %u %s", statusCode, statusText.get());
  rv = mResponseHead->ParseStatusLine(line);
  NS_ENSURE_SUCCESS(rv, rv);

  // Apply override response headers.
  AddResponseHeadersToResponseHead visitor(mResponseHead.get());
  rv = mOverrideResponse->VisitResponseHeaders(&visitor);
  NS_ENSURE_SUCCESS(rv, rv);

  if (WillRedirect(*mResponseHead)) {
    // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here,
    // to avoid event dispatching latency.
    LOG(("Skipping read of overridden response redirect entity\n"));
    return AsyncCall(&nsHttpChannel::HandleAsyncRedirect);
  }

  // Handle Set-Cookie headers as if the response was from networking.
  CookieVisitor cookieVisitor(mResponseHead.get());
  SetCookieHeaders(cookieVisitor.CookieHeaders());
  nsCOMPtr<nsIParentChannel> parentChannel;
  NS_QueryNotificationCallbacks(this, parentChannel);
  if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel)) {
    httpParent->SetCookieHeaders(cookieVisitor.CookieHeaders());
  }

  rv = ProcessSecurityHeaders();
  if (NS_FAILED(rv)) {
    NS_WARNING("ProcessSecurityHeaders failed, continuing load.");
  }

  if ((statusCode < 500) && (statusCode != 421)) {
    ProcessAltService();
  }

  nsAutoCString body;
  rv = mOverrideResponse->GetResponseBody(body);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIInputStream> stringStream;
  rv = NS_NewCStringInputStream(getter_AddRefs(stringStream), body);
  NS_ENSURE_SUCCESS(rv, rv);
  rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), stringStream, 0, 0,
                                 true);
  if (NS_FAILED(rv)) {
    stringStream->Close();
    return rv;
  }

  rv = mCachePump->AsyncRead(this);
  if (NS_FAILED(rv)) return rv;

  return NS_OK;
}

nsresult nsHttpChannel::Connect() {
  LOG(("nsHttpChannel::Connect [this=%p]\n"this));

  if (mAPIRedirectTo) {
    LOG(("nsHttpChannel::Connect [transparent=%d]\n",
         mAPIRedirectTo->second()));

    nsresult rv = StartRedirectChannelToURI(
        mAPIRedirectTo->first(),
        mAPIRedirectTo->second() ? nsIChannelEventSink::REDIRECT_PERMANENT |
                                       nsIChannelEventSink::REDIRECT_TRANSPARENT
                                 : nsIChannelEventSink::REDIRECT_PERMANENT);
    mAPIRedirectTo = Nothing();
    if (NS_SUCCEEDED(rv)) {
      return NS_OK;
    }
    return NS_ERROR_FAILURE;
  }

  // If mOverrideResponse is set, bypass the rest of the connection and reply
  // immediately with a response built using the data from mOverrideResponse.
  if (mOverrideResponse) {
    return HandleOverrideResponse();
  }

  // Don't allow resuming when cache must be used
  if (LoadResuming() && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
    LOG(("Resuming from cache is not supported yet"));
    return NS_ERROR_DOCUMENT_NOT_CACHED;
  }

  // Step 8.18 of HTTP-network-or-cache fetch
  // https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
  nsAutoCString rangeVal;
  if (NS_SUCCEEDED(GetRequestHeader("Range"_ns, rangeVal))) {
    SetRequestHeader("Accept-Encoding"_ns, "identity"_ns, true);
  }

  if (mRequestHead.IsPost() || mRequestHead.IsPatch()) {
    // If the post id is already set then this is an attempt to replay
    // a post/patch transaction via the cache.  Otherwise, we need a unique
    // post id for this transaction.
    if (mPostID == 0) {
      mPostID = gHttpHandler->GenerateUniqueID();
    }

    if (StaticPrefs::network_http_idempotencyKey_enabled() &&
        !mRequestHead.HasHeader(nsHttp::Idempotency_Key)) {
      // check if we need to add
      // idempotency-key header
      // See Bug 1830022 for more details
      nsAutoCString key;
      gHttpHandler->GenerateIdempotencyKeyForPost(mPostID, mLoadInfo, key);
      MOZ_ALWAYS_SUCCEEDS(
          mRequestHead.SetHeader(nsHttp::Idempotency_Key, key, false));
    }
  }

#ifdef MOZ_WIDGET_ANDROID
  bool val = false;
  if (nsIOService::ShouldAddAdditionalSearchHeaders(mURI, &val)) {
    SetRequestHeader("X-Search-Subdivision"_ns, val ? "1"_ns : "0"_ns, false);
  }
#endif

  bool isTrackingResource = IsThirdPartyTrackingResource();
  LOG(("nsHttpChannel %p tracking resource=%d, cos=%lu, inc=%d"this,
       isTrackingResource, mClassOfService.Flags(),
       mClassOfService.Incremental()));

  if (isTrackingResource) {
    AddClassFlags(nsIClassOfService::Tail);
  }

  if (WaitingForTailUnblock()) {
    MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock);
    mOnTailUnblock = &nsHttpChannel::ConnectOnTailUnblock;
    return NS_OK;
  }

  return ConnectOnTailUnblock();
}

nsresult nsHttpChannel::ConnectOnTailUnblock() {
  nsresult rv;

  LOG(("nsHttpChannel::ConnectOnTailUnblock [this=%p]\n"this));

  // Consider opening a TCP connection right away.
  SpeculativeConnect();

  // open a cache entry for this channel...
  rv = OpenCacheEntry(mURI->SchemeIs("https"));

  // do not continue if asyncOpenCacheEntry is in progress
  if (AwaitingCacheCallbacks()) {
    LOG(("nsHttpChannel::Connect %p AwaitingCacheCallbacks forces async\n",
         this));
    MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state");

    if (mNetworkTriggered && mWaitingForProxy) {
      // Someone has called TriggerNetwork(), meaning we are racing the
      // network with the cache.
      mWaitingForProxy = false;
      return ContinueConnect();
    }

    return NS_OK;
  }

  if (NS_FAILED(rv)) {
    LOG(("OpenCacheEntry failed [rv=%" PRIx32 "]\n",
         static_cast<uint32_t>(rv)));
    // if this channel is only allowed to pull from the cache, then
    // we must fail if we were unable to open a cache entry.
    if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
      return NS_ERROR_DOCUMENT_NOT_CACHED;
    }
    // otherwise, let's just proceed without using the cache.
  }

  if (mRaceCacheWithNetwork && ((mCacheEntry && !CachedContentIsValid() &&
                                 (mDidReval || LoadCachedContentIsPartial())) ||
                                mIgnoreCacheEntry)) {
    // We won't send the conditional request because the unconditional
    // request was already sent (see bug 1377223).
    AccumulateCategorical(
        Telemetry::LABELS_NETWORK_RACE_CACHE_VALIDATION::NotSent);
  }

  // When racing, if OnCacheEntryAvailable is called before AsyncOpenURI
  // returns, then we may not have started reading from the cache.
  // If the content is valid, we should attempt to do so, as technically the
  // cache has won the race.
  if (mRaceCacheWithNetwork && CachedContentIsValid()) {
    Unused << ReadFromCache();
  }

  return TriggerNetwork();
}

nsresult nsHttpChannel::ContinueConnect() {
  // If we need to start a CORS preflight, do it now!
  // Note that it is important to do this before the early returns below.
  if (!LoadIsCorsPreflightDone() && LoadRequireCORSPreflight()) {
    MOZ_ASSERT(!mPreflightChannel);
    nsresult rv = nsCORSListenerProxy::StartCORSPreflight(
        thisthis, mUnsafeHeaders, getter_AddRefs(mPreflightChannel));
    return rv;
  }

  MOZ_RELEASE_ASSERT(!LoadRequireCORSPreflight() || LoadIsCorsPreflightDone(),
                     "CORS preflight must have been finished by the time we "
                     "do the rest of ContinueConnect");

  RefPtr<mozilla::dom::BrowsingContext> bc;
  mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));

  // we may or may not have a cache entry at this point
  if (mCacheEntry) {
    // read straight from the cache if possible...
    if (CachedContentIsValid()) {
      // If we're forced offline, and set to bypass the cache, return offline.
      if (bc && bc->Top()->GetForceOffline() &&
          BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) {
        return NS_ERROR_OFFLINE;
      }

      nsRunnableMethod<nsHttpChannel>* event = nullptr;
      nsresult rv;
      if (!LoadCachedContentIsPartial()) {
        rv = AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse, &event);
        if (NS_FAILED(rv)) {
          LOG((" AsyncCall failed (%08x)"static_cast<uint32_t>(rv)));
        }
      }
      rv = ReadFromCache();
      if (NS_FAILED(rv) && event) {
        event->Revoke();
      }

      AccumulateCacheHitTelemetry(kCacheHit, this);
      mCacheDisposition = kCacheHit;

      return rv;
    }
    if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
      // the cache contains the requested resource, but it must be
      // validated before we can reuse it.  since we are not allowed
      // to hit the net, there's nothing more to do.  the document
      // is effectively not in the cache.
      LOG((" !CachedContentIsValid() && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
      return NS_ERROR_DOCUMENT_NOT_CACHED;
    }
  } else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) {
    LOG((" !mCacheEntry && mLoadFlags & LOAD_ONLY_FROM_CACHE"));
    return NS_ERROR_DOCUMENT_NOT_CACHED;
  }

  if (mLoadFlags & LOAD_NO_NETWORK_IO) {
    LOG((" mLoadFlags & LOAD_NO_NETWORK_IO"));
    return NS_ERROR_DOCUMENT_NOT_CACHED;
  }

  // We're about to hit the network. Don't if we're forced offline.
  if (bc && bc->Top()->GetForceOffline()) {
    return NS_ERROR_OFFLINE;
  }

  // hit the net...
  nsresult rv = DoConnect(mTransactionSticky);
  mTransactionSticky = nullptr;
  return rv;
}

nsresult nsHttpChannel::DoConnect(HttpTransactionShell* aTransWithStickyConn) {
  LOG(("nsHttpChannel::DoConnect [this=%p]\n"this));

  if (!mDNSBlockingPromise.IsEmpty()) {
    LOG((" waiting for DNS prefetch"));

    // Transaction is passed only from auth retry for which we will definitely
    // not block on DNS to alter the origin server name for IP; it has already
    // been done.
    MOZ_ASSERT(!aTransWithStickyConn);
    MOZ_ASSERT(mDNSBlockingThenable);

    nsCOMPtr<nsISerialEventTarget> target(do_GetMainThread());
    RefPtr<nsHttpChannel> self(this);
    mDNSBlockingThenable->Then(
        target, __func__,
        [self](const nsCOMPtr<nsIDNSRecord>& aRec) {
          nsresult rv = self->DoConnectActual(nullptr);
          if (NS_FAILED(rv)) {
            self->CloseCacheEntry(false);
            Unused << self->AsyncAbort(rv);
          }
        },
        [self](nsresult err) {
          self->CloseCacheEntry(false);
          Unused << self->AsyncAbort(err);
        });

    // The connection will continue when the promise is resolved in
    // OnLookupComplete.
    return NS_OK;
  }

  return DoConnectActual(aTransWithStickyConn);
}

nsresult nsHttpChannel::DoConnectActual(
    HttpTransactionShell* aTransWithStickyConn) {
  LOG(("nsHttpChannel::DoConnectActual [this=%p, aTransWithStickyConn=%p]\n",
       this, aTransWithStickyConn));

  nsresult rv = SetupChannelForTransaction();
  if (NS_FAILED(rv)) {
    return rv;
  }

  return DispatchTransaction(aTransWithStickyConn);
}

nsresult nsHttpChannel::DispatchTransaction(
    HttpTransactionShell* aTransWithStickyConn) {
  LOG(("nsHttpChannel::DispatchTransaction [this=%p, aTransWithStickyConn=%p]",
       this, aTransWithStickyConn));
  nsresult rv = InitTransaction();
  if (NS_FAILED(rv)) {
    return rv;
  }
  if (aTransWithStickyConn) {
    rv = gHttpHandler->InitiateTransactionWithStickyConn(
        mTransaction, mPriority, aTransWithStickyConn);
  } else {
    rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority);
  }

  if (NS_FAILED(rv)) {
    return rv;
  }

  rv = mTransaction->AsyncRead(this, getter_AddRefs(mTransactionPump));
  if (NS_FAILED(rv)) {
    return rv;
  }

  uint32_t suspendCount = mSuspendCount;
  if (LoadAsyncResumePending()) {
    LOG(
        (" Suspend()'ing transaction pump once because of async resume pending"
         ", sc=%u, pump=%p, this=%p",
         suspendCount, mTransactionPump.get(), this));
    ++suspendCount;
  }
  while (suspendCount--) {
    mTransactionPump->Suspend();
  }

  return NS_OK;
}

void nsHttpChannel::SpeculativeConnect() {
  // Before we take the latency hit of dealing with the cache, try and
  // get the TCP (and SSL) handshakes going so they can overlap.

  // don't speculate if we are offline, when doing http upgrade (i.e.
  // websockets bootstrap), or if we can't do keep-alive (because then we
  // couldn't reuse the speculative connection anyhow).
  RefPtr<mozilla::dom::BrowsingContext> bc;
  mLoadInfo->GetBrowsingContext(getter_AddRefs(bc));

  if (gIOService->IsOffline() || mUpgradeProtocolCallback ||
      !(mCaps & NS_HTTP_ALLOW_KEEPALIVE) ||
      (bc && bc->Top()->GetForceOffline())) {
    return;
  }

  // LOAD_ONLY_FROM_CACHE and LOAD_NO_NETWORK_IO must not hit network.
  // LOAD_FROM_CACHE is unlikely to hit network, so skip preconnects for it.
  if (mLoadFlags &
      (LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE | LOAD_NO_NETWORK_IO)) {
    return;
  }

  if (LoadAllowStaleCacheContent()) {
    return;
  }

  nsCOMPtr<nsIInterfaceRequestor> callbacks;
  NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
                                         getter_AddRefs(callbacks));
  if (!callbacks) return;
  bool httpsRRAllowed = !(mCaps & NS_HTTP_DISALLOW_HTTPS_RR);
  Unused << gHttpHandler->MaybeSpeculativeConnectWithHTTPSRR(
      mConnectionInfo, callbacks,
      mCaps & (NS_HTTP_DISALLOW_SPDY | NS_HTTP_TRR_MODE_MASK |
               NS_HTTP_DISABLE_IPV4 | NS_HTTP_DISABLE_IPV6 |
               NS_HTTP_DISALLOW_HTTP3 | NS_HTTP_REFRESH_DNS),
      gHttpHandler->EchConfigEnabled() && httpsRRAllowed);
}

void nsHttpChannel::DoNotifyListenerCleanup() {
  // We don't need this info anymore
  CleanRedirectCacheChainIfNecessary();
}

void nsHttpChannel::ReleaseListeners() {
  HttpBaseChannel::ReleaseListeners();
  mChannelClassifier = nullptr;
  mWarningReporter = nullptr;
  mEarlyHintObserver = nullptr;
  mWebTransportSessionEventListener = nullptr;

  for (StreamFilterRequest& request : mStreamFilterRequests) {
    request.mPromise->Reject(false, __func__);
  }
  mStreamFilterRequests.Clear();
}

void nsHttpChannel::DoAsyncAbort(nsresult aStatus) {
  Unused << AsyncAbort(aStatus);
}

void nsHttpChannel::HandleAsyncRedirect() {
  MOZ_ASSERT(!mCallOnResume, "How did that happen?");

  if (mSuspendCount) {
    LOG(("Waiting until resume to do async redirect [this=%p]\n"this));
    mCallOnResume = [](nsHttpChannel* self) {
      self->HandleAsyncRedirect();
      return NS_OK;
    };
    return;
  }

  nsresult rv = NS_OK;

  LOG(("nsHttpChannel::HandleAsyncRedirect [this=%p]\n"this));

  // since this event is handled asynchronously, it is possible that this
  // channel could have been canceled, in which case there would be no point
  // in processing the redirect.
  if (NS_SUCCEEDED(mStatus)) {
    PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
    rv = AsyncProcessRedirection(mResponseHead->Status());
    if (NS_FAILED(rv)) {
      PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect);
      // TODO: if !DoNotRender3xxBody(), render redirect body instead.
      // But first we need to cache 3xx bodies (bug 748510)
      rv = ContinueHandleAsyncRedirect(rv);
      MOZ_ASSERT(NS_SUCCEEDED(rv));
    }
  } else {
    rv = ContinueHandleAsyncRedirect(mStatus);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
  }
}

nsresult nsHttpChannel::ContinueHandleAsyncRedirect(nsresult rv) {
  if (NS_FAILED(rv)) {
    // If AsyncProcessRedirection fails, then we have to send out the
    // OnStart/OnStop notifications.
    LOG(("ContinueHandleAsyncRedirect got failure result [rv=%" PRIx32 "]\n",
         static_cast<uint32_t>(rv)));

    bool redirectsEnabled = !mLoadInfo->GetDontFollowRedirects();

    if (redirectsEnabled) {
      // TODO: stop failing original channel if redirect vetoed?
      mStatus = rv;

      DoNotifyListener();

      // Blow away cache entry if we couldn't process the redirect
      // for some reason (the cache entry might be corrupt).
      if (mCacheEntry) {
        mCacheEntry->AsyncDoom(nullptr);
      }
    } else {
      DoNotifyListener();
    }
  }

  CloseCacheEntry(true);

  StoreIsPending(false);

  if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);

  return NS_OK;
}

void nsHttpChannel::HandleAsyncNotModified() {
  MOZ_ASSERT(!mCallOnResume, "How did that happen?");

  if (mSuspendCount) {
    LOG(("Waiting until resume to do async not-modified [this=%p]\n"this));
    mCallOnResume = [](nsHttpChannel* self) {
      self->HandleAsyncNotModified();
      return NS_OK;
    };
    return;
  }

  LOG(("nsHttpChannel::HandleAsyncNotModified [this=%p]\n"this));

  DoNotifyListener();

  CloseCacheEntry(false);

  StoreIsPending(false);

  if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus);
}

nsresult nsHttpChannel::SetupChannelForTransaction() {
  LOG((
      "nsHttpChannel::SetupChannelForTransaction [this=%p, cos=%lu, inc=%d "
      "prio=%d]\n",
      this, mClassOfService.Flags(), mClassOfService.Incremental(), mPriority));

  NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED);

  nsresult rv;

  mozilla::MutexAutoLock lock(mRCWNLock);

  if (StaticPrefs::network_http_priority_header_enabled()) {
    SetPriorityHeader();
  }

  // If we're racing cache with network, conditional or byte range header
  // could be added in OnCacheEntryCheck. We cannot send conditional request
  // without having the entry, so we need to remove the headers here and
  // ignore the cache entry in OnCacheEntryAvailable.
  if (mRaceCacheWithNetwork && AwaitingCacheCallbacks()) {
    if (mDidReval) {
      LOG((" Removing conditional request headers"));
      UntieValidationRequest();
      mDidReval = false;
      mIgnoreCacheEntry = true;
    }

    if (LoadCachedContentIsPartial()) {
      LOG((" Removing byte range request headers"));
      UntieByteRangeRequest();
      StoreCachedContentIsPartial(false);
      mIgnoreCacheEntry = true;
    }

    if (mIgnoreCacheEntry) {
      mAvailableCachedAltDataType.Truncate();
      StoreDeliveringAltData(false);
      mAltDataLength = -1;
      mCacheInputStream.CloseAndRelease();
    }
  }

  StoreUsedNetwork(1);

  if (!LoadAllowSpdy()) {
    mCaps |= NS_HTTP_DISALLOW_SPDY;
  }
  if (!LoadAllowHttp3()) {
    mCaps |= NS_HTTP_DISALLOW_HTTP3;
  }
  if (LoadBeConservative()) {
    mCaps |= NS_HTTP_BE_CONSERVATIVE;
  }

  if (mLoadFlags & LOAD_ANONYMOUS_ALLOW_CLIENT_CERT) {
    mCaps |= NS_HTTP_LOAD_ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT;
  }

  if (nsContentUtils::ShouldResistFingerprinting(this,
                                                 RFPTarget::HttpUserAgent)) {
    mCaps |= NS_HTTP_USE_RFP;
  }

  // Use the URI path if not proxying (transparent proxying such as proxy
  // CONNECT does not count here). Also figure out what HTTP version to use.
  nsAutoCString buf, path;
  nsCString* requestURI;

  // This is the normal e2e H1 path syntax "/index.html"
  rv = mURI->GetPathQueryRef(path);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // path may contain UTF-8 characters, so ensure that they're escaped.
  if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII | esc_Spaces,
                   buf)) {
    requestURI = &buf;
  } else {
    requestURI = &path;
  }

  // trim off the #ref portion if any...
  int32_t ref1 = requestURI->FindChar('#');
  if (ref1 != kNotFound) {
    requestURI->SetLength(ref1);
  }

  if (mConnectionInfo->UsingConnect() || !mConnectionInfo->UsingHttpProxy()) {
    mRequestHead.SetVersion(gHttpHandler->HttpVersion());
  } else {
    mRequestHead.SetPath(*requestURI);

    // RequestURI should be the absolute uri H1 proxy syntax
    // "http://foo/index.html" so we will overwrite the relative version in
    // requestURI
    rv = mURI->GetUserPass(buf);
    if (NS_FAILED(rv)) return rv;
    if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) ||
                           strncmp(mSpec.get(), "https:", 6) == 0)) {
      nsCOMPtr<nsIURI> tempURI = nsIOService::CreateExposableURI(mURI);
      rv = tempURI->GetAsciiSpec(path);
      if (NS_FAILED(rv)) return rv;
      requestURI = &path;
    } else {
      requestURI = &mSpec;
    }

    // trim off the #ref portion if any...
    int32_t ref2 = requestURI->FindChar('#');
    if (ref2 != kNotFound) {
      requestURI->SetLength(ref2);
    }

    mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion());
  }

  mRequestHead.SetRequestURI(*requestURI);

  // set the request time for cache expiration calculations
  mRequestTime = NowInSeconds();
  StoreRequestTimeInitialized(true);

  // if doing a reload, force end-to-end
  if (mLoadFlags & LOAD_BYPASS_CACHE) {
    // We need to send 'Pragma:no-cache' to inhibit proxy caching even if
    // no proxy is configured since we might be talking with a transparent
    // proxy, i.e. one that operates at the network level.  See bug #14772.
    // But we should not touch Pragma if Cache-Control is already set
    // (https://fetch.spec.whatwg.org/#ref-for-concept-request-cache-mode%E2%91%A3)
    if (!mRequestHead.HasHeader(nsHttp::Pragma)) {
      rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache"true);
      MOZ_ASSERT(NS_SUCCEEDED(rv));
    }
    // If we're configured to speak HTTP/1.1 then also send 'Cache-control:
    // no-cache'. But likewise don't touch Cache-Control if it's already set.
    if (mRequestHead.Version() >= HttpVersion::v1_1 &&
        !mRequestHead.HasHeader(nsHttp::Cache_Control)) {
      rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache"true);
      MOZ_ASSERT(NS_SUCCEEDED(rv));
    }
  } else if (mLoadFlags & VALIDATE_ALWAYS) {
    // We need to send 'Cache-Control: max-age=0' to force each cache along
    // the path to the origin server to revalidate its own entry, if any,
    // with the next cache or server.  See bug #84847.
    //
    // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache'
    //
    // But don't send the headers if they're already set:
    // https://fetch.spec.whatwg.org/#ref-for-concept-request-cache-mode%E2%91%A2
    if (mRequestHead.Version() >= HttpVersion::v1_1) {
      if (!mRequestHead.HasHeader(nsHttp::Cache_Control)) {
        rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "max-age=0",
                                        true);
      }
    } else {
      if (!mRequestHead.HasHeader(nsHttp::Pragma)) {
        rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache"true);
      }
    }
    MOZ_ASSERT(NS_SUCCEEDED(rv));
  }

  if (LoadResuming()) {
    char byteRange[32];
    SprintfLiteral(byteRange, "bytes=%" PRIu64 "-", mStartPos);
    rv = mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(byteRange));
    MOZ_ASSERT(NS_SUCCEEDED(rv));

    if (!mEntityID.IsEmpty()) {
      // Also, we want an error if this resource changed in the meantime
      // Format of the entity id is: escaped_etag/size/lastmod
      nsCString::const_iterator start, end, slash;
      mEntityID.BeginReading(start);
      mEntityID.EndReading(end);
      mEntityID.BeginReading(slash);

      if (FindCharInReadable('/', slash, end)) {
        nsAutoCString ifMatch;
        rv = mRequestHead.SetHeader(
            nsHttp::If_Match,
            NS_UnescapeURL(Substring(start, slash), 0, ifMatch));
        MOZ_ASSERT(NS_SUCCEEDED(rv));

        ++slash;  // Incrementing, so that searching for '/' won't find
                  // the same slash again
      }

      if (FindCharInReadable('/', slash, end)) {
        rv = mRequestHead.SetHeader(nsHttp::If_Unmodified_Since,
                                    Substring(++slash, end));
        MOZ_ASSERT(NS_SUCCEEDED(rv));
      }
    }
  }

  // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.
  if (mLoadFlags & LOAD_ANONYMOUS) mCaps |= NS_HTTP_LOAD_ANONYMOUS;

  if (mUpgradeProtocolCallback) {
    rv = mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    rv = mRequestHead.SetHeaderOnce(nsHttp::Connection, nsHttp::Upgrade.get(),
                                    true);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    mCaps |= NS_HTTP_STICKY_CONNECTION;
    mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
  }

  if (mWebTransportSessionEventListener) {
    mCaps |= NS_HTTP_STICKY_CONNECTION;
  }

  return NS_OK;
}

nsresult nsHttpChannel::InitTransaction() {
  nsresult rv;
  // create wrapper for this channel's notification callbacks
  nsCOMPtr<nsIInterfaceRequestor> callbacks;
  NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup,
                                         getter_AddRefs(callbacks));

  // create the transaction object
  if (nsIOService::UseSocketProcess()) {
    if (NS_WARN_IF(!gIOService->SocketProcessReady())) {
      return NS_ERROR_NOT_AVAILABLE;
    }
    RefPtr<SocketProcessParent> socketProcess =
        SocketProcessParent::GetSingleton();
    if (!socketProcess->CanSend()) {
      return NS_ERROR_NOT_AVAILABLE;
    }

    nsCOMPtr<nsIParentChannel> parentChannel;
    NS_QueryNotificationCallbacks(this, parentChannel);
    RefPtr<DocumentLoadListener> documentChannelParent =
        do_QueryObject(parentChannel);
    // See HttpTransactionChild::CanSendODAToContentProcessDirectly() and
    // nsHttpChannel::CallOnStartRequest() for the reason why we need to know if
    // this is a document load. We only send ODA directly to child process for
    // non document loads.
    RefPtr<HttpTransactionParent> transParent =
        new HttpTransactionParent(!!documentChannelParent);
    LOG1(("nsHttpChannel %p created HttpTransactionParent %p\n"this,
          transParent.get()));

    // Since OnStopRequest could be sent to child process from socket process
    // directly, we need to store these two values in HttpTransactionChild and
    // forward to child process until HttpTransactionChild::OnStopRequest is
    // called.
    transParent->SetRedirectTimestamp(mRedirectStartTimeStamp,
                                      mRedirectEndTimeStamp);

    if (socketProcess) {
      MOZ_ALWAYS_TRUE(
          socketProcess->SendPHttpTransactionConstructor(transParent));
    }
    mTransaction = transParent;
  } else {
    mTransaction = new nsHttpTransaction();
    LOG1(("nsHttpChannel %p created nsHttpTransaction %p\n"this,
          mTransaction.get()));
  }

  // Save the mapping of channel id and the channel. We need this mapping for
  // nsIHttpActivityObserver.
  gHttpHandler->AddHttpChannel(mChannelId, ToSupports(this));

  nsCOMPtr<nsIHttpPushListener> pushListener;
  NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
                                NS_GET_IID(nsIHttpPushListener),
                                getter_AddRefs(pushListener));
  HttpTransactionShell::OnPushCallback pushCallback = nullptr;
  if (pushListener) {
    mCaps |= NS_HTTP_ONPUSH_LISTENER;
    nsWeakPtr weakPtrThis(
        do_GetWeakReference(static_cast<nsIHttpChannel*>(this)));
    pushCallback = [weakPtrThis](uint32_t aPushedStreamId,
                                 const nsACString& aUrl,
                                 const nsACString& aRequestString,
                                 HttpTransactionShell* aTransaction) {
      if (nsCOMPtr<nsIHttpChannel> channel = do_QueryReferent(weakPtrThis)) {
        return static_cast<nsHttpChannel*>(channel.get())
            ->OnPush(aPushedStreamId, aUrl, aRequestString, aTransaction);
      }
      return NS_ERROR_NOT_AVAILABLE;
    };
  }

  EnsureBrowserId();
  EnsureRequestContext();

  HttpTrafficCategory category = CreateTrafficCategory();
  std::function<void(TransactionObserverResult&&)> observer;
  if (mTransactionObserver) {
    observer = [transactionObserver{std::move(mTransactionObserver)}](
                   TransactionObserverResult&& aResult) {
      transactionObserver->Complete(aResult.versionOk(), aResult.authOk(),
                                    aResult.closeReason());
    };
  }
  mTransaction->SetIsForWebTransport(!!mWebTransportSessionEventListener);
  rv = mTransaction->Init(
      mCaps, mConnectionInfo, &mRequestHead, mUploadStream, mReqContentLength,
      LoadUploadStreamHasHeaders(), GetCurrentSerialEventTarget(), callbacks,
      this, mBrowserId, category, mRequestContext, mClassOfService,
      mInitialRwin, LoadResponseTimeoutEnabled(), mChannelId,
      std::move(observer), std::move(pushCallback), mTransWithPushedStream,
      mPushedStreamId);
  if (NS_FAILED(rv)) {
    mTransaction = nullptr;
    return rv;
  }

  return rv;
}

HttpTrafficCategory nsHttpChannel::CreateTrafficCategory() {
  MOZ_ASSERT(!mFirstPartyClassificationFlags ||
             !mThirdPartyClassificationFlags);

  if (!StaticPrefs::network_traffic_analyzer_enabled()) {
    return HttpTrafficCategory::eInvalid;
  }

  HttpTrafficAnalyzer::ClassOfService cos;
  {
    if ((mClassOfService.Flags() & nsIClassOfService::Leader) &&
        mLoadInfo->GetExternalContentPolicyType() ==
            ExtContentPolicy::TYPE_SCRIPT) {
      cos = HttpTrafficAnalyzer::ClassOfService::eLeader;
    } else if (mLoadFlags & nsIRequest::LOAD_BACKGROUND) {
      cos = HttpTrafficAnalyzer::ClassOfService::eBackground;
    } else {
      cos = HttpTrafficAnalyzer::ClassOfService::eOther;
    }
  }

  bool isThirdParty = AntiTrackingUtils::IsThirdPartyChannel(this);

  HttpTrafficAnalyzer::TrackingClassification tc;
  {
    uint32_t flags = isThirdParty ? mThirdPartyClassificationFlags
                                  : mFirstPartyClassificationFlags;

    using CF = nsIClassifiedChannel::ClassificationFlags;
    using TC = HttpTrafficAnalyzer::TrackingClassification;

    if (flags & CF::CLASSIFIED_TRACKING_CONTENT) {
      tc = TC::eContent;
    } else if (flags & CF::CLASSIFIED_FINGERPRINTING_CONTENT) {
      tc = TC::eFingerprinting;
    } else if (flags & CF::CLASSIFIED_ANY_BASIC_TRACKING) {
      tc = TC::eBasic;
    } else {
      tc = TC::eNone;
    }
  }

  bool isSystemPrincipal =
      mLoadInfo->GetLoadingPrincipal() &&
      mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal();
  return HttpTrafficAnalyzer::CreateTrafficCategory(
      NS_UsePrivateBrowsing(this), isSystemPrincipal, isThirdParty, cos, tc);
}

--> --------------------

--> maximum size reached

--> --------------------

99%


¤ Dauer der Verarbeitung: 0.32 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge