author | Kershaw Chang <kershaw@mozilla.com> |
Mon, 11 Oct 2021 19:17:58 +0000 | |
changeset 595410 | 21bb8cd199ba6ada31ec7b17de0cd7ae7cb46440 |
parent 595409 | 76dadf1dab5f0ccc294e901de725c1476f491439 |
child 595411 | 4a1898ec99d0f6920e6981d68655269c120418e9 |
push id | 151253 |
push user | kjang@mozilla.com |
push date | Mon, 11 Oct 2021 19:20:29 +0000 |
treeherder | autoland@21bb8cd199ba [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | necko-reviewers, dragana |
bugs | 1475641 |
milestone | 95.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/netwerk/base/ProxyAutoConfig.cpp +++ b/netwerk/base/ProxyAutoConfig.cpp @@ -23,17 +23,22 @@ #include "js/PropertySpec.h" #include "js/SourceText.h" // JS::Source{Ownership,Text} #include "js/Utility.h" #include "js/Warnings.h" // JS::SetWarningReporter #include "prnetdb.h" #include "nsITimer.h" #include "mozilla/Atomics.h" #include "mozilla/SpinEventLoopUntil.h" +#include "mozilla/ipc/Endpoint.h" #include "mozilla/net/DNS.h" +#include "mozilla/net/SocketProcessChild.h" +#include "mozilla/net/SocketProcessParent.h" +#include "mozilla/net/ProxyAutoConfigChild.h" +#include "mozilla/net/ProxyAutoConfigParent.h" #include "mozilla/Utf8.h" // mozilla::Utf8Unit #include "nsServiceManagerUtils.h" #include "nsNetCID.h" #if defined(XP_MACOSX) # include "nsMacUtilsImpl.h" #endif @@ -361,16 +366,31 @@ class PACResolver final : public nsIDNSL Mutex mMutex; private: ~PACResolver() = default; }; NS_IMPL_ISUPPORTS(PACResolver, nsIDNSListener, nsITimerCallback, nsINamed) static void PACLogToConsole(nsString& aMessage) { + if (XRE_IsSocketProcess()) { + auto task = [message(aMessage)]() { + SocketProcessChild* child = SocketProcessChild::GetSingleton(); + if (child) { + Unused << child->SendOnConsoleMessage(message); + } + }; + if (NS_IsMainThread()) { + task(); + } else { + NS_DispatchToMainThread(NS_NewRunnableFunction("PACLogToConsole", task)); + } + return; + } + nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID); if (!consoleService) return; consoleService->LogStringMessage(aMessage.get()); } // Javascript errors and warnings are logged to the main error console @@ -463,26 +483,29 @@ bool ProxyAutoConfig::ResolveAddress(con if (!mTimer) mTimer = NS_NewTimer(); if (mTimer) { mTimer->SetTarget(mMainThreadEventTarget); mTimer->InitWithCallback(helper, aTimeout, nsITimer::TYPE_ONE_SHOT); helper->mTimer = mTimer; } } + mWaitingForDNSResolve = true; // Spin the event loop of the pac thread until lookup is complete. // nsPACman is responsible for keeping a queue and only allowing // one PAC execution at a time even when it is called re-entrantly. SpinEventLoopUntil("ProxyAutoConfig::ResolveAddress"_ns, [&, helper, this]() { if (!helper->mRequest) { + mWaitingForDNSResolve = false; return true; } if (this->mShutdown) { NS_WARNING("mShutdown set with PAC request not cancelled"); MOZ_ASSERT(NS_FAILED(helper->mStatus)); + mWaitingForDNSResolve = false; return true; } return false; }); if (NS_FAILED(helper->mStatus)) { return false; } @@ -688,20 +711,21 @@ class JSContextWrapper { const JSClass JSContextWrapper::sGlobalClass = {"PACResolutionThreadGlobal", JSCLASS_GLOBAL_FLAGS, &JS::DefaultGlobalClassOps}; void ProxyAutoConfig::SetThreadLocalIndex(uint32_t index) { RunningIndex() = index; } -nsresult ProxyAutoConfig::Init(const nsCString& aPACURI, - const nsCString& aPACScriptData, - bool aIncludePath, uint32_t aExtraHeapSize, - nsIEventTarget* aEventTarget) { +nsresult ProxyAutoConfig::ConfigurePAC(const nsCString& aPACURI, + const nsCString& aPACScriptData, + bool aIncludePath, + uint32_t aExtraHeapSize, + nsIEventTarget* aEventTarget) { mShutdown = false; // Shutdown needs to be called prior to destruction mPACURI = aPACURI; // The full PAC script data is the concatenation of 1) the various functions // exposed to PAC scripts in |sAsciiPacUtils| and 2) the user-provided PAC // script data. Historically this was single-byte Latin-1 text (usually just // ASCII, but bug 296163 has a real-world Latin-1 example). We now support @@ -815,16 +839,25 @@ nsresult ProxyAutoConfig::SetupJS() { // we don't need these now mConcatenatedPACData.Truncate(); mPACURI.Truncate(); return NS_OK; } +void ProxyAutoConfig::GetProxyForURIWithCallback( + const nsCString& aTestURI, const nsCString& aTestHost, + std::function<void(nsresult aStatus, const nsACString& aResult)>&& + aCallback) { + nsAutoCString result; + nsresult status = GetProxyForURI(aTestURI, aTestHost, result); + aCallback(status, result); +} + nsresult ProxyAutoConfig::GetProxyForURI(const nsCString& aTestURI, const nsCString& aTestHost, nsACString& result) { if (mJSNeedsSetup) SetupJS(); if (!mJSContext || !mJSContext->IsOK()) return NS_ERROR_NOT_AVAILABLE; JSContext* cx = mJSContext->Context(); @@ -1054,10 +1087,77 @@ bool ProxyAutoConfig::MyIPAddress(const if (!dottedDecimalString) { return false; } aArgs.rval().setString(dottedDecimalString); return true; } +RemoteProxyAutoConfig::RemoteProxyAutoConfig() = default; + +RemoteProxyAutoConfig::~RemoteProxyAutoConfig() = default; + +nsresult RemoteProxyAutoConfig::Init(nsIThread* aPACThread) { + MOZ_ASSERT(NS_IsMainThread()); + + SocketProcessParent* socketProcessParent = + SocketProcessParent::GetSingleton(); + if (!socketProcessParent) { + return NS_ERROR_NOT_AVAILABLE; + } + + ipc::Endpoint<PProxyAutoConfigParent> parent; + ipc::Endpoint<PProxyAutoConfigChild> child; + nsresult rv = PProxyAutoConfig::CreateEndpoints( + base::GetCurrentProcId(), socketProcessParent->OtherPid(), &parent, + &child); + if (NS_FAILED(rv)) { + return rv; + } + + Unused << socketProcessParent->SendInitProxyAutoConfigChild(std::move(child)); + mProxyAutoConfigParent = new ProxyAutoConfigParent(); + return aPACThread->Dispatch( + NS_NewRunnableFunction("ProxyAutoConfigParent::ProxyAutoConfigParent", + [proxyAutoConfigParent(mProxyAutoConfigParent), + endpoint{std::move(parent)}]() mutable { + proxyAutoConfigParent->Init(std::move(endpoint)); + })); +} + +nsresult RemoteProxyAutoConfig::ConfigurePAC(const nsCString& aPACURI, + const nsCString& aPACScriptData, + bool aIncludePath, + uint32_t aExtraHeapSize, + nsIEventTarget*) { + Unused << mProxyAutoConfigParent->SendConfigurePAC( + aPACURI, aPACScriptData, aIncludePath, aExtraHeapSize); + return NS_OK; +} + +void RemoteProxyAutoConfig::Shutdown() { mProxyAutoConfigParent->Close(); } + +void RemoteProxyAutoConfig::GC() { Unused << mProxyAutoConfigParent->SendGC(); } + +void RemoteProxyAutoConfig::GetProxyForURIWithCallback( + const nsCString& aTestURI, const nsCString& aTestHost, + std::function<void(nsresult aStatus, const nsACString& aResult)>&& + aCallback) { + if (!mProxyAutoConfigParent->CanSend()) { + return; + } + + mProxyAutoConfigParent->SendGetProxyForURI( + aTestURI, aTestHost, + [aCallback](Tuple<nsresult, nsCString>&& aResult) { + nsresult status; + nsCString result; + Tie(status, result) = aResult; + aCallback(status, result); + }, + [aCallback](mozilla::ipc::ResponseRejectReason&& aReason) { + aCallback(NS_ERROR_FAILURE, ""_ns); + }); +} + } // namespace net } // namespace mozilla
--- a/netwerk/base/ProxyAutoConfig.h +++ b/netwerk/base/ProxyAutoConfig.h @@ -2,46 +2,66 @@ /* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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/. */ #ifndef ProxyAutoConfig_h__ #define ProxyAutoConfig_h__ +#include <functional> #include "nsString.h" #include "nsCOMPtr.h" class nsIEventTarget; class nsITimer; namespace JS { class CallArgs; } // namespace JS namespace mozilla { namespace net { class JSContextWrapper; +class ProxyAutoConfigParent; union NetAddr; +class ProxyAutoConfigBase { + public: + virtual ~ProxyAutoConfigBase() = default; + virtual nsresult Init(nsIThread* aPACThread) { return NS_OK; } + virtual nsresult ConfigurePAC(const nsCString& aPACURI, + const nsCString& aPACScriptData, + bool aIncludePath, uint32_t aExtraHeapSize, + nsIEventTarget* aEventTarget) = 0; + virtual void SetThreadLocalIndex(uint32_t index) {} + virtual void Shutdown() = 0; + virtual void GC() = 0; + virtual void GetProxyForURIWithCallback( + const nsCString& aTestURI, const nsCString& aTestHost, + std::function<void(nsresult aStatus, const nsACString& aResult)>&& + aCallback) = 0; +}; + // The ProxyAutoConfig class is meant to be created and run on a // non main thread. It synchronously resolves PAC files by blocking that // thread and running nested event loops. GetProxyForURI is not re-entrant. -class ProxyAutoConfig { +class ProxyAutoConfig : public ProxyAutoConfigBase { public: ProxyAutoConfig(); - ~ProxyAutoConfig(); + virtual ~ProxyAutoConfig(); - nsresult Init(const nsCString& aPACURI, const nsCString& aPACScriptData, - bool aIncludePath, uint32_t aExtraHeapSize, - nsIEventTarget* aEventTarget); - void SetThreadLocalIndex(uint32_t index); - void Shutdown(); - void GC(); + nsresult ConfigurePAC(const nsCString& aPACURI, + const nsCString& aPACScriptData, bool aIncludePath, + uint32_t aExtraHeapSize, + nsIEventTarget* aEventTarget) override; + void SetThreadLocalIndex(uint32_t index) override; + void Shutdown() override; + void GC() override; bool MyIPAddress(const JS::CallArgs& aArgs); bool ResolveAddress(const nsCString& aHostName, NetAddr* aNetAddr, unsigned int aTimeout); /** * Get the proxy string for the specified URI. The proxy string is * given by the following: * @@ -73,16 +93,23 @@ class ProxyAutoConfig { * The ASCII hostname to test. * * @param result * result string as defined above. */ nsresult GetProxyForURI(const nsCString& aTestURI, const nsCString& aTestHost, nsACString& result); + void GetProxyForURIWithCallback( + const nsCString& aTestURI, const nsCString& aTestHost, + std::function<void(nsresult aStatus, const nsACString& aResult)>&& + aCallback) override; + + bool WaitingForDNSResolve() const { return mWaitingForDNSResolve; } + private: // allow 665ms for myipaddress dns queries. That's 95th percentile. const static unsigned int kTimeout = 665; // used to compile the PAC file and setup the execution context nsresult SetupJS(); bool SrcAddress(const NetAddr* remoteAddress, nsCString& localAddress); @@ -90,18 +117,40 @@ class ProxyAutoConfig { const JS::CallArgs& aArgs, bool* aResult); JSContextWrapper* mJSContext{nullptr}; bool mJSNeedsSetup{false}; bool mShutdown{true}; nsCString mConcatenatedPACData; nsCString mPACURI; bool mIncludePath{false}; + bool mWaitingForDNSResolve{false}; uint32_t mExtraHeapSize{0}; nsCString mRunningHost; nsCOMPtr<nsITimer> mTimer; nsCOMPtr<nsIEventTarget> mMainThreadEventTarget; }; +class RemoteProxyAutoConfig : public ProxyAutoConfigBase { + public: + RemoteProxyAutoConfig(); + virtual ~RemoteProxyAutoConfig(); + + nsresult Init(nsIThread* aPACThread) override; + nsresult ConfigurePAC(const nsCString& aPACURI, + const nsCString& aPACScriptData, bool aIncludePath, + uint32_t aExtraHeapSize, + nsIEventTarget* aEventTarget) override; + void Shutdown() override; + void GC() override; + void GetProxyForURIWithCallback( + const nsCString& aTestURI, const nsCString& aTestHost, + std::function<void(nsresult aStatus, const nsACString& aResult)>&& + aCallback) override; + + private: + RefPtr<ProxyAutoConfigParent> mProxyAutoConfigParent; +}; + } // namespace net } // namespace mozilla #endif // ProxyAutoConfig_h__
--- a/netwerk/base/nsIOService.cpp +++ b/netwerk/base/nsIOService.cpp @@ -216,16 +216,17 @@ static const char* gCallbackPrefsForSock "network.ssl_tokens_cache_enabled", "network.send_ODA_to_content_directly", "network.trr.", "doh-rollout.", "network.dns.disableIPv6", "network.dns.skipTRR-when-parental-control-enabled", "network.offline-mirrors-connectivity", "network.disable-localhost-when-offline", + "network.proxy.parse_pac_on_socket_process", nullptr, }; static const char* gCallbackSecurityPrefs[] = { // Note the prefs listed below should be in sync with the code in // HandleTLSPrefChange(). "security.tls.version.min", "security.tls.version.max",
--- a/netwerk/base/nsPACMan.cpp +++ b/netwerk/base/nsPACMan.cpp @@ -12,20 +12,22 @@ #include "nsIAsyncVerifyRedirectCallback.h" #include "nsIAuthPrompt.h" #include "nsIDHCPClient.h" #include "nsIHttpChannel.h" #include "nsIPrefBranch.h" #include "nsIPromptFactory.h" #include "nsIProtocolProxyService.h" #include "nsISystemProxySettings.h" +#include "nsIOService.h" #include "nsNetUtil.h" #include "nsThreadUtils.h" #include "mozilla/Result.h" #include "mozilla/ResultExtensions.h" +#include "mozilla/StaticPrefs_network.h" #include "mozilla/Telemetry.h" //----------------------------------------------------------------------------- namespace mozilla { namespace net { LazyLogModule gProxyLog("proxy"); @@ -265,18 +267,19 @@ class ExecutePACThreadAction final : pub mCancel = false; return NS_OK; } if (mSetupPAC) { mSetupPAC = false; nsCOMPtr<nsIEventTarget> target = mPACMan->GetNeckoTarget(); - mPACMan->mPAC.Init(mSetupPACURI, mSetupPACData, mPACMan->mIncludePath, - mExtraHeapSize, target); + mPACMan->mPAC->ConfigurePAC(mSetupPACURI, mSetupPACData, + mPACMan->mIncludePath, mExtraHeapSize, + target); RefPtr<PACLoadComplete> runnable = new PACLoadComplete(mPACMan); mPACMan->Dispatch(runnable.forget()); return NS_OK; } if (mConfigureWPAD) { nsAutoCString spec; @@ -367,22 +370,28 @@ nsPACMan::nsPACMan(nsISerialEventTarget* mLoadPending(false), mShutdown(false), mLoadFailureCount(0), mInProgress(false), mAutoDetect(false), mWPADOverDHCPEnabled(false), mProxyConfigType(0) { MOZ_ASSERT(NS_IsMainThread(), "pacman must be created on main thread"); - if (!sThreadLocalSetup) { - sThreadLocalSetup = true; - PR_NewThreadPrivateIndex(&sThreadLocalIndex, nullptr); + mIncludePath = Preferences::GetBool(kPACIncludePath, false); + if (StaticPrefs::network_proxy_parse_pac_on_socket_process() && + gIOService->SocketProcessReady()) { + mPAC = MakeUnique<RemoteProxyAutoConfig>(); + } else { + mPAC = MakeUnique<ProxyAutoConfig>(); + if (!sThreadLocalSetup) { + sThreadLocalSetup = true; + PR_NewThreadPrivateIndex(&sThreadLocalIndex, nullptr); + } + mPAC->SetThreadLocalIndex(sThreadLocalIndex); } - mPAC.SetThreadLocalIndex(sThreadLocalIndex); - mIncludePath = Preferences::GetBool(kPACIncludePath, false); } nsPACMan::~nsPACMan() { MOZ_ASSERT(mShutdown, "Shutdown must be called before dtor."); if (mPACThread) { if (NS_IsMainThread()) { mPACThread->Shutdown(); @@ -433,16 +442,20 @@ nsresult nsPACMan::DispatchToPAC(already if (mShutdown) { return NS_ERROR_NOT_AVAILABLE; } // Lazily create the PAC thread. This method is main-thread only so we don't // have to worry about threading issues here. if (!mPACThread) { MOZ_TRY(NS_NewNamedThread("ProxyResolution", getter_AddRefs(mPACThread))); + nsresult rv = mPAC->Init(mPACThread); + if (NS_FAILED(rv)) { + return rv; + } } return mPACThread->Dispatch( e.forget(), aSync ? nsIEventTarget::DISPATCH_SYNC : nsIEventTarget::DISPATCH_NORMAL); } nsresult nsPACMan::AsyncGetProxyForURI(nsIURI* uri, nsPACManCallback* callback, @@ -755,30 +768,32 @@ void nsPACMan::CancelPendingQ(nsresult s MOZ_ASSERT(!NS_IsMainThread(), "wrong thread"); RefPtr<PendingPACQuery> query; while (!mPendingQ.isEmpty()) { query = dont_AddRef(mPendingQ.popLast()); query->Complete(status, ""_ns); } - if (aShutdown) mPAC.Shutdown(); + if (aShutdown) { + mPAC->Shutdown(); + } } void nsPACMan::ProcessPendingQ() { MOZ_ASSERT(!NS_IsMainThread(), "wrong thread"); while (ProcessPending()) { ; } if (mShutdown) { - mPAC.Shutdown(); + mPAC->Shutdown(); } else { // do GC while the thread has nothing pending - mPAC.GC(); + mPAC->GC(); } } // returns true if progress was made by shortening the queue bool nsPACMan::ProcessPending() { if (mPendingQ.isEmpty()) return false; // queue during normal load, but if we are retrying a failed load then @@ -826,20 +841,23 @@ bool nsPACMan::ProcessPending() { } LOG(("Use proxy from system settings: %s\n", pacString.get())); query->Complete(NS_OK, pacString); completed = true; } // the systemproxysettings didn't complete the resolution. try via PAC if (!completed) { - nsresult status = - mPAC.GetProxyForURI(query->mSpec, query->mHost, pacString); - LOG(("Use proxy from PAC: %s\n", pacString.get())); - query->Complete(status, pacString); + auto callback = [query(query)](nsresult aStatus, + const nsACString& aResult) { + LOG(("Use proxy from PAC: %s\n", PromiseFlatCString(aResult).get())); + query->Complete(aStatus, aResult); + }; + mPAC->GetProxyForURIWithCallback(query->mSpec, query->mHost, + std::move(callback)); } mInProgress = false; return true; } NS_IMPL_ISUPPORTS(nsPACMan, nsIStreamLoaderObserver, nsIInterfaceRequestor, nsIChannelEventSink)
--- a/netwerk/base/nsPACMan.h +++ b/netwerk/base/nsPACMan.h @@ -255,17 +255,17 @@ class nsPACMan final : public nsIStreamL * instantiation of the thread. * * @param aEvent The event to disptach. * @param aSync Whether or not this should be synchronous dispatch. */ nsresult DispatchToPAC(already_AddRefed<nsIRunnable> aEvent, bool aSync = false); - ProxyAutoConfig mPAC; + UniquePtr<ProxyAutoConfigBase> mPAC; nsCOMPtr<nsIThread> mPACThread; nsCOMPtr<nsISystemProxySettings> mSystemProxySettings; nsCOMPtr<nsIDHCPClient> mDHCPClient; LinkedList<PendingPACQuery> mPendingQ; /* pac thread only */ // These specs are not nsIURI so that they can be used off the main thread. // The non-normalized versions are directly from the configuration, the
new file mode 100644 --- /dev/null +++ b/netwerk/ipc/PProxyAutoConfig.ipdl @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +namespace mozilla { +namespace net { + +sync protocol PProxyAutoConfig +{ +child: + async ConfigurePAC(nsCString aPACURI, nsCString aPACScriptData, + bool aIncludePath, uint32_t aExtraHeapSize); + async GetProxyForURI(nsCString aTestURI, nsCString aTestHost) + returns (nsresult aStatus, nsCString aResult); + async GC(); + +}; + +} // namespace net +} // namespace mozilla
--- a/netwerk/ipc/PSocketProcess.ipdl +++ b/netwerk/ipc/PSocketProcess.ipdl @@ -15,16 +15,17 @@ include protocol PParentToChildStream; include protocol PInputChannelThrottleQueue; include protocol PBackground; include protocol PAltService; include protocol PAltSvcTransaction; include protocol PTRRService; include protocol PProxyConfigLookup; include protocol PNativeDNSResolverOverride; include protocol PRemoteLazyInputStream; +include protocol PProxyAutoConfig; #if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) include protocol PSandboxTesting; #endif include MemoryReportTypes; include NeckoChannelParams; include PrefsTypes; @@ -131,16 +132,18 @@ parent: async CachePushCheck(nsIURI aPushedURL, OriginAttributes aOriginAttributes, nsCString aRequestString) returns (bool aAccepted); async ODoHServiceActivated(bool aActivated); async ExcludeHttp2OrHttp3(HttpConnectionInfoCloneArgs aArgs); + async OnConsoleMessage(nsString aMessage); + child: async Init(SocketPorcessInitAttributes aAttributes); async PreferenceUpdate(Pref pref); async RequestMemoryReport(uint32_t generation, bool anonymize, bool minimizeMemoryUsage, FileDescriptor? DMDFile) returns (uint32_t aGeneration); @@ -176,16 +179,18 @@ child: async GetSocketData() returns (SocketDataArgs data); async GetDNSCacheEntries() returns (DNSCacheEntries[] entries); async GetHttpConnectionData() returns (HttpRetParams[] params); + async InitProxyAutoConfigChild(Endpoint<PProxyAutoConfigChild> endpoint); + both: async PFileDescriptorSet(FileDescriptor fd); async PDNSRequest(nsCString hostName, nsCString trrServer, uint16_t type, OriginAttributes originAttributes, uint32_t flags); }; } // namespace net } // namespace mozilla
new file mode 100644 --- /dev/null +++ b/netwerk/ipc/ProxyAutoConfigChild.cpp @@ -0,0 +1,199 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#include "ProxyAutoConfigChild.h" + +#include "mozilla/ipc/Endpoint.h" +#include "mozilla/net/SocketProcessChild.h" +#include "mozilla/SpinEventLoopUntil.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsThreadUtils.h" +#include "ProxyAutoConfig.h" + +namespace mozilla::net { + +static bool sThreadLocalSetup = false; +static uint32_t sThreadLocalIndex = 0xdeadbeef; +StaticRefPtr<nsIThread> ProxyAutoConfigChild::sPACThread; +bool ProxyAutoConfigChild::sShutdownObserverRegistered = false; +Atomic<uint32_t> ProxyAutoConfigChild::sLiveActorCount(0); + +namespace { + +class ShutdownObserver final : public nsIObserver { + public: + ShutdownObserver() = default; + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + private: + ~ShutdownObserver() = default; +}; + +NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver) + +NS_IMETHODIMP +ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + ProxyAutoConfigChild::ShutdownPACThread(); + return NS_OK; +} + +} // namespace + +// static +bool ProxyAutoConfigChild::Create(Endpoint<PProxyAutoConfigChild>&& aEndpoint) { + if (!sPACThread && !CreatePACThread()) { + NS_WARNING("Failed to create pac thread!"); + return false; + } + + if (!sShutdownObserverRegistered) { + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + if (NS_WARN_IF(!obs)) { + return false; + } + nsCOMPtr<nsIObserver> observer = new ShutdownObserver(); + nsresult rv = obs->AddObserver(observer, "xpcom-shutdown-threads", false); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + sShutdownObserverRegistered = true; + } + + RefPtr<ProxyAutoConfigChild> actor = new ProxyAutoConfigChild(); + if (NS_FAILED(sPACThread->Dispatch( + NS_NewRunnableFunction("ProxyAutoConfigChild::ProxyAutoConfigChild", + [actor = std::move(actor), + endpoint = std::move(aEndpoint)]() mutable { + MOZ_ASSERT(endpoint.IsValid()); + + // Transfer ownership to PAC thread. If + // Bind() fails then we will release this + // reference in Destroy. + ProxyAutoConfigChild* actorTmp; + actor.forget(&actorTmp); + + if (!endpoint.Bind(actorTmp)) { + actorTmp->Destroy(); + } + })))) { + NS_WARNING("Failed to dispatch runnable!"); + return false; + } + + return true; +} + +// static +bool ProxyAutoConfigChild::CreatePACThread() { + MOZ_ASSERT(NS_IsMainThread()); + + if (SocketProcessChild::GetSingleton()->IsShuttingDown()) { + NS_WARNING("Trying to create pac thread after shutdown has already begun!"); + return false; + } + + nsCOMPtr<nsIThread> thread; + if (NS_FAILED(NS_NewNamedThread("ProxyResolution", getter_AddRefs(thread)))) { + NS_WARNING("NS_NewNamedThread failed!"); + return false; + } + + sPACThread = thread.forget(); + return true; +} + +// static +void ProxyAutoConfigChild::ShutdownPACThread() { + MOZ_ASSERT(NS_IsMainThread()); + + if (sPACThread) { + // Wait until all actos are released. + SpinEventLoopUntil("ProxyAutoConfigChild::ShutdownPACThread"_ns, + [&]() { return !sLiveActorCount; }); + + nsCOMPtr<nsIThread> thread = sPACThread.get(); + sPACThread = nullptr; + MOZ_ALWAYS_SUCCEEDS(thread->Shutdown()); + } +} + +ProxyAutoConfigChild::ProxyAutoConfigChild() + : mPAC(MakeUnique<ProxyAutoConfig>()) { + if (!sThreadLocalSetup) { + sThreadLocalSetup = true; + PR_NewThreadPrivateIndex(&sThreadLocalIndex, nullptr); + } + + mPAC->SetThreadLocalIndex(sThreadLocalIndex); + sLiveActorCount++; +} + +ProxyAutoConfigChild::~ProxyAutoConfigChild() { + MOZ_ASSERT(NS_IsMainThread()); + sLiveActorCount--; +} + +mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvConfigurePAC( + const nsCString& aPACURI, const nsCString& aPACScriptData, + const bool& aIncludePath, const uint32_t& aExtraHeapSize) { + mPAC->ConfigurePAC(aPACURI, aPACScriptData, aIncludePath, aExtraHeapSize, + GetMainThreadSerialEventTarget()); + return IPC_OK(); +} + +mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvGetProxyForURI( + const nsCString& aTestURI, const nsCString& aTestHost, + GetProxyForURIResolver&& aResolver) { + // When PAC is waiting for DNS result, we need to wait. + if (mPAC->WaitingForDNSResolve()) { + RefPtr<ProxyAutoConfigChild> self = this; + NS_DispatchToCurrentThread(NS_NewRunnableFunction( + "ProxyAutoConfigChild::RecvGetProxyForURI", + [self, testURI(aTestURI), testHost(aTestHost), + resolver{std::move(aResolver)}]() mutable { + self->RecvGetProxyForURI(testURI, testHost, std::move(resolver)); + })); + return IPC_OK(); + } + nsCString result; + nsresult rv = mPAC->GetProxyForURI(aTestURI, aTestHost, result); + aResolver(Tuple<const nsresult&, const nsCString&>(rv, result)); + return IPC_OK(); +} + +mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvGC() { + mPAC->GC(); + return IPC_OK(); +} + +void ProxyAutoConfigChild::ActorDestroy(ActorDestroyReason aWhy) { + UniquePtr<ProxyAutoConfig> pac(std::move(mPAC)); + pac->Shutdown(); + + // To avoid racing with the main thread, we need to dispatch + // ProxyAutoConfigChild::Destroy again. + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(NewNonOwningRunnableMethod( + "ProxyAutoConfigChild::Destroy", this, &ProxyAutoConfigChild::Destroy))); +} + +void ProxyAutoConfigChild::Destroy() { + // May be called on any thread! + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewNonOwningRunnableMethod( + "ProxyAutoConfigChild::MainThreadActorDestroy", this, + &ProxyAutoConfigChild::MainThreadActorDestroy))); +} + +void ProxyAutoConfigChild::MainThreadActorDestroy() { + MOZ_ASSERT(NS_IsMainThread()); + + // This may be the last reference! + Release(); +} + +} // namespace mozilla::net
new file mode 100644 --- /dev/null +++ b/netwerk/ipc/ProxyAutoConfigChild.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#ifndef ProxyAutoConfigChild_h__ +#define ProxyAutoConfigChild_h__ + +#include "mozilla/net/PProxyAutoConfigChild.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/UniquePtr.h" + +namespace mozilla { +namespace net { + +class ProxyAutoConfig; + +class ProxyAutoConfigChild final : public PProxyAutoConfigChild { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProxyAutoConfigChild) + + static bool Create(Endpoint<PProxyAutoConfigChild>&& aEndpoint); + static bool CreatePACThread(); + static void ShutdownPACThread(); + + ProxyAutoConfigChild(); + + void ActorDestroy(ActorDestroyReason aWhy) override; + mozilla::ipc::IPCResult RecvConfigurePAC(const nsCString& aPACURI, + const nsCString& aPACScriptData, + const bool& aIncludePath, + const uint32_t& aExtraHeapSize); + mozilla::ipc::IPCResult RecvGetProxyForURI( + const nsCString& aTestURI, const nsCString& aTestHost, + GetProxyForURIResolver&& aResolver); + + mozilla::ipc::IPCResult RecvGC(); + + void Destroy(); + + private: + virtual ~ProxyAutoConfigChild(); + void MainThreadActorDestroy(); + + UniquePtr<ProxyAutoConfig> mPAC; + + static StaticRefPtr<nsIThread> sPACThread; + static bool sShutdownObserverRegistered; + static Atomic<uint32_t> sLiveActorCount; +}; + +} // namespace net +} // namespace mozilla + +#endif // ProxyAutoConfigChild_h__
new file mode 100644 --- /dev/null +++ b/netwerk/ipc/ProxyAutoConfigParent.cpp @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#include "ProxyAutoConfigParent.h" + +#include "mozilla/ipc/Endpoint.h" +#include "nsIConsoleService.h" + +namespace mozilla::net { + +ProxyAutoConfigParent::ProxyAutoConfigParent() = default; + +ProxyAutoConfigParent::~ProxyAutoConfigParent() = default; + +void ProxyAutoConfigParent::Init(Endpoint<PProxyAutoConfigParent>&& aEndpoint) { + DebugOnly<bool> ok = aEndpoint.Bind(this); + MOZ_ASSERT(ok); +} + +} // namespace mozilla::net
new file mode 100644 --- /dev/null +++ b/netwerk/ipc/ProxyAutoConfigParent.h @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#ifndef ProxyAutoConfigParent_h__ +#define ProxyAutoConfigParent_h__ + +#include "mozilla/net/PProxyAutoConfigParent.h" + +namespace mozilla { +namespace net { + +class ProxyAutoConfigParent final : public PProxyAutoConfigParent { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ProxyAutoConfigParent) + + ProxyAutoConfigParent(); + void Init(Endpoint<PProxyAutoConfigParent>&& aEndpoint); + + private: + virtual ~ProxyAutoConfigParent(); +}; + +} // namespace net +} // namespace mozilla + +#endif // ProxyAutoConfigParent_h__
--- a/netwerk/ipc/SocketProcessChild.cpp +++ b/netwerk/ipc/SocketProcessChild.cpp @@ -20,33 +20,38 @@ #include "mozilla/ipc/FileDescriptorSetChild.h" #include "mozilla/ipc/IPCStreamAlloc.h" #include "mozilla/ipc/ProcessChild.h" #include "mozilla/net/AltSvcTransactionChild.h" #include "mozilla/net/BackgroundDataBridgeParent.h" #include "mozilla/net/DNSRequestChild.h" #include "mozilla/net/DNSRequestParent.h" #include "mozilla/net/NativeDNSResolverOverrideChild.h" +#include "mozilla/net/ProxyAutoConfigChild.h" #include "mozilla/net/TRRServiceChild.h" #include "mozilla/ipc/PChildToParentStreamChild.h" #include "mozilla/ipc/PParentToChildStreamChild.h" #include "mozilla/ipc/ProcessUtils.h" #include "mozilla/Preferences.h" #include "mozilla/RemoteLazyInputStreamChild.h" +#include "mozilla/StaticPrefs_network.h" #include "mozilla/Telemetry.h" #include "nsDebugImpl.h" #include "nsHttpConnectionInfo.h" #include "nsHttpHandler.h" #include "nsIDNSService.h" #include "nsIHttpActivityObserver.h" #include "nsNetUtil.h" #include "nsNSSComponent.h" #include "nsSocketTransportService2.h" #include "nsThreadManager.h" #include "SocketProcessBridgeParent.h" +#include "jsapi.h" +#include "js/Initialization.h" +#include "XPCSelfHostedShmem.h" #if defined(XP_WIN) # include <process.h> #else # include <unistd.h> #endif #if defined(XP_LINUX) && defined(MOZ_SANDBOX) @@ -63,16 +68,18 @@ # include "mozilla/SandboxTestingChild.h" #endif namespace mozilla { namespace net { using namespace ipc; +static bool sInitializedJS = false; + SocketProcessChild* sSocketProcessChild; SocketProcessChild::SocketProcessChild() { LOG(("CONSTRUCT SocketProcessChild::SocketProcessChild\n")); nsDebugImpl::SetMultiprocessMode("Socket"); MOZ_COUNT_CTOR(SocketProcessChild); sSocketProcessChild = this; @@ -116,16 +123,27 @@ bool SocketProcessChild::Init(base::Proc // Init crash reporter support. CrashReporterClient::InitSingleton(this); if (NS_FAILED(NS_InitMinimalXPCOM())) { return false; } + if (StaticPrefs::network_proxy_parse_pac_on_socket_process()) { + // For parsing PAC. + const char* jsInitFailureReason = JS_InitWithFailureDiagnostic(); + if (jsInitFailureReason) { + MOZ_CRASH_UNSAFE(jsInitFailureReason); + } + sInitializedJS = true; + + xpc::SelfHostedShmem::GetSingleton(); + } + BackgroundChild::Startup(); SetThisProcessName("Socket Process"); #if defined(XP_MACOSX) // Close all current connections to the WindowServer. This ensures that the // Activity Monitor will not label the socket process as "Not responding" // because it's not running a native event loop. See bug 1384336. CGSShutdownServerConnections(); #endif // XP_MACOSX @@ -176,16 +194,20 @@ void SocketProcessChild::CleanUp() { } } { MutexAutoLock lock(mMutex); mBackgroundDataBridgeMap.Clear(); } NS_ShutdownXPCOM(nullptr); + + if (sInitializedJS) { + JS_ShutDown(); + } } mozilla::ipc::IPCResult SocketProcessChild::RecvInit( const SocketPorcessInitAttributes& aAttributes) { Unused << RecvSetOffline(aAttributes.mOffline()); Unused << RecvSetConnectivity(aAttributes.mConnectivity()); if (aAttributes.mInitSandbox()) { Unused << RecvInitLinuxSandbox(aAttributes.mSandboxBroker()); @@ -613,10 +635,16 @@ mozilla::ipc::IPCResult SocketProcessChi nsTArray<HttpRetParams> data; HttpInfo::GetHttpConnectionData(&data); resolver->OnResolve(std::move(data)); }), NS_DISPATCH_NORMAL); return IPC_OK(); } +mozilla::ipc::IPCResult SocketProcessChild::RecvInitProxyAutoConfigChild( + Endpoint<PProxyAutoConfigChild>&& aEndpoint) { + Unused << ProxyAutoConfigChild::Create(std::move(aEndpoint)); + return IPC_OK(); +} + } // namespace net } // namespace mozilla
--- a/netwerk/ipc/SocketProcessChild.h +++ b/netwerk/ipc/SocketProcessChild.h @@ -14,16 +14,17 @@ namespace mozilla { class ChildProfilerController; } namespace mozilla { namespace net { +class ProxyAutoConfigChild; class SocketProcessBridgeParent; class BackgroundDataBridgeParent; // The IPC actor implements PSocketProcessChild in child process. // This is allocated and kept alive by SocketProcessImpl. class SocketProcessChild final : public PSocketProcessChild, public mozilla::ipc::ChildToParentStreamActorManager { @@ -136,16 +137,19 @@ class SocketProcessChild final AllocPRemoteLazyInputStreamChild(const nsID& aID, const uint64_t& aSize); mozilla::ipc::IPCResult RecvGetSocketData(GetSocketDataResolver&& aResolve); mozilla::ipc::IPCResult RecvGetDNSCacheEntries( GetDNSCacheEntriesResolver&& aResolve); mozilla::ipc::IPCResult RecvGetHttpConnectionData( GetHttpConnectionDataResolver&& aResolve); + mozilla::ipc::IPCResult RecvInitProxyAutoConfigChild( + Endpoint<PProxyAutoConfigChild>&& aEndpoint); + protected: friend class SocketProcessImpl; ~SocketProcessChild(); private: // Mapping of content process id and the SocketProcessBridgeParent. // This table keeps SocketProcessBridgeParent alive in socket process. nsRefPtrHashtable<nsUint32HashKey, SocketProcessBridgeParent>
--- a/netwerk/ipc/SocketProcessParent.cpp +++ b/netwerk/ipc/SocketProcessParent.cpp @@ -17,16 +17,17 @@ #include "mozilla/ipc/PChildToParentStreamParent.h" #include "mozilla/ipc/PParentToChildStreamParent.h" #include "mozilla/net/DNSRequestParent.h" #include "mozilla/net/ProxyConfigLookupParent.h" #include "mozilla/RemoteLazyInputStreamParent.h" #include "mozilla/Telemetry.h" #include "mozilla/TelemetryIPC.h" #include "nsIAppStartup.h" +#include "nsIConsoleService.h" #include "nsIHttpActivityObserver.h" #include "nsIObserverService.h" #include "nsNSSIOLayer.h" #include "PSMIPCCommon.h" #include "secerr.h" #ifdef MOZ_WEBRTC # include "mozilla/dom/ContentProcessManager.h" # include "mozilla/dom/BrowserParent.h" @@ -451,10 +452,20 @@ mozilla::ipc::IPCResult SocketProcessPar if (cinfo->IsHttp3()) { gHttpHandler->ExcludeHttp3(cinfo); } else { gHttpHandler->ExcludeHttp2(cinfo); } return IPC_OK(); } +mozilla::ipc::IPCResult SocketProcessParent::RecvOnConsoleMessage( + const nsString& aMessage) { + nsCOMPtr<nsIConsoleService> consoleService = + do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (consoleService) { + consoleService->LogStringMessage(aMessage.get()); + } + return IPC_OK(); +} + } // namespace net } // namespace mozilla
--- a/netwerk/ipc/SocketProcessParent.h +++ b/netwerk/ipc/SocketProcessParent.h @@ -118,16 +118,17 @@ class SocketProcessParent final mozilla::ipc::IPCResult RecvPRemoteLazyInputStreamConstructor( PRemoteLazyInputStreamParent* aActor, const nsID& aID, const uint64_t& aSize); mozilla::ipc::IPCResult RecvODoHServiceActivated(const bool& aActivated); mozilla::ipc::IPCResult RecvExcludeHttp2OrHttp3( const HttpConnectionInfoCloneArgs& aArgs); + mozilla::ipc::IPCResult RecvOnConsoleMessage(const nsString& aMessage); private: SocketProcessHost* mHost; UniquePtr<dom::MemoryReportRequestHost> mMemoryReportRequest; static void Destroy(UniquePtr<SocketProcessParent>&& aParent); };
--- a/netwerk/ipc/moz.build +++ b/netwerk/ipc/moz.build @@ -14,16 +14,18 @@ EXPORTS.mozilla.net += [ "InputChannelThrottleQueueParent.h", "NeckoChild.h", "NeckoCommon.h", "NeckoMessageUtils.h", "NeckoParent.h", "NeckoTargetHolder.h", "ParentChannelWrapper.h", "ParentProcessDocumentChannel.h", + "ProxyAutoConfigChild.h", + "ProxyAutoConfigParent.h", "ProxyConfigLookup.h", "ProxyConfigLookupChild.h", "ProxyConfigLookupParent.h", "SocketProcessBridgeChild.h", "SocketProcessBridgeParent.h", "SocketProcessChild.h", "SocketProcessHost.h", "SocketProcessImpl.h", @@ -49,27 +51,34 @@ UNIFIED_SOURCES += [ "SocketProcessBridgeChild.cpp", "SocketProcessBridgeParent.cpp", "SocketProcessChild.cpp", "SocketProcessHost.cpp", "SocketProcessImpl.cpp", "SocketProcessParent.cpp", ] +SOURCES += [ + "ProxyAutoConfigChild.cpp", + "ProxyAutoConfigParent.cpp", +] + + PREPROCESSED_IPDL_SOURCES += [ "PNecko.ipdl", "PSocketProcess.ipdl", ] IPDL_SOURCES = [ "NeckoChannelParams.ipdlh", "PDataChannel.ipdl", "PDocumentChannel.ipdl", "PFileChannel.ipdl", "PInputChannelThrottleQueue.ipdl", + "PProxyAutoConfig.ipdl", "PProxyConfigLookup.ipdl", "PSimpleChannel.ipdl", "PSocketProcessBridge.ipdl", ] # needed so --disable-webrtc builds work (yes, a bit messy) if not CONFIG["MOZ_WEBRTC"]: IPDL_SOURCES += [