diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index bf89edaee5de..2e4a9bc46d6d 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -7366,25 +7366,25 @@ # Single TRR request timeout, in milliseconds - name: network.trr.request_timeout_ms - type: uint32_t + type: RelaxedAtomicUint32 value: 1500 mirror: always # Single TRR request timeout, in milliseconds for mode 3 - name: network.trr.request_timeout_mode_trronly_ms - type: uint32_t + type: RelaxedAtomicUint32 value: 30000 mirror: always # Whether to send the Accept-Language header for TRR requests - name: network.trr.send_accept-language_headers - type: bool + type: RelaxedAtomicBool value: false mirror: always # Whether to send the User-Agent header for TRR requests - name: network.trr.send_user-agent_headers - type: bool + type: RelaxedAtomicBool value: false mirror: always @@ -7408,6 +7408,12 @@ value: false mirror: always +# This pref controls whether to use SimpleHttpChannel off main thread. +- name: network.trr.fetch_off_main_thread + type: RelaxedAtomicBool + value: false + mirror: always + # Allow the network changed event to get sent when a network topology or setup # change is noticed while running. - name: network.notify.changed diff --git a/netwerk/dns/TRR.cpp b/netwerk/dns/TRR.cpp index 72979ab11c28..41043fbacba2 100644 --- a/netwerk/dns/TRR.cpp +++ b/netwerk/dns/TRR.cpp @@ -8,6 +8,7 @@ #include "nsCharSeparatedTokenizer.h" #include "nsContentUtils.h" #include "nsHostResolver.h" +#include "nsHttpHandler.h" #include "nsIHttpChannel.h" #include "nsIHttpChannelInternal.h" #include "nsIIOService.h" @@ -28,6 +29,7 @@ #include "mozilla/Logging.h" #include "mozilla/Preferences.h" #include "mozilla/StaticPrefs_network.h" +#include "mozilla/SyncRunnable.h" #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" #include "mozilla/Tokenizer.h" @@ -156,7 +158,12 @@ nsresult TRR::DohEncode(nsCString& aBody, bool aDisableECS) { NS_IMETHODIMP TRR::Run() { - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT_IF(gTRRService && StaticPrefs::network_trr_fetch_off_main_thread(), + gTRRService->IsOnTRRThread()); + MOZ_ASSERT_IF( + gTRRService && !StaticPrefs::network_trr_fetch_off_main_thread(), + NS_IsMainThread()); + if ((gTRRService == nullptr) || NS_FAILED(SendHTTPRequest())) { FailData(NS_ERROR_FAILURE); // The dtor will now be run @@ -164,9 +171,63 @@ TRR::Run() { return NS_OK; } +static void InitHttpHandler() { + nsresult rv; + nsCOMPtr ios = do_GetIOService(&rv); + if (NS_FAILED(rv)) { + return; + } + + nsCOMPtr handler; + rv = ios->GetProtocolHandler("http", getter_AddRefs(handler)); + if (NS_FAILED(rv)) { + return; + } +} + +nsresult TRR::CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult) { + *aResult = nullptr; + + if (NS_IsMainThread()) { + nsresult rv; + nsCOMPtr ios(do_GetIOService(&rv)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_NewChannel(aResult, aUri, nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, + nsIContentPolicy::TYPE_OTHER, + nullptr, // nsICookieJarSettings + nullptr, // PerformanceStorage + nullptr, // aLoadGroup + nullptr, // aCallbacks + nsIRequest::LOAD_NORMAL, ios); + } + + // Unfortunately, we can only initialize gHttpHandler on main thread. + if (!gHttpHandler) { + nsCOMPtr main = GetMainThreadEventTarget(); + if (main) { + // Forward to the main thread synchronously. + SyncRunnable::DispatchToThread( + main, new SyncRunnable(NS_NewRunnableFunction( + "InitHttpHandler", []() { InitHttpHandler(); }))); + } + } + + if (!gHttpHandler) { + return NS_ERROR_UNEXPECTED; + } + + return gHttpHandler->CreateSimpleHttpChannel(aUri, + nullptr, // givenProxyInfo + 0, // proxyResolveFlags + nullptr, // proxyURI + nullptr, // aLoadInfo + aResult); +} + nsresult TRR::SendHTTPRequest() { // This is essentially the "run" method - created from nsHostResolver - MOZ_ASSERT(NS_IsMainThread(), "wrong thread"); if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) && (mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT)) { @@ -196,14 +257,11 @@ nsresult TRR::SendHTTPRequest() { } } - nsresult rv; - nsCOMPtr ios(do_GetIOService(&rv)); - NS_ENSURE_SUCCESS(rv, rv); - bool useGet = gTRRService->UseGET(); nsAutoCString body; nsCOMPtr dnsURI; bool disableECS = gTRRService->DisableECS(); + nsresult rv; LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType)); @@ -246,23 +304,20 @@ nsresult TRR::SendHTTPRequest() { return rv; } - rv = NS_NewChannel(getter_AddRefs(mChannel), dnsURI, - nsContentUtils::GetSystemPrincipal(), - nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, - nsIContentPolicy::TYPE_OTHER, - nullptr, // nsICookieJarSettings - nullptr, // PerformanceStorage - nullptr, // aLoadGroup - this, - nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING | - nsIRequest::LOAD_BYPASS_CACHE | - nsIChannel::LOAD_BYPASS_URL_CLASSIFIER, - ios); + rv = CreateChannelHelper(dnsURI, getter_AddRefs(mChannel)); if (NS_FAILED(rv)) { LOG(("TRR:SendHTTPRequest: NewChannel failed!\n")); return rv; } + mChannel->SetLoadFlags( + nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING | + nsIRequest::LOAD_BYPASS_CACHE | nsIChannel::LOAD_BYPASS_URL_CLASSIFIER); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mChannel->SetNotificationCallbacks(this); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr httpChannel = do_QueryInterface(mChannel); if (!httpChannel) { return NS_ERROR_UNEXPECTED; @@ -1019,7 +1074,10 @@ nsresult TRR::On200Response(nsIChannel* aChannel) { mCname.get(), mCnameLoop)); RefPtr trr = new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB); - rv = NS_DispatchToMainThread(trr); + if (!gTRRService) { + return NS_ERROR_FAILURE; + } + rv = gTRRService->DispatchTRRRequest(trr); if (NS_SUCCEEDED(rv)) { return rv; } @@ -1188,10 +1246,22 @@ class ProxyCancel : public Runnable { }; void TRR::Cancel() { - if (!NS_IsMainThread()) { - NS_DispatchToMainThread(new ProxyCancel(this)); - return; + if (StaticPrefs::network_trr_fetch_off_main_thread()) { + if (gTRRService) { + nsCOMPtr thread = gTRRService->TRRThread(); + if (thread && !thread->IsOnCurrentThread()) { + nsCOMPtr r = new ProxyCancel(this); + thread->Dispatch(r.forget()); + return; + } + } + } else { + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(new ProxyCancel(this)); + return; + } } + if (mChannel) { LOG(("TRR: %p canceling Channel %p %s %d\n", this, mChannel.get(), mHost.get(), mType)); diff --git a/netwerk/dns/TRR.h b/netwerk/dns/TRR.h index c0af491b578c..023b6a79576b 100644 --- a/netwerk/dns/TRR.h +++ b/netwerk/dns/TRR.h @@ -165,6 +165,8 @@ class TRR : public Runnable, bool UseDefaultServer(); + nsresult CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult); + nsCOMPtr mChannel; enum TrrType mType; TimeStamp mStartTime; diff --git a/netwerk/dns/TRRService.cpp b/netwerk/dns/TRRService.cpp index 2b33af4bc123..489341c923e6 100644 --- a/netwerk/dns/TRRService.cpp +++ b/netwerk/dns/TRRService.cpp @@ -34,6 +34,7 @@ extern mozilla::LazyLogModule gHostResolverLog; #define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args) TRRService* gTRRService = nullptr; +StaticRefPtr sTRRBackgroundThread; NS_IMPL_ISUPPORTS(TRRService, nsIObserver, nsISupportsWeakReference) @@ -75,6 +76,7 @@ nsresult TRRService::Init() { observerService->AddObserver(this, kPurge, true); observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, true); observerService->AddObserver(this, NS_DNS_SUFFIX_LIST_UPDATED_TOPIC, true); + observerService->AddObserver(this, "xpcom-shutdown-threads", true); } nsCOMPtr prefBranch; GetPrefBranch(getter_AddRefs(prefBranch)); @@ -106,6 +108,18 @@ nsresult TRRService::Init() { nsCOMPtr nls = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID); RebuildSuffixList(nls); + + if (StaticPrefs::network_trr_fetch_off_main_thread()) { + nsCOMPtr thread; + if (NS_FAILED( + NS_NewNamedThread("TRR Background", getter_AddRefs(thread)))) { + NS_WARNING("NS_NewNamedThread failed!"); + return NS_ERROR_FAILURE; + } + + sTRRBackgroundThread = thread; + } + LOG(("Initialized TRRService\n")); return NS_OK; } @@ -397,6 +411,49 @@ TRRService::~TRRService() { gTRRService = nullptr; } +nsresult TRRService::DispatchTRRRequest(TRR* aTrrRequest) { + return DispatchTRRRequestInternal(aTrrRequest, true); +} + +nsresult TRRService::DispatchTRRRequestInternal(TRR* aTrrRequest, + bool aWithLock) { + NS_ENSURE_ARG_POINTER(aTrrRequest); + if (!StaticPrefs::network_trr_fetch_off_main_thread()) { + return NS_DispatchToMainThread(aTrrRequest); + } + + RefPtr trr = aTrrRequest; + nsCOMPtr thread = aWithLock ? TRRThread() : TRRThread_locked(); + if (!thread) { + return NS_ERROR_FAILURE; + } + + return thread->Dispatch(trr.forget()); +} + +already_AddRefed TRRService::TRRThread() { + MutexAutoLock lock(mLock); + return TRRThread_locked(); +} + +already_AddRefed TRRService::TRRThread_locked() { + RefPtr thread = sTRRBackgroundThread; + return thread.forget(); +} + +bool TRRService::IsOnTRRThread() { + nsCOMPtr thread; + { + MutexAutoLock lock(mLock); + thread = sTRRBackgroundThread; + } + if (!thread) { + return false; + } + + return thread->IsOnCurrentThread(); +} + NS_IMETHODIMP TRRService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { @@ -462,6 +519,16 @@ TRRService::Observe(nsISupports* aSubject, const char* aTopic, nsCOMPtr link = do_QueryInterface(aSubject); RebuildSuffixList(link); CheckPlatformDNSStatus(link); + } else if (!strcmp(aTopic, "xpcom-shutdown-threads")) { + if (sTRRBackgroundThread) { + nsCOMPtr thread; + { + MutexAutoLock lock(mLock); + thread = sTRRBackgroundThread.get(); + sTRRBackgroundThread = nullptr; + } + MOZ_ALWAYS_SUCCEEDS(thread->Shutdown()); + } } return NS_OK; } @@ -526,7 +593,7 @@ void TRRService::MaybeConfirm_locked() { mConfirmationNS.get())); mConfirmer = new TRR(this, mConfirmationNS, TRRTYPE_NS, EmptyCString(), false); - NS_DispatchToMainThread(mConfirmer); + DispatchTRRRequestInternal(mConfirmer, false); } } @@ -563,9 +630,6 @@ bool TRRService::MaybeBootstrap(const nsACString& aPossible, bool TRRService::IsDomainBlacklisted(const nsACString& aHost, const nsACString& aOriginSuffix, bool aPrivateBrowsing) { - // Only use the Storage API on the main thread - MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(), "wrong thread"); - if (!Enabled(nsIRequest::TRR_DEFAULT_MODE)) { return true; } @@ -576,8 +640,15 @@ bool TRRService::IsDomainBlacklisted(const nsACString& aHost, // Calling the locking version of this method would cause us to grab // the mutex for every label of the hostname, which would be very // inefficient. - if (IsExcludedFromTRR_unlocked(aHost)) { - return true; + if (NS_IsMainThread()) { + if (IsExcludedFromTRR_unlocked(aHost)) { + return true; + } + } else { + MOZ_ASSERT(IsOnTRRThread()); + if (IsExcludedFromTRR(aHost)) { + return true; + } } if (!mTRRBLStorage) { @@ -770,7 +841,7 @@ void TRRService::TRRBlacklist(const nsACString& aHost, // check if there's an NS entry for this name RefPtr trr = new TRR(this, check, TRRTYPE_NS, aOriginSuffix, privateBrowsing); - NS_DispatchToMainThread(trr); + DispatchTRRRequest(trr); } } } @@ -792,7 +863,11 @@ TRRService::Notify(nsITimer* aTimer) { } void TRRService::TRRIsOkay(enum TrrOkay aReason) { - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT_IF(StaticPrefs::network_trr_fetch_off_main_thread(), + IsOnTRRThread()); + MOZ_ASSERT_IF(!StaticPrefs::network_trr_fetch_off_main_thread(), + NS_IsMainThread()); + Telemetry::AccumulateCategorical( aReason == OKAY_NORMAL ? Telemetry::LABELS_DNS_TRR_SUCCESS::Fine : (aReason == OKAY_TIMEOUT @@ -819,7 +894,10 @@ AHostResolver::LookupStatus TRRService::CompleteLookup( const nsACString& aOriginSuffix) { // this is an NS check for the TRR blacklist or confirmationNS check - MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT_IF(StaticPrefs::network_trr_fetch_off_main_thread(), + IsOnTRRThread()); + MOZ_ASSERT_IF(!StaticPrefs::network_trr_fetch_off_main_thread(), + NS_IsMainThread()); MOZ_ASSERT(!rec); RefPtr newRRSet(aNewRRSet); diff --git a/netwerk/dns/TRRService.h b/netwerk/dns/TRRService.h index 0e5f920c2bf2..ed92a9c636c2 100644 --- a/netwerk/dns/TRRService.h +++ b/netwerk/dns/TRRService.h @@ -67,6 +67,10 @@ class TRRService : public nsIObserver, void TRRIsOkay(enum TrrOkay aReason); bool ParentalControlEnabled() const { return mParentalControlEnabled; } + nsresult DispatchTRRRequest(TRR* aTrrRequest); + already_AddRefed TRRThread(); + bool IsOnTRRThread(); + private: virtual ~TRRService(); nsresult ReadPrefs(const char* name); @@ -84,6 +88,9 @@ class TRRService : public nsIObserver, void RebuildSuffixList(nsINetworkLinkService* aLinkService); void CheckPlatformDNSStatus(nsINetworkLinkService* aLinkService); + nsresult DispatchTRRRequestInternal(TRR* aTrrRequest, bool aWithLock); + already_AddRefed TRRThread_locked(); + bool mInitialized; Atomic mMode; Atomic mTRRBlacklistExpireTime; diff --git a/netwerk/dns/moz.build b/netwerk/dns/moz.build index 1e3c1d57c8d3..c1520c7cda52 100644 --- a/netwerk/dns/moz.build +++ b/netwerk/dns/moz.build @@ -82,6 +82,7 @@ GeneratedFile('etld_data.inc', script='prepare_tlds.py', # need to include etld_data.inc LOCAL_INCLUDES += [ '/netwerk/base', + '/netwerk/protocol/http', ] USE_LIBS += ['icu'] diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp index 9043cc759795..7edba72f429f 100644 --- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -1272,7 +1272,7 @@ nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec, TRR* pushedTRR) { RefPtr trr; MutexAutoLock trrlock(addrRec->mTrrLock); trr = pushedTRR ? pushedTRR : new TRR(this, rec, rectype); - if (pushedTRR || NS_SUCCEEDED(NS_DispatchToMainThread(trr))) { + if (pushedTRR || NS_SUCCEEDED(gTRRService->DispatchTRRRequest(trr))) { addrRec->mResolving++; if (rectype == TRRTYPE_A) { MOZ_ASSERT(!addrRec->mTrrA); @@ -1305,7 +1305,8 @@ nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec, TRR* pushedTRR) { RefPtr trr; MutexAutoLock trrlock(typeRec->mTrrLock); trr = pushedTRR ? pushedTRR : new TRR(this, rec, rectype); - if (pushedTRR || NS_SUCCEEDED(NS_DispatchToMainThread(trr))) { + RefPtr trrRequest = trr; + if (pushedTRR || NS_SUCCEEDED(gTRRService->DispatchTRRRequest(trr))) { typeRec->mResolving++; MOZ_ASSERT(!typeRec->mTrr); typeRec->mTrr = trr;