Blob Blame History Raw
/* vim:set ts=4 sw=4 sts=4 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/. */

#ifndef nsHttpConnectionMgr_h__
#define nsHttpConnectionMgr_h__

#include "nsHttpConnection.h"
#include "nsHttpTransaction.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "nsClassHashtable.h"
#include "nsDataHashtable.h"
#include "nsAutoPtr.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Attributes.h"
#include "AlternateServices.h"
#include "ARefBase.h"
#include "nsWeakReference.h"
#include "TCPFastOpen.h"

#include "nsINamed.h"
#include "nsIObserver.h"
#include "nsITimer.h"

class nsIHttpUpgradeListener;

namespace mozilla {
namespace net {
class EventTokenBucket;
class NullHttpTransaction;
struct HttpRetParams;

// 8d411b53-54bc-4a99-8b78-ff125eab1564
#define NS_HALFOPENSOCKET_IID                        \
  {                                                  \
    0x8d411b53, 0x54bc, 0x4a99, {                    \
      0x8b, 0x78, 0xff, 0x12, 0x5e, 0xab, 0x15, 0x64 \
    }                                                \
  }

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

// message handlers have this signature
class nsHttpConnectionMgr;
typedef void (nsHttpConnectionMgr::*nsConnEventHandler)(int32_t, ARefBase *);

class nsHttpConnectionMgr final : public nsIObserver, public AltSvcCache {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSIOBSERVER

  // parameter names
  enum nsParamName {
    MAX_URGENT_START_Q,
    MAX_CONNECTIONS,
    MAX_PERSISTENT_CONNECTIONS_PER_HOST,
    MAX_PERSISTENT_CONNECTIONS_PER_PROXY,
    MAX_REQUEST_DELAY,
    THROTTLING_ENABLED,
    THROTTLING_SUSPEND_FOR,
    THROTTLING_RESUME_FOR,
    THROTTLING_READ_LIMIT,
    THROTTLING_READ_INTERVAL,
    THROTTLING_HOLD_TIME,
    THROTTLING_MAX_TIME
  };

  //-------------------------------------------------------------------------
  // NOTE: functions below may only be called on the main thread.
  //-------------------------------------------------------------------------

  nsHttpConnectionMgr();

  MOZ_MUST_USE nsresult
  Init(uint16_t maxUrgentExcessiveConns, uint16_t maxConnections,
       uint16_t maxPersistentConnectionsPerHost,
       uint16_t maxPersistentConnectionsPerProxy, uint16_t maxRequestDelay,
       bool throttleEnabled, uint32_t throttleVersion,
       uint32_t throttleSuspendFor, uint32_t throttleResumeFor,
       uint32_t throttleReadLimit, uint32_t throttleReadInterval,
       uint32_t throttleHoldTime, uint32_t throttleMaxTime);
  MOZ_MUST_USE nsresult Shutdown();

  //-------------------------------------------------------------------------
  // NOTE: functions below may be called on any thread.
  //-------------------------------------------------------------------------

  // Schedules next pruning of dead connection to happen after
  // given time.
  void PruneDeadConnectionsAfter(uint32_t time);

  // this cancels all outstanding transactions but does not shut down the mgr
  void AbortAndCloseAllConnections(int32_t, ARefBase *);

  // Stops timer scheduled for next pruning of dead connections if
  // there are no more idle connections or active spdy ones
  void ConditionallyStopPruneDeadConnectionsTimer();

  // Stops timer used for the read timeout tick if there are no currently
  // active connections.
  void ConditionallyStopTimeoutTick();

  // adds a transaction to the list of managed transactions.
  MOZ_MUST_USE nsresult AddTransaction(nsHttpTransaction *, int32_t priority);

  // called to reschedule the given transaction.  it must already have been
  // added to the connection manager via AddTransaction.
  MOZ_MUST_USE nsresult RescheduleTransaction(nsHttpTransaction *,
                                              int32_t priority);

  // TOOD
  void UpdateClassOfServiceOnTransaction(nsHttpTransaction *,
                                         uint32_t classOfService);

  // cancels a transaction w/ the given reason.
  MOZ_MUST_USE nsresult CancelTransaction(nsHttpTransaction *, nsresult reason);
  MOZ_MUST_USE nsresult CancelTransactions(nsHttpConnectionInfo *,
                                           nsresult reason);

  // called to force the connection manager to prune its list of idle
  // connections.
  MOZ_MUST_USE nsresult PruneDeadConnections();

  // called to close active connections with no registered "traffic"
  MOZ_MUST_USE nsresult PruneNoTraffic();

  // "VerifyTraffic" means marking connections now, and then check again in
  // N seconds to see if there's been any traffic and if not, kill
  // that connection.
  MOZ_MUST_USE nsresult VerifyTraffic();

  // Close all idle persistent connections and prevent any active connections
  // from being reused. Optional connection info resets CI specific
  // information such as Happy Eyeballs history.
  MOZ_MUST_USE nsresult DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *);

  // called to get a reference to the socket transport service.  the socket
  // transport service is not available when the connection manager is down.
  MOZ_MUST_USE nsresult GetSocketThreadTarget(nsIEventTarget **);

  // called to indicate a transaction for the connectionInfo is likely coming
  // soon. The connection manager may use this information to start a TCP
  // and/or SSL level handshake for that resource immediately so that it is
  // ready when the transaction is submitted. No obligation is taken on by the
  // connection manager, nor is the submitter obligated to actually submit a
  // real transaction for this connectionInfo.
  MOZ_MUST_USE nsresult SpeculativeConnect(nsHttpConnectionInfo *,
                                           nsIInterfaceRequestor *,
                                           uint32_t caps = 0,
                                           NullHttpTransaction * = nullptr);

  // called when a connection is done processing a transaction.  if the
  // connection can be reused then it will be added to the idle list, else
  // it will be closed.
  MOZ_MUST_USE nsresult ReclaimConnection(nsHttpConnection *conn);

  // called by the main thread to execute the taketransport() logic on the
  // socket thread after a 101 response has been received and the socket
  // needs to be transferred to an expectant upgrade listener such as
  // websockets.
  MOZ_MUST_USE nsresult CompleteUpgrade(
      nsAHttpConnection *aConn, nsIHttpUpgradeListener *aUpgradeListener);

  // called to update a parameter after the connection manager has already
  // been initialized.
  MOZ_MUST_USE nsresult UpdateParam(nsParamName name, uint16_t value);

  // called from main thread to post a new request token bucket
  // to the socket thread
  MOZ_MUST_USE nsresult UpdateRequestTokenBucket(EventTokenBucket *aBucket);

  // clears the connection history mCT
  MOZ_MUST_USE nsresult ClearConnectionHistory();

  void ReportFailedToProcess(nsIURI *uri);

  // Causes a large amount of connection diagnostic information to be
  // printed to the javascript console
  void PrintDiagnostics();

  //-------------------------------------------------------------------------
  // NOTE: functions below may be called only on the socket thread.
  //-------------------------------------------------------------------------

  // called to change the connection entry associated with conn from specific
  // into a wildcard (i.e. http2 proxy friendy) mapping
  void MoveToWildCardConnEntry(nsHttpConnectionInfo *specificCI,
                               nsHttpConnectionInfo *wildcardCI,
                               nsHttpConnection *conn);

  // called to force the transaction queue to be processed once more, giving
  // preference to the specified connection.
  MOZ_MUST_USE nsresult ProcessPendingQ(nsHttpConnectionInfo *);
  MOZ_MUST_USE bool ProcessPendingQForEntry(nsHttpConnectionInfo *);

  // Try and process all pending transactions
  MOZ_MUST_USE nsresult ProcessPendingQ();

  // This is used to force an idle connection to be closed and removed from
  // the idle connection list. It is called when the idle connection detects
  // that the network peer has closed the transport.
  MOZ_MUST_USE nsresult CloseIdleConnection(nsHttpConnection *);
  MOZ_MUST_USE nsresult RemoveIdleConnection(nsHttpConnection *);

  // The connection manager needs to know when a normal HTTP connection has been
  // upgraded to SPDY because the dispatch and idle semantics are a little
  // bit different.
  void ReportSpdyConnection(nsHttpConnection *, bool usingSpdy);

  bool GetConnectionData(nsTArray<HttpRetParams> *);

  void ResetIPFamilyPreference(nsHttpConnectionInfo *);

  uint16_t MaxRequestDelay() { return mMaxRequestDelay; }

  // public, so that the SPDY/http2 seesions can activate
  void ActivateTimeoutTick();

  nsresult UpdateCurrentTopLevelOuterContentWindowId(uint64_t aWindowId);

  // tracks and untracks active transactions according their throttle status
  void AddActiveTransaction(nsHttpTransaction *aTrans);
  void RemoveActiveTransaction(nsHttpTransaction *aTrans,
                               Maybe<bool> const &aOverride = Nothing());
  void UpdateActiveTransaction(nsHttpTransaction *aTrans);

  // called by nsHttpTransaction::WriteSegments.  decides whether the
  // transaction should limit reading its reponse data.  There are various
  // conditions this methods evaluates.  If called by an active-tab
  // non-throttled transaction, the throttling window time will be prolonged.
  bool ShouldThrottle(nsHttpTransaction *aTrans);

  // prolongs the throttling time window to now + the window preferred delay.
  // called when:
  // - any transaction is activated
  // - or when a currently unthrottled transaction for the active window
  // receives data
  void TouchThrottlingTimeWindow(bool aEnsureTicker = true);

  // return true iff the connection has pending transactions for the active tab.
  // it's mainly used to disallow throttling (limit reading) of a response
  // belonging to the same conn info to free up a connection ASAP.
  // NOTE: relatively expensive to call, there are two hashtable lookups.
  bool IsConnEntryUnderPressure(nsHttpConnectionInfo *);

  uint64_t CurrentTopLevelOuterContentWindowId() {
    return mCurrentTopLevelOuterContentWindowId;
  }

 private:
  virtual ~nsHttpConnectionMgr();

  class nsHalfOpenSocket;
  class PendingTransactionInfo;

  // nsConnectionEntry
  //
  // mCT maps connection info hash key to nsConnectionEntry object, which
  // contains list of active and idle connections as well as the list of
  // pending transactions.
  //
  class nsConnectionEntry {
   public:
    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsConnectionEntry)
    explicit nsConnectionEntry(nsHttpConnectionInfo *ci);

    RefPtr<nsHttpConnectionInfo> mConnInfo;
    nsTArray<RefPtr<PendingTransactionInfo>>
        mUrgentStartQ;  // the urgent start transaction queue

    // This table provides a mapping from top level outer content window id
    // to a queue of pending transaction information.
    // The transaction's order in pending queue is decided by whether it's a
    // blocking transaction and its priority.
    // Note that the window id could be 0 if the http request
    // is initialized without a window.
    nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<PendingTransactionInfo>>>
        mPendingTransactionTable;
    nsTArray<RefPtr<nsHttpConnection>> mActiveConns;  // active connections
    nsTArray<RefPtr<nsHttpConnection>>
        mIdleConns;                           // idle persistent connections
    nsTArray<nsHalfOpenSocket *> mHalfOpens;  // half open connections
    nsTArray<RefPtr<nsHalfOpenSocket>> mHalfOpenFastOpenBackups;  // backup half
                                                                  // open
                                                                  // connections
                                                                  // for
                                                                  // connection
                                                                  // in fast
                                                                  // open phase

    bool AvailableForDispatchNow();

    // calculate the number of half open sockets that have not had at least 1
    // connection complete
    uint32_t UnconnectedHalfOpens();

    // Remove a particular half open socket from the mHalfOpens array
    void RemoveHalfOpen(nsHalfOpenSocket *);

    // Spdy sometimes resolves the address in the socket manager in order
    // to re-coalesce sharded HTTP hosts. The dotted decimal address is
    // combined with the Anonymous flag and OA from the connection information
    // to build the hash key for hosts in the same ip pool.
    //
    nsTArray<nsCString> mCoalescingKeys;

    // To have the UsingSpdy flag means some host with the same connection
    // entry has done NPN=spdy/* at some point. It does not mean every
    // connection is currently using spdy.
    bool mUsingSpdy : 1;

    // Flags to remember our happy-eyeballs decision.
    // Reset only by Ctrl-F5 reload.
    // True when we've first connected an IPv4 server for this host,
    // initially false.
    bool mPreferIPv4 : 1;
    // True when we've first connected an IPv6 server for this host,
    // initially false.
    bool mPreferIPv6 : 1;

    // True if this connection entry has initiated a socket
    bool mUsedForConnection : 1;

    // Try using TCP Fast Open.
    bool mUseFastOpen : 1;

    bool mDoNotDestroy : 1;

    // Set the IP family preference flags according the connected family
    void RecordIPFamilyPreference(uint16_t family);
    // Resets all flags to their default values
    void ResetIPFamilyPreference();

    // Return the count of pending transactions for all window ids.
    size_t PendingQLength() const;

    // Add a transaction information into the pending queue in
    // |mPendingTransactionTable| according to the transaction's
    // top level outer content window id.
    void InsertTransaction(PendingTransactionInfo *info,
                           bool aInsertAsFirstForTheSamePriority = false);

    // Append transactions to the |result| whose window id
    // is equal to |windowId|.
    // NOTE: maxCount == 0 will get all transactions in the queue.
    void AppendPendingQForFocusedWindow(
        uint64_t windowId, nsTArray<RefPtr<PendingTransactionInfo>> &result,
        uint32_t maxCount = 0);

    // Append transactions whose window id isn't equal to |windowId|.
    // NOTE: windowId == 0 will get all transactions for both
    // focused and non-focused windows.
    void AppendPendingQForNonFocusedWindows(
        uint64_t windowId, nsTArray<RefPtr<PendingTransactionInfo>> &result,
        uint32_t maxCount = 0);

    // Remove the empty pendingQ in |mPendingTransactionTable|.
    void RemoveEmptyPendingQ();

   private:
    ~nsConnectionEntry();
  };

 public:
  static nsAHttpConnection *MakeConnectionHandle(nsHttpConnection *aWrapped);
  void RegisterOriginCoalescingKey(nsHttpConnection *, const nsACString &host,
                                   int32_t port);

 private:
  // nsHalfOpenSocket is used to hold the state of an opening TCP socket
  // while we wait for it to establish and bind it to a connection

  class nsHalfOpenSocket final : public nsIOutputStreamCallback,
                                 public nsITransportEventSink,
                                 public nsIInterfaceRequestor,
                                 public nsITimerCallback,
                                 public nsINamed,
                                 public nsSupportsWeakReference,
                                 public TCPFastOpen {
    ~nsHalfOpenSocket();

   public:
    NS_DECLARE_STATIC_IID_ACCESSOR(NS_HALFOPENSOCKET_IID)
    NS_DECL_THREADSAFE_ISUPPORTS
    NS_DECL_NSIOUTPUTSTREAMCALLBACK
    NS_DECL_NSITRANSPORTEVENTSINK
    NS_DECL_NSIINTERFACEREQUESTOR
    NS_DECL_NSITIMERCALLBACK
    NS_DECL_NSINAMED

    nsHalfOpenSocket(nsConnectionEntry *ent, nsAHttpTransaction *trans,
                     uint32_t caps, bool speculative, bool isFromPredictor,
                     bool urgentStart);

    MOZ_MUST_USE nsresult SetupStreams(nsISocketTransport **,
                                       nsIAsyncInputStream **,
                                       nsIAsyncOutputStream **, bool isBackup);
    MOZ_MUST_USE nsresult SetupPrimaryStreams();
    MOZ_MUST_USE nsresult SetupBackupStreams();
    void SetupBackupTimer();
    void CancelBackupTimer();
    void Abandon();
    double Duration(TimeStamp epoch);
    nsISocketTransport *SocketTransport() { return mSocketTransport; }
    nsISocketTransport *BackupTransport() { return mBackupTransport; }

    nsAHttpTransaction *Transaction() { return mTransaction; }

    bool IsSpeculative() { return mSpeculative; }

    bool IsFromPredictor() { return mIsFromPredictor; }

    bool Allow1918() { return mAllow1918; }
    void SetAllow1918(bool val) { mAllow1918 = val; }

    bool HasConnected() { return mHasConnected; }

    void PrintDiagnostics(nsCString &log);

    // Checks whether the transaction can be dispatched using this
    // half-open's connection.  If this half-open is marked as urgent-start,
    // it only accepts urgent start transactions.  Call only before Claim().
    bool AcceptsTransaction(nsHttpTransaction *trans);
    bool Claim();
    void Unclaim();

    bool FastOpenEnabled() override;
    nsresult StartFastOpen() override;
    void SetFastOpenConnected(nsresult, bool aWillRetry) override;
    void FastOpenNotSupported() override;
    void SetFastOpenStatus(uint8_t tfoStatus) override;
    void CancelFastOpenConnection();

   private:
    nsresult SetupConn(nsIAsyncOutputStream *out, bool aFastOpen);

    // To find out whether |mTransaction| is still in the connection entry's
    // pending queue. If the transaction is found and |removeWhenFound| is
    // true, the transaction will be removed from the pending queue.
    already_AddRefed<PendingTransactionInfo> FindTransactionHelper(
        bool removeWhenFound);

    RefPtr<nsAHttpTransaction> mTransaction;
    bool mDispatchedMTransaction;
    nsCOMPtr<nsISocketTransport> mSocketTransport;
    nsCOMPtr<nsIAsyncOutputStream> mStreamOut;
    nsCOMPtr<nsIAsyncInputStream> mStreamIn;
    uint32_t mCaps;

    // mSpeculative is set if the socket was created from
    // SpeculativeConnect(). It is cleared when a transaction would normally
    // start a new connection from scratch but instead finds this one in
    // the half open list and claims it for its own use. (which due to
    // the vagaries of scheduling from the pending queue might not actually
    // match up - but it prevents a speculative connection from opening
    // more connections that are needed.)
    bool mSpeculative;

    // If created with a non-null urgent transaction, remember it, so we can
    // mark the connection as urgent rightaway it's created.
    bool mUrgentStart;

    // mIsFromPredictor is set if the socket originated from the network
    // Predictor. It is used to gather telemetry data on used speculative
    // connections from the predictor.
    bool mIsFromPredictor;

    bool mAllow1918;

    TimeStamp mPrimarySynStarted;
    TimeStamp mBackupSynStarted;

    // mHasConnected tracks whether one of the sockets has completed the
    // connection process. It may have completed unsuccessfully.
    bool mHasConnected;

    bool mPrimaryConnectedOK;
    bool mBackupConnectedOK;
    bool mBackupConnStatsSet;

    // A nsHalfOpenSocket can be made for a concrete non-null transaction,
    // but the transaction can be dispatch to another connection. In that
    // case we can free this transaction to be claimed by other
    // transactions.
    bool mFreeToUse;
    nsresult mPrimaryStreamStatus;

    bool mFastOpenInProgress;
    RefPtr<nsHttpConnection> mConnectionNegotiatingFastOpen;
    uint8_t mFastOpenStatus;

    RefPtr<nsConnectionEntry> mEnt;
    nsCOMPtr<nsITimer> mSynTimer;
    nsCOMPtr<nsISocketTransport> mBackupTransport;
    nsCOMPtr<nsIAsyncOutputStream> mBackupStreamOut;
    nsCOMPtr<nsIAsyncInputStream> mBackupStreamIn;
  };
  friend class nsHalfOpenSocket;

  class PendingTransactionInfo : public ARefBase {
   public:
    explicit PendingTransactionInfo(nsHttpTransaction *trans)
        : mTransaction(trans) {}

    NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PendingTransactionInfo, override)

    void PrintDiagnostics(nsCString &log);

   public:  // meant to be public.
    RefPtr<nsHttpTransaction> mTransaction;
    nsWeakPtr mHalfOpen;
    nsWeakPtr mActiveConn;

   private:
    virtual ~PendingTransactionInfo() {}
  };
  friend class PendingTransactionInfo;

  class PendingComparator {
   public:
    bool Equals(const PendingTransactionInfo *aPendingTrans,
                const nsAHttpTransaction *aTrans) const {
      return aPendingTrans->mTransaction.get() == aTrans;
    }
  };

  //-------------------------------------------------------------------------
  // NOTE: these members may be accessed from any thread (use mReentrantMonitor)
  //-------------------------------------------------------------------------

  ReentrantMonitor mReentrantMonitor;
  nsCOMPtr<nsIEventTarget> mSocketThreadTarget;

  // connection limits
  uint16_t mMaxUrgentExcessiveConns;
  uint16_t mMaxConns;
  uint16_t mMaxPersistConnsPerHost;
  uint16_t mMaxPersistConnsPerProxy;
  uint16_t mMaxRequestDelay;  // in seconds
  bool mThrottleEnabled;
  uint32_t mThrottleVersion;
  uint32_t mThrottleSuspendFor;
  uint32_t mThrottleResumeFor;
  uint32_t mThrottleReadLimit;
  uint32_t mThrottleReadInterval;
  uint32_t mThrottleHoldTime;
  TimeDuration mThrottleMaxTime;
  Atomic<bool, mozilla::Relaxed> mIsShuttingDown;

  //-------------------------------------------------------------------------
  // NOTE: these members are only accessed on the socket transport thread
  //-------------------------------------------------------------------------

  MOZ_MUST_USE bool ProcessPendingQForEntry(nsConnectionEntry *,
                                            bool considerAll);
  bool DispatchPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
                        nsConnectionEntry *ent, bool considerAll);

  // This function selects transactions from mPendingTransactionTable to
  // dispatch according to the following conditions:
  // 1. When ActiveTabPriority() is false, only get transactions from the
  //    queue whose window id is 0.
  // 2. If |considerAll| is false, either get transactions from the focused
  //    window queue or non-focused ones.
  // 3. If |considerAll| is true, fill the |pendingQ| with the transactions from
  //    both focused window and non-focused window queues.
  void PreparePendingQForDispatching(
      nsConnectionEntry *ent,
      nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ, bool considerAll);

  // Return total active connection count, which is the sum of
  // active connections and unconnected half open connections.
  uint32_t TotalActiveConnections(nsConnectionEntry *ent) const;

  // Return |mMaxPersistConnsPerProxy| or |mMaxPersistConnsPerHost|,
  // depending whether the proxy is used.
  uint32_t MaxPersistConnections(nsConnectionEntry *ent) const;

  bool AtActiveConnectionLimit(nsConnectionEntry *, uint32_t caps);
  MOZ_MUST_USE nsresult
  TryDispatchTransaction(nsConnectionEntry *ent, bool onlyReusedConnection,
                         PendingTransactionInfo *pendingTransInfo);
  MOZ_MUST_USE nsresult TryDispatchTransactionOnIdleConn(
      nsConnectionEntry *ent, PendingTransactionInfo *pendingTransInfo,
      bool respectUrgency, bool *allUrgent = nullptr);
  MOZ_MUST_USE nsresult DispatchTransaction(nsConnectionEntry *,
                                            nsHttpTransaction *,
                                            nsHttpConnection *);
  MOZ_MUST_USE nsresult DispatchAbstractTransaction(nsConnectionEntry *,
                                                    nsAHttpTransaction *,
                                                    uint32_t,
                                                    nsHttpConnection *,
                                                    int32_t);
  bool RestrictConnections(nsConnectionEntry *);
  MOZ_MUST_USE nsresult ProcessNewTransaction(nsHttpTransaction *);
  MOZ_MUST_USE nsresult EnsureSocketThreadTarget();
  void ClosePersistentConnections(nsConnectionEntry *ent);
  void ReportProxyTelemetry(nsConnectionEntry *ent);
  MOZ_MUST_USE nsresult
  CreateTransport(nsConnectionEntry *, nsAHttpTransaction *, uint32_t, bool,
                  bool, bool, bool, PendingTransactionInfo *pendingTransInfo);
  void AddActiveConn(nsHttpConnection *, nsConnectionEntry *);
  void DecrementActiveConnCount(nsHttpConnection *);
  void StartedConnect();
  void RecvdConnect();

  // This function will unclaim the claimed connection or set a halfOpen
  // socket to the speculative state if the transaction claiming them ends up
  // using another connection.
  void ReleaseClaimedSockets(nsConnectionEntry *ent,
                             PendingTransactionInfo *pendingTransInfo);

  void InsertTransactionSorted(
      nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
      PendingTransactionInfo *pendingTransInfo,
      bool aInsertAsFirstForTheSamePriority = false);

  nsConnectionEntry *GetOrCreateConnectionEntry(nsHttpConnectionInfo *,
                                                bool allowWildCard);

  MOZ_MUST_USE nsresult MakeNewConnection(
      nsConnectionEntry *ent, PendingTransactionInfo *pendingTransInfo);

  // Manage h2 connection coalescing
  // The hashtable contains arrays of weak pointers to nsHttpConnections
  nsClassHashtable<nsCStringHashKey, nsTArray<nsWeakPtr>> mCoalescingHash;

  nsHttpConnection *FindCoalescableConnection(nsConnectionEntry *ent,
                                              bool justKidding);
  nsHttpConnection *FindCoalescableConnectionByHashKey(nsConnectionEntry *ent,
                                                       const nsCString &key,
                                                       bool justKidding);
  void UpdateCoalescingForNewConn(nsHttpConnection *conn,
                                  nsConnectionEntry *ent);
  nsHttpConnection *GetSpdyActiveConn(nsConnectionEntry *ent);

  void ProcessSpdyPendingQ(nsConnectionEntry *ent);
  void DispatchSpdyPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
                            nsConnectionEntry *ent, nsHttpConnection *conn);
  // used to marshall events to the socket transport thread.
  MOZ_MUST_USE nsresult PostEvent(nsConnEventHandler handler,
                                  int32_t iparam = 0,
                                  ARefBase *vparam = nullptr);

  // Used to close all transactions in the |pendingQ| with the given |reason|.
  // Note that the |pendingQ| will be also cleared.
  void CancelTransactionsHelper(
      nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
      const nsHttpConnectionInfo *ci, const nsConnectionEntry *ent,
      nsresult reason);

  // message handlers
  void OnMsgShutdown(int32_t, ARefBase *);
  void OnMsgShutdownConfirm(int32_t, ARefBase *);
  void OnMsgNewTransaction(int32_t, ARefBase *);
  void OnMsgReschedTransaction(int32_t, ARefBase *);
  void OnMsgUpdateClassOfServiceOnTransaction(int32_t, ARefBase *);
  void OnMsgCancelTransaction(int32_t, ARefBase *);
  void OnMsgCancelTransactions(int32_t, ARefBase *);
  void OnMsgProcessPendingQ(int32_t, ARefBase *);
  void OnMsgPruneDeadConnections(int32_t, ARefBase *);
  void OnMsgSpeculativeConnect(int32_t, ARefBase *);
  void OnMsgReclaimConnection(int32_t, ARefBase *);
  void OnMsgCompleteUpgrade(int32_t, ARefBase *);
  void OnMsgUpdateParam(int32_t, ARefBase *);
  void OnMsgDoShiftReloadConnectionCleanup(int32_t, ARefBase *);
  void OnMsgProcessFeedback(int32_t, ARefBase *);
  void OnMsgProcessAllSpdyPendingQ(int32_t, ARefBase *);
  void OnMsgUpdateRequestTokenBucket(int32_t, ARefBase *);
  void OnMsgVerifyTraffic(int32_t, ARefBase *);
  void OnMsgPruneNoTraffic(int32_t, ARefBase *);
  void OnMsgUpdateCurrentTopLevelOuterContentWindowId(int32_t, ARefBase *);

  // Total number of active connections in all of the ConnectionEntry objects
  // that are accessed from mCT connection table.
  uint16_t mNumActiveConns;
  // Total number of idle connections in all of the ConnectionEntry objects
  // that are accessed from mCT connection table.
  uint16_t mNumIdleConns;
  // Total number of spdy connections which are a subset of the active conns
  uint16_t mNumSpdyActiveConns;
  // Total number of connections in mHalfOpens ConnectionEntry objects
  // that are accessed from mCT connection table
  uint32_t mNumHalfOpenConns;

  // Holds time in seconds for next wake-up to prune dead connections.
  uint64_t mTimeOfNextWakeUp;
  // Timer for next pruning of dead connections.
  nsCOMPtr<nsITimer> mTimer;
  // Timer for pruning stalled connections after changed network.
  nsCOMPtr<nsITimer> mTrafficTimer;
  bool mPruningNoTraffic;

  // A 1s tick to call nsHttpConnection::ReadTimeoutTick on
  // active http/1 connections and check for orphaned half opens.
  // Disabled when there are no active or half open connections.
  nsCOMPtr<nsITimer> mTimeoutTick;
  bool mTimeoutTickArmed;
  uint32_t mTimeoutTickNext;

  //
  // the connection table
  //
  // this table is indexed by connection key.  each entry is a
  // nsConnectionEntry object. It is unlocked and therefore must only
  // be accessed from the socket thread.
  //
  nsRefPtrHashtable<nsCStringHashKey, nsConnectionEntry> mCT;

  // Read Timeout Tick handlers
  void TimeoutTick();

  // For diagnostics
  void OnMsgPrintDiagnostics(int32_t, ARefBase *);

  nsCString mLogData;
  uint64_t mCurrentTopLevelOuterContentWindowId;

  // Called on a pref change
  void SetThrottlingEnabled(bool aEnable);

  // we only want to throttle for a limited amount of time after a new
  // active transaction is added so that we don't block downloads on comet,
  // socket and any kind of longstanding requests that don't need bandwidth.
  // these methods track this time.
  bool InThrottlingTimeWindow();

  // Two hashtalbes keeping track of active transactions regarding window id and
  // throttling. Used by the throttling algorithm to obtain number of
  // transactions for the active tab and for inactive tabs according their
  // throttle status. mActiveTransactions[0] are all unthrottled transactions,
  // mActiveTransactions[1] throttled.
  nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<nsHttpTransaction>>>
      mActiveTransactions[2];

  // V1 specific
  // Whether we are inside the "stop reading" interval, altered by the throttle
  // ticker
  bool mThrottlingInhibitsReading;

  TimeStamp mThrottlingWindowEndsAt;

  // ticker for the 'stop reading'/'resume reading' signal
  nsCOMPtr<nsITimer> mThrottleTicker;
  // Checks if the combination of active transactions requires the ticker.
  bool IsThrottleTickerNeeded();
  // The method also unschedules the delayed resume of background tabs timer
  // if the ticker was about to be scheduled.
  void EnsureThrottleTickerIfNeeded();
  // V1:
  // Drops also the mThrottlingInhibitsReading flag.  Immediate or delayed
  // resume of currently throttled transactions is not affected by this method.
  // V2:
  // Immediate or delayed resume of currently throttled transactions is not
  // affected by this method.
  void DestroyThrottleTicker();
  // V1:
  // Handler for the ticker: alters the mThrottlingInhibitsReading flag.
  // V2:
  // Handler for the ticker: calls ResumeReading() for all throttled
  // transactions.
  void ThrottlerTick();

  // mechanism to delay immediate resume of background tabs and chrome initiated
  // throttled transactions after the last transaction blocking their unthrottle
  // has been removed.  Needs to be delayed because during a page load there is
  // a number of intervals when there is no transaction that would cause
  // throttling. Hence, throttling of long standing responses, like downloads,
  // would be mostly ineffective if resumed during every such interval.
  nsCOMPtr<nsITimer> mDelayedResumeReadTimer;
  // Schedule the resume
  void DelayedResumeBackgroundThrottledTransactions();
  // Simply destroys the timer
  void CancelDelayedResumeBackgroundThrottledTransactions();
  // Handler for the timer: resumes all background throttled transactions
  void ResumeBackgroundThrottledTransactions();

  // Simple helpers, iterates the given hash/array and resume.
  // @param excludeActive: skip active tabid transactions.
  void ResumeReadOf(
      nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<nsHttpTransaction>>> &,
      bool excludeActive = false);
  void ResumeReadOf(nsTArray<RefPtr<nsHttpTransaction>> *);

  // Cached status of the active tab active transactions existence,
  // saves a lot of hashtable lookups
  bool mActiveTabTransactionsExist;
  bool mActiveTabUnthrottledTransactionsExist;

  void LogActiveTransactions(char);

  nsTArray<RefPtr<PendingTransactionInfo>> *GetTransactionPendingQHelper(
      nsConnectionEntry *ent, nsAHttpTransaction *trans);

  // When current active tab is changed, this function uses
  // |previousWindowId| to select background transactions and
  // mCurrentTopLevelOuterContentWindowId| to select foreground transactions.
  // Then, it notifies selected transactions' connection of the new active tab
  // id.
  void NotifyConnectionOfWindowIdChange(uint64_t previousWindowId);
};

NS_DEFINE_STATIC_IID_ACCESSOR(nsHttpConnectionMgr::nsHalfOpenSocket,
                              NS_HALFOPENSOCKET_IID)

}  // namespace net
}  // namespace mozilla

#endif  // !nsHttpConnectionMgr_h__