summaryrefslogtreecommitdiffstats
path: root/netwerk/protocol/http/nsHttpHandler.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /netwerk/protocol/http/nsHttpHandler.cpp
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--netwerk/protocol/http/nsHttpHandler.cpp3053
1 files changed, 3053 insertions, 0 deletions
diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp
new file mode 100644
index 0000000000..3e86877ed0
--- /dev/null
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -0,0 +1,3053 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=4 sw=2 sts=2 et 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 "prsystem.h"
+
+#include "AltServiceChild.h"
+#include "nsError.h"
+#include "nsHttp.h"
+#include "nsHttpHandler.h"
+#include "nsHttpChannel.h"
+#include "nsHttpAuthCache.h"
+#include "nsStandardURL.h"
+#include "LoadContextInfo.h"
+#include "nsCategoryManagerUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsSocketProviderService.h"
+#include "nsISocketProvider.h"
+#include "nsPrintfCString.h"
+#include "nsCOMPtr.h"
+#include "nsNetCID.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Printf.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/StaticPrefs_privacy.h"
+#include "mozilla/StoragePrincipalHelper.h"
+#include "nsAsyncRedirectVerifyHelper.h"
+#include "nsSocketTransportService2.h"
+#include "nsAlgorithm.h"
+#include "ASpdySession.h"
+#include "EventTokenBucket.h"
+#include "Tickler.h"
+#include "nsIXULAppInfo.h"
+#include "nsICookieService.h"
+#include "nsIObserverService.h"
+#include "nsISiteSecurityService.h"
+#include "nsIStreamConverterService.h"
+#include "nsCRT.h"
+#include "nsIParentalControlsService.h"
+#include "nsPIDOMWindow.h"
+#include "nsHttpChannelAuthProvider.h"
+#include "nsINetworkLinkService.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsSocketTransportService2.h"
+#include "nsIOService.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIXULRuntime.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsRFPService.h"
+#include "mozilla/net/rust_helper.h"
+
+#include "mozilla/net/HttpConnectionMgrParent.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/NeckoParent.h"
+#include "mozilla/net/RequestContextService.h"
+#include "mozilla/net/SocketProcessParent.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "mozilla/AntiTrackingRedirectHeuristic.h"
+#include "mozilla/DynamicFpiRedirectHeuristic.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/LazyIdleThread.h"
+#include "mozilla/StaticPrefs_image.h"
+#include "mozilla/SyncRunnable.h"
+
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Navigator.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/network/Connection.h"
+
+#include "nsNSSComponent.h"
+#include "TRRServiceChannel.h"
+
+#if defined(XP_UNIX)
+# include <sys/utsname.h>
+#endif
+
+#if defined(XP_WIN)
+# include <windows.h>
+# include "mozilla/WindowsVersion.h"
+#endif
+
+#if defined(XP_MACOSX)
+# include <CoreServices/CoreServices.h>
+# include "nsCocoaFeatures.h"
+#endif
+
+#ifdef MOZ_TASK_TRACER
+# include "GeckoTaskTracer.h"
+#endif
+
+//-----------------------------------------------------------------------------
+#include "mozilla/net/HttpChannelChild.h"
+
+#define UA_PREF_PREFIX "general.useragent."
+#ifdef XP_WIN
+# define UA_SPARE_PLATFORM
+#endif
+
+#define HTTP_PREF_PREFIX "network.http."
+#define INTL_ACCEPT_LANGUAGES "intl.accept_languages"
+#define BROWSER_PREF_PREFIX "browser.cache."
+#define H2MANDATORY_SUITE "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256"
+#define SAFE_HINT_HEADER_VALUE "safeHint.enabled"
+#define SECURITY_PREFIX "security."
+#define DOM_SECURITY_PREFIX "dom.security"
+#define TCP_FAST_OPEN_ENABLE "network.tcp.tcp_fastopen_enable"
+#define TCP_FAST_OPEN_FAILURE_LIMIT \
+ "network.tcp.tcp_fastopen_consecutive_failure_limit"
+#define TCP_FAST_OPEN_STALLS_LIMIT "network.tcp.tcp_fastopen_http_stalls_limit"
+#define TCP_FAST_OPEN_STALLS_IDLE \
+ "network.tcp.tcp_fastopen_http_check_for_stalls_only_if_idle_for"
+#define TCP_FAST_OPEN_STALLS_TIMEOUT \
+ "network.tcp.tcp_fastopen_http_stalls_timeout"
+
+#define ACCEPT_HEADER_STYLE "text/css,*/*;q=0.1"
+#define ACCEPT_HEADER_ALL "*/*"
+
+#define UA_PREF(_pref) UA_PREF_PREFIX _pref
+#define HTTP_PREF(_pref) HTTP_PREF_PREFIX _pref
+#define BROWSER_PREF(_pref) BROWSER_PREF_PREFIX _pref
+
+#define NS_HTTP_PROTOCOL_FLAGS \
+ (URI_STD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP | URI_LOADABLE_BY_ANYONE)
+
+//-----------------------------------------------------------------------------
+
+using mozilla::dom::Promise;
+
+namespace mozilla::net {
+
+LazyLogModule gHttpLog("nsHttp");
+
+#ifdef ANDROID
+static nsCString GetDeviceModelId() {
+ // Assumed to be running on the main thread
+ // We need the device property in either case
+ nsAutoCString deviceModelId;
+ nsCOMPtr<nsIPropertyBag2> infoService =
+ do_GetService("@mozilla.org/system-info;1");
+ MOZ_ASSERT(infoService, "Could not find a system info service");
+ nsAutoString androidDevice;
+ nsresult rv = infoService->GetPropertyAsAString(u"device"_ns, androidDevice);
+ if (NS_SUCCEEDED(rv)) {
+ deviceModelId = NS_LossyConvertUTF16toASCII(androidDevice);
+ }
+ nsAutoCString deviceString;
+ rv = Preferences::GetCString(UA_PREF("device_string"), deviceString);
+ if (NS_SUCCEEDED(rv)) {
+ deviceString.Trim(" ", true, true);
+ deviceString.ReplaceSubstring("%DEVICEID%"_ns, deviceModelId);
+ return std::move(deviceString);
+ }
+ return std::move(deviceModelId);
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// nsHttpHandler <public>
+//-----------------------------------------------------------------------------
+
+StaticRefPtr<nsHttpHandler> gHttpHandler;
+
+/* static */
+already_AddRefed<nsHttpHandler> nsHttpHandler::GetInstance() {
+ if (!gHttpHandler) {
+ gHttpHandler = new nsHttpHandler();
+ DebugOnly<nsresult> rv = gHttpHandler->Init();
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ // There is code that may be executed during the final cycle collection
+ // shutdown and still referencing gHttpHandler.
+ ClearOnShutdown(&gHttpHandler,
+ ShutdownPhase::ShutdownPostLastCycleCollection);
+ }
+ RefPtr<nsHttpHandler> httpHandler = gHttpHandler;
+ return httpHandler.forget();
+}
+
+/// Derive the HTTP Accept header for image requests based on the enabled prefs
+/// for non-universal image types. This may be overridden in its entirety by
+/// the image.http.accept pref.
+static nsCString ImageAcceptHeader() {
+ nsCString mimeTypes;
+
+ if (mozilla::StaticPrefs::image_avif_enabled()) {
+ mimeTypes.Append("image/avif,");
+ }
+
+ if (mozilla::StaticPrefs::image_webp_enabled()) {
+ mimeTypes.Append("image/webp,");
+ }
+
+ mimeTypes.Append("*/*");
+
+ return mimeTypes;
+}
+
+static nsCString DocumentAcceptHeader(const nsCString& aImageAcceptHeader) {
+ nsPrintfCString mimeTypes(
+ "text/html,application/xhtml+xml,application/xml;q=0.9,%s;q=0.8",
+ aImageAcceptHeader.get());
+
+ return std::move(mimeTypes);
+}
+
+nsHttpHandler::nsHttpHandler()
+ : mHttpVersion(HttpVersion::v1_1),
+ mProxyHttpVersion(HttpVersion::v1_1),
+ mCapabilities(NS_HTTP_ALLOW_KEEPALIVE),
+ mFastFallbackToIPv4(false),
+ mIdleTimeout(PR_SecondsToInterval(10)),
+ mSpdyTimeout(PR_SecondsToInterval(180)),
+ mResponseTimeout(PR_SecondsToInterval(300)),
+ mResponseTimeoutEnabled(false),
+ mNetworkChangedTimeout(5000),
+ mMaxRequestAttempts(6),
+ mMaxRequestDelay(10),
+ mIdleSynTimeout(250),
+ mFallbackSynTimeout(5),
+ mH2MandatorySuiteEnabled(false),
+ mMaxUrgentExcessiveConns(3),
+ mMaxConnections(24),
+ mMaxPersistentConnectionsPerServer(2),
+ mMaxPersistentConnectionsPerProxy(4),
+ mThrottleEnabled(true),
+ mThrottleVersion(2),
+ mThrottleSuspendFor(3000),
+ mThrottleResumeFor(200),
+ mThrottleReadLimit(8000),
+ mThrottleReadInterval(500),
+ mThrottleHoldTime(600),
+ mThrottleMaxTime(3000),
+ mSendWindowSize(1024),
+ mUrgentStartEnabled(true),
+ mTailBlockingEnabled(true),
+ mTailDelayQuantum(600),
+ mTailDelayQuantumAfterDCL(100),
+ mTailDelayMax(6000),
+ mTailTotalMax(0),
+ mRedirectionLimit(10),
+ mBeConservativeForProxy(true),
+ mPhishyUserPassLength(1),
+ mQoSBits(0x00),
+ mEnforceAssocReq(false),
+ mImageAcceptHeader(ImageAcceptHeader()),
+ mDocumentAcceptHeader(DocumentAcceptHeader(ImageAcceptHeader())),
+ mLastUniqueID(NowInSeconds()),
+ mSessionStartTime(0),
+ mLegacyAppName("Mozilla"),
+ mLegacyAppVersion("5.0"),
+ mProduct("Gecko"),
+ mCompatFirefoxEnabled(false),
+ mUserAgentIsDirty(true),
+ mAcceptLanguagesIsDirty(true),
+ mPromptTempRedirect(true),
+ mEnablePersistentHttpsCaching(false),
+ mSafeHintEnabled(false),
+ mParentalControlEnabled(false),
+ mHandlerActive(false),
+ mDebugObservations(false),
+ mEnableSpdy(false),
+ mHttp2Enabled(true),
+ mUseH2Deps(true),
+ mEnforceHttp2TlsProfile(true),
+ mCoalesceSpdy(true),
+ mSpdyPersistentSettings(false),
+ mAllowPush(true),
+ mEnableAltSvc(false),
+ mEnableAltSvcOE(false),
+ mEnableOriginExtension(false),
+ mEnableH2Websockets(true),
+ mDumpHpackTables(false),
+ mSpdySendingChunkSize(ASpdySession::kSendingChunkSize),
+ mSpdySendBufferSize(ASpdySession::kTCPSendBufferSize),
+ mSpdyPushAllowance(131072) // match default pref
+ ,
+ mSpdyPullAllowance(ASpdySession::kInitialRwin),
+ mDefaultSpdyConcurrent(ASpdySession::kDefaultMaxConcurrent),
+ mSpdyPingThreshold(PR_SecondsToInterval(58)),
+ mSpdyPingTimeout(PR_SecondsToInterval(8)),
+ mConnectTimeout(90000),
+ mTLSHandshakeTimeout(30000),
+ mParallelSpeculativeConnectLimit(6),
+ mRequestTokenBucketEnabled(true),
+ mRequestTokenBucketMinParallelism(6),
+ mRequestTokenBucketHz(100),
+ mRequestTokenBucketBurst(32),
+ mCriticalRequestPrioritization(true),
+ mTCPKeepaliveShortLivedEnabled(false),
+ mTCPKeepaliveShortLivedTimeS(60),
+ mTCPKeepaliveShortLivedIdleTimeS(10),
+ mTCPKeepaliveLongLivedEnabled(false),
+ mTCPKeepaliveLongLivedIdleTimeS(600),
+ mEnforceH1Framing(FRAMECHECK_BARELY),
+ mDefaultHpackBuffer(4096),
+ mBug1563538(true),
+ mHttp3Enabled(true),
+ mQpackTableSize(4096),
+ mHttp3MaxBlockedStreams(10),
+ mMaxHttpResponseHeaderSize(393216),
+ mFocusedWindowTransactionRatio(0.9f),
+ mSpeculativeConnectEnabled(false),
+ mUseFastOpen(true),
+ mFastOpenConsecutiveFailureLimit(5),
+ mFastOpenConsecutiveFailureCounter(0),
+ mFastOpenStallsLimit(3),
+ mFastOpenStallsCounter(0),
+ mFastOpenStallsIdleTime(10),
+ mFastOpenStallsTimeout(20),
+ mActiveTabPriority(true),
+ mProcessId(0),
+ mNextChannelId(1),
+ mLastActiveTabLoadOptimizationLock(
+ "nsHttpConnectionMgr::LastActiveTabLoadOptimization"),
+ mHttpExclusionLock("nsHttpHandler::HttpExclusion"),
+ mThroughCaptivePortal(false) {
+ LOG(("Creating nsHttpHandler [this=%p].\n", this));
+
+ mUserAgentOverride.SetIsVoid(true);
+
+ MOZ_ASSERT(!gHttpHandler, "HTTP handler already created!");
+
+ nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
+ if (runtime) {
+ runtime->GetProcessID(&mProcessId);
+ }
+
+ mFastOpenSupported = false;
+ if (XRE_IsParentProcess()) {
+ SetFastOpenOSSupport();
+ }
+}
+
+void nsHttpHandler::SetFastOpenOSSupport() {
+#if !defined(XP_WIN) && !defined(XP_LINUX) && !defined(ANDROID) && \
+ !defined(HAS_CONNECTX)
+ return;
+#elif defined(XP_WIN)
+ mFastOpenSupported = IsWindows10BuildOrLater(16299);
+
+ if (mFastOpenSupported) {
+ // We have some problems with lavasoft software and tcp fast open.
+ if (GetModuleHandleW(L"pmls64.dll") || GetModuleHandleW(L"rlls64.dll")) {
+ mFastOpenSupported = false;
+ }
+ }
+#else
+
+ nsAutoCString version;
+ nsresult rv;
+# ifdef ANDROID
+ nsCOMPtr<nsIPropertyBag2> infoService =
+ do_GetService("@mozilla.org/system-info;1");
+ MOZ_ASSERT(infoService, "Could not find a system info service");
+ rv = infoService->GetPropertyAsACString(u"sdk_version"_ns, version);
+# else
+ char buf[SYS_INFO_BUFFER_LENGTH];
+ if (PR_GetSystemInfo(PR_SI_RELEASE, buf, sizeof(buf)) == PR_SUCCESS) {
+ version = buf;
+ rv = NS_OK;
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+# endif
+
+ LOG(("nsHttpHandler::SetFastOpenOSSupport version %s", version.get()));
+
+ if (NS_SUCCEEDED(rv)) {
+ // set min version minus 1.
+# if XP_MACOSX
+ int min_version[] = {17, 5}; // High Sierra 10.13.4
+# elif ANDROID
+ int min_version[] = {4, 4};
+# elif XP_LINUX
+ int min_version[] = {3, 6};
+# endif
+ int inx = 0;
+ nsCCharSeparatedTokenizer tokenizer(version, '.');
+ while ((inx < 2) && tokenizer.hasMoreTokens()) {
+ nsAutoCString token(tokenizer.nextToken());
+ const char* nondigit = NS_strspnp("0123456789", token.get());
+ if (nondigit && *nondigit) {
+ break;
+ }
+ nsresult rv;
+ int32_t ver = token.ToInteger(&rv);
+ if (NS_FAILED(rv)) {
+ break;
+ }
+ if (ver > min_version[inx]) {
+ mFastOpenSupported = true;
+ break;
+ } else if (ver == min_version[inx] && inx == 1) {
+ mFastOpenSupported = true;
+ } else if (ver < min_version[inx]) {
+ break;
+ }
+ inx++;
+ }
+ }
+#endif
+ LOG(("nsHttpHandler::SetFastOpenOSSupport %s supported.\n",
+ mFastOpenSupported ? "" : "not"));
+}
+
+nsHttpHandler::~nsHttpHandler() {
+ LOG(("Deleting nsHttpHandler [this=%p]\n", this));
+
+ // make sure the connection manager is shutdown
+ if (mConnMgr) {
+ nsresult rv = mConnMgr->Shutdown();
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("nsHttpHandler [this=%p] "
+ "failed to shutdown connection manager (%08x)\n",
+ this, static_cast<uint32_t>(rv)));
+ }
+ mConnMgr = nullptr;
+ }
+
+ // Note: don't call NeckoChild::DestroyNeckoChild() here, as it's too late
+ // and it'll segfault. NeckoChild will get cleaned up by process exit.
+
+ nsHttp::DestroyAtomTable();
+}
+
+static const char* gCallbackPrefs[] = {
+ HTTP_PREF_PREFIX,
+ UA_PREF_PREFIX,
+ INTL_ACCEPT_LANGUAGES,
+ BROWSER_PREF("disk_cache_ssl"),
+ H2MANDATORY_SUITE,
+ HTTP_PREF("tcp_keepalive.short_lived_connections"),
+ HTTP_PREF("tcp_keepalive.long_lived_connections"),
+ SAFE_HINT_HEADER_VALUE,
+ SECURITY_PREFIX,
+ DOM_SECURITY_PREFIX,
+ TCP_FAST_OPEN_ENABLE,
+ TCP_FAST_OPEN_FAILURE_LIMIT,
+ TCP_FAST_OPEN_STALLS_LIMIT,
+ TCP_FAST_OPEN_STALLS_IDLE,
+ TCP_FAST_OPEN_STALLS_TIMEOUT,
+ "image.http.accept",
+ "image.avif.enabled",
+ "image.webp.enabled",
+ nullptr,
+};
+
+nsresult nsHttpHandler::Init() {
+ nsresult rv;
+
+ LOG(("nsHttpHandler::Init\n"));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ rv = nsHttp::CreateAtomTable();
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIIOService> service = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("unable to continue without io service");
+ return rv;
+ }
+ mIOService = new nsMainThreadPtrHolder<nsIIOService>(
+ "nsHttpHandler::mIOService", service);
+
+ gIOService->LaunchSocketProcess();
+
+ if (IsNeckoChild()) NeckoChild::InitNeckoChild();
+
+ InitUserAgentComponents();
+
+ // This perference is only used in parent process.
+ if (!IsNeckoChild()) {
+ mActiveTabPriority =
+ Preferences::GetBool(HTTP_PREF("active_tab_priority"), true);
+ }
+
+ auto initQLogDir = [&]() {
+ nsCOMPtr<nsIFile> qlogDir;
+ nsresult rv =
+ NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(qlogDir));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return EmptyCString();
+ }
+
+ nsAutoCString dirName("qlog_");
+ dirName.AppendInt(mProcessId);
+ rv = qlogDir->AppendNative(dirName);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return EmptyCString();
+ }
+
+ return qlogDir->HumanReadablePath();
+ };
+ mHttp3QlogDir = initQLogDir();
+
+ // monitor some preference changes
+ Preferences::RegisterPrefixCallbacks(nsHttpHandler::PrefsChanged,
+ gCallbackPrefs, this);
+ PrefsChanged(nullptr);
+ Telemetry::ScalarSet(Telemetry::ScalarID::NETWORKING_HTTP3_ENABLED,
+ mHttp3Enabled);
+
+ mMisc.AssignLiteral("rv:" MOZILLA_UAVERSION);
+
+ mCompatFirefox.AssignLiteral("Firefox/" MOZILLA_UAVERSION);
+
+ nsCOMPtr<nsIXULAppInfo> appInfo =
+ do_GetService("@mozilla.org/xre/app-info;1");
+
+ mAppName.AssignLiteral(MOZ_APP_UA_NAME);
+ if (mAppName.Length() == 0 && appInfo) {
+ // Try to get the UA name from appInfo, falling back to the name
+ appInfo->GetUAName(mAppName);
+ if (mAppName.Length() == 0) {
+ appInfo->GetName(mAppName);
+ }
+ appInfo->GetVersion(mAppVersion);
+ mAppName.StripChars(R"( ()<>@,;:\"/[]?={})");
+ } else {
+ mAppVersion.AssignLiteral(MOZ_APP_UA_VERSION);
+ }
+
+ // Generate the spoofed User Agent for fingerprinting resistance.
+ nsRFPService::GetSpoofedUserAgent(mSpoofedUserAgent, true);
+
+ mSessionStartTime = NowInSeconds();
+ mHandlerActive = true;
+
+ rv = InitConnectionMgr();
+ if (NS_FAILED(rv)) return rv;
+
+ mAltSvcCache = MakeUnique<AltSvcCache>();
+
+ mRequestContextService = RequestContextService::GetOrCreate();
+
+#if defined(ANDROID)
+ mProductSub.AssignLiteral(MOZILLA_UAVERSION);
+#else
+ mProductSub.AssignLiteral(LEGACY_UA_GECKO_TRAIL);
+#endif
+
+#if DEBUG
+ // dump user agent prefs
+ LOG(("> legacy-app-name = %s\n", mLegacyAppName.get()));
+ LOG(("> legacy-app-version = %s\n", mLegacyAppVersion.get()));
+ LOG(("> platform = %s\n", mPlatform.get()));
+ LOG(("> oscpu = %s\n", mOscpu.get()));
+ LOG(("> misc = %s\n", mMisc.get()));
+ LOG(("> product = %s\n", mProduct.get()));
+ LOG(("> product-sub = %s\n", mProductSub.get()));
+ LOG(("> app-name = %s\n", mAppName.get()));
+ LOG(("> app-version = %s\n", mAppVersion.get()));
+ LOG(("> compat-firefox = %s\n", mCompatFirefox.get()));
+ LOG(("> user-agent = %s\n", UserAgent().get()));
+#endif
+
+ // Startup the http category
+ // Bring alive the objects in the http-protocol-startup category
+ NS_CreateServicesFromCategory(
+ NS_HTTP_STARTUP_CATEGORY,
+ static_cast<nsISupports*>(static_cast<void*>(this)),
+ NS_HTTP_STARTUP_TOPIC);
+
+ nsCOMPtr<nsIObserverService> obsService =
+ static_cast<nsIObserverService*>(gIOService);
+ if (obsService) {
+ // register the handler object as a weak callback as we don't need to worry
+ // about shutdown ordering.
+ obsService->AddObserver(this, "profile-change-net-teardown", true);
+ obsService->AddObserver(this, "profile-change-net-restore", true);
+ obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
+ obsService->AddObserver(this, "net:clear-active-logins", true);
+ obsService->AddObserver(this, "net:prune-dead-connections", true);
+ // Sent by the TorButton add-on in the Tor Browser
+ obsService->AddObserver(this, "net:prune-all-connections", true);
+ obsService->AddObserver(this, "net:cancel-all-connections", true);
+ obsService->AddObserver(this, "last-pb-context-exited", true);
+ obsService->AddObserver(this, "browser:purge-session-history", true);
+ obsService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true);
+ obsService->AddObserver(this, "application-background", true);
+ obsService->AddObserver(this, "psm:user-certificate-added", true);
+ obsService->AddObserver(this, "psm:user-certificate-deleted", true);
+ obsService->AddObserver(this, "intl:app-locales-changed", true);
+ obsService->AddObserver(this, "browser-delayed-startup-finished", true);
+ obsService->AddObserver(this, "network:captive-portal-connectivity", true);
+ obsService->AddObserver(this, "network:reset-http3-excluded-list", true);
+
+ if (!IsNeckoChild()) {
+ obsService->AddObserver(
+ this, "net:current-toplevel-outer-content-windowid", true);
+ }
+
+ if (mFastOpenSupported) {
+ obsService->AddObserver(this, "captive-portal-login", true);
+ obsService->AddObserver(this, "captive-portal-login-success", true);
+ }
+
+ // disabled as its a nop right now
+ // obsService->AddObserver(this, "net:failed-to-process-uri-content", true);
+ }
+
+ MakeNewRequestTokenBucket();
+ mWifiTickler = new Tickler();
+ if (NS_FAILED(mWifiTickler->Init())) mWifiTickler = nullptr;
+
+ nsCOMPtr<nsIParentalControlsService> pc =
+ do_CreateInstance("@mozilla.org/parental-controls-service;1");
+ if (pc) {
+ pc->GetParentalControlsEnabled(&mParentalControlEnabled);
+ }
+
+ return NS_OK;
+}
+
+const nsCString& nsHttpHandler::Http3QlogDir() {
+ if (StaticPrefs::network_http_http3_enable_qlog()) {
+ return mHttp3QlogDir;
+ }
+
+ return EmptyCString();
+}
+
+void nsHttpHandler::MakeNewRequestTokenBucket() {
+ LOG(("nsHttpHandler::MakeNewRequestTokenBucket this=%p child=%d\n", this,
+ IsNeckoChild()));
+ if (!mConnMgr || IsNeckoChild()) {
+ return;
+ }
+ RefPtr<EventTokenBucket> tokenBucket =
+ new EventTokenBucket(RequestTokenBucketHz(), RequestTokenBucketBurst());
+ // NOTE The thread or socket may be gone already.
+ nsresult rv = mConnMgr->UpdateRequestTokenBucket(tokenBucket);
+ if (NS_FAILED(rv)) {
+ LOG((" failed to update request token bucket\n"));
+ }
+}
+
+nsresult nsHttpHandler::InitConnectionMgr() {
+ // Init ConnectionManager only on parent!
+ if (IsNeckoChild()) {
+ return NS_OK;
+ }
+
+ if (mConnMgr) {
+ return NS_OK;
+ }
+
+ if (nsIOService::UseSocketProcess(true) && XRE_IsParentProcess()) {
+ mConnMgr = new HttpConnectionMgrParent();
+ RefPtr<nsHttpHandler> self = this;
+ auto task = [self]() {
+ HttpConnectionMgrParent* parent =
+ self->mConnMgr->AsHttpConnectionMgrParent();
+ Unused << SocketProcessParent::GetSingleton()
+ ->SendPHttpConnectionMgrConstructor(
+ parent,
+ HttpHandlerInitArgs(
+ self->mFastOpenSupported, self->mLegacyAppName,
+ self->mLegacyAppVersion, self->mPlatform,
+ self->mOscpu, self->mMisc, self->mProduct,
+ self->mProductSub, self->mAppName,
+ self->mAppVersion, self->mCompatFirefox,
+ self->mCompatDevice, self->mDeviceModelId));
+ };
+ gIOService->CallOrWaitForSocketProcess(std::move(task));
+ } else {
+ MOZ_ASSERT(XRE_IsSocketProcess() || !nsIOService::UseSocketProcess());
+ mConnMgr = new nsHttpConnectionMgr();
+ }
+
+ return mConnMgr->Init(
+ mMaxUrgentExcessiveConns, mMaxConnections,
+ mMaxPersistentConnectionsPerServer, mMaxPersistentConnectionsPerProxy,
+ mMaxRequestDelay, mThrottleEnabled, mThrottleVersion, mThrottleSuspendFor,
+ mThrottleResumeFor, mThrottleReadLimit, mThrottleReadInterval,
+ mThrottleHoldTime, mThrottleMaxTime, mBeConservativeForProxy);
+}
+
+nsresult nsHttpHandler::AddStandardRequestHeaders(
+ nsHttpRequestHead* request, bool isSecure,
+ ExtContentPolicyType aContentPolicyType) {
+ nsresult rv;
+
+ // Add the "User-Agent" header
+ rv = request->SetHeader(nsHttp::User_Agent, UserAgent(), false,
+ nsHttpHeaderArray::eVarietyRequestDefault);
+ if (NS_FAILED(rv)) return rv;
+
+ // MIME based content negotiation lives!
+ // Add the "Accept" header. Note, this is set as an override because the
+ // service worker expects to see it. The other "default" headers are
+ // hidden from service worker interception.
+ nsAutoCString accept;
+ if (aContentPolicyType == ExtContentPolicy::TYPE_DOCUMENT ||
+ aContentPolicyType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
+ accept.Assign(mDocumentAcceptHeader);
+ } else if (aContentPolicyType == ExtContentPolicy::TYPE_IMAGE ||
+ aContentPolicyType == ExtContentPolicy::TYPE_IMAGESET) {
+ accept.Assign(mImageAcceptHeader);
+ } else if (aContentPolicyType == ExtContentPolicy::TYPE_STYLESHEET) {
+ accept.Assign(ACCEPT_HEADER_STYLE);
+ } else {
+ accept.Assign(ACCEPT_HEADER_ALL);
+ }
+
+ rv = request->SetHeader(nsHttp::Accept, accept, false,
+ nsHttpHeaderArray::eVarietyRequestOverride);
+ if (NS_FAILED(rv)) return rv;
+
+ // Add the "Accept-Language" header. This header is also exposed to the
+ // service worker.
+ if (mAcceptLanguagesIsDirty) {
+ rv = SetAcceptLanguages();
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+
+ // Add the "Accept-Language" header
+ if (!mAcceptLanguages.IsEmpty()) {
+ rv = request->SetHeader(nsHttp::Accept_Language, mAcceptLanguages, false,
+ nsHttpHeaderArray::eVarietyRequestOverride);
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ // Add the "Accept-Encoding" header
+ if (isSecure) {
+ rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpsAcceptEncodings,
+ false, nsHttpHeaderArray::eVarietyRequestDefault);
+ } else {
+ rv = request->SetHeader(nsHttp::Accept_Encoding, mHttpAcceptEncodings,
+ false, nsHttpHeaderArray::eVarietyRequestDefault);
+ }
+ if (NS_FAILED(rv)) return rv;
+
+ // add the "Send Hint" header
+ if (mSafeHintEnabled || mParentalControlEnabled) {
+ rv = request->SetHeader(nsHttp::Prefer, "safe"_ns, false,
+ nsHttpHeaderArray::eVarietyRequestDefault);
+ if (NS_FAILED(rv)) return rv;
+ }
+ return NS_OK;
+}
+
+nsresult nsHttpHandler::AddConnectionHeader(nsHttpRequestHead* request,
+ uint32_t caps) {
+ // RFC2616 section 19.6.2 states that the "Connection: keep-alive"
+ // and "Keep-alive" request headers should not be sent by HTTP/1.1
+ // user-agents. But this is not a problem in practice, and the
+ // alternative proxy-connection is worse. see 570283
+
+ constexpr auto close = "close"_ns;
+ constexpr auto keepAlive = "keep-alive"_ns;
+
+ const nsLiteralCString* connectionType = &close;
+ if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
+ connectionType = &keepAlive;
+ }
+
+ return request->SetHeader(nsHttp::Connection, *connectionType);
+}
+
+bool nsHttpHandler::IsAcceptableEncoding(const char* enc, bool isSecure) {
+ if (!enc) return false;
+
+ // we used to accept x-foo anytime foo was acceptable, but that's just
+ // continuing bad behavior.. so limit it to known x-* patterns
+ bool rv;
+ if (isSecure) {
+ rv = nsHttp::FindToken(mHttpsAcceptEncodings.get(), enc, HTTP_LWS ",") !=
+ nullptr;
+ } else {
+ rv = nsHttp::FindToken(mHttpAcceptEncodings.get(), enc, HTTP_LWS ",") !=
+ nullptr;
+ }
+ // gzip and deflate are inherently acceptable in modern HTTP - always
+ // process them if a stream converter can also be found.
+ if (!rv &&
+ (!PL_strcasecmp(enc, "gzip") || !PL_strcasecmp(enc, "deflate") ||
+ !PL_strcasecmp(enc, "x-gzip") || !PL_strcasecmp(enc, "x-deflate"))) {
+ rv = true;
+ }
+ LOG(("nsHttpHandler::IsAceptableEncoding %s https=%d %d\n", enc, isSecure,
+ rv));
+ return rv;
+}
+
+void nsHttpHandler::IncrementFastOpenConsecutiveFailureCounter() {
+ LOG(
+ ("nsHttpHandler::IncrementFastOpenConsecutiveFailureCounter - "
+ "failed=%d failure_limit=%d",
+ mFastOpenConsecutiveFailureCounter, mFastOpenConsecutiveFailureLimit));
+ if (mFastOpenConsecutiveFailureCounter < mFastOpenConsecutiveFailureLimit) {
+ mFastOpenConsecutiveFailureCounter++;
+ if (mFastOpenConsecutiveFailureCounter ==
+ mFastOpenConsecutiveFailureLimit) {
+ LOG(
+ ("nsHttpHandler::IncrementFastOpenConsecutiveFailureCounter - "
+ "Fast open failed too many times"));
+ }
+ }
+}
+
+void nsHttpHandler::IncrementFastOpenStallsCounter() {
+ LOG(
+ ("nsHttpHandler::IncrementFastOpenStallsCounter - failed=%d "
+ "failure_limit=%d",
+ mFastOpenStallsCounter, mFastOpenStallsLimit));
+ if (mFastOpenStallsCounter < mFastOpenStallsLimit) {
+ mFastOpenStallsCounter++;
+ if (mFastOpenStallsCounter == mFastOpenStallsLimit) {
+ LOG(
+ ("nsHttpHandler::IncrementFastOpenStallsCounter - "
+ "There are too many stalls involving TFO and TLS."));
+ }
+ }
+}
+
+nsresult nsHttpHandler::GetStreamConverterService(
+ nsIStreamConverterService** result) {
+ if (!mStreamConvSvc) {
+ nsresult rv;
+ nsCOMPtr<nsIStreamConverterService> service =
+ do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) return rv;
+ mStreamConvSvc = new nsMainThreadPtrHolder<nsIStreamConverterService>(
+ "nsHttpHandler::mStreamConvSvc", service);
+ }
+ *result = do_AddRef(mStreamConvSvc.get()).take();
+ return NS_OK;
+}
+
+nsISiteSecurityService* nsHttpHandler::GetSSService() {
+ if (!mSSService) {
+ nsCOMPtr<nsISiteSecurityService> service =
+ do_GetService(NS_SSSERVICE_CONTRACTID);
+ mSSService = new nsMainThreadPtrHolder<nsISiteSecurityService>(
+ "nsHttpHandler::mSSService", service);
+ }
+ return mSSService;
+}
+
+nsICookieService* nsHttpHandler::GetCookieService() {
+ if (!mCookieService) {
+ nsCOMPtr<nsICookieService> service =
+ do_GetService(NS_COOKIESERVICE_CONTRACTID);
+ mCookieService = new nsMainThreadPtrHolder<nsICookieService>(
+ "nsHttpHandler::mCookieService", service);
+ }
+ return mCookieService;
+}
+
+nsresult nsHttpHandler::GetIOService(nsIIOService** result) {
+ NS_ENSURE_ARG_POINTER(result);
+
+ *result = do_AddRef(mIOService.get()).take();
+ return NS_OK;
+}
+
+void nsHttpHandler::NotifyObservers(nsIChannel* chan, const char* event) {
+ LOG(("nsHttpHandler::NotifyObservers [chan=%p event=\"%s\"]\n", chan, event));
+ nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
+ if (obsService) obsService->NotifyObservers(chan, event, nullptr);
+}
+
+nsresult nsHttpHandler::AsyncOnChannelRedirect(
+ nsIChannel* oldChan, nsIChannel* newChan, uint32_t flags,
+ nsIEventTarget* mainThreadEventTarget) {
+ MOZ_ASSERT(NS_IsMainThread() && (oldChan && newChan));
+
+ nsCOMPtr<nsIURI> oldURI;
+ oldChan->GetURI(getter_AddRefs(oldURI));
+ MOZ_ASSERT(oldURI);
+
+ nsCOMPtr<nsIURI> newURI;
+ newChan->GetURI(getter_AddRefs(newURI));
+ MOZ_ASSERT(newURI);
+
+ AntiTrackingRedirectHeuristic(oldChan, oldURI, newChan, newURI);
+
+ DynamicFpiRedirectHeuristic(oldChan, oldURI, newChan, newURI);
+
+ // TODO E10S This helper has to be initialized on the other process
+ RefPtr<nsAsyncRedirectVerifyHelper> redirectCallbackHelper =
+ new nsAsyncRedirectVerifyHelper();
+
+ return redirectCallbackHelper->Init(oldChan, newChan, flags,
+ mainThreadEventTarget);
+}
+
+/* static */
+nsresult nsHttpHandler::GenerateHostPort(const nsCString& host, int32_t port,
+ nsACString& hostLine) {
+ return NS_GenerateHostPort(host, port, hostLine);
+}
+
+//-----------------------------------------------------------------------------
+// nsHttpHandler <private>
+//-----------------------------------------------------------------------------
+
+const nsCString& nsHttpHandler::UserAgent() {
+ if (nsContentUtils::ShouldResistFingerprinting() &&
+ !mSpoofedUserAgent.IsEmpty()) {
+ LOG(("using spoofed userAgent : %s\n", mSpoofedUserAgent.get()));
+ return mSpoofedUserAgent;
+ }
+
+ if (!mUserAgentOverride.IsVoid()) {
+ LOG(("using general.useragent.override : %s\n", mUserAgentOverride.get()));
+ return mUserAgentOverride;
+ }
+
+ if (mUserAgentIsDirty) {
+ BuildUserAgent();
+ mUserAgentIsDirty = false;
+ }
+
+ return mUserAgent;
+}
+
+void nsHttpHandler::BuildUserAgent() {
+ LOG(("nsHttpHandler::BuildUserAgent\n"));
+
+ MOZ_ASSERT(!mLegacyAppName.IsEmpty() && !mLegacyAppVersion.IsEmpty(),
+ "HTTP cannot send practical requests without this much");
+
+ // preallocate to worst-case size, which should always be better
+ // than if we didn't preallocate at all.
+ mUserAgent.SetCapacity(mLegacyAppName.Length() + mLegacyAppVersion.Length() +
+#ifndef UA_SPARE_PLATFORM
+ mPlatform.Length() +
+#endif
+ mOscpu.Length() + mMisc.Length() + mProduct.Length() +
+ mProductSub.Length() + mAppName.Length() +
+ mAppVersion.Length() + mCompatFirefox.Length() +
+ mCompatDevice.Length() + mDeviceModelId.Length() + 13);
+
+ // Application portion
+ mUserAgent.Assign(mLegacyAppName);
+ mUserAgent += '/';
+ mUserAgent += mLegacyAppVersion;
+ mUserAgent += ' ';
+
+ // Application comment
+ mUserAgent += '(';
+#ifndef UA_SPARE_PLATFORM
+ if (!mPlatform.IsEmpty()) {
+ mUserAgent += mPlatform;
+ mUserAgent.AppendLiteral("; ");
+ }
+#endif
+ if (!mCompatDevice.IsEmpty()) {
+ mUserAgent += mCompatDevice;
+ mUserAgent.AppendLiteral("; ");
+ } else if (!mOscpu.IsEmpty()) {
+ mUserAgent += mOscpu;
+ mUserAgent.AppendLiteral("; ");
+ }
+ if (!mDeviceModelId.IsEmpty()) {
+ mUserAgent += mDeviceModelId;
+ mUserAgent.AppendLiteral("; ");
+ }
+ mUserAgent += mMisc;
+ mUserAgent += ')';
+
+ // Product portion
+ mUserAgent += ' ';
+ mUserAgent += mProduct;
+ mUserAgent += '/';
+ mUserAgent += mProductSub;
+
+ bool isFirefox = mAppName.EqualsLiteral("Firefox");
+ if (isFirefox || mCompatFirefoxEnabled) {
+ // "Firefox/x.y" (compatibility) app token
+ mUserAgent += ' ';
+ mUserAgent += mCompatFirefox;
+ }
+ if (!isFirefox) {
+ // App portion
+ mUserAgent += ' ';
+ mUserAgent += mAppName;
+ mUserAgent += '/';
+ mUserAgent += mAppVersion;
+ }
+}
+
+#ifdef XP_WIN
+# define OSCPU_WINDOWS "Windows NT %ld.%ld"
+# define OSCPU_WIN64 OSCPU_WINDOWS "; Win64; x64"
+#endif
+
+void nsHttpHandler::InitUserAgentComponents() {
+ // Don't build user agent components in socket process, since the system info
+ // is not available.
+ if (XRE_IsSocketProcess()) {
+ mUserAgentIsDirty = true;
+ return;
+ }
+
+#ifndef MOZ_UA_OS_AGNOSTIC
+ // Gather platform.
+ mPlatform.AssignLiteral(
+# if defined(ANDROID)
+ "Android"
+# elif defined(XP_WIN)
+ "Windows"
+# elif defined(XP_MACOSX)
+ "Macintosh"
+# elif defined(XP_UNIX)
+ // We historically have always had X11 here,
+ // and there seems little a webpage can sensibly do
+ // based on it being something else, so use X11 for
+ // backwards compatibility in all cases.
+ "X11"
+# endif
+ );
+#endif
+
+#ifdef ANDROID
+ nsCOMPtr<nsIPropertyBag2> infoService =
+ do_GetService("@mozilla.org/system-info;1");
+ MOZ_ASSERT(infoService, "Could not find a system info service");
+ nsresult rv;
+ // Add the Android version number to the Fennec platform identifier.
+# if defined MOZ_WIDGET_ANDROID
+# ifndef MOZ_UA_OS_AGNOSTIC // Don't add anything to mPlatform since it's
+ // empty.
+ nsAutoString androidVersion;
+ rv = infoService->GetPropertyAsAString(u"release_version"_ns, androidVersion);
+ if (NS_SUCCEEDED(rv)) {
+ mPlatform += " ";
+ // If the 2nd character is a ".", we know the major version is a single
+ // digit. If we're running on a version below 4 we pretend to be on
+ // Android KitKat (4.4) to work around scripts sniffing for low versions.
+ if (androidVersion[1] == 46 && androidVersion[0] < 52) {
+ mPlatform += "4.4";
+ } else {
+ mPlatform += NS_LossyConvertUTF16toASCII(androidVersion);
+ }
+ }
+# endif
+# endif
+ // Add the `Mobile` or `Tablet` or `TV` token when running on device.
+ bool isTablet;
+ rv = infoService->GetPropertyAsBool(u"tablet"_ns, &isTablet);
+ if (NS_SUCCEEDED(rv) && isTablet) {
+ mCompatDevice.AssignLiteral("Tablet");
+ } else {
+ bool isTV;
+ rv = infoService->GetPropertyAsBool(u"tv"_ns, &isTV);
+ if (NS_SUCCEEDED(rv) && isTV) {
+ mCompatDevice.AssignLiteral("TV");
+ } else {
+ mCompatDevice.AssignLiteral("Mobile");
+ }
+ }
+
+ if (Preferences::GetBool(UA_PREF("use_device"), false)) {
+ mDeviceModelId = mozilla::net::GetDeviceModelId();
+ }
+#endif // ANDROID
+
+#ifndef MOZ_UA_OS_AGNOSTIC
+ // Gather OS/CPU.
+# if defined(XP_WIN)
+ OSVERSIONINFO info = {sizeof(OSVERSIONINFO)};
+# pragma warning(push)
+# pragma warning(disable : 4996)
+ if (GetVersionEx(&info)) {
+# pragma warning(pop)
+
+ const char* format;
+# if defined _M_X64 || defined _M_AMD64
+ format = OSCPU_WIN64;
+# else
+ BOOL isWow64 = FALSE;
+ if (!IsWow64Process(GetCurrentProcess(), &isWow64)) {
+ isWow64 = FALSE;
+ }
+ format = isWow64 ? OSCPU_WIN64 : OSCPU_WINDOWS;
+# endif
+
+ SmprintfPointer buf =
+ mozilla::Smprintf(format, info.dwMajorVersion, info.dwMinorVersion);
+ if (buf) {
+ mOscpu = buf.get();
+ }
+ }
+# elif defined(XP_MACOSX)
+ SInt32 majorVersion = nsCocoaFeatures::macOSVersionMajor();
+ SInt32 minorVersion = nsCocoaFeatures::macOSVersionMinor();
+
+ // Always return an "Intel" UA string, even on ARM64 macOS like Safari does.
+ mOscpu =
+ nsPrintfCString("Intel Mac OS X %d.%d", static_cast<int>(majorVersion),
+ static_cast<int>(minorVersion));
+# elif defined(XP_UNIX)
+ struct utsname name;
+ int ret = uname(&name);
+ if (ret >= 0) {
+ nsAutoCString buf;
+ buf = (char*)name.sysname;
+ buf += ' ';
+
+# ifdef AIX
+ // AIX uname returns machine specific info in the uname.machine
+ // field and does not return the cpu type like other platforms.
+ // We use the AIX version and release numbers instead.
+ buf += (char*)name.version;
+ buf += '.';
+ buf += (char*)name.release;
+# else
+ buf += (char*)name.machine;
+# endif
+
+ mOscpu.Assign(buf);
+ }
+# endif
+#endif
+
+ mUserAgentIsDirty = true;
+}
+
+uint32_t nsHttpHandler::MaxSocketCount() {
+ PR_CallOnce(&nsSocketTransportService::gMaxCountInitOnce,
+ nsSocketTransportService::DiscoverMaxCount);
+ // Don't use the full max count because sockets can be held in
+ // the persistent connection pool for a long time and that could
+ // starve other users.
+
+ uint32_t maxCount = nsSocketTransportService::gMaxCount;
+ if (maxCount <= 8)
+ maxCount = 1;
+ else
+ maxCount -= 8;
+
+ return maxCount;
+}
+
+// static
+void nsHttpHandler::PrefsChanged(const char* pref, void* self) {
+ static_cast<nsHttpHandler*>(self)->PrefsChanged(pref);
+}
+
+void nsHttpHandler::PrefsChanged(const char* pref) {
+ nsresult rv = NS_OK;
+ int32_t val;
+
+ LOG(("nsHttpHandler::PrefsChanged [pref=%s]\n", pref));
+
+ if (pref) {
+ gIOService->NotifySocketProcessPrefsChanged(pref);
+ }
+
+#define PREF_CHANGED(p) ((pref == nullptr) || !PL_strcmp(pref, p))
+#define MULTI_PREF_CHANGED(p) \
+ ((pref == nullptr) || !PL_strncmp(pref, p, sizeof(p) - 1))
+
+ // If a security pref changed, lets clear our connection pool reuse
+ if (MULTI_PREF_CHANGED(SECURITY_PREFIX)) {
+ LOG(("nsHttpHandler::PrefsChanged Security Pref Changed %s\n", pref));
+ if (mConnMgr) {
+ rv = mConnMgr->DoShiftReloadConnectionCleanup();
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("nsHttpHandler::PrefsChanged "
+ "DoShiftReloadConnectionCleanup failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ rv = mConnMgr->PruneDeadConnections();
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("nsHttpHandler::PrefsChanged "
+ "PruneDeadConnections failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ }
+ }
+
+ //
+ // UA components
+ //
+
+ bool cVar = false;
+
+ if (PREF_CHANGED(UA_PREF("compatMode.firefox"))) {
+ rv = Preferences::GetBool(UA_PREF("compatMode.firefox"), &cVar);
+ mCompatFirefoxEnabled = (NS_SUCCEEDED(rv) && cVar);
+ mUserAgentIsDirty = true;
+ }
+
+ // general.useragent.override
+ if (PREF_CHANGED(UA_PREF("override"))) {
+ Preferences::GetCString(UA_PREF("override"), mUserAgentOverride);
+ mUserAgentIsDirty = true;
+ }
+
+#ifdef ANDROID
+ // general.useragent.use_device
+ if (PREF_CHANGED(UA_PREF("use_device"))) {
+ if (Preferences::GetBool(UA_PREF("use_device"), false)) {
+ if (!XRE_IsSocketProcess()) {
+ mDeviceModelId = mozilla::net::GetDeviceModelId();
+ if (gIOService->SocketProcessReady()) {
+ Unused << SocketProcessParent::GetSingleton()
+ ->SendUpdateDeviceModelId(mDeviceModelId);
+ }
+ }
+ } else {
+ mDeviceModelId.Truncate();
+ }
+ mUserAgentIsDirty = true;
+ }
+#endif
+
+ //
+ // HTTP options
+ //
+
+ if (PREF_CHANGED(HTTP_PREF("keep-alive.timeout"))) {
+ rv = Preferences::GetInt(HTTP_PREF("keep-alive.timeout"), &val);
+ if (NS_SUCCEEDED(rv))
+ mIdleTimeout = PR_SecondsToInterval(clamped(val, 1, 0xffff));
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("request.max-attempts"))) {
+ rv = Preferences::GetInt(HTTP_PREF("request.max-attempts"), &val);
+ if (NS_SUCCEEDED(rv))
+ mMaxRequestAttempts = (uint16_t)clamped(val, 1, 0xffff);
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("request.max-start-delay"))) {
+ rv = Preferences::GetInt(HTTP_PREF("request.max-start-delay"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mMaxRequestDelay = (uint16_t)clamped(val, 0, 0xffff);
+ if (mConnMgr) {
+ rv = mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_REQUEST_DELAY,
+ mMaxRequestDelay);
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("nsHttpHandler::PrefsChanged (request.max-start-delay)"
+ "UpdateParam failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ }
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("response.timeout"))) {
+ rv = Preferences::GetInt(HTTP_PREF("response.timeout"), &val);
+ if (NS_SUCCEEDED(rv))
+ mResponseTimeout = PR_SecondsToInterval(clamped(val, 0, 0xffff));
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("network-changed.timeout"))) {
+ rv = Preferences::GetInt(HTTP_PREF("network-changed.timeout"), &val);
+ if (NS_SUCCEEDED(rv)) mNetworkChangedTimeout = clamped(val, 1, 600) * 1000;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("max-connections"))) {
+ rv = Preferences::GetInt(HTTP_PREF("max-connections"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mMaxConnections =
+ (uint16_t)clamped((uint32_t)val, (uint32_t)1, MaxSocketCount());
+
+ if (mConnMgr) {
+ rv = mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_CONNECTIONS,
+ mMaxConnections);
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("nsHttpHandler::PrefsChanged (max-connections)"
+ "UpdateParam failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ }
+ }
+ }
+
+ if (PREF_CHANGED(
+ HTTP_PREF("max-urgent-start-excessive-connections-per-host"))) {
+ rv = Preferences::GetInt(
+ HTTP_PREF("max-urgent-start-excessive-connections-per-host"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mMaxUrgentExcessiveConns = (uint8_t)clamped(val, 1, 0xff);
+ if (mConnMgr) {
+ rv = mConnMgr->UpdateParam(nsHttpConnectionMgr::MAX_URGENT_START_Q,
+ mMaxUrgentExcessiveConns);
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("nsHttpHandler::PrefsChanged "
+ "(max-urgent-start-excessive-connections-per-host)"
+ "UpdateParam failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ }
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("max-persistent-connections-per-server"))) {
+ rv = Preferences::GetInt(HTTP_PREF("max-persistent-connections-per-server"),
+ &val);
+ if (NS_SUCCEEDED(rv)) {
+ mMaxPersistentConnectionsPerServer = (uint8_t)clamped(val, 1, 0xff);
+ if (mConnMgr) {
+ rv = mConnMgr->UpdateParam(
+ nsHttpConnectionMgr::MAX_PERSISTENT_CONNECTIONS_PER_HOST,
+ mMaxPersistentConnectionsPerServer);
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("nsHttpHandler::PrefsChanged "
+ "(max-persistent-connections-per-server)"
+ "UpdateParam failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ }
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("max-persistent-connections-per-proxy"))) {
+ rv = Preferences::GetInt(HTTP_PREF("max-persistent-connections-per-proxy"),
+ &val);
+ if (NS_SUCCEEDED(rv)) {
+ mMaxPersistentConnectionsPerProxy = (uint8_t)clamped(val, 1, 0xff);
+ if (mConnMgr) {
+ rv = mConnMgr->UpdateParam(
+ nsHttpConnectionMgr::MAX_PERSISTENT_CONNECTIONS_PER_PROXY,
+ mMaxPersistentConnectionsPerProxy);
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("nsHttpHandler::PrefsChanged "
+ "(max-persistent-connections-per-proxy)"
+ "UpdateParam failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ }
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("redirection-limit"))) {
+ rv = Preferences::GetInt(HTTP_PREF("redirection-limit"), &val);
+ if (NS_SUCCEEDED(rv)) mRedirectionLimit = (uint8_t)clamped(val, 0, 0xff);
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("connection-retry-timeout"))) {
+ rv = Preferences::GetInt(HTTP_PREF("connection-retry-timeout"), &val);
+ if (NS_SUCCEEDED(rv)) mIdleSynTimeout = (uint16_t)clamped(val, 0, 3000);
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("fast-fallback-to-IPv4"))) {
+ rv = Preferences::GetBool(HTTP_PREF("fast-fallback-to-IPv4"), &cVar);
+ if (NS_SUCCEEDED(rv)) mFastFallbackToIPv4 = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("fallback-connection-timeout"))) {
+ rv = Preferences::GetInt(HTTP_PREF("fallback-connection-timeout"), &val);
+ if (NS_SUCCEEDED(rv))
+ mFallbackSynTimeout = (uint16_t)clamped(val, 0, 10 * 60);
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("version"))) {
+ nsAutoCString httpVersion;
+ Preferences::GetCString(HTTP_PREF("version"), httpVersion);
+ if (!httpVersion.IsVoid()) {
+ if (httpVersion.EqualsLiteral("1.1"))
+ mHttpVersion = HttpVersion::v1_1;
+ else if (httpVersion.EqualsLiteral("0.9"))
+ mHttpVersion = HttpVersion::v0_9;
+ else
+ mHttpVersion = HttpVersion::v1_0;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("proxy.version"))) {
+ nsAutoCString httpVersion;
+ Preferences::GetCString(HTTP_PREF("proxy.version"), httpVersion);
+ if (!httpVersion.IsVoid()) {
+ if (httpVersion.EqualsLiteral("1.1"))
+ mProxyHttpVersion = HttpVersion::v1_1;
+ else
+ mProxyHttpVersion = HttpVersion::v1_0;
+ // it does not make sense to issue a HTTP/0.9 request to a proxy server
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("proxy.respect-be-conservative"))) {
+ rv =
+ Preferences::GetBool(HTTP_PREF("proxy.respect-be-conservative"), &cVar);
+ if (NS_SUCCEEDED(rv)) {
+ mBeConservativeForProxy = cVar;
+ if (mConnMgr) {
+ Unused << mConnMgr->UpdateParam(
+ nsHttpConnectionMgr::PROXY_BE_CONSERVATIVE,
+ static_cast<int32_t>(mBeConservativeForProxy));
+ }
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("qos"))) {
+ rv = Preferences::GetInt(HTTP_PREF("qos"), &val);
+ if (NS_SUCCEEDED(rv)) mQoSBits = (uint8_t)clamped(val, 0, 0xff);
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("accept-encoding"))) {
+ nsAutoCString acceptEncodings;
+ rv = Preferences::GetCString(HTTP_PREF("accept-encoding"), acceptEncodings);
+ if (NS_SUCCEEDED(rv)) {
+ rv = SetAcceptEncodings(acceptEncodings.get(), false);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("accept-encoding.secure"))) {
+ nsAutoCString acceptEncodings;
+ rv = Preferences::GetCString(HTTP_PREF("accept-encoding.secure"),
+ acceptEncodings);
+ if (NS_SUCCEEDED(rv)) {
+ rv = SetAcceptEncodings(acceptEncodings.get(), true);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("default-socket-type"))) {
+ nsAutoCString sval;
+ rv = Preferences::GetCString(HTTP_PREF("default-socket-type"), sval);
+ if (NS_SUCCEEDED(rv)) {
+ if (sval.IsEmpty())
+ mDefaultSocketType.SetIsVoid(true);
+ else {
+ // verify that this socket type is actually valid
+ nsCOMPtr<nsISocketProviderService> sps =
+ nsSocketProviderService::GetOrCreate();
+ if (sps) {
+ nsCOMPtr<nsISocketProvider> sp;
+ rv = sps->GetSocketProvider(sval.get(), getter_AddRefs(sp));
+ if (NS_SUCCEEDED(rv)) {
+ // OK, this looks like a valid socket provider.
+ mDefaultSocketType.Assign(sval);
+ }
+ }
+ }
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("prompt-temp-redirect"))) {
+ rv = Preferences::GetBool(HTTP_PREF("prompt-temp-redirect"), &cVar);
+ if (NS_SUCCEEDED(rv)) {
+ mPromptTempRedirect = cVar;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("assoc-req.enforce"))) {
+ cVar = false;
+ rv = Preferences::GetBool(HTTP_PREF("assoc-req.enforce"), &cVar);
+ if (NS_SUCCEEDED(rv)) mEnforceAssocReq = cVar;
+ }
+
+ // enable Persistent caching for HTTPS - bug#205921
+ if (PREF_CHANGED(BROWSER_PREF("disk_cache_ssl"))) {
+ cVar = false;
+ rv = Preferences::GetBool(BROWSER_PREF("disk_cache_ssl"), &cVar);
+ if (NS_SUCCEEDED(rv)) mEnablePersistentHttpsCaching = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("phishy-userpass-length"))) {
+ rv = Preferences::GetInt(HTTP_PREF("phishy-userpass-length"), &val);
+ if (NS_SUCCEEDED(rv))
+ mPhishyUserPassLength = (uint8_t)clamped(val, 0, 0xff);
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.enabled"))) {
+ rv = Preferences::GetBool(HTTP_PREF("spdy.enabled"), &cVar);
+ if (NS_SUCCEEDED(rv)) mEnableSpdy = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.enabled.http2"))) {
+ rv = Preferences::GetBool(HTTP_PREF("spdy.enabled.http2"), &cVar);
+ if (NS_SUCCEEDED(rv)) mHttp2Enabled = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.enabled.deps"))) {
+ rv = Preferences::GetBool(HTTP_PREF("spdy.enabled.deps"), &cVar);
+ if (NS_SUCCEEDED(rv)) mUseH2Deps = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.enforce-tls-profile"))) {
+ rv = Preferences::GetBool(HTTP_PREF("spdy.enforce-tls-profile"), &cVar);
+ if (NS_SUCCEEDED(rv)) mEnforceHttp2TlsProfile = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.coalesce-hostnames"))) {
+ rv = Preferences::GetBool(HTTP_PREF("spdy.coalesce-hostnames"), &cVar);
+ if (NS_SUCCEEDED(rv)) mCoalesceSpdy = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.persistent-settings"))) {
+ rv = Preferences::GetBool(HTTP_PREF("spdy.persistent-settings"), &cVar);
+ if (NS_SUCCEEDED(rv)) mSpdyPersistentSettings = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.timeout"))) {
+ rv = Preferences::GetInt(HTTP_PREF("spdy.timeout"), &val);
+ if (NS_SUCCEEDED(rv))
+ mSpdyTimeout = PR_SecondsToInterval(clamped(val, 1, 0xffff));
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.chunk-size"))) {
+ // keep this within http/2 ranges of 1 to 2^14-1
+ rv = Preferences::GetInt(HTTP_PREF("spdy.chunk-size"), &val);
+ if (NS_SUCCEEDED(rv))
+ mSpdySendingChunkSize = (uint32_t)clamped(val, 1, 0x3fff);
+ }
+
+ // The amount of idle seconds on a spdy connection before initiating a
+ // server ping. 0 will disable.
+ if (PREF_CHANGED(HTTP_PREF("spdy.ping-threshold"))) {
+ rv = Preferences::GetInt(HTTP_PREF("spdy.ping-threshold"), &val);
+ if (NS_SUCCEEDED(rv))
+ mSpdyPingThreshold =
+ PR_SecondsToInterval((uint16_t)clamped(val, 0, 0x7fffffff));
+ }
+
+ // The amount of seconds to wait for a spdy ping response before
+ // closing the session.
+ if (PREF_CHANGED(HTTP_PREF("spdy.ping-timeout"))) {
+ rv = Preferences::GetInt(HTTP_PREF("spdy.ping-timeout"), &val);
+ if (NS_SUCCEEDED(rv))
+ mSpdyPingTimeout =
+ PR_SecondsToInterval((uint16_t)clamped(val, 0, 0x7fffffff));
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.allow-push"))) {
+ rv = Preferences::GetBool(HTTP_PREF("spdy.allow-push"), &cVar);
+ if (NS_SUCCEEDED(rv)) mAllowPush = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("altsvc.enabled"))) {
+ rv = Preferences::GetBool(HTTP_PREF("altsvc.enabled"), &cVar);
+ if (NS_SUCCEEDED(rv)) mEnableAltSvc = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("altsvc.oe"))) {
+ rv = Preferences::GetBool(HTTP_PREF("altsvc.oe"), &cVar);
+ if (NS_SUCCEEDED(rv)) mEnableAltSvcOE = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("originextension"))) {
+ rv = Preferences::GetBool(HTTP_PREF("originextension"), &cVar);
+ if (NS_SUCCEEDED(rv)) mEnableOriginExtension = cVar;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.websockets"))) {
+ rv = Preferences::GetBool(HTTP_PREF("spdy.websockets"), &cVar);
+ if (NS_SUCCEEDED(rv)) {
+ mEnableH2Websockets = cVar;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.push-allowance"))) {
+ rv = Preferences::GetInt(HTTP_PREF("spdy.push-allowance"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mSpdyPushAllowance = static_cast<uint32_t>(
+ clamped(val, 1024, static_cast<int32_t>(ASpdySession::kInitialRwin)));
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.pull-allowance"))) {
+ rv = Preferences::GetInt(HTTP_PREF("spdy.pull-allowance"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mSpdyPullAllowance =
+ static_cast<uint32_t>(clamped(val, 1024, 0x7fffffff));
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.default-concurrent"))) {
+ rv = Preferences::GetInt(HTTP_PREF("spdy.default-concurrent"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mDefaultSpdyConcurrent = static_cast<uint32_t>(
+ std::max<int32_t>(std::min<int32_t>(val, 9999), 1));
+ }
+ }
+
+ // The amount of seconds to wait for a spdy ping response before
+ // closing the session.
+ if (PREF_CHANGED(HTTP_PREF("spdy.send-buffer-size"))) {
+ rv = Preferences::GetInt(HTTP_PREF("spdy.send-buffer-size"), &val);
+ if (NS_SUCCEEDED(rv))
+ mSpdySendBufferSize = (uint32_t)clamped(val, 1500, 0x7fffffff);
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.enable-hpack-dump"))) {
+ rv = Preferences::GetBool(HTTP_PREF("spdy.enable-hpack-dump"), &cVar);
+ if (NS_SUCCEEDED(rv)) {
+ mDumpHpackTables = cVar;
+ }
+ }
+
+ // The maximum amount of time to wait for socket transport to be
+ // established
+ if (PREF_CHANGED(HTTP_PREF("connection-timeout"))) {
+ rv = Preferences::GetInt(HTTP_PREF("connection-timeout"), &val);
+ if (NS_SUCCEEDED(rv))
+ // the pref is in seconds, but the variable is in milliseconds
+ mConnectTimeout = clamped(val, 1, 0xffff) * PR_MSEC_PER_SEC;
+ }
+
+ // The maximum amount of time to wait for a tls handshake to finish.
+ if (PREF_CHANGED(HTTP_PREF("tls-handshake-timeout"))) {
+ rv = Preferences::GetInt(HTTP_PREF("tls-handshake-timeout"), &val);
+ if (NS_SUCCEEDED(rv))
+ // the pref is in seconds, but the variable is in milliseconds
+ mTLSHandshakeTimeout = clamped(val, 1, 0xffff) * PR_MSEC_PER_SEC;
+ }
+
+ // The maximum number of current global half open sockets allowable
+ // for starting a new speculative connection.
+ if (PREF_CHANGED(HTTP_PREF("speculative-parallel-limit"))) {
+ rv = Preferences::GetInt(HTTP_PREF("speculative-parallel-limit"), &val);
+ if (NS_SUCCEEDED(rv))
+ mParallelSpeculativeConnectLimit = (uint32_t)clamped(val, 0, 1024);
+ }
+
+ // Whether or not to block requests for non head js/css items (e.g. media)
+ // while those elements load.
+ if (PREF_CHANGED(HTTP_PREF("rendering-critical-requests-prioritization"))) {
+ rv = Preferences::GetBool(
+ HTTP_PREF("rendering-critical-requests-prioritization"), &cVar);
+ if (NS_SUCCEEDED(rv)) mCriticalRequestPrioritization = cVar;
+ }
+
+ // on transition of network.http.diagnostics to true print
+ // a bunch of information to the console
+ if (pref && PREF_CHANGED(HTTP_PREF("diagnostics"))) {
+ rv = Preferences::GetBool(HTTP_PREF("diagnostics"), &cVar);
+ if (NS_SUCCEEDED(rv) && cVar) {
+ if (mConnMgr) mConnMgr->PrintDiagnostics();
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("max_response_header_size"))) {
+ rv = Preferences::GetInt(HTTP_PREF("max_response_header_size"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mMaxHttpResponseHeaderSize = val;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("throttle.enable"))) {
+ rv = Preferences::GetBool(HTTP_PREF("throttle.enable"), &mThrottleEnabled);
+ if (NS_SUCCEEDED(rv) && mConnMgr) {
+ Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_ENABLED,
+ static_cast<int32_t>(mThrottleEnabled));
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("throttle.version"))) {
+ Unused << Preferences::GetInt(HTTP_PREF("throttle.version"), &val);
+ mThrottleVersion = (uint32_t)clamped(val, 1, 2);
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("throttle.suspend-for"))) {
+ rv = Preferences::GetInt(HTTP_PREF("throttle.suspend-for"), &val);
+ mThrottleSuspendFor = (uint32_t)clamped(val, 0, 120000);
+ if (NS_SUCCEEDED(rv) && mConnMgr) {
+ Unused << mConnMgr->UpdateParam(
+ nsHttpConnectionMgr::THROTTLING_SUSPEND_FOR, mThrottleSuspendFor);
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("throttle.resume-for"))) {
+ rv = Preferences::GetInt(HTTP_PREF("throttle.resume-for"), &val);
+ mThrottleResumeFor = (uint32_t)clamped(val, 0, 120000);
+ if (NS_SUCCEEDED(rv) && mConnMgr) {
+ Unused << mConnMgr->UpdateParam(
+ nsHttpConnectionMgr::THROTTLING_RESUME_FOR, mThrottleResumeFor);
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("throttle.read-limit-bytes"))) {
+ rv = Preferences::GetInt(HTTP_PREF("throttle.read-limit-bytes"), &val);
+ mThrottleReadLimit = (uint32_t)clamped(val, 0, 500000);
+ if (NS_SUCCEEDED(rv) && mConnMgr) {
+ Unused << mConnMgr->UpdateParam(
+ nsHttpConnectionMgr::THROTTLING_READ_LIMIT, mThrottleReadLimit);
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("throttle.read-interval-ms"))) {
+ rv = Preferences::GetInt(HTTP_PREF("throttle.read-interval-ms"), &val);
+ mThrottleReadInterval = (uint32_t)clamped(val, 0, 120000);
+ if (NS_SUCCEEDED(rv) && mConnMgr) {
+ Unused << mConnMgr->UpdateParam(
+ nsHttpConnectionMgr::THROTTLING_READ_INTERVAL, mThrottleReadInterval);
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("throttle.hold-time-ms"))) {
+ rv = Preferences::GetInt(HTTP_PREF("throttle.hold-time-ms"), &val);
+ mThrottleHoldTime = (uint32_t)clamped(val, 0, 120000);
+ if (NS_SUCCEEDED(rv) && mConnMgr) {
+ Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_HOLD_TIME,
+ mThrottleHoldTime);
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("throttle.max-time-ms"))) {
+ rv = Preferences::GetInt(HTTP_PREF("throttle.max-time-ms"), &val);
+ mThrottleMaxTime = (uint32_t)clamped(val, 0, 120000);
+ if (NS_SUCCEEDED(rv) && mConnMgr) {
+ Unused << mConnMgr->UpdateParam(nsHttpConnectionMgr::THROTTLING_MAX_TIME,
+ mThrottleMaxTime);
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("send_window_size"))) {
+ Unused << Preferences::GetInt(HTTP_PREF("send_window_size"), &val);
+ mSendWindowSize = val >= 0 ? val : 0;
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("on_click_priority"))) {
+ Unused << Preferences::GetBool(HTTP_PREF("on_click_priority"),
+ &mUrgentStartEnabled);
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("tailing.enabled"))) {
+ Unused << Preferences::GetBool(HTTP_PREF("tailing.enabled"),
+ &mTailBlockingEnabled);
+ }
+ if (PREF_CHANGED(HTTP_PREF("tailing.delay-quantum"))) {
+ Unused << Preferences::GetInt(HTTP_PREF("tailing.delay-quantum"), &val);
+ mTailDelayQuantum = (uint32_t)clamped(val, 0, 60000);
+ }
+ if (PREF_CHANGED(HTTP_PREF("tailing.delay-quantum-after-domcontentloaded"))) {
+ Unused << Preferences::GetInt(
+ HTTP_PREF("tailing.delay-quantum-after-domcontentloaded"), &val);
+ mTailDelayQuantumAfterDCL = (uint32_t)clamped(val, 0, 60000);
+ }
+ if (PREF_CHANGED(HTTP_PREF("tailing.delay-max"))) {
+ Unused << Preferences::GetInt(HTTP_PREF("tailing.delay-max"), &val);
+ mTailDelayMax = (uint32_t)clamped(val, 0, 60000);
+ }
+ if (PREF_CHANGED(HTTP_PREF("tailing.total-max"))) {
+ Unused << Preferences::GetInt(HTTP_PREF("tailing.total-max"), &val);
+ mTailTotalMax = (uint32_t)clamped(val, 0, 60000);
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("focused_window_transaction_ratio"))) {
+ float ratio = 0;
+ rv = Preferences::GetFloat(HTTP_PREF("focused_window_transaction_ratio"),
+ &ratio);
+ if (NS_SUCCEEDED(rv)) {
+ if (ratio > 0 && ratio < 1) {
+ mFocusedWindowTransactionRatio = ratio;
+ } else {
+ NS_WARNING("Wrong value for focused_window_transaction_ratio");
+ }
+ }
+ }
+
+ //
+ // INTL options
+ //
+
+ if (PREF_CHANGED(INTL_ACCEPT_LANGUAGES)) {
+ // We don't want to set the new accept languages here since
+ // this pref is a complex type and it may be racy with flushing
+ // string resources.
+ mAcceptLanguagesIsDirty = true;
+ }
+
+ //
+ // Tracking options
+ //
+
+ // Hint option
+ if (PREF_CHANGED(SAFE_HINT_HEADER_VALUE)) {
+ cVar = false;
+ rv = Preferences::GetBool(SAFE_HINT_HEADER_VALUE, &cVar);
+ if (NS_SUCCEEDED(rv)) {
+ mSafeHintEnabled = cVar;
+ }
+ }
+
+ // toggle to true anytime a token bucket related pref is changed.. that
+ // includes telemetry and allow-experiments because of the abtest profile
+ bool requestTokenBucketUpdated = false;
+
+ // "security.ssl3.ecdhe_rsa_aes_128_gcm_sha256" is the required h2 interop
+ // suite.
+
+ if (PREF_CHANGED(H2MANDATORY_SUITE)) {
+ cVar = false;
+ rv = Preferences::GetBool(H2MANDATORY_SUITE, &cVar);
+ if (NS_SUCCEEDED(rv)) {
+ mH2MandatorySuiteEnabled = cVar;
+ }
+ }
+
+ // network.http.debug-observations
+ if (PREF_CHANGED("network.http.debug-observations")) {
+ cVar = false;
+ rv = Preferences::GetBool("network.http.debug-observations", &cVar);
+ if (NS_SUCCEEDED(rv)) {
+ mDebugObservations = cVar;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("pacing.requests.enabled"))) {
+ rv = Preferences::GetBool(HTTP_PREF("pacing.requests.enabled"), &cVar);
+ if (NS_SUCCEEDED(rv)) {
+ mRequestTokenBucketEnabled = cVar;
+ requestTokenBucketUpdated = true;
+ }
+ }
+ if (PREF_CHANGED(HTTP_PREF("pacing.requests.min-parallelism"))) {
+ rv =
+ Preferences::GetInt(HTTP_PREF("pacing.requests.min-parallelism"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mRequestTokenBucketMinParallelism =
+ static_cast<uint16_t>(clamped(val, 1, 1024));
+ requestTokenBucketUpdated = true;
+ }
+ }
+ if (PREF_CHANGED(HTTP_PREF("pacing.requests.hz"))) {
+ rv = Preferences::GetInt(HTTP_PREF("pacing.requests.hz"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mRequestTokenBucketHz = static_cast<uint32_t>(clamped(val, 1, 10000));
+ requestTokenBucketUpdated = true;
+ }
+ }
+ if (PREF_CHANGED(HTTP_PREF("pacing.requests.burst"))) {
+ rv = Preferences::GetInt(HTTP_PREF("pacing.requests.burst"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mRequestTokenBucketBurst = val ? val : 1;
+ requestTokenBucketUpdated = true;
+ }
+ }
+ if (requestTokenBucketUpdated) {
+ MakeNewRequestTokenBucket();
+ }
+
+ // Keepalive values for initial and idle connections.
+ if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.short_lived_connections"))) {
+ rv = Preferences::GetBool(
+ HTTP_PREF("tcp_keepalive.short_lived_connections"), &cVar);
+ if (NS_SUCCEEDED(rv) && cVar != mTCPKeepaliveShortLivedEnabled) {
+ mTCPKeepaliveShortLivedEnabled = cVar;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.short_lived_time"))) {
+ rv = Preferences::GetInt(HTTP_PREF("tcp_keepalive.short_lived_time"), &val);
+ if (NS_SUCCEEDED(rv) && val > 0)
+ mTCPKeepaliveShortLivedTimeS = clamped(val, 1, 300); // Max 5 mins.
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.short_lived_idle_time"))) {
+ rv = Preferences::GetInt(HTTP_PREF("tcp_keepalive.short_lived_idle_time"),
+ &val);
+ if (NS_SUCCEEDED(rv) && val > 0)
+ mTCPKeepaliveShortLivedIdleTimeS = clamped(val, 1, kMaxTCPKeepIdle);
+ }
+
+ // Keepalive values for Long-lived Connections.
+ if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.long_lived_connections"))) {
+ rv = Preferences::GetBool(HTTP_PREF("tcp_keepalive.long_lived_connections"),
+ &cVar);
+ if (NS_SUCCEEDED(rv) && cVar != mTCPKeepaliveLongLivedEnabled) {
+ mTCPKeepaliveLongLivedEnabled = cVar;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("tcp_keepalive.long_lived_idle_time"))) {
+ rv = Preferences::GetInt(HTTP_PREF("tcp_keepalive.long_lived_idle_time"),
+ &val);
+ if (NS_SUCCEEDED(rv) && val > 0)
+ mTCPKeepaliveLongLivedIdleTimeS = clamped(val, 1, kMaxTCPKeepIdle);
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("enforce-framing.http1")) ||
+ PREF_CHANGED(HTTP_PREF("enforce-framing.soft")) ||
+ PREF_CHANGED(HTTP_PREF("enforce-framing.strict_chunked_encoding"))) {
+ rv = Preferences::GetBool(HTTP_PREF("enforce-framing.http1"), &cVar);
+ if (NS_SUCCEEDED(rv) && cVar) {
+ mEnforceH1Framing = FRAMECHECK_STRICT;
+ } else {
+ rv = Preferences::GetBool(
+ HTTP_PREF("enforce-framing.strict_chunked_encoding"), &cVar);
+ if (NS_SUCCEEDED(rv) && cVar) {
+ mEnforceH1Framing = FRAMECHECK_STRICT_CHUNKED;
+ } else {
+ rv = Preferences::GetBool(HTTP_PREF("enforce-framing.soft"), &cVar);
+ if (NS_SUCCEEDED(rv) && cVar) {
+ mEnforceH1Framing = FRAMECHECK_BARELY;
+ } else {
+ mEnforceH1Framing = FRAMECHECK_LAX;
+ }
+ }
+ }
+ }
+
+ if (PREF_CHANGED(TCP_FAST_OPEN_ENABLE)) {
+ rv = Preferences::GetBool(TCP_FAST_OPEN_ENABLE, &cVar);
+ if (NS_SUCCEEDED(rv)) {
+ mUseFastOpen = cVar;
+ }
+ }
+
+ if (PREF_CHANGED(TCP_FAST_OPEN_FAILURE_LIMIT)) {
+ rv = Preferences::GetInt(TCP_FAST_OPEN_FAILURE_LIMIT, &val);
+ if (NS_SUCCEEDED(rv)) {
+ if (val < 0) {
+ val = 0;
+ }
+ mFastOpenConsecutiveFailureLimit = val;
+ }
+ }
+
+ if (PREF_CHANGED(TCP_FAST_OPEN_STALLS_LIMIT)) {
+ rv = Preferences::GetInt(TCP_FAST_OPEN_STALLS_LIMIT, &val);
+ if (NS_SUCCEEDED(rv)) {
+ if (val < 0) {
+ val = 0;
+ }
+ mFastOpenStallsLimit = val;
+ }
+ }
+
+ if (PREF_CHANGED(TCP_FAST_OPEN_STALLS_TIMEOUT)) {
+ rv = Preferences::GetInt(TCP_FAST_OPEN_STALLS_TIMEOUT, &val);
+ if (NS_SUCCEEDED(rv)) {
+ if (val < 0) {
+ val = 0;
+ }
+ mFastOpenStallsTimeout = val;
+ }
+ }
+
+ if (PREF_CHANGED(TCP_FAST_OPEN_STALLS_IDLE)) {
+ rv = Preferences::GetInt(TCP_FAST_OPEN_STALLS_IDLE, &val);
+ if (NS_SUCCEEDED(rv)) {
+ if (val < 0) {
+ val = 0;
+ }
+ mFastOpenStallsIdleTime = val;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.default-hpack-buffer"))) {
+ rv = Preferences::GetInt(HTTP_PREF("spdy.default-hpack-buffer"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mDefaultHpackBuffer = val;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("spdy.bug1563538"))) {
+ rv = Preferences::GetBool(HTTP_PREF("spdy.bug1563538"), &cVar);
+ if (NS_SUCCEEDED(rv)) {
+ mBug1563538 = cVar;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("http3.enabled"))) {
+ rv = Preferences::GetBool(HTTP_PREF("http3.enabled"), &cVar);
+ if (NS_SUCCEEDED(rv)) {
+ mHttp3Enabled = cVar;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("http3.default-qpack-table-size"))) {
+ rv = Preferences::GetInt(HTTP_PREF("http3.default-qpack-table-size"), &val);
+ if (NS_SUCCEEDED(rv)) {
+ mQpackTableSize = val;
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("http3.default-max-stream-blocked"))) {
+ rv = Preferences::GetInt(HTTP_PREF("http3.default-max-stream-blocked"),
+ &val);
+ if (NS_SUCCEEDED(rv)) {
+ mHttp3MaxBlockedStreams = clamped(val, 0, 0xffff);
+ }
+ }
+
+ const bool imageAcceptPrefChanged = PREF_CHANGED("image.http.accept") ||
+ PREF_CHANGED("image.avif.enabled") ||
+ PREF_CHANGED("image.webp.enabled");
+
+ if (imageAcceptPrefChanged) {
+ nsAutoCString userSetImageAcceptHeader;
+
+ if (Preferences::HasUserValue("image.http.accept")) {
+ rv = Preferences::GetCString("image.http.accept",
+ userSetImageAcceptHeader);
+ if (NS_FAILED(rv)) {
+ userSetImageAcceptHeader.Truncate();
+ }
+ }
+
+ if (userSetImageAcceptHeader.IsEmpty()) {
+ mImageAcceptHeader.Assign(ImageAcceptHeader());
+ } else {
+ mImageAcceptHeader.Assign(userSetImageAcceptHeader);
+ }
+ }
+
+ if (PREF_CHANGED("network.http.accept") || imageAcceptPrefChanged) {
+ nsAutoCString userSetDocumentAcceptHeader;
+
+ if (Preferences::HasUserValue("network.http.accept")) {
+ rv = Preferences::GetCString("network.http.accept",
+ userSetDocumentAcceptHeader);
+ if (NS_FAILED(rv)) {
+ userSetDocumentAcceptHeader.Truncate();
+ }
+ }
+
+ if (userSetDocumentAcceptHeader.IsEmpty()) {
+ mDocumentAcceptHeader.Assign(DocumentAcceptHeader(mImageAcceptHeader));
+ } else {
+ mDocumentAcceptHeader.Assign(userSetDocumentAcceptHeader);
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("http3.alt-svc-mapping-for-testing"))) {
+ nsAutoCString altSvcMappings;
+ rv = Preferences::GetCString(HTTP_PREF("http3.alt-svc-mapping-for-testing"),
+ altSvcMappings);
+ if (NS_SUCCEEDED(rv)) {
+ for (const nsACString& tokenSubstring :
+ nsCCharSeparatedTokenizer(altSvcMappings, ',').ToRange()) {
+ nsAutoCString token{tokenSubstring};
+ int32_t index = token.Find(";");
+ if (index != kNotFound) {
+ auto* map = new nsCString(Substring(token, index + 1));
+ mAltSvcMappingTemptativeMap.Put(Substring(token, 0, index), map);
+ }
+ }
+ }
+ }
+
+ if (PREF_CHANGED(HTTP_PREF("http3.enable_qlog"))) {
+ // Initialize the directory.
+ nsCOMPtr<nsIFile> qlogDir;
+ if (Preferences::GetBool(HTTP_PREF("http3.enable_qlog")) &&
+ !mHttp3QlogDir.IsEmpty() &&
+ NS_SUCCEEDED(NS_NewNativeLocalFile(mHttp3QlogDir, false,
+ getter_AddRefs(qlogDir)))) {
+ // Here we do main thread IO, but since this only happens
+ // when enabling a developer feature it's not a problem for users.
+ rv = qlogDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS) {
+ NS_WARNING("Creating qlog dir failed");
+ }
+ }
+ }
+
+ // Enable HTTP response timeout if TCP Keepalives are disabled.
+ mResponseTimeoutEnabled =
+ !mTCPKeepaliveShortLivedEnabled && !mTCPKeepaliveLongLivedEnabled;
+
+#undef PREF_CHANGED
+#undef MULTI_PREF_CHANGED
+}
+
+/**
+ * Allocates a C string into that contains a ISO 639 language list
+ * notated with HTTP "q" values for output with a HTTP Accept-Language
+ * header. Previous q values will be stripped because the order of
+ * the langs imply the q value. The q values are calculated by dividing
+ * 1.0 amongst the number of languages present.
+ *
+ * Ex: passing: "en, ja"
+ * returns: "en,ja;q=0.5"
+ *
+ * passing: "en, ja, fr_CA"
+ * returns: "en,ja;q=0.7,fr_CA;q=0.3"
+ */
+static nsresult PrepareAcceptLanguages(const char* i_AcceptLanguages,
+ nsACString& o_AcceptLanguages) {
+ if (!i_AcceptLanguages) return NS_OK;
+
+ const nsAutoCString ns_accept_languages(i_AcceptLanguages);
+ return rust_prepare_accept_languages(&ns_accept_languages,
+ &o_AcceptLanguages);
+}
+
+nsresult nsHttpHandler::SetAcceptLanguages() {
+ if (!NS_IsMainThread()) {
+ nsCOMPtr<nsIThread> mainThread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Forward to the main thread synchronously.
+ SyncRunnable::DispatchToThread(
+ mainThread, new SyncRunnable(NS_NewRunnableFunction(
+ "nsHttpHandler::SetAcceptLanguages",
+ [&rv]() { rv = gHttpHandler->SetAcceptLanguages(); })));
+ return rv;
+ }
+
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mAcceptLanguagesIsDirty = false;
+
+ nsAutoCString acceptLanguages;
+ Preferences::GetLocalizedCString(INTL_ACCEPT_LANGUAGES, acceptLanguages);
+
+ nsAutoCString buf;
+ nsresult rv = PrepareAcceptLanguages(acceptLanguages.get(), buf);
+ if (NS_SUCCEEDED(rv)) {
+ mAcceptLanguages.Assign(buf);
+ }
+ return rv;
+}
+
+nsresult nsHttpHandler::SetAcceptEncodings(const char* aAcceptEncodings,
+ bool isSecure) {
+ if (isSecure) {
+ mHttpsAcceptEncodings = aAcceptEncodings;
+ } else {
+ // use legacy list if a secure override is not specified
+ mHttpAcceptEncodings = aAcceptEncodings;
+ if (mHttpsAcceptEncodings.IsEmpty()) {
+ mHttpsAcceptEncodings = aAcceptEncodings;
+ }
+ }
+
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsHttpHandler::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(nsHttpHandler, nsIHttpProtocolHandler,
+ nsIProxiedProtocolHandler, nsIProtocolHandler, nsIObserver,
+ nsISupportsWeakReference, nsISpeculativeConnect)
+
+//-----------------------------------------------------------------------------
+// nsHttpHandler::nsIProtocolHandler
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsHttpHandler::GetScheme(nsACString& aScheme) {
+ aScheme.AssignLiteral("http");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::GetDefaultPort(int32_t* result) {
+ *result = NS_HTTP_DEFAULT_PORT;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::GetProtocolFlags(uint32_t* result) {
+ *result = NS_HTTP_PROTOCOL_FLAGS;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::NewChannel(nsIURI* uri, nsILoadInfo* aLoadInfo,
+ nsIChannel** result) {
+ LOG(("nsHttpHandler::NewChannel\n"));
+
+ NS_ENSURE_ARG_POINTER(uri);
+ NS_ENSURE_ARG_POINTER(result);
+
+ // Verify that we have been given a valid scheme
+ if (!uri->SchemeIs("http") && !uri->SchemeIs("https")) {
+ NS_WARNING("Invalid URI scheme");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NewProxiedChannel(uri, nullptr, 0, nullptr, aLoadInfo, result);
+}
+
+NS_IMETHODIMP
+nsHttpHandler::AllowPort(int32_t port, const char* scheme, bool* _retval) {
+ // don't override anything.
+ *_retval = false;
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsHttpHandler::nsIProxiedProtocolHandler
+//-----------------------------------------------------------------------------
+
+nsresult nsHttpHandler::SetupChannelInternal(
+ HttpBaseChannel* aChannel, nsIURI* uri, nsIProxyInfo* givenProxyInfo,
+ uint32_t proxyResolveFlags, nsIURI* proxyURI, nsILoadInfo* aLoadInfo,
+ nsIChannel** result) {
+ RefPtr<HttpBaseChannel> httpChannel = aChannel;
+
+#ifdef MOZ_TASK_TRACER
+ if (tasktracer::IsStartLogging()) {
+ nsAutoCString urispec;
+ uri->GetSpec(urispec);
+ tasktracer::AddLabel("nsHttpHandler::NewProxiedChannel2 %s", urispec.get());
+ }
+#endif
+
+ nsCOMPtr<nsProxyInfo> proxyInfo;
+ if (givenProxyInfo) {
+ proxyInfo = do_QueryInterface(givenProxyInfo);
+ NS_ENSURE_ARG(proxyInfo);
+ }
+
+ uint32_t caps = mCapabilities;
+
+ uint64_t channelId;
+ nsresult rv = NewChannelId(channelId);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = httpChannel->Init(uri, caps, proxyInfo, proxyResolveFlags, proxyURI,
+ channelId, aLoadInfo->GetExternalContentPolicyType());
+ if (NS_FAILED(rv)) return rv;
+
+ // TRRServiceChannel doesn't need loadInfo.
+ if (aLoadInfo) {
+ // set the loadInfo on the new channel
+ rv = httpChannel->SetLoadInfo(aLoadInfo);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ httpChannel.forget(result);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::NewProxiedChannel(nsIURI* uri, nsIProxyInfo* givenProxyInfo,
+ uint32_t proxyResolveFlags, nsIURI* proxyURI,
+ nsILoadInfo* aLoadInfo, nsIChannel** result) {
+ HttpBaseChannel* httpChannel;
+
+ LOG(("nsHttpHandler::NewProxiedChannel [proxyInfo=%p]\n", givenProxyInfo));
+
+ if (IsNeckoChild()) {
+ httpChannel = new HttpChannelChild();
+ } else {
+ // HACK: make sure PSM gets initialized on the main thread.
+ net_EnsurePSMInit();
+ httpChannel = new nsHttpChannel();
+ }
+
+ return SetupChannelInternal(httpChannel, uri, givenProxyInfo,
+ proxyResolveFlags, proxyURI, aLoadInfo, result);
+}
+
+nsresult nsHttpHandler::CreateTRRServiceChannel(
+ nsIURI* uri, nsIProxyInfo* givenProxyInfo, uint32_t proxyResolveFlags,
+ nsIURI* proxyURI, nsILoadInfo* aLoadInfo, nsIChannel** result) {
+ HttpBaseChannel* httpChannel = new TRRServiceChannel();
+
+ LOG(("nsHttpHandler::CreateTRRServiceChannel [proxyInfo=%p]\n",
+ givenProxyInfo));
+
+ return SetupChannelInternal(httpChannel, uri, givenProxyInfo,
+ proxyResolveFlags, proxyURI, aLoadInfo, result);
+}
+
+//-----------------------------------------------------------------------------
+// nsHttpHandler::nsIHttpProtocolHandler
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsHttpHandler::GetUserAgent(nsACString& value) {
+ value = UserAgent();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::GetAppName(nsACString& value) {
+ value = mLegacyAppName;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::GetAppVersion(nsACString& value) {
+ value = mLegacyAppVersion;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::GetPlatform(nsACString& value) {
+ value = mPlatform;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::GetOscpu(nsACString& value) {
+ value = mOscpu;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::GetMisc(nsACString& value) {
+ value = mMisc;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::GetAltSvcCacheKeys(nsTArray<nsCString>& value) {
+ return mAltSvcCache->GetAltSvcCacheKeys(value);
+}
+
+NS_IMETHODIMP
+nsHttpHandler::GetAuthCacheKeys(nsTArray<nsCString>& aValues) {
+ mAuthCache.CollectKeys(aValues);
+ mPrivateAuthCache.CollectKeys(aValues);
+ return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsHttpHandler::nsIObserver
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsHttpHandler::Observe(nsISupports* subject, const char* topic,
+ const char16_t* data) {
+ MOZ_ASSERT(NS_IsMainThread());
+ LOG(("nsHttpHandler::Observe [topic=\"%s\"]\n", topic));
+
+ nsresult rv;
+ if (!strcmp(topic, "profile-change-net-teardown") ||
+ !strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+ mHandlerActive = false;
+
+ // clear cache of all authentication credentials.
+ mAuthCache.ClearAll();
+ mPrivateAuthCache.ClearAll();
+ if (mWifiTickler) mWifiTickler->Cancel();
+
+ // Inform nsIOService that network is tearing down.
+ gIOService->SetHttpHandlerAlreadyShutingDown();
+
+ ShutdownConnectionManager();
+
+ // need to reset the session start time since cache validation may
+ // depend on this value.
+ mSessionStartTime = NowInSeconds();
+
+ // Since nsHttpHandler::Observe() is also called in socket process, we don't
+ // want to do telemetry twice.
+ if (XRE_IsParentProcess()) {
+ if (!StaticPrefs::privacy_donottrackheader_enabled()) {
+ Telemetry::Accumulate(Telemetry::DNT_USAGE, 2);
+ } else {
+ Telemetry::Accumulate(Telemetry::DNT_USAGE, 1);
+ }
+
+ if (UseFastOpen()) {
+ Telemetry::Accumulate(Telemetry::TCP_FAST_OPEN_STATUS, 0);
+ } else if (!mFastOpenSupported) {
+ Telemetry::Accumulate(Telemetry::TCP_FAST_OPEN_STATUS, 1);
+ } else if (!mUseFastOpen) {
+ Telemetry::Accumulate(Telemetry::TCP_FAST_OPEN_STATUS, 2);
+ } else if (mFastOpenConsecutiveFailureCounter >=
+ mFastOpenConsecutiveFailureLimit) {
+ Telemetry::Accumulate(Telemetry::TCP_FAST_OPEN_STATUS, 3);
+ } else {
+ Telemetry::Accumulate(Telemetry::TCP_FAST_OPEN_STATUS, 4);
+ }
+ }
+ } else if (!strcmp(topic, "profile-change-net-restore")) {
+ // initialize connection manager
+ rv = InitConnectionMgr();
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ mAltSvcCache = MakeUnique<AltSvcCache>();
+ } else if (!strcmp(topic, "net:clear-active-logins")) {
+ mAuthCache.ClearAll();
+ mPrivateAuthCache.ClearAll();
+ } else if (!strcmp(topic, "net:cancel-all-connections")) {
+ if (mConnMgr) {
+ mConnMgr->AbortAndCloseAllConnections(0, nullptr);
+ }
+ } else if (!strcmp(topic, "net:prune-dead-connections")) {
+ if (mConnMgr) {
+ rv = mConnMgr->PruneDeadConnections();
+ if (NS_FAILED(rv)) {
+ LOG((" PruneDeadConnections failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ }
+ } else if (!strcmp(topic, "net:prune-all-connections")) {
+ if (mConnMgr) {
+ rv = mConnMgr->DoShiftReloadConnectionCleanup();
+ if (NS_FAILED(rv)) {
+ LOG((" DoShiftReloadConnectionCleanup failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ rv = mConnMgr->PruneDeadConnections();
+ if (NS_FAILED(rv)) {
+ LOG((" PruneDeadConnections failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ }
+#if 0
+ } else if (!strcmp(topic, "net:failed-to-process-uri-content")) {
+ // nop right now - we used to cancel h1 pipelines based on this,
+ // but those are no longer implemented
+ nsCOMPtr<nsIURI> uri = do_QueryInterface(subject);
+#endif
+ } else if (!strcmp(topic, "last-pb-context-exited")) {
+ mPrivateAuthCache.ClearAll();
+ if (mAltSvcCache) {
+ mAltSvcCache->ClearAltServiceMappings();
+ }
+ } else if (!strcmp(topic, "browser:purge-session-history")) {
+ if (mConnMgr) {
+ Unused << mConnMgr->ClearConnectionHistory();
+ }
+ if (mAltSvcCache) {
+ mAltSvcCache->ClearAltServiceMappings();
+ }
+ } else if (!strcmp(topic, NS_NETWORK_LINK_TOPIC)) {
+ nsAutoCString converted = NS_ConvertUTF16toUTF8(data);
+ if (!strcmp(converted.get(), NS_NETWORK_LINK_DATA_CHANGED)) {
+ if (mConnMgr) {
+ rv = mConnMgr->PruneDeadConnections();
+ if (NS_FAILED(rv)) {
+ LOG((" PruneDeadConnections failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ rv = mConnMgr->VerifyTraffic();
+ if (NS_FAILED(rv)) {
+ LOG((" VerifyTraffic failed (%08x)\n", static_cast<uint32_t>(rv)));
+ }
+ }
+ }
+ } else if (!strcmp(topic, "application-background")) {
+ // going to the background on android means we should close
+ // down idle connections for power conservation
+ if (mConnMgr) {
+ rv = mConnMgr->DoShiftReloadConnectionCleanup();
+ if (NS_FAILED(rv)) {
+ LOG((" DoShiftReloadConnectionCleanup failed (%08x)\n",
+ static_cast<uint32_t>(rv)));
+ }
+ }
+ } else if (!strcmp(topic, "net:current-toplevel-outer-content-windowid")) {
+ // The window id will be updated by HttpConnectionMgrParent.
+ if (XRE_IsParentProcess()) {
+ nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(subject);
+ MOZ_RELEASE_ASSERT(wrapper);
+
+ uint64_t windowId = 0;
+ wrapper->GetData(&windowId);
+ MOZ_ASSERT(windowId);
+
+ static uint64_t sCurrentTopLevelOuterContentWindowId = 0;
+ if (sCurrentTopLevelOuterContentWindowId != windowId) {
+ sCurrentTopLevelOuterContentWindowId = windowId;
+ if (mConnMgr) {
+ mConnMgr->UpdateCurrentTopLevelOuterContentWindowId(
+ sCurrentTopLevelOuterContentWindowId);
+ }
+ }
+ }
+ } else if (!strcmp(topic, "captive-portal-login") ||
+ !strcmp(topic, "captive-portal-login-success")) {
+ // We have detected a captive portal and we will reset the Fast Open
+ // failure counter.
+ ResetFastOpenConsecutiveFailureCounter();
+ } else if (!strcmp(topic, "psm:user-certificate-added")) {
+ // A user certificate has just been added.
+ // We should immediately disable speculative connect
+ mSpeculativeConnectEnabled = false;
+ } else if (!strcmp(topic, "psm:user-certificate-deleted")) {
+ // If a user certificate has been removed, we need to check if there
+ // are others installed
+ MaybeEnableSpeculativeConnect();
+ } else if (!strcmp(topic, "intl:app-locales-changed")) {
+ // If the locale changed, there's a chance the accept language did too
+ mAcceptLanguagesIsDirty = true;
+ } else if (!strcmp(topic, "browser-delayed-startup-finished")) {
+ MaybeEnableSpeculativeConnect();
+ } else if (!strcmp(topic, "network:captive-portal-connectivity")) {
+ nsAutoCString data8 = NS_ConvertUTF16toUTF8(data);
+ mThroughCaptivePortal = data8.EqualsLiteral("captive");
+ } else if (!strcmp(topic, "network:reset-http3-excluded-list")) {
+ MutexAutoLock lock(mHttpExclusionLock);
+ mExcludedHttp3Origins.Clear();
+ }
+
+ return NS_OK;
+}
+
+// nsISpeculativeConnect
+
+static bool CanEnableSpeculativeConnect() {
+ nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
+
+ MOZ_ASSERT(!NS_IsMainThread(), "Must run on the background thread");
+ // Check if any 3rd party PKCS#11 module are installed, as they may produce
+ // client certificates
+ bool activeSmartCards = false;
+ nsresult rv = component->HasActiveSmartCards(&activeSmartCards);
+ if (NS_FAILED(rv) || activeSmartCards) {
+ return false;
+ }
+
+ // If there are any client certificates installed, we can't enable speculative
+ // connect, as it may pop up the certificate chooser at any time.
+ bool hasUserCerts = false;
+ rv = component->HasUserCertsInstalled(&hasUserCerts);
+ if (NS_FAILED(rv) || hasUserCerts) {
+ return false;
+ }
+
+ // No smart cards and no client certificates means
+ // we can enable speculative connect.
+ return true;
+}
+
+void nsHttpHandler::MaybeEnableSpeculativeConnect() {
+ MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
+
+ // We don't need to and can't check this in the child and socket process.
+ if (!XRE_IsParentProcess()) {
+ return;
+ }
+
+ net_EnsurePSMInit();
+
+ NS_DispatchBackgroundTask(
+ NS_NewRunnableFunction("CanEnableSpeculativeConnect", [] {
+ gHttpHandler->mSpeculativeConnectEnabled =
+ CanEnableSpeculativeConnect();
+ }));
+}
+
+nsresult nsHttpHandler::SpeculativeConnectInternal(
+ nsIURI* aURI, nsIPrincipal* aPrincipal, nsIInterfaceRequestor* aCallbacks,
+ bool anonymous) {
+ if (IsNeckoChild()) {
+ gNeckoChild->SendSpeculativeConnect(aURI, aPrincipal, anonymous);
+ return NS_OK;
+ }
+
+ if (!mHandlerActive) return NS_OK;
+
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
+ if (mDebugObservations && obsService) {
+ // this is basically used for test coverage of an otherwise 'hintable'
+ // feature
+ obsService->NotifyObservers(nullptr, "speculative-connect-request",
+ nullptr);
+ for (auto* cp :
+ dom::ContentParent::AllProcesses(dom::ContentParent::eLive)) {
+ PNeckoParent* neckoParent =
+ SingleManagedOrNull(cp->ManagedPNeckoParent());
+ if (!neckoParent) {
+ continue;
+ }
+ Unused << neckoParent->SendSpeculativeConnectRequest();
+ }
+ }
+
+ nsISiteSecurityService* sss = gHttpHandler->GetSSService();
+ bool isStsHost = false;
+ if (!sss) return NS_OK;
+
+ nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(aCallbacks);
+ uint32_t flags = 0;
+ if (loadContext && loadContext->UsePrivateBrowsing())
+ flags |= nsISocketProvider::NO_PERMANENT_STORAGE;
+
+ OriginAttributes originAttributes;
+ // If the principal is given, we use the originAttributes from this
+ // principal. Otherwise, we use the originAttributes from the loadContext.
+ if (aPrincipal) {
+ originAttributes = aPrincipal->OriginAttributesRef();
+ } else if (loadContext) {
+ loadContext->GetOriginAttributes(originAttributes);
+ }
+
+ StoragePrincipalHelper::UpdateOriginAttributesForNetworkState(
+ aURI, originAttributes);
+
+ nsCOMPtr<nsIURI> clone;
+ if (NS_SUCCEEDED(sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI,
+ flags, originAttributes, nullptr, nullptr,
+ &isStsHost)) &&
+ isStsHost) {
+ if (NS_SUCCEEDED(NS_GetSecureUpgradedURI(aURI, getter_AddRefs(clone)))) {
+ aURI = clone.get();
+ // (NOTE: We better make sure |clone| stays alive until the end
+ // of the function now, since our aURI arg now points to it!)
+ }
+ }
+
+ nsAutoCString scheme;
+ nsresult rv = aURI->GetScheme(scheme);
+ if (NS_FAILED(rv)) return rv;
+
+ // If this is HTTPS, make sure PSM is initialized as the channel
+ // creation path may have been bypassed
+ if (scheme.EqualsLiteral("https")) {
+ if (!IsNeckoChild()) {
+ // make sure PSM gets initialized on the main thread.
+ net_EnsurePSMInit();
+ }
+ }
+ // Ensure that this is HTTP or HTTPS, otherwise we don't do preconnect here
+ else if (!scheme.EqualsLiteral("http"))
+ return NS_ERROR_UNEXPECTED;
+
+ // Construct connection info object
+ if (aURI->SchemeIs("https") && !mSpeculativeConnectEnabled) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsAutoCString host;
+ rv = aURI->GetAsciiHost(host);
+ if (NS_FAILED(rv)) return rv;
+
+ int32_t port = -1;
+ rv = aURI->GetPort(&port);
+ if (NS_FAILED(rv)) return rv;
+
+ nsAutoCString username;
+ aURI->GetUsername(username);
+
+ // TODO For now pass ""_ns for topWindowOrigin for all speculative
+ // connection attempts, but ideally we should pass the accurate top window
+ // origin here. This would require updating the nsISpeculativeConnect API
+ // and all of its consumers.
+ RefPtr<nsHttpConnectionInfo> ci =
+ new nsHttpConnectionInfo(host, port, ""_ns, username, ""_ns, nullptr,
+ originAttributes, aURI->SchemeIs("https"));
+ ci->SetAnonymous(anonymous);
+
+ return SpeculativeConnect(ci, aCallbacks);
+}
+
+NS_IMETHODIMP
+nsHttpHandler::SpeculativeConnect(nsIURI* aURI, nsIPrincipal* aPrincipal,
+ nsIInterfaceRequestor* aCallbacks) {
+ return SpeculativeConnectInternal(aURI, aPrincipal, aCallbacks, false);
+}
+
+NS_IMETHODIMP
+nsHttpHandler::SpeculativeAnonymousConnect(nsIURI* aURI,
+ nsIPrincipal* aPrincipal,
+ nsIInterfaceRequestor* aCallbacks) {
+ return SpeculativeConnectInternal(aURI, aPrincipal, aCallbacks, true);
+}
+
+void nsHttpHandler::TickleWifi(nsIInterfaceRequestor* cb) {
+ if (!cb || !mWifiTickler) return;
+
+ // If B2G requires a similar mechanism nsINetworkManager, currently only avail
+ // on B2G, contains the necessary information on wifi and gateway
+
+ nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(cb);
+ nsCOMPtr<nsPIDOMWindowOuter> piWindow = do_QueryInterface(domWindow);
+ if (!piWindow) return;
+
+ RefPtr<dom::Navigator> navigator = piWindow->GetNavigator();
+ if (!navigator) return;
+
+ RefPtr<dom::network::Connection> networkProperties =
+ navigator->GetConnection(IgnoreErrors());
+ if (!networkProperties) return;
+
+ uint32_t gwAddress = networkProperties->GetDhcpGateway();
+ bool isWifi = networkProperties->GetIsWifi();
+ if (!gwAddress || !isWifi) return;
+
+ mWifiTickler->SetIPV4Address(gwAddress);
+ mWifiTickler->Tickle();
+}
+
+//-----------------------------------------------------------------------------
+// nsHttpsHandler implementation
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS(nsHttpsHandler, nsIHttpProtocolHandler,
+ nsIProxiedProtocolHandler, nsIProtocolHandler,
+ nsISupportsWeakReference, nsISpeculativeConnect)
+
+nsresult nsHttpsHandler::Init() {
+ nsCOMPtr<nsIProtocolHandler> httpHandler(
+ do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http"));
+ MOZ_ASSERT(httpHandler.get() != nullptr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpsHandler::GetScheme(nsACString& aScheme) {
+ aScheme.AssignLiteral("https");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpsHandler::GetDefaultPort(int32_t* aPort) {
+ *aPort = NS_HTTPS_DEFAULT_PORT;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpsHandler::GetProtocolFlags(uint32_t* aProtocolFlags) {
+ *aProtocolFlags = NS_HTTP_PROTOCOL_FLAGS | URI_IS_POTENTIALLY_TRUSTWORTHY;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpsHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo,
+ nsIChannel** _retval) {
+ MOZ_ASSERT(gHttpHandler);
+ if (!gHttpHandler) return NS_ERROR_UNEXPECTED;
+ return gHttpHandler->NewChannel(aURI, aLoadInfo, _retval);
+}
+
+NS_IMETHODIMP
+nsHttpsHandler::AllowPort(int32_t aPort, const char* aScheme, bool* _retval) {
+ // don't override anything.
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::EnsureHSTSDataReadyNative(
+ RefPtr<mozilla::net::HSTSDataCallbackWrapper> aCallback) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), "http://example.com");
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool shouldUpgrade = false;
+ bool willCallback = false;
+ OriginAttributes originAttributes;
+ auto func = [callback(aCallback)](bool aResult, nsresult aStatus) {
+ callback->DoCallback(aResult);
+ };
+ rv = NS_ShouldSecureUpgrade(uri, nullptr, nullptr, false, false,
+ originAttributes, shouldUpgrade, std::move(func),
+ willCallback);
+ if (NS_FAILED(rv) || !willCallback) {
+ aCallback->DoCallback(false);
+ return rv;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsHttpHandler::EnsureHSTSDataReady(JSContext* aCx, Promise** aPromise) {
+ if (NS_WARN_IF(!aCx)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
+ if (NS_WARN_IF(!globalObject)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ ErrorResult result;
+ RefPtr<Promise> promise = Promise::Create(globalObject, result);
+ if (NS_WARN_IF(result.Failed())) {
+ return result.StealNSResult();
+ }
+
+ if (IsNeckoChild()) {
+ gNeckoChild->SendEnsureHSTSData()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [promise(promise)](
+ NeckoChild::EnsureHSTSDataPromise::ResolveOrRejectValue&& aResult) {
+ if (aResult.IsResolve()) {
+ promise->MaybeResolve(aResult.ResolveValue());
+ } else {
+ promise->MaybeReject(NS_ERROR_FAILURE);
+ }
+ });
+ promise.forget(aPromise);
+ return NS_OK;
+ }
+
+ auto callback = [promise(promise)](bool aResult) {
+ promise->MaybeResolve(aResult);
+ };
+
+ RefPtr<HSTSDataCallbackWrapper> wrapper =
+ new HSTSDataCallbackWrapper(std::move(callback));
+ promise.forget(aPromise);
+ return EnsureHSTSDataReadyNative(wrapper);
+}
+
+void nsHttpHandler::ShutdownConnectionManager() {
+ // ensure connection manager is shutdown
+ if (mConnMgr) {
+ nsresult rv = mConnMgr->Shutdown();
+ if (NS_FAILED(rv)) {
+ LOG(
+ ("nsHttpHandler::ShutdownConnectionManager\n"
+ " failed to shutdown connection manager\n"));
+ }
+ }
+}
+
+nsresult nsHttpHandler::NewChannelId(uint64_t& channelId) {
+ channelId =
+ // channelId is sometimes passed to JavaScript code (e.g. devtools),
+ // and since on Linux PID_MAX_LIMIT is 2^22 we cannot
+ // shift PID more than 31 bits left. Otherwise resulting values
+ // will be exceed safe JavaScript integer range.
+ ((static_cast<uint64_t>(mProcessId) << 31) & 0xFFFFFFFF80000000LL) |
+ mNextChannelId++;
+ return NS_OK;
+}
+
+void nsHttpHandler::NotifyActiveTabLoadOptimization() {
+ SetLastActiveTabLoadOptimizationHit(TimeStamp::Now());
+}
+
+TimeStamp const nsHttpHandler::GetLastActiveTabLoadOptimizationHit() {
+ MutexAutoLock lock(mLastActiveTabLoadOptimizationLock);
+
+ return mLastActiveTabLoadOptimizationHit;
+}
+
+void nsHttpHandler::SetLastActiveTabLoadOptimizationHit(TimeStamp const& when) {
+ MutexAutoLock lock(mLastActiveTabLoadOptimizationLock);
+
+ if (mLastActiveTabLoadOptimizationHit.IsNull() ||
+ (!when.IsNull() && mLastActiveTabLoadOptimizationHit < when)) {
+ mLastActiveTabLoadOptimizationHit = when;
+ }
+}
+
+bool nsHttpHandler::IsBeforeLastActiveTabLoadOptimization(
+ TimeStamp const& when) {
+ MutexAutoLock lock(mLastActiveTabLoadOptimizationLock);
+
+ return !mLastActiveTabLoadOptimizationHit.IsNull() &&
+ when <= mLastActiveTabLoadOptimizationHit;
+}
+
+void nsHttpHandler::ExcludeHttp2(const nsHttpConnectionInfo* ci) {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ mConnMgr->ExcludeHttp2(ci);
+ if (!mExcludedHttp2Origins.Contains(ci->GetOrigin())) {
+ MutexAutoLock lock(mHttpExclusionLock);
+ mExcludedHttp2Origins.PutEntry(ci->GetOrigin());
+ }
+}
+
+bool nsHttpHandler::IsHttp2Excluded(const nsHttpConnectionInfo* ci) {
+ MutexAutoLock lock(mHttpExclusionLock);
+ return mExcludedHttp2Origins.Contains(ci->GetOrigin());
+}
+
+void nsHttpHandler::ExcludeHttp3(const nsHttpConnectionInfo* ci) {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ mConnMgr->ExcludeHttp3(ci);
+ if (!mExcludedHttp3Origins.Contains(ci->GetRoutedHost())) {
+ MutexAutoLock lock(mHttpExclusionLock);
+ mExcludedHttp3Origins.PutEntry(ci->GetRoutedHost());
+ }
+}
+
+bool nsHttpHandler::IsHttp3Excluded(const nsACString& aRoutedHost) {
+ MutexAutoLock lock(mHttpExclusionLock);
+ return mExcludedHttp3Origins.Contains(aRoutedHost);
+}
+
+HttpTrafficAnalyzer* nsHttpHandler::GetHttpTrafficAnalyzer() {
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ if (!StaticPrefs::network_traffic_analyzer_enabled()) {
+ return nullptr;
+ }
+
+ return &mHttpTrafficAnalyzer;
+}
+
+bool nsHttpHandler::IsHttp3VersionSupported(const nsACString& version) {
+ for (uint32_t i = 0; i < kHttp3VersionCount; i++) {
+ if (version.Equals(kHttp3Versions[i])) {
+ return true;
+ }
+ }
+ return false;
+}
+
+nsresult nsHttpHandler::InitiateTransaction(HttpTransactionShell* aTrans,
+ int32_t aPriority) {
+ return mConnMgr->AddTransaction(aTrans, aPriority);
+}
+nsresult nsHttpHandler::InitiateTransactionWithStickyConn(
+ HttpTransactionShell* aTrans, int32_t aPriority,
+ HttpTransactionShell* aTransWithStickyConn) {
+ return mConnMgr->AddTransactionWithStickyConn(aTrans, aPriority,
+ aTransWithStickyConn);
+}
+
+nsresult nsHttpHandler::RescheduleTransaction(HttpTransactionShell* trans,
+ int32_t priority) {
+ return mConnMgr->RescheduleTransaction(trans, priority);
+}
+
+void nsHttpHandler::UpdateClassOfServiceOnTransaction(
+ HttpTransactionShell* trans, uint32_t classOfService) {
+ mConnMgr->UpdateClassOfServiceOnTransaction(trans, classOfService);
+}
+
+nsresult nsHttpHandler::CancelTransaction(HttpTransactionShell* trans,
+ nsresult reason) {
+ return mConnMgr->CancelTransaction(trans, reason);
+}
+
+void nsHttpHandler::AddHttpChannel(uint64_t aId, nsISupports* aChannel) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsWeakPtr channel(do_GetWeakReference(aChannel));
+ mIDToHttpChannelMap.Put(aId, std::move(channel));
+}
+
+void nsHttpHandler::RemoveHttpChannel(uint64_t aId) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (!NS_IsMainThread()) {
+ nsCOMPtr<nsIRunnable> idleRunnable(NewCancelableRunnableMethod<uint64_t>(
+ "nsHttpHandler::RemoveHttpChannel", this,
+ &nsHttpHandler::RemoveHttpChannel, aId));
+
+ NS_DispatchToMainThreadQueue(do_AddRef(idleRunnable),
+ EventQueuePriority::Idle);
+ return;
+ }
+
+ auto entry = mIDToHttpChannelMap.Lookup(aId);
+ if (entry) {
+ entry.Remove();
+ }
+}
+
+nsWeakPtr nsHttpHandler::GetWeakHttpChannel(uint64_t aId) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return mIDToHttpChannelMap.Get(aId);
+}
+
+nsresult nsHttpHandler::CompleteUpgrade(
+ HttpTransactionShell* aTrans, nsIHttpUpgradeListener* aUpgradeListener) {
+ return mConnMgr->CompleteUpgrade(aTrans, aUpgradeListener);
+}
+
+nsresult nsHttpHandler::DoShiftReloadConnectionCleanupWithConnInfo(
+ nsHttpConnectionInfo* aCi) {
+ MOZ_ASSERT(aCi);
+ return mConnMgr->DoShiftReloadConnectionCleanupWithConnInfo(aCi);
+}
+
+void nsHttpHandler::ClearHostMapping(nsHttpConnectionInfo* aConnInfo) {
+ if (XRE_IsSocketProcess()) {
+ AltServiceChild::ClearHostMapping(aConnInfo);
+ return;
+ }
+
+ AltServiceCache()->ClearHostMapping(aConnInfo);
+}
+
+void nsHttpHandler::SetHttpHandlerInitArgs(const HttpHandlerInitArgs& aArgs) {
+ MOZ_ASSERT(XRE_IsSocketProcess());
+ mFastOpenSupported = aArgs.mFastOpenSupported();
+ mLegacyAppName = aArgs.mLegacyAppName();
+ mLegacyAppVersion = aArgs.mLegacyAppVersion();
+ mPlatform = aArgs.mPlatform();
+ mOscpu = aArgs.mOscpu();
+ mMisc = aArgs.mMisc();
+ mProduct = aArgs.mProduct();
+ mProductSub = aArgs.mProductSub();
+ mAppName = aArgs.mAppName();
+ mAppVersion = aArgs.mAppVersion();
+ mCompatFirefox = aArgs.mCompatFirefox();
+ mCompatDevice = aArgs.mCompatDevice();
+ mDeviceModelId = aArgs.mDeviceModelId();
+}
+
+void nsHttpHandler::SetDeviceModelId(const nsCString& aModelId) {
+ MOZ_ASSERT(XRE_IsSocketProcess());
+ mDeviceModelId = aModelId;
+}
+
+void nsHttpHandler::MaybeAddAltSvcForTesting(
+ nsIURI* aUri, const nsACString& aUsername,
+ const nsACString& aTopWindowOrigin, bool aPrivateBrowsing, bool aIsolated,
+ nsIInterfaceRequestor* aCallbacks,
+ const OriginAttributes& aOriginAttributes) {
+ if (!IsHttp3Enabled() || mAltSvcMappingTemptativeMap.IsEmpty()) {
+ return;
+ }
+
+ bool isHttps = false;
+ if (NS_FAILED(aUri->SchemeIs("https", &isHttps)) || !isHttps) {
+ // Only set forr HTTPS.
+ return;
+ }
+
+ nsAutoCString originHost;
+ if (NS_FAILED(aUri->GetAsciiHost(originHost))) {
+ return;
+ }
+
+ nsCString* map = mAltSvcMappingTemptativeMap.Get(originHost);
+ if (map) {
+ int32_t originPort = 80;
+ aUri->GetPort(&originPort);
+ LOG(("nsHttpHandler::MaybeAddAltSvcForTesting for %s map: %s",
+ originHost.get(), PromiseFlatCString(*map).get()));
+ AltSvcMapping::ProcessHeader(*map, nsCString("https"), originHost,
+ originPort, aUsername, aTopWindowOrigin,
+ aPrivateBrowsing, aIsolated, aCallbacks,
+ nullptr, 0, aOriginAttributes, true);
+ }
+}
+
+bool nsHttpHandler::UseHTTPSRRAsAltSvcEnabled() const {
+ return StaticPrefs::network_dns_use_https_rr_as_altsvc();
+}
+
+bool nsHttpHandler::EchConfigEnabled() const {
+ return StaticPrefs::network_dns_echconfig_enabled();
+}
+
+bool nsHttpHandler::FallbackToOriginIfConfigsAreECHAndAllFailed() const {
+ return StaticPrefs::
+ network_dns_echconfig_fallback_to_origin_when_all_failed();
+}
+
+bool nsHttpHandler::UseHTTPSRRForSpeculativeConnection() const {
+ return StaticPrefs::network_dns_use_https_rr_for_speculative_connection();
+}
+
+} // namespace mozilla::net