tor-browser

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

nsHttpConnectionMgr.cpp (137958B)


      1 /* vim:set ts=4 sw=2 sts=2 et cin: */
      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 // HttpLog.h should generally be included first
      7 #include "HttpLog.h"
      8 
      9 // Log on level :5, instead of default :4.
     10 #undef LOG
     11 #define LOG(args) LOG5(args)
     12 #undef LOG_ENABLED
     13 #define LOG_ENABLED() LOG5_ENABLED()
     14 
     15 #include <algorithm>
     16 #include <utility>
     17 
     18 #include "ConnectionHandle.h"
     19 #include "HttpConnectionUDP.h"
     20 #include "NullHttpTransaction.h"
     21 #include "SpeculativeTransaction.h"
     22 #include "mozilla/Components.h"
     23 #include "mozilla/PerfStats.h"
     24 #include "mozilla/ProfilerMarkers.h"
     25 #include "mozilla/SpinEventLoopUntil.h"
     26 #include "mozilla/StaticPrefs_network.h"
     27 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
     28 #include "mozilla/glean/NetwerkMetrics.h"
     29 #include "mozilla/net/DNS.h"
     30 #include "mozilla/net/DashboardTypes.h"
     31 #include "nsCOMPtr.h"
     32 #include "nsHttpConnectionMgr.h"
     33 #include "nsHttpHandler.h"
     34 #include "nsIClassOfService.h"
     35 #include "nsIDNSByTypeRecord.h"
     36 #include "nsIDNSListener.h"
     37 #include "nsIDNSRecord.h"
     38 #include "nsIDNSService.h"
     39 #include "nsIHttpChannelInternal.h"
     40 #include "nsIPipe.h"
     41 #include "nsIRequestContext.h"
     42 #include "nsISocketTransport.h"
     43 #include "nsISocketTransportService.h"
     44 #include "nsITransport.h"
     45 #include "nsIXPConnect.h"
     46 #include "nsInterfaceRequestorAgg.h"
     47 #include "nsNetCID.h"
     48 #include "nsNetSegmentUtils.h"
     49 #include "nsNetUtil.h"
     50 #include "nsQueryObject.h"
     51 #include "nsSocketTransportService2.h"
     52 #include "nsStreamUtils.h"
     53 
     54 using namespace mozilla;
     55 
     56 namespace geckoprofiler::markers {
     57 
     58 struct UrlMarker {
     59  static constexpr Span<const char> MarkerTypeName() {
     60    return MakeStringSpan("Url");
     61  }
     62  static void StreamJSONMarkerData(
     63      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
     64      const mozilla::ProfilerString8View& aURL, const TimeDuration& aDuration,
     65      uint64_t aChannelId) {
     66    if (aURL.Length() != 0) {
     67      aWriter.StringProperty("url", aURL);
     68    }
     69    if (!aDuration.IsZero()) {
     70      aWriter.DoubleProperty("duration", aDuration.ToMilliseconds());
     71    }
     72    aWriter.IntProperty("channelId", static_cast<int64_t>(aChannelId));
     73  }
     74  static MarkerSchema MarkerTypeDisplay() {
     75    using MS = MarkerSchema;
     76    MS schema(MS::Location::MarkerChart, MS::Location::MarkerTable);
     77    schema.SetTableLabel("{marker.data.url}");
     78    schema.AddKeyFormat("url", MS::Format::Url, MS::PayloadFlags::Searchable);
     79    schema.AddKeyLabelFormat("duration", "Duration", MS::Format::Duration);
     80    return schema;
     81  }
     82 };
     83 
     84 }  // namespace geckoprofiler::markers
     85 
     86 namespace mozilla::net {
     87 
     88 //-----------------------------------------------------------------------------
     89 
     90 NS_IMPL_ISUPPORTS(nsHttpConnectionMgr, nsIObserver, nsINamed)
     91 
     92 //-----------------------------------------------------------------------------
     93 
     94 nsHttpConnectionMgr::nsHttpConnectionMgr() {
     95  LOG(("Creating nsHttpConnectionMgr @%p\n", this));
     96 }
     97 
     98 nsHttpConnectionMgr::~nsHttpConnectionMgr() {
     99  LOG(("Destroying nsHttpConnectionMgr @%p\n", this));
    100  MOZ_ASSERT(mCoalescingHash.Count() == 0);
    101  if (mTimeoutTick) mTimeoutTick->Cancel();
    102 }
    103 
    104 nsresult nsHttpConnectionMgr::EnsureSocketThreadTarget() {
    105  nsCOMPtr<nsIEventTarget> sts;
    106  nsCOMPtr<nsIIOService> ioService = components::IO::Service();
    107  if (ioService) {
    108    nsCOMPtr<nsISocketTransportService> realSTS =
    109        components::SocketTransport::Service();
    110    sts = do_QueryInterface(realSTS);
    111  }
    112 
    113  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    114 
    115  // do nothing if already initialized or if we've shut down
    116  if (mSocketThreadTarget || mIsShuttingDown) return NS_OK;
    117 
    118  mSocketThreadTarget = sts;
    119 
    120  return sts ? NS_OK : NS_ERROR_NOT_AVAILABLE;
    121 }
    122 
    123 nsresult nsHttpConnectionMgr::Init(
    124    uint16_t maxUrgentExcessiveConns, uint16_t maxConns,
    125    uint16_t maxPersistConnsPerHost, uint16_t maxPersistConnsPerProxy,
    126    uint16_t maxRequestDelay, bool throttleEnabled, uint32_t throttleSuspendFor,
    127    uint32_t throttleResumeFor, uint32_t throttleHoldTime,
    128    uint32_t throttleMaxTime, bool beConservativeForProxy) {
    129  LOG(("nsHttpConnectionMgr::Init\n"));
    130 
    131  {
    132    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    133 
    134    mMaxUrgentExcessiveConns = maxUrgentExcessiveConns;
    135    mMaxConns = maxConns;
    136    mMaxPersistConnsPerHost = maxPersistConnsPerHost;
    137    mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
    138    mMaxRequestDelay = maxRequestDelay;
    139 
    140    mThrottleEnabled = throttleEnabled;
    141    mThrottleSuspendFor = throttleSuspendFor;
    142    mThrottleResumeFor = throttleResumeFor;
    143    mThrottleHoldTime = throttleHoldTime;
    144    mThrottleMaxTime = TimeDuration::FromMilliseconds(throttleMaxTime);
    145 
    146    mBeConservativeForProxy = beConservativeForProxy;
    147 
    148    mIsShuttingDown = false;
    149  }
    150 
    151  return EnsureSocketThreadTarget();
    152 }
    153 
    154 class BoolWrapper : public ARefBase {
    155 public:
    156  BoolWrapper() = default;
    157  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BoolWrapper, override)
    158 
    159 public:  // intentional!
    160  bool mBool{false};
    161 
    162 private:
    163  virtual ~BoolWrapper() = default;
    164 };
    165 
    166 nsresult nsHttpConnectionMgr::Shutdown() {
    167  LOG(("nsHttpConnectionMgr::Shutdown\n"));
    168 
    169  RefPtr<BoolWrapper> shutdownWrapper = new BoolWrapper();
    170  {
    171    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    172 
    173    // do nothing if already shutdown
    174    if (!mSocketThreadTarget) return NS_OK;
    175 
    176    nsresult rv =
    177        PostEvent(&nsHttpConnectionMgr::OnMsgShutdown, 0, shutdownWrapper);
    178 
    179    // release our reference to the STS to prevent further events
    180    // from being posted.  this is how we indicate that we are
    181    // shutting down.
    182    mIsShuttingDown = true;
    183    mSocketThreadTarget = nullptr;
    184 
    185    if (NS_FAILED(rv)) {
    186      NS_WARNING("unable to post SHUTDOWN message");
    187      return rv;
    188    }
    189  }
    190 
    191  // wait for shutdown event to complete
    192  SpinEventLoopUntil("nsHttpConnectionMgr::Shutdown"_ns,
    193                     [&, shutdownWrapper]() { return shutdownWrapper->mBool; });
    194 
    195  return NS_OK;
    196 }
    197 
    198 class ConnEvent : public Runnable {
    199 public:
    200  ConnEvent(nsHttpConnectionMgr* mgr, nsConnEventHandler handler,
    201            int32_t iparam, ARefBase* vparam)
    202      : Runnable("net::ConnEvent"),
    203        mMgr(mgr),
    204        mHandler(handler),
    205        mIParam(iparam),
    206        mVParam(vparam) {}
    207 
    208  NS_IMETHOD Run() override {
    209    (mMgr->*mHandler)(mIParam, mVParam);
    210    return NS_OK;
    211  }
    212 
    213 private:
    214  virtual ~ConnEvent() = default;
    215 
    216  RefPtr<nsHttpConnectionMgr> mMgr;
    217  nsConnEventHandler mHandler;
    218  int32_t mIParam;
    219  RefPtr<ARefBase> mVParam;
    220 };
    221 
    222 nsresult nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler,
    223                                        int32_t iparam, ARefBase* vparam) {
    224  (void)EnsureSocketThreadTarget();
    225 
    226  nsCOMPtr<nsIEventTarget> target;
    227  {
    228    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    229    target = mSocketThreadTarget;
    230  }
    231 
    232  if (!target) {
    233    NS_WARNING("cannot post event if not initialized");
    234    return NS_ERROR_NOT_INITIALIZED;
    235  }
    236 
    237  nsCOMPtr<nsIRunnable> event = new ConnEvent(this, handler, iparam, vparam);
    238  return target->Dispatch(event, NS_DISPATCH_NORMAL);
    239 }
    240 
    241 void nsHttpConnectionMgr::PruneDeadConnectionsAfter(uint32_t timeInSeconds) {
    242  LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));
    243 
    244  if (!mTimer) mTimer = NS_NewTimer();
    245 
    246  // failure to create a timer is not a fatal error, but idle connections
    247  // will not be cleaned up until we try to use them.
    248  if (mTimer) {
    249    mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
    250    mTimer->Init(this, timeInSeconds * 1000, nsITimer::TYPE_ONE_SHOT);
    251  } else {
    252    NS_WARNING("failed to create: timer for pruning the dead connections!");
    253  }
    254 }
    255 
    256 void nsHttpConnectionMgr::ConditionallyStopPruneDeadConnectionsTimer() {
    257  // Leave the timer in place if there are connections that potentially
    258  // need management
    259  if (mNumIdleConns ||
    260      (mNumActiveConns && StaticPrefs::network_http_http2_enabled())) {
    261    return;
    262  }
    263 
    264  LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));
    265 
    266  // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
    267  mTimeOfNextWakeUp = UINT64_MAX;
    268  if (mTimer) {
    269    mTimer->Cancel();
    270    mTimer = nullptr;
    271  }
    272 }
    273 
    274 void nsHttpConnectionMgr::ConditionallyStopTimeoutTick() {
    275  LOG(
    276      ("nsHttpConnectionMgr::ConditionallyStopTimeoutTick "
    277       "armed=%d active=%d\n",
    278       mTimeoutTickArmed, mNumActiveConns));
    279 
    280  if (!mTimeoutTickArmed) return;
    281 
    282  if (mNumActiveConns) return;
    283 
    284  LOG(("nsHttpConnectionMgr::ConditionallyStopTimeoutTick stop==true\n"));
    285 
    286  mTimeoutTick->Cancel();
    287  mTimeoutTickArmed = false;
    288 }
    289 
    290 //-----------------------------------------------------------------------------
    291 // nsHttpConnectionMgr::nsINamed
    292 //-----------------------------------------------------------------------------
    293 
    294 NS_IMETHODIMP
    295 nsHttpConnectionMgr::GetName(nsACString& aName) {
    296  aName.AssignLiteral("nsHttpConnectionMgr");
    297  return NS_OK;
    298 }
    299 
    300 //-----------------------------------------------------------------------------
    301 // nsHttpConnectionMgr::nsIObserver
    302 //-----------------------------------------------------------------------------
    303 
    304 NS_IMETHODIMP
    305 nsHttpConnectionMgr::Observe(nsISupports* subject, const char* topic,
    306                             const char16_t* data) {
    307  LOG(("nsHttpConnectionMgr::Observe [topic=\"%s\"]\n", topic));
    308 
    309  if (0 == strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
    310    nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
    311    if (timer == mTimer) {
    312      (void)PruneDeadConnections();
    313    } else if (timer == mTimeoutTick) {
    314      TimeoutTick();
    315    } else if (timer == mTrafficTimer) {
    316      (void)PruneNoTraffic();
    317    } else if (timer == mThrottleTicker) {
    318      ThrottlerTick();
    319    } else if (timer == mDelayedResumeReadTimer) {
    320      ResumeBackgroundThrottledTransactions();
    321    } else {
    322      MOZ_ASSERT(false, "unexpected timer-callback");
    323      LOG(("Unexpected timer object\n"));
    324      return NS_ERROR_UNEXPECTED;
    325    }
    326  }
    327 
    328  return NS_OK;
    329 }
    330 
    331 //-----------------------------------------------------------------------------
    332 
    333 nsresult nsHttpConnectionMgr::AddTransaction(HttpTransactionShell* trans,
    334                                             int32_t priority) {
    335  LOG(("nsHttpConnectionMgr::AddTransaction [trans=%p %d]\n", trans, priority));
    336  // Make sure a transaction is not in a pending queue.
    337  CheckTransInPendingQueue(trans->AsHttpTransaction());
    338  return PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority,
    339                   trans->AsHttpTransaction());
    340 }
    341 
    342 class NewTransactionData : public ARefBase {
    343 public:
    344  NewTransactionData(nsHttpTransaction* trans, int32_t priority,
    345                     nsHttpTransaction* transWithStickyConn)
    346      : mTrans(trans),
    347        mPriority(priority),
    348        mTransWithStickyConn(transWithStickyConn) {}
    349 
    350  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NewTransactionData, override)
    351 
    352  RefPtr<nsHttpTransaction> mTrans;
    353  int32_t mPriority;
    354  RefPtr<nsHttpTransaction> mTransWithStickyConn;
    355 
    356 private:
    357  virtual ~NewTransactionData() = default;
    358 };
    359 
    360 nsresult nsHttpConnectionMgr::AddTransactionWithStickyConn(
    361    HttpTransactionShell* trans, int32_t priority,
    362    HttpTransactionShell* transWithStickyConn) {
    363  LOG(
    364      ("nsHttpConnectionMgr::AddTransactionWithStickyConn "
    365       "[trans=%p %d transWithStickyConn=%p]\n",
    366       trans, priority, transWithStickyConn));
    367  // Make sure a transaction is not in a pending queue.
    368  CheckTransInPendingQueue(trans->AsHttpTransaction());
    369 
    370  RefPtr<NewTransactionData> data =
    371      new NewTransactionData(trans->AsHttpTransaction(), priority,
    372                             transWithStickyConn->AsHttpTransaction());
    373  return PostEvent(&nsHttpConnectionMgr::OnMsgNewTransactionWithStickyConn, 0,
    374                   data);
    375 }
    376 
    377 nsresult nsHttpConnectionMgr::RescheduleTransaction(HttpTransactionShell* trans,
    378                                                    int32_t priority) {
    379  LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%p %d]\n", trans,
    380       priority));
    381  return PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority,
    382                   trans->AsHttpTransaction());
    383 }
    384 
    385 void nsHttpConnectionMgr::UpdateClassOfServiceOnTransaction(
    386    HttpTransactionShell* trans, const ClassOfService& classOfService) {
    387  LOG(
    388      ("nsHttpConnectionMgr::UpdateClassOfServiceOnTransaction [trans=%p "
    389       "classOfService flags=%" PRIu32 " inc=%d]\n",
    390       trans, static_cast<uint32_t>(classOfService.Flags()),
    391       classOfService.Incremental()));
    392 
    393  (void)EnsureSocketThreadTarget();
    394 
    395  nsCOMPtr<nsIEventTarget> target;
    396  {
    397    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    398    target = mSocketThreadTarget;
    399  }
    400 
    401  if (!target) {
    402    NS_WARNING("cannot post event if not initialized");
    403    return;
    404  }
    405 
    406  RefPtr<nsHttpConnectionMgr> self(this);
    407  (void)target->Dispatch(NS_NewRunnableFunction(
    408      "nsHttpConnectionMgr::CallUpdateClassOfServiceOnTransaction",
    409      [cos{classOfService}, self{std::move(self)}, trans = RefPtr{trans}]() {
    410        self->OnMsgUpdateClassOfServiceOnTransaction(
    411            cos, trans->AsHttpTransaction());
    412      }));
    413 }
    414 
    415 nsresult nsHttpConnectionMgr::CancelTransaction(HttpTransactionShell* trans,
    416                                                nsresult reason) {
    417  LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%p reason=%" PRIx32 "]\n",
    418       trans, static_cast<uint32_t>(reason)));
    419  return PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction,
    420                   static_cast<int32_t>(reason), trans->AsHttpTransaction());
    421 }
    422 
    423 nsresult nsHttpConnectionMgr::PruneDeadConnections() {
    424  return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
    425 }
    426 
    427 //
    428 // Called after a timeout. Check for active connections that have had no
    429 // traffic since they were "marked" and nuke them.
    430 nsresult nsHttpConnectionMgr::PruneNoTraffic() {
    431  LOG(("nsHttpConnectionMgr::PruneNoTraffic\n"));
    432  mPruningNoTraffic = true;
    433  return PostEvent(&nsHttpConnectionMgr::OnMsgPruneNoTraffic);
    434 }
    435 
    436 nsresult nsHttpConnectionMgr::VerifyTraffic() {
    437  LOG(("nsHttpConnectionMgr::VerifyTraffic\n"));
    438  return PostEvent(&nsHttpConnectionMgr::OnMsgVerifyTraffic);
    439 }
    440 
    441 nsresult nsHttpConnectionMgr::DoShiftReloadConnectionCleanup() {
    442  return PostEvent(&nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup, 0,
    443                   nullptr);
    444 }
    445 
    446 nsresult nsHttpConnectionMgr::DoShiftReloadConnectionCleanupWithConnInfo(
    447    nsHttpConnectionInfo* aCI) {
    448  if (!aCI) {
    449    return NS_ERROR_INVALID_ARG;
    450  }
    451 
    452  RefPtr<nsHttpConnectionInfo> ci = aCI->Clone();
    453  return PostEvent(&nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup, 0,
    454                   ci);
    455 }
    456 
    457 nsresult nsHttpConnectionMgr::DoSingleConnectionCleanup(
    458    nsHttpConnectionInfo* aCI) {
    459  if (!aCI) {
    460    return NS_ERROR_INVALID_ARG;
    461  }
    462 
    463  RefPtr<nsHttpConnectionInfo> ci = aCI->Clone();
    464  return PostEvent(&nsHttpConnectionMgr::OnMsgDoSingleConnectionCleanup, 0, ci);
    465 }
    466 
    467 class SpeculativeConnectArgs : public ARefBase {
    468 public:
    469  SpeculativeConnectArgs() = default;
    470  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SpeculativeConnectArgs, override)
    471 
    472 public:  // intentional!
    473  RefPtr<SpeculativeTransaction> mTrans;
    474 
    475  bool mFetchHTTPSRR{false};
    476 
    477 private:
    478  virtual ~SpeculativeConnectArgs() = default;
    479  NS_DECL_OWNINGTHREAD
    480 };
    481 
    482 nsresult nsHttpConnectionMgr::SpeculativeConnect(
    483    nsHttpConnectionInfo* ci, nsIInterfaceRequestor* callbacks, uint32_t caps,
    484    SpeculativeTransaction* aTransaction, bool aFetchHTTPSRR) {
    485  if (!IsNeckoChild() && NS_IsMainThread()) {
    486    // HACK: make sure PSM gets initialized on the main thread.
    487    net_EnsurePSMInit();
    488  }
    489 
    490  LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n",
    491       ci->HashKey().get()));
    492 
    493  nsCOMPtr<nsISpeculativeConnectionOverrider> overrider =
    494      do_GetInterface(callbacks);
    495 
    496  bool allow1918 = overrider ? overrider->GetAllow1918() : false;
    497 
    498  // Hosts that are Local IP Literals should not be speculatively
    499  // connected - Bug 853423.
    500  if ((!allow1918) && ci && ci->HostIsLocalIPLiteral()) {
    501    LOG(
    502        ("nsHttpConnectionMgr::SpeculativeConnect skipping RFC1918 "
    503         "address [%s]",
    504         ci->Origin()));
    505    return NS_OK;
    506  }
    507 
    508  nsAutoCString url(ci->EndToEndSSL() ? "https://"_ns : "http://"_ns);
    509  url += ci->GetOrigin();
    510  PROFILER_MARKER("SpeculativeConnect", NETWORK, {}, UrlMarker, url,
    511                  TimeDuration::Zero(), 0);
    512 
    513  RefPtr<SpeculativeConnectArgs> args = new SpeculativeConnectArgs();
    514 
    515  // Wrap up the callbacks and the target to ensure they're released on the
    516  // target thread properly.
    517  nsCOMPtr<nsIInterfaceRequestor> wrappedCallbacks;
    518  NS_NewInterfaceRequestorAggregation(callbacks, nullptr,
    519                                      getter_AddRefs(wrappedCallbacks));
    520 
    521  caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
    522  caps |= NS_HTTP_ERROR_SOFTLY;
    523  args->mTrans = aTransaction
    524                     ? aTransaction
    525                     : new SpeculativeTransaction(ci, wrappedCallbacks, caps);
    526  args->mFetchHTTPSRR = aFetchHTTPSRR;
    527 
    528  if (overrider) {
    529    args->mTrans->SetParallelSpeculativeConnectLimit(
    530        overrider->GetParallelSpeculativeConnectLimit());
    531    args->mTrans->SetIgnoreIdle(overrider->GetIgnoreIdle());
    532    args->mTrans->SetAllow1918(overrider->GetAllow1918());
    533  }
    534 
    535  return PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, args);
    536 }
    537 
    538 nsresult nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget** target) {
    539  (void)EnsureSocketThreadTarget();
    540 
    541  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    542  nsCOMPtr<nsIEventTarget> temp(mSocketThreadTarget);
    543  temp.forget(target);
    544  return NS_OK;
    545 }
    546 
    547 nsresult nsHttpConnectionMgr::ReclaimConnection(HttpConnectionBase* conn) {
    548  LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%p]\n", conn));
    549 
    550  (void)EnsureSocketThreadTarget();
    551 
    552  nsCOMPtr<nsIEventTarget> target;
    553  {
    554    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    555    target = mSocketThreadTarget;
    556  }
    557 
    558  if (!target) {
    559    NS_WARNING("cannot post event if not initialized");
    560    return NS_ERROR_NOT_INITIALIZED;
    561  }
    562 
    563  RefPtr<HttpConnectionBase> connRef(conn);
    564  RefPtr<nsHttpConnectionMgr> self(this);
    565  return target->Dispatch(NS_NewRunnableFunction(
    566      "nsHttpConnectionMgr::CallReclaimConnection",
    567      [conn{std::move(connRef)}, self{std::move(self)}]() {
    568        self->OnMsgReclaimConnection(conn);
    569      }));
    570 }
    571 
    572 // A structure used to marshall 6 pointers across the various necessary
    573 // threads to complete an HTTP upgrade.
    574 class nsCompleteUpgradeData : public ARefBase {
    575 public:
    576  nsCompleteUpgradeData(nsHttpTransaction* aTrans,
    577                        nsIHttpUpgradeListener* aListener, bool aJsWrapped)
    578      : mTrans(aTrans), mUpgradeListener(aListener), mJsWrapped(aJsWrapped) {}
    579 
    580  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsCompleteUpgradeData, override)
    581 
    582  RefPtr<nsHttpTransaction> mTrans;
    583  nsCOMPtr<nsIHttpUpgradeListener> mUpgradeListener;
    584 
    585  nsCOMPtr<nsISocketTransport> mSocketTransport;
    586  nsCOMPtr<nsIAsyncInputStream> mSocketIn;
    587  nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
    588 
    589  bool mJsWrapped;
    590 
    591 private:
    592  virtual ~nsCompleteUpgradeData() {
    593    NS_ReleaseOnMainThread("nsCompleteUpgradeData.mUpgradeListener",
    594                           mUpgradeListener.forget());
    595  }
    596 };
    597 
    598 nsresult nsHttpConnectionMgr::CompleteUpgrade(
    599    HttpTransactionShell* aTrans, nsIHttpUpgradeListener* aUpgradeListener) {
    600  // test if aUpgradeListener is a wrapped JsObject
    601  nsCOMPtr<nsIXPConnectWrappedJS> wrapper = do_QueryInterface(aUpgradeListener);
    602 
    603  bool wrapped = !!wrapper;
    604 
    605  RefPtr<nsCompleteUpgradeData> data = new nsCompleteUpgradeData(
    606      aTrans->AsHttpTransaction(), aUpgradeListener, wrapped);
    607  return PostEvent(&nsHttpConnectionMgr::OnMsgCompleteUpgrade, 0, data);
    608 }
    609 
    610 nsresult nsHttpConnectionMgr::UpdateParam(nsParamName name, uint16_t value) {
    611  uint32_t param = (uint32_t(name) << 16) | uint32_t(value);
    612  return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam,
    613                   static_cast<int32_t>(param), nullptr);
    614 }
    615 
    616 nsresult nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo* aCI) {
    617  LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", aCI->HashKey().get()));
    618  RefPtr<nsHttpConnectionInfo> ci;
    619  if (aCI) {
    620    ci = aCI->Clone();
    621  }
    622  return PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
    623 }
    624 
    625 nsresult nsHttpConnectionMgr::ProcessPendingQ() {
    626  LOG(("nsHttpConnectionMgr::ProcessPendingQ [All CI]\n"));
    627  return PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, nullptr);
    628 }
    629 
    630 void nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket(int32_t,
    631                                                        ARefBase* param) {
    632  EventTokenBucket* tokenBucket = static_cast<EventTokenBucket*>(param);
    633  gHttpHandler->SetRequestTokenBucket(tokenBucket);
    634 }
    635 
    636 nsresult nsHttpConnectionMgr::UpdateRequestTokenBucket(
    637    EventTokenBucket* aBucket) {
    638  // Call From main thread when a new EventTokenBucket has been made in order
    639  // to post the new value to the socket thread.
    640  return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket, 0,
    641                   aBucket);
    642 }
    643 
    644 nsresult nsHttpConnectionMgr::ClearConnectionHistory() {
    645  return PostEvent(&nsHttpConnectionMgr::OnMsgClearConnectionHistory, 0,
    646                   nullptr);
    647 }
    648 
    649 void nsHttpConnectionMgr::OnMsgClearConnectionHistory(int32_t,
    650                                                      ARefBase* param) {
    651  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    652 
    653  LOG(("nsHttpConnectionMgr::OnMsgClearConnectionHistory"));
    654 
    655  for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    656    RefPtr<ConnectionEntry> ent = iter.Data();
    657    if (ent->IdleConnectionsLength() == 0 && ent->ActiveConnsLength() == 0 &&
    658        ent->DnsAndConnectSocketsLength() == 0 &&
    659        ent->UrgentStartQueueLength() == 0 && ent->PendingQueueLength() == 0 &&
    660        !ent->mDoNotDestroy) {
    661      iter.Remove();
    662    }
    663  }
    664 }
    665 
    666 nsresult nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection* conn) {
    667  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    668  LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p", this, conn));
    669 
    670  if (!conn->ConnectionInfo()) {
    671    return NS_ERROR_UNEXPECTED;
    672  }
    673 
    674  ConnectionEntry* ent = mCT.GetWeak(conn->ConnectionInfo()->HashKey());
    675 
    676  if (!ent || NS_FAILED(ent->CloseIdleConnection(conn))) {
    677    return NS_ERROR_UNEXPECTED;
    678  }
    679 
    680  return NS_OK;
    681 }
    682 
    683 nsresult nsHttpConnectionMgr::RemoveIdleConnection(nsHttpConnection* conn) {
    684  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    685 
    686  LOG(("nsHttpConnectionMgr::RemoveIdleConnection %p conn=%p", this, conn));
    687 
    688  if (!conn->ConnectionInfo()) {
    689    return NS_ERROR_UNEXPECTED;
    690  }
    691 
    692  ConnectionEntry* ent = mCT.GetWeak(conn->ConnectionInfo()->HashKey());
    693 
    694  if (!ent || NS_FAILED(ent->RemoveIdleConnection(conn))) {
    695    return NS_ERROR_UNEXPECTED;
    696  }
    697 
    698  return NS_OK;
    699 }
    700 
    701 HttpConnectionBase* nsHttpConnectionMgr::FindCoalescableConnectionByHashKey(
    702    ConnectionEntry* ent, const nsCString& key, bool justKidding, bool aNoHttp2,
    703    bool aNoHttp3) {
    704  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    705  MOZ_ASSERT(!aNoHttp2 || !aNoHttp3);
    706  MOZ_ASSERT(ent->mConnInfo);
    707  nsHttpConnectionInfo* ci = ent->mConnInfo;
    708 
    709  nsTArray<nsWeakPtr>* listOfWeakConns = mCoalescingHash.Get(key);
    710  if (!listOfWeakConns) {
    711    return nullptr;
    712  }
    713 
    714  uint32_t listLen = listOfWeakConns->Length();
    715  for (uint32_t j = 0; j < listLen;) {
    716    RefPtr<HttpConnectionBase> potentialMatch =
    717        do_QueryReferent(listOfWeakConns->ElementAt(j));
    718    if (!potentialMatch) {
    719      // This is a connection that needs to be removed from the list
    720      LOG(
    721          ("FindCoalescableConnectionByHashKey() found old conn %p that has "
    722           "null weak ptr - removing\n",
    723           listOfWeakConns->ElementAt(j).get()));
    724      if (j != listLen - 1) {
    725        listOfWeakConns->Elements()[j] =
    726            listOfWeakConns->Elements()[listLen - 1];
    727      }
    728      listOfWeakConns->RemoveLastElement();
    729      MOZ_ASSERT(listOfWeakConns->Length() == listLen - 1);
    730      listLen--;
    731      continue;  // without adjusting iterator
    732    }
    733 
    734    if (aNoHttp3 && potentialMatch->UsingHttp3()) {
    735      j++;
    736      continue;
    737    }
    738    if (aNoHttp2 && potentialMatch->UsingSpdy()) {
    739      j++;
    740      continue;
    741    }
    742    bool couldJoin;
    743    if (justKidding) {
    744      couldJoin =
    745          potentialMatch->TestJoinConnection(ci->GetOrigin(), ci->OriginPort());
    746    } else {
    747      couldJoin =
    748          potentialMatch->JoinConnection(ci->GetOrigin(), ci->OriginPort());
    749    }
    750    if (couldJoin) {
    751      LOG(
    752          ("FindCoalescableConnectionByHashKey() found match conn=%p key=%s "
    753           "newCI=%s matchedCI=%s join ok\n",
    754           potentialMatch.get(), key.get(), ci->HashKey().get(),
    755           potentialMatch->ConnectionInfo()->HashKey().get()));
    756      return potentialMatch.get();
    757    }
    758    LOG(
    759        ("FindCoalescableConnectionByHashKey() found match conn=%p key=%s "
    760         "newCI=%s matchedCI=%s join failed\n",
    761         potentialMatch.get(), key.get(), ci->HashKey().get(),
    762         potentialMatch->ConnectionInfo()->HashKey().get()));
    763 
    764    ++j;  // bypassed by continue when weakptr fails
    765  }
    766 
    767  if (!listLen) {  // shrunk to 0 while iterating
    768    LOG(("FindCoalescableConnectionByHashKey() removing empty list element\n"));
    769    mCoalescingHash.Remove(key);
    770  }
    771  return nullptr;
    772 }
    773 
    774 HttpConnectionBase* nsHttpConnectionMgr::FindCoalescableConnection(
    775    ConnectionEntry* ent, bool justKidding, bool aNoHttp2, bool aNoHttp3) {
    776  MOZ_ASSERT(!aNoHttp2 || !aNoHttp3);
    777  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    778  MOZ_ASSERT(ent->mConnInfo);
    779  nsHttpConnectionInfo* ci = ent->mConnInfo;
    780  LOG(("FindCoalescableConnection %s\n", ci->HashKey().get()));
    781 
    782  if (ci->GetWebTransport()) {
    783    LOG(("Don't coalesce a WebTransport conn "));
    784    return nullptr;
    785  }
    786  // First try and look it up by origin frame
    787  HttpConnectionBase* conn = FindCoalescableConnectionByHashKey(
    788      ent, ent->OriginFrameHashKey(), justKidding, aNoHttp2, aNoHttp3);
    789  if (conn) {
    790    LOG(("FindCoalescableConnection(%s) match conn %p on frame key %s\n",
    791         ci->HashKey().get(), conn, ent->OriginFrameHashKey().get()));
    792    return conn;
    793  }
    794 
    795  // now check for DNS based keys
    796  // deleted conns (null weak pointers) are removed from list
    797  uint32_t keyLen = ent->mCoalescingKeys.Length();
    798  for (uint32_t i = 0; i < keyLen; ++i) {
    799    conn = FindCoalescableConnectionByHashKey(ent, ent->mCoalescingKeys[i],
    800                                              justKidding, aNoHttp2, aNoHttp3);
    801 
    802    auto usableEntry = [&](HttpConnectionBase* conn) {
    803      // This is allowed by the spec, but other browsers don't coalesce
    804      // so agressively, which surprises developers. See bug 1420777.
    805      if (StaticPrefs::network_http_http2_aggressive_coalescing()) {
    806        return true;
    807      }
    808 
    809      // Make sure that the connection's IP address is one that is in
    810      // the set of IP addresses in the entry's DNS response.
    811      NetAddr addr;
    812      nsresult rv = conn->GetPeerAddr(&addr);
    813      if (NS_FAILED(rv)) {
    814        // Err on the side of not coalescing
    815        return false;
    816      }
    817      // We don't care about remote port when matching entries.
    818      addr.inet.port = 0;
    819      return ent->mAddresses.Contains(addr);
    820    };
    821 
    822    if (conn) {
    823      LOG(("Found connection with matching hash"));
    824      if (usableEntry(conn)) {
    825        LOG(("> coalescing"));
    826        return conn;
    827      } else {
    828        LOG(("> not coalescing as remote address not present in DNS records"));
    829      }
    830    }
    831  }
    832 
    833  LOG(("FindCoalescableConnection(%s) no matching conn\n",
    834       ci->HashKey().get()));
    835  return nullptr;
    836 }
    837 
    838 void nsHttpConnectionMgr::UpdateCoalescingForNewConn(
    839    HttpConnectionBase* newConn, ConnectionEntry* ent, bool aNoHttp3) {
    840  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    841  MOZ_ASSERT(newConn);
    842  MOZ_ASSERT(newConn->ConnectionInfo());
    843  MOZ_ASSERT(ent);
    844  MOZ_ASSERT(mCT.GetWeak(newConn->ConnectionInfo()->HashKey()) == ent);
    845  LOG(("UpdateCoalescingForNewConn newConn=%p aNoHttp3=%d", newConn, aNoHttp3));
    846  if (newConn->ConnectionInfo()->GetWebTransport()) {
    847    LOG(("Don't coalesce a WebTransport conn %p", newConn));
    848    // TODO: implement this properly in bug 1815735.
    849    return;
    850  }
    851 
    852  HttpConnectionBase* existingConn =
    853      FindCoalescableConnection(ent, true, false, false);
    854  if (existingConn) {
    855    // Prefer http3 connection, but allow an HTTP/2 connection if it is used for
    856    // WebSocket.
    857    if (newConn->UsingHttp3() && existingConn->UsingSpdy()) {
    858      RefPtr<nsHttpConnection> connTCP = do_QueryObject(existingConn);
    859      if (connTCP && !connTCP->IsForWebSocket()) {
    860        LOG(
    861            ("UpdateCoalescingForNewConn() found existing active H2 conn that "
    862             "could have served newConn, but new connection is H3, therefore "
    863             "close the H2 conncetion"));
    864        existingConn->SetCloseReason(
    865            ConnectionCloseReason::CLOSE_EXISTING_CONN_FOR_COALESCING);
    866        existingConn->DontReuse();
    867      }
    868    } else if (existingConn->UsingHttp3() && newConn->UsingSpdy()) {
    869      RefPtr<nsHttpConnection> connTCP = do_QueryObject(newConn);
    870      if (connTCP && !connTCP->IsForWebSocket() && !aNoHttp3) {
    871        LOG(
    872            ("UpdateCoalescingForNewConn() found existing active H3 conn that "
    873             "could have served H2 newConn graceful close of newConn=%p to "
    874             "migrate to existingConn %p\n",
    875             newConn, existingConn));
    876        newConn->SetCloseReason(
    877            ConnectionCloseReason::CLOSE_NEW_CONN_FOR_COALESCING);
    878        newConn->DontReuse();
    879        return;
    880      }
    881    } else {
    882      LOG(
    883          ("UpdateCoalescingForNewConn() found existing active conn that could "
    884           "have served newConn "
    885           "graceful close of newConn=%p to migrate to existingConn %p\n",
    886           newConn, existingConn));
    887      newConn->SetCloseReason(
    888          ConnectionCloseReason::CLOSE_NEW_CONN_FOR_COALESCING);
    889      newConn->DontReuse();
    890      return;
    891    }
    892  }
    893 
    894  // This connection might go into the mCoalescingHash for new transactions to
    895  // be coalesced onto if it can accept new transactions
    896  if (!newConn->CanDirectlyActivate()) {
    897    return;
    898  }
    899 
    900  uint32_t keyLen = ent->mCoalescingKeys.Length();
    901  for (uint32_t i = 0; i < keyLen; ++i) {
    902    LOG((
    903        "UpdateCoalescingForNewConn() registering newConn %p %s under key %s\n",
    904        newConn, newConn->ConnectionInfo()->HashKey().get(),
    905        ent->mCoalescingKeys[i].get()));
    906 
    907    mCoalescingHash
    908        .LookupOrInsertWith(
    909            ent->mCoalescingKeys[i],
    910            [] {
    911              LOG(("UpdateCoalescingForNewConn() need new list element\n"));
    912              return MakeUnique<nsTArray<nsWeakPtr>>(1);
    913            })
    914        ->AppendElement(do_GetWeakReference(
    915            static_cast<nsISupportsWeakReference*>(newConn)));
    916  }
    917 
    918  // this is a new connection that can be coalesced onto. hooray!
    919  // if there are other connection to this entry (e.g.
    920  // some could still be handshaking, shutting down, etc..) then close
    921  // them down after any transactions that are on them are complete.
    922  // This probably happened due to the parallel connection algorithm
    923  // that is used only before the host is known to speak h2.
    924  ent->MakeAllDontReuseExcept(newConn);
    925 }
    926 
    927 // This function lets a connection, after completing the NPN phase,
    928 // report whether or not it is using spdy through the usingSpdy
    929 // argument. It would not be necessary if NPN were driven out of
    930 // the connection manager. The connection entry associated with the
    931 // connection is then updated to indicate whether or not we want to use
    932 // spdy with that host and update the coalescing hash
    933 // entries used for de-sharding hostsnames.
    934 void nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection* conn,
    935                                               bool usingSpdy,
    936                                               bool disallowHttp3) {
    937  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    938  if (!conn->ConnectionInfo()) {
    939    return;
    940  }
    941  ConnectionEntry* ent = mCT.GetWeak(conn->ConnectionInfo()->HashKey());
    942  if (!ent || !usingSpdy) {
    943    return;
    944  }
    945 
    946  ent->mUsingSpdy = true;
    947  mNumSpdyHttp3ActiveConns++;
    948 
    949  // adjust timeout timer
    950  uint32_t ttl = conn->TimeToLive();
    951  uint64_t timeOfExpire = NowInSeconds() + ttl;
    952  if (!mTimer || timeOfExpire < mTimeOfNextWakeUp) {
    953    PruneDeadConnectionsAfter(ttl);
    954  }
    955 
    956  UpdateCoalescingForNewConn(conn, ent, disallowHttp3);
    957 
    958  nsresult rv = ProcessPendingQ(ent->mConnInfo);
    959  if (NS_FAILED(rv)) {
    960    LOG(
    961        ("ReportSpdyConnection conn=%p ent=%p "
    962         "failed to process pending queue (%08x)\n",
    963         conn, ent, static_cast<uint32_t>(rv)));
    964  }
    965  rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ);
    966  if (NS_FAILED(rv)) {
    967    LOG(
    968        ("ReportSpdyConnection conn=%p ent=%p "
    969         "failed to post event (%08x)\n",
    970         conn, ent, static_cast<uint32_t>(rv)));
    971  }
    972 }
    973 
    974 void nsHttpConnectionMgr::ReportHttp3Connection(HttpConnectionBase* conn) {
    975  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    976  if (!conn->ConnectionInfo()) {
    977    return;
    978  }
    979  ConnectionEntry* ent = mCT.GetWeak(conn->ConnectionInfo()->HashKey());
    980  if (!ent) {
    981    return;
    982  }
    983 
    984  mNumSpdyHttp3ActiveConns++;
    985 
    986  UpdateCoalescingForNewConn(conn, ent, false);
    987  nsresult rv = ProcessPendingQ(ent->mConnInfo);
    988  if (NS_FAILED(rv)) {
    989    LOG(
    990        ("ReportHttp3Connection conn=%p ent=%p "
    991         "failed to process pending queue (%08x)\n",
    992         conn, ent, static_cast<uint32_t>(rv)));
    993  }
    994  rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ);
    995  if (NS_FAILED(rv)) {
    996    LOG(
    997        ("ReportHttp3Connection conn=%p ent=%p "
    998         "failed to post event (%08x)\n",
    999         conn, ent, static_cast<uint32_t>(rv)));
   1000  }
   1001 }
   1002 
   1003 //-----------------------------------------------------------------------------
   1004 bool nsHttpConnectionMgr::DispatchPendingQ(
   1005    nsTArray<RefPtr<PendingTransactionInfo>>& pendingQ, ConnectionEntry* ent,
   1006    bool considerAll) {
   1007  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1008 
   1009  PendingTransactionInfo* pendingTransInfo = nullptr;
   1010  nsresult rv;
   1011  bool dispatchedSuccessfully = false;
   1012 
   1013  // if !considerAll iterate the pending list until one is dispatched
   1014  // successfully. Keep iterating afterwards only until a transaction fails to
   1015  // dispatch. if considerAll == true then try and dispatch all items.
   1016  for (uint32_t i = 0; i < pendingQ.Length();) {
   1017    pendingTransInfo = pendingQ[i];
   1018 
   1019    bool alreadyDnsAndConnectSocketOrWaitingForTLS =
   1020        pendingTransInfo->IsAlreadyClaimedInitializingConn();
   1021 
   1022    // The transaction could be closed after calling TryDispatchTransaction
   1023    rv = TryDispatchTransaction(ent, alreadyDnsAndConnectSocketOrWaitingForTLS,
   1024                                pendingTransInfo);
   1025    if (NS_SUCCEEDED(rv) || (rv != NS_ERROR_NOT_AVAILABLE) ||
   1026        pendingTransInfo->Transaction()->Closed()) {
   1027      if (NS_SUCCEEDED(rv)) {
   1028        LOG(("  dispatching pending transaction...\n"));
   1029      } else {
   1030        LOG(
   1031            ("  removing pending transaction based on "
   1032             "TryDispatchTransaction returning hard error %" PRIx32 "\n",
   1033             static_cast<uint32_t>(rv)));
   1034        if (rv == NS_ERROR_HTTP2_FALLBACK_TO_HTTP1) {
   1035          pendingTransInfo->Transaction()->Close(
   1036              NS_ERROR_HTTP2_FALLBACK_TO_HTTP1);
   1037        }
   1038      }
   1039      if (pendingQ.RemoveElement(pendingTransInfo)) {
   1040        // pendingTransInfo is now potentially destroyed
   1041        dispatchedSuccessfully = true;
   1042        continue;  // dont ++i as we just made the array shorter
   1043      }
   1044 
   1045      LOG(("  transaction not found in pending queue\n"));
   1046    }
   1047 
   1048    if (dispatchedSuccessfully && !considerAll) break;
   1049 
   1050    ++i;
   1051  }
   1052  return dispatchedSuccessfully;
   1053 }
   1054 
   1055 uint32_t nsHttpConnectionMgr::MaxPersistConnections(
   1056    ConnectionEntry* ent) const {
   1057  if (ent->mConnInfo->UsingHttpProxy() && !ent->mConnInfo->UsingConnect()) {
   1058    return static_cast<uint32_t>(mMaxPersistConnsPerProxy);
   1059  }
   1060 
   1061  return static_cast<uint32_t>(mMaxPersistConnsPerHost);
   1062 }
   1063 
   1064 void nsHttpConnectionMgr::PreparePendingQForDispatching(
   1065    ConnectionEntry* ent, nsTArray<RefPtr<PendingTransactionInfo>>& pendingQ,
   1066    bool considerAll) {
   1067  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1068 
   1069  pendingQ.Clear();
   1070 
   1071  uint32_t totalCount = ent->TotalActiveConnections();
   1072  uint32_t maxPersistConns = MaxPersistConnections(ent);
   1073  uint32_t availableConnections =
   1074      maxPersistConns > totalCount ? maxPersistConns - totalCount : 0;
   1075 
   1076  // No need to try dispatching if we reach the active connection limit.
   1077  if (!availableConnections) {
   1078    return;
   1079  }
   1080 
   1081  // Only have to get transactions from the queue whose window id is 0.
   1082  if (!StaticPrefs::network_http_active_tab_priority()) {
   1083    ent->AppendPendingQForFocusedWindow(0, pendingQ, availableConnections);
   1084    return;
   1085  }
   1086 
   1087  uint32_t maxFocusedWindowConnections =
   1088      availableConnections * gHttpHandler->FocusedWindowTransactionRatio();
   1089  MOZ_ASSERT(maxFocusedWindowConnections < availableConnections);
   1090 
   1091  if (!maxFocusedWindowConnections) {
   1092    maxFocusedWindowConnections = 1;
   1093  }
   1094 
   1095  // Only need to dispatch transactions for either focused or
   1096  // non-focused window because considerAll is false.
   1097  if (!considerAll) {
   1098    ent->AppendPendingQForFocusedWindow(mCurrentBrowserId, pendingQ,
   1099                                        maxFocusedWindowConnections);
   1100 
   1101    if (pendingQ.IsEmpty()) {
   1102      ent->AppendPendingQForNonFocusedWindows(mCurrentBrowserId, pendingQ,
   1103                                              availableConnections);
   1104    }
   1105    return;
   1106  }
   1107 
   1108  uint32_t maxNonFocusedWindowConnections =
   1109      availableConnections - maxFocusedWindowConnections;
   1110  nsTArray<RefPtr<PendingTransactionInfo>> remainingPendingQ;
   1111 
   1112  ent->AppendPendingQForFocusedWindow(mCurrentBrowserId, pendingQ,
   1113                                      maxFocusedWindowConnections);
   1114 
   1115  if (maxNonFocusedWindowConnections) {
   1116    ent->AppendPendingQForNonFocusedWindows(
   1117        mCurrentBrowserId, remainingPendingQ, maxNonFocusedWindowConnections);
   1118  }
   1119 
   1120  // If the slots for either focused or non-focused window are not filled up
   1121  // to the availability, try to use the remaining available connections
   1122  // for the other slot (with preference for the focused window).
   1123  if (remainingPendingQ.Length() < maxNonFocusedWindowConnections) {
   1124    ent->AppendPendingQForFocusedWindow(
   1125        mCurrentBrowserId, pendingQ,
   1126        maxNonFocusedWindowConnections - remainingPendingQ.Length());
   1127  } else if (pendingQ.Length() < maxFocusedWindowConnections) {
   1128    ent->AppendPendingQForNonFocusedWindows(
   1129        mCurrentBrowserId, remainingPendingQ,
   1130        maxFocusedWindowConnections - pendingQ.Length());
   1131  }
   1132 
   1133  MOZ_ASSERT(pendingQ.Length() + remainingPendingQ.Length() <=
   1134             availableConnections);
   1135 
   1136  LOG(
   1137      ("nsHttpConnectionMgr::PreparePendingQForDispatching "
   1138       "focused window pendingQ.Length()=%zu"
   1139       ", remainingPendingQ.Length()=%zu\n",
   1140       pendingQ.Length(), remainingPendingQ.Length()));
   1141 
   1142  // Append elements in |remainingPendingQ| to |pendingQ|. The order in
   1143  // |pendingQ| is like: [focusedWindowTrans...nonFocusedWindowTrans].
   1144  pendingQ.AppendElements(std::move(remainingPendingQ));
   1145 }
   1146 
   1147 bool nsHttpConnectionMgr::ProcessPendingQForEntry(ConnectionEntry* ent,
   1148                                                  bool considerAll) {
   1149  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1150 
   1151  LOG(
   1152      ("nsHttpConnectionMgr::ProcessPendingQForEntry "
   1153       "[ci=%s ent=%p active=%zu idle=%zu urgent-start-queue=%zu"
   1154       " queued=%zu]\n",
   1155       ent->mConnInfo->HashKey().get(), ent, ent->ActiveConnsLength(),
   1156       ent->IdleConnectionsLength(), ent->UrgentStartQueueLength(),
   1157       ent->PendingQueueLength()));
   1158 
   1159  if (LOG_ENABLED()) {
   1160    ent->PrintPendingQ();
   1161    ent->LogConnections();
   1162  }
   1163 
   1164  if (!ent->PendingQueueLength() && !ent->UrgentStartQueueLength()) {
   1165    return false;
   1166  }
   1167  ProcessSpdyPendingQ(ent);
   1168 
   1169  bool dispatchedSuccessfully = false;
   1170 
   1171  if (ent->UrgentStartQueueLength()) {
   1172    nsTArray<RefPtr<PendingTransactionInfo>> pendingQ;
   1173    ent->AppendPendingUrgentStartQ(pendingQ);
   1174    dispatchedSuccessfully = DispatchPendingQ(pendingQ, ent, considerAll);
   1175    for (const auto& transactionInfo : Reversed(pendingQ)) {
   1176      ent->InsertTransaction(transactionInfo);
   1177    }
   1178  }
   1179 
   1180  if (dispatchedSuccessfully && !considerAll) {
   1181    return dispatchedSuccessfully;
   1182  }
   1183 
   1184  nsTArray<RefPtr<PendingTransactionInfo>> pendingQ;
   1185  PreparePendingQForDispatching(ent, pendingQ, considerAll);
   1186 
   1187  // The only case that |pendingQ| is empty is when there is no
   1188  // connection available for dispatching.
   1189  if (pendingQ.IsEmpty()) {
   1190    return dispatchedSuccessfully;
   1191  }
   1192 
   1193  dispatchedSuccessfully |= DispatchPendingQ(pendingQ, ent, considerAll);
   1194 
   1195  // Put the leftovers into connection entry, in the same order as they
   1196  // were before to keep the natural ordering.
   1197  for (const auto& transactionInfo : Reversed(pendingQ)) {
   1198    ent->InsertTransaction(transactionInfo, true);
   1199  }
   1200 
   1201  // Only remove empty pendingQ when considerAll is true.
   1202  if (considerAll) {
   1203    ent->RemoveEmptyPendingQ();
   1204  }
   1205 
   1206  return dispatchedSuccessfully;
   1207 }
   1208 
   1209 bool nsHttpConnectionMgr::ProcessPendingQForEntry(nsHttpConnectionInfo* ci) {
   1210  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1211 
   1212  ConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
   1213  if (ent) return ProcessPendingQForEntry(ent, false);
   1214  return false;
   1215 }
   1216 
   1217 // we're at the active connection limit if any one of the following conditions
   1218 // is true:
   1219 //  (1) at max-connections
   1220 //  (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
   1221 //  (3) keep-alive disabled and at max-connections-per-server
   1222 bool nsHttpConnectionMgr::AtActiveConnectionLimit(ConnectionEntry* ent,
   1223                                                  uint32_t caps) {
   1224  nsHttpConnectionInfo* ci = ent->mConnInfo;
   1225  uint32_t totalCount = ent->TotalActiveConnections();
   1226 
   1227  if (ci->IsHttp3() || ci->IsHttp3ProxyConnection()) {
   1228    if (ci->GetWebTransport()) {
   1229      // TODO: implement this properly in bug 1815735.
   1230      return false;
   1231    }
   1232    return totalCount > 0;
   1233  }
   1234 
   1235  uint32_t maxPersistConns = MaxPersistConnections(ent);
   1236 
   1237  LOG(
   1238      ("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x,"
   1239       "totalCount=%u, maxPersistConns=%u]\n",
   1240       ci->HashKey().get(), caps, totalCount, maxPersistConns));
   1241 
   1242  if (caps & NS_HTTP_URGENT_START) {
   1243    if (totalCount >= (mMaxUrgentExcessiveConns + maxPersistConns)) {
   1244      LOG((
   1245          "The number of total connections are greater than or equal to sum of "
   1246          "max urgent-start queue length and the number of max persistent "
   1247          "connections.\n"));
   1248      return true;
   1249    }
   1250    return false;
   1251  }
   1252 
   1253  // update maxconns if potentially limited by the max socket count
   1254  // this requires a dynamic reduction in the max socket count to a point
   1255  // lower than the max-connections pref.
   1256  uint32_t maxSocketCount = gHttpHandler->MaxSocketCount();
   1257  if (mMaxConns > maxSocketCount) {
   1258    mMaxConns = maxSocketCount;
   1259    LOG(("nsHttpConnectionMgr %p mMaxConns dynamically reduced to %u", this,
   1260         mMaxConns));
   1261  }
   1262 
   1263  // If there are more active connections than the global limit, then we're
   1264  // done. Purging idle connections won't get us below it.
   1265  if (mNumActiveConns >= mMaxConns) {
   1266    LOG(("  num active conns == max conns\n"));
   1267    return true;
   1268  }
   1269 
   1270  bool result = (totalCount >= maxPersistConns);
   1271  LOG(("AtActiveConnectionLimit result: %s", result ? "true" : "false"));
   1272  return result;
   1273 }
   1274 
   1275 // returns NS_OK if a connection was started
   1276 // return NS_ERROR_NOT_AVAILABLE if a new connection cannot be made due to
   1277 //        ephemeral limits
   1278 // returns other NS_ERROR on hard failure conditions
   1279 nsresult nsHttpConnectionMgr::MakeNewConnection(
   1280    ConnectionEntry* ent, PendingTransactionInfo* pendingTransInfo) {
   1281  LOG(("nsHttpConnectionMgr::MakeNewConnection %p ent=%p trans=%p", this, ent,
   1282       pendingTransInfo->Transaction()));
   1283  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1284 
   1285  if (ent->FindConnToClaim(pendingTransInfo)) {
   1286    return NS_OK;
   1287  }
   1288 
   1289  nsHttpTransaction* trans = pendingTransInfo->Transaction();
   1290 
   1291  // If this host is trying to negotiate a SPDY session right now,
   1292  // don't create any new connections until the result of the
   1293  // negotiation is known.
   1294  if (!(trans->Caps() & NS_HTTP_DISALLOW_SPDY) &&
   1295      (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) && ent->RestrictConnections()) {
   1296    LOG(
   1297        ("nsHttpConnectionMgr::MakeNewConnection [ci = %s] "
   1298         "Not Available Due to RestrictConnections()\n",
   1299         ent->mConnInfo->HashKey().get()));
   1300    return NS_ERROR_NOT_AVAILABLE;
   1301  }
   1302 
   1303  // We need to make a new connection. If that is going to exceed the
   1304  // global connection limit then try and free up some room by closing
   1305  // an idle connection to another host. We know it won't select "ent"
   1306  // because we have already determined there are no idle connections
   1307  // to our destination
   1308 
   1309  if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) && mNumIdleConns) {
   1310    // If the global number of connections is preventing the opening of new
   1311    // connections to a host without idle connections, then close them
   1312    // regardless of their TTL.
   1313    auto iter = mCT.ConstIter();
   1314    while (mNumIdleConns + mNumActiveConns + 1 >= mMaxConns && !iter.Done()) {
   1315      RefPtr<ConnectionEntry> entry = iter.Data();
   1316      entry->CloseIdleConnections((mNumIdleConns + mNumActiveConns + 1) -
   1317                                  mMaxConns);
   1318      iter.Next();
   1319    }
   1320  }
   1321 
   1322  if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) && mNumActiveConns &&
   1323      StaticPrefs::network_http_http2_enabled()) {
   1324    // If the global number of connections is preventing the opening of new
   1325    // connections to a host without idle connections, then close any spdy
   1326    // ASAP.
   1327    for (const RefPtr<ConnectionEntry>& entry : mCT.Values()) {
   1328      while (entry->MakeFirstActiveSpdyConnDontReuse()) {
   1329        // Stop on <= (particularly =) because this dontreuse
   1330        // causes async close.
   1331        if (mNumIdleConns + mNumActiveConns + 1 <= mMaxConns) {
   1332          goto outerLoopEnd;
   1333        }
   1334      }
   1335    }
   1336  outerLoopEnd:;
   1337  }
   1338 
   1339  if (AtActiveConnectionLimit(ent, trans->Caps())) {
   1340    return NS_ERROR_NOT_AVAILABLE;
   1341  }
   1342 
   1343  nsresult rv = ent->CreateDnsAndConnectSocket(
   1344      trans, trans->Caps(), false,
   1345      trans->GetClassOfService().Flags() & nsIClassOfService::UrgentStart, true,
   1346      pendingTransInfo);
   1347  if (NS_FAILED(rv)) {
   1348    /* hard failure */
   1349    LOG(
   1350        ("nsHttpConnectionMgr::MakeNewConnection [ci = %s trans = %p] "
   1351         "CreateDnsAndConnectSocket() hard failure.\n",
   1352         ent->mConnInfo->HashKey().get(), trans));
   1353    trans->Close(rv);
   1354    if (rv == NS_ERROR_NOT_AVAILABLE) rv = NS_ERROR_FAILURE;
   1355    return rv;
   1356  }
   1357 
   1358  return NS_OK;
   1359 }
   1360 
   1361 // returns OK if a connection is found for the transaction
   1362 //   and the transaction is started.
   1363 // returns ERROR_NOT_AVAILABLE if no connection can be found and it
   1364 //   should be queued until circumstances change
   1365 // returns other ERROR when transaction has a hard failure and should
   1366 //   not remain in the pending queue
   1367 nsresult nsHttpConnectionMgr::TryDispatchTransaction(
   1368    ConnectionEntry* ent, bool onlyReusedConnection,
   1369    PendingTransactionInfo* pendingTransInfo) {
   1370  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1371 
   1372  nsHttpTransaction* trans = pendingTransInfo->Transaction();
   1373 
   1374  LOG(
   1375      ("nsHttpConnectionMgr::TryDispatchTransaction without conn "
   1376       "[trans=%p ci=%p ci=%s caps=%x onlyreused=%d active=%zu "
   1377       "idle=%zu]\n",
   1378       trans, ent->mConnInfo.get(), ent->mConnInfo->HashKey().get(),
   1379       uint32_t(trans->Caps()), onlyReusedConnection, ent->ActiveConnsLength(),
   1380       ent->IdleConnectionsLength()));
   1381 
   1382  uint32_t caps = trans->Caps();
   1383 
   1384  // 0 - If this should use spdy then dispatch it post haste.
   1385  // 1 - If there is connection pressure then see if we can pipeline this on
   1386  //     a connection of a matching type instead of using a new conn
   1387  // 2 - If there is an idle connection, use it!
   1388  // 3 - if class == reval or script and there is an open conn of that type
   1389  //     then pipeline onto shortest pipeline of that class if limits allow
   1390  // 4 - If we aren't up against our connection limit,
   1391  //     then open a new one
   1392  // 5 - Try a pipeline if we haven't already - this will be unusual because
   1393  //     it implies a low connection pressure situation where
   1394  //     MakeNewConnection() failed.. that is possible, but unlikely, due to
   1395  //     global limits
   1396  // 6 - no connection is available - queue it
   1397 
   1398  RefPtr<HttpConnectionBase> unusedSpdyPersistentConnection;
   1399 
   1400  // step 0
   1401  // look for existing spdy connection - that's always best because it is
   1402  // essentially pipelining without head of line blocking
   1403 
   1404  // For WebSocket/WebTransport through H3 proxy, we need to create a TCP
   1405  // tunnel through the H3 proxy first. But if there's already an H2 session
   1406  // available (from a previously established tunnel), we should use that
   1407  // instead of creating a new tunnel.
   1408  // The WebSocket transaction doesn't have a connection set (it was queued
   1409  // without one in DnsAndConnectSocket::SetupConn to avoid triggering reclaim
   1410  // when we clear it here).
   1411  if ((trans->IsWebsocketUpgrade() || trans->IsForWebTransport()) &&
   1412      ent->IsHttp3ProxyConnection()) {
   1413    // First check if there's an H2 session available (from existing tunnel)
   1414    // This handles the case where the tunnel was already established and the
   1415    // WebSocket transaction was reset to wait for H2 negotiation.
   1416    // We can't use GetH2orH3ActiveConn because it skips H3 proxy entries when
   1417    // looking for H2 connections. We use GetH2TunnelActiveConn to directly
   1418    // look for an H2 tunnel connection in the active connections.
   1419    RefPtr<nsHttpConnection> h2Tunnel = ent->GetH2TunnelActiveConn();
   1420    if (h2Tunnel) {
   1421      LOG(
   1422          ("TryDispatchTransaction: WebSocket through H3 proxy - using "
   1423           "existing H2 tunnel"));
   1424      return TryDispatchExtendedCONNECTransaction(ent, trans, h2Tunnel);
   1425    }
   1426 
   1427    // No H2 session available yet - create a tunnel through the H3 proxy
   1428    RefPtr<HttpConnectionBase> conn = GetH2orH3ActiveConn(ent, true, false);
   1429    RefPtr<HttpConnectionUDP> connUDP = do_QueryObject(conn);
   1430    if (connUDP) {
   1431      LOG(("TryDispatchTransaction: WebSocket through HTTP/3 proxy"));
   1432      RefPtr<HttpConnectionBase> tunnelConn;
   1433      nsresult rv =
   1434          connUDP->CreateTunnelStream(trans, getter_AddRefs(tunnelConn), true);
   1435      if (NS_FAILED(rv)) {
   1436        return rv;
   1437      }
   1438      ent->InsertIntoActiveConns(tunnelConn);
   1439      tunnelConn->SetInTunnel();
   1440      if (trans->IsWebsocketUpgrade()) {
   1441        trans->SetIsHttp2Websocket(true);
   1442      }
   1443      return DispatchTransaction(ent, trans, tunnelConn);
   1444    }
   1445  }
   1446 
   1447  RefPtr<HttpConnectionBase> conn = GetH2orH3ActiveConn(
   1448      ent,
   1449      (!StaticPrefs::network_http_http2_enabled() ||
   1450       (caps & NS_HTTP_DISALLOW_SPDY)),
   1451      (!nsHttpHandler::IsHttp3Enabled() || (caps & NS_HTTP_DISALLOW_HTTP3)));
   1452  if (conn) {
   1453    LOG(("TryingDispatchTransaction: an active h2 connection exists"));
   1454    if (trans->IsWebsocketUpgrade() || trans->IsForWebTransport()) {
   1455      RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn);
   1456      if (connTCP) {
   1457        return TryDispatchExtendedCONNECTransaction(ent, trans, connTCP);
   1458      }
   1459    } else {
   1460      if ((caps & NS_HTTP_ALLOW_KEEPALIVE) ||
   1461          (caps & NS_HTTP_ALLOW_SPDY_WITHOUT_KEEPALIVE) ||
   1462          !conn->IsExperienced()) {
   1463        LOG(("   dispatch to spdy: [conn=%p]\n", conn.get()));
   1464        trans->RemoveDispatchedAsBlocking(); /* just in case */
   1465        nsresult rv = DispatchTransaction(ent, trans, conn);
   1466        NS_ENSURE_SUCCESS(rv, rv);
   1467        return NS_OK;
   1468      }
   1469      unusedSpdyPersistentConnection = conn;
   1470    }
   1471  }
   1472 
   1473  // If this is not a blocking transaction and the request context for it is
   1474  // currently processing one or more blocking transactions then we
   1475  // need to just leave it in the queue until those are complete unless it is
   1476  // explicitly marked as unblocked.
   1477  if (!(caps & NS_HTTP_LOAD_AS_BLOCKING)) {
   1478    if (!(caps & NS_HTTP_LOAD_UNBLOCKED)) {
   1479      nsIRequestContext* requestContext = trans->RequestContext();
   1480      if (requestContext) {
   1481        uint32_t blockers = 0;
   1482        if (NS_SUCCEEDED(
   1483                requestContext->GetBlockingTransactionCount(&blockers)) &&
   1484            blockers) {
   1485          // need to wait for blockers to clear
   1486          LOG(("   blocked by request context: [rc=%p trans=%p blockers=%d]\n",
   1487               requestContext, trans, blockers));
   1488          return NS_ERROR_NOT_AVAILABLE;
   1489        }
   1490      }
   1491    }
   1492  } else {
   1493    // Mark the transaction and its load group as blocking right now to prevent
   1494    // other transactions from being reordered in the queue due to slow syns.
   1495    trans->DispatchedAsBlocking();
   1496  }
   1497 
   1498  // step 1
   1499  // If connection pressure, then we want to favor pipelining of any kind
   1500  // h1 pipelining has been removed
   1501 
   1502  // Subject most transactions at high parallelism to rate pacing.
   1503  // It will only be actually submitted to the
   1504  // token bucket once, and if possible it is granted admission synchronously.
   1505  // It is important to leave a transaction in the pending queue when blocked by
   1506  // pacing so it can be found on cancel if necessary.
   1507  // Transactions that cause blocking or bypass it (e.g. js/css) are not rate
   1508  // limited.
   1509  if (gHttpHandler->UseRequestTokenBucket()) {
   1510    // submit even whitelisted transactions to the token bucket though they will
   1511    // not be slowed by it
   1512    bool runNow = trans->TryToRunPacedRequest();
   1513    if (!runNow) {
   1514      if ((mNumActiveConns - mNumSpdyHttp3ActiveConns) <=
   1515          gHttpHandler->RequestTokenBucketMinParallelism()) {
   1516        runNow = true;  // white list it
   1517      } else if (caps & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED)) {
   1518        runNow = true;  // white list it
   1519      }
   1520    }
   1521    if (!runNow) {
   1522      LOG(("   blocked due to rate pacing trans=%p\n", trans));
   1523      return NS_ERROR_NOT_AVAILABLE;
   1524    }
   1525  }
   1526 
   1527  // step 2
   1528  // consider an idle persistent connection
   1529  bool idleConnsAllUrgent = false;
   1530  if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
   1531    nsresult rv = TryDispatchTransactionOnIdleConn(ent, pendingTransInfo, true,
   1532                                                   &idleConnsAllUrgent);
   1533    if (NS_SUCCEEDED(rv)) {
   1534      LOG(("   dispatched step 2 (idle) trans=%p\n", trans));
   1535      return NS_OK;
   1536    }
   1537 
   1538    if (rv == NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED) {
   1539      LOG(("   Local network access failure in step 2 (idle) trans=%p ",
   1540           trans));
   1541      return NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED;
   1542    }
   1543  }
   1544 
   1545  // step 3
   1546  // consider pipelining scripts and revalidations
   1547  // h1 pipelining has been removed
   1548 
   1549  // Don't dispatch if this transaction is waiting for HTTPS RR.
   1550  // This usually happens when the pref "network.dns.force_waiting_https_rr" is
   1551  // true or when echConfig is enabled.
   1552  if (trans->WaitingForHTTPSRR()) {
   1553    return NS_ERROR_NOT_AVAILABLE;
   1554  }
   1555 
   1556  // step 4
   1557  if (!onlyReusedConnection) {
   1558    nsresult rv = MakeNewConnection(ent, pendingTransInfo);
   1559    if (NS_SUCCEEDED(rv)) {
   1560      // this function returns NOT_AVAILABLE for asynchronous connects
   1561      LOG(("   dispatched step 4 (async new conn) trans=%p\n", trans));
   1562      return NS_ERROR_NOT_AVAILABLE;
   1563    }
   1564 
   1565    if (rv != NS_ERROR_NOT_AVAILABLE) {
   1566      // not available return codes should try next step as they are
   1567      // not hard errors. Other codes should stop now
   1568      LOG(("   failed step 4 (%" PRIx32 ") trans=%p\n",
   1569           static_cast<uint32_t>(rv), trans));
   1570      return rv;
   1571    }
   1572 
   1573    // repeat step 2 when there are only idle connections and all are urgent,
   1574    // don't respect urgency so that non-urgent transaction will be allowed
   1575    // to dispatch on an urgent-start-only marked connection to avoid
   1576    // dispatch deadlocks
   1577    if (!(trans->GetClassOfService().Flags() &
   1578          nsIClassOfService::UrgentStart) &&
   1579        idleConnsAllUrgent &&
   1580        ent->ActiveConnsLength() < MaxPersistConnections(ent)) {
   1581      rv = TryDispatchTransactionOnIdleConn(ent, pendingTransInfo, false);
   1582      if (NS_SUCCEEDED(rv)) {
   1583        LOG(("   dispatched step 2a (idle, reuse urgent) trans=%p\n", trans));
   1584        return NS_OK;
   1585      }
   1586    }
   1587  }
   1588 
   1589  // step 5
   1590  // previously pipelined anything here if allowed but h1 pipelining has been
   1591  // removed
   1592 
   1593  // step 6
   1594  if (unusedSpdyPersistentConnection) {
   1595    // to avoid deadlocks, we need to throw away this perfectly valid SPDY
   1596    // connection to make room for a new one that can service a no KEEPALIVE
   1597    // request
   1598    unusedSpdyPersistentConnection->DontReuse();
   1599  }
   1600 
   1601  LOG(("   not dispatched (queued) trans=%p\n", trans));
   1602  return NS_ERROR_NOT_AVAILABLE; /* queue it */
   1603 }
   1604 
   1605 nsresult nsHttpConnectionMgr::TryDispatchTransactionOnIdleConn(
   1606    ConnectionEntry* ent, PendingTransactionInfo* pendingTransInfo,
   1607    bool respectUrgency, bool* allUrgent) {
   1608  bool onlyUrgent = !!ent->IdleConnectionsLength();
   1609 
   1610  nsHttpTransaction* trans = pendingTransInfo->Transaction();
   1611  bool urgentTrans =
   1612      trans->GetClassOfService().Flags() & nsIClassOfService::UrgentStart;
   1613 
   1614  LOG(
   1615      ("nsHttpConnectionMgr::TryDispatchTransactionOnIdleConn, ent=%p, "
   1616       "trans=%p, urgent=%d",
   1617       ent, trans, urgentTrans));
   1618 
   1619  RefPtr<nsHttpConnection> conn =
   1620      ent->GetIdleConnection(respectUrgency, urgentTrans, &onlyUrgent);
   1621 
   1622  if (allUrgent) {
   1623    *allUrgent = onlyUrgent;
   1624  }
   1625 
   1626  if (conn) {
   1627    // This will update the class of the connection to be the class of
   1628    // the transaction dispatched on it.
   1629    ent->InsertIntoActiveConns(conn);
   1630    nsresult rv = DispatchTransaction(ent, trans, conn);
   1631    NS_ENSURE_SUCCESS(rv, rv);
   1632 
   1633    return NS_OK;
   1634  }
   1635 
   1636  return NS_ERROR_NOT_AVAILABLE;
   1637 }
   1638 
   1639 nsresult nsHttpConnectionMgr::TryDispatchExtendedCONNECTransaction(
   1640    ConnectionEntry* aEnt, nsHttpTransaction* aTrans, nsHttpConnection* aConn) {
   1641  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1642  MOZ_ASSERT(aTrans->IsWebsocketUpgrade() || aTrans->IsForWebTransport());
   1643  MOZ_ASSERT(aConn);
   1644 
   1645  ExtendedCONNECTSupport extendedConnect = aConn->GetExtendedCONNECTSupport();
   1646  LOG(("TryingDispatchTransaction: extended CONNECT"));
   1647  if (extendedConnect == ExtendedCONNECTSupport::NO_SUPPORT) {
   1648    LOG(
   1649        ("TryingDispatchTransaction: no support for extended CONNECT over "
   1650         "HTTP/2"));
   1651    // This is a transaction wants to do extended CONNECT and we already
   1652    // have a h2 connection that do not support it, we should disable h2
   1653    // for this transaction.
   1654    aTrans->DisableSpdy();
   1655 
   1656    // WebTransport doesn’t allow to fall back to HTTP/1.1.
   1657    if (aTrans->IsForWebTransport()) {
   1658      return NS_ERROR_CONNECTION_REFUSED;
   1659    }
   1660 
   1661    // See the assertion in nsHttpConnectionMgr::OnMsgCompleteUpgrade.
   1662    // Transactions used for WebSockets are expected to have
   1663    // NS_HTTP_STICKY_CONNECTION, so we also need to call MakeRestartable to
   1664    // allow them to restart successfully.
   1665    aTrans->MakeSticky();
   1666    aTrans->MakeRestartable();
   1667 
   1668    return NS_ERROR_HTTP2_FALLBACK_TO_HTTP1;
   1669  } else if (extendedConnect == ExtendedCONNECTSupport::SUPPORTED) {
   1670    LOG(("TryingDispatchTransaction: extended CONNECT supported"));
   1671 
   1672    // No limit for number of websockets, dispatch transaction to the
   1673    // tunnel
   1674    RefPtr<HttpConnectionBase> connToTunnel;
   1675    nsresult rv =
   1676        aConn->CreateTunnelStream(aTrans, getter_AddRefs(connToTunnel), true);
   1677    if (rv == NS_ERROR_WEBTRANSPORT_SESSION_LIMIT_EXCEEDED) {
   1678      LOG(
   1679          ("TryingDispatchTransaction: WebTransport session limit "
   1680           "exceeded"));
   1681      return rv;
   1682    }
   1683    aEnt->InsertIntoExtendedCONNECTConns(connToTunnel);
   1684    aTrans->SetConnection(nullptr);
   1685    connToTunnel->SetInTunnel();  // tells conn it is already in tunnel
   1686    if (aTrans->IsWebsocketUpgrade()) {
   1687      aTrans->SetIsHttp2Websocket(true);
   1688    }
   1689    rv = DispatchTransaction(aEnt, aTrans, connToTunnel);
   1690    // need to undo NonSticky bypass for transaction reset to continue
   1691    // for correct websocket upgrade handling
   1692    aTrans->MakeSticky();
   1693    aTrans->SetResettingForTunnelConn(false);
   1694    return rv;
   1695  }
   1696 
   1697  // if we aren't sure that extended CONNECT is supported yet or we are
   1698  // already at the connection limit then we queue the transaction
   1699  LOG(
   1700      ("TryingDispatchTransaction: unsure if extended CONNECT "
   1701       "supported"));
   1702  return NS_ERROR_NOT_AVAILABLE;
   1703 }
   1704 
   1705 nsresult nsHttpConnectionMgr::DispatchTransaction(ConnectionEntry* ent,
   1706                                                  nsHttpTransaction* trans,
   1707                                                  HttpConnectionBase* conn) {
   1708  uint32_t caps = trans->Caps();
   1709  int32_t priority = trans->Priority();
   1710  nsresult rv;
   1711 
   1712  LOG(
   1713      ("nsHttpConnectionMgr::DispatchTransaction "
   1714       "[ent-ci=%s %p trans=%p caps=%x conn=%p priority=%d isHttp2=%d "
   1715       "isHttp3=%d]\n",
   1716       ent->mConnInfo->HashKey().get(), ent, trans, caps, conn, priority,
   1717       conn->UsingSpdy(), conn->UsingHttp3()));
   1718 
   1719  // It is possible for a rate-paced transaction to be dispatched independent
   1720  // of the token bucket when the amount of parallelization has changed or
   1721  // when a muxed connection (e.g. h2) becomes available.
   1722  trans->CancelPacing(NS_OK);
   1723 
   1724  TimeStamp now = TimeStamp::Now();
   1725  TimeDuration elapsed = now - trans->GetPendingTime();
   1726  auto recordPendingTimeForHTTPSRR = [&](nsCString& aKey) {
   1727    uint32_t stage = trans->HTTPSSVCReceivedStage();
   1728    if (HTTPS_RR_IS_USED(stage)) {
   1729      glean::networking::transaction_wait_time_https_rr.AccumulateRawDuration(
   1730          elapsed);
   1731 
   1732    } else {
   1733      glean::networking::transaction_wait_time.AccumulateRawDuration(elapsed);
   1734    }
   1735  };
   1736 
   1737  PerfStats::RecordMeasurement(PerfStats::Metric::HttpTransactionWaitTime,
   1738                               elapsed);
   1739 
   1740  PROFILER_MARKER(
   1741      "DispatchTransaction", NETWORK,
   1742      MarkerOptions(MarkerThreadId::MainThread(),
   1743                    MarkerTiming::Interval(trans->GetPendingTime(), now)),
   1744      UrlMarker, trans->GetUrl(), elapsed, trans->ChannelId());
   1745 
   1746  nsAutoCString httpVersionkey("h1"_ns);
   1747  if (conn->UsingSpdy() || conn->UsingHttp3()) {
   1748    LOG(
   1749        ("Spdy Dispatch Transaction via Activate(). Transaction host = %s, "
   1750         "Connection host = %s\n",
   1751         trans->ConnectionInfo()->Origin(), conn->ConnectionInfo()->Origin()));
   1752    rv = conn->Activate(trans, caps, priority);
   1753    if (NS_SUCCEEDED(rv) && !trans->GetPendingTime().IsNull()) {
   1754      if (conn->UsingSpdy()) {
   1755        httpVersionkey = "h2"_ns;
   1756        glean::http::transaction_wait_time_spdy.AccumulateRawDuration(
   1757            now - trans->GetPendingTime());
   1758      } else {
   1759        httpVersionkey = "h3"_ns;
   1760        glean::http::transaction_wait_time_http3.AccumulateRawDuration(
   1761            now - trans->GetPendingTime());
   1762      }
   1763      recordPendingTimeForHTTPSRR(httpVersionkey);
   1764      trans->SetPendingTime(false);
   1765    }
   1766    return rv;
   1767  }
   1768 
   1769  MOZ_ASSERT(conn && !conn->Transaction(),
   1770             "DispatchTranaction() on non spdy active connection");
   1771 
   1772  rv = DispatchAbstractTransaction(ent, trans, caps, conn, priority);
   1773 
   1774  if (NS_SUCCEEDED(rv) && !trans->GetPendingTime().IsNull()) {
   1775    glean::http::transaction_wait_time_http.AccumulateRawDuration(
   1776        now - trans->GetPendingTime());
   1777    recordPendingTimeForHTTPSRR(httpVersionkey);
   1778    trans->SetPendingTime(false);
   1779  }
   1780  return rv;
   1781 }
   1782 
   1783 // Use this method for dispatching nsAHttpTransction's. It can only safely be
   1784 // used upon first use of a connection when NPN has not negotiated SPDY vs
   1785 // HTTP/1 yet as multiplexing onto an existing SPDY session requires a
   1786 // concrete nsHttpTransaction
   1787 nsresult nsHttpConnectionMgr::DispatchAbstractTransaction(
   1788    ConnectionEntry* ent, nsAHttpTransaction* aTrans, uint32_t caps,
   1789    HttpConnectionBase* conn, int32_t priority) {
   1790  MOZ_ASSERT(ent);
   1791 
   1792  nsresult rv;
   1793  MOZ_ASSERT(!conn->UsingSpdy(),
   1794             "Spdy Must Not Use DispatchAbstractTransaction");
   1795  LOG(
   1796      ("nsHttpConnectionMgr::DispatchAbstractTransaction "
   1797       "[ci=%s trans=%p caps=%x conn=%p]\n",
   1798       ent->mConnInfo->HashKey().get(), aTrans, caps, conn));
   1799 
   1800  RefPtr<nsAHttpTransaction> transaction(aTrans);
   1801  RefPtr<ConnectionHandle> handle = new ConnectionHandle(conn);
   1802 
   1803  // give the transaction the indirect reference to the connection.
   1804  transaction->SetConnection(handle);
   1805 
   1806  rv = conn->Activate(transaction, caps, priority);
   1807  if (NS_FAILED(rv)) {
   1808    LOG(("  conn->Activate failed [rv=%" PRIx32 "]\n",
   1809         static_cast<uint32_t>(rv)));
   1810    DebugOnly<nsresult> rv_remove = ent->RemoveActiveConnection(conn);
   1811    MOZ_ASSERT(NS_SUCCEEDED(rv_remove));
   1812 
   1813    // sever back references to connection, and do so without triggering
   1814    // a call to ReclaimConnection ;-)
   1815    transaction->SetConnection(nullptr);
   1816    handle->Reset();  // destroy the connection
   1817  }
   1818 
   1819  return rv;
   1820 }
   1821 
   1822 nsresult nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction* trans) {
   1823  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1824 
   1825  // since "adds" and "cancels" are processed asynchronously and because
   1826  // various events might trigger an "add" directly on the socket thread,
   1827  // we must take care to avoid dispatching a transaction that has already
   1828  // been canceled (see bug 190001).
   1829  if (NS_FAILED(trans->Status())) {
   1830    LOG(("  transaction was canceled... dropping event!\n"));
   1831    return NS_OK;
   1832  }
   1833 
   1834  // Make sure a transaction is not in a pending queue.
   1835  CheckTransInPendingQueue(trans);
   1836 
   1837  trans->SetPendingTime();
   1838 
   1839  PROFILER_MARKER("ProcessNewTransaction", NETWORK,
   1840                  MarkerThreadId::MainThread(), UrlMarker, trans->GetUrl(),
   1841                  TimeDuration::Zero(), trans->ChannelId());
   1842 
   1843  nsresult rv = NS_OK;
   1844  nsHttpConnectionInfo* ci = trans->ConnectionInfo();
   1845  MOZ_ASSERT(ci);
   1846  MOZ_ASSERT(!ci->IsHttp3() || !(trans->Caps() & NS_HTTP_DISALLOW_HTTP3));
   1847 
   1848  bool isWildcard = false;
   1849  ConnectionEntry* ent = GetOrCreateConnectionEntry(
   1850      ci, trans->Caps() & NS_HTTP_DISALLOW_HTTP2_PROXY,
   1851      trans->Caps() & NS_HTTP_DISALLOW_SPDY,
   1852      trans->Caps() & NS_HTTP_DISALLOW_HTTP3, &isWildcard);
   1853  MOZ_ASSERT(ent);
   1854 
   1855  if (nsHttpHandler::EchConfigEnabled(ci->IsHttp3())) {
   1856    ent->MaybeUpdateEchConfig(ci);
   1857  }
   1858 
   1859  // Check if the transaction already has a sticky reference to a connection.
   1860  // If so, then we can just use it directly by transferring its reference
   1861  // to the new connection variable instead of searching for a new one
   1862 
   1863  nsAHttpConnection* wrappedConnection = trans->Connection();
   1864  RefPtr<HttpConnectionBase> conn;
   1865  RefPtr<PendingTransactionInfo> pendingTransInfo;
   1866  if (wrappedConnection) conn = wrappedConnection->TakeHttpConnection();
   1867 
   1868  if (conn) {
   1869    MOZ_ASSERT(trans->Caps() & NS_HTTP_STICKY_CONNECTION);
   1870    LOG(
   1871        ("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
   1872         "sticky connection=%p\n",
   1873         trans, conn.get()));
   1874 
   1875    if (!ent->IsInActiveConns(conn)) {
   1876      LOG(
   1877          ("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
   1878           "sticky connection=%p needs to go on the active list\n",
   1879           trans, conn.get()));
   1880 
   1881      // make sure it isn't on the idle list - we expect this to be an
   1882      // unknown fresh connection
   1883      MOZ_ASSERT(!ent->IsInIdleConnections(conn));
   1884      MOZ_ASSERT(!conn->IsExperienced());
   1885 
   1886      ent->InsertIntoActiveConns(conn);  // make it active
   1887    }
   1888 
   1889    trans->SetConnection(nullptr);
   1890    rv = DispatchTransaction(ent, trans, conn);
   1891  } else if (isWildcard) {
   1892    // Determine which connections we want to use.
   1893    // - If this is an HTTP/3 proxy connection (`isHttp3Proxy == true`), we only
   1894    //   want to consider existing HTTP/3 connections, so we set aNoHttp2 = true
   1895    //   and aNoHttp3 = false.
   1896    // - Otherwise (`isHttp3Proxy == false`), we are looking for a regular
   1897    //   HTTP/2 connection, so we set aNoHttp2 = false and aNoHttp3 = true.
   1898    bool isHttp3Proxy = ci->IsHttp3ProxyConnection();
   1899    RefPtr<HttpConnectionBase> conn =
   1900        GetH2orH3ActiveConn(ent, isHttp3Proxy, !isHttp3Proxy);
   1901    if (ci->UsingHttpsProxy() && ci->UsingConnect()) {
   1902      LOG(("About to create new tunnel conn from [%p]", conn.get()));
   1903      ConnectionEntry* specificEnt = mCT.GetWeak(ci->HashKey());
   1904 
   1905      if (!specificEnt) {
   1906        RefPtr<nsHttpConnectionInfo> clone(ci->Clone());
   1907        specificEnt = new ConnectionEntry(clone);
   1908        mCT.InsertOrUpdate(clone->HashKey(), RefPtr{specificEnt});
   1909      }
   1910 
   1911      ent = specificEnt;
   1912      bool atLimit = AtActiveConnectionLimit(ent, trans->Caps());
   1913      if (atLimit) {
   1914        rv = NS_ERROR_NOT_AVAILABLE;
   1915      } else {
   1916        RefPtr<HttpConnectionBase> newTunnel;
   1917        conn->CreateTunnelStream(trans, getter_AddRefs(newTunnel));
   1918 
   1919        ent->InsertIntoActiveConns(newTunnel);
   1920        trans->SetConnection(nullptr);
   1921        newTunnel->SetInTunnel();
   1922        rv = DispatchTransaction(ent, trans, newTunnel);
   1923        // need to undo the bypass for transaction reset for proxy
   1924        trans->MakeNonRestartable();
   1925      }
   1926    } else {
   1927      rv = DispatchTransaction(ent, trans, conn);
   1928    }
   1929  } else {
   1930    if (!ent->AllowHttp2()) {
   1931      trans->DisableSpdy();
   1932    }
   1933    pendingTransInfo = new PendingTransactionInfo(trans);
   1934    rv = TryDispatchTransaction(ent, false, pendingTransInfo);
   1935  }
   1936 
   1937  if (NS_SUCCEEDED(rv)) {
   1938    LOG(("  ProcessNewTransaction Dispatch Immediately trans=%p\n", trans));
   1939    return rv;
   1940  }
   1941 
   1942  if (rv == NS_ERROR_NOT_AVAILABLE) {
   1943    if (!pendingTransInfo) {
   1944      pendingTransInfo = new PendingTransactionInfo(trans);
   1945    }
   1946 
   1947    ent->InsertTransaction(pendingTransInfo);
   1948    return NS_OK;
   1949  }
   1950 
   1951  LOG(("  ProcessNewTransaction Hard Error trans=%p rv=%" PRIx32 "\n", trans,
   1952       static_cast<uint32_t>(rv)));
   1953  return rv;
   1954 }
   1955 
   1956 void nsHttpConnectionMgr::IncrementActiveConnCount() {
   1957  mNumActiveConns++;
   1958  ActivateTimeoutTick();
   1959 }
   1960 
   1961 void nsHttpConnectionMgr::DecrementActiveConnCount(HttpConnectionBase* conn) {
   1962  MOZ_DIAGNOSTIC_ASSERT(mNumActiveConns > 0);
   1963  if (mNumActiveConns > 0) {
   1964    mNumActiveConns--;
   1965  }
   1966 
   1967  RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn);
   1968  if (!connTCP || connTCP->EverUsedSpdy()) mNumSpdyHttp3ActiveConns--;
   1969  ConditionallyStopTimeoutTick();
   1970 }
   1971 
   1972 void nsHttpConnectionMgr::StartedConnect() {
   1973  mNumActiveConns++;
   1974  ActivateTimeoutTick();  // likely disabled by RecvdConnect()
   1975 }
   1976 
   1977 void nsHttpConnectionMgr::RecvdConnect() {
   1978  MOZ_DIAGNOSTIC_ASSERT(mNumActiveConns > 0);
   1979  if (mNumActiveConns > 0) {
   1980    mNumActiveConns--;
   1981  }
   1982 
   1983  ConditionallyStopTimeoutTick();
   1984 }
   1985 
   1986 void nsHttpConnectionMgr::DispatchSpdyPendingQ(
   1987    nsTArray<RefPtr<PendingTransactionInfo>>& pendingQ, ConnectionEntry* ent,
   1988    HttpConnectionBase* connH2, HttpConnectionBase* connH3) {
   1989  if (pendingQ.Length() == 0) {
   1990    return;
   1991  }
   1992 
   1993  nsTArray<RefPtr<PendingTransactionInfo>> leftovers;
   1994  uint32_t index;
   1995  // Dispatch all the transactions we can
   1996  for (index = 0; index < pendingQ.Length() &&
   1997                  ((connH3 && connH3->CanDirectlyActivate()) ||
   1998                   (connH2 && connH2->CanDirectlyActivate()));
   1999       ++index) {
   2000    PendingTransactionInfo* pendingTransInfo = pendingQ[index];
   2001 
   2002    // We can not dispatch NS_HTTP_ALLOW_KEEPALIVE transactions.
   2003    // For WebTransport case, we can't dispatch the transaction here. We need to
   2004    // put the transaction back, so TryDispatchTransaction can be called later.
   2005    // TODO: this is hacky and should be improved.
   2006    if (!(pendingTransInfo->Transaction()->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
   2007        pendingTransInfo->Transaction()->IsResettingForTunnelConn()) {
   2008      leftovers.AppendElement(pendingTransInfo);
   2009      continue;
   2010    }
   2011 
   2012    // Try dispatching on HTTP3 first
   2013    HttpConnectionBase* conn = nullptr;
   2014    if (!(pendingTransInfo->Transaction()->Caps() & NS_HTTP_DISALLOW_HTTP3) &&
   2015        connH3 && connH3->CanDirectlyActivate()) {
   2016      conn = connH3;
   2017    } else if (!(pendingTransInfo->Transaction()->Caps() &
   2018                 NS_HTTP_DISALLOW_SPDY) &&
   2019               connH2 && connH2->CanDirectlyActivate()) {
   2020      conn = connH2;
   2021    } else {
   2022      leftovers.AppendElement(pendingTransInfo);
   2023      continue;
   2024    }
   2025 
   2026    nsresult rv =
   2027        DispatchTransaction(ent, pendingTransInfo->Transaction(), conn);
   2028    if (NS_FAILED(rv)) {
   2029      // Dispatching a transaction to an existing HTTP/2 session should not
   2030      // fail. The only expected failure here is
   2031      // NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED (e.g., when a
   2032      // speculative/preconnected HTTP/2 session was created before). Any other
   2033      // rv indicates a bug.
   2034      MOZ_ASSERT(rv == NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED,
   2035                 "Dispatch H2 transaction should only fail with Local Network "
   2036                 "Access denied");
   2037      LOG(("ProcessSpdyPendingQ Dispatch Transaction failed trans=%p\n",
   2038           pendingTransInfo->Transaction()));
   2039      pendingTransInfo->Transaction()->Close(rv);
   2040    }
   2041  }
   2042 
   2043  // Slurp up the rest of the pending queue into our leftovers bucket (we
   2044  // might have some left if conn->CanDirectlyActivate returned false)
   2045  for (; index < pendingQ.Length(); ++index) {
   2046    PendingTransactionInfo* pendingTransInfo = pendingQ[index];
   2047    leftovers.AppendElement(pendingTransInfo);
   2048  }
   2049 
   2050  // Put the leftovers back in the pending queue and get rid of the
   2051  // transactions we dispatched
   2052  pendingQ = std::move(leftovers);
   2053 }
   2054 
   2055 // This function tries to dispatch the pending h2 or h3 transactions on
   2056 // the connection entry sent in as an argument. It will do so on the
   2057 // active h2 or h3 connection either in that same entry or from the
   2058 // coalescing hash table
   2059 void nsHttpConnectionMgr::ProcessSpdyPendingQ(ConnectionEntry* ent) {
   2060  // Look for one HTTP2 and one HTTP3 connection.
   2061  // We may have transactions that have NS_HTTP_DISALLOW_SPDY/HTTP3 set
   2062  // and we may need an alternative.
   2063  HttpConnectionBase* connH3 = GetH2orH3ActiveConn(ent, true, false);
   2064  HttpConnectionBase* connH2 = GetH2orH3ActiveConn(ent, false, true);
   2065  if ((!connH3 || !connH3->CanDirectlyActivate()) &&
   2066      (!connH2 || !connH2->CanDirectlyActivate())) {
   2067    return;
   2068  }
   2069 
   2070  nsTArray<RefPtr<PendingTransactionInfo>> urgentQ;
   2071  ent->AppendPendingUrgentStartQ(urgentQ);
   2072  DispatchSpdyPendingQ(urgentQ, ent, connH2, connH3);
   2073  for (const auto& transactionInfo : Reversed(urgentQ)) {
   2074    ent->InsertTransaction(transactionInfo);
   2075  }
   2076 
   2077  if ((!connH3 || !connH3->CanDirectlyActivate()) &&
   2078      (!connH2 || !connH2->CanDirectlyActivate())) {
   2079    return;
   2080  }
   2081 
   2082  nsTArray<RefPtr<PendingTransactionInfo>> pendingQ;
   2083  // XXX Get all transactions for SPDY currently.
   2084  ent->AppendPendingQForNonFocusedWindows(0, pendingQ);
   2085  DispatchSpdyPendingQ(pendingQ, ent, connH2, connH3);
   2086 
   2087  // Put the leftovers back in the pending queue.
   2088  for (const auto& transactionInfo : pendingQ) {
   2089    ent->InsertTransaction(transactionInfo);
   2090  }
   2091 }
   2092 
   2093 void nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ(int32_t, ARefBase*) {
   2094  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2095  LOG(("nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ\n"));
   2096  for (const auto& entry : mCT.Values()) {
   2097    ProcessSpdyPendingQ(entry.get());
   2098  }
   2099 }
   2100 
   2101 // Given a connection entry, return an active h2 or h3 connection
   2102 // that can be directly activated or null.
   2103 HttpConnectionBase* nsHttpConnectionMgr::GetH2orH3ActiveConn(
   2104    ConnectionEntry* ent, bool aNoHttp2, bool aNoHttp3) {
   2105  if (aNoHttp2 && aNoHttp3) {
   2106    return nullptr;
   2107  }
   2108  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2109  MOZ_ASSERT(ent);
   2110 
   2111  bool isHttp3 = ent->IsHttp3() || ent->IsHttp3ProxyConnection();
   2112  // First look at ent. If protocol that ent provides is no forbidden,
   2113  // i.e. ent use HTTP3 and !aNoHttp3 or en uses HTTP over TCP and !aNoHttp2.
   2114  if ((!aNoHttp3 && isHttp3) || (!aNoHttp2 && !isHttp3)) {
   2115    HttpConnectionBase* conn = ent->GetH2orH3ActiveConn();
   2116    if (conn) {
   2117      return conn;
   2118    }
   2119  }
   2120 
   2121  nsHttpConnectionInfo* ci = ent->mConnInfo;
   2122 
   2123  // there was no active HTTP2/3 connection in the connection entry, but
   2124  // there might be one in the hash table for coalescing
   2125  HttpConnectionBase* existingConn =
   2126      FindCoalescableConnection(ent, false, aNoHttp2, aNoHttp3);
   2127  if (existingConn) {
   2128    LOG(
   2129        ("GetH2orH3ActiveConn() request for ent %p %s "
   2130         "found an active connection %p in the coalescing hashtable\n",
   2131         ent, ci->HashKey().get(), existingConn));
   2132    return existingConn;
   2133  }
   2134 
   2135  LOG(
   2136      ("GetH2orH3ActiveConn() request for ent %p %s "
   2137       "did not find an active connection\n",
   2138       ent, ci->HashKey().get()));
   2139  return nullptr;
   2140 }
   2141 
   2142 //-----------------------------------------------------------------------------
   2143 
   2144 void nsHttpConnectionMgr::AbortAndCloseAllConnections(int32_t, ARefBase*) {
   2145  if (!OnSocketThread()) {
   2146    (void)PostEvent(&nsHttpConnectionMgr::AbortAndCloseAllConnections);
   2147    return;
   2148  }
   2149 
   2150  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2151  LOG(("nsHttpConnectionMgr::AbortAndCloseAllConnections\n"));
   2152  for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
   2153    RefPtr<ConnectionEntry> ent = iter.Data();
   2154 
   2155    // Close all active connections.
   2156    ent->CloseActiveConnections();
   2157 
   2158    // Close all idle connections.
   2159    ent->CloseIdleConnections();
   2160 
   2161    // Close tunneled connections
   2162    ent->CloseExtendedCONNECTConnections();
   2163 
   2164    ent->ClosePendingConnections();
   2165 
   2166    // Close all pending transactions.
   2167    ent->CancelAllTransactions(NS_ERROR_ABORT);
   2168 
   2169    // Close all half open tcp connections.
   2170    ent->CloseAllDnsAndConnectSockets();
   2171 
   2172    MOZ_ASSERT(!ent->mDoNotDestroy);
   2173    iter.Remove();
   2174  }
   2175 
   2176  mActiveTransactions[false].Clear();
   2177  mActiveTransactions[true].Clear();
   2178 }
   2179 
   2180 void nsHttpConnectionMgr::OnMsgShutdown(int32_t, ARefBase* param) {
   2181  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2182  LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
   2183 
   2184  gHttpHandler->StopRequestTokenBucket();
   2185  AbortAndCloseAllConnections(0, nullptr);
   2186 
   2187  // If all idle connections are removed we can stop pruning dead
   2188  // connections.
   2189  ConditionallyStopPruneDeadConnectionsTimer();
   2190 
   2191  if (mTimeoutTick) {
   2192    mTimeoutTick->Cancel();
   2193    mTimeoutTick = nullptr;
   2194    mTimeoutTickArmed = false;
   2195  }
   2196  if (mTimer) {
   2197    mTimer->Cancel();
   2198    mTimer = nullptr;
   2199  }
   2200  if (mTrafficTimer) {
   2201    mTrafficTimer->Cancel();
   2202    mTrafficTimer = nullptr;
   2203  }
   2204  DestroyThrottleTicker();
   2205 
   2206  mCoalescingHash.Clear();
   2207 
   2208  // signal shutdown complete
   2209  nsCOMPtr<nsIRunnable> runnable =
   2210      new ConnEvent(this, &nsHttpConnectionMgr::OnMsgShutdownConfirm, 0, param);
   2211  NS_DispatchToMainThread(runnable);
   2212 }
   2213 
   2214 void nsHttpConnectionMgr::OnMsgShutdownConfirm(int32_t priority,
   2215                                               ARefBase* param) {
   2216  MOZ_ASSERT(NS_IsMainThread());
   2217  LOG(("nsHttpConnectionMgr::OnMsgShutdownConfirm\n"));
   2218 
   2219  BoolWrapper* shutdown = static_cast<BoolWrapper*>(param);
   2220  shutdown->mBool = true;
   2221 }
   2222 
   2223 void nsHttpConnectionMgr::OnMsgNewTransaction(int32_t priority,
   2224                                              ARefBase* param) {
   2225  nsHttpTransaction* trans = static_cast<nsHttpTransaction*>(param);
   2226 
   2227  LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", trans));
   2228  trans->SetPriority(priority);
   2229  nsresult rv = ProcessNewTransaction(trans);
   2230  if (NS_FAILED(rv)) trans->Close(rv);  // for whatever its worth
   2231 }
   2232 
   2233 void nsHttpConnectionMgr::OnMsgNewTransactionWithStickyConn(int32_t priority,
   2234                                                            ARefBase* param) {
   2235  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2236 
   2237  NewTransactionData* data = static_cast<NewTransactionData*>(param);
   2238  LOG(
   2239      ("nsHttpConnectionMgr::OnMsgNewTransactionWithStickyConn "
   2240       "[trans=%p, transWithStickyConn=%p, conn=%p]\n",
   2241       data->mTrans.get(), data->mTransWithStickyConn.get(),
   2242       data->mTransWithStickyConn->Connection()));
   2243 
   2244  MOZ_ASSERT(data->mTransWithStickyConn &&
   2245             data->mTransWithStickyConn->Caps() & NS_HTTP_STICKY_CONNECTION);
   2246 
   2247  data->mTrans->SetPriority(data->mPriority);
   2248 
   2249  RefPtr<nsAHttpConnection> conn = data->mTransWithStickyConn->Connection();
   2250  if (conn && conn->IsPersistent()) {
   2251    // This is so far a workaround to only reuse persistent
   2252    // connection for authentication retry. See bug 459620 comment 4
   2253    // for details.
   2254    LOG((" Reuse connection [%p] for transaction [%p]", conn.get(),
   2255         data->mTrans.get()));
   2256    data->mTrans->SetConnection(conn);
   2257  }
   2258 
   2259  nsresult rv = ProcessNewTransaction(data->mTrans);
   2260  if (NS_FAILED(rv)) {
   2261    data->mTrans->Close(rv);  // for whatever its worth
   2262  }
   2263 }
   2264 
   2265 void nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority,
   2266                                                  ARefBase* param) {
   2267  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2268  LOG(("nsHttpConnectionMgr::OnMsgReschedTransaction [trans=%p]\n", param));
   2269 
   2270  RefPtr<nsHttpTransaction> trans = static_cast<nsHttpTransaction*>(param);
   2271  trans->SetPriority(priority);
   2272 
   2273  if (!trans->ConnectionInfo()) {
   2274    return;
   2275  }
   2276  ConnectionEntry* ent = mCT.GetWeak(trans->ConnectionInfo()->HashKey());
   2277 
   2278  if (ent) {
   2279    ent->ReschedTransaction(trans);
   2280  }
   2281 }
   2282 
   2283 void nsHttpConnectionMgr::OnMsgUpdateClassOfServiceOnTransaction(
   2284    ClassOfService cos, ARefBase* param) {
   2285  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2286  LOG(
   2287      ("nsHttpConnectionMgr::OnMsgUpdateClassOfServiceOnTransaction "
   2288       "[trans=%p]\n",
   2289       param));
   2290 
   2291  nsHttpTransaction* trans = static_cast<nsHttpTransaction*>(param);
   2292 
   2293  ClassOfService previous = trans->GetClassOfService();
   2294  trans->SetClassOfService(cos);
   2295 
   2296  // incremental change alone will not trigger a reschedule
   2297  if ((previous.Flags() ^ cos.Flags()) &
   2298      (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED)) {
   2299    (void)RescheduleTransaction(trans, trans->Priority());
   2300  }
   2301 }
   2302 
   2303 void nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason,
   2304                                                 ARefBase* param) {
   2305  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2306  LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
   2307 
   2308  nsresult closeCode = static_cast<nsresult>(reason);
   2309 
   2310  // caller holds a ref to param/trans on stack
   2311  nsHttpTransaction* trans = static_cast<nsHttpTransaction*>(param);
   2312 
   2313  //
   2314  // if the transaction owns a connection and the transaction is not done,
   2315  // then ask the connection to close the transaction.  otherwise, close the
   2316  // transaction directly (removing it from the pending queue first).
   2317  //
   2318  RefPtr<nsAHttpConnection> conn(trans->Connection());
   2319  if (conn && !trans->IsDone()) {
   2320    conn->CloseTransaction(trans, closeCode);
   2321  } else {
   2322    ConnectionEntry* ent = nullptr;
   2323    if (trans->ConnectionInfo()) {
   2324      ent = mCT.GetWeak(trans->ConnectionInfo()->HashKey());
   2325    }
   2326    if (ent && ent->RemoveTransFromPendingQ(trans)) {
   2327      LOG(
   2328          ("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]"
   2329           " removed from pending queue\n",
   2330           trans));
   2331    }
   2332 
   2333    trans->Close(closeCode);
   2334 
   2335    // Cancel is a pretty strong signal that things might be hanging
   2336    // so we want to cancel any null transactions related to this connection
   2337    // entry. They are just optimizations, but they aren't hooked up to
   2338    // anything that might get canceled from the rest of gecko, so best
   2339    // to assume that's what was meant by the cancel we did receive if
   2340    // it only applied to something in the queue.
   2341    if (ent) {
   2342      ent->CloseAllActiveConnsWithNullTransactcion(closeCode);
   2343    }
   2344  }
   2345 }
   2346 
   2347 void nsHttpConnectionMgr::OnMsgProcessPendingQ(int32_t, ARefBase* param) {
   2348  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2349  nsHttpConnectionInfo* ci = static_cast<nsHttpConnectionInfo*>(param);
   2350 
   2351  if (!ci) {
   2352    LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=nullptr]\n"));
   2353    // Try and dispatch everything
   2354    for (const auto& entry : mCT.Values()) {
   2355      (void)ProcessPendingQForEntry(entry.get(), true);
   2356    }
   2357    return;
   2358  }
   2359 
   2360  LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n",
   2361       ci->HashKey().get()));
   2362 
   2363  // start by processing the queue identified by the given connection info.
   2364  ConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
   2365  if (!(ent && ProcessPendingQForEntry(ent, false))) {
   2366    // if we reach here, it means that we couldn't dispatch a transaction
   2367    // for the specified connection info.  walk the connection table...
   2368    for (const auto& entry : mCT.Values()) {
   2369      if (ProcessPendingQForEntry(entry.get(), false)) {
   2370        break;
   2371      }
   2372    }
   2373  }
   2374 }
   2375 
   2376 nsresult nsHttpConnectionMgr::CancelTransactions(nsHttpConnectionInfo* ci,
   2377                                                 nsresult code) {
   2378  LOG(("nsHttpConnectionMgr::CancelTransactions %s\n", ci->HashKey().get()));
   2379 
   2380  int32_t intReason = static_cast<int32_t>(code);
   2381  return PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransactions, intReason,
   2382                   ci);
   2383 }
   2384 
   2385 void nsHttpConnectionMgr::OnMsgCancelTransactions(int32_t code,
   2386                                                  ARefBase* param) {
   2387  nsresult reason = static_cast<nsresult>(code);
   2388  nsHttpConnectionInfo* ci = static_cast<nsHttpConnectionInfo*>(param);
   2389  ConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
   2390  LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p\n",
   2391       ci->HashKey().get(), ent));
   2392  if (ent) {
   2393    ent->CancelAllTransactions(reason);
   2394  }
   2395 }
   2396 
   2397 void nsHttpConnectionMgr::OnMsgPruneDeadConnections(int32_t, ARefBase*) {
   2398  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2399  LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
   2400 
   2401  // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
   2402  mTimeOfNextWakeUp = UINT64_MAX;
   2403 
   2404  // check canreuse() for all idle connections plus any active connections on
   2405  // connection entries that are using spdy.
   2406  if (mNumIdleConns ||
   2407      (mNumActiveConns && StaticPrefs::network_http_http2_enabled())) {
   2408    for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
   2409      RefPtr<ConnectionEntry> ent = iter.Data();
   2410 
   2411      LOG(("  pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
   2412 
   2413      // Find out how long it will take for next idle connection to not
   2414      // be reusable anymore.
   2415      uint32_t timeToNextExpire = ent->PruneDeadConnections();
   2416 
   2417      // If time to next expire found is shorter than time to next
   2418      // wake-up, we need to change the time for next wake-up.
   2419      if (timeToNextExpire != UINT32_MAX) {
   2420        uint32_t now = NowInSeconds();
   2421        uint64_t timeOfNextExpire = now + timeToNextExpire;
   2422        // If pruning of dead connections is not already scheduled to
   2423        // happen or time found for next connection to expire is is
   2424        // before mTimeOfNextWakeUp, we need to schedule the pruning to
   2425        // happen after timeToNextExpire.
   2426        if (!mTimer || timeOfNextExpire < mTimeOfNextWakeUp) {
   2427          PruneDeadConnectionsAfter(timeToNextExpire);
   2428        }
   2429      } else {
   2430        ConditionallyStopPruneDeadConnectionsTimer();
   2431      }
   2432 
   2433      ent->RemoveEmptyPendingQ();
   2434 
   2435      // If this entry is empty, we have too many entries busy then
   2436      // we can clean it up and restart
   2437      if (mCT.Count() > 125 && ent->IdleConnectionsLength() == 0 &&
   2438          ent->ActiveConnsLength() == 0 &&
   2439          ent->DnsAndConnectSocketsLength() == 0 &&
   2440          ent->PendingQueueLength() == 0 &&
   2441          ent->UrgentStartQueueLength() == 0 && !ent->mDoNotDestroy &&
   2442          (!ent->mUsingSpdy || mCT.Count() > 300)) {
   2443        LOG(("    removing empty connection entry\n"));
   2444        iter.Remove();
   2445        continue;
   2446      }
   2447 
   2448      // Otherwise use this opportunity to compact our arrays...
   2449      ent->Compact();
   2450    }
   2451  }
   2452 }
   2453 
   2454 void nsHttpConnectionMgr::OnMsgPruneNoTraffic(int32_t, ARefBase*) {
   2455  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2456  LOG(("nsHttpConnectionMgr::OnMsgPruneNoTraffic\n"));
   2457 
   2458  // Prune connections without traffic
   2459  for (const RefPtr<ConnectionEntry>& ent : mCT.Values()) {
   2460    // Close the connections with no registered traffic.
   2461    ent->PruneNoTraffic();
   2462  }
   2463 
   2464  mPruningNoTraffic = false;  // not pruning anymore
   2465 }
   2466 
   2467 void nsHttpConnectionMgr::OnMsgVerifyTraffic(int32_t, ARefBase*) {
   2468  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2469  LOG(("nsHttpConnectionMgr::OnMsgVerifyTraffic\n"));
   2470 
   2471  if (mPruningNoTraffic) {
   2472    // Called in the time gap when the timeout to prune notraffic
   2473    // connections has triggered but the pruning hasn't happened yet.
   2474    return;
   2475  }
   2476 
   2477  mCoalescingHash.Clear();
   2478 
   2479  // Mark connections for traffic verification
   2480  for (const auto& entry : mCT.Values()) {
   2481    entry->ResetIPFamilyPreference();
   2482    entry->VerifyTraffic();
   2483  }
   2484 
   2485  // If the timer is already there. we just re-init it
   2486  if (!mTrafficTimer) {
   2487    mTrafficTimer = NS_NewTimer();
   2488  }
   2489 
   2490  // failure to create a timer is not a fatal error, but dead
   2491  // connections will not be cleaned up as nicely
   2492  if (mTrafficTimer) {
   2493    // Give active connections time to get more traffic before killing
   2494    // them off. Default: 5000 milliseconds
   2495    mTrafficTimer->Init(this, gHttpHandler->NetworkChangedTimeout(),
   2496                        nsITimer::TYPE_ONE_SHOT);
   2497  } else {
   2498    NS_WARNING("failed to create timer for VerifyTraffic!");
   2499  }
   2500  // Calling ActivateTimeoutTick to ensure the next timeout tick is 1s.
   2501  ActivateTimeoutTick();
   2502 }
   2503 
   2504 void nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup(int32_t,
   2505                                                              ARefBase* param) {
   2506  LOG(("nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup\n"));
   2507  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2508 
   2509  mCoalescingHash.Clear();
   2510 
   2511  nsHttpConnectionInfo* ci = static_cast<nsHttpConnectionInfo*>(param);
   2512 
   2513  for (const auto& entry : mCT.Values()) {
   2514    entry->ClosePersistentConnections();
   2515  }
   2516 
   2517  if (ci) ResetIPFamilyPreference(ci);
   2518 }
   2519 
   2520 void nsHttpConnectionMgr::OnMsgDoSingleConnectionCleanup(int32_t,
   2521                                                         ARefBase* param) {
   2522  LOG(("nsHttpConnectionMgr::OnMsgDoSingleConnectionCleanup\n"));
   2523  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2524 
   2525  nsHttpConnectionInfo* ci = static_cast<nsHttpConnectionInfo*>(param);
   2526 
   2527  if (!ci) {
   2528    return;
   2529  }
   2530 
   2531  ConnectionEntry* entry = mCT.GetWeak(ci->HashKey());
   2532  if (entry) {
   2533    entry->ClosePersistentConnections();
   2534  }
   2535 
   2536  ResetIPFamilyPreference(ci);
   2537 }
   2538 
   2539 void nsHttpConnectionMgr::OnMsgReclaimConnection(HttpConnectionBase* conn) {
   2540  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2541 
   2542  //
   2543  // 1) remove the connection from the active list
   2544  // 2) if keep-alive, add connection to idle list
   2545  // 3) post event to process the pending transaction queue
   2546  //
   2547 
   2548  MOZ_ASSERT(conn);
   2549  ConnectionEntry* ent = conn->ConnectionInfo()
   2550                             ? mCT.GetWeak(conn->ConnectionInfo()->HashKey())
   2551                             : nullptr;
   2552 
   2553  if (!ent) {
   2554    // this can happen if the connection is made outside of the
   2555    // connection manager and is being "reclaimed" for use with
   2556    // future transactions. HTTP/2 tunnels work like this.
   2557    bool isWildcard = false;
   2558    ent = GetOrCreateConnectionEntry(conn->ConnectionInfo(), true, false, false,
   2559                                     &isWildcard);
   2560    LOG(
   2561        ("nsHttpConnectionMgr::OnMsgReclaimConnection conn %p "
   2562         "forced new hash entry %s\n",
   2563         conn, conn->ConnectionInfo()->HashKey().get()));
   2564  }
   2565 
   2566  MOZ_ASSERT(ent);
   2567  RefPtr<nsHttpConnectionInfo> ci(ent->mConnInfo);
   2568 
   2569  LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [ent=%p conn=%p]\n", ent,
   2570       conn));
   2571 
   2572  // If the connection is in the active list, remove that entry
   2573  // and the reference held by the mActiveConns list.
   2574  // This is never the final reference on conn as the event context
   2575  // is also holding one that is released at the end of this function.
   2576 
   2577  RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn);
   2578  if (!connTCP || connTCP->EverUsedSpdy()) {
   2579    // Spdyand Http3 connections aren't reused in the traditional HTTP way in
   2580    // the idleconns list, they are actively multplexed as active
   2581    // conns. Even when they have 0 transactions on them they are
   2582    // considered active connections. So when one is reclaimed it
   2583    // is really complete and is meant to be shut down and not
   2584    // reused.
   2585    conn->DontReuse();
   2586  }
   2587 
   2588  // a connection that still holds a reference to a transaction was
   2589  // not closed naturally (i.e. it was reset or aborted) and is
   2590  // therefore not something that should be reused.
   2591  if (conn->Transaction()) {
   2592    conn->DontReuse();
   2593  }
   2594 
   2595  if (NS_SUCCEEDED(ent->RemoveActiveConnection(conn)) ||
   2596      NS_SUCCEEDED(ent->RemovePendingConnection(conn))) {
   2597  } else {
   2598    LOG(
   2599        ("HttpConnectionBase %p not found in its connection entry, try "
   2600         "OwnerEntry",
   2601         conn));
   2602    RefPtr<ConnectionEntry> entry = conn->OwnerEntry();
   2603    if (entry) {
   2604      entry->RemoveActiveConnection(conn);
   2605    }
   2606  }
   2607 
   2608  MOZ_ASSERT(conn->OwnerEntry() == nullptr);
   2609 
   2610  if (connTCP && connTCP->CanReuse()) {
   2611    LOG(("  adding connection to idle list\n"));
   2612    // Keep The idle connection list sorted with the connections that
   2613    // have moved the largest data pipelines at the front because these
   2614    // connections have the largest cwnds on the server.
   2615 
   2616    // The linear search is ok here because the number of idleconns
   2617    // in a single entry is generally limited to a small number (i.e. 6)
   2618 
   2619    ent->InsertIntoIdleConnections(connTCP);
   2620  } else {
   2621    if (ent->IsInExtendedCONNECTConns(conn)) {
   2622      ent->RemoveExtendedCONNECTConns(conn);
   2623    }
   2624    LOG(("  connection cannot be reused; closing connection\n"));
   2625    conn->SetCloseReason(ConnectionCloseReason::CANT_REUSED);
   2626    conn->Close(NS_ERROR_ABORT);
   2627  }
   2628 
   2629  OnMsgProcessPendingQ(0, ci);
   2630 }
   2631 
   2632 void nsHttpConnectionMgr::OnMsgCompleteUpgrade(int32_t, ARefBase* param) {
   2633  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2634 
   2635  nsresult rv = NS_OK;
   2636  nsCompleteUpgradeData* data = static_cast<nsCompleteUpgradeData*>(param);
   2637  MOZ_ASSERT(data->mTrans && data->mTrans->Caps() & NS_HTTP_STICKY_CONNECTION);
   2638 
   2639  RefPtr<nsAHttpConnection> conn(data->mTrans->Connection());
   2640  LOG(
   2641      ("nsHttpConnectionMgr::OnMsgCompleteUpgrade "
   2642       "conn=%p listener=%p wrapped=%d\n",
   2643       conn.get(), data->mUpgradeListener.get(), data->mJsWrapped));
   2644 
   2645  if (!conn) {
   2646    // Delay any error reporting to happen in transportAvailableFunc
   2647    rv = NS_ERROR_UNEXPECTED;
   2648  } else {
   2649    MOZ_ASSERT(!data->mSocketTransport);
   2650    rv = conn->TakeTransport(getter_AddRefs(data->mSocketTransport),
   2651                             getter_AddRefs(data->mSocketIn),
   2652                             getter_AddRefs(data->mSocketOut));
   2653 
   2654    if (NS_FAILED(rv)) {
   2655      LOG(("  conn->TakeTransport failed with %" PRIx32,
   2656           static_cast<uint32_t>(rv)));
   2657    }
   2658  }
   2659 
   2660  RefPtr<nsCompleteUpgradeData> upgradeData(data);
   2661 
   2662  nsCOMPtr<nsIAsyncInputStream> socketIn;
   2663  nsCOMPtr<nsIAsyncOutputStream> socketOut;
   2664 
   2665  // If this is for JS, the input and output sockets need to be piped over the
   2666  // socket thread. Otherwise, the JS may attempt to read and/or write the
   2667  // sockets on the main thread, which could cause network I/O on the main
   2668  // thread. This is particularly bad in the case of TLS connections, because
   2669  // PSM and NSS rely on those connections only being used on the socket
   2670  // thread.
   2671  if (data->mJsWrapped) {
   2672    nsCOMPtr<nsIAsyncInputStream> pipeIn;
   2673    uint32_t segsize = 0;
   2674    uint32_t segcount = 0;
   2675    net_ResolveSegmentParams(segsize, segcount);
   2676    if (NS_SUCCEEDED(rv)) {
   2677      NS_NewPipe2(getter_AddRefs(pipeIn), getter_AddRefs(socketOut), true, true,
   2678                  segsize, segcount);
   2679      rv = NS_AsyncCopy(pipeIn, data->mSocketOut, gSocketTransportService,
   2680                        NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
   2681    }
   2682 
   2683    nsCOMPtr<nsIAsyncOutputStream> pipeOut;
   2684    if (NS_SUCCEEDED(rv)) {
   2685      NS_NewPipe2(getter_AddRefs(socketIn), getter_AddRefs(pipeOut), true, true,
   2686                  segsize, segcount);
   2687      rv = NS_AsyncCopy(data->mSocketIn, pipeOut, gSocketTransportService,
   2688                        NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
   2689    }
   2690  } else {
   2691    socketIn = upgradeData->mSocketIn;
   2692    socketOut = upgradeData->mSocketOut;
   2693  }
   2694 
   2695  auto transportAvailableFunc = [upgradeData{std::move(upgradeData)}, socketIn,
   2696                                 socketOut, aRv(rv)]() {
   2697    // Handle any potential previous errors first
   2698    // and call OnUpgradeFailed if necessary.
   2699    nsresult rv = aRv;
   2700 
   2701    if (NS_FAILED(rv)) {
   2702      rv = upgradeData->mUpgradeListener->OnUpgradeFailed(rv);
   2703      if (NS_FAILED(rv)) {
   2704        LOG(
   2705            ("nsHttpConnectionMgr::OnMsgCompleteUpgrade OnUpgradeFailed failed."
   2706             " listener=%p\n",
   2707             upgradeData->mUpgradeListener.get()));
   2708      }
   2709      return;
   2710    }
   2711 
   2712    rv = upgradeData->mUpgradeListener->OnTransportAvailable(
   2713        upgradeData->mSocketTransport, socketIn, socketOut);
   2714    if (NS_FAILED(rv)) {
   2715      LOG(
   2716          ("nsHttpConnectionMgr::OnMsgCompleteUpgrade OnTransportAvailable "
   2717           "failed. listener=%p\n",
   2718           upgradeData->mUpgradeListener.get()));
   2719    }
   2720  };
   2721 
   2722  if (data->mJsWrapped) {
   2723    LOG(
   2724        ("nsHttpConnectionMgr::OnMsgCompleteUpgrade "
   2725         "conn=%p listener=%p wrapped=%d pass to main thread\n",
   2726         conn.get(), data->mUpgradeListener.get(), data->mJsWrapped));
   2727    NS_DispatchToMainThread(
   2728        NS_NewRunnableFunction("net::nsHttpConnectionMgr::OnMsgCompleteUpgrade",
   2729                               transportAvailableFunc));
   2730  } else {
   2731    transportAvailableFunc();
   2732  }
   2733 }
   2734 
   2735 void nsHttpConnectionMgr::OnMsgUpdateParam(int32_t inParam, ARefBase*) {
   2736  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2737  uint32_t param = static_cast<uint32_t>(inParam);
   2738  uint16_t name = ((param) & 0xFFFF0000) >> 16;
   2739  uint16_t value = param & 0x0000FFFF;
   2740 
   2741  switch (name) {
   2742    case MAX_CONNECTIONS:
   2743      mMaxConns = value;
   2744      break;
   2745    case MAX_URGENT_START_Q:
   2746      mMaxUrgentExcessiveConns = value;
   2747      break;
   2748    case MAX_PERSISTENT_CONNECTIONS_PER_HOST:
   2749      mMaxPersistConnsPerHost = value;
   2750      break;
   2751    case MAX_PERSISTENT_CONNECTIONS_PER_PROXY:
   2752      mMaxPersistConnsPerProxy = value;
   2753      break;
   2754    case MAX_REQUEST_DELAY:
   2755      mMaxRequestDelay = value;
   2756      break;
   2757    case THROTTLING_ENABLED:
   2758      SetThrottlingEnabled(!!value);
   2759      break;
   2760    case THROTTLING_SUSPEND_FOR:
   2761      mThrottleSuspendFor = value;
   2762      break;
   2763    case THROTTLING_RESUME_FOR:
   2764      mThrottleResumeFor = value;
   2765      break;
   2766    case THROTTLING_HOLD_TIME:
   2767      mThrottleHoldTime = value;
   2768      break;
   2769    case THROTTLING_MAX_TIME:
   2770      mThrottleMaxTime = TimeDuration::FromMilliseconds(value);
   2771      break;
   2772    case PROXY_BE_CONSERVATIVE:
   2773      mBeConservativeForProxy = !!value;
   2774      break;
   2775    default:
   2776      MOZ_ASSERT_UNREACHABLE("unexpected parameter name");
   2777  }
   2778 }
   2779 
   2780 // Read Timeout Tick handlers
   2781 
   2782 void nsHttpConnectionMgr::ActivateTimeoutTick() {
   2783  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2784  LOG(
   2785      ("nsHttpConnectionMgr::ActivateTimeoutTick() "
   2786       "this=%p mTimeoutTick=%p\n",
   2787       this, mTimeoutTick.get()));
   2788 
   2789  // The timer tick should be enabled if it is not already pending.
   2790  // Upon running the tick will rearm itself if there are active
   2791  // connections available.
   2792 
   2793  if (mTimeoutTick && mTimeoutTickArmed) {
   2794    // make sure we get one iteration on a quick tick
   2795    if (mTimeoutTickNext > 1) {
   2796      mTimeoutTickNext = 1;
   2797      mTimeoutTick->SetDelay(1000);
   2798    }
   2799    return;
   2800  }
   2801 
   2802  if (!mTimeoutTick) {
   2803    mTimeoutTick = NS_NewTimer();
   2804    if (!mTimeoutTick) {
   2805      NS_WARNING("failed to create timer for http timeout management");
   2806      return;
   2807    }
   2808    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   2809    if (!mSocketThreadTarget) {
   2810      NS_WARNING("cannot activate timout if not initialized or shutdown");
   2811      return;
   2812    }
   2813    mTimeoutTick->SetTarget(mSocketThreadTarget);
   2814  }
   2815 
   2816  if (mIsShuttingDown) {  // Atomic
   2817    // don't set a timer to generate an event if we're shutting down
   2818    // (and mSocketThreadTarget might be null or garbage if we're shutting down)
   2819    return;
   2820  }
   2821  MOZ_ASSERT(!mTimeoutTickArmed, "timer tick armed");
   2822  mTimeoutTickArmed = true;
   2823  mTimeoutTick->Init(this, 1000, nsITimer::TYPE_REPEATING_SLACK);
   2824 }
   2825 
   2826 class UINT64Wrapper : public ARefBase {
   2827 public:
   2828  explicit UINT64Wrapper(uint64_t aUint64) : mUint64(aUint64) {}
   2829  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UINT64Wrapper, override)
   2830 
   2831  uint64_t GetValue() { return mUint64; }
   2832 
   2833 private:
   2834  uint64_t mUint64;
   2835  virtual ~UINT64Wrapper() = default;
   2836 };
   2837 
   2838 nsresult nsHttpConnectionMgr::UpdateCurrentBrowserId(uint64_t aId) {
   2839  RefPtr<UINT64Wrapper> idWrapper = new UINT64Wrapper(aId);
   2840  return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateCurrentBrowserId, 0,
   2841                   idWrapper);
   2842 }
   2843 
   2844 void nsHttpConnectionMgr::SetThrottlingEnabled(bool aEnable) {
   2845  LOG(("nsHttpConnectionMgr::SetThrottlingEnabled enable=%d", aEnable));
   2846  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2847 
   2848  mThrottleEnabled = aEnable;
   2849 
   2850  if (mThrottleEnabled) {
   2851    EnsureThrottleTickerIfNeeded();
   2852  } else {
   2853    DestroyThrottleTicker();
   2854    ResumeReadOf(mActiveTransactions[false]);
   2855    ResumeReadOf(mActiveTransactions[true]);
   2856  }
   2857 }
   2858 
   2859 bool nsHttpConnectionMgr::InThrottlingTimeWindow() {
   2860  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2861 
   2862  if (mThrottlingWindowEndsAt.IsNull()) {
   2863    return true;
   2864  }
   2865  return TimeStamp::NowLoRes() <= mThrottlingWindowEndsAt;
   2866 }
   2867 
   2868 void nsHttpConnectionMgr::TouchThrottlingTimeWindow(bool aEnsureTicker) {
   2869  LOG(("nsHttpConnectionMgr::TouchThrottlingTimeWindow"));
   2870 
   2871  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2872 
   2873  mThrottlingWindowEndsAt = TimeStamp::NowLoRes() + mThrottleMaxTime;
   2874 
   2875  if (!mThrottleTicker && MOZ_LIKELY(aEnsureTicker) &&
   2876      MOZ_LIKELY(mThrottleEnabled)) {
   2877    EnsureThrottleTickerIfNeeded();
   2878  }
   2879 }
   2880 
   2881 void nsHttpConnectionMgr::LogActiveTransactions(char operation) {
   2882  if (!LOG_ENABLED()) {
   2883    return;
   2884  }
   2885 
   2886  nsTArray<RefPtr<nsHttpTransaction>>* trs = nullptr;
   2887  uint32_t au, at, bu = 0, bt = 0;
   2888 
   2889  trs = mActiveTransactions[false].Get(mCurrentBrowserId);
   2890  au = trs ? trs->Length() : 0;
   2891  trs = mActiveTransactions[true].Get(mCurrentBrowserId);
   2892  at = trs ? trs->Length() : 0;
   2893 
   2894  for (const auto& data : mActiveTransactions[false].Values()) {
   2895    bu += data->Length();
   2896  }
   2897  bu -= au;
   2898  for (const auto& data : mActiveTransactions[true].Values()) {
   2899    bt += data->Length();
   2900  }
   2901  bt -= at;
   2902 
   2903  // Shows counts of:
   2904  // - unthrottled transaction for the active tab
   2905  // - throttled transaction for the active tab
   2906  // - unthrottled transaction for background tabs
   2907  // - throttled transaction for background tabs
   2908  LOG(("Active transactions %c[%u,%u,%u,%u]", operation, au, at, bu, bt));
   2909 }
   2910 
   2911 void nsHttpConnectionMgr::AddActiveTransaction(nsHttpTransaction* aTrans) {
   2912  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2913 
   2914  uint64_t tabId = aTrans->BrowserId();
   2915  bool throttled = aTrans->EligibleForThrottling();
   2916 
   2917  nsTArray<RefPtr<nsHttpTransaction>>* transactions =
   2918      mActiveTransactions[throttled].GetOrInsertNew(tabId);
   2919 
   2920  MOZ_ASSERT(!transactions->Contains(aTrans));
   2921 
   2922  transactions->AppendElement(aTrans);
   2923 
   2924  LOG(("nsHttpConnectionMgr::AddActiveTransaction    t=%p tabid=%" PRIx64
   2925       "(%d) thr=%d",
   2926       aTrans, tabId, tabId == mCurrentBrowserId, throttled));
   2927  LogActiveTransactions('+');
   2928 
   2929  if (tabId == mCurrentBrowserId) {
   2930    mActiveTabTransactionsExist = true;
   2931    if (!throttled) {
   2932      mActiveTabUnthrottledTransactionsExist = true;
   2933    }
   2934  }
   2935 
   2936  // Shift the throttling window to the future (actually, makes sure
   2937  // that throttling will engage when there is anything to throttle.)
   2938  // The |false| argument means we don't need this call to ensure
   2939  // the ticker, since we do it just below.  Calling
   2940  // EnsureThrottleTickerIfNeeded directly does a bit more than call
   2941  // from inside of TouchThrottlingTimeWindow.
   2942  TouchThrottlingTimeWindow(false);
   2943 
   2944  if (!mThrottleEnabled) {
   2945    return;
   2946  }
   2947 
   2948  EnsureThrottleTickerIfNeeded();
   2949 }
   2950 
   2951 void nsHttpConnectionMgr::RemoveActiveTransaction(
   2952    nsHttpTransaction* aTrans, Maybe<bool> const& aOverride) {
   2953  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2954 
   2955  uint64_t tabId = aTrans->BrowserId();
   2956  bool forActiveTab = tabId == mCurrentBrowserId;
   2957  bool throttled = aOverride.valueOr(aTrans->EligibleForThrottling());
   2958 
   2959  nsTArray<RefPtr<nsHttpTransaction>>* transactions =
   2960      mActiveTransactions[throttled].Get(tabId);
   2961 
   2962  if (!transactions || !transactions->RemoveElement(aTrans)) {
   2963    // Was not tracked as active, probably just ignore.
   2964    return;
   2965  }
   2966 
   2967  LOG(("nsHttpConnectionMgr::RemoveActiveTransaction t=%p tabid=%" PRIx64
   2968       "(%d) thr=%d",
   2969       aTrans, tabId, forActiveTab, throttled));
   2970 
   2971  if (!transactions->IsEmpty()) {
   2972    // There are still transactions of the type, hence nothing in the throttling
   2973    // conditions has changed and we don't need to update "Exists" caches nor we
   2974    // need to wake any now throttled transactions.
   2975    LogActiveTransactions('-');
   2976    return;
   2977  }
   2978 
   2979  // To optimize the following logic, always remove the entry when the array is
   2980  // empty.
   2981  mActiveTransactions[throttled].Remove(tabId);
   2982  LogActiveTransactions('-');
   2983 
   2984  if (forActiveTab) {
   2985    // Update caches of the active tab transaction existence, since it's now
   2986    // affected
   2987    if (!throttled) {
   2988      mActiveTabUnthrottledTransactionsExist = false;
   2989    }
   2990    if (mActiveTabTransactionsExist) {
   2991      mActiveTabTransactionsExist =
   2992          mActiveTransactions[!throttled].Contains(tabId);
   2993    }
   2994  }
   2995 
   2996  if (!mThrottleEnabled) {
   2997    return;
   2998  }
   2999 
   3000  bool unthrottledExist = !mActiveTransactions[false].IsEmpty();
   3001  bool throttledExist = !mActiveTransactions[true].IsEmpty();
   3002 
   3003  if (!unthrottledExist && !throttledExist) {
   3004    // Nothing active globally, just get rid of the timer completely and we are
   3005    // done.
   3006    MOZ_ASSERT(!mActiveTabUnthrottledTransactionsExist);
   3007    MOZ_ASSERT(!mActiveTabTransactionsExist);
   3008 
   3009    DestroyThrottleTicker();
   3010    return;
   3011  }
   3012 
   3013  if (!mThrottlingInhibitsReading) {
   3014    // There is then nothing to wake up.  Affected transactions will not be
   3015    // put to sleep automatically on next tick.
   3016    LOG(("  reading not currently inhibited"));
   3017    return;
   3018  }
   3019 
   3020  if (mActiveTabUnthrottledTransactionsExist) {
   3021    // There are still unthrottled transactions for the active tab, hence the
   3022    // state is unaffected and we don't need to do anything (nothing to wake).
   3023    LOG(("  there are unthrottled for the active tab"));
   3024    return;
   3025  }
   3026 
   3027  if (mActiveTabTransactionsExist) {
   3028    // There are only trottled transactions for the active tab.
   3029    // If the last transaction we just removed was a non-throttled for the
   3030    // active tab we can wake the throttled transactions for the active tab.
   3031    if (forActiveTab && !throttled) {
   3032      LOG(("  resuming throttled for active tab"));
   3033      ResumeReadOf(mActiveTransactions[true].Get(mCurrentBrowserId));
   3034    }
   3035    return;
   3036  }
   3037 
   3038  if (!unthrottledExist) {
   3039    // There are no unthrottled transactions for any tab.  Resume all throttled,
   3040    // all are only for background tabs.
   3041    LOG(("  delay resuming throttled for background tabs"));
   3042    DelayedResumeBackgroundThrottledTransactions();
   3043    return;
   3044  }
   3045 
   3046  if (forActiveTab) {
   3047    // Removing the last transaction for the active tab frees up the unthrottled
   3048    // background tabs transactions.
   3049    LOG(("  delay resuming unthrottled for background tabs"));
   3050    DelayedResumeBackgroundThrottledTransactions();
   3051    return;
   3052  }
   3053 
   3054  LOG(("  not resuming anything"));
   3055 }
   3056 
   3057 void nsHttpConnectionMgr::UpdateActiveTransaction(nsHttpTransaction* aTrans) {
   3058  LOG(("nsHttpConnectionMgr::UpdateActiveTransaction ENTER t=%p", aTrans));
   3059 
   3060  // First remove then add.  In case of a download that is the only active
   3061  // transaction and has just been marked as download (goes unthrottled to
   3062  // throttled), adding first would cause it to be throttled for first few
   3063  // milliseconds - becuause it would appear as if there were both throttled
   3064  // and unthrottled transactions at the time.
   3065 
   3066  Maybe<bool> reversed;
   3067  reversed.emplace(!aTrans->EligibleForThrottling());
   3068  RemoveActiveTransaction(aTrans, reversed);
   3069 
   3070  AddActiveTransaction(aTrans);
   3071 
   3072  LOG(("nsHttpConnectionMgr::UpdateActiveTransaction EXIT t=%p", aTrans));
   3073 }
   3074 
   3075 bool nsHttpConnectionMgr::ShouldThrottle(nsHttpTransaction* aTrans) {
   3076  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3077 
   3078  LOG(("nsHttpConnectionMgr::ShouldThrottle trans=%p", aTrans));
   3079 
   3080  if (!mThrottlingInhibitsReading || !mThrottleEnabled) {
   3081    return false;
   3082  }
   3083 
   3084  uint64_t tabId = aTrans->BrowserId();
   3085  bool forActiveTab = tabId == mCurrentBrowserId;
   3086  bool throttled = aTrans->EligibleForThrottling();
   3087 
   3088  bool stop = [=]() {
   3089    if (mActiveTabTransactionsExist) {
   3090      if (!tabId) {
   3091        // Chrome initiated and unidentified transactions just respect
   3092        // their throttle flag, when something for the active tab is happening.
   3093        // This also includes downloads.
   3094        LOG(("  active tab loads, trans is tab-less, throttled=%d", throttled));
   3095        return throttled;
   3096      }
   3097      if (!forActiveTab) {
   3098        // This is a background tab request, we want them to always throttle
   3099        // when there are transactions running for the ative tab.
   3100        LOG(("  active tab loads, trans not of the active tab"));
   3101        return true;
   3102      }
   3103 
   3104      if (mActiveTabUnthrottledTransactionsExist) {
   3105        // Unthrottled transactions for the active tab take precedence
   3106        LOG(("  active tab loads unthrottled, trans throttled=%d", throttled));
   3107        return throttled;
   3108      }
   3109 
   3110      LOG(("  trans for active tab, don't throttle"));
   3111      return false;
   3112    }
   3113 
   3114    MOZ_ASSERT(!forActiveTab);
   3115 
   3116    if (!mActiveTransactions[false].IsEmpty()) {
   3117      // This means there are unthrottled active transactions for background
   3118      // tabs. If we are here, there can't be any transactions for the active
   3119      // tab. (If there is no transaction for a tab id, there is no entry for it
   3120      // in the hashtable.)
   3121      LOG(("  backround tab(s) load unthrottled, trans throttled=%d",
   3122           throttled));
   3123      return throttled;
   3124    }
   3125 
   3126    // There are only unthrottled transactions for background tabs: don't
   3127    // throttle.
   3128    LOG(("  backround tab(s) load throttled, don't throttle"));
   3129    return false;
   3130  }();
   3131 
   3132  if (forActiveTab && !stop) {
   3133    // This is an active-tab transaction and is allowed to read.  Hence,
   3134    // prolong the throttle time window to make sure all 'lower-decks'
   3135    // transactions will actually throttle.
   3136    TouchThrottlingTimeWindow();
   3137    return false;
   3138  }
   3139 
   3140  // Only stop reading when in the configured throttle max-time (aka time
   3141  // window). This window is prolonged (restarted) by a call to
   3142  // TouchThrottlingTimeWindow called on new transaction activation or on
   3143  // receive of response bytes of an active tab transaction.
   3144  bool inWindow = InThrottlingTimeWindow();
   3145 
   3146  LOG(("  stop=%d, in-window=%d, delayed-bck-timer=%d", stop, inWindow,
   3147       !!mDelayedResumeReadTimer));
   3148 
   3149  if (!forActiveTab) {
   3150    // If the delayed background resume timer exists, background transactions
   3151    // are scheduled to be woken after a delay, hence leave them throttled.
   3152    inWindow = inWindow || mDelayedResumeReadTimer;
   3153  }
   3154 
   3155  return stop && inWindow;
   3156 }
   3157 
   3158 bool nsHttpConnectionMgr::IsConnEntryUnderPressure(
   3159    nsHttpConnectionInfo* connInfo) {
   3160  ConnectionEntry* ent = mCT.GetWeak(connInfo->HashKey());
   3161  if (!ent) {
   3162    // No entry, no pressure.
   3163    return false;
   3164  }
   3165 
   3166  return ent->PendingQueueLengthForWindow(mCurrentBrowserId) > 0;
   3167 }
   3168 
   3169 bool nsHttpConnectionMgr::IsThrottleTickerNeeded() {
   3170  LOG(("nsHttpConnectionMgr::IsThrottleTickerNeeded"));
   3171 
   3172  if (mActiveTabUnthrottledTransactionsExist &&
   3173      mActiveTransactions[false].Count() > 1) {
   3174    LOG(("  there are unthrottled transactions for both active and bck"));
   3175    return true;
   3176  }
   3177 
   3178  if (mActiveTabTransactionsExist && mActiveTransactions[true].Count() > 1) {
   3179    LOG(("  there are throttled transactions for both active and bck"));
   3180    return true;
   3181  }
   3182 
   3183  if (!mActiveTransactions[true].IsEmpty() &&
   3184      !mActiveTransactions[false].IsEmpty()) {
   3185    LOG(("  there are both throttled and unthrottled transactions"));
   3186    return true;
   3187  }
   3188 
   3189  LOG(("  nothing to throttle"));
   3190  return false;
   3191 }
   3192 
   3193 void nsHttpConnectionMgr::EnsureThrottleTickerIfNeeded() {
   3194  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3195 
   3196  LOG(("nsHttpConnectionMgr::EnsureThrottleTickerIfNeeded"));
   3197  if (!IsThrottleTickerNeeded()) {
   3198    return;
   3199  }
   3200 
   3201  // There is a new demand to throttle, hence unschedule delayed resume
   3202  // of background throttled transastions.
   3203  CancelDelayedResumeBackgroundThrottledTransactions();
   3204 
   3205  if (mThrottleTicker) {
   3206    return;
   3207  }
   3208 
   3209  mThrottleTicker = NS_NewTimer();
   3210  if (mThrottleTicker) {
   3211    MOZ_ASSERT(!mThrottlingInhibitsReading);
   3212 
   3213    mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
   3214    mThrottlingInhibitsReading = true;
   3215  }
   3216 
   3217  LogActiveTransactions('^');
   3218 }
   3219 
   3220 // Can be called with or without the monitor held
   3221 void nsHttpConnectionMgr::DestroyThrottleTicker() {
   3222  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3223 
   3224  // Nothing to throttle, hence no need for this timer anymore.
   3225  CancelDelayedResumeBackgroundThrottledTransactions();
   3226 
   3227  MOZ_ASSERT(!mThrottleEnabled || !IsThrottleTickerNeeded());
   3228 
   3229  if (!mThrottleTicker) {
   3230    return;
   3231  }
   3232 
   3233  LOG(("nsHttpConnectionMgr::DestroyThrottleTicker"));
   3234  mThrottleTicker->Cancel();
   3235  mThrottleTicker = nullptr;
   3236 
   3237  mThrottlingInhibitsReading = false;
   3238 
   3239  LogActiveTransactions('v');
   3240 }
   3241 
   3242 void nsHttpConnectionMgr::ThrottlerTick() {
   3243  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3244 
   3245  mThrottlingInhibitsReading = !mThrottlingInhibitsReading;
   3246 
   3247  LOG(("nsHttpConnectionMgr::ThrottlerTick inhibit=%d",
   3248       mThrottlingInhibitsReading));
   3249 
   3250  // If there are only background transactions to be woken after a delay, keep
   3251  // the ticker so that we woke them only for the resume-for interval and then
   3252  // throttle them again until the background-resume delay passes.
   3253  if (!mThrottlingInhibitsReading && !mDelayedResumeReadTimer &&
   3254      (!IsThrottleTickerNeeded() || !InThrottlingTimeWindow())) {
   3255    LOG(("  last tick"));
   3256    mThrottleTicker = nullptr;
   3257  }
   3258 
   3259  if (mThrottlingInhibitsReading) {
   3260    if (mThrottleTicker) {
   3261      mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
   3262    }
   3263  } else {
   3264    if (mThrottleTicker) {
   3265      mThrottleTicker->Init(this, mThrottleResumeFor, nsITimer::TYPE_ONE_SHOT);
   3266    }
   3267 
   3268    ResumeReadOf(mActiveTransactions[false], true);
   3269    ResumeReadOf(mActiveTransactions[true]);
   3270  }
   3271 }
   3272 
   3273 void nsHttpConnectionMgr::DelayedResumeBackgroundThrottledTransactions() {
   3274  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3275 
   3276  if (mDelayedResumeReadTimer) {
   3277    return;
   3278  }
   3279 
   3280  LOG(("nsHttpConnectionMgr::DelayedResumeBackgroundThrottledTransactions"));
   3281  NS_NewTimerWithObserver(getter_AddRefs(mDelayedResumeReadTimer), this,
   3282                          mThrottleHoldTime, nsITimer::TYPE_ONE_SHOT);
   3283 }
   3284 
   3285 void nsHttpConnectionMgr::CancelDelayedResumeBackgroundThrottledTransactions() {
   3286  if (!mDelayedResumeReadTimer) {
   3287    return;
   3288  }
   3289 
   3290  LOG(
   3291      ("nsHttpConnectionMgr::"
   3292       "CancelDelayedResumeBackgroundThrottledTransactions"));
   3293  mDelayedResumeReadTimer->Cancel();
   3294  mDelayedResumeReadTimer = nullptr;
   3295 }
   3296 
   3297 void nsHttpConnectionMgr::ResumeBackgroundThrottledTransactions() {
   3298  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3299 
   3300  LOG(("nsHttpConnectionMgr::ResumeBackgroundThrottledTransactions"));
   3301  mDelayedResumeReadTimer = nullptr;
   3302 
   3303  if (!IsThrottleTickerNeeded()) {
   3304    DestroyThrottleTicker();
   3305  }
   3306 
   3307  if (!mActiveTransactions[false].IsEmpty()) {
   3308    ResumeReadOf(mActiveTransactions[false], true);
   3309  } else {
   3310    ResumeReadOf(mActiveTransactions[true], true);
   3311  }
   3312 }
   3313 
   3314 void nsHttpConnectionMgr::ResumeReadOf(
   3315    nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<nsHttpTransaction>>>&
   3316        hashtable,
   3317    bool excludeForActiveTab) {
   3318  for (const auto& entry : hashtable) {
   3319    if (excludeForActiveTab && entry.GetKey() == mCurrentBrowserId) {
   3320      // These have never been throttled (never stopped reading)
   3321      continue;
   3322    }
   3323    ResumeReadOf(entry.GetWeak());
   3324  }
   3325 }
   3326 
   3327 void nsHttpConnectionMgr::ResumeReadOf(
   3328    nsTArray<RefPtr<nsHttpTransaction>>* transactions) {
   3329  MOZ_ASSERT(transactions);
   3330 
   3331  for (const auto& trans : *transactions) {
   3332    trans->ResumeReading();
   3333  }
   3334 }
   3335 
   3336 void nsHttpConnectionMgr::NotifyConnectionOfBrowserIdChange(
   3337    uint64_t previousId) {
   3338  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3339 
   3340  nsTArray<RefPtr<nsHttpTransaction>>* transactions = nullptr;
   3341  nsTArray<RefPtr<nsAHttpConnection>> connections;
   3342 
   3343  auto addConnectionHelper =
   3344      [&connections](nsTArray<RefPtr<nsHttpTransaction>>* trans) {
   3345        if (!trans) {
   3346          return;
   3347        }
   3348 
   3349        for (const auto& t : *trans) {
   3350          RefPtr<nsAHttpConnection> conn = t->Connection();
   3351          if (conn && !connections.Contains(conn)) {
   3352            connections.AppendElement(conn);
   3353          }
   3354        }
   3355      };
   3356 
   3357  // Get unthrottled transactions with the previous and current window id.
   3358  transactions = mActiveTransactions[false].Get(previousId);
   3359  addConnectionHelper(transactions);
   3360  transactions = mActiveTransactions[false].Get(mCurrentBrowserId);
   3361  addConnectionHelper(transactions);
   3362 
   3363  // Get throttled transactions with the previous and current window id.
   3364  transactions = mActiveTransactions[true].Get(previousId);
   3365  addConnectionHelper(transactions);
   3366  transactions = mActiveTransactions[true].Get(mCurrentBrowserId);
   3367  addConnectionHelper(transactions);
   3368 
   3369  for (const auto& conn : connections) {
   3370    conn->CurrentBrowserIdChanged(mCurrentBrowserId);
   3371  }
   3372 }
   3373 
   3374 void nsHttpConnectionMgr::OnMsgUpdateCurrentBrowserId(int32_t aLoading,
   3375                                                      ARefBase* param) {
   3376  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3377 
   3378  uint64_t id = static_cast<UINT64Wrapper*>(param)->GetValue();
   3379 
   3380  if (mCurrentBrowserId == id) {
   3381    // duplicate notification
   3382    return;
   3383  }
   3384 
   3385  bool activeTabWasLoading = mActiveTabTransactionsExist;
   3386 
   3387  uint64_t previousId = mCurrentBrowserId;
   3388  mCurrentBrowserId = id;
   3389 
   3390  if (StaticPrefs::network_http_active_tab_priority()) {
   3391    NotifyConnectionOfBrowserIdChange(previousId);
   3392  }
   3393 
   3394  LOG(
   3395      ("nsHttpConnectionMgr::OnMsgUpdateCurrentBrowserId"
   3396       " id=%" PRIx64 "\n",
   3397       mCurrentBrowserId));
   3398 
   3399  nsTArray<RefPtr<nsHttpTransaction>>* transactions = nullptr;
   3400 
   3401  // Update the "Exists" caches and resume any transactions that now deserve it,
   3402  // changing the active tab changes the conditions for throttling.
   3403  transactions = mActiveTransactions[false].Get(mCurrentBrowserId);
   3404  mActiveTabUnthrottledTransactionsExist = !!transactions;
   3405 
   3406  if (!mActiveTabUnthrottledTransactionsExist) {
   3407    transactions = mActiveTransactions[true].Get(mCurrentBrowserId);
   3408  }
   3409  mActiveTabTransactionsExist = !!transactions;
   3410 
   3411  if (transactions) {
   3412    // This means there are some transactions for this newly activated tab,
   3413    // resume them but anything else.
   3414    LOG(("  resuming newly activated tab transactions"));
   3415    ResumeReadOf(transactions);
   3416    return;
   3417  }
   3418 
   3419  if (!activeTabWasLoading) {
   3420    // There were no transactions for the previously active tab, hence
   3421    // all remaning transactions, if there were, were all unthrottled,
   3422    // no need to wake them.
   3423    return;
   3424  }
   3425 
   3426  if (!mActiveTransactions[false].IsEmpty()) {
   3427    LOG(("  resuming unthrottled background transactions"));
   3428    ResumeReadOf(mActiveTransactions[false]);
   3429    return;
   3430  }
   3431 
   3432  if (!mActiveTransactions[true].IsEmpty()) {
   3433    LOG(("  resuming throttled background transactions"));
   3434    ResumeReadOf(mActiveTransactions[true]);
   3435    return;
   3436  }
   3437 
   3438  DestroyThrottleTicker();
   3439 }
   3440 
   3441 void nsHttpConnectionMgr::TimeoutTick() {
   3442  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3443  MOZ_ASSERT(mTimeoutTick, "no readtimeout tick");
   3444 
   3445  LOG(("nsHttpConnectionMgr::TimeoutTick active=%d\n", mNumActiveConns));
   3446  // The next tick will be between 1 second and 1 hr
   3447  // Set it to the max value here, and the TimeoutTick()s can
   3448  // reduce it to their local needs.
   3449  mTimeoutTickNext = 3600;  // 1hr
   3450 
   3451  for (const RefPtr<ConnectionEntry>& ent : mCT.Values()) {
   3452    uint32_t timeoutTickNext = ent->TimeoutTick();
   3453    mTimeoutTickNext = std::min(mTimeoutTickNext, timeoutTickNext);
   3454  }
   3455 
   3456  if (mTimeoutTick) {
   3457    mTimeoutTickNext = std::max(mTimeoutTickNext, 1U);
   3458    mTimeoutTick->SetDelay(mTimeoutTickNext * 1000);
   3459  }
   3460 }
   3461 
   3462 // GetOrCreateConnectionEntry finds a ent for a particular CI for use in
   3463 // dispatching a transaction according to these rules
   3464 // 1] use an ent that matches the ci that can be dispatched immediately
   3465 // 2] otherwise use an ent of wildcard(ci) than can be dispatched immediately
   3466 // 3] otherwise create an ent that matches ci and make new conn on it
   3467 
   3468 ConnectionEntry* nsHttpConnectionMgr::GetOrCreateConnectionEntry(
   3469    nsHttpConnectionInfo* specificCI, bool prohibitWildCard, bool aNoHttp2,
   3470    bool aNoHttp3, bool* aIsWildcard, bool* aAvailableForDispatchNow) {
   3471  if (aAvailableForDispatchNow) {
   3472    *aAvailableForDispatchNow = false;
   3473  }
   3474  *aIsWildcard = false;
   3475 
   3476  // step 1
   3477  LOG(("GetOrCreateConnectionEntry step 1"));
   3478  ConnectionEntry* specificEnt = mCT.GetWeak(specificCI->HashKey());
   3479  if (specificEnt && specificEnt->AvailableForDispatchNow()) {
   3480    if (aAvailableForDispatchNow) {
   3481      *aAvailableForDispatchNow = true;
   3482    }
   3483    return specificEnt;
   3484  }
   3485 
   3486  // step 1 repeated for an inverted anonymous flag; we return an entry
   3487  // only when it has an h2 established connection that is not authenticated
   3488  // with a client certificate.
   3489  RefPtr<nsHttpConnectionInfo> anonInvertedCI(specificCI->Clone());
   3490  anonInvertedCI->SetAnonymous(!specificCI->GetAnonymous());
   3491  ConnectionEntry* invertedEnt = mCT.GetWeak(anonInvertedCI->HashKey());
   3492  if (invertedEnt) {
   3493    HttpConnectionBase* h2orh3conn =
   3494        GetH2orH3ActiveConn(invertedEnt, aNoHttp2, aNoHttp3);
   3495    if (h2orh3conn && h2orh3conn->IsExperienced() &&
   3496        h2orh3conn->NoClientCertAuth()) {
   3497      MOZ_ASSERT(h2orh3conn->UsingSpdy() || h2orh3conn->UsingHttp3());
   3498      LOG(
   3499          ("GetOrCreateConnectionEntry is coalescing h2/3 an/onymous "
   3500           "connections, ent=%p",
   3501           invertedEnt));
   3502      if (aAvailableForDispatchNow) {
   3503        *aAvailableForDispatchNow = true;
   3504      }
   3505      return invertedEnt;
   3506    }
   3507  }
   3508 
   3509  if (!specificCI->UsingHttpsProxy()) {
   3510    prohibitWildCard = true;
   3511  }
   3512 
   3513  // step 2
   3514  LOG(("GetOrCreateConnectionEntry step 2 prohibitWildCard=%d, aNoHttp3=%d",
   3515       prohibitWildCard, aNoHttp3));
   3516  if (!prohibitWildCard) {
   3517    RefPtr<nsHttpConnectionInfo> wildCardProxyCI;
   3518    DebugOnly<nsresult> rv =
   3519        specificCI->CreateWildCard(getter_AddRefs(wildCardProxyCI));
   3520    MOZ_ASSERT(NS_SUCCEEDED(rv));
   3521    ConnectionEntry* wildCardEnt = mCT.GetWeak(wildCardProxyCI->HashKey());
   3522    if (wildCardEnt && wildCardEnt->AvailableForDispatchNow()) {
   3523      if (aAvailableForDispatchNow) {
   3524        *aAvailableForDispatchNow = true;
   3525      }
   3526      *aIsWildcard = true;
   3527      return wildCardEnt;
   3528    }
   3529  }
   3530 
   3531  // step 3
   3532  LOG(("GetOrCreateConnectionEntry step 3"));
   3533  if (!specificEnt) {
   3534    RefPtr<nsHttpConnectionInfo> clone(specificCI->Clone());
   3535    specificEnt = new ConnectionEntry(clone);
   3536    mCT.InsertOrUpdate(clone->HashKey(), RefPtr{specificEnt});
   3537  }
   3538  return specificEnt;
   3539 }
   3540 
   3541 void nsHttpConnectionMgr::DoSpeculativeConnection(
   3542    SpeculativeTransaction* aTrans, bool aFetchHTTPSRR) {
   3543  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3544  MOZ_ASSERT(aTrans);
   3545 
   3546  bool isWildcard = false;
   3547  ConnectionEntry* ent = GetOrCreateConnectionEntry(
   3548      aTrans->ConnectionInfo(), false, aTrans->Caps() & NS_HTTP_DISALLOW_SPDY,
   3549      aTrans->Caps() & NS_HTTP_DISALLOW_HTTP3, &isWildcard);
   3550  if (!aFetchHTTPSRR &&
   3551      nsHttpHandler::EchConfigEnabled(aTrans->ConnectionInfo()->IsHttp3())) {
   3552    // This happens when this is called from
   3553    // SpeculativeTransaction::OnHTTPSRRAvailable. We have to update this
   3554    // entry's echConfig so that the newly created connection can use the latest
   3555    // echConfig.
   3556    ent->MaybeUpdateEchConfig(aTrans->ConnectionInfo());
   3557  }
   3558  DoSpeculativeConnectionInternal(ent, aTrans, aFetchHTTPSRR);
   3559 }
   3560 
   3561 void nsHttpConnectionMgr::DoSpeculativeConnectionInternal(
   3562    ConnectionEntry* aEnt, SpeculativeTransaction* aTrans, bool aFetchHTTPSRR) {
   3563  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3564  MOZ_ASSERT(aTrans);
   3565  MOZ_ASSERT(aEnt);
   3566  if (!gHttpHandler->Active()) {
   3567    // Do nothing if we are shutting down.
   3568    return;
   3569  }
   3570 
   3571  ProxyDNSStrategy strategy = GetProxyDNSStrategyHelper(
   3572      aEnt->mConnInfo->ProxyType(), aEnt->mConnInfo->ProxyFlag());
   3573  // Speculative connections can be triggered by non-Necko consumers,
   3574  // so add an extra check to ensure HTTPS RR isn't fetched when a proxy is
   3575  // used.
   3576  if (aFetchHTTPSRR && strategy == ProxyDNSStrategy::ORIGIN &&
   3577      NS_SUCCEEDED(aTrans->FetchHTTPSRR())) {
   3578    // nsHttpConnectionMgr::DoSpeculativeConnection will be called again
   3579    // when HTTPS RR is available.
   3580    return;
   3581  }
   3582 
   3583  uint32_t parallelSpeculativeConnectLimit =
   3584      aTrans->ParallelSpeculativeConnectLimit()
   3585          ? *aTrans->ParallelSpeculativeConnectLimit()
   3586          : gHttpHandler->ParallelSpeculativeConnectLimit();
   3587  bool ignoreIdle = aTrans->IgnoreIdle() ? *aTrans->IgnoreIdle() : false;
   3588  bool allow1918 = aTrans->Allow1918() ? *aTrans->Allow1918() : false;
   3589 
   3590  bool keepAlive = aTrans->Caps() & NS_HTTP_ALLOW_KEEPALIVE;
   3591  if (mNumDnsAndConnectSockets < parallelSpeculativeConnectLimit &&
   3592      ((ignoreIdle &&
   3593        (aEnt->IdleConnectionsLength() < parallelSpeculativeConnectLimit)) ||
   3594       !aEnt->IdleConnectionsLength()) &&
   3595      !(keepAlive && aEnt->RestrictConnections()) &&
   3596      !AtActiveConnectionLimit(aEnt, aTrans->Caps())) {
   3597    nsresult rv = aEnt->CreateDnsAndConnectSocket(aTrans, aTrans->Caps(), true,
   3598                                                  false, allow1918, nullptr);
   3599    if (NS_FAILED(rv)) {
   3600      LOG(
   3601          ("DoSpeculativeConnectionInternal Transport socket creation "
   3602           "failure: %" PRIx32 "\n",
   3603           static_cast<uint32_t>(rv)));
   3604    }
   3605  } else {
   3606    LOG(
   3607        ("DoSpeculativeConnectionInternal Transport ci=%s "
   3608         "not created due to existing connection count:%d",
   3609         aEnt->mConnInfo->HashKey().get(), parallelSpeculativeConnectLimit));
   3610  }
   3611 }
   3612 
   3613 void nsHttpConnectionMgr::DoFallbackConnection(SpeculativeTransaction* aTrans,
   3614                                               bool aFetchHTTPSRR) {
   3615  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3616  MOZ_ASSERT(aTrans);
   3617 
   3618  LOG(("nsHttpConnectionMgr::DoFallbackConnection"));
   3619 
   3620  bool availableForDispatchNow = false;
   3621  bool aIsWildcard = false;
   3622  ConnectionEntry* ent = GetOrCreateConnectionEntry(
   3623      aTrans->ConnectionInfo(), false, aTrans->Caps() & NS_HTTP_DISALLOW_SPDY,
   3624      aTrans->Caps() & NS_HTTP_DISALLOW_HTTP3, &aIsWildcard,
   3625      &availableForDispatchNow);
   3626 
   3627  if (availableForDispatchNow) {
   3628    LOG(
   3629        ("nsHttpConnectionMgr::DoFallbackConnection fallback connection is "
   3630         "ready for dispatching ent=%p",
   3631         ent));
   3632    aTrans->InvokeCallback();
   3633    return;
   3634  }
   3635 
   3636  DoSpeculativeConnectionInternal(ent, aTrans, aFetchHTTPSRR);
   3637 }
   3638 
   3639 void nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, ARefBase* param) {
   3640  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3641 
   3642  SpeculativeConnectArgs* args = static_cast<SpeculativeConnectArgs*>(param);
   3643 
   3644  LOG(
   3645      ("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s, "
   3646       "mFetchHTTPSRR=%d]\n",
   3647       args->mTrans->ConnectionInfo()->HashKey().get(), args->mFetchHTTPSRR));
   3648  DoSpeculativeConnection(args->mTrans, args->mFetchHTTPSRR);
   3649 }
   3650 
   3651 bool nsHttpConnectionMgr::BeConservativeIfProxied(nsIProxyInfo* proxy) {
   3652  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3653  if (mBeConservativeForProxy) {
   3654    // The pref says to be conservative for proxies.
   3655    return true;
   3656  }
   3657 
   3658  if (!proxy) {
   3659    // There is no proxy, so be conservative by default.
   3660    return true;
   3661  }
   3662 
   3663  // Be conservative only if there is no proxy host set either.
   3664  // This logic was copied from nsSSLIOLayerAddToSocket.
   3665  nsAutoCString proxyHost;
   3666  proxy->GetHost(proxyHost);
   3667  return proxyHost.IsEmpty();
   3668 }
   3669 
   3670 // register a connection to receive CanJoinConnection() for particular
   3671 // origin keys
   3672 void nsHttpConnectionMgr::RegisterOriginCoalescingKey(HttpConnectionBase* conn,
   3673                                                      const nsACString& host,
   3674                                                      int32_t port) {
   3675  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3676  nsHttpConnectionInfo* ci = conn ? conn->ConnectionInfo() : nullptr;
   3677  if (!ci || !conn->CanDirectlyActivate()) {
   3678    return;
   3679  }
   3680 
   3681  nsAutoCString newKey;
   3682  nsHttpConnectionInfo::BuildOriginFrameHashKey(newKey, ci, host, port);
   3683  mCoalescingHash.GetOrInsertNew(newKey, 1)->AppendElement(
   3684      do_GetWeakReference(static_cast<nsISupportsWeakReference*>(conn)));
   3685 
   3686  LOG(
   3687      ("nsHttpConnectionMgr::RegisterOriginCoalescingKey "
   3688       "Established New Coalescing Key %s to %p %s\n",
   3689       newKey.get(), conn, ci->HashKey().get()));
   3690 }
   3691 
   3692 bool nsHttpConnectionMgr::GetConnectionData(nsTArray<HttpRetParams>* aArg) {
   3693  for (const RefPtr<ConnectionEntry>& ent : mCT.Values()) {
   3694    if (ent->mConnInfo->GetPrivate()) {
   3695      continue;
   3696    }
   3697    aArg->AppendElement(ent->GetConnectionData());
   3698  }
   3699 
   3700  return true;
   3701 }
   3702 
   3703 bool nsHttpConnectionMgr::GetHttp3ConnectionStatsData(
   3704    nsTArray<Http3ConnectionStatsParams>* aArg) {
   3705  for (const RefPtr<ConnectionEntry>& ent : mCT.Values()) {
   3706    if (ent->mConnInfo->GetPrivate()) {
   3707      continue;
   3708    }
   3709    aArg->AppendElement(ent->GetHttp3ConnectionStatsData());
   3710  }
   3711 
   3712  return true;
   3713 }
   3714 
   3715 void nsHttpConnectionMgr::ResetIPFamilyPreference(nsHttpConnectionInfo* ci) {
   3716  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3717  ConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
   3718  if (ent) {
   3719    ent->ResetIPFamilyPreference();
   3720  }
   3721 }
   3722 
   3723 void nsHttpConnectionMgr::ExcludeHttp2(const nsHttpConnectionInfo* ci) {
   3724  LOG(("nsHttpConnectionMgr::ExcludeHttp2 excluding ci %s",
   3725       ci->HashKey().BeginReading()));
   3726  ConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
   3727  if (!ent) {
   3728    LOG(("nsHttpConnectionMgr::ExcludeHttp2 no entry found?!"));
   3729    return;
   3730  }
   3731 
   3732  ent->DisallowHttp2();
   3733 }
   3734 
   3735 void nsHttpConnectionMgr::ExcludeHttp3(const nsHttpConnectionInfo* ci) {
   3736  LOG(("nsHttpConnectionMgr::ExcludeHttp3 exclude ci %s",
   3737       ci->HashKey().BeginReading()));
   3738  ConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
   3739  if (!ent) {
   3740    LOG(("nsHttpConnectionMgr::ExcludeHttp3 no entry found?!"));
   3741    return;
   3742  }
   3743 
   3744  ent->DontReuseHttp3Conn();
   3745 }
   3746 
   3747 void nsHttpConnectionMgr::MoveToWildCardConnEntry(
   3748    nsHttpConnectionInfo* specificCI, nsHttpConnectionInfo* wildCardCI,
   3749    HttpConnectionBase* proxyConn) {
   3750  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3751  MOZ_ASSERT(specificCI->UsingHttpsProxy());
   3752 
   3753  LOG(
   3754      ("nsHttpConnectionMgr::MakeConnEntryWildCard conn %p has requested to "
   3755       "change CI from %s to %s\n",
   3756       proxyConn, specificCI->HashKey().get(), wildCardCI->HashKey().get()));
   3757 
   3758  ConnectionEntry* ent = mCT.GetWeak(specificCI->HashKey());
   3759  LOG(
   3760      ("nsHttpConnectionMgr::MakeConnEntryWildCard conn %p using ent %p (spdy "
   3761       "%d, h3=%d)\n",
   3762       proxyConn, ent, ent ? ent->mUsingSpdy : 0,
   3763       ent ? ent->IsHttp3ProxyConnection() : 0));
   3764 
   3765  if (!ent || (!ent->mUsingSpdy && !ent->IsHttp3ProxyConnection())) {
   3766    return;
   3767  }
   3768 
   3769  bool isWildcard = false;
   3770  ConnectionEntry* wcEnt =
   3771      GetOrCreateConnectionEntry(wildCardCI, true, false, false, &isWildcard);
   3772  if (wcEnt == ent) {
   3773    // nothing to do!
   3774    LOG(("nothing to do "));
   3775    return;
   3776  }
   3777  if (ent->mUsingSpdy) {
   3778    wcEnt->mUsingSpdy = true;
   3779  } else {
   3780    MOZ_ASSERT(wcEnt->IsHttp3ProxyConnection());
   3781  }
   3782 
   3783  LOG(
   3784      ("nsHttpConnectionMgr::MakeConnEntryWildCard ent %p "
   3785       "idle=%zu active=%zu half=%zu pending=%zu\n",
   3786       ent, ent->IdleConnectionsLength(), ent->ActiveConnsLength(),
   3787       ent->DnsAndConnectSocketsLength(), ent->PendingQueueLength()));
   3788 
   3789  LOG(
   3790      ("nsHttpConnectionMgr::MakeConnEntryWildCard wc-ent %p "
   3791       "idle=%zu active=%zu half=%zu pending=%zu\n",
   3792       wcEnt, wcEnt->IdleConnectionsLength(), wcEnt->ActiveConnsLength(),
   3793       wcEnt->DnsAndConnectSocketsLength(), wcEnt->PendingQueueLength()));
   3794 
   3795  ent->MoveConnection(proxyConn, wcEnt);
   3796  // Ensure other wildcard connections are closed gracefully.
   3797  wcEnt->MakeAllDontReuseExcept(proxyConn);
   3798 }
   3799 
   3800 bool nsHttpConnectionMgr::RemoveTransFromConnEntry(nsHttpTransaction* aTrans,
   3801                                                   const nsACString& aHashKey) {
   3802  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3803 
   3804  LOG(("nsHttpConnectionMgr::RemoveTransFromConnEntry: trans=%p ci=%s", aTrans,
   3805       PromiseFlatCString(aHashKey).get()));
   3806 
   3807  if (aHashKey.IsEmpty()) {
   3808    return false;
   3809  }
   3810 
   3811  // Step 1: Get the transaction's connection entry.
   3812  ConnectionEntry* entry = mCT.GetWeak(aHashKey);
   3813  if (!entry) {
   3814    return false;
   3815  }
   3816 
   3817  // Step 2: Try to find the undispatched transaction.
   3818  return entry->RemoveTransFromPendingQ(aTrans);
   3819 }
   3820 
   3821 void nsHttpConnectionMgr::IncreaseNumDnsAndConnectSockets() {
   3822  mNumDnsAndConnectSockets++;
   3823 }
   3824 
   3825 void nsHttpConnectionMgr::DecreaseNumDnsAndConnectSockets() {
   3826  MOZ_ASSERT(mNumDnsAndConnectSockets);
   3827  if (mNumDnsAndConnectSockets) {  // just in case
   3828    mNumDnsAndConnectSockets--;
   3829  }
   3830 }
   3831 
   3832 already_AddRefed<PendingTransactionInfo>
   3833 nsHttpConnectionMgr::FindTransactionHelper(bool removeWhenFound,
   3834                                           ConnectionEntry* aEnt,
   3835                                           nsAHttpTransaction* aTrans) {
   3836  nsTArray<RefPtr<PendingTransactionInfo>>* pendingQ =
   3837      aEnt->GetTransactionPendingQHelper(aTrans);
   3838 
   3839  int32_t index =
   3840      pendingQ ? pendingQ->IndexOf(aTrans, 0, PendingComparator()) : -1;
   3841 
   3842  RefPtr<PendingTransactionInfo> info;
   3843  if (index != -1) {
   3844    info = (*pendingQ)[index];
   3845    if (removeWhenFound) {
   3846      pendingQ->RemoveElementAt(index);
   3847    }
   3848  }
   3849  return info.forget();
   3850 }
   3851 
   3852 already_AddRefed<ConnectionEntry> nsHttpConnectionMgr::FindConnectionEntry(
   3853    const nsHttpConnectionInfo* ci) {
   3854  return mCT.Get(ci->HashKey());
   3855 }
   3856 
   3857 nsHttpConnectionMgr* nsHttpConnectionMgr::AsHttpConnectionMgr() { return this; }
   3858 
   3859 HttpConnectionMgrParent* nsHttpConnectionMgr::AsHttpConnectionMgrParent() {
   3860  return nullptr;
   3861 }
   3862 
   3863 void nsHttpConnectionMgr::NewIdleConnectionAdded(uint32_t timeToLive) {
   3864  mNumIdleConns++;
   3865 
   3866  // If the added connection was first idle connection or has shortest
   3867  // time to live among the watched connections, pruning dead
   3868  // connections needs to be done when it can't be reused anymore.
   3869  if (!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp) {
   3870    PruneDeadConnectionsAfter(timeToLive);
   3871  }
   3872 }
   3873 
   3874 void nsHttpConnectionMgr::DecrementNumIdleConns() {
   3875  MOZ_ASSERT(mNumIdleConns);
   3876  mNumIdleConns--;
   3877  ConditionallyStopPruneDeadConnectionsTimer();
   3878 }
   3879 
   3880 // A structure used to marshall objects necessary for ServerCertificateHashaes
   3881 class nsStoreServerCertHashesData : public ARefBase {
   3882 public:
   3883  nsStoreServerCertHashesData(
   3884      nsHttpConnectionInfo* aConnInfo, bool aNoSpdy, bool aNoHttp3,
   3885      nsTArray<RefPtr<nsIWebTransportHash>>&& aServerCertHashes)
   3886      : mConnInfo(aConnInfo),
   3887        mNoSpdy(aNoSpdy),
   3888        mNoHttp3(aNoHttp3),
   3889        mServerCertHashes(std::move(aServerCertHashes)) {}
   3890 
   3891  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsStoreServerCertHashesData, override)
   3892 
   3893  RefPtr<nsHttpConnectionInfo> mConnInfo;
   3894  bool mNoSpdy;
   3895  bool mNoHttp3;
   3896  nsTArray<RefPtr<nsIWebTransportHash>> mServerCertHashes;
   3897 
   3898 private:
   3899  virtual ~nsStoreServerCertHashesData() = default;
   3900 };
   3901 
   3902 // The connection manager needs to know the hashes used for a WebTransport
   3903 // connection authenticated with serverCertHashes
   3904 nsresult nsHttpConnectionMgr::StoreServerCertHashes(
   3905    nsHttpConnectionInfo* aConnInfo, bool aNoSpdy, bool aNoHttp3,
   3906    nsTArray<RefPtr<nsIWebTransportHash>>&& aServerCertHashes) {
   3907  RefPtr<nsHttpConnectionInfo> ci = aConnInfo->Clone();
   3908  RefPtr<nsStoreServerCertHashesData> data = new nsStoreServerCertHashesData(
   3909      ci, aNoSpdy, aNoHttp3, std::move(aServerCertHashes));
   3910  return PostEvent(&nsHttpConnectionMgr::OnMsgStoreServerCertHashes, 0, data);
   3911 }
   3912 
   3913 void nsHttpConnectionMgr::OnMsgStoreServerCertHashes(int32_t, ARefBase* param) {
   3914  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   3915 
   3916  nsStoreServerCertHashesData* data =
   3917      static_cast<nsStoreServerCertHashesData*>(param);
   3918 
   3919  bool isWildcard;
   3920  ConnectionEntry* connEnt = GetOrCreateConnectionEntry(
   3921      data->mConnInfo, true, data->mNoSpdy, data->mNoHttp3, &isWildcard);
   3922  MOZ_ASSERT(!isWildcard, "No webtransport with wildcard");
   3923  connEnt->SetServerCertHashes(std::move(data->mServerCertHashes));
   3924 }
   3925 
   3926 const nsTArray<RefPtr<nsIWebTransportHash>>*
   3927 nsHttpConnectionMgr::GetServerCertHashes(nsHttpConnectionInfo* aConnInfo) {
   3928  ConnectionEntry* connEnt = mCT.GetWeak(aConnInfo->HashKey());
   3929  if (!connEnt) {
   3930    MOZ_ASSERT(0);
   3931    return nullptr;
   3932  }
   3933  return &connEnt->GetServerCertHashes();
   3934 }
   3935 
   3936 void nsHttpConnectionMgr::CheckTransInPendingQueue(nsHttpTransaction* aTrans) {
   3937 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   3938  // We only do this check on socket thread. When this function is called on
   3939  // main thread, the transaction is newly created, so we can skip this check.
   3940  if (!OnSocketThread()) {
   3941    return;
   3942  }
   3943 
   3944  nsAutoCString hashKey;
   3945  aTrans->GetHashKeyOfConnectionEntry(hashKey);
   3946  if (hashKey.IsEmpty()) {
   3947    return;
   3948  }
   3949 
   3950  bool foundInPendingQ = RemoveTransFromConnEntry(aTrans, hashKey);
   3951  MOZ_DIAGNOSTIC_ASSERT(!foundInPendingQ);
   3952 #endif
   3953 }
   3954 
   3955 bool nsHttpConnectionMgr::AllowToRetryDifferentIPFamilyForHttp3(
   3956    nsHttpConnectionInfo* ci, nsresult aError) {
   3957  ConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
   3958  if (!ent) {
   3959    return false;
   3960  }
   3961 
   3962  return ent->AllowToRetryDifferentIPFamilyForHttp3(aError);
   3963 }
   3964 
   3965 void nsHttpConnectionMgr::SetRetryDifferentIPFamilyForHttp3(
   3966    nsHttpConnectionInfo* ci, uint16_t aIPFamily) {
   3967  ConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
   3968  if (!ent) {
   3969    return;
   3970  }
   3971 
   3972  ent->SetRetryDifferentIPFamilyForHttp3(aIPFamily);
   3973 }
   3974 
   3975 }  // namespace mozilla::net