tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ProxyAutoConfigChild.cpp (6617B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "ProxyAutoConfigChild.h"
      7 
      8 #include "mozilla/ipc/Endpoint.h"
      9 #include "mozilla/net/SocketProcessChild.h"
     10 #include "mozilla/SpinEventLoopUntil.h"
     11 #include "nsIObserver.h"
     12 #include "nsIObserverService.h"
     13 #include "nsThreadUtils.h"
     14 #include "ProxyAutoConfig.h"
     15 
     16 namespace mozilla::net {
     17 
     18 static bool sThreadLocalSetup = false;
     19 static uint32_t sThreadLocalIndex = 0xdeadbeef;
     20 StaticRefPtr<nsIThread> ProxyAutoConfigChild::sPACThread;
     21 bool ProxyAutoConfigChild::sShutdownObserverRegistered = false;
     22 static StaticRefPtr<ProxyAutoConfigChild> sActor;
     23 
     24 namespace {
     25 
     26 class ShutdownObserver final : public nsIObserver {
     27 public:
     28  ShutdownObserver() = default;
     29 
     30  NS_DECL_ISUPPORTS
     31  NS_DECL_NSIOBSERVER
     32 
     33 private:
     34  ~ShutdownObserver() = default;
     35 };
     36 
     37 NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
     38 
     39 NS_IMETHODIMP
     40 ShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
     41                          const char16_t* aData) {
     42  ProxyAutoConfigChild::ShutdownPACThread();
     43  return NS_OK;
     44 }
     45 
     46 }  // namespace
     47 
     48 // static
     49 void ProxyAutoConfigChild::BindProxyAutoConfigChild(
     50    RefPtr<ProxyAutoConfigChild>&& aActor,
     51    Endpoint<PProxyAutoConfigChild>&& aEndpoint) {
     52  // We only allow one ProxyAutoConfigChild at a time, so we need to
     53  // wait until the old one to be destroyed.
     54  if (sActor) {
     55    NS_DispatchToCurrentThread(NS_NewRunnableFunction(
     56        "BindProxyAutoConfigChild",
     57        [actor = std::move(aActor), endpoint = std::move(aEndpoint)]() mutable {
     58          ProxyAutoConfigChild::BindProxyAutoConfigChild(std::move(actor),
     59                                                         std::move(endpoint));
     60        }));
     61    return;
     62  }
     63 
     64  if (aEndpoint.Bind(aActor)) {
     65    sActor = aActor;
     66  }
     67 }
     68 
     69 // static
     70 bool ProxyAutoConfigChild::Create(Endpoint<PProxyAutoConfigChild>&& aEndpoint) {
     71  if (!sPACThread && !CreatePACThread()) {
     72    NS_WARNING("Failed to create pac thread!");
     73    return false;
     74  }
     75 
     76  if (!sShutdownObserverRegistered) {
     77    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     78    if (NS_WARN_IF(!obs)) {
     79      return false;
     80    }
     81    nsCOMPtr<nsIObserver> observer = new ShutdownObserver();
     82    nsresult rv = obs->AddObserver(observer, "xpcom-shutdown-threads", false);
     83    if (NS_WARN_IF(NS_FAILED(rv))) {
     84      return false;
     85    }
     86    sShutdownObserverRegistered = true;
     87  }
     88 
     89  RefPtr<ProxyAutoConfigChild> actor = new ProxyAutoConfigChild();
     90  if (NS_FAILED(sPACThread->Dispatch(NS_NewRunnableFunction(
     91          "ProxyAutoConfigChild::ProxyAutoConfigChild",
     92          [actor = std::move(actor),
     93           endpoint = std::move(aEndpoint)]() mutable {
     94            MOZ_ASSERT(endpoint.IsValid());
     95            ProxyAutoConfigChild::BindProxyAutoConfigChild(std::move(actor),
     96                                                           std::move(endpoint));
     97          })))) {
     98    NS_WARNING("Failed to dispatch runnable!");
     99    return false;
    100  }
    101 
    102  return true;
    103 }
    104 
    105 // static
    106 bool ProxyAutoConfigChild::CreatePACThread() {
    107  MOZ_ASSERT(NS_IsMainThread());
    108 
    109  if (SocketProcessChild::GetSingleton()->IsShuttingDown()) {
    110    NS_WARNING("Trying to create pac thread after shutdown has already begun!");
    111    return false;
    112  }
    113 
    114  nsCOMPtr<nsIThread> thread;
    115  if (NS_FAILED(NS_NewNamedThread("ProxyResolution", getter_AddRefs(thread)))) {
    116    NS_WARNING("NS_NewNamedThread failed!");
    117    return false;
    118  }
    119 
    120  sPACThread = thread.forget();
    121  return true;
    122 }
    123 
    124 // static
    125 void ProxyAutoConfigChild::ShutdownPACThread() {
    126  MOZ_ASSERT(NS_IsMainThread());
    127 
    128  if (sPACThread) {
    129    // Wait until all actos are released.
    130    SpinEventLoopUntil("ProxyAutoConfigChild::ShutdownPACThread"_ns,
    131                       [&]() { return !sActor; });
    132 
    133    nsCOMPtr<nsIThread> thread = sPACThread.get();
    134    sPACThread = nullptr;
    135    MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
    136  }
    137 }
    138 
    139 ProxyAutoConfigChild::ProxyAutoConfigChild()
    140    : mPAC(MakeUnique<ProxyAutoConfig>()) {
    141  if (!sThreadLocalSetup) {
    142    sThreadLocalSetup = true;
    143    PR_NewThreadPrivateIndex(&sThreadLocalIndex, nullptr);
    144  }
    145 
    146  mPAC->SetThreadLocalIndex(sThreadLocalIndex);
    147 }
    148 
    149 ProxyAutoConfigChild::~ProxyAutoConfigChild() = default;
    150 
    151 mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvConfigurePAC(
    152    const nsACString& aPACURI, const nsACString& aPACScriptData,
    153    const bool& aIncludePath, const uint32_t& aExtraHeapSize) {
    154  mPAC->ConfigurePAC(aPACURI, aPACScriptData, aIncludePath, aExtraHeapSize,
    155                     GetMainThreadSerialEventTarget());
    156  mPACLoaded = true;
    157  NS_DispatchToCurrentThread(
    158      NewRunnableMethod("ProxyAutoConfigChild::ProcessPendingQ", this,
    159                        &ProxyAutoConfigChild::ProcessPendingQ));
    160  return IPC_OK();
    161 }
    162 
    163 void ProxyAutoConfigChild::PendingQuery::Resolve(nsresult aStatus,
    164                                                 const nsACString& aResult) {
    165  mResolver(std::tuple<const nsresult&, const nsACString&>(aStatus, aResult));
    166 }
    167 
    168 mozilla::ipc::IPCResult ProxyAutoConfigChild::RecvGetProxyForURI(
    169    const nsACString& aTestURI, const nsACString& aTestHost,
    170    GetProxyForURIResolver&& aResolver) {
    171  mPendingQ.insertBack(
    172      new PendingQuery(aTestURI, aTestHost, std::move(aResolver)));
    173  ProcessPendingQ();
    174  return IPC_OK();
    175 }
    176 
    177 void ProxyAutoConfigChild::ProcessPendingQ() {
    178  while (ProcessPending()) {
    179    ;
    180  }
    181 
    182  if (mShutdown) {
    183    mPAC->Shutdown();
    184  } else {
    185    // do GC while the thread has nothing pending
    186    mPAC->GC();
    187  }
    188 }
    189 
    190 bool ProxyAutoConfigChild::ProcessPending() {
    191  if (mPendingQ.isEmpty()) {
    192    return false;
    193  }
    194 
    195  if (mInProgress || !mPACLoaded) {
    196    return false;
    197  }
    198 
    199  if (mShutdown) {
    200    return true;
    201  }
    202 
    203  mInProgress = true;
    204  RefPtr<PendingQuery> query = mPendingQ.popFirst();
    205  nsCString result;
    206  nsresult rv = mPAC->GetProxyForURI(query->URI(), query->Host(), result);
    207  query->Resolve(rv, result);
    208  mInProgress = false;
    209  return true;
    210 }
    211 
    212 void ProxyAutoConfigChild::ActorDestroy(ActorDestroyReason aWhy) {
    213  mPendingQ.clear();
    214  mShutdown = true;
    215  mPAC->Shutdown();
    216 
    217  // To avoid racing with the main thread, we need to dispatch
    218  // ProxyAutoConfigChild::Destroy again.
    219  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(NewNonOwningRunnableMethod(
    220      "ProxyAutoConfigChild::Destroy", this, &ProxyAutoConfigChild::Destroy)));
    221 }
    222 
    223 void ProxyAutoConfigChild::Destroy() { sActor = nullptr; }
    224 
    225 }  // namespace mozilla::net