Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/netwerk/protocol/http/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 132 kB image not shown  

 nsHttpConnectionMgr.cpp   Interaktion und
PortierbarkeitC

 
/* vim:set ts=4 sw=2 sts=2 et cin: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


// HttpLog.h should generally be included first
#include "HttpLog.h"

// Log on level :5, instead of default :4.
#undef LOG
#define LOG(args) LOG5(args)
#undef LOG_ENABLED
#define LOG_ENABLED() LOG5_ENABLED()

#include <algorithm>
#include <utility>

#include "ConnectionHandle.h"
#include "HttpConnectionUDP.h"
#include "NullHttpTransaction.h"
#include "SpeculativeTransaction.h"
#include "mozilla/Components.h"
#include "mozilla/PerfStats.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/Telemetry.h"
#include "mozilla/Unused.h"
#include "mozilla/glean/NetwerkMetrics.h"
#include "mozilla/net/DNS.h"
#include "mozilla/net/DashboardTypes.h"
#include "nsCOMPtr.h"
#include "nsHttpConnectionMgr.h"
#include "nsHttpHandler.h"
#include "nsIClassOfService.h"
#include "nsIDNSByTypeRecord.h"
#include "nsIDNSListener.h"
#include "nsIDNSRecord.h"
#include "nsIDNSService.h"
#include "nsIHttpChannelInternal.h"
#include "nsIPipe.h"
#include "nsIRequestContext.h"
#include "nsISocketTransport.h"
#include "nsISocketTransportService.h"
#include "nsITransport.h"
#include "nsIXPConnect.h"
#include "nsInterfaceRequestorAgg.h"
#include "nsNetCID.h"
#include "nsNetSegmentUtils.h"
#include "nsNetUtil.h"
#include "nsQueryObject.h"
#include "nsSocketTransportService2.h"
#include "nsStreamUtils.h"

using namespace mozilla;

namespace geckoprofiler::markers {

struct UrlMarker {
  static constexpr Span<const char> MarkerTypeName() {
    return MakeStringSpan("Url");
  }
  static void StreamJSONMarkerData(
      mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
      const mozilla::ProfilerString8View& aURL, const TimeDuration& aDuration,
      uint64_t aChannelId) {
    if (aURL.Length() != 0) {
      aWriter.StringProperty("url", aURL);
    }
    if (!aDuration.IsZero()) {
      aWriter.DoubleProperty("duration", aDuration.ToMilliseconds());
    }
    aWriter.IntProperty("channelId"static_cast<int64_t>(aChannelId));
  }
  static MarkerSchema MarkerTypeDisplay() {
    using MS = MarkerSchema;
    MS schema(MS::Location::MarkerChart, MS::Location::MarkerTable);
    schema.SetTableLabel("{marker.name} - {marker.data.url}");
    schema.AddKeyFormatSearchable("url", MS::Format::Url,
                                  MS::Searchable::Searchable);
    schema.AddKeyLabelFormat("duration""Duration", MS::Format::Duration);
    return schema;
  }
};

}  // namespace geckoprofiler::markers

namespace mozilla::net {

//-----------------------------------------------------------------------------

NS_IMPL_ISUPPORTS(nsHttpConnectionMgr, nsIObserver, nsINamed)

//-----------------------------------------------------------------------------

nsHttpConnectionMgr::nsHttpConnectionMgr() {
  LOG(("Creating nsHttpConnectionMgr @%p\n"this));
}

nsHttpConnectionMgr::~nsHttpConnectionMgr() {
  LOG(("Destroying nsHttpConnectionMgr @%p\n"this));
  MOZ_ASSERT(mCoalescingHash.Count() == 0);
  if (mTimeoutTick) mTimeoutTick->Cancel();
}

nsresult nsHttpConnectionMgr::EnsureSocketThreadTarget() {
  nsCOMPtr<nsIEventTarget> sts;
  nsCOMPtr<nsIIOService> ioService = components::IO::Service();
  if (ioService) {
    nsCOMPtr<nsISocketTransportService> realSTS =
        components::SocketTransport::Service();
    sts = do_QueryInterface(realSTS);
  }

  ReentrantMonitorAutoEnter mon(mReentrantMonitor);

  // do nothing if already initialized or if we've shut down
  if (mSocketThreadTarget || mIsShuttingDown) return NS_OK;

  mSocketThreadTarget = sts;

  return sts ? NS_OK : NS_ERROR_NOT_AVAILABLE;
}

nsresult nsHttpConnectionMgr::Init(
    uint16_t maxUrgentExcessiveConns, uint16_t maxConns,
    uint16_t maxPersistConnsPerHost, uint16_t maxPersistConnsPerProxy,
    uint16_t maxRequestDelay, bool throttleEnabled, uint32_t throttleSuspendFor,
    uint32_t throttleResumeFor, uint32_t throttleHoldTime,
    uint32_t throttleMaxTime, bool beConservativeForProxy) {
  LOG(("nsHttpConnectionMgr::Init\n"));

  {
    ReentrantMonitorAutoEnter mon(mReentrantMonitor);

    mMaxUrgentExcessiveConns = maxUrgentExcessiveConns;
    mMaxConns = maxConns;
    mMaxPersistConnsPerHost = maxPersistConnsPerHost;
    mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
    mMaxRequestDelay = maxRequestDelay;

    mThrottleEnabled = throttleEnabled;
    mThrottleSuspendFor = throttleSuspendFor;
    mThrottleResumeFor = throttleResumeFor;
    mThrottleHoldTime = throttleHoldTime;
    mThrottleMaxTime = TimeDuration::FromMilliseconds(throttleMaxTime);

    mBeConservativeForProxy = beConservativeForProxy;

    mIsShuttingDown = false;
  }

  return EnsureSocketThreadTarget();
}

class BoolWrapper : public ARefBase {
 public:
  BoolWrapper() = default;
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BoolWrapper, override)

 public:  // intentional!
  bool mBool{false};

 private:
  virtual ~BoolWrapper() = default;
};

nsresult nsHttpConnectionMgr::Shutdown() {
  LOG(("nsHttpConnectionMgr::Shutdown\n"));

  RefPtr<BoolWrapper> shutdownWrapper = new BoolWrapper();
  {
    ReentrantMonitorAutoEnter mon(mReentrantMonitor);

    // do nothing if already shutdown
    if (!mSocketThreadTarget) return NS_OK;

    nsresult rv =
        PostEvent(&nsHttpConnectionMgr::OnMsgShutdown, 0, shutdownWrapper);

    // release our reference to the STS to prevent further events
    // from being posted.  this is how we indicate that we are
    // shutting down.
    mIsShuttingDown = true;
    mSocketThreadTarget = nullptr;

    if (NS_FAILED(rv)) {
      NS_WARNING("unable to post SHUTDOWN message");
      return rv;
    }
  }

  // wait for shutdown event to complete
  SpinEventLoopUntil("nsHttpConnectionMgr::Shutdown"_ns,
                     [&, shutdownWrapper]() { return shutdownWrapper->mBool; });

  return NS_OK;
}

class ConnEvent : public Runnable {
 public:
  ConnEvent(nsHttpConnectionMgr* mgr, nsConnEventHandler handler,
            int32_t iparam, ARefBase* vparam)
      : Runnable("net::ConnEvent"),
        mMgr(mgr),
        mHandler(handler),
        mIParam(iparam),
        mVParam(vparam) {}

  NS_IMETHOD Run() override {
    (mMgr->*mHandler)(mIParam, mVParam);
    return NS_OK;
  }

 private:
  virtual ~ConnEvent() = default;

  RefPtr<nsHttpConnectionMgr> mMgr;
  nsConnEventHandler mHandler;
  int32_t mIParam;
  RefPtr<ARefBase> mVParam;
};

nsresult nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler,
                                        int32_t iparam, ARefBase* vparam) {
  Unused << EnsureSocketThreadTarget();

  nsCOMPtr<nsIEventTarget> target;
  {
    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    target = mSocketThreadTarget;
  }

  if (!target) {
    NS_WARNING("cannot post event if not initialized");
    return NS_ERROR_NOT_INITIALIZED;
  }

  nsCOMPtr<nsIRunnable> event = new ConnEvent(this, handler, iparam, vparam);
  return target->Dispatch(event, NS_DISPATCH_NORMAL);
}

void nsHttpConnectionMgr::PruneDeadConnectionsAfter(uint32_t timeInSeconds) {
  LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));

  if (!mTimer) mTimer = NS_NewTimer();

  // failure to create a timer is not a fatal error, but idle connections
  // will not be cleaned up until we try to use them.
  if (mTimer) {
    mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
    mTimer->Init(this, timeInSeconds * 1000, nsITimer::TYPE_ONE_SHOT);
  } else {
    NS_WARNING("failed to create: timer for pruning the dead connections!");
  }
}

void nsHttpConnectionMgr::ConditionallyStopPruneDeadConnectionsTimer() {
  // Leave the timer in place if there are connections that potentially
  // need management
  if (mNumIdleConns ||
      (mNumActiveConns && StaticPrefs::network_http_http2_enabled())) {
    return;
  }

  LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));

  // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
  mTimeOfNextWakeUp = UINT64_MAX;
  if (mTimer) {
    mTimer->Cancel();
    mTimer = nullptr;
  }
}

void nsHttpConnectionMgr::ConditionallyStopTimeoutTick() {
  LOG(
      ("nsHttpConnectionMgr::ConditionallyStopTimeoutTick "
       "armed=%d active=%d\n",
       mTimeoutTickArmed, mNumActiveConns));

  if (!mTimeoutTickArmed) return;

  if (mNumActiveConns) return;

  LOG(("nsHttpConnectionMgr::ConditionallyStopTimeoutTick stop==true\n"));

  mTimeoutTick->Cancel();
  mTimeoutTickArmed = false;
}

//-----------------------------------------------------------------------------
// nsHttpConnectionMgr::nsINamed
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsHttpConnectionMgr::GetName(nsACString& aName) {
  aName.AssignLiteral("nsHttpConnectionMgr");
  return NS_OK;
}

//-----------------------------------------------------------------------------
// nsHttpConnectionMgr::nsIObserver
//-----------------------------------------------------------------------------

NS_IMETHODIMP
nsHttpConnectionMgr::Observe(nsISupports* subject, const char* topic,
                             const char16_t* data) {
  LOG(("nsHttpConnectionMgr::Observe [topic=\"%s\"]\n", topic));

  if (0 == strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
    nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
    if (timer == mTimer) {
      Unused << PruneDeadConnections();
    } else if (timer == mTimeoutTick) {
      TimeoutTick();
    } else if (timer == mTrafficTimer) {
      Unused << PruneNoTraffic();
    } else if (timer == mThrottleTicker) {
      ThrottlerTick();
    } else if (timer == mDelayedResumeReadTimer) {
      ResumeBackgroundThrottledTransactions();
    } else {
      MOZ_ASSERT(false"unexpected timer-callback");
      LOG(("Unexpected timer object\n"));
      return NS_ERROR_UNEXPECTED;
    }
  }

  return NS_OK;
}

//-----------------------------------------------------------------------------

nsresult nsHttpConnectionMgr::AddTransaction(HttpTransactionShell* trans,
                                             int32_t priority) {
  LOG(("nsHttpConnectionMgr::AddTransaction [trans=%p %d]\n", trans, priority));
  // Make sure a transaction is not in a pending queue.
  CheckTransInPendingQueue(trans->AsHttpTransaction());
  return PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority,
                   trans->AsHttpTransaction());
}

class NewTransactionData : public ARefBase {
 public:
  NewTransactionData(nsHttpTransaction* trans, int32_t priority,
                     nsHttpTransaction* transWithStickyConn)
      : mTrans(trans),
        mPriority(priority),
        mTransWithStickyConn(transWithStickyConn) {}

  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NewTransactionData, override)

  RefPtr<nsHttpTransaction> mTrans;
  int32_t mPriority;
  RefPtr<nsHttpTransaction> mTransWithStickyConn;

 private:
  virtual ~NewTransactionData() = default;
};

nsresult nsHttpConnectionMgr::AddTransactionWithStickyConn(
    HttpTransactionShell* trans, int32_t priority,
    HttpTransactionShell* transWithStickyConn) {
  LOG(
      ("nsHttpConnectionMgr::AddTransactionWithStickyConn "
       "[trans=%p %d transWithStickyConn=%p]\n",
       trans, priority, transWithStickyConn));
  // Make sure a transaction is not in a pending queue.
  CheckTransInPendingQueue(trans->AsHttpTransaction());

  RefPtr<NewTransactionData> data =
      new NewTransactionData(trans->AsHttpTransaction(), priority,
                             transWithStickyConn->AsHttpTransaction());
  return PostEvent(&nsHttpConnectionMgr::OnMsgNewTransactionWithStickyConn, 0,
                   data);
}

nsresult nsHttpConnectionMgr::RescheduleTransaction(HttpTransactionShell* trans,
                                                    int32_t priority) {
  LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%p %d]\n", trans,
       priority));
  return PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority,
                   trans->AsHttpTransaction());
}

void nsHttpConnectionMgr::UpdateClassOfServiceOnTransaction(
    HttpTransactionShell* trans, const ClassOfService& classOfService) {
  LOG(
      ("nsHttpConnectionMgr::UpdateClassOfServiceOnTransaction [trans=%p "
       "classOfService flags=%" PRIu32 " inc=%d]\n",
       trans, static_cast<uint32_t>(classOfService.Flags()),
       classOfService.Incremental()));

  Unused << EnsureSocketThreadTarget();

  nsCOMPtr<nsIEventTarget> target;
  {
    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    target = mSocketThreadTarget;
  }

  if (!target) {
    NS_WARNING("cannot post event if not initialized");
    return;
  }

  RefPtr<nsHttpConnectionMgr> self(this);
  Unused << target->Dispatch(NS_NewRunnableFunction(
      "nsHttpConnectionMgr::CallUpdateClassOfServiceOnTransaction",
      [cos{classOfService}, self{std::move(self)}, trans = RefPtr{trans}]() {
        self->OnMsgUpdateClassOfServiceOnTransaction(
            cos, trans->AsHttpTransaction());
      }));
}

nsresult nsHttpConnectionMgr::CancelTransaction(HttpTransactionShell* trans,
                                                nsresult reason) {
  LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%p reason=%" PRIx32 "]\n",
       trans, static_cast<uint32_t>(reason)));
  return PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction,
                   static_cast<int32_t>(reason), trans->AsHttpTransaction());
}

nsresult nsHttpConnectionMgr::PruneDeadConnections() {
  return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
}

//
// Called after a timeout. Check for active connections that have had no
// traffic since they were "marked" and nuke them.
nsresult nsHttpConnectionMgr::PruneNoTraffic() {
  LOG(("nsHttpConnectionMgr::PruneNoTraffic\n"));
  mPruningNoTraffic = true;
  return PostEvent(&nsHttpConnectionMgr::OnMsgPruneNoTraffic);
}

nsresult nsHttpConnectionMgr::VerifyTraffic() {
  LOG(("nsHttpConnectionMgr::VerifyTraffic\n"));
  return PostEvent(&nsHttpConnectionMgr::OnMsgVerifyTraffic);
}

nsresult nsHttpConnectionMgr::DoShiftReloadConnectionCleanup() {
  return PostEvent(&nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup, 0,
                   nullptr);
}

nsresult nsHttpConnectionMgr::DoShiftReloadConnectionCleanupWithConnInfo(
    nsHttpConnectionInfo* aCI) {
  if (!aCI) {
    return NS_ERROR_INVALID_ARG;
  }

  RefPtr<nsHttpConnectionInfo> ci = aCI->Clone();
  return PostEvent(&nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup, 0,
                   ci);
}

nsresult nsHttpConnectionMgr::DoSingleConnectionCleanup(
    nsHttpConnectionInfo* aCI) {
  if (!aCI) {
    return NS_ERROR_INVALID_ARG;
  }

  RefPtr<nsHttpConnectionInfo> ci = aCI->Clone();
  return PostEvent(&nsHttpConnectionMgr::OnMsgDoSingleConnectionCleanup, 0, ci);
}

class SpeculativeConnectArgs : public ARefBase {
 public:
  SpeculativeConnectArgs() = default;
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SpeculativeConnectArgs, override)

 public:  // intentional!
  RefPtr<SpeculativeTransaction> mTrans;

  bool mFetchHTTPSRR{false};

 private:
  virtual ~SpeculativeConnectArgs() = default;
  NS_DECL_OWNINGTHREAD
};

nsresult nsHttpConnectionMgr::SpeculativeConnect(
    nsHttpConnectionInfo* ci, nsIInterfaceRequestor* callbacks, uint32_t caps,
    SpeculativeTransaction* aTransaction, bool aFetchHTTPSRR) {
  if (!IsNeckoChild() && NS_IsMainThread()) {
    // HACK: make sure PSM gets initialized on the main thread.
    net_EnsurePSMInit();
  }

  LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n",
       ci->HashKey().get()));

  nsCOMPtr<nsISpeculativeConnectionOverrider> overrider =
      do_GetInterface(callbacks);

  bool allow1918 = overrider ? overrider->GetAllow1918() : false;

  // Hosts that are Local IP Literals should not be speculatively
  // connected - Bug 853423.
  if ((!allow1918) && ci && ci->HostIsLocalIPLiteral()) {
    LOG(
        ("nsHttpConnectionMgr::SpeculativeConnect skipping RFC1918 "
         "address [%s]",
         ci->Origin()));
    return NS_OK;
  }

  nsAutoCString url(ci->EndToEndSSL() ? "https://"_ns : "http://"_ns);
  url += ci->GetOrigin();
  PROFILER_MARKER("SpeculativeConnect", NETWORK, {}, UrlMarker, url,
                  TimeDuration::Zero(), 0);

  RefPtr<SpeculativeConnectArgs> args = new SpeculativeConnectArgs();

  // Wrap up the callbacks and the target to ensure they're released on the
  // target thread properly.
  nsCOMPtr<nsIInterfaceRequestor> wrappedCallbacks;
  NS_NewInterfaceRequestorAggregation(callbacks, nullptr,
                                      getter_AddRefs(wrappedCallbacks));

  caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
  caps |= NS_HTTP_ERROR_SOFTLY;
  args->mTrans = aTransaction
                     ? aTransaction
                     : new SpeculativeTransaction(ci, wrappedCallbacks, caps);
  args->mFetchHTTPSRR = aFetchHTTPSRR;

  if (overrider) {
    args->mTrans->SetParallelSpeculativeConnectLimit(
        overrider->GetParallelSpeculativeConnectLimit());
    args->mTrans->SetIgnoreIdle(overrider->GetIgnoreIdle());
    args->mTrans->SetIsFromPredictor(overrider->GetIsFromPredictor());
    args->mTrans->SetAllow1918(overrider->GetAllow1918());
  }

  return PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, args);
}

nsresult nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget** target) {
  Unused << EnsureSocketThreadTarget();

  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
  nsCOMPtr<nsIEventTarget> temp(mSocketThreadTarget);
  temp.forget(target);
  return NS_OK;
}

nsresult nsHttpConnectionMgr::ReclaimConnection(HttpConnectionBase* conn) {
  LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%p]\n", conn));

  Unused << EnsureSocketThreadTarget();

  nsCOMPtr<nsIEventTarget> target;
  {
    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    target = mSocketThreadTarget;
  }

  if (!target) {
    NS_WARNING("cannot post event if not initialized");
    return NS_ERROR_NOT_INITIALIZED;
  }

  RefPtr<HttpConnectionBase> connRef(conn);
  RefPtr<nsHttpConnectionMgr> self(this);
  return target->Dispatch(NS_NewRunnableFunction(
      "nsHttpConnectionMgr::CallReclaimConnection",
      [conn{std::move(connRef)}, self{std::move(self)}]() {
        self->OnMsgReclaimConnection(conn);
      }));
}

// A structure used to marshall 6 pointers across the various necessary
// threads to complete an HTTP upgrade.
class nsCompleteUpgradeData : public ARefBase {
 public:
  nsCompleteUpgradeData(nsHttpTransaction* aTrans,
                        nsIHttpUpgradeListener* aListener, bool aJsWrapped)
      : mTrans(aTrans), mUpgradeListener(aListener), mJsWrapped(aJsWrapped) {}

  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsCompleteUpgradeData, override)

  RefPtr<nsHttpTransaction> mTrans;
  nsCOMPtr<nsIHttpUpgradeListener> mUpgradeListener;

  nsCOMPtr<nsISocketTransport> mSocketTransport;
  nsCOMPtr<nsIAsyncInputStream> mSocketIn;
  nsCOMPtr<nsIAsyncOutputStream> mSocketOut;

  bool mJsWrapped;

 private:
  virtual ~nsCompleteUpgradeData() {
    NS_ReleaseOnMainThread("nsCompleteUpgradeData.mUpgradeListener",
                           mUpgradeListener.forget());
  }
};

nsresult nsHttpConnectionMgr::CompleteUpgrade(
    HttpTransactionShell* aTrans, nsIHttpUpgradeListener* aUpgradeListener) {
  // test if aUpgradeListener is a wrapped JsObject
  nsCOMPtr<nsIXPConnectWrappedJS> wrapper = do_QueryInterface(aUpgradeListener);

  bool wrapped = !!wrapper;

  RefPtr<nsCompleteUpgradeData> data = new nsCompleteUpgradeData(
      aTrans->AsHttpTransaction(), aUpgradeListener, wrapped);
  return PostEvent(&nsHttpConnectionMgr::OnMsgCompleteUpgrade, 0, data);
}

nsresult nsHttpConnectionMgr::UpdateParam(nsParamName name, uint16_t value) {
  uint32_t param = (uint32_t(name) << 16) | uint32_t(value);
  return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam,
                   static_cast<int32_t>(param), nullptr);
}

nsresult nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo* aCI) {
  LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", aCI->HashKey().get()));
  RefPtr<nsHttpConnectionInfo> ci;
  if (aCI) {
    ci = aCI->Clone();
  }
  return PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
}

nsresult nsHttpConnectionMgr::ProcessPendingQ() {
  LOG(("nsHttpConnectionMgr::ProcessPendingQ [All CI]\n"));
  return PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, nullptr);
}

void nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket(int32_t,
                                                        ARefBase* param) {
  EventTokenBucket* tokenBucket = static_cast<EventTokenBucket*>(param);
  gHttpHandler->SetRequestTokenBucket(tokenBucket);
}

nsresult nsHttpConnectionMgr::UpdateRequestTokenBucket(
    EventTokenBucket* aBucket) {
  // Call From main thread when a new EventTokenBucket has been made in order
  // to post the new value to the socket thread.
  return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket, 0,
                   aBucket);
}

nsresult nsHttpConnectionMgr::ClearConnectionHistory() {
  return PostEvent(&nsHttpConnectionMgr::OnMsgClearConnectionHistory, 0,
                   nullptr);
}

void nsHttpConnectionMgr::OnMsgClearConnectionHistory(int32_t,
                                                      ARefBase* param) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  LOG(("nsHttpConnectionMgr::OnMsgClearConnectionHistory"));

  for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    RefPtr<ConnectionEntry> ent = iter.Data();
    if (ent->IdleConnectionsLength() == 0 && ent->ActiveConnsLength() == 0 &&
        ent->DnsAndConnectSocketsLength() == 0 &&
        ent->UrgentStartQueueLength() == 0 && ent->PendingQueueLength() == 0 &&
        !ent->mDoNotDestroy) {
      iter.Remove();
    }
  }
}

nsresult nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection* conn) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p"this, conn));

  if (!conn->ConnectionInfo()) {
    return NS_ERROR_UNEXPECTED;
  }

  ConnectionEntry* ent = mCT.GetWeak(conn->ConnectionInfo()->HashKey());

  if (!ent || NS_FAILED(ent->CloseIdleConnection(conn))) {
    return NS_ERROR_UNEXPECTED;
  }

  return NS_OK;
}

nsresult nsHttpConnectionMgr::RemoveIdleConnection(nsHttpConnection* conn) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  LOG(("nsHttpConnectionMgr::RemoveIdleConnection %p conn=%p"this, conn));

  if (!conn->ConnectionInfo()) {
    return NS_ERROR_UNEXPECTED;
  }

  ConnectionEntry* ent = mCT.GetWeak(conn->ConnectionInfo()->HashKey());

  if (!ent || NS_FAILED(ent->RemoveIdleConnection(conn))) {
    return NS_ERROR_UNEXPECTED;
  }

  return NS_OK;
}

HttpConnectionBase* nsHttpConnectionMgr::FindCoalescableConnectionByHashKey(
    ConnectionEntry* ent, const nsCString& key, bool justKidding, bool aNoHttp2,
    bool aNoHttp3) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  MOZ_ASSERT(!aNoHttp2 || !aNoHttp3);
  MOZ_ASSERT(ent->mConnInfo);
  nsHttpConnectionInfo* ci = ent->mConnInfo;

  nsTArray<nsWeakPtr>* listOfWeakConns = mCoalescingHash.Get(key);
  if (!listOfWeakConns) {
    return nullptr;
  }

  uint32_t listLen = listOfWeakConns->Length();
  for (uint32_t j = 0; j < listLen;) {
    RefPtr<HttpConnectionBase> potentialMatch =
        do_QueryReferent(listOfWeakConns->ElementAt(j));
    if (!potentialMatch) {
      // This is a connection that needs to be removed from the list
      LOG(
          ("FindCoalescableConnectionByHashKey() found old conn %p that has "
           "null weak ptr - removing\n",
           listOfWeakConns->ElementAt(j).get()));
      if (j != listLen - 1) {
        listOfWeakConns->Elements()[j] =
            listOfWeakConns->Elements()[listLen - 1];
      }
      listOfWeakConns->RemoveLastElement();
      MOZ_ASSERT(listOfWeakConns->Length() == listLen - 1);
      listLen--;
      continue;  // without adjusting iterator
    }

    if (aNoHttp3 && potentialMatch->UsingHttp3()) {
      j++;
      continue;
    }
    if (aNoHttp2 && potentialMatch->UsingSpdy()) {
      j++;
      continue;
    }
    bool couldJoin;
    if (justKidding) {
      couldJoin =
          potentialMatch->TestJoinConnection(ci->GetOrigin(), ci->OriginPort());
    } else {
      couldJoin =
          potentialMatch->JoinConnection(ci->GetOrigin(), ci->OriginPort());
    }
    if (couldJoin) {
      LOG(
          ("FindCoalescableConnectionByHashKey() found match conn=%p key=%s "
           "newCI=%s matchedCI=%s join ok\n",
           potentialMatch.get(), key.get(), ci->HashKey().get(),
           potentialMatch->ConnectionInfo()->HashKey().get()));
      return potentialMatch.get();
    }
    LOG(
        ("FindCoalescableConnectionByHashKey() found match conn=%p key=%s "
         "newCI=%s matchedCI=%s join failed\n",
         potentialMatch.get(), key.get(), ci->HashKey().get(),
         potentialMatch->ConnectionInfo()->HashKey().get()));

    ++j;  // bypassed by continue when weakptr fails
  }

  if (!listLen) {  // shrunk to 0 while iterating
    LOG(("FindCoalescableConnectionByHashKey() removing empty list element\n"));
    mCoalescingHash.Remove(key);
  }
  return nullptr;
}

static void BuildOriginFrameHashKey(nsACString& newKey,
                                    nsHttpConnectionInfo* ci,
                                    const nsACString& host, int32_t port) {
  newKey.Assign(host);
  if (ci->GetAnonymous()) {
    newKey.AppendLiteral("~A:");
  } else {
    newKey.AppendLiteral("~.:");
  }
  if (ci->GetFallbackConnection()) {
    newKey.AppendLiteral("~F:");
  } else {
    newKey.AppendLiteral("~.:");
  }
  newKey.AppendInt(port);
  newKey.AppendLiteral("/[");
  nsAutoCString suffix;
  ci->GetOriginAttributes().CreateSuffix(suffix);
  newKey.Append(suffix);
  newKey.AppendLiteral("]viaORIGIN.FRAME");
}

HttpConnectionBase* nsHttpConnectionMgr::FindCoalescableConnection(
    ConnectionEntry* ent, bool justKidding, bool aNoHttp2, bool aNoHttp3) {
  MOZ_ASSERT(!aNoHttp2 || !aNoHttp3);
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  MOZ_ASSERT(ent->mConnInfo);
  nsHttpConnectionInfo* ci = ent->mConnInfo;
  LOG(("FindCoalescableConnection %s\n", ci->HashKey().get()));

  if (ci->GetWebTransport()) {
    LOG(("Don't coalesce a WebTransport conn "));
    return nullptr;
  }
  // First try and look it up by origin frame
  nsCString newKey;
  BuildOriginFrameHashKey(newKey, ci, ci->GetOrigin(), ci->OriginPort());
  HttpConnectionBase* conn = FindCoalescableConnectionByHashKey(
      ent, newKey, justKidding, aNoHttp2, aNoHttp3);
  if (conn) {
    LOG(("FindCoalescableConnection(%s) match conn %p on frame key %s\n",
         ci->HashKey().get(), conn, newKey.get()));
    return conn;
  }

  // now check for DNS based keys
  // deleted conns (null weak pointers) are removed from list
  uint32_t keyLen = ent->mCoalescingKeys.Length();
  for (uint32_t i = 0; i < keyLen; ++i) {
    conn = FindCoalescableConnectionByHashKey(ent, ent->mCoalescingKeys[i],
                                              justKidding, aNoHttp2, aNoHttp3);

    auto usableEntry = [&](HttpConnectionBase* conn) {
      // This is allowed by the spec, but other browsers don't coalesce
      // so agressively, which surprises developers. See bug 1420777.
      if (StaticPrefs::network_http_http2_aggressive_coalescing()) {
        return true;
      }

      // Make sure that the connection's IP address is one that is in
      // the set of IP addresses in the entry's DNS response.
      NetAddr addr;
      nsresult rv = conn->GetPeerAddr(&addr);
      if (NS_FAILED(rv)) {
        // Err on the side of not coalescing
        return false;
      }
      // We don't care about remote port when matching entries.
      addr.inet.port = 0;
      return ent->mAddresses.Contains(addr);
    };

    if (conn) {
      LOG(("Found connection with matching hash"));
      if (usableEntry(conn)) {
        LOG(("> coalescing"));
        return conn;
      } else {
        LOG(("> not coalescing as remote address not present in DNS records"));
      }
    }
  }

  LOG(("FindCoalescableConnection(%s) no matching conn\n",
       ci->HashKey().get()));
  return nullptr;
}

void nsHttpConnectionMgr::UpdateCoalescingForNewConn(
    HttpConnectionBase* newConn, ConnectionEntry* ent, bool aNoHttp3) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  MOZ_ASSERT(newConn);
  MOZ_ASSERT(newConn->ConnectionInfo());
  MOZ_ASSERT(ent);
  MOZ_ASSERT(mCT.GetWeak(newConn->ConnectionInfo()->HashKey()) == ent);
  LOG(("UpdateCoalescingForNewConn newConn=%p aNoHttp3=%d", newConn, aNoHttp3));
  if (newConn->ConnectionInfo()->GetWebTransport()) {
    LOG(("Don't coalesce a WebTransport conn %p", newConn));
    // TODO: implement this properly in bug 1815735.
    return;
  }

  HttpConnectionBase* existingConn =
      FindCoalescableConnection(ent, truefalsefalse);
  if (existingConn) {
    // Prefer http3 connection, but allow an HTTP/2 connection if it is used for
    // WebSocket.
    if (newConn->UsingHttp3() && existingConn->UsingSpdy()) {
      RefPtr<nsHttpConnection> connTCP = do_QueryObject(existingConn);
      if (connTCP && !connTCP->IsForWebSocket()) {
        LOG(
            ("UpdateCoalescingForNewConn() found existing active H2 conn that "
             "could have served newConn, but new connection is H3, therefore "
             "close the H2 conncetion"));
        existingConn->SetCloseReason(
            ConnectionCloseReason::CLOSE_EXISTING_CONN_FOR_COALESCING);
        existingConn->DontReuse();
      }
    } else if (existingConn->UsingHttp3() && newConn->UsingSpdy()) {
      RefPtr<nsHttpConnection> connTCP = do_QueryObject(newConn);
      if (connTCP && !connTCP->IsForWebSocket() && !aNoHttp3) {
        LOG(
            ("UpdateCoalescingForNewConn() found existing active H3 conn that "
             "could have served H2 newConn graceful close of newConn=%p to "
             "migrate to existingConn %p\n",
             newConn, existingConn));
        newConn->SetCloseReason(
            ConnectionCloseReason::CLOSE_NEW_CONN_FOR_COALESCING);
        newConn->DontReuse();
        return;
      }
    } else {
      LOG(
          ("UpdateCoalescingForNewConn() found existing active conn that could "
           "have served newConn "
           "graceful close of newConn=%p to migrate to existingConn %p\n",
           newConn, existingConn));
      newConn->SetCloseReason(
          ConnectionCloseReason::CLOSE_NEW_CONN_FOR_COALESCING);
      newConn->DontReuse();
      return;
    }
  }

  // This connection might go into the mCoalescingHash for new transactions to
  // be coalesced onto if it can accept new transactions
  if (!newConn->CanDirectlyActivate()) {
    return;
  }

  uint32_t keyLen = ent->mCoalescingKeys.Length();
  for (uint32_t i = 0; i < keyLen; ++i) {
    LOG((
        "UpdateCoalescingForNewConn() registering newConn %p %s under key %s\n",
        newConn, newConn->ConnectionInfo()->HashKey().get(),
        ent->mCoalescingKeys[i].get()));

    mCoalescingHash
        .LookupOrInsertWith(
            ent->mCoalescingKeys[i],
            [] {
              LOG(("UpdateCoalescingForNewConn() need new list element\n"));
              return MakeUnique<nsTArray<nsWeakPtr>>(1);
            })
        ->AppendElement(do_GetWeakReference(
            static_cast<nsISupportsWeakReference*>(newConn)));
  }

  // this is a new connection that can be coalesced onto. hooray!
  // if there are other connection to this entry (e.g.
  // some could still be handshaking, shutting down, etc..) then close
  // them down after any transactions that are on them are complete.
  // This probably happened due to the parallel connection algorithm
  // that is used only before the host is known to speak h2.
  ent->MakeAllDontReuseExcept(newConn);
}

// This function lets a connection, after completing the NPN phase,
// report whether or not it is using spdy through the usingSpdy
// argument. It would not be necessary if NPN were driven out of
// the connection manager. The connection entry associated with the
// connection is then updated to indicate whether or not we want to use
// spdy with that host and update the coalescing hash
// entries used for de-sharding hostsnames.
void nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection* conn,
                                               bool usingSpdy,
                                               bool disallowHttp3) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  if (!conn->ConnectionInfo()) {
    return;
  }
  ConnectionEntry* ent = mCT.GetWeak(conn->ConnectionInfo()->HashKey());
  if (!ent || !usingSpdy) {
    return;
  }

  ent->mUsingSpdy = true;
  mNumSpdyHttp3ActiveConns++;

  // adjust timeout timer
  uint32_t ttl = conn->TimeToLive();
  uint64_t timeOfExpire = NowInSeconds() + ttl;
  if (!mTimer || timeOfExpire < mTimeOfNextWakeUp) {
    PruneDeadConnectionsAfter(ttl);
  }

  UpdateCoalescingForNewConn(conn, ent, disallowHttp3);

  nsresult rv = ProcessPendingQ(ent->mConnInfo);
  if (NS_FAILED(rv)) {
    LOG(
        ("ReportSpdyConnection conn=%p ent=%p "
         "failed to process pending queue (%08x)\n",
         conn, ent, static_cast<uint32_t>(rv)));
  }
  rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ);
  if (NS_FAILED(rv)) {
    LOG(
        ("ReportSpdyConnection conn=%p ent=%p "
         "failed to post event (%08x)\n",
         conn, ent, static_cast<uint32_t>(rv)));
  }
}

void nsHttpConnectionMgr::ReportHttp3Connection(HttpConnectionBase* conn) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  if (!conn->ConnectionInfo()) {
    return;
  }
  ConnectionEntry* ent = mCT.GetWeak(conn->ConnectionInfo()->HashKey());
  if (!ent) {
    return;
  }

  mNumSpdyHttp3ActiveConns++;

  UpdateCoalescingForNewConn(conn, ent, false);
  nsresult rv = ProcessPendingQ(ent->mConnInfo);
  if (NS_FAILED(rv)) {
    LOG(
        ("ReportHttp3Connection conn=%p ent=%p "
         "failed to process pending queue (%08x)\n",
         conn, ent, static_cast<uint32_t>(rv)));
  }
  rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ);
  if (NS_FAILED(rv)) {
    LOG(
        ("ReportHttp3Connection conn=%p ent=%p "
         "failed to post event (%08x)\n",
         conn, ent, static_cast<uint32_t>(rv)));
  }
}

//-----------------------------------------------------------------------------
bool nsHttpConnectionMgr::DispatchPendingQ(
    nsTArray<RefPtr<PendingTransactionInfo>>& pendingQ, ConnectionEntry* ent,
    bool considerAll) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  PendingTransactionInfo* pendingTransInfo = nullptr;
  nsresult rv;
  bool dispatchedSuccessfully = false;

  // if !considerAll iterate the pending list until one is dispatched
  // successfully. Keep iterating afterwards only until a transaction fails to
  // dispatch. if considerAll == true then try and dispatch all items.
  for (uint32_t i = 0; i < pendingQ.Length();) {
    pendingTransInfo = pendingQ[i];

    bool alreadyDnsAndConnectSocketOrWaitingForTLS =
        pendingTransInfo->IsAlreadyClaimedInitializingConn();

    rv = TryDispatchTransaction(ent, alreadyDnsAndConnectSocketOrWaitingForTLS,
                                pendingTransInfo);
    if (NS_SUCCEEDED(rv) || (rv != NS_ERROR_NOT_AVAILABLE)) {
      if (NS_SUCCEEDED(rv)) {
        LOG((" dispatching pending transaction...\n"));
      } else {
        LOG(
            (" removing pending transaction based on "
             "TryDispatchTransaction returning hard error %" PRIx32 "\n",
             static_cast<uint32_t>(rv)));
      }
      if (pendingQ.RemoveElement(pendingTransInfo)) {
        // pendingTransInfo is now potentially destroyed
        dispatchedSuccessfully = true;
        continue;  // dont ++i as we just made the array shorter
      }

      LOG((" transaction not found in pending queue\n"));
    }

    if (dispatchedSuccessfully && !considerAll) break;

    ++i;
  }
  return dispatchedSuccessfully;
}

uint32_t nsHttpConnectionMgr::MaxPersistConnections(
    ConnectionEntry* ent) const {
  if (ent->mConnInfo->UsingHttpProxy() && !ent->mConnInfo->UsingConnect()) {
    return static_cast<uint32_t>(mMaxPersistConnsPerProxy);
  }

  return static_cast<uint32_t>(mMaxPersistConnsPerHost);
}

void nsHttpConnectionMgr::PreparePendingQForDispatching(
    ConnectionEntry* ent, nsTArray<RefPtr<PendingTransactionInfo>>& pendingQ,
    bool considerAll) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  pendingQ.Clear();

  uint32_t totalCount = ent->TotalActiveConnections();
  uint32_t maxPersistConns = MaxPersistConnections(ent);
  uint32_t availableConnections =
      maxPersistConns > totalCount ? maxPersistConns - totalCount : 0;

  // No need to try dispatching if we reach the active connection limit.
  if (!availableConnections) {
    return;
  }

  // Only have to get transactions from the queue whose window id is 0.
  if (!gHttpHandler->ActiveTabPriority()) {
    ent->AppendPendingQForFocusedWindow(0, pendingQ, availableConnections);
    return;
  }

  uint32_t maxFocusedWindowConnections =
      availableConnections * gHttpHandler->FocusedWindowTransactionRatio();
  MOZ_ASSERT(maxFocusedWindowConnections < availableConnections);

  if (!maxFocusedWindowConnections) {
    maxFocusedWindowConnections = 1;
  }

  // Only need to dispatch transactions for either focused or
  // non-focused window because considerAll is false.
  if (!considerAll) {
    ent->AppendPendingQForFocusedWindow(mCurrentBrowserId, pendingQ,
                                        maxFocusedWindowConnections);

    if (pendingQ.IsEmpty()) {
      ent->AppendPendingQForNonFocusedWindows(mCurrentBrowserId, pendingQ,
                                              availableConnections);
    }
    return;
  }

  uint32_t maxNonFocusedWindowConnections =
      availableConnections - maxFocusedWindowConnections;
  nsTArray<RefPtr<PendingTransactionInfo>> remainingPendingQ;

  ent->AppendPendingQForFocusedWindow(mCurrentBrowserId, pendingQ,
                                      maxFocusedWindowConnections);

  if (maxNonFocusedWindowConnections) {
    ent->AppendPendingQForNonFocusedWindows(
        mCurrentBrowserId, remainingPendingQ, maxNonFocusedWindowConnections);
  }

  // If the slots for either focused or non-focused window are not filled up
  // to the availability, try to use the remaining available connections
  // for the other slot (with preference for the focused window).
  if (remainingPendingQ.Length() < maxNonFocusedWindowConnections) {
    ent->AppendPendingQForFocusedWindow(
        mCurrentBrowserId, pendingQ,
        maxNonFocusedWindowConnections - remainingPendingQ.Length());
  } else if (pendingQ.Length() < maxFocusedWindowConnections) {
    ent->AppendPendingQForNonFocusedWindows(
        mCurrentBrowserId, remainingPendingQ,
        maxFocusedWindowConnections - pendingQ.Length());
  }

  MOZ_ASSERT(pendingQ.Length() + remainingPendingQ.Length() <=
             availableConnections);

  LOG(
      ("nsHttpConnectionMgr::PreparePendingQForDispatching "
       "focused window pendingQ.Length()=%zu"
       ", remainingPendingQ.Length()=%zu\n",
       pendingQ.Length(), remainingPendingQ.Length()));

  // Append elements in |remainingPendingQ| to |pendingQ|. The order in
  // |pendingQ| is like: [focusedWindowTrans...nonFocusedWindowTrans].
  pendingQ.AppendElements(std::move(remainingPendingQ));
}

bool nsHttpConnectionMgr::ProcessPendingQForEntry(ConnectionEntry* ent,
                                                  bool considerAll) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  LOG(
      ("nsHttpConnectionMgr::ProcessPendingQForEntry "
       "[ci=%s ent=%p active=%zu idle=%zu urgent-start-queue=%zu"
       " queued=%zu]\n",
       ent->mConnInfo->HashKey().get(), ent, ent->ActiveConnsLength(),
       ent->IdleConnectionsLength(), ent->UrgentStartQueueLength(),
       ent->PendingQueueLength()));

  if (LOG_ENABLED()) {
    ent->PrintPendingQ();
    ent->LogConnections();
  }

  if (!ent->PendingQueueLength() && !ent->UrgentStartQueueLength()) {
    return false;
  }
  ProcessSpdyPendingQ(ent);

  bool dispatchedSuccessfully = false;

  if (ent->UrgentStartQueueLength()) {
    nsTArray<RefPtr<PendingTransactionInfo>> pendingQ;
    ent->AppendPendingUrgentStartQ(pendingQ);
    dispatchedSuccessfully = DispatchPendingQ(pendingQ, ent, considerAll);
    for (const auto& transactionInfo : Reversed(pendingQ)) {
      ent->InsertTransaction(transactionInfo);
    }
  }

  if (dispatchedSuccessfully && !considerAll) {
    return dispatchedSuccessfully;
  }

  nsTArray<RefPtr<PendingTransactionInfo>> pendingQ;
  PreparePendingQForDispatching(ent, pendingQ, considerAll);

  // The only case that |pendingQ| is empty is when there is no
  // connection available for dispatching.
  if (pendingQ.IsEmpty()) {
    return dispatchedSuccessfully;
  }

  dispatchedSuccessfully |= DispatchPendingQ(pendingQ, ent, considerAll);

  // Put the leftovers into connection entry, in the same order as they
  // were before to keep the natural ordering.
  for (const auto& transactionInfo : Reversed(pendingQ)) {
    ent->InsertTransaction(transactionInfo, true);
  }

  // Only remove empty pendingQ when considerAll is true.
  if (considerAll) {
    ent->RemoveEmptyPendingQ();
  }

  return dispatchedSuccessfully;
}

bool nsHttpConnectionMgr::ProcessPendingQForEntry(nsHttpConnectionInfo* ci) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  ConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
  if (ent) return ProcessPendingQForEntry(ent, false);
  return false;
}

// we're at the active connection limit if any one of the following conditions
// is true:
//  (1) at max-connections
//  (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
//  (3) keep-alive disabled and at max-connections-per-server
bool nsHttpConnectionMgr::AtActiveConnectionLimit(ConnectionEntry* ent,
                                                  uint32_t caps) {
  nsHttpConnectionInfo* ci = ent->mConnInfo;
  uint32_t totalCount = ent->TotalActiveConnections();

  if (ci->IsHttp3()) {
    if (ci->GetWebTransport()) {
      // TODO: implement this properly in bug 1815735.
      return false;
    }
    return totalCount > 0;
  }

  uint32_t maxPersistConns = MaxPersistConnections(ent);

  LOG(
      ("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x,"
       "totalCount=%u, maxPersistConns=%u]\n",
       ci->HashKey().get(), caps, totalCount, maxPersistConns));

  if (caps & NS_HTTP_URGENT_START) {
    if (totalCount >= (mMaxUrgentExcessiveConns + maxPersistConns)) {
      LOG((
          "The number of total connections are greater than or equal to sum of "
          "max urgent-start queue length and the number of max persistent "
          "connections.\n"));
      return true;
    }
    return false;
  }

  // update maxconns if potentially limited by the max socket count
  // this requires a dynamic reduction in the max socket count to a point
  // lower than the max-connections pref.
  uint32_t maxSocketCount = gHttpHandler->MaxSocketCount();
  if (mMaxConns > maxSocketCount) {
    mMaxConns = maxSocketCount;
    LOG(("nsHttpConnectionMgr %p mMaxConns dynamically reduced to %u"this,
         mMaxConns));
  }

  // If there are more active connections than the global limit, then we're
  // done. Purging idle connections won't get us below it.
  if (mNumActiveConns >= mMaxConns) {
    LOG((" num active conns == max conns\n"));
    return true;
  }

  bool result = (totalCount >= maxPersistConns);
  LOG(("AtActiveConnectionLimit result: %s", result ? "true" : "false"));
  return result;
}

// returns NS_OK if a connection was started
// return NS_ERROR_NOT_AVAILABLE if a new connection cannot be made due to
//        ephemeral limits
// returns other NS_ERROR on hard failure conditions
nsresult nsHttpConnectionMgr::MakeNewConnection(
    ConnectionEntry* ent, PendingTransactionInfo* pendingTransInfo) {
  LOG(("nsHttpConnectionMgr::MakeNewConnection %p ent=%p trans=%p"this, ent,
       pendingTransInfo->Transaction()));
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  if (ent->FindConnToClaim(pendingTransInfo)) {
    return NS_OK;
  }

  nsHttpTransaction* trans = pendingTransInfo->Transaction();

  // If this host is trying to negotiate a SPDY session right now,
  // don't create any new connections until the result of the
  // negotiation is known.
  if (!(trans->Caps() & NS_HTTP_DISALLOW_SPDY) &&
      (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) && ent->RestrictConnections()) {
    LOG(
        ("nsHttpConnectionMgr::MakeNewConnection [ci = %s] "
         "Not Available Due to RestrictConnections()\n",
         ent->mConnInfo->HashKey().get()));
    return NS_ERROR_NOT_AVAILABLE;
  }

  // We need to make a new connection. If that is going to exceed the
  // global connection limit then try and free up some room by closing
  // an idle connection to another host. We know it won't select "ent"
  // because we have already determined there are no idle connections
  // to our destination

  if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) && mNumIdleConns) {
    // If the global number of connections is preventing the opening of new
    // connections to a host without idle connections, then close them
    // regardless of their TTL.
    auto iter = mCT.ConstIter();
    while (mNumIdleConns + mNumActiveConns + 1 >= mMaxConns && !iter.Done()) {
      RefPtr<ConnectionEntry> entry = iter.Data();
      entry->CloseIdleConnections((mNumIdleConns + mNumActiveConns + 1) -
                                  mMaxConns);
      iter.Next();
    }
  }

  if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) && mNumActiveConns &&
      StaticPrefs::network_http_http2_enabled()) {
    // If the global number of connections is preventing the opening of new
    // connections to a host without idle connections, then close any spdy
    // ASAP.
    for (const RefPtr<ConnectionEntry>& entry : mCT.Values()) {
      while (entry->MakeFirstActiveSpdyConnDontReuse()) {
        // Stop on <= (particularly =) because this dontreuse
        // causes async close.
        if (mNumIdleConns + mNumActiveConns + 1 <= mMaxConns) {
          goto outerLoopEnd;
        }
      }
    }
  outerLoopEnd:;
  }

  if (AtActiveConnectionLimit(ent, trans->Caps())) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  nsresult rv = ent->CreateDnsAndConnectSocket(
      trans, trans->Caps(), falsefalse,
      trans->GetClassOfService().Flags() & nsIClassOfService::UrgentStart, true,
      pendingTransInfo);
  if (NS_FAILED(rv)) {
    /* hard failure */
    LOG(
        ("nsHttpConnectionMgr::MakeNewConnection [ci = %s trans = %p] "
         "CreateDnsAndConnectSocket() hard failure.\n",
         ent->mConnInfo->HashKey().get(), trans));
    trans->Close(rv);
    if (rv == NS_ERROR_NOT_AVAILABLE) rv = NS_ERROR_FAILURE;
    return rv;
  }

  return NS_OK;
}

// returns OK if a connection is found for the transaction
//   and the transaction is started.
// returns ERROR_NOT_AVAILABLE if no connection can be found and it
//   should be queued until circumstances change
// returns other ERROR when transaction has a hard failure and should
//   not remain in the pending queue
nsresult nsHttpConnectionMgr::TryDispatchTransaction(
    ConnectionEntry* ent, bool onlyReusedConnection,
    PendingTransactionInfo* pendingTransInfo) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  nsHttpTransaction* trans = pendingTransInfo->Transaction();

  LOG(
      ("nsHttpConnectionMgr::TryDispatchTransaction without conn "
       "[trans=%p ci=%p ci=%s caps=%x onlyreused=%d active=%zu "
       "idle=%zu]\n",
       trans, ent->mConnInfo.get(), ent->mConnInfo->HashKey().get(),
       uint32_t(trans->Caps()), onlyReusedConnection, ent->ActiveConnsLength(),
       ent->IdleConnectionsLength()));

  uint32_t caps = trans->Caps();

  // 0 - If this should use spdy then dispatch it post haste.
  // 1 - If there is connection pressure then see if we can pipeline this on
  //     a connection of a matching type instead of using a new conn
  // 2 - If there is an idle connection, use it!
  // 3 - if class == reval or script and there is an open conn of that type
  //     then pipeline onto shortest pipeline of that class if limits allow
  // 4 - If we aren't up against our connection limit,
  //     then open a new one
  // 5 - Try a pipeline if we haven't already - this will be unusual because
  //     it implies a low connection pressure situation where
  //     MakeNewConnection() failed.. that is possible, but unlikely, due to
  //     global limits
  // 6 - no connection is available - queue it

  RefPtr<HttpConnectionBase> unusedSpdyPersistentConnection;

  // step 0
  // look for existing spdy connection - that's always best because it is
  // essentially pipelining without head of line blocking

  RefPtr<HttpConnectionBase> conn = GetH2orH3ActiveConn(
      ent,
      (!StaticPrefs::network_http_http2_enabled() ||
       (caps & NS_HTTP_DISALLOW_SPDY)),
      (!nsHttpHandler::IsHttp3Enabled() || (caps & NS_HTTP_DISALLOW_HTTP3)));
  if (conn) {
    LOG(("TryingDispatchTransaction: an active h2 connection exists"));
    WebSocketSupport wsSupp = conn->GetWebSocketSupport();
    if (trans->IsWebsocketUpgrade()) {
      LOG(("TryingDispatchTransaction: this is a websocket upgrade"));
      if (wsSupp == WebSocketSupport::NO_SUPPORT) {
        LOG((
            "TryingDispatchTransaction: no support for websockets over Http2"));
        // This is a websocket transaction and we already have a h2 connection
        // that do not support websockets, we should disable h2 for this
        // transaction.
        trans->DisableSpdy();
        caps &= NS_HTTP_DISALLOW_SPDY;
        trans->MakeSticky();
      } else if (wsSupp == WebSocketSupport::SUPPORTED) {
        RefPtr<nsHttpConnection> connTCP = do_QueryObject(conn);
        LOG(("TryingDispatchTransaction: websockets over Http2"));

        // No limit for number of websockets, dispatch transaction to the tunnel
        RefPtr<nsHttpConnection> connToTunnel;
        connTCP->CreateTunnelStream(trans, getter_AddRefs(connToTunnel), true);
        ent->InsertIntoH2WebsocketConns(connToTunnel);
        trans->SetConnection(nullptr);
        connToTunnel->SetInSpdyTunnel();  // tells conn it is already in tunnel
        trans->SetIsHttp2Websocket(true);
        nsresult rv = DispatchTransaction(ent, trans, connToTunnel);
        // need to undo NonSticky bypass for transaction reset to continue
        // for correct websocket upgrade handling
        trans->MakeSticky();
        return rv;
      } else {
        // if we aren't sure that websockets are supported yet or we are
        // already at the connection limit then we queue the transaction
        LOG(("TryingDispatchTransaction: unsure if websockets over Http2"));
        return NS_ERROR_NOT_AVAILABLE;
      }
    } else {
      if ((caps & NS_HTTP_ALLOW_KEEPALIVE) ||
          (caps & NS_HTTP_ALLOW_SPDY_WITHOUT_KEEPALIVE) ||
          !conn->IsExperienced()) {
        LOG((" dispatch to spdy: [conn=%p]\n", conn.get()));
        trans->RemoveDispatchedAsBlocking(); /* just in case */
        nsresult rv = DispatchTransaction(ent, trans, conn);
        NS_ENSURE_SUCCESS(rv, rv);
        return NS_OK;
      }
      unusedSpdyPersistentConnection = conn;
    }
  }

  // If this is not a blocking transaction and the request context for it is
  // currently processing one or more blocking transactions then we
  // need to just leave it in the queue until those are complete unless it is
  // explicitly marked as unblocked.
  if (!(caps & NS_HTTP_LOAD_AS_BLOCKING)) {
    if (!(caps & NS_HTTP_LOAD_UNBLOCKED)) {
      nsIRequestContext* requestContext = trans->RequestContext();
      if (requestContext) {
        uint32_t blockers = 0;
        if (NS_SUCCEEDED(
                requestContext->GetBlockingTransactionCount(&blockers)) &&
            blockers) {
          // need to wait for blockers to clear
          LOG((" blocked by request context: [rc=%p trans=%p blockers=%d]\n",
               requestContext, trans, blockers));
          return NS_ERROR_NOT_AVAILABLE;
        }
      }
    }
  } else {
    // Mark the transaction and its load group as blocking right now to prevent
    // other transactions from being reordered in the queue due to slow syns.
    trans->DispatchedAsBlocking();
  }

  // step 1
  // If connection pressure, then we want to favor pipelining of any kind
  // h1 pipelining has been removed

  // Subject most transactions at high parallelism to rate pacing.
  // It will only be actually submitted to the
  // token bucket once, and if possible it is granted admission synchronously.
  // It is important to leave a transaction in the pending queue when blocked by
  // pacing so it can be found on cancel if necessary.
  // Transactions that cause blocking or bypass it (e.g. js/css) are not rate
  // limited.
  if (gHttpHandler->UseRequestTokenBucket()) {
    // submit even whitelisted transactions to the token bucket though they will
    // not be slowed by it
    bool runNow = trans->TryToRunPacedRequest();
    if (!runNow) {
      if ((mNumActiveConns - mNumSpdyHttp3ActiveConns) <=
          gHttpHandler->RequestTokenBucketMinParallelism()) {
        runNow = true;  // white list it
      } else if (caps & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED)) {
        runNow = true;  // white list it
      }
    }
    if (!runNow) {
      LOG((" blocked due to rate pacing trans=%p\n", trans));
      return NS_ERROR_NOT_AVAILABLE;
    }
  }

  // step 2
  // consider an idle persistent connection
  bool idleConnsAllUrgent = false;
  if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
    nsresult rv = TryDispatchTransactionOnIdleConn(ent, pendingTransInfo, true,
                                                   &idleConnsAllUrgent);
    if (NS_SUCCEEDED(rv)) {
      LOG((" dispatched step 2 (idle) trans=%p\n", trans));
      return NS_OK;
    }
  }

  // step 3
  // consider pipelining scripts and revalidations
  // h1 pipelining has been removed

  // Don't dispatch if this transaction is waiting for HTTPS RR.
  // This usually happens when the pref "network.dns.force_waiting_https_rr" is
  // true or when echConfig is enabled.
  if (trans->WaitingForHTTPSRR()) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  // step 4
  if (!onlyReusedConnection) {
    nsresult rv = MakeNewConnection(ent, pendingTransInfo);
    if (NS_SUCCEEDED(rv)) {
      // this function returns NOT_AVAILABLE for asynchronous connects
      LOG((" dispatched step 4 (async new conn) trans=%p\n", trans));
      return NS_ERROR_NOT_AVAILABLE;
    }

    if (rv != NS_ERROR_NOT_AVAILABLE) {
      // not available return codes should try next step as they are
      // not hard errors. Other codes should stop now
      LOG((" failed step 4 (%" PRIx32 ") trans=%p\n",
           static_cast<uint32_t>(rv), trans));
      return rv;
    }

    // repeat step 2 when there are only idle connections and all are urgent,
    // don't respect urgency so that non-urgent transaction will be allowed
    // to dispatch on an urgent-start-only marked connection to avoid
    // dispatch deadlocks
    if (!(trans->GetClassOfService().Flags() &
          nsIClassOfService::UrgentStart) &&
        idleConnsAllUrgent &&
        ent->ActiveConnsLength() < MaxPersistConnections(ent)) {
      rv = TryDispatchTransactionOnIdleConn(ent, pendingTransInfo, false);
      if (NS_SUCCEEDED(rv)) {
        LOG((" dispatched step 2a (idle, reuse urgent) trans=%p\n", trans));
        return NS_OK;
      }
    }
  }

  // step 5
  // previously pipelined anything here if allowed but h1 pipelining has been
  // removed

  // step 6
  if (unusedSpdyPersistentConnection) {
    // to avoid deadlocks, we need to throw away this perfectly valid SPDY
    // connection to make room for a new one that can service a no KEEPALIVE
    // request
    unusedSpdyPersistentConnection->DontReuse();
  }

  LOG((" not dispatched (queued) trans=%p\n", trans));
  return NS_ERROR_NOT_AVAILABLE; /* queue it */
}

nsresult nsHttpConnectionMgr::TryDispatchTransactionOnIdleConn(
    ConnectionEntry* ent, PendingTransactionInfo* pendingTransInfo,
    bool respectUrgency, bool* allUrgent) {
  bool onlyUrgent = !!ent->IdleConnectionsLength();

  nsHttpTransaction* trans = pendingTransInfo->Transaction();
  bool urgentTrans =
      trans->GetClassOfService().Flags() & nsIClassOfService::UrgentStart;

  LOG(
      ("nsHttpConnectionMgr::TryDispatchTransactionOnIdleConn, ent=%p, "
       "trans=%p, urgent=%d",
       ent, trans, urgentTrans));

  RefPtr<nsHttpConnection> conn =
      ent->GetIdleConnection(respectUrgency, urgentTrans, &onlyUrgent);

  if (allUrgent) {
    *allUrgent = onlyUrgent;
  }

  if (conn) {
    // This will update the class of the connection to be the class of
    // the transaction dispatched on it.
    ent->InsertIntoActiveConns(conn);
    nsresult rv = DispatchTransaction(ent, trans, conn);
    NS_ENSURE_SUCCESS(rv, rv);

    return NS_OK;
  }

  return NS_ERROR_NOT_AVAILABLE;
}

nsresult nsHttpConnectionMgr::DispatchTransaction(ConnectionEntry* ent,
                                                  nsHttpTransaction* trans,
                                                  HttpConnectionBase* conn) {
  uint32_t caps = trans->Caps();
  int32_t priority = trans->Priority();
  nsresult rv;

  LOG(
      ("nsHttpConnectionMgr::DispatchTransaction "
       "[ent-ci=%s %p trans=%p caps=%x conn=%p priority=%d isHttp2=%d "
       "isHttp3=%d]\n",
       ent->mConnInfo->HashKey().get(), ent, trans, caps, conn, priority,
       conn->UsingSpdy(), conn->UsingHttp3()));

  // It is possible for a rate-paced transaction to be dispatched independent
  // of the token bucket when the amount of parallelization has changed or
  // when a muxed connection (e.g. h2) becomes available.
  trans->CancelPacing(NS_OK);

  TimeStamp now = TimeStamp::Now();
  TimeDuration elapsed = now - trans->GetPendingTime();
  auto recordPendingTimeForHTTPSRR = [&](nsCString& aKey) {
    uint32_t stage = trans->HTTPSSVCReceivedStage();
    if (HTTPS_RR_IS_USED(stage)) {
      glean::networking::transaction_wait_time_https_rr.AccumulateRawDuration(
          elapsed);

    } else {
      glean::networking::transaction_wait_time.AccumulateRawDuration(elapsed);
    }
  };

  PerfStats::RecordMeasurement(PerfStats::Metric::HttpTransactionWaitTime,
                               elapsed);

  PROFILER_MARKER(
      "DispatchTransaction", NETWORK,
      MarkerOptions(MarkerThreadId::MainThread(),
                    MarkerTiming::Interval(trans->GetPendingTime(), now)),
      UrlMarker, trans->GetUrl(), elapsed, trans->ChannelId());

  nsAutoCString httpVersionkey("h1"_ns);
  if (conn->UsingSpdy() || conn->UsingHttp3()) {
    LOG(
        ("Spdy Dispatch Transaction via Activate(). Transaction host = %s, "
         "Connection host = %s\n",
         trans->ConnectionInfo()->Origin(), conn->ConnectionInfo()->Origin()));
    rv = conn->Activate(trans, caps, priority);
    if (NS_SUCCEEDED(rv) && !trans->GetPendingTime().IsNull()) {
      if (conn->UsingSpdy()) {
        httpVersionkey = "h2"_ns;
        AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_SPDY,
                            trans->GetPendingTime(), now);
      } else {
        httpVersionkey = "h3"_ns;
        AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_HTTP3,
                            trans->GetPendingTime(), now);
      }
      recordPendingTimeForHTTPSRR(httpVersionkey);
      trans->SetPendingTime(false);
    }
    return rv;
  }

  MOZ_ASSERT(conn && !conn->Transaction(),
             "DispatchTranaction() on non spdy active connection");

  rv = DispatchAbstractTransaction(ent, trans, caps, conn, priority);

  if (NS_SUCCEEDED(rv) && !trans->GetPendingTime().IsNull()) {
    AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_HTTP,
                        trans->GetPendingTime(), now);
    recordPendingTimeForHTTPSRR(httpVersionkey);
    trans->SetPendingTime(false);
  }
  return rv;
}

// Use this method for dispatching nsAHttpTransction's. It can only safely be
// used upon first use of a connection when NPN has not negotiated SPDY vs
// HTTP/1 yet as multiplexing onto an existing SPDY session requires a
// concrete nsHttpTransaction
nsresult nsHttpConnectionMgr::DispatchAbstractTransaction(
    ConnectionEntry* ent, nsAHttpTransaction* aTrans, uint32_t caps,
    HttpConnectionBase* conn, int32_t priority) {
  MOZ_ASSERT(ent);

  nsresult rv;
  MOZ_ASSERT(!conn->UsingSpdy(),
             "Spdy Must Not Use DispatchAbstractTransaction");
  LOG(
      ("nsHttpConnectionMgr::DispatchAbstractTransaction "
       "[ci=%s trans=%p caps=%x conn=%p]\n",
       ent->mConnInfo->HashKey().get(), aTrans, caps, conn));

  RefPtr<nsAHttpTransaction> transaction(aTrans);
  RefPtr<ConnectionHandle> handle = new ConnectionHandle(conn);

  // give the transaction the indirect reference to the connection.
  transaction->SetConnection(handle);

  rv = conn->Activate(transaction, caps, priority);
  if (NS_FAILED(rv)) {
    LOG((" conn->Activate failed [rv=%" PRIx32 "]\n",
         static_cast<uint32_t>(rv)));
    DebugOnly<nsresult> rv_remove = ent->RemoveActiveConnection(conn);
    MOZ_ASSERT(NS_SUCCEEDED(rv_remove));

    // sever back references to connection, and do so without triggering
    // a call to ReclaimConnection ;-)
    transaction->SetConnection(nullptr);
    handle->Reset();  // destroy the connection
  }

  return rv;
}

void nsHttpConnectionMgr::ReportProxyTelemetry(ConnectionEntry* ent) {
  enum { PROXY_NONE = 1, PROXY_HTTP = 2, PROXY_SOCKS = 3, PROXY_HTTPS = 4 };

  if (!ent->mConnInfo->UsingProxy()) {
    Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_NONE);
  } else if (ent->mConnInfo->UsingHttpsProxy()) {
    Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_HTTPS);
  } else if (ent->mConnInfo->UsingHttpProxy()) {
    Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_HTTP);
  } else {
    Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_SOCKS);
  }
}

nsresult nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction* trans) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  // since "adds" and "cancels" are processed asynchronously and because
  // various events might trigger an "add" directly on the socket thread,
  // we must take care to avoid dispatching a transaction that has already
  // been canceled (see bug 190001).
  if (NS_FAILED(trans->Status())) {
    LOG((" transaction was canceled... dropping event!\n"));
    return NS_OK;
  }

  // Make sure a transaction is not in a pending queue.
  CheckTransInPendingQueue(trans);

  trans->SetPendingTime();

  PROFILER_MARKER("ProcessNewTransaction", NETWORK,
                  MarkerThreadId::MainThread(), UrlMarker, trans->GetUrl(),
                  TimeDuration::Zero(), trans->ChannelId());

  RefPtr<Http2PushedStreamWrapper> pushedStreamWrapper =
      trans->GetPushedStream();
  if (pushedStreamWrapper) {
    Http2PushedStream* pushedStream = pushedStreamWrapper->GetStream();
    if (pushedStream) {
      RefPtr<Http2Session> session = pushedStream->Session();
      LOG((" ProcessNewTransaction %p tied to h2 session push %p\n", trans,
           session.get()));
      return session->AddStream(trans, trans->Priority(), nullptr)
                 ? NS_OK
                 : NS_ERROR_UNEXPECTED;
    }
  }

  nsresult rv = NS_OK;
  nsHttpConnectionInfo* ci = trans->ConnectionInfo();
  MOZ_ASSERT(ci);
  MOZ_ASSERT(!ci->IsHttp3() || !(trans->Caps() & NS_HTTP_DISALLOW_HTTP3));

  bool isWildcard = false;
  ConnectionEntry* ent = GetOrCreateConnectionEntry(
      ci, trans->Caps() & NS_HTTP_DISALLOW_HTTP2_PROXY,
      trans->Caps() & NS_HTTP_DISALLOW_SPDY,
      trans->Caps() & NS_HTTP_DISALLOW_HTTP3, &isWildcard);
  MOZ_ASSERT(ent);

  if (gHttpHandler->EchConfigEnabled(ci->IsHttp3())) {
    ent->MaybeUpdateEchConfig(ci);
  }

  ReportProxyTelemetry(ent);

  // Check if the transaction already has a sticky reference to a connection.
  // If so, then we can just use it directly by transferring its reference
  // to the new connection variable instead of searching for a new one

  nsAHttpConnection* wrappedConnection = trans->Connection();
  RefPtr<HttpConnectionBase> conn;
  RefPtr<PendingTransactionInfo> pendingTransInfo;
  if (wrappedConnection) conn = wrappedConnection->TakeHttpConnection();

  if (conn) {
    MOZ_ASSERT(trans->Caps() & NS_HTTP_STICKY_CONNECTION);
    LOG(
        ("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
         "sticky connection=%p\n",
         trans, conn.get()));

    if (!ent->IsInActiveConns(conn)) {
      LOG(
          ("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
           "sticky connection=%p needs to go on the active list\n",
           trans, conn.get()));

      // make sure it isn't on the idle list - we expect this to be an
      // unknown fresh connection
      MOZ_ASSERT(!ent->IsInIdleConnections(conn));
--> --------------------

--> maximum size reached

--> --------------------

96%


¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.37Angebot  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.