LCOV - code coverage report
Current view: top level - netwerk/protocol/http - nsHttpConnectionMgr.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 836 2354 35.5 %
Date: 2017-07-14 16:53:18 Functions: 107 203 52.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* vim:set ts=4 sw=4 sts=4 et cin: */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : // HttpLog.h should generally be included first
       7             : #include "HttpLog.h"
       8             : 
       9             : // Log on level :5, instead of default :4.
      10             : #undef LOG
      11             : #define LOG(args) LOG5(args)
      12             : #undef LOG_ENABLED
      13             : #define LOG_ENABLED() LOG5_ENABLED()
      14             : 
      15             : #include "nsHttpConnectionMgr.h"
      16             : #include "nsHttpConnection.h"
      17             : #include "nsHttpHandler.h"
      18             : #include "nsIHttpChannelInternal.h"
      19             : #include "nsNetCID.h"
      20             : #include "nsCOMPtr.h"
      21             : #include "nsNetUtil.h"
      22             : #include "mozilla/net/DNS.h"
      23             : #include "nsISocketTransport.h"
      24             : #include "nsISSLSocketControl.h"
      25             : #include "mozilla/Services.h"
      26             : #include "mozilla/Telemetry.h"
      27             : #include "mozilla/net/DashboardTypes.h"
      28             : #include "NullHttpTransaction.h"
      29             : #include "nsIDNSRecord.h"
      30             : #include "nsITransport.h"
      31             : #include "nsInterfaceRequestorAgg.h"
      32             : #include "nsIRequestContext.h"
      33             : #include "nsISocketTransportService.h"
      34             : #include <algorithm>
      35             : #include "mozilla/ChaosMode.h"
      36             : #include "mozilla/SizePrintfMacros.h"
      37             : #include "mozilla/Unused.h"
      38             : #include "nsIURI.h"
      39             : 
      40             : #include "mozilla/Move.h"
      41             : #include "mozilla/Telemetry.h"
      42             : 
      43             : namespace mozilla {
      44             : namespace net {
      45             : 
      46             : //-----------------------------------------------------------------------------
      47             : 
      48          46 : NS_IMPL_ISUPPORTS(nsHttpConnectionMgr, nsIObserver)
      49             : 
      50             : // This function decides the transaction's order in the pending queue.
      51             : // Given two transactions t1 and t2, returning true means that t2 is
      52             : // more important than t1 and thus should be dispatched first.
      53             : static bool
      54           0 : TransactionComparator(nsHttpTransaction *t1, nsHttpTransaction *t2)
      55             : {
      56             :     bool t1Blocking =
      57           0 :         t1->Caps() & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED);
      58             :     bool t2Blocking =
      59           0 :         t2->Caps() & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED);
      60             : 
      61           0 :     if (t1Blocking > t2Blocking) {
      62           0 :         return false;
      63             :     }
      64             : 
      65           0 :     if (t2Blocking > t1Blocking) {
      66           0 :         return true;
      67             :     }
      68             : 
      69           0 :     return t1->Priority() >= t2->Priority();
      70             : }
      71             : 
      72             : void
      73           3 : nsHttpConnectionMgr::InsertTransactionSorted(nsTArray<RefPtr<nsHttpConnectionMgr::PendingTransactionInfo> > &pendingQ,
      74             :                                              nsHttpConnectionMgr::PendingTransactionInfo *pendingTransInfo,
      75             :                                              bool aInsertAsFirstForTheSamePriority /*= false*/)
      76             : {
      77             :     // insert the transaction into the front of the queue based on following rules:
      78             :     // 1. The transaction has NS_HTTP_LOAD_AS_BLOCKING or NS_HTTP_LOAD_UNBLOCKED.
      79             :     // 2. The transaction's priority is higher.
      80             :     //
      81             :     // search in reverse order under the assumption that many of the
      82             :     // existing transactions will have the same priority (usually 0).
      83             : 
      84           3 :     nsHttpTransaction *trans = pendingTransInfo->mTransaction;
      85             : 
      86           3 :     for (int32_t i = pendingQ.Length() - 1; i >= 0; --i) {
      87           0 :         nsHttpTransaction *t = pendingQ[i]->mTransaction;
      88           0 :         if (TransactionComparator(trans, t)) {
      89           0 :             if (ChaosMode::isActive(ChaosFeature::NetworkScheduling) || aInsertAsFirstForTheSamePriority) {
      90             :                 int32_t samePriorityCount;
      91           0 :                 for (samePriorityCount = 0; i - samePriorityCount >= 0; ++samePriorityCount) {
      92           0 :                     if (pendingQ[i - samePriorityCount]->mTransaction->Priority() != trans->Priority()) {
      93           0 :                         break;
      94             :                     }
      95             :                 }
      96           0 :                 if (aInsertAsFirstForTheSamePriority) {
      97           0 :                     i -= samePriorityCount;
      98             :                 } else {
      99             :                     // skip over 0...all of the elements with the same priority.
     100           0 :                     i -= ChaosMode::randomUint32LessThan(samePriorityCount + 1);
     101             :                 }
     102             :             }
     103           0 :             pendingQ.InsertElementAt(i+1, pendingTransInfo);
     104           0 :             return;
     105             :         }
     106             :     }
     107           3 :     pendingQ.InsertElementAt(0, pendingTransInfo);
     108             : }
     109             : 
     110             : //-----------------------------------------------------------------------------
     111             : 
     112           1 : nsHttpConnectionMgr::nsHttpConnectionMgr()
     113             :     : mReentrantMonitor("nsHttpConnectionMgr.mReentrantMonitor")
     114             :     , mMaxUrgentExcessiveConns(0)
     115             :     , mMaxConns(0)
     116             :     , mMaxPersistConnsPerHost(0)
     117             :     , mMaxPersistConnsPerProxy(0)
     118             :     , mMaxRequestDelay(0)
     119             :     , mThrottleEnabled(false)
     120             :     , mThrottleSuspendFor(0)
     121             :     , mThrottleResumeFor(0)
     122             :     , mThrottleResumeIn(0)
     123             :     , mIsShuttingDown(false)
     124             :     , mNumActiveConns(0)
     125             :     , mNumIdleConns(0)
     126             :     , mNumSpdyActiveConns(0)
     127             :     , mNumHalfOpenConns(0)
     128             :     , mTimeOfNextWakeUp(UINT64_MAX)
     129             :     , mPruningNoTraffic(false)
     130             :     , mTimeoutTickArmed(false)
     131             :     , mTimeoutTickNext(1)
     132             :     , mCurrentTopLevelOuterContentWindowId(0)
     133             :     , mThrottlingInhibitsReading(false)
     134             :     , mActiveTabTransactionsExist(false)
     135           1 :     , mActiveTabUnthrottledTransactionsExist(false)
     136             : {
     137           1 :     LOG(("Creating nsHttpConnectionMgr @%p\n", this));
     138           1 : }
     139             : 
     140           0 : nsHttpConnectionMgr::~nsHttpConnectionMgr()
     141             : {
     142           0 :     LOG(("Destroying nsHttpConnectionMgr @%p\n", this));
     143           0 :     MOZ_ASSERT(mCoalescingHash.Count() == 0);
     144           0 :     if (mTimeoutTick)
     145           0 :         mTimeoutTick->Cancel();
     146           0 : }
     147             : 
     148             : nsresult
     149          17 : nsHttpConnectionMgr::EnsureSocketThreadTarget()
     150             : {
     151          34 :     nsCOMPtr<nsIEventTarget> sts;
     152          34 :     nsCOMPtr<nsIIOService> ioService = services::GetIOService();
     153          17 :     if (ioService) {
     154             :         nsCOMPtr<nsISocketTransportService> realSTS =
     155          34 :             services::GetSocketTransportService();
     156          17 :         sts = do_QueryInterface(realSTS);
     157             :     }
     158             : 
     159          34 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     160             : 
     161             :     // do nothing if already initialized or if we've shut down
     162          17 :     if (mSocketThreadTarget || mIsShuttingDown)
     163          16 :         return NS_OK;
     164             : 
     165           1 :     mSocketThreadTarget = sts;
     166             : 
     167           1 :     return sts ? NS_OK : NS_ERROR_NOT_AVAILABLE;
     168             : }
     169             : 
     170             : nsresult
     171           1 : nsHttpConnectionMgr::Init(uint16_t maxUrgentExcessiveConns,
     172             :                           uint16_t maxConns,
     173             :                           uint16_t maxPersistConnsPerHost,
     174             :                           uint16_t maxPersistConnsPerProxy,
     175             :                           uint16_t maxRequestDelay,
     176             :                           bool throttleEnabled,
     177             :                           uint32_t throttleSuspendFor,
     178             :                           uint32_t throttleResumeFor,
     179             :                           uint32_t throttleResumeIn)
     180             : {
     181           1 :     LOG(("nsHttpConnectionMgr::Init\n"));
     182             : 
     183             :     {
     184           2 :         ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     185             : 
     186           1 :         mMaxUrgentExcessiveConns = maxUrgentExcessiveConns;
     187           1 :         mMaxConns = maxConns;
     188           1 :         mMaxPersistConnsPerHost = maxPersistConnsPerHost;
     189           1 :         mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
     190           1 :         mMaxRequestDelay = maxRequestDelay;
     191             : 
     192           1 :         mThrottleEnabled = throttleEnabled;
     193           1 :         mThrottleSuspendFor = throttleSuspendFor;
     194           1 :         mThrottleResumeFor = throttleResumeFor;
     195           1 :         mThrottleResumeIn = throttleResumeIn;
     196             : 
     197           1 :         mIsShuttingDown = false;
     198             :     }
     199             : 
     200           1 :     return EnsureSocketThreadTarget();
     201             : }
     202             : 
     203             : class BoolWrapper : public ARefBase
     204             : {
     205             : public:
     206           0 :     BoolWrapper() : mBool(false) {}
     207           0 :     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BoolWrapper)
     208             : 
     209             : public: // intentional!
     210             :     bool mBool;
     211             : 
     212             : private:
     213           0 :     virtual ~BoolWrapper() {}
     214             : };
     215             : 
     216             : nsresult
     217           0 : nsHttpConnectionMgr::Shutdown()
     218             : {
     219           0 :     LOG(("nsHttpConnectionMgr::Shutdown\n"));
     220             : 
     221           0 :     RefPtr<BoolWrapper> shutdownWrapper = new BoolWrapper();
     222             :     {
     223           0 :         ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     224             : 
     225             :         // do nothing if already shutdown
     226           0 :         if (!mSocketThreadTarget)
     227           0 :             return NS_OK;
     228             : 
     229           0 :         nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgShutdown,
     230           0 :                                 0, shutdownWrapper);
     231             : 
     232             :         // release our reference to the STS to prevent further events
     233             :         // from being posted.  this is how we indicate that we are
     234             :         // shutting down.
     235           0 :         mIsShuttingDown = true;
     236           0 :         mSocketThreadTarget = nullptr;
     237             : 
     238           0 :         if (NS_FAILED(rv)) {
     239           0 :             NS_WARNING("unable to post SHUTDOWN message");
     240           0 :             return rv;
     241             :         }
     242             :     }
     243             : 
     244             :     // wait for shutdown event to complete
     245           0 :     SpinEventLoopUntil([&, shutdownWrapper]() { return shutdownWrapper->mBool; });
     246             : 
     247           0 :     return NS_OK;
     248             : }
     249             : 
     250             : class ConnEvent : public Runnable
     251             : {
     252             : public:
     253          16 :   ConnEvent(nsHttpConnectionMgr* mgr,
     254             :             nsConnEventHandler handler,
     255             :             int32_t iparam,
     256             :             ARefBase* vparam)
     257          16 :     : Runnable("net::ConnEvent")
     258             :     , mMgr(mgr)
     259             :     , mHandler(handler)
     260             :     , mIParam(iparam)
     261          16 :     , mVParam(vparam)
     262             :   {
     263          16 :   }
     264             : 
     265          16 :   NS_IMETHOD Run() override
     266             :   {
     267          16 :     (mMgr->*mHandler)(mIParam, mVParam);
     268          16 :     return NS_OK;
     269             :     }
     270             : 
     271             : private:
     272          48 :     virtual ~ConnEvent() {}
     273             : 
     274             :     RefPtr<nsHttpConnectionMgr>  mMgr;
     275             :     nsConnEventHandler           mHandler;
     276             :     int32_t                      mIParam;
     277             :     RefPtr<ARefBase>             mVParam;
     278             : };
     279             : 
     280             : nsresult
     281          16 : nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler,
     282             :                                int32_t iparam, ARefBase *vparam)
     283             : {
     284          16 :     Unused << EnsureSocketThreadTarget();
     285             : 
     286          32 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     287             : 
     288             :     nsresult rv;
     289          16 :     if (!mSocketThreadTarget) {
     290           0 :         NS_WARNING("cannot post event if not initialized");
     291           0 :         rv = NS_ERROR_NOT_INITIALIZED;
     292             :     }
     293             :     else {
     294          32 :         nsCOMPtr<nsIRunnable> event = new ConnEvent(this, handler, iparam, vparam);
     295          16 :         rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
     296             :     }
     297          32 :     return rv;
     298             : }
     299             : 
     300             : void
     301           1 : nsHttpConnectionMgr::PruneDeadConnectionsAfter(uint32_t timeInSeconds)
     302             : {
     303           1 :     LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));
     304             : 
     305           1 :     if(!mTimer)
     306           1 :         mTimer = do_CreateInstance("@mozilla.org/timer;1");
     307             : 
     308             :     // failure to create a timer is not a fatal error, but idle connections
     309             :     // will not be cleaned up until we try to use them.
     310           1 :     if (mTimer) {
     311           1 :         mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
     312           1 :         mTimer->Init(this, timeInSeconds*1000, nsITimer::TYPE_ONE_SHOT);
     313             :     } else {
     314           0 :         NS_WARNING("failed to create: timer for pruning the dead connections!");
     315             :     }
     316           1 : }
     317             : 
     318             : void
     319           0 : nsHttpConnectionMgr::ConditionallyStopPruneDeadConnectionsTimer()
     320             : {
     321             :     // Leave the timer in place if there are connections that potentially
     322             :     // need management
     323           0 :     if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
     324           0 :         return;
     325             : 
     326           0 :     LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));
     327             : 
     328             :     // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
     329           0 :     mTimeOfNextWakeUp = UINT64_MAX;
     330           0 :     if (mTimer) {
     331           0 :         mTimer->Cancel();
     332           0 :         mTimer = nullptr;
     333             :     }
     334             : }
     335             : 
     336             : void
     337           6 : nsHttpConnectionMgr::ConditionallyStopTimeoutTick()
     338             : {
     339           6 :     LOG(("nsHttpConnectionMgr::ConditionallyStopTimeoutTick "
     340             :          "armed=%d active=%d\n", mTimeoutTickArmed, mNumActiveConns));
     341             : 
     342           6 :     if (!mTimeoutTickArmed)
     343           0 :         return;
     344             : 
     345           6 :     if (mNumActiveConns)
     346           0 :         return;
     347             : 
     348           6 :     LOG(("nsHttpConnectionMgr::ConditionallyStopTimeoutTick stop==true\n"));
     349             : 
     350           6 :     mTimeoutTick->Cancel();
     351           6 :     mTimeoutTickArmed = false;
     352             : }
     353             : 
     354             : //-----------------------------------------------------------------------------
     355             : // nsHttpConnectionMgr::nsIObserver
     356             : //-----------------------------------------------------------------------------
     357             : 
     358             : NS_IMETHODIMP
     359           0 : nsHttpConnectionMgr::Observe(nsISupports *subject,
     360             :                              const char *topic,
     361             :                              const char16_t *data)
     362             : {
     363           0 :     LOG(("nsHttpConnectionMgr::Observe [topic=\"%s\"]\n", topic));
     364             : 
     365           0 :     if (0 == strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
     366           0 :         nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
     367           0 :         if (timer == mTimer) {
     368           0 :             Unused << PruneDeadConnections();
     369           0 :         } else if (timer == mTimeoutTick) {
     370           0 :             TimeoutTick();
     371           0 :         } else if (timer == mTrafficTimer) {
     372           0 :             Unused << PruneNoTraffic();
     373           0 :         } else if (timer == mThrottleTicker) {
     374           0 :             ThrottlerTick();
     375           0 :         } else if (timer == mDelayedResumeReadTimer) {
     376           0 :             ResumeBackgroundThrottledTransactions();
     377             :         } else {
     378           0 :             MOZ_ASSERT(false, "unexpected timer-callback");
     379             :             LOG(("Unexpected timer object\n"));
     380             :             return NS_ERROR_UNEXPECTED;
     381             :         }
     382             :     }
     383             : 
     384           0 :     return NS_OK;
     385             : }
     386             : 
     387             : 
     388             : //-----------------------------------------------------------------------------
     389             : 
     390             : nsresult
     391           3 : nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, int32_t priority)
     392             : {
     393           3 :     LOG(("nsHttpConnectionMgr::AddTransaction [trans=%p %d]\n", trans, priority));
     394           3 :     return PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
     395             : }
     396             : 
     397             : nsresult
     398           0 : nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, int32_t priority)
     399             : {
     400           0 :     LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%p %d]\n", trans, priority));
     401           0 :     return PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority, trans);
     402             : }
     403             : 
     404             : void
     405           0 : nsHttpConnectionMgr::UpdateClassOfServiceOnTransaction(nsHttpTransaction *trans, uint32_t classOfService)
     406             : {
     407           0 :     LOG(("nsHttpConnectionMgr::UpdateClassOfServiceOnTransaction [trans=%p classOfService=%" PRIu32 "]\n",
     408             :          trans, static_cast<uint32_t>(classOfService)));
     409           0 :     Unused << PostEvent(&nsHttpConnectionMgr::OnMsgUpdateClassOfServiceOnTransaction,
     410             :                         static_cast<int32_t>(classOfService), trans);
     411           0 : }
     412             : 
     413             : nsresult
     414           0 : nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
     415             : {
     416           0 :     LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%p reason=%" PRIx32 "]\n",
     417             :          trans, static_cast<uint32_t>(reason)));
     418           0 :     return PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction,
     419           0 :                      static_cast<int32_t>(reason), trans);
     420             : }
     421             : 
     422             : nsresult
     423           0 : nsHttpConnectionMgr::PruneDeadConnections()
     424             : {
     425           0 :     return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
     426             : }
     427             : 
     428             : //
     429             : // Called after a timeout. Check for active connections that have had no
     430             : // traffic since they were "marked" and nuke them.
     431             : nsresult
     432           0 : nsHttpConnectionMgr::PruneNoTraffic()
     433             : {
     434           0 :     LOG(("nsHttpConnectionMgr::PruneNoTraffic\n"));
     435           0 :     mPruningNoTraffic = true;
     436           0 :     return PostEvent(&nsHttpConnectionMgr::OnMsgPruneNoTraffic);
     437             : }
     438             : 
     439             : nsresult
     440           0 : nsHttpConnectionMgr::VerifyTraffic()
     441             : {
     442           0 :     LOG(("nsHttpConnectionMgr::VerifyTraffic\n"));
     443           0 :     return PostEvent(&nsHttpConnectionMgr::OnMsgVerifyTraffic);
     444             : }
     445             : 
     446             : nsresult
     447           0 : nsHttpConnectionMgr::DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *aCI)
     448             : {
     449           0 :     return PostEvent(&nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup,
     450           0 :                      0, aCI);
     451             : }
     452             : 
     453             : class SpeculativeConnectArgs : public ARefBase
     454             : {
     455             : public:
     456           5 :     SpeculativeConnectArgs() { mOverridesOK = false; }
     457          20 :     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SpeculativeConnectArgs)
     458             : 
     459             : public: // intentional!
     460             :     RefPtr<NullHttpTransaction> mTrans;
     461             : 
     462             :     bool mOverridesOK;
     463             :     uint32_t mParallelSpeculativeConnectLimit;
     464             :     bool mIgnoreIdle;
     465             :     bool mIsFromPredictor;
     466             :     bool mAllow1918;
     467             : 
     468             : private:
     469          15 :     virtual ~SpeculativeConnectArgs() {}
     470             :     NS_DECL_OWNINGTHREAD
     471             : };
     472             : 
     473             : nsresult
     474           5 : nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
     475             :                                         nsIInterfaceRequestor *callbacks,
     476             :                                         uint32_t caps,
     477             :                                         NullHttpTransaction *nullTransaction)
     478             : {
     479           5 :     MOZ_ASSERT(NS_IsMainThread(), "nsHttpConnectionMgr::SpeculativeConnect called off main thread!");
     480             : 
     481           5 :     if (!IsNeckoChild()) {
     482             :         // HACK: make sure PSM gets initialized on the main thread.
     483           5 :         net_EnsurePSMInit();
     484             :     }
     485             : 
     486           5 :     LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n",
     487             :          ci->HashKey().get()));
     488             : 
     489             :     nsCOMPtr<nsISpeculativeConnectionOverrider> overrider =
     490          10 :         do_GetInterface(callbacks);
     491             : 
     492           5 :     bool allow1918 = overrider ? overrider->GetAllow1918() : false;
     493             : 
     494             :     // Hosts that are Local IP Literals should not be speculatively
     495             :     // connected - Bug 853423.
     496           5 :     if ((!allow1918) && ci && ci->HostIsLocalIPLiteral()) {
     497           0 :         LOG(("nsHttpConnectionMgr::SpeculativeConnect skipping RFC1918 "
     498             :              "address [%s]", ci->Origin()));
     499           0 :         return NS_OK;
     500             :     }
     501             : 
     502          10 :     RefPtr<SpeculativeConnectArgs> args = new SpeculativeConnectArgs();
     503             : 
     504             :     // Wrap up the callbacks and the target to ensure they're released on the target
     505             :     // thread properly.
     506          10 :     nsCOMPtr<nsIInterfaceRequestor> wrappedCallbacks;
     507           5 :     NS_NewInterfaceRequestorAggregation(callbacks, nullptr, getter_AddRefs(wrappedCallbacks));
     508             : 
     509           5 :     caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
     510           5 :     caps |= NS_HTTP_ERROR_SOFTLY;
     511           5 :     args->mTrans =
     512          15 :         nullTransaction ? nullTransaction : new NullHttpTransaction(ci, wrappedCallbacks, caps);
     513             : 
     514           5 :     if (overrider) {
     515           0 :         args->mOverridesOK = true;
     516           0 :         args->mParallelSpeculativeConnectLimit =
     517           0 :             overrider->GetParallelSpeculativeConnectLimit();
     518           0 :         args->mIgnoreIdle = overrider->GetIgnoreIdle();
     519           0 :         args->mIsFromPredictor = overrider->GetIsFromPredictor();
     520           0 :         args->mAllow1918 = overrider->GetAllow1918();
     521             :     }
     522             : 
     523           5 :     return PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, args);
     524             : }
     525             : 
     526             : nsresult
     527           0 : nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget **target)
     528             : {
     529           0 :     Unused << EnsureSocketThreadTarget();
     530             : 
     531           0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     532           0 :     nsCOMPtr<nsIEventTarget> temp(mSocketThreadTarget);
     533           0 :     temp.forget(target);
     534           0 :     return NS_OK;
     535             : }
     536             : 
     537             : nsresult
     538           3 : nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
     539             : {
     540           3 :     LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%p]\n", conn));
     541           3 :     return PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
     542             : }
     543             : 
     544             : // A structure used to marshall 2 pointers across the various necessary
     545             : // threads to complete an HTTP upgrade.
     546             : class nsCompleteUpgradeData : public ARefBase
     547             : {
     548             : public:
     549           0 :     nsCompleteUpgradeData(nsAHttpConnection *aConn,
     550             :                           nsIHttpUpgradeListener *aListener)
     551           0 :         : mConn(aConn)
     552           0 :         , mUpgradeListener(aListener) { }
     553             : 
     554           0 :     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsCompleteUpgradeData)
     555             : 
     556             :     RefPtr<nsAHttpConnection> mConn;
     557             :     nsCOMPtr<nsIHttpUpgradeListener> mUpgradeListener;
     558             : private:
     559           0 :     virtual ~nsCompleteUpgradeData() { }
     560             : };
     561             : 
     562             : nsresult
     563           0 : nsHttpConnectionMgr::CompleteUpgrade(nsAHttpConnection *aConn,
     564             :                                      nsIHttpUpgradeListener *aUpgradeListener)
     565             : {
     566             :     RefPtr<nsCompleteUpgradeData> data =
     567           0 :         new nsCompleteUpgradeData(aConn, aUpgradeListener);
     568           0 :     return PostEvent(&nsHttpConnectionMgr::OnMsgCompleteUpgrade, 0, data);
     569             : }
     570             : 
     571             : nsresult
     572           0 : nsHttpConnectionMgr::UpdateParam(nsParamName name, uint16_t value)
     573             : {
     574           0 :     uint32_t param = (uint32_t(name) << 16) | uint32_t(value);
     575           0 :     return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam,
     576           0 :                      static_cast<int32_t>(param), nullptr);
     577             : }
     578             : 
     579             : nsresult
     580           3 : nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
     581             : {
     582           3 :     LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
     583           3 :     return PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
     584             : }
     585             : 
     586             : nsresult
     587           0 : nsHttpConnectionMgr::ProcessPendingQ()
     588             : {
     589           0 :     LOG(("nsHttpConnectionMgr::ProcessPendingQ [All CI]\n"));
     590           0 :     return PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, nullptr);
     591             : }
     592             : 
     593             : void
     594           1 : nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket(int32_t, ARefBase *param)
     595             : {
     596           1 :     EventTokenBucket *tokenBucket = static_cast<EventTokenBucket *>(param);
     597           1 :     gHttpHandler->SetRequestTokenBucket(tokenBucket);
     598           1 : }
     599             : 
     600             : nsresult
     601           1 : nsHttpConnectionMgr::UpdateRequestTokenBucket(EventTokenBucket *aBucket)
     602             : {
     603             :     // Call From main thread when a new EventTokenBucket has been made in order
     604             :     // to post the new value to the socket thread.
     605           1 :     return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket,
     606           1 :                      0, aBucket);
     607             : }
     608             : 
     609             : nsresult
     610           0 : nsHttpConnectionMgr::ClearConnectionHistory()
     611             : {
     612           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     613             : 
     614           0 :     for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
     615           0 :         nsAutoPtr<nsConnectionEntry>& ent = iter.Data();
     616           0 :         if (ent->mIdleConns.Length()    == 0 &&
     617           0 :             ent->mActiveConns.Length()  == 0 &&
     618           0 :             ent->mHalfOpens.Length()    == 0 &&
     619           0 :             ent->mUrgentStartQ.Length() == 0 &&
     620           0 :             ent->PendingQLength()       == 0 &&
     621           0 :             ent->mHalfOpenFastOpenBackups.Length() == 0 &&
     622           0 :             !ent->mDoNotDestroy) {
     623           0 :             iter.Remove();
     624             :         }
     625             :     }
     626             : 
     627           0 :     return NS_OK;
     628             : }
     629             : 
     630             : nsresult
     631           0 : nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
     632             : {
     633           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     634           0 :     LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p",
     635             :          this, conn));
     636             : 
     637           0 :     if (!conn->ConnectionInfo()) {
     638           0 :         return NS_ERROR_UNEXPECTED;
     639             :     }
     640             : 
     641           0 :     nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
     642             : 
     643           0 :     RefPtr<nsHttpConnection> deleteProtector(conn);
     644           0 :     if (!ent || !ent->mIdleConns.RemoveElement(conn))
     645           0 :         return NS_ERROR_UNEXPECTED;
     646             : 
     647           0 :     conn->Close(NS_ERROR_ABORT);
     648           0 :     mNumIdleConns--;
     649           0 :     ConditionallyStopPruneDeadConnectionsTimer();
     650           0 :     return NS_OK;
     651             : }
     652             : 
     653             : nsresult
     654           0 : nsHttpConnectionMgr::RemoveIdleConnection(nsHttpConnection *conn)
     655             : {
     656           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     657             : 
     658           0 :     LOG(("nsHttpConnectionMgr::RemoveIdleConnection %p conn=%p",
     659             :          this, conn));
     660             : 
     661           0 :     if (!conn->ConnectionInfo()) {
     662           0 :         return NS_ERROR_UNEXPECTED;
     663             :     }
     664             : 
     665           0 :     nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
     666             : 
     667           0 :     if (!ent || !ent->mIdleConns.RemoveElement(conn)) {
     668           0 :         return NS_ERROR_UNEXPECTED;
     669             :     }
     670             : 
     671           0 :     mNumIdleConns--;
     672           0 :     ConditionallyStopPruneDeadConnectionsTimer();
     673           0 :     return NS_OK;
     674             : }
     675             : 
     676             : nsHttpConnection *
     677          12 : nsHttpConnectionMgr::FindCoalescableConnectionByHashKey(nsConnectionEntry *ent,
     678             :                                                         const nsCString &key,
     679             :                                                         bool justKidding)
     680             : {
     681          12 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     682          12 :     MOZ_ASSERT(ent->mConnInfo);
     683          12 :     nsHttpConnectionInfo *ci = ent->mConnInfo;
     684             : 
     685          12 :     nsTArray<nsWeakPtr> *listOfWeakConns =  mCoalescingHash.Get(key);
     686          12 :     if (!listOfWeakConns) {
     687          12 :         return nullptr;
     688             :     }
     689             : 
     690           0 :     uint32_t listLen = listOfWeakConns->Length();
     691           0 :     for (uint32_t j = 0; j < listLen; ) {
     692             : 
     693           0 :         RefPtr<nsHttpConnection> potentialMatch = do_QueryReferent(listOfWeakConns->ElementAt(j));
     694           0 :         if (!potentialMatch) {
     695             :             // This is a connection that needs to be removed from the list
     696           0 :             LOG(("FindCoalescableConnectionByHashKey() found old conn %p that has null weak ptr - removing\n",
     697             :                  listOfWeakConns->ElementAt(j).get()));
     698           0 :             if (j != listLen - 1) {
     699           0 :                 listOfWeakConns->Elements()[j] = listOfWeakConns->Elements()[listLen - 1];
     700             :             }
     701           0 :             listOfWeakConns->RemoveElementAt(listLen - 1);
     702           0 :             MOZ_ASSERT(listOfWeakConns->Length() == listLen - 1);
     703           0 :             listLen--;
     704           0 :             continue; // without adjusting iterator
     705             :         }
     706             : 
     707             :         bool couldJoin;
     708           0 :         if (justKidding) {
     709           0 :             couldJoin = potentialMatch->TestJoinConnection(ci->GetOrigin(), ci->OriginPort());
     710             :         } else {
     711           0 :             couldJoin = potentialMatch->JoinConnection(ci->GetOrigin(), ci->OriginPort());
     712             :         }
     713           0 :         if (couldJoin) {
     714           0 :             LOG(("FindCoalescableConnectionByHashKey() found match conn=%p key=%s newCI=%s matchedCI=%s join ok\n",
     715             :                  potentialMatch.get(), key.get(), ci->HashKey().get(), potentialMatch->ConnectionInfo()->HashKey().get()));
     716           0 :             return potentialMatch.get();
     717             :         } else {
     718           0 :             LOG(("FindCoalescableConnectionByHashKey() found match conn=%p key=%s newCI=%s matchedCI=%s join failed\n",
     719             :                  potentialMatch.get(), key.get(), ci->HashKey().get(), potentialMatch->ConnectionInfo()->HashKey().get()));
     720             :         }
     721           0 :         ++j; // bypassed by continue when weakptr fails
     722             :     }
     723             : 
     724           0 :     if (!listLen) { // shrunk to 0 while iterating
     725           0 :         LOG(("FindCoalescableConnectionByHashKey() removing empty list element\n"));
     726           0 :         mCoalescingHash.Remove(key);
     727             :     }
     728           0 :     return nullptr;
     729             : }
     730             : 
     731             : static void
     732          12 : BuildOriginFrameHashKey(nsACString &newKey, nsHttpConnectionInfo *ci,
     733             :                         const nsACString &host, int32_t port)
     734             : {
     735          12 :     newKey.Assign(host);
     736          12 :     if (ci->GetAnonymous()) {
     737           0 :         newKey.AppendLiteral("~A:");
     738             :     } else {
     739          12 :         newKey.AppendLiteral("~.:");
     740             :     }
     741          12 :     newKey.AppendInt(port);
     742          12 :     newKey.AppendLiteral("/[");
     743          24 :     nsAutoCString suffix;
     744          12 :     ci->GetOriginAttributes().CreateSuffix(suffix);
     745          12 :     newKey.Append(suffix);
     746          12 :     newKey.AppendLiteral("]viaORIGIN.FRAME");
     747          12 : }
     748             : 
     749             : nsHttpConnection *
     750          12 : nsHttpConnectionMgr::FindCoalescableConnection(nsConnectionEntry *ent,
     751             :                                                bool justKidding)
     752             : {
     753          12 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     754          12 :     MOZ_ASSERT(ent->mConnInfo);
     755          12 :     nsHttpConnectionInfo *ci = ent->mConnInfo;
     756          12 :     LOG(("FindCoalescableConnection %s\n", ci->HashKey().get()));
     757             :     // First try and look it up by origin frame
     758          24 :     nsCString newKey;
     759          12 :     BuildOriginFrameHashKey(newKey, ci, ci->GetOrigin(), ci->OriginPort());
     760             :     nsHttpConnection *conn =
     761          12 :         FindCoalescableConnectionByHashKey(ent, newKey, justKidding);
     762          12 :     if (conn) {
     763           0 :         LOG(("FindCoalescableConnection(%s) match conn %p on frame key %s\n",
     764             :              ci->HashKey().get(), conn, newKey.get()));
     765           0 :         return conn;
     766             :     }
     767             : 
     768             :     // now check for DNS based keys
     769             :     // deleted conns (null weak pointers) are removed from list
     770          12 :     uint32_t keyLen = ent->mCoalescingKeys.Length();
     771          12 :     for (uint32_t i = 0; i < keyLen; ++i) {
     772           0 :         conn = FindCoalescableConnectionByHashKey(ent, ent->mCoalescingKeys[i], justKidding);
     773           0 :         if (conn) {
     774           0 :             LOG(("FindCoalescableConnection(%s) match conn %p on dns key %s\n",
     775             :                  ci->HashKey().get(), conn, ent->mCoalescingKeys[i].get()));
     776           0 :             return conn;
     777             :         }
     778             :     }
     779             : 
     780          12 :     LOG(("FindCoalescableConnection(%s) no matching conn\n", ci->HashKey().get()));
     781          12 :     return nullptr;
     782             : }
     783             : 
     784             : void
     785           0 : nsHttpConnectionMgr::UpdateCoalescingForNewConn(nsHttpConnection *newConn,
     786             :                                                 nsConnectionEntry *ent)
     787             : {
     788           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     789           0 :     MOZ_ASSERT(newConn);
     790           0 :     MOZ_ASSERT(newConn->ConnectionInfo());
     791           0 :     MOZ_ASSERT(ent);
     792           0 :     MOZ_ASSERT(mCT.Get(newConn->ConnectionInfo()->HashKey()) == ent);
     793             : 
     794           0 :     nsHttpConnection *existingConn = FindCoalescableConnection(ent, true);
     795           0 :     if (existingConn) {
     796           0 :         LOG(("UpdateCoalescingForNewConn() found existing active conn that could have served newConn "
     797             :              "graceful close of newConn=%p to migrate to existingConn %p\n", newConn, existingConn));
     798           0 :         newConn->DontReuse();
     799           0 :         return;
     800             :     }
     801             : 
     802             :     // This connection might go into the mCoalescingHash for new transactions to be coalesced onto
     803             :     // if it can accept new transactions
     804           0 :     if (!newConn->CanDirectlyActivate()) {
     805           0 :         return;
     806             :     }
     807             : 
     808           0 :     uint32_t keyLen = ent->mCoalescingKeys.Length();
     809           0 :     for (uint32_t i = 0;i < keyLen; ++i) {
     810           0 :         LOG(("UpdateCoalescingForNewConn() registering newConn %p %s under key %s\n",
     811             :              newConn, newConn->ConnectionInfo()->HashKey().get(), ent->mCoalescingKeys[i].get()));
     812           0 :         nsTArray<nsWeakPtr> *listOfWeakConns =  mCoalescingHash.Get(ent->mCoalescingKeys[i]);
     813           0 :         if (!listOfWeakConns) {
     814           0 :             LOG(("UpdateCoalescingForNewConn() need new list element\n"));
     815           0 :             listOfWeakConns = new nsTArray<nsWeakPtr>(1);
     816           0 :             mCoalescingHash.Put(ent->mCoalescingKeys[i], listOfWeakConns);
     817             :         }
     818           0 :         listOfWeakConns->AppendElement(
     819           0 :             do_GetWeakReference(static_cast<nsISupportsWeakReference*>(newConn)));
     820             :     }
     821             : 
     822             :     // Cancel any other pending connections - their associated transactions
     823             :     // are in the pending queue and will be dispatched onto this new connection
     824           0 :     for (int32_t index = ent->mHalfOpens.Length() - 1; index >= 0; --index) {
     825           0 :         nsHalfOpenSocket *half = ent->mHalfOpens[index];
     826           0 :         LOG(("UpdateCoalescingForNewConn() forcing halfopen abandon %p\n",
     827             :              half));
     828           0 :         ent->mHalfOpens[index]->Abandon();
     829             :     }
     830             : 
     831           0 :     if (ent->mActiveConns.Length() > 1) {
     832             :         // this is a new connection that can be coalesced onto. hooray!
     833             :         // if there are other connection to this entry (e.g.
     834             :         // some could still be handshaking, shutting down, etc..) then close
     835             :         // them down after any transactions that are on them are complete.
     836             :         // This probably happened due to the parallel connection algorithm
     837             :         // that is used only before the host is known to speak h2.
     838           0 :         for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
     839           0 :             nsHttpConnection *otherConn = ent->mActiveConns[index];
     840           0 :             if (otherConn != newConn) {
     841           0 :                 LOG(("UpdateCoalescingForNewConn() shutting down old connection (%p) because new "
     842             :                      "spdy connection (%p) takes precedence\n", otherConn, newConn));
     843           0 :                 otherConn->DontReuse();
     844             :             }
     845             :         }
     846             :     }
     847             : 
     848           0 :     for (int32_t index = ent->mHalfOpenFastOpenBackups.Length() - 1; index >= 0; --index) {
     849           0 :         LOG(("UpdateCoalescingForNewConn() shutting down connection in fast "
     850             :              "open state (%p) because new spdy connection (%p) takes "
     851             :              "precedence\n", ent->mHalfOpenFastOpenBackups[index].get(), newConn));
     852           0 :         ent->mHalfOpenFastOpenBackups[index]->CancelFastOpenConnection();
     853             :     }
     854             : }
     855             : 
     856             : // This function lets a connection, after completing the NPN phase,
     857             : // report whether or not it is using spdy through the usingSpdy
     858             : // argument. It would not be necessary if NPN were driven out of
     859             : // the connection manager. The connection entry associated with the
     860             : // connection is then updated to indicate whether or not we want to use
     861             : // spdy with that host and update the coalescing hash
     862             : // entries used for de-sharding hostsnames.
     863             : void
     864           3 : nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
     865             :                                           bool usingSpdy)
     866             : {
     867           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     868           3 :     if (!conn->ConnectionInfo()) {
     869           0 :         return;
     870             :     }
     871           3 :     nsConnectionEntry *ent = mCT.Get(conn->ConnectionInfo()->HashKey());
     872           3 :     if (!ent || !usingSpdy) {
     873           3 :         return;
     874             :     }
     875             : 
     876           0 :     ent->mUsingSpdy = true;
     877           0 :     mNumSpdyActiveConns++;
     878             : 
     879             :     // adjust timeout timer
     880           0 :     uint32_t ttl = conn->TimeToLive();
     881           0 :     uint64_t timeOfExpire = NowInSeconds() + ttl;
     882           0 :     if (!mTimer || timeOfExpire < mTimeOfNextWakeUp) {
     883           0 :         PruneDeadConnectionsAfter(ttl);
     884             :     }
     885             : 
     886           0 :     UpdateCoalescingForNewConn(conn, ent);
     887             : 
     888           0 :     nsresult rv = ProcessPendingQ(ent->mConnInfo);
     889           0 :     if (NS_FAILED(rv)) {
     890           0 :         LOG(("ReportSpdyConnection conn=%p ent=%p "
     891             :              "failed to process pending queue (%08x)\n", conn, ent,
     892             :              static_cast<uint32_t>(rv)));
     893             :     }
     894           0 :     rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ);
     895           0 :     if (NS_FAILED(rv)) {
     896           0 :         LOG(("ReportSpdyConnection conn=%p ent=%p "
     897             :              "failed to post event (%08x)\n", conn, ent,
     898             :              static_cast<uint32_t>(rv)));
     899             :     }
     900             : }
     901             : 
     902             : //-----------------------------------------------------------------------------
     903             : bool
     904           0 : nsHttpConnectionMgr::DispatchPendingQ(nsTArray<RefPtr<nsHttpConnectionMgr::PendingTransactionInfo> > &pendingQ,
     905             :                                       nsConnectionEntry *ent,
     906             :                                       bool considerAll)
     907             : {
     908           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     909             : 
     910           0 :     PendingTransactionInfo *pendingTransInfo = nullptr;
     911             :     nsresult rv;
     912           0 :     bool dispatchedSuccessfully = false;
     913             : 
     914             :     // if !considerAll iterate the pending list until one is dispatched successfully.
     915             :     // Keep iterating afterwards only until a transaction fails to dispatch.
     916             :     // if considerAll == true then try and dispatch all items.
     917           0 :     for (uint32_t i = 0; i < pendingQ.Length(); ) {
     918           0 :         pendingTransInfo = pendingQ[i];
     919           0 :         LOG(("nsHttpConnectionMgr::DispatchPendingQ "
     920             :              "[trans=%p, halfOpen=%p, activeConn=%p]\n",
     921             :              pendingTransInfo->mTransaction.get(),
     922             :              pendingTransInfo->mHalfOpen.get(),
     923             :              pendingTransInfo->mActiveConn.get()));
     924             : 
     925             :         // When this transaction has already established a half-open
     926             :         // connection, we want to prevent any duplicate half-open
     927             :         // connections from being established and bound to this
     928             :         // transaction. Allow only use of an idle persistent connection
     929             :         // (if found) for transactions referred by a half-open connection.
     930           0 :         bool alreadyHalfOpenOrWaitingForTLS = false;
     931           0 :         if (pendingTransInfo->mHalfOpen) {
     932           0 :             MOZ_ASSERT(!pendingTransInfo->mActiveConn);
     933             :             RefPtr<nsHalfOpenSocket> halfOpen =
     934           0 :                 do_QueryReferent(pendingTransInfo->mHalfOpen);
     935           0 :             LOG(("nsHttpConnectionMgr::DispatchPendingQ "
     936             :                  "[trans=%p, halfOpen=%p]\n",
     937             :                  pendingTransInfo->mTransaction.get(), halfOpen.get()));
     938           0 :             if (halfOpen) {
     939           0 :                 alreadyHalfOpenOrWaitingForTLS = true;
     940             :             } else {
     941             :                 // If we have not found the halfOpen socket, remove the pointer.
     942           0 :                 pendingTransInfo->mHalfOpen = nullptr;
     943             :             }
     944           0 :         }  else if (pendingTransInfo->mActiveConn) {
     945           0 :             MOZ_ASSERT(!pendingTransInfo->mHalfOpen);
     946             :             RefPtr<nsHttpConnection> activeConn =
     947           0 :                 do_QueryReferent(pendingTransInfo->mActiveConn);
     948           0 :             LOG(("nsHttpConnectionMgr::DispatchPendingQ "
     949             :                  "[trans=%p, activeConn=%p]\n",
     950             :                  pendingTransInfo->mTransaction.get(), activeConn.get()));
     951             :             // Check if this transaction claimed a connection that is still
     952             :             // performing tls handshake with a NullHttpTransaction or it is between
     953             :             // finishing tls and reclaiming (When nullTrans finishes tls handshake,
     954             :             // httpConnection does not have a transaction any more and a
     955             :             // ReclaimConnection is dispatched). But if an error occurred the
     956             :             // connection will be closed, it will exist but CanReused will be
     957             :             // false.
     958           0 :             if (activeConn &&
     959           0 :                 ((activeConn->Transaction() &&
     960           0 :                   activeConn->Transaction()->IsNullTransaction()) ||
     961           0 :                  (!activeConn->Transaction() && activeConn->CanReuse()))) {
     962           0 :                 alreadyHalfOpenOrWaitingForTLS = true;
     963             :             } else {
     964             :                 // If we have not found the connection, remove the pointer.
     965           0 :                 pendingTransInfo->mActiveConn = nullptr;
     966             :             }
     967             :         }
     968             : 
     969           0 :         rv = TryDispatchTransaction(ent,
     970           0 :                                     alreadyHalfOpenOrWaitingForTLS || !!pendingTransInfo->mTransaction->TunnelProvider(),
     971           0 :                                     pendingTransInfo);
     972           0 :         if (NS_SUCCEEDED(rv) || (rv != NS_ERROR_NOT_AVAILABLE)) {
     973           0 :             if (NS_SUCCEEDED(rv)) {
     974           0 :                 LOG(("  dispatching pending transaction...\n"));
     975             :             } else {
     976           0 :                 LOG(("  removing pending transaction based on "
     977             :                      "TryDispatchTransaction returning hard error %" PRIx32 "\n",
     978             :                      static_cast<uint32_t>(rv)));
     979             :             }
     980           0 :             ReleaseClaimedSockets(ent, pendingTransInfo);
     981           0 :             if (pendingQ.RemoveElement(pendingTransInfo)) {
     982             :                 // pendingTransInfo is now potentially destroyed
     983           0 :                 dispatchedSuccessfully = true;
     984           0 :                 continue; // dont ++i as we just made the array shorter
     985             :             }
     986             : 
     987           0 :             LOG(("  transaction not found in pending queue\n"));
     988             :         }
     989             : 
     990           0 :         if (dispatchedSuccessfully && !considerAll)
     991           0 :             break;
     992             : 
     993           0 :         ++i;
     994             : 
     995             :     }
     996           0 :     return dispatchedSuccessfully;
     997             : }
     998             : 
     999             : uint32_t
    1000           3 : nsHttpConnectionMgr::TotalActiveConnections(nsConnectionEntry *ent) const
    1001             : {
    1002             :     // Add in the in-progress tcp connections, we will assume they are
    1003             :     // keepalive enabled.
    1004             :     // Exclude half-open's that has already created a usable connection.
    1005             :     // This prevents the limit being stuck on ipv6 connections that
    1006             :     // eventually time out after typical 21 seconds of no ACK+SYN reply.
    1007           3 :     return ent->mActiveConns.Length() + ent->UnconnectedHalfOpens();
    1008             : }
    1009             : 
    1010             : uint32_t
    1011           3 : nsHttpConnectionMgr::MaxPersistConnections(nsConnectionEntry *ent) const
    1012             : {
    1013           3 :     if (ent->mConnInfo->UsingHttpProxy() && !ent->mConnInfo->UsingConnect()) {
    1014           0 :         return static_cast<uint32_t>(mMaxPersistConnsPerProxy);
    1015             :     }
    1016             : 
    1017           3 :     return static_cast<uint32_t>(mMaxPersistConnsPerHost);
    1018             : }
    1019             : 
    1020             : bool
    1021          16 : nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent, bool considerAll)
    1022             : {
    1023          16 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1024             : 
    1025          16 :     LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry "
    1026             :          "[ci=%s ent=%p active=%" PRIuSIZE " idle=%" PRIuSIZE " urgent-start-queue=%" PRIuSIZE
    1027             :          " queued=%" PRIuSIZE "]\n",
    1028             :          ent->mConnInfo->HashKey().get(), ent, ent->mActiveConns.Length(),
    1029             :          ent->mIdleConns.Length(), ent->mUrgentStartQ.Length(),
    1030             :          ent->PendingQLength()));
    1031             : 
    1032          16 :     if (LOG_ENABLED()) {
    1033           0 :       LOG(("urgent queue ["));
    1034           0 :       for (auto info : ent->mUrgentStartQ) {
    1035           0 :         LOG(("  %p", info->mTransaction.get()));
    1036             :       }
    1037           0 :       for (auto it = ent->mPendingTransactionTable.Iter(); !it.Done(); it.Next()) {
    1038           0 :         LOG(("] window id = %" PRIx64 " queue [", it.Key()));
    1039           0 :         for (auto info : *it.UserData()) {
    1040           0 :           LOG(("  %p", info->mTransaction.get()));
    1041             :         }
    1042             :       }
    1043           0 :       LOG(("]"));
    1044             :     }
    1045             : 
    1046          16 :     if (!ent->mUrgentStartQ.Length() && !ent->PendingQLength()) {
    1047          16 :         return false;
    1048             :     }
    1049           0 :     ProcessSpdyPendingQ(ent);
    1050             : 
    1051           0 :     bool dispatchedSuccessfully = false;
    1052             : 
    1053           0 :     if (!ent->mUrgentStartQ.IsEmpty()) {
    1054           0 :         dispatchedSuccessfully = DispatchPendingQ(ent->mUrgentStartQ,
    1055             :                                                   ent,
    1056           0 :                                                   considerAll);
    1057             :     }
    1058             : 
    1059           0 :     if (dispatchedSuccessfully && !considerAll) {
    1060           0 :         return dispatchedSuccessfully;
    1061             :     }
    1062             : 
    1063           0 :     uint32_t totalCount = TotalActiveConnections(ent);
    1064           0 :     uint32_t maxPersistConns = MaxPersistConnections(ent);
    1065             :     uint32_t availableConnections = maxPersistConns > totalCount
    1066           0 :         ? maxPersistConns - totalCount
    1067           0 :         : 0;
    1068             : 
    1069             :     // No need to try dispatching if we reach the active connection limit.
    1070           0 :     if (!availableConnections) {
    1071           0 :         return dispatchedSuccessfully;
    1072             :     }
    1073             : 
    1074             :     uint32_t maxFocusedWindowConnections =
    1075           0 :         availableConnections * gHttpHandler->FocusedWindowTransactionRatio();
    1076           0 :     MOZ_ASSERT(maxFocusedWindowConnections < availableConnections);
    1077             : 
    1078           0 :     if (!maxFocusedWindowConnections) {
    1079           0 :         maxFocusedWindowConnections = 1;
    1080             :     }
    1081             : 
    1082             :     // Only need to dispatch transactions for either focused or
    1083             :     // non-focused window because considerAll is false.
    1084           0 :     if (!considerAll) {
    1085           0 :         nsTArray<RefPtr<PendingTransactionInfo>> pendingQ;
    1086           0 :         ent->AppendPendingQForFocusedWindow(
    1087             :             mCurrentTopLevelOuterContentWindowId,
    1088             :             pendingQ,
    1089           0 :             maxFocusedWindowConnections);
    1090             : 
    1091           0 :         if (pendingQ.IsEmpty()) {
    1092           0 :             ent->AppendPendingQForNonFocusedWindows(
    1093             :                 mCurrentTopLevelOuterContentWindowId,
    1094             :                 pendingQ,
    1095           0 :                 availableConnections);
    1096             :         }
    1097             : 
    1098           0 :         dispatchedSuccessfully |=
    1099           0 :             DispatchPendingQ(pendingQ, ent, considerAll);
    1100             : 
    1101             :         // Put the leftovers into connection entry
    1102           0 :         for (const auto& transactionInfo : pendingQ) {
    1103           0 :             ent->InsertTransaction(transactionInfo);
    1104             :         }
    1105             : 
    1106           0 :         return dispatchedSuccessfully;
    1107             :     }
    1108             : 
    1109             :     uint32_t maxNonFocusedWindowConnections =
    1110           0 :         availableConnections - maxFocusedWindowConnections;
    1111           0 :     nsTArray<RefPtr<PendingTransactionInfo>> pendingQ;
    1112           0 :     nsTArray<RefPtr<PendingTransactionInfo>> remainingPendingQ;
    1113             : 
    1114           0 :     ent->AppendPendingQForFocusedWindow(
    1115             :         mCurrentTopLevelOuterContentWindowId,
    1116             :         pendingQ,
    1117           0 :         maxFocusedWindowConnections);
    1118             : 
    1119           0 :     if (maxNonFocusedWindowConnections) {
    1120           0 :         ent->AppendPendingQForNonFocusedWindows(
    1121             :             mCurrentTopLevelOuterContentWindowId,
    1122             :             remainingPendingQ,
    1123           0 :             maxNonFocusedWindowConnections);
    1124             :     }
    1125             : 
    1126             :     // If the slots for either focused or non-focused window are not filled up
    1127             :     // to the availability, try to use the remaining available connections
    1128             :     // for the other slot (with preference for the focused window).
    1129           0 :     if (remainingPendingQ.Length() < maxNonFocusedWindowConnections) {
    1130           0 :         ent->AppendPendingQForFocusedWindow(
    1131             :             mCurrentTopLevelOuterContentWindowId,
    1132             :             pendingQ,
    1133           0 :             maxNonFocusedWindowConnections - remainingPendingQ.Length());
    1134           0 :     } else if (pendingQ.Length() < maxFocusedWindowConnections) {
    1135           0 :         ent->AppendPendingQForNonFocusedWindows(
    1136             :             mCurrentTopLevelOuterContentWindowId,
    1137             :             remainingPendingQ,
    1138           0 :             maxFocusedWindowConnections - pendingQ.Length());
    1139             :     }
    1140             : 
    1141           0 :     MOZ_ASSERT(pendingQ.Length() + remainingPendingQ.Length() <=
    1142             :                availableConnections);
    1143             : 
    1144           0 :     LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry "
    1145             :          "pendingQ.Length()=%" PRIuSIZE
    1146             :          ", remainingPendingQ.Length()=%" PRIuSIZE "\n",
    1147             :          pendingQ.Length(), remainingPendingQ.Length()));
    1148             : 
    1149             :     // Append elements in |remainingPendingQ| to |pendingQ|. The order in
    1150             :     // |pendingQ| is like: [focusedWindowTrans...nonFocusedWindowTrans].
    1151           0 :     pendingQ.AppendElements(Move(remainingPendingQ));
    1152             : 
    1153           0 :     dispatchedSuccessfully |=
    1154           0 :         DispatchPendingQ(pendingQ, ent, considerAll);
    1155             : 
    1156             :     // Put the leftovers into connection entry
    1157           0 :     for (const auto& transactionInfo : pendingQ) {
    1158           0 :         ent->InsertTransaction(transactionInfo);
    1159             :     }
    1160             : 
    1161             :     // Only remove empty pendingQ when considerAll is true.
    1162           0 :     ent->RemoveEmptyPendingQ();
    1163             : 
    1164           0 :     return dispatchedSuccessfully;
    1165             : }
    1166             : 
    1167             : bool
    1168           0 : nsHttpConnectionMgr::ProcessPendingQForEntry(nsHttpConnectionInfo *ci)
    1169             : {
    1170           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1171             : 
    1172           0 :     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
    1173           0 :     if (ent)
    1174           0 :         return ProcessPendingQForEntry(ent, false);
    1175           0 :     return false;
    1176             : }
    1177             : 
    1178             : // we're at the active connection limit if any one of the following conditions is true:
    1179             : //  (1) at max-connections
    1180             : //  (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
    1181             : //  (3) keep-alive disabled and at max-connections-per-server
    1182             : bool
    1183           3 : nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, uint32_t caps)
    1184             : {
    1185           3 :     nsHttpConnectionInfo *ci = ent->mConnInfo;
    1186           3 :     uint32_t totalCount = TotalActiveConnections(ent);
    1187           3 :     uint32_t maxPersistConns = MaxPersistConnections(ent);
    1188             : 
    1189           3 :     LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x,"
    1190             :          "totalCount=%u, maxPersistConns=%u]\n",
    1191             :          ci->HashKey().get(), caps, totalCount, maxPersistConns));
    1192             : 
    1193           3 :     if (caps & NS_HTTP_URGENT_START) {
    1194           1 :         if (totalCount >= (mMaxUrgentExcessiveConns + maxPersistConns)) {
    1195           0 :             LOG(("The number of total connections are greater than or equal to sum of "
    1196             :                  "max urgent-start queue length and the number of max persistent connections.\n"));
    1197           0 :             return true;
    1198             :         }
    1199           1 :         return false;
    1200             :     }
    1201             : 
    1202             :     // update maxconns if potentially limited by the max socket count
    1203             :     // this requires a dynamic reduction in the max socket count to a point
    1204             :     // lower than the max-connections pref.
    1205           2 :     uint32_t maxSocketCount = gHttpHandler->MaxSocketCount();
    1206           2 :     if (mMaxConns > maxSocketCount) {
    1207           0 :         mMaxConns = maxSocketCount;
    1208           0 :         LOG(("nsHttpConnectionMgr %p mMaxConns dynamically reduced to %u",
    1209             :              this, mMaxConns));
    1210             :     }
    1211             : 
    1212             :     // If there are more active connections than the global limit, then we're
    1213             :     // done. Purging idle connections won't get us below it.
    1214           2 :     if (mNumActiveConns >= mMaxConns) {
    1215           0 :         LOG(("  num active conns == max conns\n"));
    1216           0 :         return true;
    1217             :     }
    1218             : 
    1219           2 :     bool result = (totalCount >= maxPersistConns);
    1220           2 :     LOG(("AtActiveConnectionLimit result: %s", result ? "true" : "false"));
    1221           2 :     return result;
    1222             : }
    1223             : 
    1224             : void
    1225           0 : nsHttpConnectionMgr::ClosePersistentConnections(nsConnectionEntry *ent)
    1226             : {
    1227           0 :     LOG(("nsHttpConnectionMgr::ClosePersistentConnections [ci=%s]\n",
    1228             :          ent->mConnInfo->HashKey().get()));
    1229           0 :     while (ent->mIdleConns.Length()) {
    1230           0 :         RefPtr<nsHttpConnection> conn(ent->mIdleConns[0]);
    1231           0 :         ent->mIdleConns.RemoveElementAt(0);
    1232           0 :         mNumIdleConns--;
    1233           0 :         conn->Close(NS_ERROR_ABORT);
    1234             :     }
    1235             : 
    1236           0 :     int32_t activeCount = ent->mActiveConns.Length();
    1237           0 :     for (int32_t i=0; i < activeCount; i++)
    1238           0 :         ent->mActiveConns[i]->DontReuse();
    1239           0 : }
    1240             : 
    1241             : bool
    1242           3 : nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent)
    1243             : {
    1244           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1245             : 
    1246           3 :     if (ent->AvailableForDispatchNow()) {
    1247             :         // this might be a h2/spdy connection in this connection entry that
    1248             :         // is able to be immediately muxxed, or it might be one that
    1249             :         // was found in the same state through a coalescing hash
    1250           0 :         LOG(("nsHttpConnectionMgr::RestrictConnections %p %s restricted due to active >=h2\n",
    1251             :              ent, ent->mConnInfo->HashKey().get()));
    1252           0 :         return true;
    1253             :     }
    1254             : 
    1255             :     // If this host is trying to negotiate a SPDY session right now,
    1256             :     // don't create any new ssl connections until the result of the
    1257             :     // negotiation is known.
    1258             : 
    1259             :     bool doRestrict =
    1260           3 :         ent->mConnInfo->FirstHopSSL() && gHttpHandler->IsSpdyEnabled() &&
    1261           3 :         ent->mUsingSpdy && (ent->mHalfOpens.Length() || ent->mActiveConns.Length());
    1262             : 
    1263             :     // If there are no restrictions, we are done
    1264           3 :     if (!doRestrict)
    1265           3 :         return false;
    1266             : 
    1267             :     // If the restriction is based on a tcp handshake in progress
    1268             :     // let that connect and then see if it was SPDY or not
    1269           0 :     if (ent->UnconnectedHalfOpens()) {
    1270           0 :         return true;
    1271             :     }
    1272             : 
    1273             :     // There is a concern that a host is using a mix of HTTP/1 and SPDY.
    1274             :     // In that case we don't want to restrict connections just because
    1275             :     // there is a single active HTTP/1 session in use.
    1276           0 :     if (ent->mUsingSpdy && ent->mActiveConns.Length()) {
    1277           0 :         bool confirmedRestrict = false;
    1278           0 :         for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
    1279           0 :             nsHttpConnection *conn = ent->mActiveConns[index];
    1280           0 :             if (!conn->ReportedNPN() || conn->CanDirectlyActivate()) {
    1281           0 :                 confirmedRestrict = true;
    1282           0 :                 break;
    1283             :             }
    1284             :         }
    1285           0 :         doRestrict = confirmedRestrict;
    1286           0 :         if (!confirmedRestrict) {
    1287           0 :             LOG(("nsHttpConnectionMgr spdy connection restriction to "
    1288             :                  "%s bypassed.\n", ent->mConnInfo->Origin()));
    1289             :         }
    1290             :     }
    1291           0 :     return doRestrict;
    1292             : }
    1293             : 
    1294             : // returns NS_OK if a connection was started
    1295             : // return NS_ERROR_NOT_AVAILABLE if a new connection cannot be made due to
    1296             : //        ephemeral limits
    1297             : // returns other NS_ERROR on hard failure conditions
    1298             : nsresult
    1299           3 : nsHttpConnectionMgr::MakeNewConnection(nsConnectionEntry *ent,
    1300             :                                        PendingTransactionInfo *pendingTransInfo)
    1301             : {
    1302           3 :     nsHttpTransaction *trans = pendingTransInfo->mTransaction;
    1303             : 
    1304           3 :     LOG(("nsHttpConnectionMgr::MakeNewConnection %p ent=%p trans=%p",
    1305             :          this, ent, trans));
    1306           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1307             : 
    1308           3 :     uint32_t halfOpenLength = ent->mHalfOpens.Length();
    1309           3 :     for (uint32_t i = 0; i < halfOpenLength; i++) {
    1310           0 :         if (ent->mHalfOpens[i]->Claim()) {
    1311             :             // We've found a speculative connection or a connection that
    1312             :             // is free to be used in the half open list.
    1313             :             // A free to be used connection is a connection that was
    1314             :             // open for a concrete transaction, but that trunsaction
    1315             :             // ended up using another connection.
    1316           0 :             LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s]\n"
    1317             :                  "Found a speculative or a free-to-use half open connection\n",
    1318             :                  ent->mConnInfo->HashKey().get()));
    1319             :             pendingTransInfo->mHalfOpen =
    1320           0 :                 do_GetWeakReference(static_cast<nsISupportsWeakReference*>(ent->mHalfOpens[i]));
    1321             :             // return OK because we have essentially opened a new connection
    1322             :             // by converting a speculative half-open to general use
    1323           0 :             return NS_OK;
    1324             :         }
    1325             :     }
    1326             : 
    1327             :     // consider null transactions that are being used to drive the ssl handshake if
    1328             :     // the transaction creating this connection can re-use persistent connections
    1329           3 :     if (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) {
    1330           3 :         uint32_t activeLength = ent->mActiveConns.Length();
    1331           3 :         for (uint32_t i = 0; i < activeLength; i++) {
    1332           0 :             nsAHttpTransaction *activeTrans = ent->mActiveConns[i]->Transaction();
    1333           0 :             NullHttpTransaction *nullTrans = activeTrans ? activeTrans->QueryNullTransaction() : nullptr;
    1334           0 :             if (nullTrans && nullTrans->Claim()) {
    1335           0 :                 LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s] "
    1336             :                      "Claiming a null transaction for later use\n",
    1337             :                      ent->mConnInfo->HashKey().get()));
    1338             :                 pendingTransInfo->mActiveConn =
    1339           0 :                     do_GetWeakReference(static_cast<nsISupportsWeakReference*>(ent->mActiveConns[i]));
    1340           0 :                 return NS_OK;
    1341             :             }
    1342             :         }
    1343             :     }
    1344             : 
    1345             :     // If this host is trying to negotiate a SPDY session right now,
    1346             :     // don't create any new connections until the result of the
    1347             :     // negotiation is known.
    1348           9 :     if (!(trans->Caps() & NS_HTTP_DISALLOW_SPDY) &&
    1349           6 :         (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) &&
    1350           3 :         RestrictConnections(ent)) {
    1351           0 :         LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s] "
    1352             :              "Not Available Due to RestrictConnections()\n",
    1353             :              ent->mConnInfo->HashKey().get()));
    1354           0 :         return NS_ERROR_NOT_AVAILABLE;
    1355             :     }
    1356             : 
    1357             :     // We need to make a new connection. If that is going to exceed the
    1358             :     // global connection limit then try and free up some room by closing
    1359             :     // an idle connection to another host. We know it won't select "ent"
    1360             :     // because we have already determined there are no idle connections
    1361             :     // to our destination
    1362             : 
    1363           3 :     if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) && mNumIdleConns) {
    1364             :         // If the global number of connections is preventing the opening of new
    1365             :         // connections to a host without idle connections, then close them
    1366             :         // regardless of their TTL.
    1367           0 :         auto iter = mCT.Iter();
    1368           0 :         while (mNumIdleConns + mNumActiveConns + 1 >= mMaxConns &&
    1369           0 :                !iter.Done()) {
    1370           0 :             nsAutoPtr<nsConnectionEntry> &entry = iter.Data();
    1371           0 :             if (!entry->mIdleConns.Length()) {
    1372           0 :               iter.Next();
    1373           0 :               continue;
    1374             :             }
    1375           0 :             RefPtr<nsHttpConnection> conn(entry->mIdleConns[0]);
    1376           0 :             entry->mIdleConns.RemoveElementAt(0);
    1377           0 :             conn->Close(NS_ERROR_ABORT);
    1378           0 :             mNumIdleConns--;
    1379           0 :             ConditionallyStopPruneDeadConnectionsTimer();
    1380             :         }
    1381             :     }
    1382             : 
    1383           6 :     if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) &&
    1384           3 :         mNumActiveConns && gHttpHandler->IsSpdyEnabled())
    1385             :     {
    1386             :         // If the global number of connections is preventing the opening of new
    1387             :         // connections to a host without idle connections, then close any spdy
    1388             :         // ASAP.
    1389           0 :         for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    1390           0 :             nsAutoPtr<nsConnectionEntry> &entry = iter.Data();
    1391           0 :             if (!entry->mUsingSpdy) {
    1392           0 :                 continue;
    1393             :             }
    1394             : 
    1395           0 :             for (uint32_t index = 0;
    1396           0 :                  index < entry->mActiveConns.Length();
    1397             :                  ++index) {
    1398           0 :                 nsHttpConnection *conn = entry->mActiveConns[index];
    1399           0 :                 if (conn->UsingSpdy() && conn->CanReuse()) {
    1400           0 :                     conn->DontReuse();
    1401             :                     // Stop on <= (particularly =) because this dontreuse
    1402             :                     // causes async close.
    1403           0 :                     if (mNumIdleConns + mNumActiveConns + 1 <= mMaxConns) {
    1404           0 :                         goto outerLoopEnd;
    1405             :                     }
    1406             :                 }
    1407             :             }
    1408             :         }
    1409             :       outerLoopEnd:
    1410             :         ;
    1411             :     }
    1412             : 
    1413           3 :     if (AtActiveConnectionLimit(ent, trans->Caps()))
    1414           0 :         return NS_ERROR_NOT_AVAILABLE;
    1415             : 
    1416           3 :     nsresult rv = CreateTransport(ent, trans, trans->Caps(), false, false,
    1417           3 :                                   true, pendingTransInfo);
    1418           3 :     if (NS_FAILED(rv)) {
    1419             :         /* hard failure */
    1420           0 :         LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s trans = %p] "
    1421             :              "CreateTransport() hard failure.\n",
    1422             :              ent->mConnInfo->HashKey().get(), trans));
    1423           0 :         trans->Close(rv);
    1424           0 :         if (rv == NS_ERROR_NOT_AVAILABLE)
    1425           0 :             rv = NS_ERROR_FAILURE;
    1426           0 :         return rv;
    1427             :     }
    1428             : 
    1429           3 :     return NS_OK;
    1430             : }
    1431             : 
    1432             : // returns OK if a connection is found for the transaction
    1433             : //   and the transaction is started.
    1434             : // returns ERROR_NOT_AVAILABLE if no connection can be found and it
    1435             : //   should be queued until circumstances change
    1436             : // returns other ERROR when transaction has a hard failure and should
    1437             : //   not remain in the pending queue
    1438             : nsresult
    1439           3 : nsHttpConnectionMgr::TryDispatchTransaction(nsConnectionEntry *ent,
    1440             :                                             bool onlyReusedConnection,
    1441             :                                             PendingTransactionInfo *pendingTransInfo)
    1442             : {
    1443           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1444             : 
    1445           3 :     nsHttpTransaction *trans = pendingTransInfo->mTransaction;
    1446             : 
    1447           3 :     LOG(("nsHttpConnectionMgr::TryDispatchTransaction without conn "
    1448             :          "[trans=%p halfOpen=%p conn=%p ci=%p ci=%s caps=%x tunnelprovider=%p "
    1449             :          "onlyreused=%d active=%" PRIuSIZE " idle=%" PRIuSIZE "]\n", trans,
    1450             :          pendingTransInfo->mHalfOpen.get(),
    1451             :          pendingTransInfo->mActiveConn.get(), ent->mConnInfo.get(),
    1452             :          ent->mConnInfo->HashKey().get(),
    1453             :          uint32_t(trans->Caps()), trans->TunnelProvider(),
    1454             :          onlyReusedConnection, ent->mActiveConns.Length(),
    1455             :          ent->mIdleConns.Length()));
    1456             : 
    1457           3 :     uint32_t caps = trans->Caps();
    1458             : 
    1459             :     // 0 - If this should use spdy then dispatch it post haste.
    1460             :     // 1 - If there is connection pressure then see if we can pipeline this on
    1461             :     //     a connection of a matching type instead of using a new conn
    1462             :     // 2 - If there is an idle connection, use it!
    1463             :     // 3 - if class == reval or script and there is an open conn of that type
    1464             :     //     then pipeline onto shortest pipeline of that class if limits allow
    1465             :     // 4 - If we aren't up against our connection limit,
    1466             :     //     then open a new one
    1467             :     // 5 - Try a pipeline if we haven't already - this will be unusual because
    1468             :     //     it implies a low connection pressure situation where
    1469             :     //     MakeNewConnection() failed.. that is possible, but unlikely, due to
    1470             :     //     global limits
    1471             :     // 6 - no connection is available - queue it
    1472             : 
    1473           6 :     RefPtr<nsHttpConnection> unusedSpdyPersistentConnection;
    1474             : 
    1475             :     // step 0
    1476             :     // look for existing spdy connection - that's always best because it is
    1477             :     // essentially pipelining without head of line blocking
    1478             : 
    1479           3 :     if (!(caps & NS_HTTP_DISALLOW_SPDY) && gHttpHandler->IsSpdyEnabled()) {
    1480           6 :         RefPtr<nsHttpConnection> conn = GetSpdyActiveConn(ent);
    1481           3 :         if (conn) {
    1482           0 :             if ((caps & NS_HTTP_ALLOW_KEEPALIVE) || !conn->IsExperienced()) {
    1483           0 :                 LOG(("   dispatch to spdy: [conn=%p]\n", conn.get()));
    1484           0 :                 trans->RemoveDispatchedAsBlocking();  /* just in case */
    1485           0 :                 nsresult rv = DispatchTransaction(ent, trans, conn);
    1486           0 :                 NS_ENSURE_SUCCESS(rv, rv);
    1487           0 :                 return NS_OK;
    1488             :             }
    1489           0 :             unusedSpdyPersistentConnection = conn;
    1490             :         }
    1491             :     }
    1492             : 
    1493             :     // If this is not a blocking transaction and the request context for it is
    1494             :     // currently processing one or more blocking transactions then we
    1495             :     // need to just leave it in the queue until those are complete unless it is
    1496             :     // explicitly marked as unblocked.
    1497           3 :     if (!(caps & NS_HTTP_LOAD_AS_BLOCKING)) {
    1498           3 :         if (!(caps & NS_HTTP_LOAD_UNBLOCKED)) {
    1499           3 :             nsIRequestContext *requestContext = trans->RequestContext();
    1500           3 :             if (requestContext) {
    1501           1 :                 uint32_t blockers = 0;
    1502           2 :                 if (NS_SUCCEEDED(requestContext->GetBlockingTransactionCount(&blockers)) &&
    1503           1 :                     blockers) {
    1504             :                     // need to wait for blockers to clear
    1505           0 :                     LOG(("   blocked by request context: [rc=%p trans=%p blockers=%d]\n",
    1506             :                          requestContext, trans, blockers));
    1507           0 :                     return NS_ERROR_NOT_AVAILABLE;
    1508             :                 }
    1509             :             }
    1510             :         }
    1511             :     } else {
    1512             :         // Mark the transaction and its load group as blocking right now to prevent
    1513             :         // other transactions from being reordered in the queue due to slow syns.
    1514           0 :         trans->DispatchedAsBlocking();
    1515             :     }
    1516             : 
    1517             :     // step 1
    1518             :     // If connection pressure, then we want to favor pipelining of any kind
    1519             :     // h1 pipelining has been removed
    1520             : 
    1521             :     // Subject most transactions at high parallelism to rate pacing.
    1522             :     // It will only be actually submitted to the
    1523             :     // token bucket once, and if possible it is granted admission synchronously.
    1524             :     // It is important to leave a transaction in the pending queue when blocked by
    1525             :     // pacing so it can be found on cancel if necessary.
    1526             :     // Transactions that cause blocking or bypass it (e.g. js/css) are not rate
    1527             :     // limited.
    1528           3 :     if (gHttpHandler->UseRequestTokenBucket()) {
    1529             :         // submit even whitelisted transactions to the token bucket though they will
    1530             :         // not be slowed by it
    1531           3 :         bool runNow = trans->TryToRunPacedRequest();
    1532           3 :         if (!runNow) {
    1533           0 :             if ((mNumActiveConns - mNumSpdyActiveConns) <=
    1534           0 :                 gHttpHandler->RequestTokenBucketMinParallelism()) {
    1535           0 :                 runNow = true; // white list it
    1536           0 :             } else if (caps & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED)) {
    1537           0 :                 runNow = true; // white list it
    1538             :             }
    1539             :         }
    1540           3 :         if (!runNow) {
    1541           0 :             LOG(("   blocked due to rate pacing trans=%p\n", trans));
    1542           0 :             return NS_ERROR_NOT_AVAILABLE;
    1543             :         }
    1544             :     }
    1545             : 
    1546             :     // step 2
    1547             :     // consider an idle persistent connection
    1548           3 :     if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
    1549           6 :         RefPtr<nsHttpConnection> conn;
    1550           3 :         while (!conn && (ent->mIdleConns.Length() > 0)) {
    1551           0 :             conn = ent->mIdleConns[0];
    1552           0 :             ent->mIdleConns.RemoveElementAt(0);
    1553           0 :             mNumIdleConns--;
    1554             : 
    1555             :             // we check if the connection can be reused before even checking if
    1556             :             // it is a "matching" connection.
    1557           0 :             if (!conn->CanReuse()) {
    1558           0 :                 LOG(("   dropping stale connection: [conn=%p]\n", conn.get()));
    1559           0 :                 conn->Close(NS_ERROR_ABORT);
    1560           0 :                 conn = nullptr;
    1561             :             }
    1562             :             else {
    1563           0 :                 LOG(("   reusing connection [conn=%p]\n", conn.get()));
    1564           0 :                 conn->EndIdleMonitoring();
    1565             :             }
    1566             : 
    1567             :             // If there are no idle connections left at all, we need to make
    1568             :             // sure that we are not pruning dead connections anymore.
    1569           0 :             ConditionallyStopPruneDeadConnectionsTimer();
    1570             :         }
    1571           3 :         if (conn) {
    1572             :             // This will update the class of the connection to be the class of
    1573             :             // the transaction dispatched on it.
    1574           0 :             AddActiveConn(conn, ent);
    1575           0 :             nsresult rv = DispatchTransaction(ent, trans, conn);
    1576           0 :             NS_ENSURE_SUCCESS(rv, rv);
    1577           0 :             LOG(("   dispatched step 2 (idle) trans=%p\n", trans));
    1578           0 :             return NS_OK;
    1579             :         }
    1580             :     }
    1581             : 
    1582             :     // step 3
    1583             :     // consider pipelining scripts and revalidations
    1584             :     // h1 pipelining has been removed
    1585             : 
    1586             :     // step 4
    1587           3 :     if (!onlyReusedConnection) {
    1588           3 :         nsresult rv = MakeNewConnection(ent, pendingTransInfo);
    1589           3 :         if (NS_SUCCEEDED(rv)) {
    1590             :             // this function returns NOT_AVAILABLE for asynchronous connects
    1591           3 :             LOG(("   dispatched step 4 (async new conn) trans=%p\n", trans));
    1592           3 :             return NS_ERROR_NOT_AVAILABLE;
    1593             :         }
    1594             : 
    1595           0 :         if (rv != NS_ERROR_NOT_AVAILABLE) {
    1596             :             // not available return codes should try next step as they are
    1597             :             // not hard errors. Other codes should stop now
    1598           0 :             LOG(("   failed step 4 (%" PRIx32 ") trans=%p\n",
    1599             :                  static_cast<uint32_t>(rv), trans));
    1600           0 :             return rv;
    1601             :         }
    1602           0 :     } else if (trans->TunnelProvider() && trans->TunnelProvider()->MaybeReTunnel(trans)) {
    1603           0 :         LOG(("   sort of dispatched step 4a tunnel requeue trans=%p\n", trans));
    1604             :         // the tunnel provider took responsibility for making a new tunnel
    1605           0 :         return NS_OK;
    1606             :     }
    1607             : 
    1608             :     // step 5
    1609             :     // previously pipelined anything here if allowed but h1 pipelining has been removed
    1610             : 
    1611             :     // step 6
    1612           0 :     if (unusedSpdyPersistentConnection) {
    1613             :         // to avoid deadlocks, we need to throw away this perfectly valid SPDY
    1614             :         // connection to make room for a new one that can service a no KEEPALIVE
    1615             :         // request
    1616           0 :         unusedSpdyPersistentConnection->DontReuse();
    1617             :     }
    1618             : 
    1619           0 :     LOG(("   not dispatched (queued) trans=%p\n", trans));
    1620           0 :     return NS_ERROR_NOT_AVAILABLE;                /* queue it */
    1621             : }
    1622             : 
    1623             : nsresult
    1624           3 : nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
    1625             :                                          nsHttpTransaction *trans,
    1626             :                                          nsHttpConnection *conn)
    1627             : {
    1628           3 :     uint32_t caps = trans->Caps();
    1629           3 :     int32_t priority = trans->Priority();
    1630             :     nsresult rv;
    1631             : 
    1632           3 :     LOG(("nsHttpConnectionMgr::DispatchTransaction "
    1633             :          "[ent-ci=%s %p trans=%p caps=%x conn=%p priority=%d]\n",
    1634             :          ent->mConnInfo->HashKey().get(), ent, trans, caps, conn, priority));
    1635             : 
    1636             :     // It is possible for a rate-paced transaction to be dispatched independent
    1637             :     // of the token bucket when the amount of parallelization has changed or
    1638             :     // when a muxed connection (e.g. h2) becomes available.
    1639           3 :     trans->CancelPacing(NS_OK);
    1640             : 
    1641           3 :     if (conn->UsingSpdy()) {
    1642           0 :         LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s, "
    1643             :              "Connection host = %s\n",
    1644             :              trans->ConnectionInfo()->Origin(),
    1645             :              conn->ConnectionInfo()->Origin()));
    1646           0 :         rv = conn->Activate(trans, caps, priority);
    1647           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv), "SPDY Cannot Fail Dispatch");
    1648           0 :         if (NS_SUCCEEDED(rv) && !trans->GetPendingTime().IsNull()) {
    1649           0 :             AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_SPDY,
    1650           0 :                 trans->GetPendingTime(), TimeStamp::Now());
    1651           0 :             trans->SetPendingTime(false);
    1652             :         }
    1653           0 :         return rv;
    1654             :     }
    1655             : 
    1656           3 :     MOZ_ASSERT(conn && !conn->Transaction(),
    1657             :                "DispatchTranaction() on non spdy active connection");
    1658             : 
    1659           3 :     rv = DispatchAbstractTransaction(ent, trans, caps, conn, priority);
    1660             : 
    1661           3 :     if (NS_SUCCEEDED(rv) && !trans->GetPendingTime().IsNull()) {
    1662           3 :         AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_HTTP,
    1663           3 :                             trans->GetPendingTime(), TimeStamp::Now());
    1664           3 :         trans->SetPendingTime(false);
    1665             :     }
    1666           3 :     return rv;
    1667             : }
    1668             : 
    1669             : //-----------------------------------------------------------------------------
    1670             : // ConnectionHandle
    1671             : //
    1672             : // thin wrapper around a real connection, used to keep track of references
    1673             : // to the connection to determine when the connection may be reused.  the
    1674             : // transaction owns a reference to this handle.  this extra
    1675             : // layer of indirection greatly simplifies consumer code, avoiding the
    1676             : // need for consumer code to know when to give the connection back to the
    1677             : // connection manager.
    1678             : //
    1679             : class ConnectionHandle : public nsAHttpConnection
    1680             : {
    1681             : public:
    1682             :     NS_DECL_THREADSAFE_ISUPPORTS
    1683          20 :     NS_DECL_NSAHTTPCONNECTION(mConn)
    1684             : 
    1685           3 :     explicit ConnectionHandle(nsHttpConnection *conn) : mConn(conn) { }
    1686           0 :     void Reset() { mConn = nullptr; }
    1687             : private:
    1688             :     virtual ~ConnectionHandle();
    1689             :     RefPtr<nsHttpConnection> mConn;
    1690             : };
    1691             : 
    1692             : nsAHttpConnection *
    1693           0 : nsHttpConnectionMgr::MakeConnectionHandle(nsHttpConnection *aWrapped)
    1694             : {
    1695           0 :     return new ConnectionHandle(aWrapped);
    1696             : }
    1697             : 
    1698           9 : ConnectionHandle::~ConnectionHandle()
    1699             : {
    1700           3 :     if (mConn) {
    1701           3 :         nsresult rv = gHttpHandler->ReclaimConnection(mConn);
    1702           3 :         if (NS_FAILED(rv)) {
    1703           0 :             LOG(("ConnectionHandle::~ConnectionHandle\n"
    1704             :                  "    failed to reclaim connection\n"));
    1705             :         }
    1706             :     }
    1707           9 : }
    1708             : 
    1709          12 : NS_IMPL_ISUPPORTS0(ConnectionHandle)
    1710             : 
    1711             : // Use this method for dispatching nsAHttpTransction's. It can only safely be
    1712             : // used upon first use of a connection when NPN has not negotiated SPDY vs
    1713             : // HTTP/1 yet as multiplexing onto an existing SPDY session requires a
    1714             : // concrete nsHttpTransaction
    1715             : nsresult
    1716           3 : nsHttpConnectionMgr::DispatchAbstractTransaction(nsConnectionEntry *ent,
    1717             :                                                  nsAHttpTransaction *aTrans,
    1718             :                                                  uint32_t caps,
    1719             :                                                  nsHttpConnection *conn,
    1720             :                                                  int32_t priority)
    1721             : {
    1722             :     nsresult rv;
    1723           3 :     MOZ_ASSERT(!conn->UsingSpdy(),
    1724             :                "Spdy Must Not Use DispatchAbstractTransaction");
    1725           3 :     LOG(("nsHttpConnectionMgr::DispatchAbstractTransaction "
    1726             :          "[ci=%s trans=%p caps=%x conn=%p]\n",
    1727             :          ent->mConnInfo->HashKey().get(), aTrans, caps, conn));
    1728             : 
    1729           6 :     RefPtr<nsAHttpTransaction> transaction(aTrans);
    1730           6 :     RefPtr<ConnectionHandle> handle = new ConnectionHandle(conn);
    1731             : 
    1732             :     // give the transaction the indirect reference to the connection.
    1733           3 :     transaction->SetConnection(handle);
    1734             : 
    1735           3 :     rv = conn->Activate(transaction, caps, priority);
    1736           3 :     if (NS_FAILED(rv)) {
    1737           0 :       LOG(("  conn->Activate failed [rv=%" PRIx32 "]\n", static_cast<uint32_t>(rv)));
    1738           0 :         ent->mActiveConns.RemoveElement(conn);
    1739           0 :         DecrementActiveConnCount(conn);
    1740           0 :         ConditionallyStopTimeoutTick();
    1741             : 
    1742             :         // sever back references to connection, and do so without triggering
    1743             :         // a call to ReclaimConnection ;-)
    1744           0 :         transaction->SetConnection(nullptr);
    1745           0 :         handle->Reset(); // destroy the connection
    1746             :     }
    1747             : 
    1748           6 :     return rv;
    1749             : }
    1750             : 
    1751             : void
    1752           3 : nsHttpConnectionMgr::ReportProxyTelemetry(nsConnectionEntry *ent)
    1753             : {
    1754             :     enum { PROXY_NONE = 1, PROXY_HTTP = 2, PROXY_SOCKS = 3, PROXY_HTTPS = 4 };
    1755             : 
    1756           3 :     if (!ent->mConnInfo->UsingProxy())
    1757           3 :         Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_NONE);
    1758           0 :     else if (ent->mConnInfo->UsingHttpsProxy())
    1759           0 :         Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_HTTPS);
    1760           0 :     else if (ent->mConnInfo->UsingHttpProxy())
    1761           0 :         Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_HTTP);
    1762             :     else
    1763           0 :         Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_SOCKS);
    1764           3 : }
    1765             : 
    1766             : nsresult
    1767           3 : nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
    1768             : {
    1769           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1770             : 
    1771             :     // since "adds" and "cancels" are processed asynchronously and because
    1772             :     // various events might trigger an "add" directly on the socket thread,
    1773             :     // we must take care to avoid dispatching a transaction that has already
    1774             :     // been canceled (see bug 190001).
    1775           3 :     if (NS_FAILED(trans->Status())) {
    1776           0 :         LOG(("  transaction was canceled... dropping event!\n"));
    1777           0 :         return NS_OK;
    1778             :     }
    1779             : 
    1780           3 :     trans->SetPendingTime();
    1781             : 
    1782           3 :     Http2PushedStream *pushedStream = trans->GetPushedStream();
    1783           3 :     if (pushedStream) {
    1784           0 :         LOG(("  ProcessNewTransaction %p tied to h2 session push %p\n",
    1785             :              trans, pushedStream->Session()));
    1786             :         return pushedStream->Session()->
    1787           0 :             AddStream(trans, trans->Priority(), false, nullptr) ?
    1788           0 :             NS_OK : NS_ERROR_UNEXPECTED;
    1789             :     }
    1790             : 
    1791           3 :     nsresult rv = NS_OK;
    1792           3 :     nsHttpConnectionInfo *ci = trans->ConnectionInfo();
    1793           3 :     MOZ_ASSERT(ci);
    1794             : 
    1795             :     nsConnectionEntry *ent =
    1796           3 :         GetOrCreateConnectionEntry(ci, !!trans->TunnelProvider());
    1797             : 
    1798           3 :     ReportProxyTelemetry(ent);
    1799             : 
    1800             :     // Check if the transaction already has a sticky reference to a connection.
    1801             :     // If so, then we can just use it directly by transferring its reference
    1802             :     // to the new connection variable instead of searching for a new one
    1803             : 
    1804           3 :     nsAHttpConnection *wrappedConnection = trans->Connection();
    1805           6 :     RefPtr<nsHttpConnection> conn;
    1806           6 :     RefPtr<PendingTransactionInfo> pendingTransInfo;
    1807           3 :     if (wrappedConnection)
    1808           0 :         conn = wrappedConnection->TakeHttpConnection();
    1809             : 
    1810           3 :     if (conn) {
    1811           0 :         MOZ_ASSERT(trans->Caps() & NS_HTTP_STICKY_CONNECTION);
    1812           0 :         LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
    1813             :              "sticky connection=%p\n", trans, conn.get()));
    1814             : 
    1815           0 :         if (static_cast<int32_t>(ent->mActiveConns.IndexOf(conn)) == -1) {
    1816           0 :             LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
    1817             :                  "sticky connection=%p needs to go on the active list\n", trans, conn.get()));
    1818             : 
    1819             :             // make sure it isn't on the idle list - we expect this to be an
    1820             :             // unknown fresh connection
    1821           0 :             MOZ_ASSERT(static_cast<int32_t>(ent->mIdleConns.IndexOf(conn)) == -1);
    1822           0 :             MOZ_ASSERT(!conn->IsExperienced());
    1823             : 
    1824           0 :             AddActiveConn(conn, ent); // make it active
    1825             :         }
    1826             : 
    1827           0 :         trans->SetConnection(nullptr);
    1828           0 :         rv = DispatchTransaction(ent, trans, conn);
    1829             :     } else {
    1830           3 :         pendingTransInfo = new PendingTransactionInfo(trans);
    1831           3 :         rv = TryDispatchTransaction(ent, !!trans->TunnelProvider(), pendingTransInfo);
    1832             :     }
    1833             : 
    1834           3 :     if (NS_SUCCEEDED(rv)) {
    1835           0 :         LOG(("  ProcessNewTransaction Dispatch Immediately trans=%p\n", trans));
    1836           0 :         return rv;
    1837             :     }
    1838             : 
    1839           3 :     if (rv == NS_ERROR_NOT_AVAILABLE) {
    1840           3 :         if (!pendingTransInfo) {
    1841           0 :             pendingTransInfo = new PendingTransactionInfo(trans);
    1842             :         }
    1843           3 :         if (trans->Caps() & NS_HTTP_URGENT_START) {
    1844           1 :             LOG(("  adding transaction to pending queue "
    1845             :                  "[trans=%p urgent-start-count=%" PRIuSIZE "]\n",
    1846             :                  trans, ent->mUrgentStartQ.Length() + 1));
    1847             :             // put this transaction on the urgent-start queue...
    1848           1 :             InsertTransactionSorted(ent->mUrgentStartQ, pendingTransInfo);
    1849             :         } else {
    1850           2 :             LOG(("  adding transaction to pending queue "
    1851             :                  "[trans=%p pending-count=%" PRIuSIZE "]\n",
    1852             :                  trans, ent->PendingQLength() + 1));
    1853             :             // put this transaction on the pending queue...
    1854           2 :             ent->InsertTransaction(pendingTransInfo);
    1855             :         }
    1856           3 :         return NS_OK;
    1857             :     }
    1858             : 
    1859           0 :     LOG(("  ProcessNewTransaction Hard Error trans=%p rv=%" PRIx32 "\n",
    1860             :          trans, static_cast<uint32_t>(rv)));
    1861           0 :     return rv;
    1862             : }
    1863             : 
    1864             : 
    1865             : void
    1866           3 : nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
    1867             :                                    nsConnectionEntry *ent)
    1868             : {
    1869           3 :     ent->mActiveConns.AppendElement(conn);
    1870           3 :     mNumActiveConns++;
    1871           3 :     ActivateTimeoutTick();
    1872           3 : }
    1873             : 
    1874             : void
    1875           3 : nsHttpConnectionMgr::DecrementActiveConnCount(nsHttpConnection *conn)
    1876             : {
    1877           3 :     mNumActiveConns--;
    1878           3 :     if (conn->EverUsedSpdy())
    1879           0 :         mNumSpdyActiveConns--;
    1880           3 : }
    1881             : 
    1882             : void
    1883           3 : nsHttpConnectionMgr::StartedConnect()
    1884             : {
    1885           3 :     mNumActiveConns++;
    1886           3 :     ActivateTimeoutTick(); // likely disabled by RecvdConnect()
    1887           3 : }
    1888             : 
    1889             : void
    1890           3 : nsHttpConnectionMgr::RecvdConnect()
    1891             : {
    1892           3 :     mNumActiveConns--;
    1893           3 :     ConditionallyStopTimeoutTick();
    1894           3 : }
    1895             : 
    1896             : void
    1897           0 : nsHttpConnectionMgr::ReleaseClaimedSockets(nsConnectionEntry *ent,
    1898             :                                            PendingTransactionInfo * pendingTransInfo)
    1899             : {
    1900           0 :     if (pendingTransInfo->mHalfOpen) {
    1901             :         RefPtr<nsHalfOpenSocket> halfOpen =
    1902           0 :             do_QueryReferent(pendingTransInfo->mHalfOpen);
    1903           0 :         LOG(("nsHttpConnectionMgr::ReleaseClaimedSockets "
    1904             :              "[trans=%p halfOpen=%p]",
    1905             :              pendingTransInfo->mTransaction.get(),
    1906             :              halfOpen.get()));
    1907           0 :         if (halfOpen) {
    1908           0 :             halfOpen->Unclaim();
    1909             :         }
    1910           0 :         pendingTransInfo->mHalfOpen = nullptr;
    1911           0 :     } else if (pendingTransInfo->mActiveConn) {
    1912             :         RefPtr<nsHttpConnection> activeConn =
    1913           0 :             do_QueryReferent(pendingTransInfo->mActiveConn);
    1914           0 :         if (activeConn && activeConn->Transaction() &&
    1915           0 :             activeConn->Transaction()->IsNullTransaction()) {
    1916           0 :             NullHttpTransaction *nullTrans = activeConn->Transaction()->QueryNullTransaction();
    1917           0 :             nullTrans->Unclaim();
    1918           0 :             LOG(("nsHttpConnectionMgr::ReleaseClaimedSockets - mark %p unclaimed.",
    1919             :                  activeConn.get()));
    1920             :         }
    1921             :     }
    1922           0 : }
    1923             : 
    1924             : nsresult
    1925           3 : nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
    1926             :                                      nsAHttpTransaction *trans,
    1927             :                                      uint32_t caps,
    1928             :                                      bool speculative,
    1929             :                                      bool isFromPredictor,
    1930             :                                      bool allow1918,
    1931             :                                      PendingTransactionInfo *pendingTransInfo)
    1932             : {
    1933           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1934           3 :     MOZ_ASSERT((speculative && !pendingTransInfo) ||
    1935             :                (!speculative && pendingTransInfo));
    1936             : 
    1937             :     RefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans, caps,
    1938             :                                                          speculative,
    1939           6 :                                                          isFromPredictor);
    1940             : 
    1941           3 :     if (speculative) {
    1942           0 :         sock->SetAllow1918(allow1918);
    1943             :     }
    1944             :     // The socket stream holds the reference to the half open
    1945             :     // socket - so if the stream fails to init the half open
    1946             :     // will go away.
    1947           3 :     nsresult rv = sock->SetupPrimaryStreams();
    1948           3 :     NS_ENSURE_SUCCESS(rv, rv);
    1949             : 
    1950           3 :     if (pendingTransInfo) {
    1951             :         pendingTransInfo->mHalfOpen =
    1952           3 :             do_GetWeakReference(static_cast<nsISupportsWeakReference*>(sock));
    1953           6 :         DebugOnly<bool> claimed = sock->Claim();
    1954           3 :         MOZ_ASSERT(claimed);
    1955             :     }
    1956             : 
    1957           3 :     ent->mHalfOpens.AppendElement(sock);
    1958           3 :     mNumHalfOpenConns++;
    1959           3 :     return NS_OK;
    1960             : }
    1961             : 
    1962             : void
    1963           0 : nsHttpConnectionMgr::DispatchSpdyPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
    1964             :                                           nsConnectionEntry *ent,
    1965             :                                           nsHttpConnection *conn)
    1966             : {
    1967           0 :     if (pendingQ.Length() == 0) {
    1968           0 :         return;
    1969             :     }
    1970             : 
    1971           0 :     nsTArray<RefPtr<PendingTransactionInfo>> leftovers;
    1972             :     uint32_t index;
    1973             :     // Dispatch all the transactions we can
    1974           0 :     for (index = 0;
    1975           0 :          index < pendingQ.Length() && conn->CanDirectlyActivate();
    1976             :          ++index) {
    1977           0 :         PendingTransactionInfo *pendingTransInfo = pendingQ[index];
    1978             : 
    1979           0 :         if (!(pendingTransInfo->mTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
    1980           0 :             pendingTransInfo->mTransaction->Caps() & NS_HTTP_DISALLOW_SPDY) {
    1981           0 :             leftovers.AppendElement(pendingTransInfo);
    1982           0 :             continue;
    1983             :         }
    1984             : 
    1985           0 :         nsresult rv = DispatchTransaction(ent, pendingTransInfo->mTransaction,
    1986           0 :                                           conn);
    1987           0 :         if (NS_FAILED(rv)) {
    1988             :             // this cannot happen, but if due to some bug it does then
    1989             :             // close the transaction
    1990           0 :             MOZ_ASSERT(false, "Dispatch SPDY Transaction");
    1991             :             LOG(("ProcessSpdyPendingQ Dispatch Transaction failed trans=%p\n",
    1992             :                  pendingTransInfo->mTransaction.get()));
    1993             :             pendingTransInfo->mTransaction->Close(rv);
    1994             :         }
    1995           0 :         ReleaseClaimedSockets(ent, pendingTransInfo);
    1996             :     }
    1997             : 
    1998             :     // Slurp up the rest of the pending queue into our leftovers bucket (we
    1999             :     // might have some left if conn->CanDirectlyActivate returned false)
    2000           0 :     for (; index < pendingQ.Length(); ++index) {
    2001           0 :         PendingTransactionInfo *pendingTransInfo = pendingQ[index];
    2002           0 :         leftovers.AppendElement(pendingTransInfo);
    2003             :     }
    2004             : 
    2005             :     // Put the leftovers back in the pending queue and get rid of the
    2006             :     // transactions we dispatched
    2007           0 :     leftovers.SwapElements(pendingQ);
    2008           0 :     leftovers.Clear();
    2009             : }
    2010             : 
    2011             : // This function tries to dispatch the pending spdy transactions on
    2012             : // the connection entry sent in as an argument. It will do so on the
    2013             : // active spdy connection either in that same entry or from the
    2014             : // coalescing hash table
    2015             : 
    2016             : void
    2017           0 : nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
    2018             : {
    2019           0 :     nsHttpConnection *conn = GetSpdyActiveConn(ent);
    2020           0 :     if (!conn || !conn->CanDirectlyActivate()) {
    2021           0 :         return;
    2022             :     }
    2023             : 
    2024           0 :     DispatchSpdyPendingQ(ent->mUrgentStartQ, ent, conn);
    2025           0 :     if (!conn->CanDirectlyActivate()) {
    2026           0 :         return;
    2027             :     }
    2028             : 
    2029           0 :     nsTArray<RefPtr<PendingTransactionInfo>> pendingQ;
    2030             :     // XXX Get all transactions for SPDY currently.
    2031           0 :     ent->AppendPendingQForNonFocusedWindows(0, pendingQ);
    2032           0 :     DispatchSpdyPendingQ(pendingQ, ent, conn);
    2033             : 
    2034             :     // Put the leftovers back in the pending queue.
    2035           0 :     for (const auto& transactionInfo : pendingQ) {
    2036           0 :         ent->InsertTransaction(transactionInfo);
    2037             :     }
    2038             : }
    2039             : 
    2040             : void
    2041           0 : nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ(int32_t, ARefBase *)
    2042             : {
    2043           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2044           0 :     LOG(("nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ\n"));
    2045           0 :     for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    2046           0 :         ProcessSpdyPendingQ(iter.Data());
    2047             :     }
    2048           0 : }
    2049             : 
    2050             : // Given a connection entry, return an active h2 connection
    2051             : // that can be directly activated or null
    2052             : nsHttpConnection *
    2053          12 : nsHttpConnectionMgr::GetSpdyActiveConn(nsConnectionEntry *ent)
    2054             : {
    2055          12 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2056          12 :     MOZ_ASSERT(ent);
    2057             : 
    2058          12 :     nsHttpConnection *experienced = nullptr;
    2059          12 :     nsHttpConnection *noExperience = nullptr;
    2060          12 :     uint32_t activeLen = ent->mActiveConns.Length();
    2061          12 :     nsHttpConnectionInfo *ci = ent->mConnInfo;
    2062             :     uint32_t index;
    2063             : 
    2064             :     // activeLen should generally be 1.. this is a setup race being resolved
    2065             :     // take a conn who can activate and is experienced
    2066          12 :     for (index = 0; index < activeLen; ++index) {
    2067           0 :         nsHttpConnection *tmp = ent->mActiveConns[index];
    2068           0 :         if (tmp->CanDirectlyActivate()) {
    2069           0 :             if (tmp->IsExperienced()) {
    2070           0 :                 experienced = tmp;
    2071           0 :                 break;
    2072             :             }
    2073           0 :             noExperience = tmp; // keep looking for a better option
    2074             :         }
    2075             :     }
    2076             : 
    2077             :     // if that worked, cleanup anything else and exit
    2078          12 :     if (experienced) {
    2079           0 :         for (index = 0; index < activeLen; ++index) {
    2080           0 :             nsHttpConnection *tmp = ent->mActiveConns[index];
    2081             :             // in the case where there is a functional h2 session, drop the others
    2082           0 :             if (tmp != experienced) {
    2083           0 :                 tmp->DontReuse();
    2084             :             }
    2085             :         }
    2086           0 :         for (int32_t index = ent->mHalfOpenFastOpenBackups.Length() - 1; index >= 0; --index) {
    2087           0 :              LOG(("GetSpdyActiveConn() shutting down connection in fast "
    2088             :                  "open state (%p) because we have an experienced spdy "
    2089             :                  "connection (%p).\n",
    2090             :                  ent->mHalfOpenFastOpenBackups[index].get(), experienced));
    2091           0 :              ent->mHalfOpenFastOpenBackups[index]->CancelFastOpenConnection();
    2092             :         }
    2093             : 
    2094           0 :         LOG(("GetSpdyActiveConn() request for ent %p %s "
    2095             :              "found an active experienced connection %p in native connection entry\n",
    2096             :              ent, ci->HashKey().get(), experienced));
    2097           0 :         return experienced;
    2098             :     }
    2099             : 
    2100          12 :     if (noExperience) {
    2101           0 :         LOG(("GetSpdyActiveConn() request for ent %p %s "
    2102             :              "found an active but inexperienced connection %p in native connection entry\n",
    2103             :              ent, ci->HashKey().get(), noExperience));
    2104           0 :         return noExperience;
    2105             :     }
    2106             : 
    2107             :     // there was no active spdy connection in the connection entry, but
    2108             :     // there might be one in the hash table for coalescing
    2109          12 :     nsHttpConnection *existingConn = FindCoalescableConnection(ent, false);
    2110          12 :     if (existingConn) {
    2111           0 :         LOG(("GetSpdyActiveConn() request for ent %p %s "
    2112             :              "found an active connection %p in the coalescing hashtable\n",
    2113             :              ent, ci->HashKey().get(), existingConn));
    2114           0 :         return existingConn;
    2115             :     }
    2116             : 
    2117          12 :     LOG(("GetSpdyActiveConn() request for ent %p %s "
    2118             :          "did not find an active connection\n", ent, ci->HashKey().get()));
    2119          12 :     return nullptr;
    2120             : }
    2121             : 
    2122             : //-----------------------------------------------------------------------------
    2123             : 
    2124             : void
    2125           0 : nsHttpConnectionMgr::OnMsgShutdown(int32_t, ARefBase *param)
    2126             : {
    2127           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2128           0 :     LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
    2129             : 
    2130           0 :     gHttpHandler->StopRequestTokenBucket();
    2131             : 
    2132           0 :     for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    2133           0 :         nsAutoPtr<nsConnectionEntry>& ent = iter.Data();
    2134             : 
    2135             :         // Close all active connections.
    2136           0 :         while (ent->mActiveConns.Length()) {
    2137           0 :             RefPtr<nsHttpConnection> conn(ent->mActiveConns[0]);
    2138           0 :             ent->mActiveConns.RemoveElementAt(0);
    2139           0 :             DecrementActiveConnCount(conn);
    2140             :             // Since nsHttpConnection::Close doesn't break the bond with
    2141             :             // the connection's transaction, we must explicitely tell it
    2142             :             // to close its transaction and not just self.
    2143           0 :             conn->CloseTransaction(conn->Transaction(), NS_ERROR_ABORT, true);
    2144             :         }
    2145             : 
    2146             :         // Close all idle connections.
    2147           0 :         while (ent->mIdleConns.Length()) {
    2148           0 :             RefPtr<nsHttpConnection> conn(ent->mIdleConns[0]);
    2149             : 
    2150           0 :             ent->mIdleConns.RemoveElementAt(0);
    2151           0 :             mNumIdleConns--;
    2152             : 
    2153           0 :             conn->Close(NS_ERROR_ABORT);
    2154             :         }
    2155             : 
    2156             :         // If all idle connections are removed we can stop pruning dead
    2157             :         // connections.
    2158           0 :         ConditionallyStopPruneDeadConnectionsTimer();
    2159             : 
    2160             :         // Close all urgentStart transactions.
    2161           0 :         while (ent->mUrgentStartQ.Length()) {
    2162           0 :             PendingTransactionInfo *pendingTransInfo = ent->mUrgentStartQ[0];
    2163           0 :             pendingTransInfo->mTransaction->Close(NS_ERROR_ABORT);
    2164           0 :             ent->mUrgentStartQ.RemoveElementAt(0);
    2165             :         }
    2166             : 
    2167             :         // Close all pending transactions.
    2168           0 :         for (auto it = ent->mPendingTransactionTable.Iter();
    2169           0 :              !it.Done();
    2170           0 :              it.Next()) {
    2171           0 :             while (it.UserData()->Length()) {
    2172           0 :                 PendingTransactionInfo *pendingTransInfo = (*it.UserData())[0];
    2173           0 :                 pendingTransInfo->mTransaction->Close(NS_ERROR_ABORT);
    2174           0 :                 it.UserData()->RemoveElementAt(0);
    2175             :             }
    2176             :         }
    2177           0 :         ent->mPendingTransactionTable.Clear();
    2178             : 
    2179             :         // Close all half open tcp connections.
    2180           0 :         for (int32_t i = int32_t(ent->mHalfOpens.Length()) - 1; i >= 0; i--) {
    2181           0 :             ent->mHalfOpens[i]->Abandon();
    2182             :         }
    2183             : 
    2184           0 :         MOZ_DIAGNOSTIC_ASSERT(ent->mHalfOpenFastOpenBackups.Length() == 0 &&
    2185             :                               !ent->mDoNotDestroy);
    2186           0 :         iter.Remove();
    2187             :     }
    2188             : 
    2189           0 :     if (mTimeoutTick) {
    2190           0 :         mTimeoutTick->Cancel();
    2191           0 :         mTimeoutTick = nullptr;
    2192           0 :         mTimeoutTickArmed = false;
    2193             :     }
    2194           0 :     if (mTimer) {
    2195           0 :       mTimer->Cancel();
    2196           0 :       mTimer = nullptr;
    2197             :     }
    2198           0 :     if (mTrafficTimer) {
    2199           0 :       mTrafficTimer->Cancel();
    2200           0 :       mTrafficTimer = nullptr;
    2201             :     }
    2202           0 :     DestroyThrottleTicker();
    2203           0 :     mActiveTransactions[false].Clear();
    2204           0 :     mActiveTransactions[true].Clear();
    2205             : 
    2206           0 :     mCoalescingHash.Clear();
    2207             : 
    2208             :     // signal shutdown complete
    2209             :     nsCOMPtr<nsIRunnable> runnable =
    2210             :         new ConnEvent(this, &nsHttpConnectionMgr::OnMsgShutdownConfirm,
    2211           0 :                       0, param);
    2212           0 :     NS_DispatchToMainThread(runnable);
    2213           0 : }
    2214             : 
    2215             : void
    2216           0 : nsHttpConnectionMgr::OnMsgShutdownConfirm(int32_t priority, ARefBase *param)
    2217             : {
    2218           0 :     MOZ_ASSERT(NS_IsMainThread());
    2219           0 :     LOG(("nsHttpConnectionMgr::OnMsgShutdownConfirm\n"));
    2220             : 
    2221           0 :     BoolWrapper *shutdown = static_cast<BoolWrapper *>(param);
    2222           0 :     shutdown->mBool = true;
    2223           0 : }
    2224             : 
    2225             : void
    2226           3 : nsHttpConnectionMgr::OnMsgNewTransaction(int32_t priority, ARefBase *param)
    2227             : {
    2228           3 :     LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
    2229             : 
    2230           3 :     nsHttpTransaction *trans = static_cast<nsHttpTransaction *>(param);
    2231           3 :     trans->SetPriority(priority);
    2232           3 :     nsresult rv = ProcessNewTransaction(trans);
    2233           3 :     if (NS_FAILED(rv))
    2234           0 :         trans->Close(rv); // for whatever its worth
    2235           3 : }
    2236             : 
    2237             : void
    2238           0 : nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, ARefBase *param)
    2239             : {
    2240           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2241           0 :     LOG(("nsHttpConnectionMgr::OnMsgReschedTransaction [trans=%p]\n", param));
    2242             : 
    2243           0 :     RefPtr<nsHttpTransaction> trans = static_cast<nsHttpTransaction *>(param);
    2244           0 :     trans->SetPriority(priority);
    2245             : 
    2246           0 :     if (!trans->ConnectionInfo()) {
    2247           0 :         return;
    2248             :     }
    2249           0 :     nsConnectionEntry *ent = mCT.Get(trans->ConnectionInfo()->HashKey());
    2250             : 
    2251           0 :     if (ent) {
    2252           0 :         int32_t caps = trans->Caps();
    2253           0 :         nsTArray<RefPtr<PendingTransactionInfo>> *pendingQ = nullptr;
    2254           0 :         if (caps & NS_HTTP_URGENT_START) {
    2255           0 :             pendingQ = &(ent->mUrgentStartQ);
    2256             :         } else {
    2257             :             pendingQ =
    2258           0 :                 ent->mPendingTransactionTable.Get(
    2259           0 :                     trans->TopLevelOuterContentWindowId());
    2260             :         }
    2261             : 
    2262             :         int32_t index = pendingQ
    2263           0 :             ? pendingQ->IndexOf(trans, 0, PendingComparator())
    2264           0 :             : -1;
    2265           0 :         if (index >= 0) {
    2266           0 :             RefPtr<PendingTransactionInfo> pendingTransInfo = (*pendingQ)[index];
    2267           0 :             pendingQ->RemoveElementAt(index);
    2268           0 :             InsertTransactionSorted(*pendingQ, pendingTransInfo);
    2269             :         }
    2270             :     }
    2271             : }
    2272             : 
    2273           0 : void nsHttpConnectionMgr::OnMsgUpdateClassOfServiceOnTransaction(int32_t arg, ARefBase *param)
    2274             : {
    2275           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2276           0 :     LOG(("nsHttpConnectionMgr::OnMsgUpdateClassOfServiceOnTransaction [trans=%p]\n", param));
    2277             : 
    2278           0 :     uint32_t cos = static_cast<uint32_t>(arg);
    2279           0 :     nsHttpTransaction *trans = static_cast<nsHttpTransaction *>(param);
    2280             : 
    2281           0 :     uint32_t previous = trans->ClassOfService();
    2282           0 :     trans->SetClassOfService(cos);
    2283             : 
    2284           0 :     if ((previous ^ cos) & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED)) {
    2285           0 :         Unused << RescheduleTransaction(trans, trans->Priority());
    2286             :     }
    2287           0 : }
    2288             : 
    2289             : void
    2290           0 : nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, ARefBase *param)
    2291             : {
    2292           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2293           0 :     LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
    2294             : 
    2295           0 :     nsresult closeCode = static_cast<nsresult>(reason);
    2296             : 
    2297             :     // caller holds a ref to param/trans on stack
    2298           0 :     nsHttpTransaction *trans = static_cast<nsHttpTransaction *>(param);
    2299             : 
    2300             :     //
    2301             :     // if the transaction owns a connection and the transaction is not done,
    2302             :     // then ask the connection to close the transaction.  otherwise, close the
    2303             :     // transaction directly (removing it from the pending queue first).
    2304             :     //
    2305           0 :     RefPtr<nsAHttpConnection> conn(trans->Connection());
    2306           0 :     if (conn && !trans->IsDone()) {
    2307           0 :         conn->CloseTransaction(trans, closeCode);
    2308             :     } else {
    2309           0 :         nsConnectionEntry *ent = nullptr;
    2310           0 :         if (trans->ConnectionInfo()) {
    2311           0 :             ent = mCT.Get(trans->ConnectionInfo()->HashKey());
    2312             :         }
    2313           0 :         if (ent) {
    2314           0 :             uint32_t caps = trans->Caps();
    2315             :             int32_t transIndex;
    2316             :             // We will abandon all half-open sockets belonging to the given
    2317             :             // transaction.
    2318             :             nsTArray<RefPtr<PendingTransactionInfo>> *infoArray;
    2319           0 :             RefPtr<PendingTransactionInfo> pendingTransInfo;
    2320           0 :             if (caps & NS_HTTP_URGENT_START) {
    2321           0 :                 infoArray = &ent->mUrgentStartQ;
    2322             :             } else {
    2323           0 :                 infoArray = ent->mPendingTransactionTable.Get(
    2324           0 :                     trans->TopLevelOuterContentWindowId());
    2325             :             }
    2326             : 
    2327           0 :             transIndex = infoArray
    2328           0 :                 ? infoArray->IndexOf(trans, 0, PendingComparator())
    2329           0 :                 : -1;
    2330           0 :             if (transIndex >=0) {
    2331           0 :                 LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]"
    2332             :                      " found in urgentStart queue\n", trans));
    2333           0 :                 pendingTransInfo = (*infoArray)[transIndex];
    2334             :                 // We do not need to ReleaseClaimedSockets while we are
    2335             :                 // going to close them all any way!
    2336           0 :                 infoArray->RemoveElementAt(transIndex);
    2337             :             }
    2338             : 
    2339             :             // Abandon all half-open sockets belonging to the given transaction.
    2340           0 :             if (pendingTransInfo) {
    2341             :                 RefPtr<nsHalfOpenSocket> half =
    2342           0 :                     do_QueryReferent(pendingTransInfo->mHalfOpen);
    2343           0 :                 if (half) {
    2344           0 :                     half->Abandon();
    2345             :                 }
    2346           0 :                 pendingTransInfo->mHalfOpen = nullptr;
    2347             :             }
    2348             :         }
    2349             : 
    2350           0 :         trans->Close(closeCode);
    2351             : 
    2352             :         // Cancel is a pretty strong signal that things might be hanging
    2353             :         // so we want to cancel any null transactions related to this connection
    2354             :         // entry. They are just optimizations, but they aren't hooked up to
    2355             :         // anything that might get canceled from the rest of gecko, so best
    2356             :         // to assume that's what was meant by the cancel we did receive if
    2357             :         // it only applied to something in the queue.
    2358           0 :         for (uint32_t index = 0;
    2359           0 :              ent && (index < ent->mActiveConns.Length());
    2360             :              ++index) {
    2361           0 :             nsHttpConnection *activeConn = ent->mActiveConns[index];
    2362           0 :             nsAHttpTransaction *liveTransaction = activeConn->Transaction();
    2363           0 :             if (liveTransaction && liveTransaction->IsNullTransaction()) {
    2364           0 :                 LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p] "
    2365             :                      "also canceling Null Transaction %p on conn %p\n",
    2366             :                      trans, liveTransaction, activeConn));
    2367           0 :                 activeConn->CloseTransaction(liveTransaction, closeCode);
    2368             :             }
    2369             :         }
    2370             :     }
    2371           0 : }
    2372             : 
    2373             : void
    2374           6 : nsHttpConnectionMgr::OnMsgProcessPendingQ(int32_t, ARefBase *param)
    2375             : {
    2376           6 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2377           6 :     nsHttpConnectionInfo *ci = static_cast<nsHttpConnectionInfo *>(param);
    2378             : 
    2379           6 :     if (!ci) {
    2380           0 :         LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=nullptr]\n"));
    2381             :         // Try and dispatch everything
    2382           0 :         for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    2383           0 :             Unused << ProcessPendingQForEntry(iter.Data(), true);
    2384             :         }
    2385           0 :         return;
    2386             :     }
    2387             : 
    2388           6 :     LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n",
    2389             :          ci->HashKey().get()));
    2390             : 
    2391             :     // start by processing the queue identified by the given connection info.
    2392           6 :     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
    2393           6 :     if (!(ent && ProcessPendingQForEntry(ent, false))) {
    2394             :         // if we reach here, it means that we couldn't dispatch a transaction
    2395             :         // for the specified connection info.  walk the connection table...
    2396          16 :         for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    2397          10 :             if (ProcessPendingQForEntry(iter.Data(), false)) {
    2398           0 :                 break;
    2399             :             }
    2400             :         }
    2401             :     }
    2402             : }
    2403             : 
    2404             : nsresult
    2405           0 : nsHttpConnectionMgr::CancelTransactions(nsHttpConnectionInfo *ci, nsresult code)
    2406             : {
    2407           0 :     LOG(("nsHttpConnectionMgr::CancelTransactions %s\n",ci->HashKey().get()));
    2408             : 
    2409           0 :     int32_t intReason = static_cast<int32_t>(code);
    2410           0 :     return PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransactions, intReason, ci);
    2411             : }
    2412             : 
    2413             : void
    2414           0 : nsHttpConnectionMgr::CancelTransactionsHelper(
    2415             :     nsTArray<RefPtr<nsHttpConnectionMgr::PendingTransactionInfo>> &pendingQ,
    2416             :     const nsHttpConnectionInfo *ci,
    2417             :     const nsHttpConnectionMgr::nsConnectionEntry *ent,
    2418             :     nsresult reason)
    2419             : {
    2420           0 :     for (const auto& pendingTransInfo : pendingQ) {
    2421           0 :         LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p %p\n",
    2422             :              ci->HashKey().get(), ent, pendingTransInfo->mTransaction.get()));
    2423           0 :         pendingTransInfo->mTransaction->Close(reason);
    2424             :     }
    2425           0 :     pendingQ.Clear();
    2426           0 : }
    2427             : 
    2428             : void
    2429           0 : nsHttpConnectionMgr::OnMsgCancelTransactions(int32_t code, ARefBase *param)
    2430             : {
    2431           0 :     nsresult reason = static_cast<nsresult>(code);
    2432           0 :     nsHttpConnectionInfo *ci = static_cast<nsHttpConnectionInfo *>(param);
    2433           0 :     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
    2434           0 :     LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p\n",
    2435             :          ci->HashKey().get(), ent));
    2436           0 :     if (!ent) {
    2437           0 :         return;
    2438             :     }
    2439             : 
    2440           0 :     CancelTransactionsHelper(ent->mUrgentStartQ, ci, ent, reason);
    2441             : 
    2442           0 :     for (auto it = ent->mPendingTransactionTable.Iter();
    2443           0 :          !it.Done();
    2444           0 :          it.Next()) {
    2445           0 :         CancelTransactionsHelper(*it.UserData(), ci, ent, reason);
    2446             :     }
    2447           0 :     ent->mPendingTransactionTable.Clear();
    2448             : }
    2449             : 
    2450             : void
    2451           0 : nsHttpConnectionMgr::OnMsgPruneDeadConnections(int32_t, ARefBase *)
    2452             : {
    2453           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2454           0 :     LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
    2455             : 
    2456             :     // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
    2457           0 :     mTimeOfNextWakeUp = UINT64_MAX;
    2458             : 
    2459             :     // check canreuse() for all idle connections plus any active connections on
    2460             :     // connection entries that are using spdy.
    2461           0 :     if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled())) {
    2462           0 :         for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    2463           0 :             nsAutoPtr<nsConnectionEntry>& ent = iter.Data();
    2464             : 
    2465           0 :             LOG(("  pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
    2466             : 
    2467             :             // Find out how long it will take for next idle connection to not
    2468             :             // be reusable anymore.
    2469           0 :             uint32_t timeToNextExpire = UINT32_MAX;
    2470           0 :             int32_t count = ent->mIdleConns.Length();
    2471           0 :             if (count > 0) {
    2472           0 :                 for (int32_t i = count - 1; i >= 0; --i) {
    2473           0 :                     RefPtr<nsHttpConnection> conn(ent->mIdleConns[i]);
    2474           0 :                     if (!conn->CanReuse()) {
    2475           0 :                         ent->mIdleConns.RemoveElementAt(i);
    2476           0 :                         conn->Close(NS_ERROR_ABORT);
    2477           0 :                         mNumIdleConns--;
    2478             :                     } else {
    2479           0 :                         timeToNextExpire =
    2480           0 :                             std::min(timeToNextExpire, conn->TimeToLive());
    2481             :                     }
    2482             :                 }
    2483             :             }
    2484             : 
    2485           0 :             if (ent->mUsingSpdy) {
    2486           0 :                 for (uint32_t i = 0; i < ent->mActiveConns.Length(); ++i) {
    2487           0 :                     nsHttpConnection* conn = ent->mActiveConns[i];
    2488           0 :                     if (conn->UsingSpdy()) {
    2489           0 :                         if (!conn->CanReuse()) {
    2490             :                             // Marking it don't-reuse will create an active
    2491             :                             // tear down if the spdy session is idle.
    2492           0 :                             conn->DontReuse();
    2493             :                         } else {
    2494           0 :                             timeToNextExpire =
    2495           0 :                                 std::min(timeToNextExpire, conn->TimeToLive());
    2496             :                         }
    2497             :                     }
    2498             :                 }
    2499             :             }
    2500             : 
    2501             :             // If time to next expire found is shorter than time to next
    2502             :             // wake-up, we need to change the time for next wake-up.
    2503           0 :             if (timeToNextExpire != UINT32_MAX) {
    2504           0 :                 uint32_t now = NowInSeconds();
    2505           0 :                 uint64_t timeOfNextExpire = now + timeToNextExpire;
    2506             :                 // If pruning of dead connections is not already scheduled to
    2507             :                 // happen or time found for next connection to expire is is
    2508             :                 // before mTimeOfNextWakeUp, we need to schedule the pruning to
    2509             :                 // happen after timeToNextExpire.
    2510           0 :                 if (!mTimer || timeOfNextExpire < mTimeOfNextWakeUp) {
    2511           0 :                     PruneDeadConnectionsAfter(timeToNextExpire);
    2512             :                 }
    2513             :             } else {
    2514           0 :                 ConditionallyStopPruneDeadConnectionsTimer();
    2515             :             }
    2516             : 
    2517           0 :             ent->RemoveEmptyPendingQ();
    2518             : 
    2519             :             // If this entry is empty, we have too many entries busy then
    2520             :             // we can clean it up and restart
    2521           0 :             if (mCT.Count()                 >  125 &&
    2522           0 :                 ent->mIdleConns.Length()    == 0 &&
    2523           0 :                 ent->mActiveConns.Length()  == 0 &&
    2524           0 :                 ent->mHalfOpens.Length()    == 0 &&
    2525           0 :                 ent->PendingQLength()       == 0 &&
    2526           0 :                 ent->mUrgentStartQ.Length() == 0 &&
    2527           0 :                 ent->mHalfOpenFastOpenBackups.Length() == 0 &&
    2528           0 :                 !ent->mDoNotDestroy &&
    2529           0 :                 (!ent->mUsingSpdy || mCT.Count() > 300)) {
    2530           0 :                 LOG(("    removing empty connection entry\n"));
    2531           0 :                 iter.Remove();
    2532           0 :                 continue;
    2533             :             }
    2534             : 
    2535             :             // Otherwise use this opportunity to compact our arrays...
    2536           0 :             ent->mIdleConns.Compact();
    2537           0 :             ent->mActiveConns.Compact();
    2538           0 :             ent->mUrgentStartQ.Compact();
    2539             : 
    2540           0 :             for (auto it = ent->mPendingTransactionTable.Iter();
    2541           0 :                  !it.Done();
    2542           0 :                  it.Next()) {
    2543           0 :                 it.UserData()->Compact();
    2544             :             }
    2545             :         }
    2546             :     }
    2547           0 : }
    2548             : 
    2549             : void
    2550           0 : nsHttpConnectionMgr::OnMsgPruneNoTraffic(int32_t, ARefBase *)
    2551             : {
    2552           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2553           0 :     LOG(("nsHttpConnectionMgr::OnMsgPruneNoTraffic\n"));
    2554             : 
    2555             :     // Prune connections without traffic
    2556           0 :     for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    2557             : 
    2558             :         // Close the connections with no registered traffic.
    2559           0 :         nsAutoPtr<nsConnectionEntry>& ent = iter.Data();
    2560             : 
    2561           0 :         LOG(("  pruning no traffic [ci=%s]\n",
    2562             :              ent->mConnInfo->HashKey().get()));
    2563             : 
    2564           0 :         uint32_t numConns = ent->mActiveConns.Length();
    2565           0 :         if (numConns) {
    2566             :             // Walk the list backwards to allow us to remove entries easily.
    2567           0 :             for (int index = numConns - 1; index >= 0; index--) {
    2568           0 :                 if (ent->mActiveConns[index]->NoTraffic()) {
    2569           0 :                     RefPtr<nsHttpConnection> conn = ent->mActiveConns[index];
    2570           0 :                     ent->mActiveConns.RemoveElementAt(index);
    2571           0 :                     DecrementActiveConnCount(conn);
    2572           0 :                     conn->Close(NS_ERROR_ABORT);
    2573           0 :                     LOG(("  closed active connection due to no traffic "
    2574             :                          "[conn=%p]\n", conn.get()));
    2575             :                 }
    2576             :             }
    2577             :         }
    2578             :     }
    2579             : 
    2580           0 :     mPruningNoTraffic = false; // not pruning anymore
    2581           0 : }
    2582             : 
    2583             : void
    2584           0 : nsHttpConnectionMgr::OnMsgVerifyTraffic(int32_t, ARefBase *)
    2585             : {
    2586           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2587           0 :     LOG(("nsHttpConnectionMgr::OnMsgVerifyTraffic\n"));
    2588             : 
    2589           0 :     if (mPruningNoTraffic) {
    2590             :       // Called in the time gap when the timeout to prune notraffic
    2591             :       // connections has triggered but the pruning hasn't happened yet.
    2592           0 :       return;
    2593             :     }
    2594             : 
    2595             :     // Mark connections for traffic verification
    2596           0 :     for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    2597           0 :         nsAutoPtr<nsConnectionEntry>& ent = iter.Data();
    2598             : 
    2599             :         // Iterate over all active connections and check them.
    2600           0 :         for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
    2601           0 :             ent->mActiveConns[index]->CheckForTraffic(true);
    2602             :         }
    2603             :         // Iterate the idle connections and unmark them for traffic checks.
    2604           0 :         for (uint32_t index = 0; index < ent->mIdleConns.Length(); ++index) {
    2605           0 :             ent->mIdleConns[index]->CheckForTraffic(false);
    2606             :         }
    2607             :     }
    2608             : 
    2609             :     // If the timer is already there. we just re-init it
    2610           0 :     if(!mTrafficTimer) {
    2611           0 :         mTrafficTimer = do_CreateInstance("@mozilla.org/timer;1");
    2612             :     }
    2613             : 
    2614             :     // failure to create a timer is not a fatal error, but dead
    2615             :     // connections will not be cleaned up as nicely
    2616           0 :     if (mTrafficTimer) {
    2617             :         // Give active connections time to get more traffic before killing
    2618             :         // them off. Default: 5000 milliseconds
    2619           0 :         mTrafficTimer->Init(this, gHttpHandler->NetworkChangedTimeout(),
    2620           0 :                             nsITimer::TYPE_ONE_SHOT);
    2621             :     } else {
    2622           0 :         NS_WARNING("failed to create timer for VerifyTraffic!");
    2623             :     }
    2624             : }
    2625             : 
    2626             : void
    2627           0 : nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup(int32_t, ARefBase *param)
    2628             : {
    2629           0 :     LOG(("nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup\n"));
    2630           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2631             : 
    2632           0 :     nsHttpConnectionInfo *ci = static_cast<nsHttpConnectionInfo *>(param);
    2633             : 
    2634           0 :     for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    2635           0 :         ClosePersistentConnections(iter.Data());
    2636             :     }
    2637             : 
    2638           0 :     if (ci)
    2639           0 :         ResetIPFamilyPreference(ci);
    2640           0 : }
    2641             : 
    2642             : void
    2643           3 : nsHttpConnectionMgr::OnMsgReclaimConnection(int32_t, ARefBase *param)
    2644             : {
    2645           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2646             : 
    2647           3 :     nsHttpConnection *conn = static_cast<nsHttpConnection *>(param);
    2648             : 
    2649             :     //
    2650             :     // 1) remove the connection from the active list
    2651             :     // 2) if keep-alive, add connection to idle list
    2652             :     // 3) post event to process the pending transaction queue
    2653             :     //
    2654             : 
    2655           6 :     nsConnectionEntry *ent = conn->ConnectionInfo() ?
    2656           6 :         mCT.Get(conn->ConnectionInfo()->HashKey()) : nullptr;
    2657             : 
    2658           3 :     if (!ent) {
    2659             :         // this can happen if the connection is made outside of the
    2660             :         // connection manager and is being "reclaimed" for use with
    2661             :         // future transactions. HTTP/2 tunnels work like this.
    2662           0 :         ent = GetOrCreateConnectionEntry(conn->ConnectionInfo(), true);
    2663           0 :         LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection conn %p "
    2664             :              "forced new hash entry %s\n",
    2665             :              conn, conn->ConnectionInfo()->HashKey().get()));
    2666             :     }
    2667             : 
    2668           3 :     MOZ_ASSERT(ent);
    2669           6 :     RefPtr<nsHttpConnectionInfo> ci(ent->mConnInfo);
    2670             : 
    2671           3 :     LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [ent=%p conn=%p]\n", ent, conn));
    2672             : 
    2673             :     // If the connection is in the active list, remove that entry
    2674             :     // and the reference held by the mActiveConns list.
    2675             :     // This is never the final reference on conn as the event context
    2676             :     // is also holding one that is released at the end of this function.
    2677             : 
    2678           3 :     if (conn->EverUsedSpdy()) {
    2679             :         // Spdy connections aren't reused in the traditional HTTP way in
    2680             :         // the idleconns list, they are actively multplexed as active
    2681             :         // conns. Even when they have 0 transactions on them they are
    2682             :         // considered active connections. So when one is reclaimed it
    2683             :         // is really complete and is meant to be shut down and not
    2684             :         // reused.
    2685           0 :         conn->DontReuse();
    2686             :     }
    2687             : 
    2688             :     // a connection that still holds a reference to a transaction was
    2689             :     // not closed naturally (i.e. it was reset or aborted) and is
    2690             :     // therefore not something that should be reused.
    2691           3 :     if (conn->Transaction()) {
    2692           0 :         conn->DontReuse();
    2693             :     }
    2694             : 
    2695           3 :     if (ent->mActiveConns.RemoveElement(conn)) {
    2696           3 :         DecrementActiveConnCount(conn);
    2697           3 :         ConditionallyStopTimeoutTick();
    2698             :     }
    2699             : 
    2700           3 :     if (conn->CanReuse()) {
    2701           1 :         LOG(("  adding connection to idle list\n"));
    2702             :         // Keep The idle connection list sorted with the connections that
    2703             :         // have moved the largest data pipelines at the front because these
    2704             :         // connections have the largest cwnds on the server.
    2705             : 
    2706             :         // The linear search is ok here because the number of idleconns
    2707             :         // in a single entry is generally limited to a small number (i.e. 6)
    2708             : 
    2709             :         uint32_t idx;
    2710           1 :         for (idx = 0; idx < ent->mIdleConns.Length(); idx++) {
    2711           0 :             nsHttpConnection *idleConn = ent->mIdleConns[idx];
    2712           0 :             if (idleConn->MaxBytesRead() < conn->MaxBytesRead())
    2713           0 :                 break;
    2714             :         }
    2715             : 
    2716           1 :         ent->mIdleConns.InsertElementAt(idx, conn);
    2717           1 :         mNumIdleConns++;
    2718           1 :         conn->BeginIdleMonitoring();
    2719             : 
    2720             :         // If the added connection was first idle connection or has shortest
    2721             :         // time to live among the watched connections, pruning dead
    2722             :         // connections needs to be done when it can't be reused anymore.
    2723           1 :         uint32_t timeToLive = conn->TimeToLive();
    2724           1 :         if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
    2725           1 :             PruneDeadConnectionsAfter(timeToLive);
    2726             :     } else {
    2727           2 :         LOG(("  connection cannot be reused; closing connection\n"));
    2728           2 :         conn->Close(NS_ERROR_ABORT);
    2729             :     }
    2730             : 
    2731           3 :     OnMsgProcessPendingQ(0, ci);
    2732           3 : }
    2733             : 
    2734             : void
    2735           0 : nsHttpConnectionMgr::OnMsgCompleteUpgrade(int32_t, ARefBase *param)
    2736             : {
    2737           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2738           0 :     nsCompleteUpgradeData *data = static_cast<nsCompleteUpgradeData *>(param);
    2739           0 :     LOG(("nsHttpConnectionMgr::OnMsgCompleteUpgrade "
    2740             :          "this=%p conn=%p listener=%p\n", this, data->mConn.get(),
    2741             :          data->mUpgradeListener.get()));
    2742             : 
    2743           0 :     nsCOMPtr<nsISocketTransport> socketTransport;
    2744           0 :     nsCOMPtr<nsIAsyncInputStream> socketIn;
    2745           0 :     nsCOMPtr<nsIAsyncOutputStream> socketOut;
    2746             : 
    2747             :     nsresult rv;
    2748           0 :     rv = data->mConn->TakeTransport(getter_AddRefs(socketTransport),
    2749           0 :                                     getter_AddRefs(socketIn),
    2750           0 :                                     getter_AddRefs(socketOut));
    2751             : 
    2752           0 :     if (NS_SUCCEEDED(rv)) {
    2753           0 :         rv = data->mUpgradeListener->OnTransportAvailable(socketTransport,
    2754             :                                                           socketIn,
    2755           0 :                                                           socketOut);
    2756           0 :         if (NS_FAILED(rv)) {
    2757           0 :             LOG(("nsHttpConnectionMgr::OnMsgCompleteUpgrade "
    2758             :                  "this=%p conn=%p listener=%p\n", this, data->mConn.get(),
    2759             :                  data->mUpgradeListener.get()));
    2760             :         }
    2761             :     }
    2762           0 : }
    2763             : 
    2764             : void
    2765           0 : nsHttpConnectionMgr::OnMsgUpdateParam(int32_t inParam, ARefBase *)
    2766             : {
    2767           0 :     uint32_t param = static_cast<uint32_t>(inParam);
    2768           0 :     uint16_t name  = ((param) & 0xFFFF0000) >> 16;
    2769           0 :     uint16_t value =  param & 0x0000FFFF;
    2770             : 
    2771           0 :     switch (name) {
    2772             :     case MAX_CONNECTIONS:
    2773           0 :         mMaxConns = value;
    2774           0 :         break;
    2775             :     case MAX_URGENT_START_Q:
    2776           0 :         mMaxUrgentExcessiveConns = value;
    2777           0 :         break;
    2778             :     case MAX_PERSISTENT_CONNECTIONS_PER_HOST:
    2779           0 :         mMaxPersistConnsPerHost = value;
    2780           0 :         break;
    2781             :     case MAX_PERSISTENT_CONNECTIONS_PER_PROXY:
    2782           0 :         mMaxPersistConnsPerProxy = value;
    2783           0 :         break;
    2784             :     case MAX_REQUEST_DELAY:
    2785           0 :         mMaxRequestDelay = value;
    2786           0 :         break;
    2787             :     case THROTTLING_ENABLED:
    2788           0 :         SetThrottlingEnabled(!!value);
    2789           0 :         break;
    2790             :     case THROTTLING_SUSPEND_FOR:
    2791           0 :         mThrottleSuspendFor = value;
    2792           0 :         break;
    2793             :     case THROTTLING_RESUME_FOR:
    2794           0 :         mThrottleResumeFor = value;
    2795           0 :         break;
    2796             :     case THROTTLING_RESUME_IN:
    2797           0 :         mThrottleResumeIn = value;
    2798           0 :         break;
    2799             :     default:
    2800           0 :         NS_NOTREACHED("unexpected parameter name");
    2801             :     }
    2802           0 : }
    2803             : 
    2804             : // nsHttpConnectionMgr::nsConnectionEntry
    2805           0 : nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
    2806             : {
    2807           0 :     LOG(("nsConnectionEntry::~nsConnectionEntry this=%p", this));
    2808             : 
    2809           0 :     MOZ_DIAGNOSTIC_ASSERT(!mIdleConns.Length());
    2810           0 :     MOZ_DIAGNOSTIC_ASSERT(!mActiveConns.Length());
    2811           0 :     MOZ_DIAGNOSTIC_ASSERT(!mHalfOpens.Length());
    2812           0 :     MOZ_DIAGNOSTIC_ASSERT(!mUrgentStartQ.Length());
    2813           0 :     MOZ_DIAGNOSTIC_ASSERT(!PendingQLength());
    2814           0 :     MOZ_DIAGNOSTIC_ASSERT(!mHalfOpenFastOpenBackups.Length());
    2815           0 :     MOZ_DIAGNOSTIC_ASSERT(!mDoNotDestroy);
    2816             : 
    2817           0 :     MOZ_COUNT_DTOR(nsConnectionEntry);
    2818           0 : }
    2819             : 
    2820             : // Read Timeout Tick handlers
    2821             : 
    2822             : void
    2823           6 : nsHttpConnectionMgr::ActivateTimeoutTick()
    2824             : {
    2825           6 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2826           6 :     LOG(("nsHttpConnectionMgr::ActivateTimeoutTick() "
    2827             :          "this=%p mTimeoutTick=%p\n", this, mTimeoutTick.get()));
    2828             : 
    2829             :     // The timer tick should be enabled if it is not already pending.
    2830             :     // Upon running the tick will rearm itself if there are active
    2831             :     // connections available.
    2832             : 
    2833           6 :     if (mTimeoutTick && mTimeoutTickArmed) {
    2834             :         // make sure we get one iteration on a quick tick
    2835           0 :         if (mTimeoutTickNext > 1) {
    2836           0 :             mTimeoutTickNext = 1;
    2837           0 :             mTimeoutTick->SetDelay(1000);
    2838             :         }
    2839           0 :         return;
    2840             :     }
    2841             : 
    2842           6 :     if (!mTimeoutTick) {
    2843           1 :         mTimeoutTick = do_CreateInstance(NS_TIMER_CONTRACTID);
    2844           1 :         if (!mTimeoutTick) {
    2845           0 :             NS_WARNING("failed to create timer for http timeout management");
    2846           0 :             return;
    2847             :         }
    2848           1 :         mTimeoutTick->SetTarget(mSocketThreadTarget);
    2849             :     }
    2850             : 
    2851           6 :     MOZ_ASSERT(!mTimeoutTickArmed, "timer tick armed");
    2852           6 :     mTimeoutTickArmed = true;
    2853           6 :     mTimeoutTick->Init(this, 1000, nsITimer::TYPE_REPEATING_SLACK);
    2854             : }
    2855             : 
    2856             : class UINT64Wrapper : public ARefBase
    2857             : {
    2858             : public:
    2859           1 :     explicit UINT64Wrapper(uint64_t aUint64) : mUint64(aUint64) {}
    2860           4 :     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UINT64Wrapper)
    2861             : 
    2862           1 :     uint64_t GetValue()
    2863             :     {
    2864           1 :         return mUint64;
    2865             :     }
    2866             : private:
    2867             :     uint64_t mUint64;
    2868           3 :     virtual ~UINT64Wrapper() = default;
    2869             : };
    2870             : 
    2871             : nsresult
    2872           1 : nsHttpConnectionMgr::UpdateCurrentTopLevelOuterContentWindowId(
    2873             :     uint64_t aWindowId)
    2874             : {
    2875           2 :     RefPtr<UINT64Wrapper> windowIdWrapper = new UINT64Wrapper(aWindowId);
    2876           1 :     return PostEvent(
    2877             :         &nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId,
    2878             :         0,
    2879           3 :         windowIdWrapper);
    2880             : }
    2881             : 
    2882           0 : void nsHttpConnectionMgr::SetThrottlingEnabled(bool aEnable)
    2883             : {
    2884           0 :     LOG(("nsHttpConnectionMgr::SetThrottlingEnabled enable=%d", aEnable));
    2885             : 
    2886           0 :     mThrottleEnabled = aEnable;
    2887             : 
    2888           0 :     if (mThrottleEnabled) {
    2889           0 :         EnsureThrottleTickerIfNeeded();
    2890             :     } else {
    2891           0 :         DestroyThrottleTicker();
    2892           0 :         ResumeReadOf(mActiveTransactions[false]);
    2893           0 :         ResumeReadOf(mActiveTransactions[true]);
    2894             :     }
    2895           0 : }
    2896             : 
    2897           6 : void nsHttpConnectionMgr::LogActiveTransactions(char operation)
    2898             : {
    2899           6 :     if (!LOG_ENABLED()) {
    2900           6 :         return;
    2901             :     }
    2902             : 
    2903           0 :     nsTArray<RefPtr<nsHttpTransaction>> *trs = nullptr;
    2904           0 :     uint32_t au, at, bu = 0, bt = 0;
    2905             : 
    2906           0 :     trs = mActiveTransactions[false].Get(mCurrentTopLevelOuterContentWindowId);
    2907           0 :     au = trs ? trs->Length() : 0;
    2908           0 :     trs = mActiveTransactions[true].Get(mCurrentTopLevelOuterContentWindowId);
    2909           0 :     at = trs ? trs->Length() : 0;
    2910             : 
    2911           0 :     for (auto iter = mActiveTransactions[false].Iter(); !iter.Done(); iter.Next()) {
    2912           0 :         bu += iter.UserData()->Length();
    2913             :     }
    2914           0 :     bu -= au;
    2915           0 :     for (auto iter = mActiveTransactions[true].Iter(); !iter.Done(); iter.Next()) {
    2916           0 :         bt += iter.UserData()->Length();
    2917             :     }
    2918           0 :     bt -= at;
    2919             : 
    2920             :     // Shows counts of:
    2921             :     // - unthrottled transaction for the active tab
    2922             :     // - throttled transaction for the active tab
    2923             :     // - unthrottled transaction for background tabs
    2924             :     // - throttled transaction for background tabs
    2925           0 :     LOG(("Active transactions %c[%u,%u,%u,%u]", operation, au, at, bu, bt));
    2926             : }
    2927             : 
    2928             : void
    2929           3 : nsHttpConnectionMgr::AddActiveTransaction(nsHttpTransaction * aTrans, bool aThrottled)
    2930             : {
    2931           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2932             : 
    2933           3 :     uint64_t tabId = aTrans->TopLevelOuterContentWindowId();
    2934             : 
    2935             :     nsTArray<RefPtr<nsHttpTransaction>> *transactions =
    2936           3 :         mActiveTransactions[aThrottled].LookupOrAdd(tabId);
    2937             : 
    2938           3 :     MOZ_ASSERT(!transactions->Contains(aTrans));
    2939             : 
    2940           3 :     transactions->AppendElement(aTrans);
    2941             : 
    2942           3 :     LOG(("nsHttpConnectionMgr::AddActiveTransaction    t=%p tabid=%" PRIx64 "(%d) thr=%d",
    2943             :           aTrans, tabId, tabId == mCurrentTopLevelOuterContentWindowId, aThrottled));
    2944           3 :     LogActiveTransactions('+');
    2945             : 
    2946           3 :     if (tabId == mCurrentTopLevelOuterContentWindowId) {
    2947           1 :         mActiveTabTransactionsExist = true;
    2948           1 :         if (!aThrottled) {
    2949           1 :             mActiveTabUnthrottledTransactionsExist = true;
    2950             :         }
    2951             :     }
    2952             : 
    2953           3 :     if (!mThrottleEnabled) {
    2954           0 :         return;
    2955             :     }
    2956             : 
    2957           3 :     EnsureThrottleTickerIfNeeded();
    2958             : }
    2959             : 
    2960             : void
    2961           3 : nsHttpConnectionMgr::RemoveActiveTransaction(nsHttpTransaction * aTrans, bool aThrottled)
    2962             : {
    2963           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2964             : 
    2965           3 :     uint64_t tabId = aTrans->TopLevelOuterContentWindowId();
    2966           3 :     bool forActiveTab = tabId == mCurrentTopLevelOuterContentWindowId;
    2967             : 
    2968             :     nsTArray<RefPtr<nsHttpTransaction>> *transactions =
    2969           3 :         mActiveTransactions[aThrottled].Get(tabId);
    2970             : 
    2971           3 :     if (!transactions || !transactions->RemoveElement(aTrans)) {
    2972             :         // Was not tracked as active, probably just ignore.
    2973           3 :         return;
    2974             :     }
    2975             : 
    2976           3 :     LOG(("nsHttpConnectionMgr::RemoveActiveTransaction t=%p tabid=%" PRIx64 "(%d) thr=%d",
    2977             :           aTrans, tabId, forActiveTab, aThrottled));
    2978             : 
    2979           3 :     if (!transactions->IsEmpty()) {
    2980             :         // There are still transactions of the type, hence nothing in the throttling conditions
    2981             :         // has changed and we don't need to update "Exists" caches nor we need to wake any now
    2982             :         // throttled transactions.
    2983           0 :         LogActiveTransactions('-');
    2984           0 :         return;
    2985             :     }
    2986             : 
    2987             :     // To optimize the following logic, always remove the entry when the array is empty.
    2988           3 :     mActiveTransactions[aThrottled].Remove(tabId);
    2989           3 :     LogActiveTransactions('-');
    2990             : 
    2991           3 :     if (forActiveTab) {
    2992             :         // Update caches of the active tab transaction existence, since it's now affected
    2993           1 :         if (!aThrottled) {
    2994           1 :             mActiveTabUnthrottledTransactionsExist = false;
    2995             :         }
    2996           1 :         if (mActiveTabTransactionsExist) {
    2997           1 :             mActiveTabTransactionsExist = mActiveTransactions[!aThrottled].Contains(tabId);
    2998             :         }
    2999             :     }
    3000             : 
    3001           3 :     if (!mThrottleEnabled) {
    3002           0 :         return;
    3003             :     }
    3004             : 
    3005           3 :     bool unthrottledExist = !mActiveTransactions[false].IsEmpty();
    3006           3 :     bool throttledExist = !mActiveTransactions[true].IsEmpty();
    3007             : 
    3008           3 :     if (!unthrottledExist && !throttledExist) {
    3009             :         // Nothing active globally, just get rid of the timer completely and we are done.
    3010           3 :         MOZ_ASSERT(!mActiveTabUnthrottledTransactionsExist);
    3011           3 :         MOZ_ASSERT(!mActiveTabTransactionsExist);
    3012             : 
    3013           3 :         DestroyThrottleTicker();
    3014           3 :         return;
    3015             :     }
    3016             : 
    3017           0 :     if (!mThrottlingInhibitsReading) {
    3018             :         // There is then nothing to wake up.  Affected transactions will not be put
    3019             :         // to sleep automatically on next tick.
    3020           0 :         LOG(("  reading not currently inhibited"));
    3021           0 :         return;
    3022             :     }
    3023             : 
    3024           0 :     if (mActiveTabUnthrottledTransactionsExist) {
    3025             :         // There are still unthrottled transactions for the active tab, hence the state
    3026             :         // is unaffected and we don't need to do anything (nothing to wake).
    3027           0 :         LOG(("  there are unthrottled for the active tab"));
    3028           0 :         return;
    3029             :     }
    3030             : 
    3031           0 :     if (mActiveTabTransactionsExist) {
    3032             :         // There are only trottled transactions for the active tab.
    3033             :         // If the last transaction we just removed was a non-throttled for the active tab
    3034             :         // we can wake the throttled transactions for the active tab.
    3035           0 :         if (forActiveTab && !aThrottled) {
    3036           0 :             LOG(("  resuming throttled for active tab"));
    3037           0 :             ResumeReadOf(mActiveTransactions[true].Get(mCurrentTopLevelOuterContentWindowId));
    3038             :         }
    3039           0 :         return;
    3040             :     }
    3041             : 
    3042           0 :     if (!unthrottledExist) {
    3043             :         // There are no unthrottled transactions for any tab.  Resume all throttled,
    3044             :         // all are only for background tabs.
    3045           0 :         LOG(("  delay resuming throttled for background tabs"));
    3046           0 :         DelayedResumeBackgroundThrottledTransactions();
    3047           0 :         return;
    3048             :     }
    3049             : 
    3050           0 :     if (forActiveTab) {
    3051             :         // Removing the last transaction for the active tab frees up the unthrottled
    3052             :         // background tabs transactions.
    3053           0 :         LOG(("  delay resuming unthrottled for background tabs"));
    3054           0 :         DelayedResumeBackgroundThrottledTransactions();
    3055           0 :         return;
    3056             :     }
    3057             : 
    3058           0 :     LOG(("  not resuming anything"));
    3059             : }
    3060             : 
    3061             : void
    3062           0 : nsHttpConnectionMgr::MoveActiveTransaction(nsHttpTransaction * aTrans, bool aThrottled)
    3063             : {
    3064           0 :     LOG(("nsHttpConnectionMgr::MoveActiveTransaction ENTER t=%p", aTrans));
    3065           0 :     AddActiveTransaction(aTrans, aThrottled);
    3066           0 :     RemoveActiveTransaction(aTrans, !aThrottled);
    3067           0 :     LOG(("nsHttpConnectionMgr::MoveActiveTransaction EXIT t=%p", aTrans));
    3068           0 : }
    3069             : 
    3070             : bool
    3071          12 : nsHttpConnectionMgr::ShouldStopReading(nsHttpTransaction * aTrans, bool aThrottled)
    3072             : {
    3073          12 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3074             : 
    3075          12 :     if (!mThrottlingInhibitsReading || !mThrottleEnabled) {
    3076          12 :         return false;
    3077             :     }
    3078             : 
    3079           0 :     uint64_t tabId = aTrans->TopLevelOuterContentWindowId();
    3080           0 :     bool forActiveTab = tabId == mCurrentTopLevelOuterContentWindowId;
    3081             : 
    3082           0 :     if (mActiveTabTransactionsExist) {
    3083           0 :         if (!tabId) {
    3084             :             // Chrome initiated and unidentified transactions just respect
    3085             :             // their throttle flag, when something for the active tab is happening.
    3086           0 :             return aThrottled;
    3087             :         }
    3088           0 :         if (!forActiveTab) {
    3089             :             // This is a background tab request, we want them to always throttle.
    3090           0 :             return true;
    3091             :         }
    3092           0 :         if (mActiveTabUnthrottledTransactionsExist) {
    3093             :             // Unthrottled transactions for the active tab take precedence
    3094           0 :             return aThrottled;
    3095             :         }
    3096             :         // This is a throttled transaction for the active tab and there are no
    3097             :         // unthrottled for the active tab, just let go on full fuel.
    3098           0 :         return false;
    3099             :     }
    3100             : 
    3101           0 :     MOZ_ASSERT(!forActiveTab);
    3102             : 
    3103           0 :     if (mDelayedResumeReadTimer) {
    3104             :         // If this timer exists, background transactions are scheduled to be woken
    3105             :         // after a delay.
    3106           0 :         return true;
    3107             :     }
    3108             : 
    3109           0 :     if (!mActiveTransactions[false].IsEmpty()) {
    3110             :         // This means there are unthrottled active transactions for background tabs.
    3111             :         // If we are here, there can't be any transactions for the active tab.
    3112             :         // (If there is no transaction for a tab id, there is no entry for it in the hashtable.)
    3113           0 :         return aThrottled;
    3114             :     }
    3115             : 
    3116             :     // There are only unthrottled transactions for background tabs: don't throttle.
    3117           0 :     return false;
    3118             : }
    3119             : 
    3120           0 : bool nsHttpConnectionMgr::IsConnEntryUnderPressure(nsHttpConnectionInfo *connInfo)
    3121             : {
    3122           0 :     nsConnectionEntry *ent = mCT.Get(connInfo->HashKey());
    3123           0 :     if (!ent) {
    3124             :       // No entry, no pressure.
    3125           0 :       return false;
    3126             :     }
    3127             : 
    3128             :     nsTArray<RefPtr<PendingTransactionInfo>> *transactions =
    3129           0 :         ent->mPendingTransactionTable.Get(mCurrentTopLevelOuterContentWindowId);
    3130             : 
    3131           0 :     return transactions && !transactions->IsEmpty();
    3132             : }
    3133             : 
    3134           6 : bool nsHttpConnectionMgr::IsThrottleTickerNeeded()
    3135             : {
    3136           6 :     LOG(("nsHttpConnectionMgr::IsThrottleTickerNeeded"));
    3137             : 
    3138           7 :     if (mActiveTabUnthrottledTransactionsExist &&
    3139           1 :         mActiveTransactions[false].Count() > 1) {
    3140           0 :         LOG(("  there are unthrottled transactions for both active and bck"));
    3141           0 :         return true;
    3142             :     }
    3143             : 
    3144           7 :     if (mActiveTabTransactionsExist &&
    3145           1 :         mActiveTransactions[true].Count() > 1) {
    3146           0 :         LOG(("  there are throttled transactions for both active and bck"));
    3147           0 :         return true;
    3148             :     }
    3149             : 
    3150           6 :     if (!mActiveTransactions[true].IsEmpty() &&
    3151           0 :         !mActiveTransactions[false].IsEmpty()) {
    3152           0 :         LOG(("  there are both throttled and unthrottled transactions"));
    3153           0 :         return true;
    3154             :     }
    3155             : 
    3156           6 :     LOG(("  nothing to throttle"));
    3157           6 :     return false;
    3158             : }
    3159             : 
    3160             : void
    3161           3 : nsHttpConnectionMgr::EnsureThrottleTickerIfNeeded()
    3162             : {
    3163           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3164             : 
    3165           3 :     LOG(("nsHttpConnectionMgr::EnsureThrottleTickerIfNeeded"));
    3166           3 :     if (!IsThrottleTickerNeeded()) {
    3167           3 :         return;
    3168             :     }
    3169             : 
    3170             :     // There is a new demand to throttle, hence unschedule delayed resume
    3171             :     // of background throttled transastions.
    3172           0 :     CancelDelayedResumeBackgroundThrottledTransactions();
    3173             : 
    3174           0 :     if (mThrottleTicker) {
    3175           0 :         return;
    3176             :     }
    3177             : 
    3178           0 :     MOZ_ASSERT(!mThrottlingInhibitsReading);
    3179             : 
    3180           0 :     mThrottleTicker = do_CreateInstance("@mozilla.org/timer;1");
    3181           0 :     if (mThrottleTicker) {
    3182           0 :         mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
    3183           0 :         mThrottlingInhibitsReading = true;
    3184             :     }
    3185             : 
    3186           0 :     LogActiveTransactions('^');
    3187             : }
    3188             : 
    3189             : void
    3190           3 : nsHttpConnectionMgr::DestroyThrottleTicker()
    3191             : {
    3192           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3193             : 
    3194             :     // Nothing to throttle, hence no need for this timer anymore.
    3195           3 :     CancelDelayedResumeBackgroundThrottledTransactions();
    3196             : 
    3197           3 :     MOZ_ASSERT(!mThrottleEnabled || !IsThrottleTickerNeeded());
    3198             : 
    3199           3 :     if (!mThrottleTicker) {
    3200           3 :         return;
    3201             :     }
    3202             : 
    3203           0 :     LOG(("nsHttpConnectionMgr::DestroyThrottleTicker"));
    3204           0 :     mThrottleTicker->Cancel();
    3205           0 :     mThrottleTicker = nullptr;
    3206           0 :     mThrottlingInhibitsReading = false;
    3207             : 
    3208           0 :     LogActiveTransactions('v');
    3209             : }
    3210             : 
    3211             : void
    3212           0 : nsHttpConnectionMgr::ThrottlerTick()
    3213             : {
    3214           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3215             : 
    3216           0 :     mThrottlingInhibitsReading = !mThrottlingInhibitsReading;
    3217             : 
    3218           0 :     LOG(("nsHttpConnectionMgr::ThrottlerTick inhibit=%d", mThrottlingInhibitsReading));
    3219             : 
    3220             :     // If there are only background transactions to be woken after a delay, keep
    3221             :     // the ticker so that we woke them only for the resume-for interval and then
    3222             :     // throttle them again until the background-resume delay passes.
    3223           0 :     if (!mThrottlingInhibitsReading &&
    3224           0 :         !mDelayedResumeReadTimer &&
    3225           0 :         !IsThrottleTickerNeeded()) {
    3226           0 :         LOG(("  last tick"));
    3227           0 :         mThrottleTicker = nullptr;
    3228             :     }
    3229             : 
    3230           0 :     if (mThrottlingInhibitsReading) {
    3231           0 :         if (mThrottleTicker) {
    3232           0 :             mThrottleTicker->Init(this, mThrottleSuspendFor, nsITimer::TYPE_ONE_SHOT);
    3233             :         }
    3234             :     } else {
    3235           0 :         if (mThrottleTicker) {
    3236           0 :             mThrottleTicker->Init(this, mThrottleResumeFor, nsITimer::TYPE_ONE_SHOT);
    3237             :         }
    3238             : 
    3239           0 :         ResumeReadOf(mActiveTransactions[false], true);
    3240           0 :         ResumeReadOf(mActiveTransactions[true]);
    3241             :     }
    3242           0 : }
    3243             : 
    3244             : void
    3245           0 : nsHttpConnectionMgr::DelayedResumeBackgroundThrottledTransactions()
    3246             : {
    3247           0 :     if (mDelayedResumeReadTimer) {
    3248           0 :         return;
    3249             :     }
    3250             : 
    3251           0 :     mDelayedResumeReadTimer = do_CreateInstance("@mozilla.org/timer;1");
    3252           0 :     if (!mDelayedResumeReadTimer) {
    3253           0 :         return;
    3254             :     }
    3255             : 
    3256           0 :     LOG(("nsHttpConnectionMgr::DelayedResumeBackgroundThrottledTransactions"));
    3257           0 :     mDelayedResumeReadTimer->Init(this, mThrottleResumeIn, nsITimer::TYPE_ONE_SHOT);
    3258             : }
    3259             : 
    3260             : void
    3261           3 : nsHttpConnectionMgr::CancelDelayedResumeBackgroundThrottledTransactions()
    3262             : {
    3263           3 :     if (!mDelayedResumeReadTimer) {
    3264           3 :         return;
    3265             :     }
    3266             : 
    3267           0 :     LOG(("nsHttpConnectionMgr::CancelDelayedResumeBackgroundThrottledTransactions"));
    3268           0 :     mDelayedResumeReadTimer->Cancel();
    3269           0 :     mDelayedResumeReadTimer = nullptr;
    3270             : }
    3271             : 
    3272             : void
    3273           0 : nsHttpConnectionMgr::ResumeBackgroundThrottledTransactions()
    3274             : {
    3275           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3276             : 
    3277           0 :     LOG(("nsHttpConnectionMgr::ResumeBackgroundThrottledTransactions"));
    3278           0 :     mDelayedResumeReadTimer = nullptr;
    3279             : 
    3280           0 :     if (!IsThrottleTickerNeeded()) {
    3281           0 :         DestroyThrottleTicker();
    3282             :     }
    3283             : 
    3284           0 :     if (!mActiveTransactions[false].IsEmpty()) {
    3285           0 :         ResumeReadOf(mActiveTransactions[false], true);
    3286             :     } else {
    3287           0 :         ResumeReadOf(mActiveTransactions[true], true);
    3288             :     }
    3289           0 : }
    3290             : 
    3291             : void
    3292           0 : nsHttpConnectionMgr::ResumeReadOf(
    3293             :     nsClassHashtable<nsUint64HashKey, nsTArray<RefPtr<nsHttpTransaction>>>& hashtable,
    3294             :     bool excludeForActiveTab)
    3295             : {
    3296           0 :     for (auto iter = hashtable.Iter(); !iter.Done(); iter.Next()) {
    3297           0 :         if (excludeForActiveTab && iter.Key() == mCurrentTopLevelOuterContentWindowId) {
    3298             :             // These have never been throttled (never stopped reading)
    3299           0 :             continue;
    3300             :         }
    3301           0 :         ResumeReadOf(iter.UserData());
    3302             :     }
    3303           0 : }
    3304             : 
    3305             : void
    3306           0 : nsHttpConnectionMgr::ResumeReadOf(nsTArray<RefPtr<nsHttpTransaction>>* transactions)
    3307             : {
    3308           0 :     MOZ_ASSERT(transactions);
    3309             : 
    3310           0 :     for (auto trans : *transactions) {
    3311           0 :         trans->ResumeReading();
    3312             :     }
    3313           0 : }
    3314             : 
    3315             : void
    3316           1 : nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId(
    3317             :     int32_t aLoading, ARefBase *param)
    3318             : {
    3319           1 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3320             : 
    3321           1 :     uint64_t winId = static_cast<UINT64Wrapper*>(param)->GetValue();
    3322             : 
    3323           1 :     if (mCurrentTopLevelOuterContentWindowId == winId) {
    3324             :         // duplicate notification
    3325           0 :         return;
    3326             :     }
    3327             : 
    3328           1 :     bool activeTabWasLoading = mActiveTabTransactionsExist;
    3329           1 :     bool activeTabIdChanged = mCurrentTopLevelOuterContentWindowId != winId;
    3330             : 
    3331           1 :     mCurrentTopLevelOuterContentWindowId = winId;
    3332             : 
    3333           1 :     LOG(("nsHttpConnectionMgr::OnMsgUpdateCurrentTopLevelOuterContentWindowId"
    3334             :          " id=%" PRIx64 "\n",
    3335             :          mCurrentTopLevelOuterContentWindowId));
    3336             : 
    3337           1 :     nsTArray<RefPtr<nsHttpTransaction>> *transactions = nullptr;
    3338             : 
    3339           1 :     if (activeTabIdChanged) {
    3340             :         // Update the "Exists" caches and resume any transactions that now deserve it,
    3341             :         // changing the active tab changes the conditions for throttling.
    3342           1 :         transactions = mActiveTransactions[false].Get(mCurrentTopLevelOuterContentWindowId);
    3343           1 :         mActiveTabUnthrottledTransactionsExist = !!transactions;
    3344             : 
    3345           1 :         if (!mActiveTabUnthrottledTransactionsExist) {
    3346           1 :             transactions = mActiveTransactions[true].Get(mCurrentTopLevelOuterContentWindowId);
    3347             :         }
    3348           1 :         mActiveTabTransactionsExist = !!transactions;
    3349             :     }
    3350             : 
    3351           1 :     if (transactions) {
    3352             :         // This means there are some transactions for this newly activated tab, resume them
    3353             :         // but anything else.
    3354           0 :         LOG(("  resuming newly activated tab transactions"));
    3355           0 :         ResumeReadOf(transactions);
    3356           0 :         return;
    3357             :     }
    3358             : 
    3359           1 :     if (!activeTabWasLoading) {
    3360             :         // There were no transactions for the previously active tab, hence
    3361             :         // all remaning transactions, if there were, were all unthrottled,
    3362             :         // no need to wake them.
    3363           1 :         return;
    3364             :     }
    3365             : 
    3366           0 :     if (!mActiveTransactions[false].IsEmpty()) {
    3367           0 :         LOG(("  resuming unthrottled background transactions"));
    3368           0 :         ResumeReadOf(mActiveTransactions[false]);
    3369           0 :         return;
    3370             :     }
    3371             : 
    3372           0 :     if (!mActiveTransactions[true].IsEmpty()) {
    3373           0 :         LOG(("  delayed resuming throttled background transactions"));
    3374           0 :         DelayedResumeBackgroundThrottledTransactions();
    3375           0 :         return;
    3376             :     }
    3377             : 
    3378           0 :     DestroyThrottleTicker();
    3379             : }
    3380             : 
    3381             : void
    3382           0 : nsHttpConnectionMgr::TimeoutTick()
    3383             : {
    3384           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3385           0 :     MOZ_ASSERT(mTimeoutTick, "no readtimeout tick");
    3386             : 
    3387           0 :     LOG(("nsHttpConnectionMgr::TimeoutTick active=%d\n", mNumActiveConns));
    3388             :     // The next tick will be between 1 second and 1 hr
    3389             :     // Set it to the max value here, and the TimeoutTick()s can
    3390             :     // reduce it to their local needs.
    3391           0 :     mTimeoutTickNext = 3600; // 1hr
    3392             : 
    3393           0 :     for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    3394           0 :         nsAutoPtr<nsConnectionEntry>& ent = iter.Data();
    3395             : 
    3396           0 :         LOG(("nsHttpConnectionMgr::TimeoutTick() this=%p host=%s "
    3397             :              "idle=%" PRIuSIZE " active=%" PRIuSIZE
    3398             :              " half-len=%" PRIuSIZE " pending=%" PRIuSIZE
    3399             :              " urgentStart pending=%" PRIuSIZE "\n",
    3400             :              this, ent->mConnInfo->Origin(), ent->mIdleConns.Length(),
    3401             :              ent->mActiveConns.Length(), ent->mHalfOpens.Length(),
    3402             :              ent->PendingQLength(), ent->mUrgentStartQ.Length()));
    3403             : 
    3404             :         // First call the tick handler for each active connection.
    3405           0 :         PRIntervalTime tickTime = PR_IntervalNow();
    3406           0 :         for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
    3407             :             uint32_t connNextTimeout =
    3408           0 :                 ent->mActiveConns[index]->ReadTimeoutTick(tickTime);
    3409           0 :             mTimeoutTickNext = std::min(mTimeoutTickNext, connNextTimeout);
    3410             :         }
    3411             : 
    3412             :         // Now check for any stalled half open sockets.
    3413           0 :         if (ent->mHalfOpens.Length()) {
    3414           0 :             TimeStamp currentTime = TimeStamp::Now();
    3415           0 :             double maxConnectTime_ms = gHttpHandler->ConnectTimeout();
    3416             : 
    3417           0 :             for (uint32_t index = ent->mHalfOpens.Length(); index > 0; ) {
    3418           0 :                 index--;
    3419             : 
    3420           0 :                 nsHalfOpenSocket *half = ent->mHalfOpens[index];
    3421           0 :                 double delta = half->Duration(currentTime);
    3422             :                 // If the socket has timed out, close it so the waiting
    3423             :                 // transaction will get the proper signal.
    3424           0 :                 if (delta > maxConnectTime_ms) {
    3425           0 :                     LOG(("Force timeout of half open to %s after %.2fms.\n",
    3426             :                          ent->mConnInfo->HashKey().get(), delta));
    3427           0 :                     if (half->SocketTransport()) {
    3428           0 :                         half->SocketTransport()->Close(NS_ERROR_NET_TIMEOUT);
    3429             :                     }
    3430           0 :                     if (half->BackupTransport()) {
    3431           0 :                         half->BackupTransport()->Close(NS_ERROR_NET_TIMEOUT);
    3432             :                     }
    3433             :                 }
    3434             : 
    3435             :                 // If this half open hangs around for 5 seconds after we've
    3436             :                 // closed() it then just abandon the socket.
    3437           0 :                 if (delta > maxConnectTime_ms + 5000) {
    3438           0 :                     LOG(("Abandon half open to %s after %.2fms.\n",
    3439             :                          ent->mConnInfo->HashKey().get(), delta));
    3440           0 :                     half->Abandon();
    3441             :                 }
    3442             :             }
    3443             :         }
    3444           0 :         if (ent->mHalfOpens.Length()) {
    3445           0 :             mTimeoutTickNext = 1;
    3446             :         }
    3447             :     }
    3448             : 
    3449           0 :     if (mTimeoutTick) {
    3450           0 :         mTimeoutTickNext = std::max(mTimeoutTickNext, 1U);
    3451           0 :         mTimeoutTick->SetDelay(mTimeoutTickNext * 1000);
    3452             :     }
    3453           0 : }
    3454             : 
    3455             : // GetOrCreateConnectionEntry finds a ent for a particular CI for use in
    3456             : // dispatching a transaction according to these rules
    3457             : // 1] use an ent that matches the ci that can be dispatched immediately
    3458             : // 2] otherwise use an ent of wildcard(ci) than can be dispatched immediately
    3459             : // 3] otherwise create an ent that matches ci and make new conn on it
    3460             : 
    3461             : nsHttpConnectionMgr::nsConnectionEntry *
    3462           8 : nsHttpConnectionMgr::GetOrCreateConnectionEntry(nsHttpConnectionInfo *specificCI,
    3463             :                                                 bool prohibitWildCard)
    3464             : {
    3465             :     // step 1
    3466           8 :     nsConnectionEntry *specificEnt = mCT.Get(specificCI->HashKey());
    3467           8 :     if (specificEnt && specificEnt->AvailableForDispatchNow()) {
    3468           0 :         return specificEnt;
    3469             :     }
    3470             : 
    3471           8 :     if (!specificCI->UsingHttpsProxy()) {
    3472           8 :         prohibitWildCard = true;
    3473             :     }
    3474             : 
    3475             :     // step 2
    3476           8 :     if (!prohibitWildCard) {
    3477           0 :         RefPtr<nsHttpConnectionInfo> wildCardProxyCI;
    3478           0 :         DebugOnly<nsresult> rv = specificCI->CreateWildCard(getter_AddRefs(wildCardProxyCI));
    3479           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
    3480           0 :         nsConnectionEntry *wildCardEnt = mCT.Get(wildCardProxyCI->HashKey());
    3481           0 :         if (wildCardEnt && wildCardEnt->AvailableForDispatchNow()) {
    3482           0 :             return wildCardEnt;
    3483             :         }
    3484             :     }
    3485             : 
    3486             :     // step 3
    3487           8 :     if (!specificEnt) {
    3488           4 :         RefPtr<nsHttpConnectionInfo> clone(specificCI->Clone());
    3489           4 :         specificEnt = new nsConnectionEntry(clone);
    3490           2 :         specificEnt->mUseFastOpen = gHttpHandler->UseFastOpen();
    3491           2 :         mCT.Put(clone->HashKey(), specificEnt);
    3492             :     }
    3493           8 :     return specificEnt;
    3494             : }
    3495             : 
    3496             : nsresult
    3497           3 : ConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
    3498             :                                      nsHttpRequestHead *req,
    3499             :                                      nsHttpResponseHead *resp,
    3500             :                                      bool *reset)
    3501             : {
    3502           3 :     return mConn->OnHeadersAvailable(trans, req, resp, reset);
    3503             : }
    3504             : 
    3505             : void
    3506           0 : ConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
    3507             : {
    3508           0 :     mConn->CloseTransaction(trans, reason);
    3509           0 : }
    3510             : 
    3511             : nsresult
    3512           0 : ConnectionHandle::TakeTransport(nsISocketTransport  **aTransport,
    3513             :                                 nsIAsyncInputStream **aInputStream,
    3514             :                                 nsIAsyncOutputStream **aOutputStream)
    3515             : {
    3516           0 :     return mConn->TakeTransport(aTransport, aInputStream, aOutputStream);
    3517             : }
    3518             : 
    3519             : void
    3520           5 : nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, ARefBase *param)
    3521             : {
    3522           5 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3523             : 
    3524           5 :     SpeculativeConnectArgs *args = static_cast<SpeculativeConnectArgs *>(param);
    3525             : 
    3526           5 :     LOG(("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s]\n",
    3527             :          args->mTrans->ConnectionInfo()->HashKey().get()));
    3528             : 
    3529             :     nsConnectionEntry *ent =
    3530           5 :         GetOrCreateConnectionEntry(args->mTrans->ConnectionInfo(), false);
    3531             : 
    3532             :     uint32_t parallelSpeculativeConnectLimit =
    3533           5 :         gHttpHandler->ParallelSpeculativeConnectLimit();
    3534           5 :     bool ignoreIdle = false;
    3535           5 :     bool isFromPredictor = false;
    3536           5 :     bool allow1918 = false;
    3537             : 
    3538           5 :     if (args->mOverridesOK) {
    3539           0 :         parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit;
    3540           0 :         ignoreIdle = args->mIgnoreIdle;
    3541           0 :         isFromPredictor = args->mIsFromPredictor;
    3542           0 :         allow1918 = args->mAllow1918;
    3543             :     }
    3544             : 
    3545           5 :     bool keepAlive = args->mTrans->Caps() & NS_HTTP_ALLOW_KEEPALIVE;
    3546          10 :     if (mNumHalfOpenConns < parallelSpeculativeConnectLimit &&
    3547           0 :         ((ignoreIdle && (ent->mIdleConns.Length() < parallelSpeculativeConnectLimit)) ||
    3548           0 :          !ent->mIdleConns.Length()) &&
    3549           5 :         !(keepAlive && RestrictConnections(ent)) &&
    3550           0 :         !AtActiveConnectionLimit(ent, args->mTrans->Caps())) {
    3551           0 :         DebugOnly<nsresult> rv = CreateTransport(ent, args->mTrans,
    3552           0 :                                                  args->mTrans->Caps(), true,
    3553             :                                                  isFromPredictor, allow1918,
    3554           0 :                                                  nullptr);
    3555           0 :         MOZ_ASSERT(NS_SUCCEEDED(rv));
    3556             :     } else {
    3557           5 :         LOG(("OnMsgSpeculativeConnect Transport "
    3558             :              "not created due to existing connection count\n"));
    3559             :     }
    3560           5 : }
    3561             : 
    3562             : bool
    3563           3 : ConnectionHandle::IsPersistent()
    3564             : {
    3565           3 :     return mConn->IsPersistent();
    3566             : }
    3567             : 
    3568             : bool
    3569           3 : ConnectionHandle::IsReused()
    3570             : {
    3571           3 :     return mConn->IsReused();
    3572             : }
    3573             : 
    3574             : void
    3575           0 : ConnectionHandle::DontReuse()
    3576             : {
    3577           0 :     mConn->DontReuse();
    3578           0 : }
    3579             : 
    3580             : nsresult
    3581           0 : ConnectionHandle::PushBack(const char *buf, uint32_t bufLen)
    3582             : {
    3583           0 :     return mConn->PushBack(buf, bufLen);
    3584             : }
    3585             : 
    3586             : 
    3587             : //////////////////////// nsHalfOpenSocket
    3588          46 : NS_IMPL_ADDREF(nsHttpConnectionMgr::nsHalfOpenSocket)
    3589          49 : NS_IMPL_RELEASE(nsHttpConnectionMgr::nsHalfOpenSocket)
    3590             : 
    3591          12 : NS_INTERFACE_MAP_BEGIN(nsHttpConnectionMgr::nsHalfOpenSocket)
    3592          12 :     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    3593           9 :     NS_INTERFACE_MAP_ENTRY(nsIOutputStreamCallback)
    3594           6 :     NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
    3595           3 :     NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
    3596           0 :     NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
    3597             :     // we have no macro that covers this case.
    3598           0 :     if (aIID.Equals(NS_GET_IID(nsHttpConnectionMgr::nsHalfOpenSocket)) ) {
    3599           0 :         AddRef();
    3600           0 :         *aInstancePtr = this;
    3601           0 :         return NS_OK;
    3602             :     } else
    3603           0 : NS_INTERFACE_MAP_END
    3604             : 
    3605           3 : nsHttpConnectionMgr::
    3606             : nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
    3607             :                                    nsAHttpTransaction *trans,
    3608             :                                    uint32_t caps,
    3609             :                                    bool speculative,
    3610           3 :                                    bool isFromPredictor)
    3611             :     : mEnt(ent)
    3612             :     , mTransaction(trans)
    3613             :     , mDispatchedMTransaction(false)
    3614             :     , mCaps(caps)
    3615             :     , mSpeculative(speculative)
    3616             :     , mIsFromPredictor(isFromPredictor)
    3617             :     , mAllow1918(true)
    3618             :     , mHasConnected(false)
    3619             :     , mPrimaryConnectedOK(false)
    3620             :     , mBackupConnectedOK(false)
    3621             :     , mFreeToUse(true)
    3622             :     , mPrimaryStreamStatus(NS_OK)
    3623           3 :     , mFastOpenInProgress(false)
    3624             : {
    3625           3 :     MOZ_ASSERT(ent && trans, "constructor with null arguments");
    3626           3 :     LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s key=%s]\n",
    3627             :          this, trans, ent->mConnInfo->Origin(), ent->mConnInfo->HashKey().get()));
    3628             : 
    3629           3 :     if (speculative) {
    3630           0 :         Telemetry::AutoCounter<Telemetry::HTTPCONNMGR_TOTAL_SPECULATIVE_CONN> totalSpeculativeConn;
    3631           0 :         ++totalSpeculativeConn;
    3632             : 
    3633           0 :         if (isFromPredictor) {
    3634           0 :           Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRECONNECTS_CREATED> totalPreconnectsCreated;
    3635           0 :           ++totalPreconnectsCreated;
    3636             :         }
    3637             :     }
    3638           3 : }
    3639             : 
    3640           6 : nsHttpConnectionMgr::nsHalfOpenSocket::~nsHalfOpenSocket()
    3641             : {
    3642           3 :     MOZ_ASSERT(!mStreamOut);
    3643           3 :     MOZ_ASSERT(!mBackupStreamOut);
    3644           3 :     MOZ_ASSERT(!mSynTimer);
    3645           3 :     LOG(("Destroying nsHalfOpenSocket [this=%p]\n", this));
    3646             : 
    3647           3 :     if (mEnt)
    3648           1 :         mEnt->RemoveHalfOpen(this);
    3649           3 : }
    3650             : 
    3651             : nsresult
    3652           3 : nsHttpConnectionMgr::
    3653             : nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
    3654             :                                nsIAsyncInputStream **instream,
    3655             :                                nsIAsyncOutputStream **outstream,
    3656             :                                bool isBackup)
    3657             : {
    3658           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3659             : 
    3660             :     nsresult rv;
    3661             :     const char *socketTypes[1];
    3662           3 :     uint32_t typeCount = 0;
    3663           3 :     const nsHttpConnectionInfo *ci = mEnt->mConnInfo;
    3664           3 :     if (ci->FirstHopSSL()) {
    3665           0 :         socketTypes[typeCount++] = "ssl";
    3666             :     } else {
    3667           3 :         socketTypes[typeCount] = gHttpHandler->DefaultSocketType();
    3668           3 :         if (socketTypes[typeCount]) {
    3669           0 :             typeCount++;
    3670             :         }
    3671             :     }
    3672             : 
    3673           6 :     nsCOMPtr<nsISocketTransport> socketTransport;
    3674           6 :     nsCOMPtr<nsISocketTransportService> sts;
    3675             : 
    3676           3 :     sts = services::GetSocketTransportService();
    3677           3 :     if (!sts) {
    3678           0 :         return NS_ERROR_NOT_AVAILABLE;
    3679             :     }
    3680             : 
    3681           3 :     LOG(("nsHalfOpenSocket::SetupStreams [this=%p ent=%s] "
    3682             :          "setup routed transport to origin %s:%d via %s:%d\n",
    3683             :          this, ci->HashKey().get(),
    3684             :          ci->Origin(), ci->OriginPort(), ci->RoutedHost(), ci->RoutedPort()));
    3685             : 
    3686           6 :     nsCOMPtr<nsIRoutedSocketTransportService> routedSTS(do_QueryInterface(sts));
    3687           3 :     if (routedSTS) {
    3688          15 :         rv = routedSTS->CreateRoutedTransport(
    3689             :             socketTypes, typeCount,
    3690           6 :             ci->GetOrigin(), ci->OriginPort(), ci->GetRoutedHost(), ci->RoutedPort(),
    3691           9 :             ci->ProxyInfo(), getter_AddRefs(socketTransport));
    3692             :     } else {
    3693           0 :         if (!ci->GetRoutedHost().IsEmpty()) {
    3694             :             // There is a route requested, but the legacy nsISocketTransportService
    3695             :             // can't handle it.
    3696             :             // Origin should be reachable on origin host name, so this should
    3697             :             // not be a problem - but log it.
    3698           0 :             LOG(("nsHalfOpenSocket this=%p using legacy nsISocketTransportService "
    3699             :                  "means explicit route %s:%d will be ignored.\n", this,
    3700             :                  ci->RoutedHost(), ci->RoutedPort()));
    3701             :         }
    3702             : 
    3703           0 :         rv = sts->CreateTransport(socketTypes, typeCount,
    3704           0 :                                   ci->GetOrigin(), ci->OriginPort(),
    3705           0 :                                   ci->ProxyInfo(),
    3706           0 :                                   getter_AddRefs(socketTransport));
    3707             :     }
    3708           3 :     NS_ENSURE_SUCCESS(rv, rv);
    3709             : 
    3710           3 :     uint32_t tmpFlags = 0;
    3711           3 :     if (mCaps & NS_HTTP_REFRESH_DNS)
    3712           1 :         tmpFlags = nsISocketTransport::BYPASS_CACHE;
    3713             : 
    3714           3 :     if (mCaps & NS_HTTP_LOAD_ANONYMOUS)
    3715           0 :         tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
    3716             : 
    3717           3 :     if (ci->GetPrivate())
    3718           0 :         tmpFlags |= nsISocketTransport::NO_PERMANENT_STORAGE;
    3719             : 
    3720           3 :     if ((mCaps & NS_HTTP_BE_CONSERVATIVE) || ci->GetBeConservative()) {
    3721           0 :         LOG(("Setting Socket to BE_CONSERVATIVE"));
    3722           0 :         tmpFlags |= nsISocketTransport::BE_CONSERVATIVE;
    3723             :     }
    3724             : 
    3725             :     // For backup connections, we disable IPv6. That's because some users have
    3726             :     // broken IPv6 connectivity (leading to very long timeouts), and disabling
    3727             :     // IPv6 on the backup connection gives them a much better user experience
    3728             :     // with dual-stack hosts, though they still pay the 250ms delay for each new
    3729             :     // connection. This strategy is also known as "happy eyeballs".
    3730           3 :     if (mEnt->mPreferIPv6) {
    3731           0 :         tmpFlags |= nsISocketTransport::DISABLE_IPV4;
    3732             :     }
    3733           4 :     else if (mEnt->mPreferIPv4 ||
    3734           0 :              (isBackup && gHttpHandler->FastFallbackToIPv4())) {
    3735           1 :         tmpFlags |= nsISocketTransport::DISABLE_IPV6;
    3736             :     }
    3737             : 
    3738           3 :     if (!Allow1918()) {
    3739           0 :         tmpFlags |= nsISocketTransport::DISABLE_RFC1918;
    3740             :     }
    3741             : 
    3742           3 :     if (!isBackup && mEnt->mUseFastOpen) {
    3743           3 :         socketTransport->SetFastOpenCallback(this);
    3744             :     }
    3745             : 
    3746           3 :     socketTransport->SetConnectionFlags(tmpFlags);
    3747             : 
    3748           3 :     const OriginAttributes& originAttributes = mEnt->mConnInfo->GetOriginAttributes();
    3749           3 :     if (originAttributes != OriginAttributes()) {
    3750           1 :         socketTransport->SetOriginAttributes(originAttributes);
    3751             :     }
    3752             : 
    3753           3 :     socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
    3754             : 
    3755           3 :     if (!ci->GetNetworkInterfaceId().IsEmpty()) {
    3756           0 :         socketTransport->SetNetworkInterfaceId(ci->GetNetworkInterfaceId());
    3757             :     }
    3758             : 
    3759           3 :     rv = socketTransport->SetEventSink(this, nullptr);
    3760           3 :     NS_ENSURE_SUCCESS(rv, rv);
    3761             : 
    3762           3 :     rv = socketTransport->SetSecurityCallbacks(this);
    3763           3 :     NS_ENSURE_SUCCESS(rv, rv);
    3764             : 
    3765           3 :     Telemetry::Accumulate(Telemetry::HTTP_CONNECTION_ENTRY_CACHE_HIT_1,
    3766           6 :                           mEnt->mUsedForConnection);
    3767           3 :     mEnt->mUsedForConnection = true;
    3768             : 
    3769           6 :     nsCOMPtr<nsIOutputStream> sout;
    3770           6 :     rv = socketTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,
    3771             :                                             0, 0,
    3772           6 :                                             getter_AddRefs(sout));
    3773           3 :     NS_ENSURE_SUCCESS(rv, rv);
    3774             : 
    3775           6 :     nsCOMPtr<nsIInputStream> sin;
    3776           6 :     rv = socketTransport->OpenInputStream(nsITransport::OPEN_UNBUFFERED,
    3777             :                                            0, 0,
    3778           6 :                                            getter_AddRefs(sin));
    3779           3 :     NS_ENSURE_SUCCESS(rv, rv);
    3780             : 
    3781           3 :     socketTransport.forget(transport);
    3782           3 :     CallQueryInterface(sin, instream);
    3783           3 :     CallQueryInterface(sout, outstream);
    3784             : 
    3785           3 :     rv = (*outstream)->AsyncWait(this, 0, 0, nullptr);
    3786           3 :     if (NS_SUCCEEDED(rv))
    3787           3 :         gHttpHandler->ConnMgr()->StartedConnect();
    3788             : 
    3789           3 :     return rv;
    3790             : }
    3791             : 
    3792             : nsresult
    3793           3 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams()
    3794             : {
    3795           3 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3796             : 
    3797             :     nsresult rv;
    3798             : 
    3799           3 :     mPrimarySynStarted = TimeStamp::Now();
    3800           9 :     rv = SetupStreams(getter_AddRefs(mSocketTransport),
    3801           6 :                       getter_AddRefs(mStreamIn),
    3802           6 :                       getter_AddRefs(mStreamOut),
    3803           3 :                       false);
    3804           3 :     LOG(("nsHalfOpenSocket::SetupPrimaryStream [this=%p ent=%s rv=%" PRIx32 "]",
    3805             :          this, mEnt->mConnInfo->Origin(), static_cast<uint32_t>(rv)));
    3806           3 :     if (NS_FAILED(rv)) {
    3807           0 :         if (mStreamOut)
    3808           0 :             mStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
    3809           0 :         if (mSocketTransport) {
    3810           0 :             mSocketTransport->SetFastOpenCallback(nullptr);
    3811             :         }
    3812           0 :         mStreamOut = nullptr;
    3813           0 :         mStreamIn = nullptr;
    3814           0 :         mSocketTransport = nullptr;
    3815             :     }
    3816           3 :     return rv;
    3817             : }
    3818             : 
    3819             : nsresult
    3820           0 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams()
    3821             : {
    3822           0 :     MOZ_ASSERT(mTransaction);
    3823             : 
    3824           0 :     mBackupSynStarted = TimeStamp::Now();
    3825           0 :     nsresult rv = SetupStreams(getter_AddRefs(mBackupTransport),
    3826           0 :                                getter_AddRefs(mBackupStreamIn),
    3827           0 :                                getter_AddRefs(mBackupStreamOut),
    3828           0 :                                true);
    3829           0 :     LOG(("nsHalfOpenSocket::SetupBackupStream [this=%p ent=%s rv=%" PRIx32 "]",
    3830             :          this, mEnt->mConnInfo->Origin(), static_cast<uint32_t>(rv)));
    3831           0 :     if (NS_FAILED(rv)) {
    3832           0 :         if (mBackupStreamOut)
    3833           0 :             mBackupStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
    3834           0 :         mBackupStreamOut = nullptr;
    3835           0 :         mBackupStreamIn = nullptr;
    3836           0 :         mBackupTransport = nullptr;
    3837             :     }
    3838           0 :     return rv;
    3839             : }
    3840             : 
    3841             : void
    3842           3 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
    3843             : {
    3844           3 :     uint16_t timeout = gHttpHandler->GetIdleSynTimeout();
    3845           3 :     MOZ_ASSERT(!mSynTimer, "timer already initd");
    3846           3 :     if (!timeout && mFastOpenInProgress) {
    3847           0 :         timeout = 250;
    3848             :     }
    3849             :     // When using Fast Open the correct transport will be setup for sure (it is
    3850             :     // guaranteed), but it can be that it will happened a bit later.
    3851           3 :     if (mFastOpenInProgress ||
    3852           3 :         (timeout && !mSpeculative)) {
    3853             :         // Setup the timer that will establish a backup socket
    3854             :         // if we do not get a writable event on the main one.
    3855             :         // We do this because a lost SYN takes a very long time
    3856             :         // to repair at the TCP level.
    3857             :         //
    3858             :         // Failure to setup the timer is something we can live with,
    3859             :         // so don't return an error in that case.
    3860             :         nsresult rv;
    3861           3 :         mSynTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
    3862           3 :         if (NS_SUCCEEDED(rv)) {
    3863           3 :             mSynTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
    3864           3 :             LOG(("nsHalfOpenSocket::SetupBackupTimer() [this=%p]", this));
    3865           3 :         }
    3866           0 :     } else if (timeout) {
    3867           0 :         LOG(("nsHalfOpenSocket::SetupBackupTimer() [this=%p], did not arm\n", this));
    3868             :     }
    3869           3 : }
    3870             : 
    3871             : void
    3872           6 : nsHttpConnectionMgr::nsHalfOpenSocket::CancelBackupTimer()
    3873             : {
    3874             :     // If the syntimer is still armed, we can cancel it because no backup
    3875             :     // socket should be formed at this point
    3876           6 :     if (!mSynTimer)
    3877           3 :         return;
    3878             : 
    3879           3 :     LOG(("nsHalfOpenSocket::CancelBackupTimer()"));
    3880           3 :     mSynTimer->Cancel();
    3881           3 :     mSynTimer = nullptr;
    3882             : }
    3883             : 
    3884             : void
    3885           2 : nsHttpConnectionMgr::nsHalfOpenSocket::Abandon()
    3886             : {
    3887           2 :     LOG(("nsHalfOpenSocket::Abandon [this=%p ent=%s] %p %p %p %p",
    3888             :          this, mEnt->mConnInfo->Origin(),
    3889             :          mSocketTransport.get(), mBackupTransport.get(),
    3890             :          mStreamOut.get(), mBackupStreamOut.get()));
    3891             : 
    3892           2 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3893             : 
    3894           4 :     RefPtr<nsHalfOpenSocket> deleteProtector(this);
    3895             : 
    3896             :     // Tell socket (and backup socket) to forget the half open socket.
    3897           2 :     if (mSocketTransport) {
    3898           0 :         mSocketTransport->SetEventSink(nullptr, nullptr);
    3899           0 :         mSocketTransport->SetSecurityCallbacks(nullptr);
    3900           0 :         mSocketTransport->SetFastOpenCallback(nullptr);
    3901           0 :         mSocketTransport = nullptr;
    3902             :     }
    3903           2 :     if (mBackupTransport) {
    3904           0 :         mBackupTransport->SetEventSink(nullptr, nullptr);
    3905           0 :         mBackupTransport->SetSecurityCallbacks(nullptr);
    3906           0 :         mBackupTransport = nullptr;
    3907             :     }
    3908             : 
    3909             :     // Tell output stream (and backup) to forget the half open socket.
    3910           2 :     if (mStreamOut) {
    3911           0 :         if (!mFastOpenInProgress) {
    3912             :             // If mFastOpenInProgress is true HalfOpen are not in mHalfOpen
    3913             :             // list and are not counted so we do not need to decrease counter.
    3914           0 :             gHttpHandler->ConnMgr()->RecvdConnect();
    3915             :         }
    3916           0 :         mStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
    3917           0 :         mStreamOut = nullptr;
    3918             :     }
    3919           2 :     if (mBackupStreamOut) {
    3920           0 :         gHttpHandler->ConnMgr()->RecvdConnect();
    3921           0 :         mBackupStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
    3922           0 :         mBackupStreamOut = nullptr;
    3923             :     }
    3924             : 
    3925             :     // Lose references to input stream (and backup).
    3926           2 :     mStreamIn = mBackupStreamIn = nullptr;
    3927             : 
    3928             :     // Stop the timer - we don't want any new backups.
    3929           2 :     CancelBackupTimer();
    3930             : 
    3931             :     // Remove the half open from the connection entry.
    3932           2 :     if (mEnt) {
    3933           2 :         mEnt->mDoNotDestroy = false;
    3934           2 :         mEnt->RemoveHalfOpen(this);
    3935             :     }
    3936           2 :     mEnt = nullptr;
    3937           2 : }
    3938             : 
    3939             : double
    3940           0 : nsHttpConnectionMgr::nsHalfOpenSocket::Duration(TimeStamp epoch)
    3941             : {
    3942           0 :     if (mPrimarySynStarted.IsNull())
    3943           0 :         return 0;
    3944             : 
    3945           0 :     return (epoch - mPrimarySynStarted).ToMilliseconds();
    3946             : }
    3947             : 
    3948             : 
    3949             : NS_IMETHODIMP // method for nsITimerCallback
    3950           0 : nsHttpConnectionMgr::nsHalfOpenSocket::Notify(nsITimer *timer)
    3951             : {
    3952           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3953           0 :     MOZ_ASSERT(timer == mSynTimer, "wrong timer");
    3954             : 
    3955           0 :     DebugOnly<nsresult> rv = SetupBackupStreams();
    3956           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    3957             : 
    3958           0 :     mSynTimer = nullptr;
    3959           0 :     return NS_OK;
    3960             : }
    3961             : 
    3962             : already_AddRefed<nsHttpConnectionMgr::PendingTransactionInfo>
    3963          16 : nsHttpConnectionMgr::
    3964             : nsHalfOpenSocket::FindTransactionHelper(bool removeWhenFound)
    3965             : {
    3966          16 :     uint32_t caps = mTransaction->Caps();
    3967          16 :     nsTArray<RefPtr<PendingTransactionInfo>> *pendingQ = nullptr;
    3968          16 :     if (caps & NS_HTTP_URGENT_START) {
    3969           5 :         pendingQ = &mEnt->mUrgentStartQ;
    3970             :     } else {
    3971             :         pendingQ =
    3972          11 :             mEnt->mPendingTransactionTable.Get(
    3973          22 :                 mTransaction->TopLevelOuterContentWindowId());
    3974             :     }
    3975             : 
    3976             :     int32_t index = pendingQ
    3977          48 :         ? pendingQ->IndexOf(mTransaction, 0, PendingComparator())
    3978          32 :         : -1;
    3979             : 
    3980          32 :     RefPtr<PendingTransactionInfo> info;
    3981          16 :     if (index != -1) {
    3982          16 :         info = (*pendingQ)[index];
    3983          16 :         if (removeWhenFound) {
    3984           3 :             pendingQ->RemoveElementAt(index);
    3985             :         }
    3986             :     }
    3987          32 :     return info.forget();
    3988             : }
    3989             : 
    3990             : // method for nsIAsyncOutputStreamCallback
    3991             : NS_IMETHODIMP
    3992           1 : nsHttpConnectionMgr::
    3993             : nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
    3994             : {
    3995           1 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    3996           1 :     MOZ_ASSERT(out == mStreamOut || out == mBackupStreamOut,
    3997             :                "stream mismatch");
    3998           1 :     LOG(("nsHalfOpenSocket::OnOutputStreamReady [this=%p ent=%s %s]\n",
    3999             :          this, mEnt->mConnInfo->Origin(),
    4000             :          out == mStreamOut ? "primary" : "backup"));
    4001             : 
    4002           1 :     mEnt->mDoNotDestroy = true;
    4003           1 :     gHttpHandler->ConnMgr()->RecvdConnect();
    4004             : 
    4005           1 :     CancelBackupTimer();
    4006             : 
    4007           1 :     if (mFastOpenInProgress) {
    4008           0 :         LOG(("nsHalfOpenSocket::OnOutputStreamReady backup stream is ready, "
    4009             :              "close the fast open socket %p [this=%p ent=%s]\n",
    4010             :              mSocketTransport.get(), this, mEnt->mConnInfo->Origin()));
    4011             :         // If fast open is used, right after a socket for the primary stream is
    4012             :         // created a nsHttpConnection is created for that socket. The connection
    4013             :         // listens for  OnOutputStreamReady not HalfOpenSocket. So this stream
    4014             :         // cannot be mStreamOut.
    4015           0 :         MOZ_ASSERT((out == mBackupStreamOut) && mConnectionNegotiatingFastOpen);
    4016             :         // Here the backup, non-TFO connection has connected successfully,
    4017             :         // before the TFO connection.
    4018             :         //
    4019             :         // The primary, TFO connection will be cancelled and the transaction
    4020             :         // will be rewind. CloseConnectionFastOpenTakesTooLongOrError will
    4021             :         // return the rewind transaction. The transaction will be put back to
    4022             :         // the pending queue and as well connected to this halfOpenSocket.
    4023             :         // SetupConn should set up a new nsHttpConnection with the backup
    4024             :         // socketTransport and the rewind transaction.
    4025           0 :         mSocketTransport->SetFastOpenCallback(nullptr);
    4026           0 :         mConnectionNegotiatingFastOpen->SetFastOpen(false);
    4027           0 :         mEnt->mHalfOpenFastOpenBackups.RemoveElement(this);
    4028             :         RefPtr<nsAHttpTransaction> trans =
    4029           0 :             mConnectionNegotiatingFastOpen->CloseConnectionFastOpenTakesTooLongOrError(true);
    4030           0 :         mSocketTransport = nullptr;
    4031           0 :         mStreamOut = nullptr;
    4032           0 :         mStreamIn = nullptr;
    4033             : 
    4034           0 :         if (trans && trans->QueryHttpTransaction()) {
    4035             :             RefPtr<PendingTransactionInfo> pendingTransInfo =
    4036           0 :                 new PendingTransactionInfo(trans->QueryHttpTransaction());
    4037           0 :             pendingTransInfo->mHalfOpen =
    4038           0 :                 do_GetWeakReference(static_cast<nsISupportsWeakReference*>(this));
    4039           0 :             if (trans->Caps() & NS_HTTP_URGENT_START) {
    4040           0 :                 gHttpHandler->ConnMgr()->InsertTransactionSorted(mEnt->mUrgentStartQ,
    4041             :                                                                  pendingTransInfo,
    4042           0 :                                                                  true);
    4043             :             } else {
    4044           0 :                 mEnt->InsertTransaction(pendingTransInfo, true);
    4045             :             }
    4046             :         }
    4047           0 :         if (mEnt->mUseFastOpen) {
    4048           0 :             gHttpHandler->IncrementFastOpenConsecutiveFailureCounter();
    4049           0 :             mEnt->mUseFastOpen = false;
    4050             :         }
    4051             : 
    4052           0 :         mFastOpenInProgress = false;
    4053           0 :         mConnectionNegotiatingFastOpen = nullptr;
    4054             :     }
    4055           1 :     MOZ_DIAGNOSTIC_ASSERT(mEnt);
    4056           1 :     nsresult rv =  SetupConn(out, false);
    4057           1 :     if (mEnt) {
    4058           1 :         mEnt->mDoNotDestroy = false;
    4059             :     }
    4060           1 :     return rv;
    4061             : }
    4062             : 
    4063             : bool
    4064           3 : nsHttpConnectionMgr::
    4065             : nsHalfOpenSocket::FastOpenEnabled()
    4066             : {
    4067           3 :     LOG(("nsHalfOpenSocket::FastOpenEnabled [this=%p]\n", this));
    4068             : 
    4069           3 :     MOZ_DIAGNOSTIC_ASSERT(mEnt);
    4070             : 
    4071           3 :     if (!mEnt) {
    4072           0 :         return false;
    4073             :     }
    4074             : 
    4075             :     // If mEnt is present this HalfOpen must be in the mHalfOpens,
    4076             :     // but we want to be sure!!!
    4077           3 :     if (!mEnt->mHalfOpens.Contains(this)) {
    4078           0 :         return false;
    4079             :     }
    4080             : 
    4081           3 :     if (!gHttpHandler->UseFastOpen()) {
    4082             :         // fast open was turned off.
    4083           0 :         LOG(("nsHalfOpenSocket::FastEnabled - fast open was turned off.\n"));
    4084           0 :         mEnt->mUseFastOpen = false;
    4085           0 :         return false;
    4086             :     }
    4087             :     // We can use FastOpen if we have a transaction or if it is ssl
    4088             :     // connection. For ssl we will use a null transaction to drive the SSL
    4089             :     // handshake to completion if there is not a pending transaction. Afterwards
    4090             :     // the connection will be 100% ready for the next transaction to use it.
    4091             :     // Make an exception for SSL tunneled HTTP proxy as the NullHttpTransaction
    4092             :     // does not know how to drive Connect.
    4093           6 :     RefPtr<PendingTransactionInfo> info = FindTransactionHelper(false);
    4094             : 
    4095           3 :     if ((!info) &&
    4096           0 :         (!mEnt->mConnInfo->FirstHopSSL() || mEnt->mConnInfo->UsingConnect())) {
    4097           0 :         LOG(("nsHalfOpenSocket::FastOpenEnabled - It is a connection without "
    4098             :              "transaction and first hop is not ssl.\n"));
    4099           0 :         return false;
    4100             :     }
    4101             : 
    4102           3 :     if ((info) && !mEnt->mConnInfo->FirstHopSSL()) {
    4103             :         // The following function call will check whether is possible to send
    4104             :         // data during fast open
    4105           3 :         if (!info->mTransaction->CanDo0RTT()) {
    4106           1 :             LOG(("nsHalfOpenSocket::FastOpenEnabled - it is not safe to restart "
    4107             :                  "transaction.\n"));
    4108           1 :             return false;
    4109             :         }
    4110             :     }
    4111             : 
    4112           2 :     return true;
    4113             : }
    4114             : 
    4115             : nsresult
    4116           2 : nsHttpConnectionMgr::
    4117             : nsHalfOpenSocket::StartFastOpen()
    4118             : {
    4119           2 :     MOZ_ASSERT(mStreamOut);
    4120           2 :     MOZ_ASSERT(mEnt && !mBackupTransport);
    4121             : 
    4122           2 :     LOG(("nsHalfOpenSocket::StartFastOpen [this=%p]\n",
    4123             :          this));
    4124             : 
    4125           4 :     RefPtr<nsHalfOpenSocket> deleteProtector(this);
    4126             : 
    4127           2 :     mFastOpenInProgress = true;
    4128             :     // Remove this HalfOpen from mEnt->mHalfOpens.
    4129             :     // The new connection will take care of closing this HalfOpen from now on!
    4130           2 :     if (!mEnt->mHalfOpens.RemoveElement(this)) {
    4131           0 :         MOZ_ASSERT(false, "HalfOpen is not in mHalfOpens!");
    4132             :         mSocketTransport->SetFastOpenCallback(nullptr);
    4133             :         CancelBackupTimer();
    4134             :         mStreamOut = nullptr;
    4135             :         mStreamIn = nullptr;
    4136             :         mSocketTransport = nullptr;
    4137             :         mFastOpenInProgress = false;
    4138             :         Abandon();
    4139             :         return NS_ERROR_ABORT;
    4140             :     }
    4141           2 :     mEnt->mDoNotDestroy = true;
    4142           2 :     MOZ_ASSERT(gHttpHandler->ConnMgr()->mNumHalfOpenConns);
    4143           2 :     if (gHttpHandler->ConnMgr()->mNumHalfOpenConns) { // just in case
    4144           2 :         gHttpHandler->ConnMgr()->mNumHalfOpenConns--;
    4145             :     }
    4146             : 
    4147             :     // Count this socketTransport as connected.
    4148           2 :     gHttpHandler->ConnMgr()->RecvdConnect();
    4149             : 
    4150             :     // Remove HalfOpen from callbacks, the new connection will take them.
    4151           2 :     mSocketTransport->SetEventSink(nullptr, nullptr);
    4152           2 :     mSocketTransport->SetSecurityCallbacks(nullptr);
    4153           2 :     mStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
    4154             : 
    4155           2 :     nsresult rv = SetupConn(mStreamOut, true);
    4156           2 :     if (!mConnectionNegotiatingFastOpen) {
    4157           0 :         LOG(("nsHalfOpenSocket::StartFastOpen SetupConn failed "
    4158             :              "[this=%p rv=%x]\n", this, static_cast<uint32_t>(rv)));
    4159           0 :         if (NS_SUCCEEDED(rv)) {
    4160           0 :             rv = NS_ERROR_ABORT;
    4161             :         }
    4162             :         // If SetupConn failed this will CloseTransaction and socketTransport
    4163             :         // with an error, therefore we can close this HalfOpen. socketTransport
    4164             :         // will remove reference to this HalfOpen as well.
    4165           0 :         mSocketTransport->SetFastOpenCallback(nullptr);
    4166           0 :         CancelBackupTimer();
    4167           0 :         mStreamOut = nullptr;
    4168           0 :         mStreamIn = nullptr;
    4169           0 :         mSocketTransport = nullptr;
    4170           0 :         mFastOpenInProgress = false;
    4171             : 
    4172             :         // The connection is responsible to take care of the halfOpen so we
    4173             :         // need to clean it up.
    4174           0 :         Abandon();
    4175             :     } else {
    4176           2 :         LOG(("nsHalfOpenSocket::StartFastOpen [this=%p conn=%p]\n",
    4177             :              this, mConnectionNegotiatingFastOpen.get()));
    4178           2 :         mEnt->mHalfOpenFastOpenBackups.AppendElement(this);
    4179             :         // SetupBackupTimer should setup timer which will hold a ref to this
    4180             :         // halfOpen. It will failed only if it cannot create timer. Anyway just
    4181             :         // to be sure I will add this deleteProtector!!!
    4182           2 :         if (!mSynTimer) {
    4183             :             // For Fast Open we will setup backup timer also for
    4184             :             // NullTransaction.
    4185             :             // So maybe it is not set and we need to set it here.
    4186           0 :             SetupBackupTimer();
    4187             :         }
    4188             :     }
    4189           2 :     if (mEnt) {
    4190           2 :       mEnt->mDoNotDestroy = false;
    4191             :     }
    4192           2 :     return rv;
    4193             : }
    4194             : 
    4195             : void
    4196           2 : nsHttpConnectionMgr::
    4197             : nsHalfOpenSocket::SetFastOpenConnected(nsresult aError, bool aWillRetry)
    4198             : {
    4199           2 :     MOZ_ASSERT(mFastOpenInProgress);
    4200             : 
    4201           2 :     LOG(("nsHalfOpenSocket::SetFastOpenConnected [this=%p conn=%p error=%x]\n",
    4202             :          this, mConnectionNegotiatingFastOpen.get(),
    4203             :          static_cast<uint32_t>(aError)));
    4204             : 
    4205             :     // mConnectionNegotiatingFastOpen is set after a StartFastOpen creates
    4206             :     // and activates a nsHttpConnection successfully (SetupConn calls
    4207             :     // DispatchTransaction and DispatchAbstractTransaction which calls
    4208             :     // conn->Activate).
    4209             :     // nsHttpConnection::Activate can fail which will close socketTransport
    4210             :     // and socketTransport will call this function. The FastOpen clean up
    4211             :     // in case nsHttpConnection::Activate fails will be done in StartFastOpen.
    4212             :     // Also OnMsgReclaimConnection can decided that we do not need this
    4213             :     // transaction and cancel it as well.
    4214             :     // In all other cases mConnectionNegotiatingFastOpen must not be nullptr.
    4215           2 :     if (!mConnectionNegotiatingFastOpen) {
    4216           0 :         return;
    4217             :     }
    4218             : 
    4219           4 :     RefPtr<nsHalfOpenSocket> deleteProtector(this);
    4220             : 
    4221           2 :     mEnt->mDoNotDestroy = true;
    4222             : 
    4223             :     // Delete 2 points of entry to FastOpen function so that we do not reenter.
    4224           2 :     mEnt->mHalfOpenFastOpenBackups.RemoveElement(this);
    4225           2 :     mSocketTransport->SetFastOpenCallback(nullptr);
    4226             : 
    4227           2 :     mConnectionNegotiatingFastOpen->SetFastOpen(false);
    4228             : 
    4229             :     // Check if we want to restart connection!
    4230           2 :     if (aWillRetry &&
    4231           0 :         ((aError == NS_ERROR_CONNECTION_REFUSED) ||
    4232             :          (aError == NS_ERROR_NET_TIMEOUT))) {
    4233           0 :         if (mEnt->mUseFastOpen) {
    4234           0 :             gHttpHandler->IncrementFastOpenConsecutiveFailureCounter();
    4235           0 :             mEnt->mUseFastOpen = false;
    4236             :         }
    4237             :         // This is called from nsSocketTransport::RecoverFromError. The
    4238             :         // socket will try connect and we need to rewind nsHttpTransaction.
    4239             : 
    4240             :         RefPtr<nsAHttpTransaction> trans =
    4241           0 :             mConnectionNegotiatingFastOpen->CloseConnectionFastOpenTakesTooLongOrError(false);
    4242           0 :         if (trans && trans->QueryHttpTransaction()) {
    4243             :             RefPtr<PendingTransactionInfo> pendingTransInfo =
    4244           0 :                 new PendingTransactionInfo(trans->QueryHttpTransaction());
    4245           0 :             pendingTransInfo->mHalfOpen =
    4246           0 :                 do_GetWeakReference(static_cast<nsISupportsWeakReference*>(this));
    4247           0 :             if (trans->Caps() & NS_HTTP_URGENT_START) {
    4248           0 :                 gHttpHandler->ConnMgr()->InsertTransactionSorted(mEnt->mUrgentStartQ,
    4249             :                                                                  pendingTransInfo,
    4250           0 :                                                                  true);
    4251             :             } else {
    4252           0 :                 mEnt->InsertTransaction(pendingTransInfo, true);
    4253             :             }
    4254             :         }
    4255             :         // We are doing a restart without fast open, so the easiest way is to
    4256             :         // return mSocketTransport to the halfOpenSock and destroy connection.
    4257             :         // This makes http2 implemenntation easier.
    4258             :         // mConnectionNegotiatingFastOpen is going away and halfOpen is taking
    4259             :         // this mSocketTransport so add halfOpen to mEnt and update
    4260             :         // mNumActiveConns.
    4261           0 :         mEnt->mHalfOpens.AppendElement(this);
    4262           0 :         gHttpHandler->ConnMgr()->mNumHalfOpenConns++;
    4263           0 :         gHttpHandler->ConnMgr()->StartedConnect();
    4264             : 
    4265             :         // Restore callbacks.
    4266           0 :         mStreamOut->AsyncWait(this, 0, 0, nullptr);
    4267           0 :         mSocketTransport->SetEventSink(this, nullptr);
    4268           0 :         mSocketTransport->SetSecurityCallbacks(this);
    4269           0 :         mStreamOut->AsyncWait(this, 0, 0, nullptr);
    4270             : 
    4271             :     } else {
    4272             :         // On success or other error we proceed with connection, we just need
    4273             :         // to close backup timer and halfOpenSock.
    4274           2 :         CancelBackupTimer();
    4275           2 :         if (NS_SUCCEEDED(aError)) {
    4276             :             NetAddr peeraddr;
    4277           2 :             if (NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr))) {
    4278           2 :                 mEnt->RecordIPFamilyPreference(peeraddr.raw.family);
    4279             :             }
    4280           2 :             gHttpHandler->ResetFastOpenConsecutiveFailureCounter();
    4281             :         }
    4282           2 :         mSocketTransport = nullptr;
    4283           2 :         mStreamOut = nullptr;
    4284           2 :         mStreamIn = nullptr;
    4285             : 
    4286           2 :         Abandon();
    4287             :     }
    4288             : 
    4289           2 :     mFastOpenInProgress = false;
    4290           2 :     mConnectionNegotiatingFastOpen = nullptr;
    4291           2 :     if (mEnt) {
    4292           0 :         mEnt->mDoNotDestroy = false;
    4293             :     }
    4294             : }
    4295             : 
    4296             : void
    4297           2 : nsHttpConnectionMgr::
    4298             : nsHalfOpenSocket::SetFastOpenStatus(uint8_t tfoStatus)
    4299             : {
    4300           2 :     MOZ_ASSERT(mFastOpenInProgress);
    4301           2 :     mConnectionNegotiatingFastOpen->SetFastOpenStatus(tfoStatus);
    4302           2 :     mConnectionNegotiatingFastOpen->Transaction()->SetFastOpenStatus(tfoStatus);
    4303           2 : }
    4304             : 
    4305             : void
    4306           0 : nsHttpConnectionMgr::
    4307             : nsHalfOpenSocket::CancelFastOpenConnection()
    4308             : {
    4309           0 :     MOZ_ASSERT(mFastOpenInProgress);
    4310             : 
    4311           0 :     LOG(("nsHalfOpenSocket::CancelFastOpenConnection [this=%p conn=%p]\n",
    4312             :          this, mConnectionNegotiatingFastOpen.get()));
    4313             : 
    4314           0 :     RefPtr<nsHalfOpenSocket> deleteProtector(this);
    4315           0 :     mEnt->mHalfOpenFastOpenBackups.RemoveElement(this);
    4316           0 :     mSocketTransport->SetFastOpenCallback(nullptr);
    4317           0 :     mConnectionNegotiatingFastOpen->SetFastOpen(false);
    4318             :     RefPtr<nsAHttpTransaction> trans =
    4319           0 :         mConnectionNegotiatingFastOpen->CloseConnectionFastOpenTakesTooLongOrError(true);
    4320           0 :     mSocketTransport = nullptr;
    4321           0 :     mStreamOut = nullptr;
    4322           0 :     mStreamIn = nullptr;
    4323             : 
    4324           0 :     if (trans && trans->QueryHttpTransaction()) {
    4325             :         RefPtr<PendingTransactionInfo> pendingTransInfo =
    4326           0 :             new PendingTransactionInfo(trans->QueryHttpTransaction());
    4327             : 
    4328           0 :         if (trans->Caps() & NS_HTTP_URGENT_START) {
    4329           0 :             gHttpHandler->ConnMgr()->InsertTransactionSorted(mEnt->mUrgentStartQ,
    4330           0 :                                                              pendingTransInfo, true);
    4331             :         } else {
    4332           0 :             mEnt->InsertTransaction(pendingTransInfo, true);
    4333             :         }
    4334             :     }
    4335             : 
    4336           0 :     mFastOpenInProgress = false;
    4337           0 :     mConnectionNegotiatingFastOpen = nullptr;
    4338           0 :     Abandon();
    4339           0 : }
    4340             : 
    4341             : void
    4342           0 : nsHttpConnectionMgr::
    4343             : nsHalfOpenSocket::FastOpenNotSupported()
    4344             : {
    4345           0 :   MOZ_ASSERT(mFastOpenInProgress);
    4346           0 :   gHttpHandler->SetFastOpenNotSupported();
    4347           0 : }
    4348             : 
    4349             : nsresult
    4350           3 : nsHttpConnectionMgr::
    4351             : nsHalfOpenSocket::SetupConn(nsIAsyncOutputStream *out,
    4352             :                             bool aFastOpen)
    4353             : {
    4354           3 :     MOZ_ASSERT(!aFastOpen || (out == mStreamOut));
    4355             :     // assign the new socket to the http connection
    4356           6 :     RefPtr<nsHttpConnection> conn = new nsHttpConnection();
    4357           3 :     LOG(("nsHalfOpenSocket::SetupConn "
    4358             :          "Created new nshttpconnection %p\n", conn.get()));
    4359             : 
    4360             :     // Some capabilities are needed before a transaciton actually gets
    4361             :     // scheduled (e.g. how to negotiate false start)
    4362           3 :     conn->SetTransactionCaps(mTransaction->Caps());
    4363             : 
    4364             :     NetAddr peeraddr;
    4365           6 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
    4366           3 :     mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
    4367             :     nsresult rv;
    4368           3 :     if (out == mStreamOut) {
    4369           3 :         TimeDuration rtt = TimeStamp::Now() - mPrimarySynStarted;
    4370           9 :         rv = conn->Init(mEnt->mConnInfo,
    4371           3 :                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
    4372             :                         mSocketTransport, mStreamIn, mStreamOut,
    4373           3 :                         mPrimaryConnectedOK || aFastOpen, callbacks,
    4374             :                         PR_MillisecondsToInterval(
    4375           6 :                           static_cast<uint32_t>(rtt.ToMilliseconds())));
    4376             : 
    4377           4 :         if (!aFastOpen &&
    4378           1 :             NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr))) {
    4379           1 :             mEnt->RecordIPFamilyPreference(peeraddr.raw.family);
    4380             :         }
    4381             : 
    4382             :         // The nsHttpConnection object now owns these streams and sockets
    4383           3 :         if (!aFastOpen) {
    4384           1 :             mStreamOut = nullptr;
    4385           1 :             mStreamIn = nullptr;
    4386           1 :             mSocketTransport = nullptr;
    4387             :         } else {
    4388           2 :             conn->SetFastOpen(true);
    4389             :         }
    4390           0 :     } else if (out == mBackupStreamOut) {
    4391           0 :         TimeDuration rtt = TimeStamp::Now() - mBackupSynStarted;
    4392           0 :         rv = conn->Init(mEnt->mConnInfo,
    4393           0 :                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
    4394             :                         mBackupTransport, mBackupStreamIn, mBackupStreamOut,
    4395           0 :                         mBackupConnectedOK, callbacks,
    4396             :                         PR_MillisecondsToInterval(
    4397           0 :                           static_cast<uint32_t>(rtt.ToMilliseconds())));
    4398             : 
    4399           0 :         if (NS_SUCCEEDED(mBackupTransport->GetPeerAddr(&peeraddr)))
    4400           0 :             mEnt->RecordIPFamilyPreference(peeraddr.raw.family);
    4401             : 
    4402             :         // The nsHttpConnection object now owns these streams and sockets
    4403           0 :         mBackupStreamOut = nullptr;
    4404           0 :         mBackupStreamIn = nullptr;
    4405           0 :         mBackupTransport = nullptr;
    4406             :     } else {
    4407           0 :         MOZ_ASSERT(false, "unexpected stream");
    4408             :         rv = NS_ERROR_UNEXPECTED;
    4409             :     }
    4410             : 
    4411           3 :     if (NS_FAILED(rv)) {
    4412           0 :         LOG(("nsHalfOpenSocket::SetupConn "
    4413             :              "conn->init (%p) failed %" PRIx32 "\n",
    4414             :              conn.get(), static_cast<uint32_t>(rv)));
    4415           0 :         return rv;
    4416             :     }
    4417             : 
    4418             :     // This half-open socket has created a connection.  This flag excludes it
    4419             :     // from counter of actual connections used for checking limits.
    4420           3 :     if (!aFastOpen) {
    4421           1 :         mHasConnected = true;
    4422             :     }
    4423             : 
    4424             :     // if this is still in the pending list, remove it and dispatch it
    4425           6 :     RefPtr<PendingTransactionInfo> pendingTransInfo = FindTransactionHelper(true);
    4426           3 :     if (pendingTransInfo) {
    4427           3 :         MOZ_ASSERT(!mSpeculative,
    4428             :                    "Speculative Half Open found mTransaction");
    4429             : 
    4430           3 :         gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
    4431           6 :         rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt,
    4432           3 :                                                           pendingTransInfo->mTransaction,
    4433           3 :                                                           conn);
    4434             :     } else {
    4435             :         // this transaction was dispatched off the pending q before all the
    4436             :         // sockets established themselves.
    4437             : 
    4438             :         // After about 1 second allow for the possibility of restarting a
    4439             :         // transaction due to server close. Keep at sub 1 second as that is the
    4440             :         // minimum granularity we can expect a server to be timing out with.
    4441           0 :         conn->SetIsReusedAfter(950);
    4442             : 
    4443             :         // if we are using ssl and no other transactions are waiting right now,
    4444             :         // then form a null transaction to drive the SSL handshake to
    4445             :         // completion. Afterwards the connection will be 100% ready for the next
    4446             :         // transaction to use it. Make an exception for SSL tunneled HTTP proxy as the
    4447             :         // NullHttpTransaction does not know how to drive Connect
    4448           0 :         if (mEnt->mConnInfo->FirstHopSSL() &&
    4449           0 :             !mEnt->mUrgentStartQ.Length() &&
    4450           0 :             !mEnt->PendingQLength() &&
    4451           0 :             !mEnt->mConnInfo->UsingConnect()) {
    4452           0 :             LOG(("nsHalfOpenSocket::SetupConn null transaction will "
    4453             :                  "be used to finish SSL handshake on conn %p\n", conn.get()));
    4454           0 :             RefPtr<nsAHttpTransaction> trans;
    4455           0 :             if (mTransaction->IsNullTransaction() && !mDispatchedMTransaction) {
    4456             :                 // null transactions cannot be put in the entry queue, so that
    4457             :                 // explains why it is not present.
    4458           0 :                 mDispatchedMTransaction = true;
    4459           0 :                 trans = mTransaction;
    4460             :             } else {
    4461           0 :                 trans = new NullHttpTransaction(mEnt->mConnInfo,
    4462           0 :                                                 callbacks, mCaps);
    4463             :             }
    4464             : 
    4465           0 :             gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
    4466             :             rv = gHttpHandler->ConnMgr()->
    4467           0 :                 DispatchAbstractTransaction(mEnt, trans, mCaps, conn, 0);
    4468             :         } else {
    4469             :             // otherwise just put this in the persistent connection pool
    4470           0 :             LOG(("nsHalfOpenSocket::SetupConn no transaction match "
    4471             :                  "returning conn %p to pool\n", conn.get()));
    4472           0 :             gHttpHandler->ConnMgr()->OnMsgReclaimConnection(0, conn);
    4473             : 
    4474             :             // We expect that there is at least one tranasction in the pending
    4475             :             // queue that can take this connection, but it can happened that
    4476             :             // all transactions are blocked or they have took other idle
    4477             :             // connections. In that case the connection has been added to the
    4478             :             // idle queue.
    4479             :             // If the connection is in the idle queue but it is using ssl, make
    4480             :             // a nulltransaction for it to finish ssl handshake!
    4481             : 
    4482             :             // !!! It can be that mEnt is null after OnMsgReclaimConnection.!!!
    4483           0 :             if (mEnt &&
    4484           0 :                 mEnt->mConnInfo->FirstHopSSL() &&
    4485           0 :                 !mEnt->mConnInfo->UsingConnect()) {
    4486           0 :                 int32_t idx = mEnt->mIdleConns.IndexOf(conn);
    4487           0 :                 if (idx != -1) {
    4488           0 :                     DebugOnly<nsresult> rvDeb = gHttpHandler->ConnMgr()->RemoveIdleConnection(conn);
    4489           0 :                     MOZ_ASSERT(NS_SUCCEEDED(rvDeb));
    4490           0 :                     conn->EndIdleMonitoring();
    4491           0 :                     RefPtr<nsAHttpTransaction> trans;
    4492           0 :                     if (mTransaction->IsNullTransaction() &&
    4493           0 :                         !mDispatchedMTransaction) {
    4494           0 :                         mDispatchedMTransaction = true;
    4495           0 :                         trans = mTransaction;
    4496             :                     } else {
    4497           0 :                         trans = new NullHttpTransaction(mEnt->mConnInfo,
    4498           0 :                                                         callbacks, mCaps);
    4499             :                     }
    4500           0 :                     gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
    4501             :                     rv = gHttpHandler->ConnMgr()->
    4502           0 :                         DispatchAbstractTransaction(mEnt, trans, mCaps, conn, 0);
    4503             :                 }
    4504             :             }
    4505             :         }
    4506             :     }
    4507             : 
    4508             :     // If this connection has a transaction get reference to its
    4509             :     // ConnectionHandler.
    4510           3 :     if (aFastOpen) {
    4511           2 :         MOZ_ASSERT(mEnt);
    4512           2 :         MOZ_ASSERT(static_cast<int32_t>(mEnt->mIdleConns.IndexOf(conn)) == -1);
    4513           2 :         int32_t idx = mEnt->mActiveConns.IndexOf(conn);
    4514           2 :         if (NS_SUCCEEDED(rv) && (idx != -1)) {
    4515           2 :             mConnectionNegotiatingFastOpen = conn;
    4516             :         } else {
    4517           0 :             conn->SetFastOpen(false);
    4518             :         }
    4519             :     }
    4520             : 
    4521             :     // If this halfOpenConn was speculative, but at the ende the conn got a
    4522             :     // non-null transaction than this halfOpen is not speculative anymore!
    4523           3 :     if (conn->Transaction() && !conn->Transaction()->IsNullTransaction()) {
    4524           3 :         Claim();
    4525             :     }
    4526             : 
    4527           3 :     return rv;
    4528             : }
    4529             : 
    4530             : // register a connection to receive CanJoinConnection() for particular
    4531             : // origin keys
    4532             : void
    4533           0 : nsHttpConnectionMgr::RegisterOriginCoalescingKey(nsHttpConnection *conn,
    4534             :                                                  const nsACString &host,
    4535             :                                                  int32_t port)
    4536             : {
    4537           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    4538           0 :     nsHttpConnectionInfo *ci = conn ? conn->ConnectionInfo() : nullptr;
    4539           0 :     if (!ci || !conn->CanDirectlyActivate()) {
    4540           0 :         return;
    4541             :     }
    4542             : 
    4543           0 :     nsCString newKey;
    4544           0 :     BuildOriginFrameHashKey(newKey, ci, host, port);
    4545           0 :     nsTArray<nsWeakPtr> *listOfWeakConns =  mCoalescingHash.Get(newKey);
    4546           0 :     if (!listOfWeakConns) {
    4547           0 :         listOfWeakConns = new nsTArray<nsWeakPtr>(1);
    4548           0 :         mCoalescingHash.Put(newKey, listOfWeakConns);
    4549             :     }
    4550           0 :     listOfWeakConns->AppendElement(do_GetWeakReference(static_cast<nsISupportsWeakReference*>(conn)));
    4551             : 
    4552           0 :     LOG(("nsHttpConnectionMgr::RegisterOriginCoalescingKey "
    4553             :          "Established New Coalescing Key %s to %p %s\n",
    4554             :          newKey.get(), conn, ci->HashKey().get()));
    4555             : }
    4556             : 
    4557             : // method for nsITransportEventSink
    4558             : NS_IMETHODIMP
    4559          10 : nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
    4560             :                                                          nsresult status,
    4561             :                                                          int64_t progress,
    4562             :                                                          int64_t progressMax)
    4563             : {
    4564          10 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    4565             : 
    4566          10 :     MOZ_DIAGNOSTIC_ASSERT(mEnt);
    4567          10 :     if (mTransaction) {
    4568          20 :         RefPtr<PendingTransactionInfo> info = FindTransactionHelper(false);
    4569          20 :         if ((trans == mSocketTransport) ||
    4570           0 :             ((trans == mBackupTransport) && (status == NS_NET_STATUS_CONNECTED_TO) &&
    4571           0 :             info)) {
    4572             :             // Send this status event only if the transaction is still panding,
    4573             :             // i.e. it has not found a free already connected socket.
    4574             :             // Sockets in halfOpen state can only get following events:
    4575             :             // NS_NET_STATUS_RESOLVING_HOST, NS_NET_STATUS_RESOLVED_HOST,
    4576             :             // NS_NET_STATUS_CONNECTING_TO and NS_NET_STATUS_CONNECTED_TO.
    4577             :             // mBackupTransport is only started after
    4578             :             // NS_NET_STATUS_CONNECTING_TO of mSocketTransport, so ignore all
    4579             :             // mBackupTransport events until NS_NET_STATUS_CONNECTED_TO.
    4580          10 :             mTransaction->OnTransportStatus(trans, status, progress);
    4581             :         }
    4582             :     }
    4583             : 
    4584          10 :     MOZ_ASSERT(trans == mSocketTransport || trans == mBackupTransport);
    4585          10 :     if (status == NS_NET_STATUS_CONNECTED_TO) {
    4586           1 :         if (trans == mSocketTransport) {
    4587           1 :             mPrimaryConnectedOK = true;
    4588             :         } else {
    4589           0 :             mBackupConnectedOK = true;
    4590             :         }
    4591             :     }
    4592             : 
    4593             :     // The rest of this method only applies to the primary transport
    4594          10 :     if (trans != mSocketTransport) {
    4595           0 :         return NS_OK;
    4596             :     }
    4597             : 
    4598          10 :     mPrimaryStreamStatus = status;
    4599             : 
    4600             :     // if we are doing spdy coalescing and haven't recorded the ip address
    4601             :     // for this entry before then make the hash key if our dns lookup
    4602             :     // just completed. We can't do coalescing if using a proxy because the
    4603             :     // ip addresses are not available to the client.
    4604             : 
    4605          13 :     if (status == NS_NET_STATUS_CONNECTING_TO &&
    4606           6 :         gHttpHandler->IsSpdyEnabled() &&
    4607           6 :         gHttpHandler->CoalesceSpdy() &&
    4608           6 :         mEnt && mEnt->mConnInfo && mEnt->mConnInfo->EndToEndSSL() &&
    4609          10 :         !mEnt->mConnInfo->UsingProxy() &&
    4610           0 :         mEnt->mCoalescingKeys.IsEmpty()) {
    4611             : 
    4612           0 :         nsCOMPtr<nsIDNSRecord> dnsRecord(do_GetInterface(mSocketTransport));
    4613           0 :         nsTArray<NetAddr> addressSet;
    4614           0 :         nsresult rv = NS_ERROR_NOT_AVAILABLE;
    4615           0 :         if (dnsRecord) {
    4616           0 :             rv = dnsRecord->GetAddresses(addressSet);
    4617             :         }
    4618             : 
    4619           0 :         if (NS_SUCCEEDED(rv) && !addressSet.IsEmpty()) {
    4620           0 :             for (uint32_t i = 0; i < addressSet.Length(); ++i) {
    4621           0 :                 nsCString *newKey = mEnt->mCoalescingKeys.AppendElement(nsCString());
    4622           0 :                 newKey->SetCapacity(kIPv6CStrBufSize + 26);
    4623           0 :                 NetAddrToString(&addressSet[i], newKey->BeginWriting(), kIPv6CStrBufSize);
    4624           0 :                 newKey->SetLength(strlen(newKey->BeginReading()));
    4625           0 :                 if (mEnt->mConnInfo->GetAnonymous()) {
    4626           0 :                     newKey->AppendLiteral("~A:");
    4627             :                 } else {
    4628           0 :                     newKey->AppendLiteral("~.:");
    4629             :                 }
    4630           0 :                 newKey->AppendInt(mEnt->mConnInfo->OriginPort());
    4631           0 :                 newKey->AppendLiteral("/[");
    4632           0 :                 nsAutoCString suffix;
    4633           0 :                 mEnt->mConnInfo->GetOriginAttributes().CreateSuffix(suffix);
    4634           0 :                 newKey->Append(suffix);
    4635           0 :                 newKey->AppendLiteral("]viaDNS");
    4636           0 :                 LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus "
    4637             :                      "STATUS_CONNECTING_TO Established New Coalescing Key # %d for host "
    4638             :                      "%s [%s]", i, mEnt->mConnInfo->Origin(), newKey->get()));
    4639             :             }
    4640           0 :             gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(mEnt);
    4641             :         }
    4642             :     }
    4643             : 
    4644          10 :     switch (status) {
    4645             :     case NS_NET_STATUS_CONNECTING_TO:
    4646             :         // Passed DNS resolution, now trying to connect, start the backup timer
    4647             :         // only prevent creating another backup transport.
    4648             :         // We also check for mEnt presence to not instantiate the timer after
    4649             :         // this half open socket has already been abandoned.  It may happen
    4650             :         // when we get this notification right between main-thread calls to
    4651             :         // nsHttpConnectionMgr::Shutdown and nsSocketTransportService::Shutdown
    4652             :         // where the first abandons all half open socket instances and only
    4653             :         // after that the second stops the socket thread.
    4654           3 :         if (mEnt && !mBackupTransport && !mSynTimer)
    4655           3 :             SetupBackupTimer();
    4656           3 :         break;
    4657             : 
    4658             :     case NS_NET_STATUS_CONNECTED_TO:
    4659             :         // TCP connection's up, now transfer or SSL negotiantion starts,
    4660             :         // no need for backup socket
    4661           1 :         CancelBackupTimer();
    4662           1 :         break;
    4663             : 
    4664             :     default:
    4665           6 :         break;
    4666             :     }
    4667             : 
    4668          10 :     return NS_OK;
    4669             : }
    4670             : 
    4671             : // method for nsIInterfaceRequestor
    4672             : NS_IMETHODIMP
    4673           0 : nsHttpConnectionMgr::nsHalfOpenSocket::GetInterface(const nsIID &iid,
    4674             :                                                     void **result)
    4675             : {
    4676           0 :     if (mTransaction) {
    4677           0 :         nsCOMPtr<nsIInterfaceRequestor> callbacks;
    4678           0 :         mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
    4679           0 :         if (callbacks)
    4680           0 :             return callbacks->GetInterface(iid, result);
    4681             :     }
    4682           0 :     return NS_ERROR_NO_INTERFACE;
    4683             : }
    4684             : 
    4685             : bool
    4686           6 : nsHttpConnectionMgr::nsHalfOpenSocket::Claim()
    4687             : {
    4688           6 :     if (mSpeculative) {
    4689           0 :         mSpeculative = false;
    4690             :         uint32_t flags;
    4691           0 :         if (mSocketTransport && NS_SUCCEEDED(mSocketTransport->GetConnectionFlags(&flags))) {
    4692           0 :             flags &= ~nsISocketTransport::DISABLE_RFC1918;
    4693           0 :             mSocketTransport->SetConnectionFlags(flags);
    4694             :         }
    4695             : 
    4696           0 :         Telemetry::AutoCounter<Telemetry::HTTPCONNMGR_USED_SPECULATIVE_CONN> usedSpeculativeConn;
    4697           0 :         ++usedSpeculativeConn;
    4698             : 
    4699           0 :         if (mIsFromPredictor) {
    4700           0 :             Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRECONNECTS_USED> totalPreconnectsUsed;
    4701           0 :             ++totalPreconnectsUsed;
    4702             :         }
    4703             : 
    4704           0 :         if ((mPrimaryStreamStatus == NS_NET_STATUS_CONNECTING_TO) &&
    4705           0 :             mEnt && !mBackupTransport && !mSynTimer) {
    4706           0 :             SetupBackupTimer();
    4707             :         }
    4708             :     }
    4709             : 
    4710           6 :     if (mFreeToUse) {
    4711           3 :         mFreeToUse = false;
    4712           3 :         return true;
    4713             :     }
    4714           3 :     return false;
    4715             : }
    4716             : 
    4717             : void
    4718           0 : nsHttpConnectionMgr::nsHalfOpenSocket::Unclaim()
    4719             : {
    4720           0 :     MOZ_ASSERT(!mSpeculative && !mFreeToUse);
    4721             :     // We will keep the backup-timer running. Most probably this halfOpen will
    4722             :     // be used by a transaction from which this transaction took the halfOpen.
    4723             :     // (this is happening because of the transaction priority.)
    4724           0 :     mFreeToUse = true;
    4725           0 : }
    4726             : 
    4727             : already_AddRefed<nsHttpConnection>
    4728           0 : ConnectionHandle::TakeHttpConnection()
    4729             : {
    4730             :     // return our connection object to the caller and clear it internally
    4731             :     // do not drop our reference - the caller now owns it.
    4732           0 :     MOZ_ASSERT(mConn);
    4733           0 :     return mConn.forget();
    4734             : }
    4735             : 
    4736             : already_AddRefed<nsHttpConnection>
    4737           0 : ConnectionHandle::HttpConnection()
    4738             : {
    4739           0 :     RefPtr<nsHttpConnection> rv(mConn);
    4740           0 :     return rv.forget();
    4741             : }
    4742             : 
    4743             : // nsConnectionEntry
    4744             : 
    4745           2 : nsHttpConnectionMgr::
    4746           2 : nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci)
    4747             :     : mConnInfo(ci)
    4748             :     , mUsingSpdy(false)
    4749             :     , mPreferIPv4(false)
    4750             :     , mPreferIPv6(false)
    4751             :     , mUsedForConnection(false)
    4752           2 :     , mDoNotDestroy(false)
    4753             : {
    4754           2 :     MOZ_COUNT_CTOR(nsConnectionEntry);
    4755           2 :     mUseFastOpen = gHttpHandler->UseFastOpen();
    4756             : 
    4757           2 :     LOG(("nsConnectionEntry::nsConnectionEntry this=%p key=%s",
    4758             :          this, ci->HashKey().get()));
    4759           2 : }
    4760             : 
    4761             : bool
    4762           9 : nsHttpConnectionMgr::nsConnectionEntry::AvailableForDispatchNow()
    4763             : {
    4764           9 :     if (mIdleConns.Length() && mIdleConns[0]->CanReuse()) {
    4765           0 :         return true;
    4766             :     }
    4767             : 
    4768             :     return gHttpHandler->ConnMgr()->
    4769           9 :         GetSpdyActiveConn(this) ? true : false;
    4770             : }
    4771             : 
    4772             : bool
    4773           0 : nsHttpConnectionMgr::GetConnectionData(nsTArray<HttpRetParams> *aArg)
    4774             : {
    4775           0 :     for (auto iter = mCT.Iter(); !iter.Done(); iter.Next()) {
    4776           0 :         nsAutoPtr<nsConnectionEntry>& ent = iter.Data();
    4777             : 
    4778           0 :         if (ent->mConnInfo->GetPrivate()) {
    4779           0 :             continue;
    4780             :         }
    4781             : 
    4782           0 :         HttpRetParams data;
    4783           0 :         data.host = ent->mConnInfo->Origin();
    4784           0 :         data.port = ent->mConnInfo->OriginPort();
    4785           0 :         for (uint32_t i = 0; i < ent->mActiveConns.Length(); i++) {
    4786           0 :             HttpConnInfo info;
    4787           0 :             info.ttl = ent->mActiveConns[i]->TimeToLive();
    4788           0 :             info.rtt = ent->mActiveConns[i]->Rtt();
    4789           0 :             if (ent->mActiveConns[i]->UsingSpdy()) {
    4790           0 :                 info.SetHTTP2ProtocolVersion(
    4791           0 :                     ent->mActiveConns[i]->GetSpdyVersion());
    4792             :             } else {
    4793           0 :                 info.SetHTTP1ProtocolVersion(
    4794           0 :                     ent->mActiveConns[i]->GetLastHttpResponseVersion());
    4795             :             }
    4796           0 :             data.active.AppendElement(info);
    4797             :         }
    4798           0 :         for (uint32_t i = 0; i < ent->mIdleConns.Length(); i++) {
    4799           0 :             HttpConnInfo info;
    4800           0 :             info.ttl = ent->mIdleConns[i]->TimeToLive();
    4801           0 :             info.rtt = ent->mIdleConns[i]->Rtt();
    4802           0 :             info.SetHTTP1ProtocolVersion(
    4803           0 :                 ent->mIdleConns[i]->GetLastHttpResponseVersion());
    4804           0 :             data.idle.AppendElement(info);
    4805             :         }
    4806           0 :         for (uint32_t i = 0; i < ent->mHalfOpens.Length(); i++) {
    4807             :             HalfOpenSockets hSocket;
    4808           0 :             hSocket.speculative = ent->mHalfOpens[i]->IsSpeculative();
    4809           0 :             data.halfOpens.AppendElement(hSocket);
    4810             :         }
    4811           0 :         data.spdy = ent->mUsingSpdy;
    4812           0 :         data.ssl = ent->mConnInfo->EndToEndSSL();
    4813           0 :         aArg->AppendElement(data);
    4814             :     }
    4815             : 
    4816           0 :     return true;
    4817             : }
    4818             : 
    4819             : void
    4820           0 : nsHttpConnectionMgr::ResetIPFamilyPreference(nsHttpConnectionInfo *ci)
    4821             : {
    4822           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    4823           0 :     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
    4824           0 :     if (ent) {
    4825           0 :         ent->ResetIPFamilyPreference();
    4826             :     }
    4827           0 : }
    4828             : 
    4829             : uint32_t
    4830           6 : nsHttpConnectionMgr::
    4831             : nsConnectionEntry::UnconnectedHalfOpens()
    4832             : {
    4833           6 :     uint32_t unconnectedHalfOpens = 0;
    4834           6 :     for (uint32_t i = 0; i < mHalfOpens.Length(); ++i) {
    4835           0 :         if (!mHalfOpens[i]->HasConnected())
    4836           0 :             ++unconnectedHalfOpens;
    4837             :     }
    4838           6 :     return unconnectedHalfOpens;
    4839             : }
    4840             : 
    4841             : void
    4842           3 : nsHttpConnectionMgr::
    4843             : nsConnectionEntry::RemoveHalfOpen(nsHalfOpenSocket *halfOpen)
    4844             : {
    4845             :     // A failure to create the transport object at all
    4846             :     // will result in it not being present in the halfopen table. That's expected.
    4847           3 :     if (mHalfOpens.RemoveElement(halfOpen)) {
    4848             : 
    4849           1 :         if (halfOpen->IsSpeculative()) {
    4850           0 :             Telemetry::AutoCounter<Telemetry::HTTPCONNMGR_UNUSED_SPECULATIVE_CONN> unusedSpeculativeConn;
    4851           0 :             ++unusedSpeculativeConn;
    4852             : 
    4853           0 :             if (halfOpen->IsFromPredictor()) {
    4854           0 :                 Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRECONNECTS_UNUSED> totalPreconnectsUnused;
    4855           0 :                 ++totalPreconnectsUnused;
    4856             :             }
    4857             :         }
    4858             : 
    4859           1 :         MOZ_ASSERT(gHttpHandler->ConnMgr()->mNumHalfOpenConns);
    4860           1 :         if (gHttpHandler->ConnMgr()->mNumHalfOpenConns) { // just in case
    4861           1 :             gHttpHandler->ConnMgr()->mNumHalfOpenConns--;
    4862             :         }
    4863             :     } else {
    4864           2 :         mHalfOpenFastOpenBackups.RemoveElement(halfOpen);
    4865             :     }
    4866             : 
    4867           3 :     if (!UnconnectedHalfOpens()) {
    4868             :         // perhaps this reverted RestrictConnections()
    4869             :         // use the PostEvent version of processpendingq to avoid
    4870             :         // altering the pending q vector from an arbitrary stack
    4871           3 :         nsresult rv = gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo);
    4872           3 :         if (NS_FAILED(rv)) {
    4873           0 :             LOG(("nsHttpConnectionMgr::nsConnectionEntry::RemoveHalfOpen\n"
    4874             :                  "    failed to process pending queue\n"));
    4875             :         }
    4876             :     }
    4877           3 : }
    4878             : 
    4879             : void
    4880           3 : nsHttpConnectionMgr::
    4881             : nsConnectionEntry::RecordIPFamilyPreference(uint16_t family)
    4882             : {
    4883           3 :   if (family == PR_AF_INET && !mPreferIPv6)
    4884           3 :     mPreferIPv4 = true;
    4885             : 
    4886           3 :   if (family == PR_AF_INET6 && !mPreferIPv4)
    4887           0 :     mPreferIPv6 = true;
    4888           3 : }
    4889             : 
    4890             : void
    4891           0 : nsHttpConnectionMgr::
    4892             : nsConnectionEntry::ResetIPFamilyPreference()
    4893             : {
    4894           0 :   mPreferIPv4 = false;
    4895           0 :   mPreferIPv6 = false;
    4896           0 : }
    4897             : 
    4898             : size_t
    4899          16 : nsHttpConnectionMgr::nsConnectionEntry::PendingQLength() const
    4900             : {
    4901          16 :   size_t length = 0;
    4902          26 :   for (auto it = mPendingTransactionTable.ConstIter(); !it.Done(); it.Next()) {
    4903          10 :     length += it.UserData()->Length();
    4904             :   }
    4905             : 
    4906          16 :   return length;
    4907             : }
    4908             : 
    4909             : void
    4910           2 : nsHttpConnectionMgr::
    4911             : nsConnectionEntry::InsertTransaction(PendingTransactionInfo *info,
    4912             :                                      bool aInsertAsFirstForTheSamePriority /*= false*/)
    4913             : {
    4914           2 :   LOG(("nsHttpConnectionMgr::nsConnectionEntry::InsertTransaction"
    4915             :        " trans=%p, windowId=%" PRIu64 "\n",
    4916             :        info->mTransaction.get(),
    4917             :        info->mTransaction->TopLevelOuterContentWindowId()));
    4918             : 
    4919           2 :   uint64_t windowId = info->mTransaction->TopLevelOuterContentWindowId();
    4920             :   nsTArray<RefPtr<PendingTransactionInfo>> *infoArray;
    4921           2 :   if (!mPendingTransactionTable.Get(windowId, &infoArray)) {
    4922           2 :     infoArray = new nsTArray<RefPtr<PendingTransactionInfo>>();
    4923           2 :     mPendingTransactionTable.Put(windowId, infoArray);
    4924             :   }
    4925             : 
    4926           2 :   gHttpHandler->ConnMgr()->InsertTransactionSorted(*infoArray, info,
    4927           2 :                                                    aInsertAsFirstForTheSamePriority);
    4928           2 : }
    4929             : 
    4930             : void
    4931           0 : nsHttpConnectionMgr::
    4932             : nsConnectionEntry::AppendPendingQForFocusedWindow(
    4933             :     uint64_t windowId,
    4934             :     nsTArray<RefPtr<PendingTransactionInfo>> &result,
    4935             :     uint32_t maxCount)
    4936             : {
    4937           0 :     nsTArray<RefPtr<PendingTransactionInfo>> *infoArray = nullptr;
    4938           0 :     if (!mPendingTransactionTable.Get(windowId, &infoArray)) {
    4939           0 :         result.Clear();
    4940           0 :         return;
    4941             :     }
    4942             : 
    4943           0 :     uint32_t countToAppend = maxCount;
    4944           0 :     countToAppend =
    4945           0 :         countToAppend > infoArray->Length() || countToAppend == 0 ?
    4946           0 :             infoArray->Length() :
    4947           0 :             countToAppend;
    4948             : 
    4949           0 :     result.InsertElementsAt(result.Length(),
    4950           0 :                             infoArray->Elements(),
    4951           0 :                             countToAppend);
    4952           0 :     infoArray->RemoveElementsAt(0, countToAppend);
    4953             : 
    4954           0 :     LOG(("nsConnectionEntry::AppendPendingQForFocusedWindow [ci=%s], "
    4955             :          "pendingQ count=%" PRIuSIZE " window.count=%" PRIuSIZE " for focused window (id=%" PRIu64 ")\n",
    4956             :          mConnInfo->HashKey().get(), result.Length(), infoArray->Length(),
    4957             :          windowId));
    4958             : }
    4959             : 
    4960             : void
    4961           0 : nsHttpConnectionMgr::
    4962             : nsConnectionEntry::AppendPendingQForNonFocusedWindows(
    4963             :     uint64_t windowId,
    4964             :     nsTArray<RefPtr<PendingTransactionInfo>> &result,
    4965             :     uint32_t maxCount)
    4966             : {
    4967             :     // XXX Adjust the order of transactions in a smarter manner.
    4968           0 :     uint32_t totalCount = 0;
    4969           0 :     for (auto it = mPendingTransactionTable.Iter(); !it.Done(); it.Next()) {
    4970           0 :         if (windowId && it.Key() == windowId) {
    4971           0 :             continue;
    4972             :         }
    4973             : 
    4974           0 :         uint32_t count = 0;
    4975           0 :         for (; count < it.UserData()->Length(); ++count) {
    4976           0 :             if (maxCount && totalCount == maxCount) {
    4977           0 :                 break;
    4978             :             }
    4979             : 
    4980             :             // Because elements in |result| could come from multiple penndingQ,
    4981             :             // call |InsertTransactionSorted| to make sure the order is correct.
    4982           0 :             gHttpHandler->ConnMgr()->InsertTransactionSorted(
    4983             :                 result,
    4984           0 :                 it.UserData()->ElementAt(count));
    4985           0 :             ++totalCount;
    4986             :         }
    4987           0 :         it.UserData()->RemoveElementsAt(0, count);
    4988             : 
    4989           0 :         if (maxCount && totalCount == maxCount) {
    4990           0 :             break;
    4991             :         }
    4992             :     }
    4993             : 
    4994           0 :     LOG(("nsConnectionEntry::AppendPendingQForNonFocusedWindows [ci=%s], "
    4995             :          "pendingQ count=%" PRIuSIZE " for non focused window\n",
    4996             :          mConnInfo->HashKey().get(), result.Length()));
    4997           0 : }
    4998             : 
    4999             : void
    5000           0 : nsHttpConnectionMgr::nsConnectionEntry::RemoveEmptyPendingQ()
    5001             : {
    5002           0 :     for (auto it = mPendingTransactionTable.Iter(); !it.Done(); it.Next()) {
    5003           0 :         if (it.UserData()->IsEmpty()) {
    5004           0 :             it.Remove();
    5005             :         }
    5006             :     }
    5007           0 : }
    5008             : 
    5009             : void
    5010           0 : nsHttpConnectionMgr::MoveToWildCardConnEntry(nsHttpConnectionInfo *specificCI,
    5011             :                                              nsHttpConnectionInfo *wildCardCI,
    5012             :                                              nsHttpConnection *proxyConn)
    5013             : {
    5014           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    5015           0 :     MOZ_ASSERT(specificCI->UsingHttpsProxy());
    5016             : 
    5017           0 :     LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard conn %p has requested to "
    5018             :          "change CI from %s to %s\n", proxyConn, specificCI->HashKey().get(),
    5019             :          wildCardCI->HashKey().get()));
    5020             : 
    5021           0 :     nsConnectionEntry *ent = mCT.Get(specificCI->HashKey());
    5022           0 :     LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard conn %p using ent %p (spdy %d)\n",
    5023             :          proxyConn, ent, ent ? ent->mUsingSpdy : 0));
    5024             : 
    5025           0 :     if (!ent || !ent->mUsingSpdy) {
    5026           0 :         return;
    5027             :     }
    5028             : 
    5029           0 :     nsConnectionEntry *wcEnt = GetOrCreateConnectionEntry(wildCardCI, true);
    5030           0 :     if (wcEnt == ent) {
    5031             :         // nothing to do!
    5032           0 :         return;
    5033             :     }
    5034           0 :     wcEnt->mUsingSpdy = true;
    5035             : 
    5036           0 :     LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard ent %p "
    5037             :          "idle=%" PRIuSIZE " active=%" PRIuSIZE " half=%" PRIuSIZE " pending=%" PRIuSIZE "\n",
    5038             :          ent, ent->mIdleConns.Length(), ent->mActiveConns.Length(),
    5039             :          ent->mHalfOpens.Length(), ent->PendingQLength()));
    5040             : 
    5041           0 :     LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard wc-ent %p "
    5042             :          "idle=%" PRIuSIZE " active=%" PRIuSIZE " half=%" PRIuSIZE " pending=%" PRIuSIZE "\n",
    5043             :          wcEnt, wcEnt->mIdleConns.Length(), wcEnt->mActiveConns.Length(),
    5044             :          wcEnt->mHalfOpens.Length(), wcEnt->PendingQLength()));
    5045             : 
    5046           0 :     int32_t count = ent->mActiveConns.Length();
    5047           0 :     RefPtr<nsHttpConnection> deleteProtector(proxyConn);
    5048           0 :     for (int32_t i = 0; i < count; ++i) {
    5049           0 :         if (ent->mActiveConns[i] == proxyConn) {
    5050           0 :             ent->mActiveConns.RemoveElementAt(i);
    5051           0 :             wcEnt->mActiveConns.InsertElementAt(0, proxyConn);
    5052           0 :             return;
    5053             :         }
    5054             :     }
    5055             : 
    5056           0 :     count = ent->mIdleConns.Length();
    5057           0 :     for (int32_t i = 0; i < count; ++i) {
    5058           0 :         if (ent->mIdleConns[i] == proxyConn) {
    5059           0 :             ent->mIdleConns.RemoveElementAt(i);
    5060           0 :             wcEnt->mIdleConns.InsertElementAt(0, proxyConn);
    5061           0 :             return;
    5062             :         }
    5063             :     }
    5064             : }
    5065             : 
    5066             : } // namespace net
    5067             : } // namespace mozilla

Generated by: LCOV version 1.13