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


Quelle  HttpBaseChannel.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */

/* 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 "mozilla/net/HttpBaseChannel.h"

#include <algorithm>
#include <utility>

#include "HttpBaseChannel.h"
#include "HttpLog.h"
#include "LoadInfo.h"
#include "ReferrerInfo.h"
#include "mozIRemoteLazyInputStream.h"
#include "mozIThirdPartyUtil.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/AntiTrackingUtils.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/BinarySearch.h"
#include "mozilla/ConsoleReportCollector.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/InputStreamLengthHelper.h"
#include "mozilla/Mutex.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/PermissionManager.h"
#include "mozilla/Components.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_security.h"
#include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Tokenizer.h"
#include "mozilla/browser/NimbusFeatures.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/FetchPriority.h"
#include "mozilla/dom/nsHTTPSOnlyUtils.h"
#include "mozilla/dom/nsMixedContentBlocker.h"
#include "mozilla/dom/Performance.h"
#include "mozilla/dom/PerformanceStorage.h"
#include "mozilla/dom/ProcessIsolation.h"
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/net/OpaqueResponseUtils.h"
#include "mozilla/net/UrlClassifierCommon.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "nsBufferedStreams.h"
#include "nsCOMPtr.h"
#include "nsCRT.h"
#include "nsContentSecurityManager.h"
#include "nsContentSecurityUtils.h"
#include "nsContentUtils.h"
#include "nsDebug.h"
#include "nsEscape.h"
#include "nsGlobalWindowInner.h"
#include "nsGlobalWindowOuter.h"
#include "nsHttpChannel.h"
#include "nsHTTPCompressConv.h"
#include "nsHttpHandler.h"
#include "nsICacheInfoChannel.h"
#include "nsICachingChannel.h"
#include "nsIChannelEventSink.h"
#include "nsIConsoleService.h"
#include "nsIContentPolicy.h"
#include "nsICookieService.h"
#include "nsIDOMWindowUtils.h"
#include "nsIDocShell.h"
#include "nsIDNSService.h"
#include "nsIEncodedChannel.h"
#include "nsIHttpHeaderVisitor.h"
#include "nsILoadGroupChild.h"
#include "nsIMIMEInputStream.h"
#include "nsIMultiplexInputStream.h"
#include "nsIMutableArray.h"
#include "nsINetworkInterceptController.h"
#include "nsIObserverService.h"
#include "nsIPrincipal.h"
#include "nsIProtocolProxyService.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsISecurityConsoleMessage.h"
#include "nsISeekableStream.h"
#include "nsIStorageStream.h"
#include "nsIStreamConverterService.h"
#include "nsITimedChannel.h"
#include "nsITransportSecurityInfo.h"
#include "nsIURIMutator.h"
#include "nsMimeTypes.h"
#include "nsNetCID.h"
#include "nsNetUtil.h"
#include "nsPIDOMWindow.h"
#include "nsProxyRelease.h"
#include "nsReadableUtils.h"
#include "nsRedirectHistoryEntry.h"
#include "nsServerTiming.h"
#include "nsStreamListenerWrapper.h"
#include "nsStreamUtils.h"
#include "nsString.h"
#include "nsThreadUtils.h"
#include "nsURLHelper.h"
#include "mozilla/RemoteLazyInputStreamChild.h"
#include "mozilla/net/SFVService.h"
#include "mozilla/dom/ContentChild.h"
#include "nsQueryObject.h"

using mozilla::dom::RequestMode;

#define LOGORB(msg, ...)                \
  MOZ_LOG(GetORBLog(), LogLevel::Debug, \
          ("%s: %p " msg, __func__, this##__VA_ARGS__))

namespace mozilla {
namespace net {

static bool IsHeaderBlacklistedForRedirectCopy(nsHttpAtom const& aHeader) {
  // IMPORTANT: keep this list ASCII-code sorted
  static nsHttpAtomLiteral const* blackList[] = {
      &nsHttp::Accept,
      &nsHttp::Accept_Encoding,
      &nsHttp::Accept_Language,
      &nsHttp::Alternate_Service_Used,
      &nsHttp::Authentication,
      &nsHttp::Authorization,
      &nsHttp::Connection,
      &nsHttp::Content_Length,
      &nsHttp::Cookie,
      &nsHttp::Host,
      &nsHttp::If,
      &nsHttp::If_Match,
      &nsHttp::If_Modified_Since,
      &nsHttp::If_None_Match,
      &nsHttp::If_None_Match_Any,
      &nsHttp::If_Range,
      &nsHttp::If_Unmodified_Since,
      &nsHttp::Proxy_Authenticate,
      &nsHttp::Proxy_Authorization,
      &nsHttp::Range,
      &nsHttp::TE,
      &nsHttp::Transfer_Encoding,
      &nsHttp::Upgrade,
      &nsHttp::User_Agent,
      &nsHttp::WWW_Authenticate};

  class HttpAtomComparator {
    nsHttpAtom const& mTarget;

   public:
    explicit HttpAtomComparator(nsHttpAtom const& aTarget) : mTarget(aTarget) {}
    int operator()(nsHttpAtom const* aVal) const {
      if (mTarget == *aVal) {
        return 0;
      }
      return strcmp(mTarget.get(), aVal->get());
    }
    int operator()(nsHttpAtomLiteral const* aVal) const {
      if (mTarget == *aVal) {
        return 0;
      }
      return strcmp(mTarget.get(), aVal->get());
    }
  };

  size_t unused;
  return BinarySearchIf(blackList, 0, std::size(blackList),
                        HttpAtomComparator(aHeader), &unused);
}

class AddHeadersToChannelVisitor final : public nsIHttpHeaderVisitor {
 public:
  NS_DECL_ISUPPORTS

  explicit AddHeadersToChannelVisitor(nsIHttpChannel* aChannel)
      : mChannel(aChannel) {}

  NS_IMETHOD VisitHeader(const nsACString& aHeader,
                         const nsACString& aValue) override {
    nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
    if (!IsHeaderBlacklistedForRedirectCopy(atom)) {
      DebugOnly<nsresult> rv =
          mChannel->SetRequestHeader(aHeader, aValue, false);
      MOZ_ASSERT(NS_SUCCEEDED(rv));
    }
    return NS_OK;
  }

 private:
  ~AddHeadersToChannelVisitor() = default;

  nsCOMPtr<nsIHttpChannel> mChannel;
};

NS_IMPL_ISUPPORTS(AddHeadersToChannelVisitor, nsIHttpHeaderVisitor)

static OpaqueResponseFilterFetch ConfiguredFilterFetchResponseBehaviour() {
  uint32_t pref = StaticPrefs::
      browser_opaqueResponseBlocking_filterFetchResponse_DoNotUseDirectly();
  if (NS_WARN_IF(pref >
                 static_cast<uint32_t>(OpaqueResponseFilterFetch::All))) {
    return OpaqueResponseFilterFetch::All;
  }

  return static_cast<OpaqueResponseFilterFetch>(pref);
}

HttpBaseChannel::HttpBaseChannel()
    : mReportCollector(new ConsoleReportCollector()),
      mHttpHandler(gHttpHandler),
      mChannelCreationTime(0),
      mComputedCrossOriginOpenerPolicy(nsILoadInfo::OPENER_POLICY_UNSAFE_NONE),
      mStartPos(UINT64_MAX),
      mTransferSize(0),
      mRequestSize(0),
      mDecodedBodySize(0),
      mSupportsHTTP3(false),
      mEncodedBodySize(0),
      mRequestContextID(0),
      mContentWindowId(0),
      mBrowserId(0),
      mAltDataLength(-1),
      mChannelId(0),
      mReqContentLength(0U),
      mStatus(NS_OK),
      mCanceled(false),
      mFirstPartyClassificationFlags(0),
      mThirdPartyClassificationFlags(0),
      mLoadFlags(LOAD_NORMAL),
      mCaps(0),
      mClassOfService(0, false),
      mTlsFlags(0),
      mSuspendCount(0),
      mInitialRwin(0),
      mProxyResolveFlags(0),
      mContentDispositionHint(UINT32_MAX),
      mRequestMode(RequestMode::No_cors),
      mRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW),
      mLastRedirectFlags(0),
      mPriority(PRIORITY_NORMAL),
      mRedirectionLimit(gHttpHandler->RedirectionLimit()),
      mRedirectCount(0),
      mInternalRedirectCount(0),
      mCachedOpaqueResponseBlockingPref(
          StaticPrefs::browser_opaqueResponseBlocking()),
      mChannelBlockedByOpaqueResponse(false),
      mDummyChannelForCachedResource(false),
      mHasContentDecompressed(false),
      mRenderBlocking(false) {
  StoreApplyConversion(true);
  StoreAllowSTS(true);
  StoreTracingEnabled(true);
  StoreReportTiming(true);
  StoreAllowSpdy(true);
  StoreAllowHttp3(true);
  StoreAllowAltSvc(true);
  StoreResponseTimeoutEnabled(true);
  StoreAllRedirectsSameOrigin(true);
  StoreAllRedirectsPassTimingAllowCheck(true);
  StoreUpgradableToSecure(true);
  StoreIsUserAgentHeaderModified(false);

  this->mSelfAddr.inet = {};
  this->mPeerAddr.inet = {};
  LOG(("Creating HttpBaseChannel @%p\n"this));

  // Subfields of unions cannot be targeted in an initializer list.
#ifdef MOZ_VALGRIND
  // Zero the entire unions so that Valgrind doesn't complain when we send them
  // to another process.
  memset(&mSelfAddr, 0, sizeof(NetAddr));
  memset(&mPeerAddr, 0, sizeof(NetAddr));
#endif
  mSelfAddr.raw.family = PR_AF_UNSPEC;
  mPeerAddr.raw.family = PR_AF_UNSPEC;
}

HttpBaseChannel::~HttpBaseChannel() {
  LOG(("Destroying HttpBaseChannel @%p\n"this));

  // Make sure we don't leak
  CleanRedirectCacheChainIfNecessary();

  ReleaseMainThreadOnlyReferences();
}

namespace {  // anon

class NonTailRemover : public nsISupports {
  NS_DECL_THREADSAFE_ISUPPORTS

  explicit NonTailRemover(nsIRequestContext* rc) : mRequestContext(rc) {}

 private:
  virtual ~NonTailRemover() {
    MOZ_ASSERT(NS_IsMainThread());
    mRequestContext->RemoveNonTailRequest();
  }

  nsCOMPtr<nsIRequestContext> mRequestContext;
};

NS_IMPL_ISUPPORTS0(NonTailRemover)

}  // namespace

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

  nsTArray<nsCOMPtr<nsISupports>> arrayToRelease;
  arrayToRelease.AppendElement(mLoadGroup.forget());
  arrayToRelease.AppendElement(mLoadInfo.forget());
  arrayToRelease.AppendElement(mCallbacks.forget());
  arrayToRelease.AppendElement(mProgressSink.forget());
  arrayToRelease.AppendElement(mPrincipal.forget());
  arrayToRelease.AppendElement(mListener.forget());
  arrayToRelease.AppendElement(mCompressListener.forget());
  arrayToRelease.AppendElement(mORB.forget());

  if (LoadAddedAsNonTailRequest()) {
    // RemoveNonTailRequest() on our request context must be called on the main
    // thread
    MOZ_RELEASE_ASSERT(mRequestContext,
                       "Someone released rc or set flags w/o having it?");

    nsCOMPtr<nsISupports> nonTailRemover(new NonTailRemover(mRequestContext));
    arrayToRelease.AppendElement(nonTailRemover.forget());
  }

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

void HttpBaseChannel::AddClassificationFlags(uint32_t aClassificationFlags,
                                             bool aIsThirdParty) {
  LOG(
      ("HttpBaseChannel::AddClassificationFlags classificationFlags=%d "
       "thirdparty=%d %p",
       aClassificationFlags, static_cast<int>(aIsThirdParty), this));

  if (aIsThirdParty) {
    mThirdPartyClassificationFlags |= aClassificationFlags;
  } else {
    mFirstPartyClassificationFlags |= aClassificationFlags;
  }
}

static bool isSecureOrTrustworthyURL(nsIURI* aURI) {
  return aURI->SchemeIs("https") ||
         (StaticPrefs::network_http_encoding_trustworthy_is_https() &&
          nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI));
}

nsresult HttpBaseChannel::Init(nsIURI* aURI, uint32_t aCaps,
                               nsProxyInfo* aProxyInfo,
                               uint32_t aProxyResolveFlags, nsIURI* aProxyURI,
                               uint64_t aChannelId,
                               ExtContentPolicyType aContentPolicyType,
                               nsILoadInfo* aLoadInfo) {
  LOG1(("HttpBaseChannel::Init [this=%p]\n"this));

  MOZ_ASSERT(aURI, "null uri");

  mURI = aURI;
  mOriginalURI = aURI;
  mDocumentURI = nullptr;
  mCaps = aCaps;
  mProxyResolveFlags = aProxyResolveFlags;
  mProxyURI = aProxyURI;
  mChannelId = aChannelId;
  mLoadInfo = aLoadInfo;

  // Construct connection info object
  nsAutoCString host;
  int32_t port = -1;
  bool isHTTPS = isSecureOrTrustworthyURL(mURI);

  nsresult rv = mURI->GetAsciiHost(host);
  if (NS_FAILED(rv)) return rv;

  // Reject the URL if it doesn't specify a host
  if (host.IsEmpty()) return NS_ERROR_MALFORMED_URI;

  rv = mURI->GetPort(&port);
  if (NS_FAILED(rv)) return rv;

  LOG1(("host=%s port=%d\n", host.get(), port));

  rv = mURI->GetAsciiSpec(mSpec);
  if (NS_FAILED(rv)) return rv;
  LOG1(("uri=%s\n", mSpec.get()));

  // Assert default request method
  MOZ_ASSERT(mRequestHead.EqualsMethod(nsHttpRequestHead::kMethod_Get));

  // Set request headers
  nsAutoCString hostLine;
  rv = nsHttpHandler::GenerateHostPort(host, port, hostLine);
  if (NS_FAILED(rv)) return rv;

  rv = mRequestHead.SetHeader(nsHttp::Host, hostLine);
  if (NS_FAILED(rv)) return rv;

  rv = gHttpHandler->AddStandardRequestHeaders(
      &mRequestHead, isHTTPS, aContentPolicyType,
      nsContentUtils::ShouldResistFingerprinting(this,
                                                 RFPTarget::HttpUserAgent));
  if (NS_FAILED(rv)) return rv;

  nsAutoCString type;
  if (aProxyInfo && NS_SUCCEEDED(aProxyInfo->GetType(type)) &&
      !type.EqualsLiteral("unknown")) {
    mProxyInfo = aProxyInfo;
  }

  mCurrentThread = GetCurrentSerialEventTarget();
  return rv;
}

//-----------------------------------------------------------------------------
// HttpBaseChannel::nsISupports
//-----------------------------------------------------------------------------

NS_IMPL_ADDREF(HttpBaseChannel)
NS_IMPL_RELEASE(HttpBaseChannel)

NS_INTERFACE_MAP_BEGIN(HttpBaseChannel)
  NS_INTERFACE_MAP_ENTRY(nsIRequest)
  NS_INTERFACE_MAP_ENTRY(nsIChannel)
  NS_INTERFACE_MAP_ENTRY(nsIIdentChannel)
  NS_INTERFACE_MAP_ENTRY(nsIEncodedChannel)
  NS_INTERFACE_MAP_ENTRY(nsIHttpChannel)
  NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal)
  NS_INTERFACE_MAP_ENTRY(nsIForcePendingChannel)
  NS_INTERFACE_MAP_ENTRY(nsIUploadChannel)
  NS_INTERFACE_MAP_ENTRY(nsIFormPOSTActionChannel)
  NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
  NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
  NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
  NS_INTERFACE_MAP_ENTRY(nsIPrivateBrowsingChannel)
  NS_INTERFACE_MAP_ENTRY(nsITimedChannel)
  NS_INTERFACE_MAP_ENTRY(nsIConsoleReportCollector)
  NS_INTERFACE_MAP_ENTRY(nsIThrottledInputChannel)
  NS_INTERFACE_MAP_ENTRY(nsIClassifiedChannel)
  NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpBaseChannel)
NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag)

//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIRequest
//-----------------------------------------------------------------------------

NS_IMETHODIMP
HttpBaseChannel::GetName(nsACString& aName) {
  aName = mSpec;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::IsPending(bool* aIsPending) {
  NS_ENSURE_ARG_POINTER(aIsPending);
  *aIsPending = LoadIsPending() || LoadForcePending();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetStatus(nsresult* aStatus) {
  NS_ENSURE_ARG_POINTER(aStatus);
  *aStatus = mStatus;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
  NS_ENSURE_ARG_POINTER(aLoadGroup);
  *aLoadGroup = do_AddRef(mLoadGroup).take();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
  MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");

  if (!CanSetLoadGroup(aLoadGroup)) {
    return NS_ERROR_FAILURE;
  }

  mLoadGroup = aLoadGroup;
  mProgressSink = nullptr;
  UpdatePrivateBrowsing();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
  NS_ENSURE_ARG_POINTER(aLoadFlags);
  *aLoadFlags = mLoadFlags;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
  mLoadFlags = aLoadFlags;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
  if (!LoadIsOCSP()) {
    return GetTRRModeImpl(aTRRMode);
  }

  nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
  nsIDNSService::ResolverMode trrMode = nsIDNSService::MODE_NATIVEONLY;
  // If this is an OCSP channel, and the global TRR mode is TRR_ONLY (3)
  // then we set the mode for this channel as TRR_DISABLED_MODE.
  // We do this to prevent a TRR service channel's OCSP validation from
  // blocking DNS resolution completely.
  if (dns && NS_SUCCEEDED(dns->GetCurrentTrrMode(&trrMode)) &&
      trrMode == nsIDNSService::MODE_TRRONLY) {
    *aTRRMode = nsIRequest::TRR_DISABLED_MODE;
    return NS_OK;
  }

  return GetTRRModeImpl(aTRRMode);
}

NS_IMETHODIMP
HttpBaseChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
  return SetTRRModeImpl(aTRRMode);
}

NS_IMETHODIMP
HttpBaseChannel::SetDocshellUserAgentOverride() {
  RefPtr<dom::BrowsingContext> bc;
  MOZ_ALWAYS_SUCCEEDS(mLoadInfo->GetBrowsingContext(getter_AddRefs(bc)));
  if (!bc) {
    return NS_OK;
  }

  nsAutoString customUserAgent;
  bc->GetCustomUserAgent(customUserAgent);
  if (customUserAgent.IsEmpty() || customUserAgent.IsVoid()) {
    return NS_OK;
  }

  NS_ConvertUTF16toUTF8 utf8CustomUserAgent(customUserAgent);
  nsresult rv = SetRequestHeaderInternal(
      "User-Agent"_ns, utf8CustomUserAgent, false,
      nsHttpHeaderArray::eVarietyRequestEnforceDefault);
  if (NS_FAILED(rv)) {
    return rv;
  }

  return NS_OK;
}

//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIChannel
//-----------------------------------------------------------------------------

NS_IMETHODIMP
HttpBaseChannel::GetOriginalURI(nsIURI** aOriginalURI) {
  NS_ENSURE_ARG_POINTER(aOriginalURI);
  *aOriginalURI = do_AddRef(mOriginalURI).take();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetOriginalURI(nsIURI* aOriginalURI) {
  ENSURE_CALLED_BEFORE_CONNECT();

  NS_ENSURE_ARG_POINTER(aOriginalURI);
  mOriginalURI = aOriginalURI;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetURI(nsIURI** aURI) {
  NS_ENSURE_ARG_POINTER(aURI);
  *aURI = do_AddRef(mURI).take();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetOwner(nsISupports** aOwner) {
  NS_ENSURE_ARG_POINTER(aOwner);
  *aOwner = do_AddRef(mOwner).take();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetOwner(nsISupports* aOwner) {
  mOwner = aOwner;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
  MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
  mLoadInfo = aLoadInfo;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
  *aLoadInfo = do_AddRef(mLoadInfo).take();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetIsDocument(bool* aIsDocument) {
  return NS_GetIsDocumentChannel(this, aIsDocument);
}

NS_IMETHODIMP
HttpBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
  *aCallbacks = do_AddRef(mCallbacks).take();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
  MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");

  if (!CanSetCallbacks(aCallbacks)) {
    return NS_ERROR_FAILURE;
  }

  mCallbacks = aCallbacks;
  mProgressSink = nullptr;

  UpdatePrivateBrowsing();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetContentType(nsACString& aContentType) {
  if (!mResponseHead) {
    aContentType.Truncate();
    return NS_ERROR_NOT_AVAILABLE;
  }

  mResponseHead->ContentType(aContentType);
  if (!aContentType.IsEmpty()) {
    return NS_OK;
  }

  aContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetContentType(const nsACString& aContentType) {
  if (mListener || LoadWasOpened() || mDummyChannelForCachedResource) {
    if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;

    nsAutoCString contentTypeBuf, charsetBuf;
    bool hadCharset;
    net_ParseContentType(aContentType, contentTypeBuf, charsetBuf, &hadCharset);

    mResponseHead->SetContentType(contentTypeBuf);

    // take care not to stomp on an existing charset
    if (hadCharset) mResponseHead->SetContentCharset(charsetBuf);

  } else {
    // We are being given a content-type hint.
    bool dummy;
    net_ParseContentType(aContentType, mContentTypeHint, mContentCharsetHint,
                         &dummy);
  }

  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetContentCharset(nsACString& aContentCharset) {
  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;

  mResponseHead->ContentCharset(aContentCharset);
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetContentCharset(const nsACString& aContentCharset) {
  if (mListener) {
    if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;

    mResponseHead->SetContentCharset(aContentCharset);
  } else {
    // Charset hint
    mContentCharsetHint = aContentCharset;
  }
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetContentDisposition(uint32_t* aContentDisposition) {
  // See bug 1658877. If mContentDispositionHint is already
  // DISPOSITION_ATTACHMENT, it means this channel is created from a
  // download attribute. In this case, we should prefer the value from the
  // download attribute rather than the value in content disposition header.
  // DISPOSITION_FORCE_INLINE is used to explicitly set inline, used by
  // the pdf reader when loading a attachment pdf without having to
  // download it.
  if (mContentDispositionHint == nsIChannel::DISPOSITION_ATTACHMENT ||
      mContentDispositionHint == nsIChannel::DISPOSITION_FORCE_INLINE) {
    *aContentDisposition = mContentDispositionHint;
    return NS_OK;
  }

  nsresult rv;
  nsCString header;

  rv = GetContentDispositionHeader(header);
  if (NS_FAILED(rv)) {
    if (mContentDispositionHint == UINT32_MAX) return rv;

    *aContentDisposition = mContentDispositionHint;
    return NS_OK;
  }

  *aContentDisposition = NS_GetContentDispositionFromHeader(header, this);
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetContentDisposition(uint32_t aContentDisposition) {
  mContentDispositionHint = aContentDisposition;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetContentDispositionFilename(
    nsAString& aContentDispositionFilename) {
  aContentDispositionFilename.Truncate();
  nsresult rv;
  nsCString header;

  rv = GetContentDispositionHeader(header);
  if (NS_SUCCEEDED(rv)) {
    rv = NS_GetFilenameFromDisposition(aContentDispositionFilename, header);
  }

  // If we failed to get the filename from header, we should use
  // mContentDispositionFilename, since mContentDispositionFilename is set from
  // the download attribute.
  if (NS_FAILED(rv)) {
    if (!mContentDispositionFilename) {
      return rv;
    }

    aContentDispositionFilename = *mContentDispositionFilename;
    return NS_OK;
  }

  return rv;
}

NS_IMETHODIMP
HttpBaseChannel::SetContentDispositionFilename(
    const nsAString& aContentDispositionFilename) {
  mContentDispositionFilename =
      MakeUnique<nsString>(aContentDispositionFilename);

  // For safety reasons ensure the filename doesn't contain null characters and
  // replace them with underscores. We may later pass the extension to system
  // MIME APIs that expect null terminated strings.
  mContentDispositionFilename->ReplaceChar(char16_t(0), '_');

  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetContentDispositionHeader(
    nsACString& aContentDispositionHeader) {
  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;

  nsresult rv = mResponseHead->GetHeader(nsHttp::Content_Disposition,
                                         aContentDispositionHeader);
  if (NS_FAILED(rv) || aContentDispositionHeader.IsEmpty()) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetContentLength(int64_t* aContentLength) {
  NS_ENSURE_ARG_POINTER(aContentLength);

  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;

  if (LoadDeliveringAltData()) {
    MOZ_ASSERT(!mAvailableCachedAltDataType.IsEmpty());
    *aContentLength = mAltDataLength;
    return NS_OK;
  }

  *aContentLength = mResponseHead->ContentLength();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetContentLength(int64_t value) {
  if (!mDummyChannelForCachedResource) {
    MOZ_ASSERT_UNREACHABLE("HttpBaseChannel::SetContentLength");
    return NS_ERROR_NOT_IMPLEMENTED;
  }
  MOZ_ASSERT(mResponseHead);
  mResponseHead->SetContentLength(value);
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::Open(nsIInputStream** aStream) {
  if (!gHttpHandler->Active()) {
    LOG(("HttpBaseChannel::Open after HTTP shutdown..."));
    return NS_ERROR_NOT_AVAILABLE;
  }

  nsCOMPtr<nsIStreamListener> listener;
  nsresult rv =
      nsContentSecurityManager::doContentSecurityCheck(this, listener);
  NS_ENSURE_SUCCESS(rv, rv);

  NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_IN_PROGRESS);

  if (!gHttpHandler->Active()) {
    LOG(("HttpBaseChannel::Open after HTTP shutdown..."));
    return NS_ERROR_NOT_AVAILABLE;
  }

  return NS_ImplementChannelOpen(this, aStream);
}

//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIUploadChannel
//-----------------------------------------------------------------------------

NS_IMETHODIMP
HttpBaseChannel::GetUploadStream(nsIInputStream** stream) {
  NS_ENSURE_ARG_POINTER(stream);
  *stream = do_AddRef(mUploadStream).take();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetUploadStream(nsIInputStream* stream,
                                 const nsACString& contentTypeArg,
                                 int64_t contentLength) {
  // NOTE: for backwards compatibility and for compatibility with old style
  // plugins, |stream| may include headers, specifically Content-Type and
  // Content-Length headers.  in this case, |contentType| and |contentLength|
  // would be unspecified.  this is traditionally the case of a POST request,
  // and so we select POST as the request method if contentType and
  // contentLength are unspecified.

  if (stream) {
    nsAutoCString method;
    bool hasHeaders = false;

    // This method and ExplicitSetUploadStream mean different things by "empty
    // content type string".  This method means "no header", but
    // ExplicitSetUploadStream means "header with empty value".  So we have to
    // massage the contentType argument into the form ExplicitSetUploadStream
    // expects.
    nsCOMPtr<nsIMIMEInputStream> mimeStream;
    nsCString contentType(contentTypeArg);
    if (contentType.IsEmpty()) {
      contentType.SetIsVoid(true);
      method = "POST"_ns;

      // MIME streams are a special case, and include headers which need to be
      // copied to the channel.
      mimeStream = do_QueryInterface(stream);
      if (mimeStream) {
        // Copy non-origin related headers to the channel.
        nsCOMPtr<nsIHttpHeaderVisitor> visitor =
            new AddHeadersToChannelVisitor(this);
        mimeStream->VisitHeaders(visitor);

        return ExplicitSetUploadStream(stream, contentType, contentLength,
                                       method, hasHeaders);
      }

      hasHeaders = true;
    } else {
      method = "PUT"_ns;

      MOZ_ASSERT(
          NS_FAILED(CallQueryInterface(stream, getter_AddRefs(mimeStream))),
          "nsIMIMEInputStream should not be set with an explicit content type");
    }
    return ExplicitSetUploadStream(stream, contentType, contentLength, method,
                                   hasHeaders);
  }

  // if stream is null, ExplicitSetUploadStream returns error.
  // So we need special case for GET method.
  StoreUploadStreamHasHeaders(false);
  SetRequestMethod("GET"_ns);  // revert to GET request
  mUploadStream = nullptr;
  return NS_OK;
}

namespace {

class MIMEHeaderCopyVisitor final : public nsIHttpHeaderVisitor {
 public:
  explicit MIMEHeaderCopyVisitor(nsIMIMEInputStream* aDest) : mDest(aDest) {}

  NS_DECL_ISUPPORTS
  NS_IMETHOD VisitHeader(const nsACString& aName,
                         const nsACString& aValue) override {
    return mDest->AddHeader(PromiseFlatCString(aName).get(),
                            PromiseFlatCString(aValue).get());
  }

 private:
  ~MIMEHeaderCopyVisitor() = default;

  nsCOMPtr<nsIMIMEInputStream> mDest;
};

NS_IMPL_ISUPPORTS(MIMEHeaderCopyVisitor, nsIHttpHeaderVisitor)

static void NormalizeCopyComplete(void* aClosure, nsresult aStatus) {
#ifdef DEBUG
  // Called on the STS thread by NS_AsyncCopy
  nsCOMPtr<nsIEventTarget> sts =
      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
  bool result = false;
  sts->IsOnCurrentThread(&result);
  MOZ_ASSERT(result, "Should only be called on the STS thread.");
#endif

  RefPtr<GenericPromise::Private> ready =
      already_AddRefed(static_cast<GenericPromise::Private*>(aClosure));
  if (NS_SUCCEEDED(aStatus)) {
    ready->Resolve(true, __func__);
  } else {
    ready->Reject(aStatus, __func__);
  }
}

// Normalize the upload stream for a HTTP channel, so that is one of the
// expected and compatible types. Components like WebExtensions and DevTools
// expect that upload streams in the parent process are cloneable, seekable, and
// synchronous to read, which this function helps guarantee somewhat efficiently
// and without loss of information.
//
// If the replacement stream outparameter is not initialized to `nullptr`, the
// returned stream should be used instead of `aUploadStream` as the upload
// stream for the HTTP channel, and the previous stream should not be touched
// again.
//
// If aReadyPromise is non-nullptr after the function is called, it is a promise
// which should be awaited before continuing to `AsyncOpen` the HTTP channel,
// as the replacement stream will not be ready until it is resolved.
static nsresult NormalizeUploadStream(nsIInputStream* aUploadStream,
                                      nsIInputStream** aReplacementStream,
                                      GenericPromise** aReadyPromise) {
  MOZ_ASSERT(XRE_IsParentProcess());

  *aReplacementStream = nullptr;
  *aReadyPromise = nullptr;

  // Unwrap RemoteLazyInputStream and normalize the contents as we're in the
  // parent process.
  if (nsCOMPtr<mozIRemoteLazyInputStream> lazyStream =
          do_QueryInterface(aUploadStream)) {
    nsCOMPtr<nsIInputStream> internal;
    if (NS_SUCCEEDED(
            lazyStream->TakeInternalStream(getter_AddRefs(internal)))) {
      nsCOMPtr<nsIInputStream> replacement;
      nsresult rv = NormalizeUploadStream(internal, getter_AddRefs(replacement),
                                          aReadyPromise);
      NS_ENSURE_SUCCESS(rv, rv);

      if (replacement) {
        replacement.forget(aReplacementStream);
      } else {
        internal.forget(aReplacementStream);
      }
      return NS_OK;
    }
  }

  // Preserve MIME information on the stream when normalizing.
  if (nsCOMPtr<nsIMIMEInputStream> mime = do_QueryInterface(aUploadStream)) {
    nsCOMPtr<nsIInputStream> data;
    nsresult rv = mime->GetData(getter_AddRefs(data));
    NS_ENSURE_SUCCESS(rv, rv);

    nsCOMPtr<nsIInputStream> replacement;
    rv =
        NormalizeUploadStream(data, getter_AddRefs(replacement), aReadyPromise);
    NS_ENSURE_SUCCESS(rv, rv);

    if (replacement) {
      nsCOMPtr<nsIMIMEInputStream> replacementMime(
          do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv));
      NS_ENSURE_SUCCESS(rv, rv);

      nsCOMPtr<nsIHttpHeaderVisitor> visitor =
          new MIMEHeaderCopyVisitor(replacementMime);
      rv = mime->VisitHeaders(visitor);
      NS_ENSURE_SUCCESS(rv, rv);

      rv = replacementMime->SetData(replacement);
      NS_ENSURE_SUCCESS(rv, rv);

      replacementMime.forget(aReplacementStream);
    }
    return NS_OK;
  }

  // Preserve "real" buffered input streams which wrap data (i.e. are backed by
  // nsBufferedInputStream), but normalize the wrapped stream.
  if (nsCOMPtr<nsIBufferedInputStream> buffered =
          do_QueryInterface(aUploadStream)) {
    nsCOMPtr<nsIInputStream> data;
    if (NS_SUCCEEDED(buffered->GetData(getter_AddRefs(data)))) {
      nsCOMPtr<nsIInputStream> replacement;
      nsresult rv = NormalizeUploadStream(data, getter_AddRefs(replacement),
                                          aReadyPromise);
      NS_ENSURE_SUCCESS(rv, rv);
      if (replacement) {
        // This buffer size should be kept in sync with HTMLFormSubmission.
        rv = NS_NewBufferedInputStream(aReplacementStream, replacement.forget(),
                                       8192);
        NS_ENSURE_SUCCESS(rv, rv);
      }
      return NS_OK;
    }
  }

  // Preserve multiplex input streams, normalizing each individual inner stream
  // to avoid unnecessary copying.
  if (nsCOMPtr<nsIMultiplexInputStream> multiplex =
          do_QueryInterface(aUploadStream)) {
    uint32_t count = multiplex->GetCount();
    nsTArray<nsCOMPtr<nsIInputStream>> streams(count);
    nsTArray<RefPtr<GenericPromise>> promises(count);
    bool replace = false;
    for (uint32_t i = 0; i < count; ++i) {
      nsCOMPtr<nsIInputStream> inner;
      nsresult rv = multiplex->GetStream(i, getter_AddRefs(inner));
      NS_ENSURE_SUCCESS(rv, rv);

      RefPtr<GenericPromise> promise;
      nsCOMPtr<nsIInputStream> replacement;
      rv = NormalizeUploadStream(inner, getter_AddRefs(replacement),
                                 getter_AddRefs(promise));
      NS_ENSURE_SUCCESS(rv, rv);
      if (promise) {
        promises.AppendElement(promise);
      }
      if (replacement) {
        streams.AppendElement(replacement);
        replace = true;
      } else {
        streams.AppendElement(inner);
      }
    }

    // If any of the inner streams needed to be replaced, replace the entire
    // nsIMultiplexInputStream.
    if (replace) {
      nsresult rv;
      nsCOMPtr<nsIMultiplexInputStream> replacement =
          do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv);
      NS_ENSURE_SUCCESS(rv, rv);
      for (auto& stream : streams) {
        rv = replacement->AppendStream(stream);
        NS_ENSURE_SUCCESS(rv, rv);
      }

      MOZ_ALWAYS_SUCCEEDS(CallQueryInterface(replacement, aReplacementStream));
    }

    // Wait for all inner promises to settle before resolving the final promise.
    if (!promises.IsEmpty()) {
      RefPtr<GenericPromise> ready =
          GenericPromise::AllSettled(GetCurrentSerialEventTarget(), promises)
              ->Then(GetCurrentSerialEventTarget(), __func__,
                     [](GenericPromise::AllSettledPromiseType::
                            ResolveOrRejectValue&& aResults)
                         -> RefPtr<GenericPromise> {
                       MOZ_ASSERT(aResults.IsResolve(),
                                  "AllSettled never rejects");
                       for (auto& result : aResults.ResolveValue()) {
                         if (result.IsReject()) {
                           return GenericPromise::CreateAndReject(
                               result.RejectValue(), __func__);
                         }
                       }
                       return GenericPromise::CreateAndResolve(true, __func__);
                     });
      ready.forget(aReadyPromise);
    }
    return NS_OK;
  }

  // If the stream is cloneable, seekable and non-async, we can allow it.  Async
  // input streams can cause issues, as various consumers of input streams
  // expect the payload to be synchronous and `Available()` to be the length of
  // the stream, which is not true for asynchronous streams.
  nsCOMPtr<nsIAsyncInputStream> async = do_QueryInterface(aUploadStream);
  nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aUploadStream);
  if (NS_InputStreamIsCloneable(aUploadStream) && seekable && !async) {
    return NS_OK;
  }

  // Asynchronously copy our non-normalized stream into a StorageStream so that
  // it is seekable, cloneable, and synchronous once the copy completes.

  NS_WARNING("Upload Stream is being copied into StorageStream");

  nsCOMPtr<nsIStorageStream> storageStream;
  nsresult rv =
      NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storageStream));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIOutputStream> sink;
  rv = storageStream->GetOutputStream(0, getter_AddRefs(sink));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIInputStream> replacementStream;
  rv = storageStream->NewInputStream(0, getter_AddRefs(replacementStream));
  NS_ENSURE_SUCCESS(rv, rv);

  // Ensure the source stream is buffered before starting the copy so we can use
  // ReadSegments, as nsStorageStream doesn't implement WriteSegments.
  nsCOMPtr<nsIInputStream> source = aUploadStream;
  if (!NS_InputStreamIsBuffered(aUploadStream)) {
    nsCOMPtr<nsIInputStream> bufferedSource;
    rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedSource),
                                   source.forget(), 4096);
    NS_ENSURE_SUCCESS(rv, rv);
    source = bufferedSource.forget();
  }

  // Perform an AsyncCopy into the input stream on the STS.
  nsCOMPtr<nsIEventTarget> target =
      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
  RefPtr<GenericPromise::Private> ready = new GenericPromise::Private(__func__);
  rv = NS_AsyncCopy(source, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096,
                    NormalizeCopyComplete, do_AddRef(ready).take());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    ready.get()->Release();
    return rv;
  }

  replacementStream.forget(aReplacementStream);
  ready.forget(aReadyPromise);
  return NS_OK;
}

}  // anonymous namespace

NS_IMETHODIMP
HttpBaseChannel::CloneUploadStream(int64_t* aContentLength,
                                   nsIInputStream** aClonedStream) {
  NS_ENSURE_ARG_POINTER(aContentLength);
  NS_ENSURE_ARG_POINTER(aClonedStream);
  *aClonedStream = nullptr;

  if (!XRE_IsParentProcess()) {
    NS_WARNING("CloneUploadStream is only supported in the parent process");
    return NS_ERROR_NOT_AVAILABLE;
  }

  if (!mUploadStream) {
    return NS_OK;
  }

  nsCOMPtr<nsIInputStream> clonedStream;
  nsresult rv =
      NS_CloneInputStream(mUploadStream, getter_AddRefs(clonedStream));
  NS_ENSURE_SUCCESS(rv, rv);

  clonedStream.forget(aClonedStream);

  *aContentLength = mReqContentLength;
  return NS_OK;
}

//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIUploadChannel2
//-----------------------------------------------------------------------------

NS_IMETHODIMP
HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream* aStream,
                                         const nsACString& aContentType,
                                         int64_t aContentLength,
                                         const nsACString& aMethod,
                                         bool aStreamHasHeaders) {
  // Ensure stream is set and method is valid
  NS_ENSURE_TRUE(aStream, NS_ERROR_FAILURE);

  {
    DebugOnly<nsCOMPtr<nsIMIMEInputStream>> mimeStream;
    MOZ_ASSERT(
        !aStreamHasHeaders || NS_FAILED(CallQueryInterface(
                                  aStream, getter_AddRefs(mimeStream.value))),
        "nsIMIMEInputStream should not include headers");
  }

  nsresult rv = SetRequestMethod(aMethod);
  NS_ENSURE_SUCCESS(rv, rv);

  if (!aStreamHasHeaders && !aContentType.IsVoid()) {
    if (aContentType.IsEmpty()) {
      SetEmptyRequestHeader("Content-Type"_ns);
    } else {
      SetRequestHeader("Content-Type"_ns, aContentType, false);
    }
  }

  StoreUploadStreamHasHeaders(aStreamHasHeaders);

  return InternalSetUploadStream(aStream, aContentLength, !aStreamHasHeaders);
}

nsresult HttpBaseChannel::InternalSetUploadStream(
    nsIInputStream* aUploadStream, int64_t aContentLength,
    bool aSetContentLengthHeader) {
  // If we're not on the main thread, such as for TRR, the content length must
  // be provided, as we can't normalize our upload stream.
  if (!NS_IsMainThread()) {
    if (aContentLength < 0) {
      MOZ_ASSERT_UNREACHABLE(
          "Upload content length must be explicit off-main-thread");
      return NS_ERROR_INVALID_ARG;
    }

    nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aUploadStream);
    if (!NS_InputStreamIsCloneable(aUploadStream) || !seekable) {
      MOZ_ASSERT_UNREACHABLE(
          "Upload stream must be cloneable & seekable off-main-thread");
      return NS_ERROR_INVALID_ARG;
    }

    mUploadStream = aUploadStream;
    ExplicitSetUploadStreamLength(aContentLength, aSetContentLengthHeader);
    return NS_OK;
  }

  // Normalize the upload stream we're provided to ensure that it is cloneable,
  // seekable, and synchronous when in the parent process.
  //
  // This might be an async operation, in which case ready will be returned and
  // resolved when the operation is complete.
  nsCOMPtr<nsIInputStream> replacement;
  RefPtr<GenericPromise> ready;
  if (XRE_IsParentProcess()) {
    nsresult rv = NormalizeUploadStream(
        aUploadStream, getter_AddRefs(replacement), getter_AddRefs(ready));
    NS_ENSURE_SUCCESS(rv, rv);
  }

  mUploadStream = replacement ? replacement.get() : aUploadStream;

  // Once the upload stream is ready, fetch its length before proceeding with
  // AsyncOpen.
  auto onReady = [self = RefPtr{this}, aContentLength, aSetContentLengthHeader,
                  stream = mUploadStream]() {
    auto setLengthAndResume = [self, aSetContentLengthHeader](int64_t aLength) {
      self->StorePendingUploadStreamNormalization(false);
      self->ExplicitSetUploadStreamLength(aLength >= 0 ? aLength : 0,
                                          aSetContentLengthHeader);
      self->MaybeResumeAsyncOpen();
    };

    if (aContentLength >= 0) {
      setLengthAndResume(aContentLength);
      return;
    }

    int64_t length;
    if (InputStreamLengthHelper::GetSyncLength(stream, &length)) {
      setLengthAndResume(length);
      return;
    }

    InputStreamLengthHelper::GetAsyncLength(stream, setLengthAndResume);
  };
  StorePendingUploadStreamNormalization(true);

  // Resolve onReady synchronously unless a promise is returned.
  if (ready) {
    ready->Then(GetCurrentSerialEventTarget(), __func__,
                [onReady = std::move(onReady)](
                    GenericPromise::ResolveOrRejectValue&&) { onReady(); });
  } else {
    onReady();
  }
  return NS_OK;
}

void HttpBaseChannel::ExplicitSetUploadStreamLength(
    uint64_t aContentLength, bool aSetContentLengthHeader) {
  // We already have the content length. We don't need to determinate it.
  mReqContentLength = aContentLength;

  if (!aSetContentLengthHeader) {
    return;
  }

  nsAutoCString header;
  header.AssignLiteral("Content-Length");

  // Maybe the content-length header has been already set.
  nsAutoCString value;
  nsresult rv = GetRequestHeader(header, value);
  if (NS_SUCCEEDED(rv) && !value.IsEmpty()) {
    return;
  }

  nsAutoCString contentLengthStr;
  contentLengthStr.AppendInt(aContentLength);
  SetRequestHeader(header, contentLengthStr, false);
}

NS_IMETHODIMP
HttpBaseChannel::GetUploadStreamHasHeaders(bool* hasHeaders) {
  NS_ENSURE_ARG(hasHeaders);

  *hasHeaders = LoadUploadStreamHasHeaders();
  return NS_OK;
}

bool HttpBaseChannel::MaybeWaitForUploadStreamNormalization(
    nsIStreamListener* aListener, nsISupports* aContext) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!LoadAsyncOpenWaitingForStreamNormalization(),
             "AsyncOpen() called twice?");

  if (!LoadPendingUploadStreamNormalization()) {
    return false;
  }

  mListener = aListener;
  StoreAsyncOpenWaitingForStreamNormalization(true);
  return true;
}

void HttpBaseChannel::MaybeResumeAsyncOpen() {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!LoadPendingUploadStreamNormalization());

  if (!LoadAsyncOpenWaitingForStreamNormalization()) {
    return;
  }

  nsCOMPtr<nsIStreamListener> listener;
  listener.swap(mListener);

  StoreAsyncOpenWaitingForStreamNormalization(false);

  nsresult rv = AsyncOpen(listener);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    DoAsyncAbort(rv);
  }
}

//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIEncodedChannel
//-----------------------------------------------------------------------------

NS_IMETHODIMP
HttpBaseChannel::GetApplyConversion(bool* value) {
  *value = LoadApplyConversion();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetApplyConversion(bool value) {
  LOG(("HttpBaseChannel::SetApplyConversion [this=%p value=%d]\n"this,
       value));
  StoreApplyConversion(value);
  return NS_OK;
}

nsresult HttpBaseChannel::DoApplyContentConversions(
    nsIStreamListener* aNextListener, nsIStreamListener** aNewNextListener) {
  return DoApplyContentConversions(aNextListener, aNewNextListener, nullptr);
}

// create a listener chain that looks like this
// http-channel -> decompressor (n times) -> InterceptFailedOnSTop ->
// channel-creator-listener
//
// we need to do this because not every decompressor has fully streamed output
// so may need a call to OnStopRequest to identify its completion state.. and if
// it creates an error there the channel status code needs to be updated before
// calling the terminal listener. Having the decompress do it via cancel() means
// channels cannot effectively be used in two contexts (specifically this one
// and a peek context for sniffing)
//
class InterceptFailedOnStop : public nsIThreadRetargetableStreamListener {
  virtual ~InterceptFailedOnStop() = default;
  nsCOMPtr<nsIStreamListener> mNext;
  HttpBaseChannel* mChannel;

 public:
  InterceptFailedOnStop(nsIStreamListener* arg, HttpBaseChannel* chan)
      : mNext(arg), mChannel(chan) {}
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER

  NS_IMETHOD OnStartRequest(nsIRequest* aRequest) override {
    return mNext->OnStartRequest(aRequest);
  }

  NS_IMETHOD OnStopRequest(nsIRequest* aRequest,
                           nsresult aStatusCode) override {
    if (NS_FAILED(aStatusCode) && NS_SUCCEEDED(mChannel->mStatus)) {
      LOG(("HttpBaseChannel::InterceptFailedOnStop %p seting status %" PRIx32,
           mChannel, static_cast<uint32_t>(aStatusCode)));
      mChannel->mStatus = aStatusCode;
    }
    return mNext->OnStopRequest(aRequest, aStatusCode);
  }

  NS_IMETHOD OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
                             uint64_t aOffset, uint32_t aCount) override {
    return mNext->OnDataAvailable(aRequest, aInputStream, aOffset, aCount);
  }
};

NS_IMPL_ADDREF(InterceptFailedOnStop)
NS_IMPL_RELEASE(InterceptFailedOnStop)

NS_INTERFACE_MAP_BEGIN(InterceptFailedOnStop)
  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
  NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
NS_INTERFACE_MAP_END

NS_IMETHODIMP
InterceptFailedOnStop::CheckListenerChain() {
  nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
      do_QueryInterface(mNext);
  if (!listener) {
    return NS_ERROR_NO_INTERFACE;
  }

  return listener->CheckListenerChain();
}

NS_IMETHODIMP
InterceptFailedOnStop::OnDataFinished(nsresult aStatus) {
  nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
      do_QueryInterface(mNext);
  if (listener) {
    return listener->OnDataFinished(aStatus);
  }

  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::DoApplyContentConversions(nsIStreamListener* aNextListener,
                                           nsIStreamListener** aNewNextListener,
                                           nsISupports* aCtxt) {
  *aNewNextListener = nullptr;
  if (!mResponseHead || !aNextListener) {
    return NS_OK;
  }

  LOG(("HttpBaseChannel::DoApplyContentConversions [this=%p]\n"this));

  if (!LoadApplyConversion()) {
    LOG(("not applying conversion per ApplyConversion\n"));
    return NS_OK;
  }

  if (LoadHasAppliedConversion()) {
    LOG(("not applying conversion because HasAppliedConversion is true\n"));
    return NS_OK;
  }

  if (LoadDeliveringAltData()) {
    MOZ_ASSERT(!mAvailableCachedAltDataType.IsEmpty());
    LOG(("not applying conversion because delivering alt-data\n"));
    return NS_OK;
  }

  nsAutoCString contentEncoding;
  nsresult rv =
      mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
  if (NS_FAILED(rv) || contentEncoding.IsEmpty()) return NS_OK;

  nsCOMPtr<nsIStreamListener> nextListener =
      new InterceptFailedOnStop(aNextListener, this);

  // The encodings are listed in the order they were applied
  // (see rfc 2616 section 14.11), so they need to removed in reverse
  // order. This is accomplished because the converter chain ends up
  // being a stack with the last converter created being the first one
  // to accept the raw network data.

  char* cePtr = contentEncoding.BeginWriting();
  uint32_t count = 0;
  while (char* val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr)) {
    if (++count > 16) {
      // That's ridiculous. We only understand 2 different ones :)
      // but for compatibility with old code, we will just carry on without
      // removing the encodings
      LOG(("Too many Content-Encodings. Ignoring remainder.\n"));
      break;
    }

    if (gHttpHandler->IsAcceptableEncoding(val,
                                           isSecureOrTrustworthyURL(mURI))) {
      RefPtr<nsHTTPCompressConv> converter = new nsHTTPCompressConv();
      nsAutoCString from(val);
      ToLowerCase(from);
      rv = converter->AsyncConvertData(from.get(), "uncompressed", nextListener,
                                       aCtxt);
      if (NS_FAILED(rv)) {
        LOG(("Unexpected failure of AsyncConvertData %s\n", val));
        return rv;
      }

      LOG(("converter removed '%s' content-encoding\n", val));
      if (Telemetry::CanRecordPrereleaseData()) {
        int mode = 0;
        if (from.EqualsLiteral("gzip") || from.EqualsLiteral("x-gzip")) {
          mode = 1;
        } else if (from.EqualsLiteral("deflate") ||
                   from.EqualsLiteral("x-deflate")) {
          mode = 2;
        } else if (from.EqualsLiteral("br")) {
          mode = 3;
        } else if (from.EqualsLiteral("zstd")) {
          mode = 4;
        }
        Telemetry::Accumulate(Telemetry::HTTP_CONTENT_ENCODING, mode);
      }
      nextListener = converter;
    } else {
      if (val) LOG(("Unknown content encoding '%s', ignoring\n", val));
    }
  }
  *aNewNextListener = do_AddRef(nextListener).take();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetContentEncodings(nsIUTF8StringEnumerator** aEncodings) {
  if (!mResponseHead) {
    *aEncodings = nullptr;
    return NS_OK;
  }

  nsAutoCString encoding;
  Unused << mResponseHead->GetHeader(nsHttp::Content_Encoding, encoding);
  if (encoding.IsEmpty()) {
    *aEncodings = nullptr;
    return NS_OK;
  }
  RefPtr<nsContentEncodings> enumerator =
      new nsContentEncodings(this, encoding.get());
  enumerator.forget(aEncodings);
  return NS_OK;
}

//-----------------------------------------------------------------------------
// HttpBaseChannel::nsContentEncodings <public>
//-----------------------------------------------------------------------------

HttpBaseChannel::nsContentEncodings::nsContentEncodings(
    nsIHttpChannel* aChannel, const char* aEncodingHeader)
    : mEncodingHeader(aEncodingHeader), mChannel(aChannel), mReady(false) {
  mCurEnd = aEncodingHeader + strlen(aEncodingHeader);
  mCurStart = mCurEnd;
}

//-----------------------------------------------------------------------------
// HttpBaseChannel::nsContentEncodings::nsISimpleEnumerator
//-----------------------------------------------------------------------------

NS_IMETHODIMP
HttpBaseChannel::nsContentEncodings::HasMore(bool* aMoreEncodings) {
  if (mReady) {
    *aMoreEncodings = true;
    return NS_OK;
  }

  nsresult rv = PrepareForNext();
  *aMoreEncodings = NS_SUCCEEDED(rv);
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::nsContentEncodings::GetNext(nsACString& aNextEncoding) {
  aNextEncoding.Truncate();
  if (!mReady) {
    nsresult rv = PrepareForNext();
    if (NS_FAILED(rv)) {
      return NS_ERROR_FAILURE;
    }
  }

  const nsACString& encoding = Substring(mCurStart, mCurEnd);

  nsACString::const_iterator start, end;
  encoding.BeginReading(start);
  encoding.EndReading(end);

  bool haveType = false;
  if (CaseInsensitiveFindInReadable("gzip"_ns, start, end)) {
    aNextEncoding.AssignLiteral(APPLICATION_GZIP);
    haveType = true;
  }

  if (!haveType) {
    encoding.BeginReading(start);
    if (CaseInsensitiveFindInReadable("compress"_ns, start, end)) {
      aNextEncoding.AssignLiteral(APPLICATION_COMPRESS);
      haveType = true;
    }
  }

  if (!haveType) {
    encoding.BeginReading(start);
    if (CaseInsensitiveFindInReadable("deflate"_ns, start, end)) {
      aNextEncoding.AssignLiteral(APPLICATION_ZIP);
      haveType = true;
    }
  }

  if (!haveType) {
    encoding.BeginReading(start);
    if (CaseInsensitiveFindInReadable("br"_ns, start, end)) {
      aNextEncoding.AssignLiteral(APPLICATION_BROTLI);
      haveType = true;
    }
  }

  if (!haveType) {
    encoding.BeginReading(start);
    if (CaseInsensitiveFindInReadable("zstd"_ns, start, end)) {
      aNextEncoding.AssignLiteral(APPLICATION_ZSTD);
      haveType = true;
    }
  }

  // Prepare to fetch the next encoding
  mCurEnd = mCurStart;
  mReady = false;

  if (haveType) return NS_OK;

  NS_WARNING("Unknown encoding type");
  return NS_ERROR_FAILURE;
}

//-----------------------------------------------------------------------------
// HttpBaseChannel::nsContentEncodings::nsISupports
//-----------------------------------------------------------------------------

NS_IMPL_ISUPPORTS(HttpBaseChannel::nsContentEncodings, nsIUTF8StringEnumerator,
                  nsIStringEnumerator)

//-----------------------------------------------------------------------------
// HttpBaseChannel::nsContentEncodings <private>
//-----------------------------------------------------------------------------

nsresult HttpBaseChannel::nsContentEncodings::PrepareForNext(void) {
  MOZ_ASSERT(mCurStart == mCurEnd, "Indeterminate state");

  // At this point both mCurStart and mCurEnd point to somewhere
  // past the end of the next thing we want to return

  while (mCurEnd != mEncodingHeader) {
    --mCurEnd;
    if (*mCurEnd != ',' && !nsCRT::IsAsciiSpace(*mCurEnd)) break;
  }
  if (mCurEnd == mEncodingHeader) {
    return NS_ERROR_NOT_AVAILABLE;  // no more encodings
  }
  ++mCurEnd;

  // At this point mCurEnd points to the first char _after_ the
  // header we want.  Furthermore, mCurEnd - 1 != mEncodingHeader

  mCurStart = mCurEnd - 1;
  while (mCurStart != mEncodingHeader && *mCurStart != ',' &&
         !nsCRT::IsAsciiSpace(*mCurStart)) {
    --mCurStart;
  }
  if (*mCurStart == ',' || nsCRT::IsAsciiSpace(*mCurStart)) {
    ++mCurStart;  // we stopped because of a weird char, so move up one
  }

  // At this point mCurStart and mCurEnd bracket the encoding string
  // we want.  Check that it's not "identity"
  if (Substring(mCurStart, mCurEnd)
          .Equals("identity", nsCaseInsensitiveCStringComparator)) {
    mCurEnd = mCurStart;
    return PrepareForNext();
  }

  mReady = true;
  return NS_OK;
}

//-----------------------------------------------------------------------------
// HttpBaseChannel::nsIHttpChannel
//-----------------------------------------------------------------------------

NS_IMETHODIMP
HttpBaseChannel::GetChannelId(uint64_t* aChannelId) {
  NS_ENSURE_ARG_POINTER(aChannelId);
  *aChannelId = mChannelId;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetChannelId(uint64_t aChannelId) {
  mChannelId = aChannelId;
  return NS_OK;
}

NS_IMETHODIMP HttpBaseChannel::GetTopLevelContentWindowId(uint64_t* aWindowId) {
  if (!mContentWindowId) {
    nsCOMPtr<nsILoadContext> loadContext;
    GetCallback(loadContext);
    if (loadContext) {
      nsCOMPtr<mozIDOMWindowProxy> topWindow;
      loadContext->GetTopWindow(getter_AddRefs(topWindow));
      if (topWindow) {
        if (nsPIDOMWindowInner* inner =
                nsPIDOMWindowOuter::From(topWindow)->GetCurrentInnerWindow()) {
          mContentWindowId = inner->WindowID();
        }
      }
    }
  }
  *aWindowId = mContentWindowId;
  return NS_OK;
}

NS_IMETHODIMP HttpBaseChannel::SetBrowserId(uint64_t aId) {
  mBrowserId = aId;
  return NS_OK;
}

NS_IMETHODIMP HttpBaseChannel::GetBrowserId(uint64_t* aId) {
  EnsureBrowserId();
  *aId = mBrowserId;
  return NS_OK;
}

NS_IMETHODIMP HttpBaseChannel::SetTopLevelContentWindowId(uint64_t aWindowId) {
  mContentWindowId = aWindowId;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::IsThirdPartyTrackingResource(bool* aIsTrackingResource) {
  MOZ_ASSERT(
      !(mFirstPartyClassificationFlags && mThirdPartyClassificationFlags));
  *aIsTrackingResource = UrlClassifierCommon::IsTrackingClassificationFlag(
      mThirdPartyClassificationFlags,
      mLoadInfo->GetOriginAttributes().IsPrivateBrowsing());
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::IsThirdPartySocialTrackingResource(
    bool* aIsThirdPartySocialTrackingResource) {
  MOZ_ASSERT(!mFirstPartyClassificationFlags ||
             !mThirdPartyClassificationFlags);
  *aIsThirdPartySocialTrackingResource =
      UrlClassifierCommon::IsSocialTrackingClassificationFlag(
          mThirdPartyClassificationFlags);
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetClassificationFlags(uint32_t* aFlags) {
  if (mThirdPartyClassificationFlags) {
    *aFlags = mThirdPartyClassificationFlags;
  } else {
    *aFlags = mFirstPartyClassificationFlags;
  }
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetFirstPartyClassificationFlags(uint32_t* aFlags) {
  *aFlags = mFirstPartyClassificationFlags;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetThirdPartyClassificationFlags(uint32_t* aFlags) {
  *aFlags = mThirdPartyClassificationFlags;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetTransferSize(uint64_t* aTransferSize) {
  MutexAutoLock lock(mOnDataFinishedMutex);
  *aTransferSize = mTransferSize;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetRequestSize(uint64_t* aRequestSize) {
  *aRequestSize = mRequestSize;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetDecodedBodySize(uint64_t* aDecodedBodySize) {
  *aDecodedBodySize = mDecodedBodySize;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) {
  MutexAutoLock lock(mOnDataFinishedMutex);
  *aEncodedBodySize = mEncodedBodySize;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetSupportsHTTP3(bool* aSupportsHTTP3) {
  *aSupportsHTTP3 = mSupportsHTTP3;
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetHasHTTPSRR(bool* aHasHTTPSRR) {
  *aHasHTTPSRR = LoadHasHTTPSRR();
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetRequestMethod(nsACString& aMethod) {
  mRequestHead.Method(aMethod);
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::SetRequestMethod(const nsACString& aMethod) {
  ENSURE_CALLED_BEFORE_CONNECT();

  mLoadInfo->SetIsGETRequest(aMethod.Equals("GET"));

  const nsCString& flatMethod = PromiseFlatCString(aMethod);

  // Method names are restricted to valid HTTP tokens.
  if (!nsHttp::IsValidToken(flatMethod)) return NS_ERROR_INVALID_ARG;

  mRequestHead.SetMethod(flatMethod);
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) {
  NS_ENSURE_ARG_POINTER(aReferrerInfo);
  *aReferrerInfo = do_AddRef(mReferrerInfo).take();
  return NS_OK;
}

nsresult HttpBaseChannel::SetReferrerInfoInternal(
    nsIReferrerInfo* aReferrerInfo, bool aClone, bool aCompute,
    bool aRespectBeforeConnect) {
  LOG(
      ("HttpBaseChannel::SetReferrerInfoInternal [this=%p aClone(%d) "
       "aCompute(%d)]\n",
       this, aClone, aCompute));
  if (aRespectBeforeConnect) {
    ENSURE_CALLED_BEFORE_CONNECT();
  }

  mReferrerInfo = aReferrerInfo;

  // clear existing referrer, if any
  nsresult rv = ClearReferrerHeader();
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  if (!mReferrerInfo) {
    return NS_OK;
  }

  if (aClone) {
    mReferrerInfo = static_cast<dom::ReferrerInfo*>(aReferrerInfo)->Clone();
  }

  dom::ReferrerInfo* referrerInfo =
      static_cast<dom::ReferrerInfo*>(mReferrerInfo.get());

  // Don't set referrerInfo if it has not been initialized.
  if (!referrerInfo->IsInitialized()) {
    mReferrerInfo = nullptr;
    return NS_ERROR_NOT_INITIALIZED;
  }

  if (aClone) {
    // Record the telemetry once we set the referrer info to the channel
    // successfully.
    referrerInfo->RecordTelemetry(this);
  }

  if (aCompute) {
    rv = referrerInfo->ComputeReferrer(this);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
  }

  nsCOMPtr<nsIURI> computedReferrer = mReferrerInfo->GetComputedReferrer();
  if (!computedReferrer) {
    return NS_OK;
  }

  nsAutoCString spec;
  rv = computedReferrer->GetSpec(spec);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  return SetReferrerHeader(spec, aRespectBeforeConnect);
}

NS_IMETHODIMP
HttpBaseChannel::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
  return SetReferrerInfoInternal(aReferrerInfo, truetruetrue);
}

NS_IMETHODIMP
HttpBaseChannel::SetReferrerInfoWithoutClone(nsIReferrerInfo* aReferrerInfo) {
  return SetReferrerInfoInternal(aReferrerInfo, falsetruetrue);
}

// Return the channel's proxy URI, or if it doesn't exist, the
// channel's main URI.
NS_IMETHODIMP
HttpBaseChannel::GetProxyURI(nsIURI** aOut) {
  NS_ENSURE_ARG_POINTER(aOut);
  nsCOMPtr<nsIURI> result(mProxyURI);
  result.forget(aOut);
  return NS_OK;
}

NS_IMETHODIMP
HttpBaseChannel::GetRequestHeader(const nsACString& aHeader,
                                  nsACString& aValue) {
  aValue.Truncate();

  // XXX might be better to search the header list directly instead of
  // hitting the http atom hash table.
  nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
  if (!atom) return NS_ERROR_NOT_AVAILABLE;

  return mRequestHead.GetHeader(atom, aValue);
}

NS_IMETHODIMP
HttpBaseChannel::SetRequestHeader(const nsACString& aHeader,
                                  const nsACString& aValue, bool aMerge) {
  return SetRequestHeaderInternal(aHeader, aValue, aMerge,
                                  nsHttpHeaderArray::eVarietyRequestOverride);
}

nsresult HttpBaseChannel::SetRequestHeaderInternal(
    const nsACString& aHeader, const nsACString& aValue, bool aMerge,
    nsHttpHeaderArray::HeaderVariety aVariety) {
  const nsCString& flatHeader = PromiseFlatCString(aHeader);
  const nsCString& flatValue = PromiseFlatCString(aValue);

  LOG(
      ("HttpBaseChannel::SetRequestHeader [this=%p header=\"%s\" value=\"%s\" "
       "merge=%u]\n",
       this, flatHeader.get(), flatValue.get(), aMerge));

  // Verify header names are valid HTTP tokens and header values are reasonably
  // close to whats allowed in RFC 2616.
  if (!nsHttp::IsValidToken(flatHeader) ||
      !nsHttp::IsReasonableHeaderValue(flatValue)) {
    return NS_ERROR_INVALID_ARG;
  }

  // Mark that the User-Agent header has been modified.
  if (nsHttp::ResolveAtom(aHeader) == nsHttp::User_Agent) {
    StoreIsUserAgentHeaderModified(true);
  }

  return mRequestHead.SetHeader(aHeader, flatValue, aMerge);
}

NS_IMETHODIMP
HttpBaseChannel::SetNewReferrerInfo(const nsACString& aUrl,
                                    nsIReferrerInfo::ReferrerPolicyIDL aPolicy,
                                    bool aSendReferrer) {
  nsresult rv;
  // Create URI from string
  nsCOMPtr<nsIURI> aURI;
  rv = NS_NewURI(getter_AddRefs(aURI), aUrl);
  NS_ENSURE_SUCCESS(rv, rv);
  // Create new ReferrerInfo and initialize it.
  nsCOMPtr<nsIReferrerInfo> referrerInfo = new mozilla::dom::ReferrerInfo();
  rv = referrerInfo->Init(aPolicy, aSendReferrer, aURI);
  NS_ENSURE_SUCCESS(rv, rv);
  // Set ReferrerInfo
  return SetReferrerInfo(referrerInfo);
}

NS_IMETHODIMP
HttpBaseChannel::SetEmptyRequestHeader(const nsACString& aHeader) {
  const nsCString& flatHeader = PromiseFlatCString(aHeader);

  LOG(("HttpBaseChannel::SetEmptyRequestHeader [this=%p header=\"%s\"]\n"this,
       flatHeader.get()));

  // Verify header names are valid HTTP tokens and header values are reasonably
  // close to whats allowed in RFC 2616.
  if (!nsHttp::IsValidToken(flatHeader)) {
    return NS_ERROR_INVALID_ARG;
  }

  // Mark that the User-Agent header has been modified.
  if (nsHttp::ResolveAtom(aHeader) == nsHttp::User_Agent) {
    StoreIsUserAgentHeaderModified(true);
  }

  return mRequestHead.SetEmptyHeader(aHeader);
}

NS_IMETHODIMP
HttpBaseChannel::VisitRequestHeaders(nsIHttpHeaderVisitor* visitor) {
  return mRequestHead.VisitHeaders(visitor);
}

NS_IMETHODIMP
HttpBaseChannel::VisitNonDefaultRequestHeaders(nsIHttpHeaderVisitor* visitor) {
  return mRequestHead.VisitHeaders(visitor,
                                   nsHttpHeaderArray::eFilterSkipDefault);
}

NS_IMETHODIMP
HttpBaseChannel::GetResponseHeader(const nsACString& header,
                                   nsACString& value) {
  value.Truncate();

  if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE;

  nsHttpAtom atom = nsHttp::ResolveAtom(header);
  if (!atom) return NS_ERROR_NOT_AVAILABLE;

  return mResponseHead->GetHeader(atom, value);
}

NS_IMETHODIMP
HttpBaseChannel::SetResponseHeader(const nsACString& header,
                                   const nsACString& value, bool merge) {
  LOG(
      ("HttpBaseChannel::SetResponseHeader [this=%p header=\"%s\" value=\"%s\" "
       "merge=%u]\n",
--> --------------------

--> maximum size reached

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

98%


¤ Dauer der Verarbeitung: 0.26 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