netwerk/protocol/http/nsHttpConnectionMgr.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     6 // HttpLog.h should generally be included first
     7 #include "HttpLog.h"
     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()
    15 #include "nsHttpConnectionMgr.h"
    16 #include "nsHttpConnection.h"
    17 #include "nsHttpPipeline.h"
    18 #include "nsHttpHandler.h"
    19 #include "nsIHttpChannelInternal.h"
    20 #include "nsNetCID.h"
    21 #include "nsCOMPtr.h"
    22 #include "nsNetUtil.h"
    23 #include "mozilla/net/DNS.h"
    24 #include "nsISocketTransport.h"
    25 #include "nsISSLSocketControl.h"
    26 #include "mozilla/Telemetry.h"
    27 #include "mozilla/net/DashboardTypes.h"
    28 #include "NullHttpTransaction.h"
    29 #include "nsITransport.h"
    30 #include "nsISocketTransportService.h"
    31 #include <algorithm>
    32 #include "Http2Compression.h"
    33 #include "mozilla/ChaosMode.h"
    34 #include "mozilla/unused.h"
    35 #include <stdlib.h>
    36 #include "nsHttpRequestHead.h"
    38 // defined by the socket transport service while active
    39 extern PRThread *gSocketThread;
    41 namespace mozilla {
    42 namespace net {
    44 //-----------------------------------------------------------------------------
    46 NS_IMPL_ISUPPORTS(nsHttpConnectionMgr, nsIObserver)
    48 static void
    49 InsertTransactionSorted(nsTArray<nsHttpTransaction*> &pendingQ, nsHttpTransaction *trans)
    50 {
    51     // insert into queue with smallest valued number first.  search in reverse
    52     // order under the assumption that many of the existing transactions will
    53     // have the same priority (usually 0).
    54     uint32_t len = pendingQ.Length();
    56     if (pendingQ.IsEmpty()) {
    57         pendingQ.InsertElementAt(0, trans);
    58         return;
    59     }
    61     pendingQ.InsertElementAt(0, trans);
    63     // FIXME: Refactor into standalone helper (for nsHttpPipeline)
    64     // Or at least simplify this function if this shuffle ends up
    65     // being an improvement.
    66     uint32_t i = 0;
    67     for (i=0; i < len; ++i) {
    68         uint32_t ridx = rand() % len;
    70         nsHttpTransaction *tmp = pendingQ[i];
    71         pendingQ[i] = pendingQ[ridx];
    72         pendingQ[ridx] = tmp;
    73     }
    74 }
    76 //-----------------------------------------------------------------------------
    78 nsHttpConnectionMgr::nsHttpConnectionMgr()
    79     : mReentrantMonitor("nsHttpConnectionMgr.mReentrantMonitor")
    80     , mMaxConns(0)
    81     , mMaxPersistConnsPerHost(0)
    82     , mMaxPersistConnsPerProxy(0)
    83     , mIsShuttingDown(false)
    84     , mNumActiveConns(0)
    85     , mNumIdleConns(0)
    86     , mNumSpdyActiveConns(0)
    87     , mNumHalfOpenConns(0)
    88     , mTimeOfNextWakeUp(UINT64_MAX)
    89     , mTimeoutTickArmed(false)
    90     , mTimeoutTickNext(1)
    91 {
    92     LOG(("Creating nsHttpConnectionMgr @%x\n", this));
    93 }
    95 nsHttpConnectionMgr::~nsHttpConnectionMgr()
    96 {
    97     LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
    98     if (mTimeoutTick)
    99         mTimeoutTick->Cancel();
   100 }
   102 nsresult
   103 nsHttpConnectionMgr::EnsureSocketThreadTarget()
   104 {
   105     nsresult rv;
   106     nsCOMPtr<nsIEventTarget> sts;
   107     nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   108     if (NS_SUCCEEDED(rv))
   109         sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
   111     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   113     // do nothing if already initialized or if we've shut down
   114     if (mSocketThreadTarget || mIsShuttingDown)
   115         return NS_OK;
   117     mSocketThreadTarget = sts;
   119     return rv;
   120 }
   122 nsresult
   123 nsHttpConnectionMgr::Init(uint16_t maxConns,
   124                           uint16_t maxPersistConnsPerHost,
   125                           uint16_t maxPersistConnsPerProxy,
   126                           uint16_t maxRequestDelay,
   127                           uint16_t maxPipelinedRequests,
   128                           uint16_t maxOptimisticPipelinedRequests)
   129 {
   130     LOG(("nsHttpConnectionMgr::Init\n"));
   132     {
   133         ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   135         mMaxConns = maxConns;
   136         mMaxPersistConnsPerHost = maxPersistConnsPerHost;
   137         mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
   138         mMaxRequestDelay = maxRequestDelay;
   139         mMaxPipelinedRequests = maxPipelinedRequests;
   140         mMaxOptimisticPipelinedRequests = maxOptimisticPipelinedRequests;
   142         mIsShuttingDown = false;
   143     }
   145     return EnsureSocketThreadTarget();
   146 }
   148 nsresult
   149 nsHttpConnectionMgr::Shutdown()
   150 {
   151     LOG(("nsHttpConnectionMgr::Shutdown\n"));
   153     bool shutdown = false;
   154     {
   155         ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   157         // do nothing if already shutdown
   158         if (!mSocketThreadTarget)
   159             return NS_OK;
   161         nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgShutdown,
   162                                 0, &shutdown);
   164         // release our reference to the STS to prevent further events
   165         // from being posted.  this is how we indicate that we are
   166         // shutting down.
   167         mIsShuttingDown = true;
   168         mSocketThreadTarget = 0;
   170         if (NS_FAILED(rv)) {
   171             NS_WARNING("unable to post SHUTDOWN message");
   172             return rv;
   173         }
   174     }
   176     // wait for shutdown event to complete
   177     while (!shutdown)
   178         NS_ProcessNextEvent(NS_GetCurrentThread());
   179     Http2CompressionCleanup();
   181     return NS_OK;
   182 }
   184 nsresult
   185 nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, int32_t iparam, void *vparam)
   186 {
   187     EnsureSocketThreadTarget();
   189     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   191     nsresult rv;
   192     if (!mSocketThreadTarget) {
   193         NS_WARNING("cannot post event if not initialized");
   194         rv = NS_ERROR_NOT_INITIALIZED;
   195     }
   196     else {
   197         nsRefPtr<nsIRunnable> event = new nsConnEvent(this, handler, iparam, vparam);
   198         rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
   199     }
   200     return rv;
   201 }
   203 void
   204 nsHttpConnectionMgr::PruneDeadConnectionsAfter(uint32_t timeInSeconds)
   205 {
   206     LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));
   208     if(!mTimer)
   209         mTimer = do_CreateInstance("@mozilla.org/timer;1");
   211     // failure to create a timer is not a fatal error, but idle connections
   212     // will not be cleaned up until we try to use them.
   213     if (mTimer) {
   214         mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
   215         mTimer->Init(this, timeInSeconds*1000, nsITimer::TYPE_ONE_SHOT);
   216     } else {
   217         NS_WARNING("failed to create: timer for pruning the dead connections!");
   218     }
   219 }
   221 void
   222 nsHttpConnectionMgr::ConditionallyStopPruneDeadConnectionsTimer()
   223 {
   224     // Leave the timer in place if there are connections that potentially
   225     // need management
   226     if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
   227         return;
   229     LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));
   231     // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
   232     mTimeOfNextWakeUp = UINT64_MAX;
   233     if (mTimer) {
   234         mTimer->Cancel();
   235         mTimer = nullptr;
   236     }
   237 }
   239 void
   240 nsHttpConnectionMgr::ConditionallyStopTimeoutTick()
   241 {
   242     LOG(("nsHttpConnectionMgr::ConditionallyStopTimeoutTick "
   243          "armed=%d active=%d\n", mTimeoutTickArmed, mNumActiveConns));
   245     if (!mTimeoutTickArmed)
   246         return;
   248     if (mNumActiveConns)
   249         return;
   251     LOG(("nsHttpConnectionMgr::ConditionallyStopTimeoutTick stop==true\n"));
   253     mTimeoutTick->Cancel();
   254     mTimeoutTickArmed = false;
   255 }
   257 //-----------------------------------------------------------------------------
   258 // nsHttpConnectionMgr::nsIObserver
   259 //-----------------------------------------------------------------------------
   261 NS_IMETHODIMP
   262 nsHttpConnectionMgr::Observe(nsISupports *subject,
   263                              const char *topic,
   264                              const char16_t *data)
   265 {
   266     LOG(("nsHttpConnectionMgr::Observe [topic=\"%s\"]\n", topic));
   268     if (0 == strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
   269         nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
   270         if (timer == mTimer) {
   271             PruneDeadConnections();
   272         }
   273         else if (timer == mTimeoutTick) {
   274             TimeoutTick();
   275         }
   276         else {
   277             MOZ_ASSERT(false, "unexpected timer-callback");
   278             LOG(("Unexpected timer object\n"));
   279             return NS_ERROR_UNEXPECTED;
   280         }
   281     }
   283     return NS_OK;
   284 }
   287 //-----------------------------------------------------------------------------
   289 nsresult
   290 nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, int32_t priority)
   291 {
   292     LOG(("nsHttpConnectionMgr::AddTransaction [trans=%x %d]\n", trans, priority));
   294     NS_ADDREF(trans);
   295     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
   296     if (NS_FAILED(rv))
   297         NS_RELEASE(trans);
   298     return rv;
   299 }
   301 nsresult
   302 nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, int32_t priority)
   303 {
   304     LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%x %d]\n", trans, priority));
   306     NS_ADDREF(trans);
   307     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority, trans);
   308     if (NS_FAILED(rv))
   309         NS_RELEASE(trans);
   310     return rv;
   311 }
   313 nsresult
   314 nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
   315 {
   316     LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%x reason=%x]\n", trans, reason));
   318     NS_ADDREF(trans);
   319     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction,
   320                             static_cast<int32_t>(reason), trans);
   321     if (NS_FAILED(rv))
   322         NS_RELEASE(trans);
   323     return rv;
   324 }
   326 nsresult
   327 nsHttpConnectionMgr::PruneDeadConnections()
   328 {
   329     return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
   330 }
   332 nsresult
   333 nsHttpConnectionMgr::DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *aCI)
   334 {
   335     nsRefPtr<nsHttpConnectionInfo> connInfo(aCI);
   337     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup,
   338                             0, connInfo);
   339     if (NS_SUCCEEDED(rv))
   340         unused << connInfo.forget();
   341     return rv;
   342 }
   344 class SpeculativeConnectArgs
   345 {
   346 public:
   347     SpeculativeConnectArgs() { mOverridesOK = false; }
   348     virtual ~SpeculativeConnectArgs() {}
   350     // Added manually so we can use nsRefPtr without inheriting from
   351     // nsISupports
   352     NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
   353     NS_IMETHOD_(MozExternalRefCountType) Release(void);
   355 public: // intentional!
   356     nsRefPtr<NullHttpTransaction> mTrans;
   358     bool mOverridesOK;
   359     uint32_t mParallelSpeculativeConnectLimit;
   360     bool mIgnoreIdle;
   361     bool mIgnorePossibleSpdyConnections;
   363     // As above, added manually so we can use nsRefPtr without inheriting from
   364     // nsISupports
   365 protected:
   366     ThreadSafeAutoRefCnt mRefCnt;
   367     NS_DECL_OWNINGTHREAD
   368 };
   370 NS_IMPL_ADDREF(SpeculativeConnectArgs)
   371 NS_IMPL_RELEASE(SpeculativeConnectArgs)
   373 nsresult
   374 nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
   375                                         nsIInterfaceRequestor *callbacks,
   376                                         uint32_t caps)
   377 {
   378     MOZ_ASSERT(NS_IsMainThread(), "nsHttpConnectionMgr::SpeculativeConnect called off main thread!");
   380     LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n",
   381          ci->HashKey().get()));
   383     // Hosts that are Local IP Literals should not be speculatively
   384     // connected - Bug 853423.
   385     if (ci && ci->HostIsLocalIPLiteral()) {
   386         LOG(("nsHttpConnectionMgr::SpeculativeConnect skipping RFC1918 "
   387              "address [%s]", ci->Host()));
   388         return NS_OK;
   389     }
   391     nsRefPtr<SpeculativeConnectArgs> args = new SpeculativeConnectArgs();
   393     // Wrap up the callbacks and the target to ensure they're released on the target
   394     // thread properly.
   395     nsCOMPtr<nsIInterfaceRequestor> wrappedCallbacks;
   396     NS_NewInterfaceRequestorAggregation(callbacks, nullptr, getter_AddRefs(wrappedCallbacks));
   398     caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
   399     args->mTrans = new NullHttpTransaction(ci, wrappedCallbacks, caps);
   401     nsCOMPtr<nsISpeculativeConnectionOverrider> overrider =
   402         do_GetInterface(callbacks);
   403     if (overrider) {
   404         args->mOverridesOK = true;
   405         overrider->GetParallelSpeculativeConnectLimit(
   406             &args->mParallelSpeculativeConnectLimit);
   407         overrider->GetIgnoreIdle(&args->mIgnoreIdle);
   408         overrider->GetIgnorePossibleSpdyConnections(
   409             &args->mIgnorePossibleSpdyConnections);
   410     }
   412     nsresult rv =
   413         PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, args);
   414     if (NS_SUCCEEDED(rv))
   415         unused << args.forget();
   416     return rv;
   417 }
   419 nsresult
   420 nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget **target)
   421 {
   422     EnsureSocketThreadTarget();
   424     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   425     NS_IF_ADDREF(*target = mSocketThreadTarget);
   426     return NS_OK;
   427 }
   429 nsresult
   430 nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
   431 {
   432     LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
   434     NS_ADDREF(conn);
   435     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
   436     if (NS_FAILED(rv))
   437         NS_RELEASE(conn);
   438     return rv;
   439 }
   441 // A structure used to marshall 2 pointers across the various necessary
   442 // threads to complete an HTTP upgrade.
   443 class nsCompleteUpgradeData
   444 {
   445 public:
   446 nsCompleteUpgradeData(nsAHttpConnection *aConn,
   447                       nsIHttpUpgradeListener *aListener)
   448     : mConn(aConn), mUpgradeListener(aListener) {}
   450     nsRefPtr<nsAHttpConnection> mConn;
   451     nsCOMPtr<nsIHttpUpgradeListener> mUpgradeListener;
   452 };
   454 nsresult
   455 nsHttpConnectionMgr::CompleteUpgrade(nsAHttpConnection *aConn,
   456                                      nsIHttpUpgradeListener *aUpgradeListener)
   457 {
   458     nsCompleteUpgradeData *data =
   459         new nsCompleteUpgradeData(aConn, aUpgradeListener);
   460     nsresult rv;
   461     rv = PostEvent(&nsHttpConnectionMgr::OnMsgCompleteUpgrade, 0, data);
   462     if (NS_FAILED(rv))
   463         delete data;
   464     return rv;
   465 }
   467 nsresult
   468 nsHttpConnectionMgr::UpdateParam(nsParamName name, uint16_t value)
   469 {
   470     uint32_t param = (uint32_t(name) << 16) | uint32_t(value);
   471     return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam, 0,
   472                      (void *)(uintptr_t) param);
   473 }
   475 nsresult
   476 nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
   477 {
   478     LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
   480     NS_ADDREF(ci);
   481     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
   482     if (NS_FAILED(rv))
   483         NS_RELEASE(ci);
   484     return rv;
   485 }
   487 nsresult
   488 nsHttpConnectionMgr::ProcessPendingQ()
   489 {
   490     LOG(("nsHttpConnectionMgr::ProcessPendingQ [All CI]\n"));
   491     return PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, nullptr);
   492 }
   494 void
   495 nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket(int32_t, void *param)
   496 {
   497     nsRefPtr<EventTokenBucket> tokenBucket =
   498         dont_AddRef(static_cast<EventTokenBucket *>(param));
   499     gHttpHandler->SetRequestTokenBucket(tokenBucket);
   500 }
   502 nsresult
   503 nsHttpConnectionMgr::UpdateRequestTokenBucket(EventTokenBucket *aBucket)
   504 {
   505     nsRefPtr<EventTokenBucket> bucket(aBucket);
   507     // Call From main thread when a new EventTokenBucket has been made in order
   508     // to post the new value to the socket thread.
   509     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket,
   510                             0, bucket);
   511     if (NS_SUCCEEDED(rv))
   512         unused << bucket.forget();
   513     return rv;
   514 }
   516 // Given a nsHttpConnectionInfo find the connection entry object that
   517 // contains either the nshttpconnection or nshttptransaction parameter.
   518 // Normally this is done by the hashkey lookup of connectioninfo,
   519 // but if spdy coalescing is in play it might be found in a redirected
   520 // entry
   521 nsHttpConnectionMgr::nsConnectionEntry *
   522 nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
   523                                            nsHttpConnection *conn,
   524                                            nsHttpTransaction *trans)
   525 {
   526     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   527     if (!ci)
   528         return nullptr;
   530     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
   532     // If there is no sign of coalescing (or it is disabled) then just
   533     // return the primary hash lookup
   534     if (!ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty())
   535         return ent;
   537     // If there is no preferred coalescing entry for this host (or the
   538     // preferred entry is the one that matched the mCT hash lookup) then
   539     // there is only option
   540     nsConnectionEntry *preferred = mSpdyPreferredHash.Get(ent->mCoalescingKey);
   541     if (!preferred || (preferred == ent))
   542         return ent;
   544     if (conn) {
   545         // The connection could be either in preferred or ent. It is most
   546         // likely the only active connection in preferred - so start with that.
   547         if (preferred->mActiveConns.Contains(conn))
   548             return preferred;
   549         if (preferred->mIdleConns.Contains(conn))
   550             return preferred;
   551     }
   553     if (trans && preferred->mPendingQ.Contains(trans))
   554         return preferred;
   556     // Neither conn nor trans found in preferred, use the default entry
   557     return ent;
   558 }
   560 nsresult
   561 nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
   562 {
   563     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   564     LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p",
   565          this, conn));
   567     if (!conn->ConnectionInfo())
   568         return NS_ERROR_UNEXPECTED;
   570     nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
   571                                                    conn, nullptr);
   573     if (!ent || !ent->mIdleConns.RemoveElement(conn))
   574         return NS_ERROR_UNEXPECTED;
   576     conn->Close(NS_ERROR_ABORT);
   577     NS_RELEASE(conn);
   578     mNumIdleConns--;
   579     ConditionallyStopPruneDeadConnectionsTimer();
   580     return NS_OK;
   581 }
   583 // This function lets a connection, after completing the NPN phase,
   584 // report whether or not it is using spdy through the usingSpdy
   585 // argument. It would not be necessary if NPN were driven out of
   586 // the connection manager. The connection entry associated with the
   587 // connection is then updated to indicate whether or not we want to use
   588 // spdy with that host and update the preliminary preferred host
   589 // entries used for de-sharding hostsnames.
   590 void
   591 nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
   592                                           bool usingSpdy)
   593 {
   594     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   596     nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
   597                                                    conn, nullptr);
   599     if (!ent)
   600         return;
   602     ent->mTestedSpdy = true;
   604     if (!usingSpdy)
   605         return;
   607     ent->mUsingSpdy = true;
   608     mNumSpdyActiveConns++;
   610     uint32_t ttl = conn->TimeToLive();
   611     uint64_t timeOfExpire = NowInSeconds() + ttl;
   612     if (!mTimer || timeOfExpire < mTimeOfNextWakeUp)
   613         PruneDeadConnectionsAfter(ttl);
   615     // Lookup preferred directly from the hash instead of using
   616     // GetSpdyPreferredEnt() because we want to avoid the cert compatibility
   617     // check at this point because the cert is never part of the hash
   618     // lookup. Filtering on that has to be done at the time of use
   619     // rather than the time of registration (i.e. now).
   620     nsConnectionEntry *joinedConnection;
   621     nsConnectionEntry *preferred =
   622         mSpdyPreferredHash.Get(ent->mCoalescingKey);
   624     LOG(("ReportSpdyConnection %s %s ent=%p preferred=%p\n",
   625          ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
   626          ent, preferred));
   628     if (!preferred) {
   629         if (!ent->mCoalescingKey.IsEmpty()) {
   630             mSpdyPreferredHash.Put(ent->mCoalescingKey, ent);
   631             ent->mSpdyPreferred = true;
   632             preferred = ent;
   633         }
   634     } else if ((preferred != ent) &&
   635                (joinedConnection = GetSpdyPreferredEnt(ent)) &&
   636                (joinedConnection != ent)) {
   637         //
   638         // A connection entry (e.g. made with a different hostname) with
   639         // the same IP address is preferred for future transactions over this
   640         // connection entry. Gracefully close down the connection to help
   641         // new transactions migrate over.
   643         LOG(("ReportSpdyConnection graceful close of conn=%p ent=%p to "
   644                  "migrate to preferred\n", conn, ent));
   646         conn->DontReuse();
   647     } else if (preferred != ent) {
   648         LOG (("ReportSpdyConnection preferred host may be in false start or "
   649               "may have insufficient cert. Leave mapping in place but do not "
   650               "abandon this connection yet."));
   651     }
   653     PostEvent(&nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ);
   654 }
   656 void
   657 nsHttpConnectionMgr::ReportSpdyCWNDSetting(nsHttpConnectionInfo *ci,
   658                                            uint32_t cwndValue)
   659 {
   660     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   662     if (!gHttpHandler->UseSpdyPersistentSettings())
   663         return;
   665     if (!ci)
   666         return;
   668     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
   669     if (!ent)
   670         return;
   672     ent = GetSpdyPreferredEnt(ent);
   673     if (!ent) // just to be thorough - but that map should always exist
   674         return;
   676     cwndValue = std::max(2U, cwndValue);
   677     cwndValue = std::min(128U, cwndValue);
   679     ent->mSpdyCWND = cwndValue;
   680     ent->mSpdyCWNDTimeStamp = TimeStamp::Now();
   681     return;
   682 }
   684 // a value of 0 means no setting is available
   685 uint32_t
   686 nsHttpConnectionMgr::GetSpdyCWNDSetting(nsHttpConnectionInfo *ci)
   687 {
   688     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   690     if (!gHttpHandler->UseSpdyPersistentSettings())
   691         return 0;
   693     if (!ci)
   694         return 0;
   696     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
   697     if (!ent)
   698         return 0;
   700     ent = GetSpdyPreferredEnt(ent);
   701     if (!ent) // just to be thorough - but that map should always exist
   702         return 0;
   704     if (ent->mSpdyCWNDTimeStamp.IsNull())
   705         return 0;
   707     // For privacy tracking reasons, and the fact that CWND is not
   708     // meaningful after some time, we don't honor stored CWND after 8
   709     // hours.
   710     TimeDuration age = TimeStamp::Now() - ent->mSpdyCWNDTimeStamp;
   711     if (age.ToMilliseconds() > (1000 * 60 * 60 * 8))
   712         return 0;
   714     return ent->mSpdyCWND;
   715 }
   717 nsHttpConnectionMgr::nsConnectionEntry *
   718 nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
   719 {
   720     if (!gHttpHandler->IsSpdyEnabled() ||
   721         !gHttpHandler->CoalesceSpdy() ||
   722         aOriginalEntry->mCoalescingKey.IsEmpty())
   723         return nullptr;
   725     nsConnectionEntry *preferred =
   726         mSpdyPreferredHash.Get(aOriginalEntry->mCoalescingKey);
   728     // if there is no redirection no cert validation is required
   729     if (preferred == aOriginalEntry)
   730         return aOriginalEntry;
   732     // if there is no preferred host or it is no longer using spdy
   733     // then skip pooling
   734     if (!preferred || !preferred->mUsingSpdy)
   735         return nullptr;
   737     // if there is not an active spdy session in this entry then
   738     // we cannot pool because the cert upon activation may not
   739     // be the same as the old one. Active sessions are prohibited
   740     // from changing certs.
   742     nsHttpConnection *activeSpdy = nullptr;
   744     for (uint32_t index = 0; index < preferred->mActiveConns.Length(); ++index) {
   745         if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
   746             activeSpdy = preferred->mActiveConns[index];
   747             break;
   748         }
   749     }
   751     if (!activeSpdy) {
   752         // remove the preferred status of this entry if it cannot be
   753         // used for pooling.
   754         preferred->mSpdyPreferred = false;
   755         RemoveSpdyPreferredEnt(preferred->mCoalescingKey);
   756         LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
   757              "preferred host mapping %s to %s removed due to inactivity.\n",
   758              aOriginalEntry->mConnInfo->Host(),
   759              preferred->mConnInfo->Host()));
   761         return nullptr;
   762     }
   764     // Check that the server cert supports redirection
   765     nsresult rv;
   766     bool isJoined = false;
   768     nsCOMPtr<nsISupports> securityInfo;
   769     nsCOMPtr<nsISSLSocketControl> sslSocketControl;
   770     nsAutoCString negotiatedNPN;
   772     activeSpdy->GetSecurityInfo(getter_AddRefs(securityInfo));
   773     if (!securityInfo) {
   774         NS_WARNING("cannot obtain spdy security info");
   775         return nullptr;
   776     }
   778     sslSocketControl = do_QueryInterface(securityInfo, &rv);
   779     if (NS_FAILED(rv)) {
   780         NS_WARNING("sslSocketControl QI Failed");
   781         return nullptr;
   782     }
   784     if (gHttpHandler->SpdyInfo()->ProtocolEnabled(0))
   785         rv = sslSocketControl->JoinConnection(gHttpHandler->SpdyInfo()->VersionString[0],
   786                                               aOriginalEntry->mConnInfo->GetHost(),
   787                                               aOriginalEntry->mConnInfo->Port(),
   788                                               &isJoined);
   789     else
   790         rv = NS_OK;                               /* simulate failed join */
   792     // JoinConnection() may have failed due to spdy version level. Try the other
   793     // level we support (if any)
   794     if (NS_SUCCEEDED(rv) && !isJoined && gHttpHandler->SpdyInfo()->ProtocolEnabled(1)) {
   795         rv = sslSocketControl->JoinConnection(gHttpHandler->SpdyInfo()->VersionString[1],
   796                                               aOriginalEntry->mConnInfo->GetHost(),
   797                                               aOriginalEntry->mConnInfo->Port(),
   798                                               &isJoined);
   799     }
   801     if (NS_FAILED(rv) || !isJoined) {
   802         LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
   803              "Host %s cannot be confirmed to be joined "
   804              "with %s connections. rv=%x isJoined=%d",
   805              preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
   806              rv, isJoined));
   807         Telemetry::Accumulate(Telemetry::SPDY_NPN_JOIN, false);
   808         return nullptr;
   809     }
   811     // IP pooling confirmed
   812     LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
   813          "Host %s has cert valid for %s connections, "
   814          "so %s will be coalesced with %s",
   815          preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
   816          aOriginalEntry->mConnInfo->Host(), preferred->mConnInfo->Host()));
   817     Telemetry::Accumulate(Telemetry::SPDY_NPN_JOIN, true);
   818     return preferred;
   819 }
   821 void
   822 nsHttpConnectionMgr::RemoveSpdyPreferredEnt(nsACString &aHashKey)
   823 {
   824     if (aHashKey.IsEmpty())
   825         return;
   827     mSpdyPreferredHash.Remove(aHashKey);
   828 }
   830 //-----------------------------------------------------------------------------
   831 // enumeration callbacks
   833 PLDHashOperator
   834 nsHttpConnectionMgr::ProcessOneTransactionCB(const nsACString &key,
   835                                              nsAutoPtr<nsConnectionEntry> &ent,
   836                                              void *closure)
   837 {
   838     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
   840     if (self->ProcessPendingQForEntry(ent, false))
   841         return PL_DHASH_STOP;
   843     return PL_DHASH_NEXT;
   844 }
   846 PLDHashOperator
   847 nsHttpConnectionMgr::ProcessAllTransactionsCB(const nsACString &key,
   848                                               nsAutoPtr<nsConnectionEntry> &ent,
   849                                               void *closure)
   850 {
   851     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
   852     self->ProcessPendingQForEntry(ent, true);
   853     return PL_DHASH_NEXT;
   854 }
   856 // If the global number of connections is preventing the opening of
   857 // new connections to a host without idle connections, then
   858 // close them regardless of their TTL
   859 PLDHashOperator
   860 nsHttpConnectionMgr::PurgeExcessIdleConnectionsCB(const nsACString &key,
   861                                                   nsAutoPtr<nsConnectionEntry> &ent,
   862                                                   void *closure)
   863 {
   864     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
   866     while (self->mNumIdleConns + self->mNumActiveConns + 1 >= self->mMaxConns) {
   867         if (!ent->mIdleConns.Length()) {
   868             // There are no idle conns left in this connection entry
   869             return PL_DHASH_NEXT;
   870         }
   871         nsHttpConnection *conn = ent->mIdleConns[0];
   872         ent->mIdleConns.RemoveElementAt(0);
   873         conn->Close(NS_ERROR_ABORT);
   874         NS_RELEASE(conn);
   875         self->mNumIdleConns--;
   876         self->ConditionallyStopPruneDeadConnectionsTimer();
   877     }
   878     return PL_DHASH_STOP;
   879 }
   881 // If the global number of connections is preventing the opening of
   882 // new connections to a host without idle connections, then
   883 // close any spdy asap
   884 PLDHashOperator
   885 nsHttpConnectionMgr::PurgeExcessSpdyConnectionsCB(const nsACString &key,
   886                                                   nsAutoPtr<nsConnectionEntry> &ent,
   887                                                   void *closure)
   888 {
   889     if (!ent->mUsingSpdy)
   890         return PL_DHASH_NEXT;
   892     nsHttpConnectionMgr *self = static_cast<nsHttpConnectionMgr *>(closure);
   893     for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
   894         nsHttpConnection *conn = ent->mActiveConns[index];
   895         if (conn->UsingSpdy() && conn->CanReuse()) {
   896             conn->DontReuse();
   897             // stop on <= (particularly =) beacuse this dontreuse causes async close
   898             if (self->mNumIdleConns + self->mNumActiveConns + 1 <= self->mMaxConns)
   899                 return PL_DHASH_STOP;
   900         }
   901     }
   902     return PL_DHASH_NEXT;
   903 }
   905 PLDHashOperator
   906 nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key,
   907                                             nsAutoPtr<nsConnectionEntry> &ent,
   908                                             void *closure)
   909 {
   910     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   911     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
   913     LOG(("  pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
   915     // Find out how long it will take for next idle connection to not be reusable
   916     // anymore.
   917     uint32_t timeToNextExpire = UINT32_MAX;
   918     int32_t count = ent->mIdleConns.Length();
   919     if (count > 0) {
   920         for (int32_t i=count-1; i>=0; --i) {
   921             nsHttpConnection *conn = ent->mIdleConns[i];
   922             if (!conn->CanReuse()) {
   923                 ent->mIdleConns.RemoveElementAt(i);
   924                 conn->Close(NS_ERROR_ABORT);
   925                 NS_RELEASE(conn);
   926                 self->mNumIdleConns--;
   927             } else {
   928                 timeToNextExpire = std::min(timeToNextExpire, conn->TimeToLive());
   929             }
   930         }
   931     }
   933     if (ent->mUsingSpdy) {
   934         for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
   935             nsHttpConnection *conn = ent->mActiveConns[index];
   936             if (conn->UsingSpdy()) {
   937                 if (!conn->CanReuse()) {
   938                     // marking it dont reuse will create an active tear down if
   939                     // the spdy session is idle.
   940                     conn->DontReuse();
   941                 }
   942                 else {
   943                     timeToNextExpire = std::min(timeToNextExpire,
   944                                               conn->TimeToLive());
   945                 }
   946             }
   947         }
   948     }
   950     // If time to next expire found is shorter than time to next wake-up, we need to
   951     // change the time for next wake-up.
   952     if (timeToNextExpire != UINT32_MAX) {
   953         uint32_t now = NowInSeconds();
   954         uint64_t timeOfNextExpire = now + timeToNextExpire;
   955         // If pruning of dead connections is not already scheduled to happen
   956         // or time found for next connection to expire is is before
   957         // mTimeOfNextWakeUp, we need to schedule the pruning to happen
   958         // after timeToNextExpire.
   959         if (!self->mTimer || timeOfNextExpire < self->mTimeOfNextWakeUp) {
   960             self->PruneDeadConnectionsAfter(timeToNextExpire);
   961         }
   962     } else {
   963         self->ConditionallyStopPruneDeadConnectionsTimer();
   964     }
   966     // if this entry is empty, we have too many entries,
   967     // and this doesn't represent some painfully determined
   968     // red condition, then we can clean it up and restart from
   969     // yellow
   970     if (ent->PipelineState()       != PS_RED &&
   971         self->mCT.Count()          >  125 &&
   972         ent->mIdleConns.Length()   == 0 &&
   973         ent->mActiveConns.Length() == 0 &&
   974         ent->mHalfOpens.Length()   == 0 &&
   975         ent->mPendingQ.Length()    == 0 &&
   976         ((!ent->mTestedSpdy && !ent->mUsingSpdy) ||
   977          !gHttpHandler->IsSpdyEnabled() ||
   978          self->mCT.Count() > 300)) {
   979         LOG(("    removing empty connection entry\n"));
   980         return PL_DHASH_REMOVE;
   981     }
   983     // otherwise use this opportunity to compact our arrays...
   984     ent->mIdleConns.Compact();
   985     ent->mActiveConns.Compact();
   986     ent->mPendingQ.Compact();
   988     return PL_DHASH_NEXT;
   989 }
   991 PLDHashOperator
   992 nsHttpConnectionMgr::ShutdownPassCB(const nsACString &key,
   993                                     nsAutoPtr<nsConnectionEntry> &ent,
   994                                     void *closure)
   995 {
   996     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
   998     nsHttpTransaction *trans;
   999     nsHttpConnection *conn;
  1001     // close all active connections
  1002     while (ent->mActiveConns.Length()) {
  1003         conn = ent->mActiveConns[0];
  1005         ent->mActiveConns.RemoveElementAt(0);
  1006         self->DecrementActiveConnCount(conn);
  1008         conn->Close(NS_ERROR_ABORT);
  1009         NS_RELEASE(conn);
  1012     // close all idle connections
  1013     while (ent->mIdleConns.Length()) {
  1014         conn = ent->mIdleConns[0];
  1016         ent->mIdleConns.RemoveElementAt(0);
  1017         self->mNumIdleConns--;
  1019         conn->Close(NS_ERROR_ABORT);
  1020         NS_RELEASE(conn);
  1022     // If all idle connections are removed,
  1023     // we can stop pruning dead connections.
  1024     self->ConditionallyStopPruneDeadConnectionsTimer();
  1026     // close all pending transactions
  1027     while (ent->mPendingQ.Length()) {
  1028         trans = ent->mPendingQ[0];
  1030         ent->mPendingQ.RemoveElementAt(0);
  1032         trans->Close(NS_ERROR_ABORT);
  1033         NS_RELEASE(trans);
  1036     // close all half open tcp connections
  1037     for (int32_t i = ((int32_t) ent->mHalfOpens.Length()) - 1; i >= 0; i--)
  1038         ent->mHalfOpens[i]->Abandon();
  1040     return PL_DHASH_REMOVE;
  1043 //-----------------------------------------------------------------------------
  1045 bool
  1046 nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent, bool considerAll)
  1048     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1050     LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
  1051          ent->mConnInfo->HashKey().get()));
  1053     ProcessSpdyPendingQ(ent);
  1055     nsHttpTransaction *trans;
  1056     nsresult rv;
  1057     bool dispatchedSuccessfully = false;
  1058     int dispatchCount = 0;
  1059 #ifdef WTF_DEBUG
  1060     uint32_t total = ent->mPendingQ.Length();
  1061 #endif
  1063     // if !considerAll iterate the pending list until one is dispatched successfully.
  1064     // Keep iterating afterwards only until a transaction fails to dispatch.
  1065     // if considerAll == true then try and dispatch all items.
  1066     for (uint32_t i = 0; i < ent->mPendingQ.Length(); ) {
  1067         trans = ent->mPendingQ[i];
  1069         // When this entry has already established a half-open
  1070         // connection, we want to prevent any duplicate half-open
  1071         // connections from being established and bound to this
  1072         // transaction.
  1073         bool alreadyHalfOpen = false;
  1074         if (ent->SupportsPipelining()) {
  1075             alreadyHalfOpen = (ent->UnconnectedHalfOpens() > 0);
  1076         } else {
  1077             for (int32_t j = 0; j < ((int32_t) ent->mHalfOpens.Length()); ++j) {
  1078                 if (ent->mHalfOpens[j]->Transaction() == trans) {
  1079                     alreadyHalfOpen = true;
  1080                     break;
  1085         rv = TryDispatchTransaction(ent, alreadyHalfOpen, trans);
  1086         if (NS_SUCCEEDED(rv) || (rv != NS_ERROR_NOT_AVAILABLE)) {
  1087             if (NS_SUCCEEDED(rv))
  1088                 LOG(("  dispatching pending transaction...\n"));
  1089             else
  1090                 LOG(("  removing pending transaction based on "
  1091                      "TryDispatchTransaction returning hard error %x\n", rv));
  1093             if (ent->mPendingQ.RemoveElement(trans)) {
  1094                 dispatchedSuccessfully = true;
  1095                 dispatchCount++;
  1096                 NS_RELEASE(trans);
  1097                 continue; // dont ++i as we just made the array shorter
  1100             LOG(("  transaction not found in pending queue\n"));
  1103         // We want to keep walking the dispatch table to ensure requests
  1104         // get combined properly.
  1105         //if (dispatchedSuccessfully && !considerAll)
  1106         //    break;
  1108         ++i;
  1111 #ifdef WTF_DEBUG
  1112     if (dispatchedSuccessfully) {
  1113         fprintf(stderr, "WTF-queue: Dispatched %d/%d pending transactions for %s\n",
  1114                 dispatchCount, total, ent->mConnInfo->Host());
  1115         return true;
  1117 #endif
  1119     return dispatchedSuccessfully;
  1122 bool
  1123 nsHttpConnectionMgr::ProcessPendingQForEntry(nsHttpConnectionInfo *ci)
  1125     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1127     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
  1128     if (ent)
  1129         return ProcessPendingQForEntry(ent, false);
  1130     return false;
  1133 bool
  1134 nsHttpConnectionMgr::SupportsPipelining(nsHttpConnectionInfo *ci)
  1136     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1138     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
  1139     if (ent)
  1140         return ent->SupportsPipelining();
  1141     return false;
  1144 // nsHttpPipelineFeedback used to hold references across events
  1146 class nsHttpPipelineFeedback
  1148 public:
  1149     nsHttpPipelineFeedback(nsHttpConnectionInfo *ci,
  1150                            nsHttpConnectionMgr::PipelineFeedbackInfoType info,
  1151                            nsHttpConnection *conn, uint32_t data)
  1152         : mConnInfo(ci)
  1153         , mConn(conn)
  1154         , mInfo(info)
  1155         , mData(data)
  1159     ~nsHttpPipelineFeedback()
  1163     nsRefPtr<nsHttpConnectionInfo> mConnInfo;
  1164     nsRefPtr<nsHttpConnection> mConn;
  1165     nsHttpConnectionMgr::PipelineFeedbackInfoType mInfo;
  1166     uint32_t mData;
  1167 };
  1169 void
  1170 nsHttpConnectionMgr::PipelineFeedbackInfo(nsHttpConnectionInfo *ci,
  1171                                           PipelineFeedbackInfoType info,
  1172                                           nsHttpConnection *conn,
  1173                                           uint32_t data)
  1175     if (!ci)
  1176         return;
  1178     // Post this to the socket thread if we are not running there already
  1179     if (PR_GetCurrentThread() != gSocketThread) {
  1180         nsHttpPipelineFeedback *fb = new nsHttpPipelineFeedback(ci, info,
  1181                                                                 conn, data);
  1183         nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessFeedback,
  1184                                 0, fb);
  1185         if (NS_FAILED(rv))
  1186             delete fb;
  1187         return;
  1190     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
  1192     if (ent)
  1193         ent->OnPipelineFeedbackInfo(info, conn, data);
  1196 void
  1197 nsHttpConnectionMgr::ReportFailedToProcess(nsIURI *uri)
  1199     MOZ_ASSERT(uri);
  1201     nsAutoCString host;
  1202     int32_t port = -1;
  1203     nsAutoCString username;
  1204     bool usingSSL = false;
  1205     bool isHttp = false;
  1207     nsresult rv = uri->SchemeIs("https", &usingSSL);
  1208     if (NS_SUCCEEDED(rv) && usingSSL)
  1209         isHttp = true;
  1210     if (NS_SUCCEEDED(rv) && !isHttp)
  1211         rv = uri->SchemeIs("http", &isHttp);
  1212     if (NS_SUCCEEDED(rv))
  1213         rv = uri->GetAsciiHost(host);
  1214     if (NS_SUCCEEDED(rv))
  1215         rv = uri->GetPort(&port);
  1216     if (NS_SUCCEEDED(rv))
  1217         uri->GetUsername(username);
  1218     if (NS_FAILED(rv) || !isHttp || host.IsEmpty())
  1219         return;
  1221     // report the event for all the permutations of anonymous and
  1222     // private versions of this host
  1223     nsRefPtr<nsHttpConnectionInfo> ci =
  1224         new nsHttpConnectionInfo(host, port, username, nullptr, usingSSL);
  1225     ci->SetAnonymous(false);
  1226     ci->SetPrivate(false);
  1227     PipelineFeedbackInfo(ci, RedCorruptedContent, nullptr, 0);
  1229     ci = ci->Clone();
  1230     ci->SetAnonymous(false);
  1231     ci->SetPrivate(true);
  1232     PipelineFeedbackInfo(ci, RedCorruptedContent, nullptr, 0);
  1234     ci = ci->Clone();
  1235     ci->SetAnonymous(true);
  1236     ci->SetPrivate(false);
  1237     PipelineFeedbackInfo(ci, RedCorruptedContent, nullptr, 0);
  1239     ci = ci->Clone();
  1240     ci->SetAnonymous(true);
  1241     ci->SetPrivate(true);
  1242     PipelineFeedbackInfo(ci, RedCorruptedContent, nullptr, 0);
  1245 // we're at the active connection limit if any one of the following conditions is true:
  1246 //  (1) at max-connections
  1247 //  (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
  1248 //  (3) keep-alive disabled and at max-connections-per-server
  1249 bool
  1250 nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, uint32_t caps)
  1252     nsHttpConnectionInfo *ci = ent->mConnInfo;
  1254     LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
  1255         ci->HashKey().get(), caps));
  1257     // update maxconns if potentially limited by the max socket count
  1258     // this requires a dynamic reduction in the max socket count to a point
  1259     // lower than the max-connections pref.
  1260     uint32_t maxSocketCount = gHttpHandler->MaxSocketCount();
  1261     if (mMaxConns > maxSocketCount) {
  1262         mMaxConns = maxSocketCount;
  1263         LOG(("nsHttpConnectionMgr %p mMaxConns dynamically reduced to %u",
  1264              this, mMaxConns));
  1267     // If there are more active connections than the global limit, then we're
  1268     // done. Purging idle connections won't get us below it.
  1269     if (mNumActiveConns >= mMaxConns) {
  1270         LOG(("  num active conns == max conns\n"));
  1271         return true;
  1274     // Add in the in-progress tcp connections, we will assume they are
  1275     // keepalive enabled.
  1276     // Exclude half-open's that has already created a usable connection.
  1277     // This prevents the limit being stuck on ipv6 connections that
  1278     // eventually time out after typical 21 seconds of no ACK+SYN reply.
  1279     uint32_t totalCount =
  1280         ent->mActiveConns.Length() + ent->UnconnectedHalfOpens();
  1282     uint16_t maxPersistConns;
  1284     if (ci->UsingHttpProxy() && !ci->UsingConnect())
  1285         maxPersistConns = mMaxPersistConnsPerProxy;
  1286     else
  1287         maxPersistConns = mMaxPersistConnsPerHost;
  1289     LOG(("   connection count = %d, limit %d\n", totalCount, maxPersistConns));
  1291     // use >= just to be safe
  1292     bool result = (totalCount >= maxPersistConns);
  1293     LOG(("  result: %s", result ? "true" : "false"));
  1294     return result;
  1297 void
  1298 nsHttpConnectionMgr::ClosePersistentConnections(nsConnectionEntry *ent)
  1300     LOG(("nsHttpConnectionMgr::ClosePersistentConnections [ci=%s]\n",
  1301          ent->mConnInfo->HashKey().get()));
  1302     while (ent->mIdleConns.Length()) {
  1303         nsHttpConnection *conn = ent->mIdleConns[0];
  1304         ent->mIdleConns.RemoveElementAt(0);
  1305         mNumIdleConns--;
  1306         conn->Close(NS_ERROR_ABORT);
  1307         NS_RELEASE(conn);
  1310     int32_t activeCount = ent->mActiveConns.Length();
  1311     for (int32_t i=0; i < activeCount; i++)
  1312         ent->mActiveConns[i]->DontReuse();
  1315 PLDHashOperator
  1316 nsHttpConnectionMgr::ClosePersistentConnectionsCB(const nsACString &key,
  1317                                                   nsAutoPtr<nsConnectionEntry> &ent,
  1318                                                   void *closure)
  1320     nsHttpConnectionMgr *self = static_cast<nsHttpConnectionMgr *>(closure);
  1321     self->ClosePersistentConnections(ent);
  1322     return PL_DHASH_NEXT;
  1325 bool
  1326 nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent,
  1327                                          bool ignorePossibleSpdyConnections)
  1329     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1331     // If this host is trying to negotiate a SPDY session right now,
  1332     // don't create any new ssl connections until the result of the
  1333     // negotiation is known.
  1335     bool doRestrict = ent->mConnInfo->UsingSSL() &&
  1336         gHttpHandler->IsSpdyEnabled() &&
  1337         ((!ent->mTestedSpdy && !ignorePossibleSpdyConnections) ||
  1338          ent->mUsingSpdy) &&
  1339         (ent->mHalfOpens.Length() || ent->mActiveConns.Length());
  1341     // If there are no restrictions, we are done
  1342     if (!doRestrict)
  1343         return false;
  1345     // If the restriction is based on a tcp handshake in progress
  1346     // let that connect and then see if it was SPDY or not
  1347     if (ent->UnconnectedHalfOpens() && !ignorePossibleSpdyConnections)
  1348         return true;
  1350     // There is a concern that a host is using a mix of HTTP/1 and SPDY.
  1351     // In that case we don't want to restrict connections just because
  1352     // there is a single active HTTP/1 session in use.
  1353     if (ent->mUsingSpdy && ent->mActiveConns.Length()) {
  1354         bool confirmedRestrict = false;
  1355         for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
  1356             nsHttpConnection *conn = ent->mActiveConns[index];
  1357             if (!conn->ReportedNPN() || conn->CanDirectlyActivate()) {
  1358                 confirmedRestrict = true;
  1359                 break;
  1362         doRestrict = confirmedRestrict;
  1363         if (!confirmedRestrict) {
  1364             LOG(("nsHttpConnectionMgr spdy connection restriction to "
  1365                  "%s bypassed.\n", ent->mConnInfo->Host()));
  1368     return doRestrict;
  1371 // returns NS_OK if a connection was started
  1372 // return NS_ERROR_NOT_AVAILABLE if a new connection cannot be made due to
  1373 //        ephemeral limits
  1374 // returns other NS_ERROR on hard failure conditions
  1375 nsresult
  1376 nsHttpConnectionMgr::MakeNewConnection(nsConnectionEntry *ent,
  1377                                        nsHttpTransaction *trans)
  1379     LOG(("nsHttpConnectionMgr::MakeNewConnection %p ent=%p trans=%p",
  1380          this, ent, trans));
  1381     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1383     uint32_t halfOpenLength = ent->mHalfOpens.Length();
  1384     for (uint32_t i = 0; i < halfOpenLength; i++) {
  1385         if (ent->mHalfOpens[i]->IsSpeculative()) {
  1386             // We've found a speculative connection in the half
  1387             // open list. Remove the speculative bit from it and that
  1388             // connection can later be used for this transaction
  1389             // (or another one in the pending queue) - we don't
  1390             // need to open a new connection here.
  1391             LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s]\n"
  1392                  "Found a speculative half open connection\n",
  1393                  ent->mConnInfo->HashKey().get()));
  1394             ent->mHalfOpens[i]->SetSpeculative(false);
  1396             // return OK because we have essentially opened a new connection
  1397             // by converting a speculative half-open to general use
  1398             return NS_OK;
  1402     // If this host is trying to negotiate a SPDY session right now,
  1403     // don't create any new connections until the result of the
  1404     // negotiation is known.
  1405     if (!(trans->Caps() & NS_HTTP_DISALLOW_SPDY) &&
  1406         (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) &&
  1407         RestrictConnections(ent)) {
  1408         LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s] "
  1409              "Not Available Due to RestrictConnections()\n",
  1410              ent->mConnInfo->HashKey().get()));
  1411         return NS_ERROR_NOT_AVAILABLE;
  1414     // We need to make a new connection. If that is going to exceed the
  1415     // global connection limit then try and free up some room by closing
  1416     // an idle connection to another host. We know it won't select "ent"
  1417     // beacuse we have already determined there are no idle connections
  1418     // to our destination
  1420     if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) && mNumIdleConns)
  1421         mCT.Enumerate(PurgeExcessIdleConnectionsCB, this);
  1423     if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) &&
  1424         mNumActiveConns && gHttpHandler->IsSpdyEnabled())
  1425         mCT.Enumerate(PurgeExcessSpdyConnectionsCB, this);
  1427     if (AtActiveConnectionLimit(ent, trans->Caps()))
  1428         return NS_ERROR_NOT_AVAILABLE;
  1430 #ifdef WTF_DEBUG
  1431         fprintf(stderr, "WTF: MakeNewConnection() is creating a transport (pipelines %d) for host %s\n",
  1432                 ent->SupportsPipelining(), ent->mConnInfo->Host());
  1433 #endif
  1434     nsresult rv = CreateTransport(ent, trans, trans->Caps(), false);
  1435     if (NS_FAILED(rv)) {
  1436         /* hard failure */
  1437         LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s trans = %p] "
  1438              "CreateTransport() hard failure.\n",
  1439              ent->mConnInfo->HashKey().get(), trans));
  1440         trans->Close(rv);
  1441         if (rv == NS_ERROR_NOT_AVAILABLE)
  1442             rv = NS_ERROR_FAILURE;
  1443         return rv;
  1446     return NS_OK;
  1449 bool
  1450 nsHttpConnectionMgr::AddToBestPipeline(nsConnectionEntry *ent,
  1451                                            nsHttpTransaction *trans,
  1452                                            nsHttpTransaction::Classifier classification,
  1453                                            uint16_t depthLimit)
  1455     if (classification == nsAHttpTransaction::CLASS_SOLO)
  1456         return false;
  1458     uint32_t maxdepth = ent->MaxPipelineDepth(classification);
  1459     if (maxdepth == 0) {
  1460         ent->CreditPenalty();
  1461         maxdepth = ent->MaxPipelineDepth(classification);
  1464     if (ent->PipelineState() == PS_RED)
  1465         return false;
  1467     if (ent->PipelineState() == PS_YELLOW && ent->mYellowConnection)
  1468         return false;
  1470     // The maximum depth of a pipeline in yellow is 1 pipeline of
  1471     // depth 2 for entire CI. When that transaction completes successfully
  1472     // we transition to green and that expands the allowed depth
  1473     // to any number of pipelines of up to depth 4.  When a transaction
  1474     // queued at position 3 or deeper succeeds we open it all the way
  1475     // up to depths limited only by configuration. The staggered start
  1476     // in green is simply because a successful yellow test of depth=2
  1477     // might really just be a race condition (i.e. depth=1 from the
  1478     // server's point of view), while depth=3 is a stronger indicator -
  1479     // keeping the pipelines to a modest depth during that period limits
  1480     // the damage if something is going to go wrong.
  1482     maxdepth = std::min<uint32_t>(maxdepth, depthLimit);
  1484     if (maxdepth < 2)
  1485         return false;
  1487     // Find out how many requests of this class we have
  1488     uint32_t sameClass = 0;
  1489     uint32_t allClasses = ent->mPendingQ.Length();
  1490     for (uint32_t i = 0; i < allClasses; ++i) {
  1491         if (trans != ent->mPendingQ[i] &&
  1492             classification == ent->mPendingQ[i]->Classification()) {
  1493             sameClass++;
  1497     nsAHttpTransaction *activeTrans;
  1498     nsHttpPipeline *pipeline;
  1499     nsHttpConnection *bestConn = nullptr;
  1500     uint32_t activeCount = ent->mActiveConns.Length();
  1501     uint32_t pipelineDepth;
  1502     uint32_t requestLen;
  1503     uint32_t totalDepth = 0;
  1505     // Now, try to find the best pipeline
  1506     nsTArray<nsHttpConnection *> validConns;
  1507     nsTArray<nsHttpConnection *> betterConns;
  1508     nsTArray<nsHttpConnection *> bestConns;
  1509     uint32_t numPipelines = 0;
  1511     for (uint32_t i = 0; i < activeCount; ++i) {
  1512         nsHttpConnection *conn = ent->mActiveConns[i];
  1514         if (!conn->SupportsPipelining())
  1515             continue;
  1517         activeTrans = conn->Transaction();
  1519         if (!activeTrans ||
  1520             activeTrans->IsDone() ||
  1521             NS_FAILED(activeTrans->Status()))
  1522             continue;
  1524         pipeline = activeTrans->QueryPipeline();
  1525         if (!pipeline)
  1526             continue;
  1528         numPipelines++;
  1530         pipelineDepth = activeTrans->PipelineDepth();
  1531         requestLen = pipeline->RequestDepth();
  1533         totalDepth += pipelineDepth;
  1535         // If we're within striking distance of our pipeline
  1536         // packaging goal, give a little slack on the depth
  1537         // limit to allow us to try to get there. Don't give
  1538         // too much slack, though, or we'll tend to have
  1539         // request packages of the same size when we have
  1540         // many content elements appear at once.
  1541         if (maxdepth +
  1542               PR_MIN(mMaxOptimisticPipelinedRequests,
  1543                      requestLen + allClasses)
  1544               <= pipelineDepth)
  1545             continue;
  1547         validConns.AppendElement(conn);
  1549         // Prefer a pipeline that either has at least two requests
  1550         // queued already, or for which we can add multiple requests
  1551         if (requestLen + allClasses < mMaxOptimisticPipelinedRequests)
  1552             continue;
  1554         betterConns.AppendElement(conn);
  1556         // Prefer a pipeline with the same classification if 
  1557         // our current classes will put it over the line
  1558         if (conn->Classification() != classification)
  1559             continue;
  1560         if (requestLen + sameClass < mMaxOptimisticPipelinedRequests)
  1561             continue;
  1563         bestConns.AppendElement(conn);
  1566     const char *type;
  1567     if (bestConns.Length()) {
  1568         type = "best";
  1569         bestConn = bestConns[rand()%bestConns.Length()];
  1570     } else if (betterConns.Length()) {
  1571         type = "better";
  1572         bestConn = betterConns[rand()%betterConns.Length()];
  1573     } else if (validConns.Length() && totalDepth == 0) {
  1574         // We only use valid conns if it's a last resort
  1575         // (No other requests are pending or in flight)
  1576         type = "valid";
  1577         bestConn = validConns[rand()%validConns.Length()];
  1578     } else {
  1579         return false;
  1582     activeTrans = bestConn->Transaction();
  1583     nsresult rv = activeTrans->AddTransaction(trans);
  1584     if (NS_FAILED(rv))
  1585         return false;
  1587     LOG(("   scheduling trans %p on pipeline at position %d, type %s\n",
  1588          trans, trans->PipelinePosition(), type));
  1590 #ifdef WTF_DEBUG
  1591     pipeline = activeTrans->QueryPipeline();
  1592     fprintf(stderr,
  1593             "WTF-depth: Added trans to %s of %d/%d/%d/%d pipelines. Request len %d/%d/%d for %s\n",
  1594             type, bestConns.Length(), betterConns.Length(), validConns.Length(),
  1595             numPipelines, pipeline->RequestDepth(), activeTrans->PipelineDepth(),
  1596             maxdepth, ent->mConnInfo->Host());
  1597 #endif
  1599     if ((ent->PipelineState() == PS_YELLOW) && (trans->PipelinePosition() > 1))
  1600         ent->SetYellowConnection(bestConn);
  1602     if (!trans->GetPendingTime().IsNull()) {
  1603         if (trans->UsesPipelining())
  1604             AccumulateTimeDelta(
  1605                 Telemetry::TRANSACTION_WAIT_TIME_HTTP_PIPELINES,
  1606                 trans->GetPendingTime(), TimeStamp::Now());
  1607         else
  1608             AccumulateTimeDelta(
  1609                 Telemetry::TRANSACTION_WAIT_TIME_HTTP,
  1610                 trans->GetPendingTime(), TimeStamp::Now());
  1611         trans->SetPendingTime(false);
  1613     return true;
  1616 bool
  1617 nsHttpConnectionMgr::IsUnderPressure(nsConnectionEntry *ent,
  1618                                    nsHttpTransaction::Classifier classification)
  1620     // A connection entry is declared to be "under pressure" if most of the
  1621     // allowed parallel connections are already used up. In that case we want to
  1622     // favor existing pipelines over more parallelism so as to reserve any
  1623     // unused parallel connections for types that don't have existing pipelines.
  1624     //
  1625     // The definition of connection pressure is a pretty liberal one here - that
  1626     // is why we are using the more restrictive maxPersist* counters.
  1627     //
  1628     // Pipelines are also favored when the requested classification is already
  1629     // using 3 or more of the connections. Failure to do this could result in
  1630     // one class (e.g. images) establishing self replenishing queues on all the
  1631     // connections that would starve the other transaction types.
  1633     int32_t currentConns = ent->mActiveConns.Length();
  1634     int32_t maxConns =
  1635         (ent->mConnInfo->UsingHttpProxy() && !ent->mConnInfo->UsingConnect()) ?
  1636         mMaxPersistConnsPerProxy : mMaxPersistConnsPerHost;
  1638     // Leave room for at least 3 distinct types to operate concurrently,
  1639     // this satisfies the typical {html, js/css, img} page.
  1640     if (currentConns >= (maxConns - 2))
  1641         return true;                           /* prefer pipeline */
  1643     int32_t sameClass = 0;
  1644     for (int32_t i = 0; i < currentConns; ++i)
  1645         if (classification == ent->mActiveConns[i]->Classification())
  1646             if (++sameClass == 3)
  1647                 return true;                   /* prefer pipeline */
  1649     return false;                              /* normal behavior */
  1652 // returns OK if a connection is found for the transaction
  1653 //   and the transaction is started.
  1654 // returns ERROR_NOT_AVAILABLE if no connection can be found and it
  1655 //   should be queued until circumstances change
  1656 // returns other ERROR when transaction has a hard failure and should
  1657 //   not remain in the pending queue
  1658 nsresult
  1659 nsHttpConnectionMgr::TryDispatchTransaction(nsConnectionEntry *ent,
  1660                                             bool onlyReusedConnection,
  1661                                             nsHttpTransaction *trans)
  1663     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1664     LOG(("nsHttpConnectionMgr::TryDispatchTransaction without conn "
  1665          "[ci=%s caps=%x]\n",
  1666          ent->mConnInfo->HashKey().get(), uint32_t(trans->Caps())));
  1668     nsHttpTransaction::Classifier classification = trans->Classification();
  1669     uint32_t caps = trans->Caps();
  1671     bool allowNewPipelines = true;
  1673     // no keep-alive means no pipelines either
  1674     if (!(caps & NS_HTTP_ALLOW_KEEPALIVE))
  1675         caps = caps & ~NS_HTTP_ALLOW_PIPELINING;
  1677     nsRefPtr<nsHttpConnection> unusedSpdyPersistentConnection;
  1679     // step 0
  1680     // look for existing spdy connection - that's always best because it is
  1681     // essentially pipelining without head of line blocking
  1683     if (!(caps & NS_HTTP_DISALLOW_SPDY) && gHttpHandler->IsSpdyEnabled()) {
  1684         nsRefPtr<nsHttpConnection> conn = GetSpdyPreferredConn(ent);
  1685         if (conn) {
  1686             if ((caps & NS_HTTP_ALLOW_KEEPALIVE) || !conn->IsExperienced()) {
  1687                 LOG(("   dispatch to spdy: [conn=%x]\n", conn.get()));
  1688                 trans->RemoveDispatchedAsBlocking();  /* just in case */
  1689                 DispatchTransaction(ent, trans, conn);
  1690                 return NS_OK;
  1692             unusedSpdyPersistentConnection = conn;
  1696     // If this is not a blocking transaction and the loadgroup for it is
  1697     // currently processing one or more blocking transactions then we
  1698     // need to just leave it in the queue until those are complete unless it is
  1699     // explicitly marked as unblocked.
  1700     if (!(caps & NS_HTTP_LOAD_AS_BLOCKING)) {
  1701         if (!(caps & NS_HTTP_LOAD_UNBLOCKED)) {
  1702             nsILoadGroupConnectionInfo *loadGroupCI = trans->LoadGroupConnectionInfo();
  1703             if (loadGroupCI) {
  1704                 uint32_t blockers = 0;
  1705                 if (NS_SUCCEEDED(loadGroupCI->GetBlockingTransactionCount(&blockers)) &&
  1706                     blockers) {
  1707                     // need to wait for blockers to clear
  1708                     LOG(("   blocked by load group: [blockers=%d]\n", blockers));
  1709                     return NS_ERROR_NOT_AVAILABLE;
  1714     else {
  1715         // Mark the transaction and its load group as blocking right now to prevent
  1716         // other transactions from being reordered in the queue due to slow syns.
  1717         trans->DispatchedAsBlocking();
  1720     // step 1: Try a pipeline
  1721     if (caps & NS_HTTP_ALLOW_PIPELINING &&
  1722         AddToBestPipeline(ent, trans, classification,
  1723                           mMaxPipelinedRequests)) {
  1724         return NS_OK;
  1727     // XXX: Kill this block? It's new.. but it may be needed for SPDY
  1728     // Subject most transactions at high parallelism to rate pacing.
  1729     // It will only be actually submitted to the
  1730     // token bucket once, and if possible it is granted admission synchronously.
  1731     // It is important to leave a transaction in the pending queue when blocked by
  1732     // pacing so it can be found on cancel if necessary.
  1733     // Transactions that cause blocking or bypass it (e.g. js/css) are not rate
  1734     // limited.
  1735     if (gHttpHandler->UseRequestTokenBucket() &&
  1736         (mNumActiveConns >= mNumSpdyActiveConns) && // just check for robustness sake
  1737         ((mNumActiveConns - mNumSpdyActiveConns) >= gHttpHandler->RequestTokenBucketMinParallelism()) &&
  1738         !(caps & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED))) {
  1739         if (!trans->TryToRunPacedRequest()) {
  1740             LOG(("   blocked due to rate pacing\n"));
  1741             return NS_ERROR_NOT_AVAILABLE;
  1745     // Step 2: Decide if we should forbid new pipeline creation.
  1746     //
  1747     // FIXME: We repurposed mMaxOptimisticPipelinedRequests here to mean:
  1748     // "Don't make a new pipeline until you have this many requests pending and 
  1749     // no potential connections to put them on". It might be nice to give this
  1750     // its own pref..
  1751     if (HasPipelines(ent) &&
  1752             ent->mPendingQ.Length() < mMaxOptimisticPipelinedRequests &&
  1753             trans->Classification() != nsAHttpTransaction::CLASS_SOLO &&
  1754             caps & NS_HTTP_ALLOW_PIPELINING)
  1755         allowNewPipelines = false;
  1757     // step 3: consider an idle persistent connection
  1758     if (allowNewPipelines && (caps & NS_HTTP_ALLOW_KEEPALIVE)) {
  1759         nsRefPtr<nsHttpConnection> conn;
  1760         while (!conn && (ent->mIdleConns.Length() > 0)) {
  1761             conn = ent->mIdleConns[0];
  1762             ent->mIdleConns.RemoveElementAt(0);
  1763             mNumIdleConns--;
  1764             nsHttpConnection *temp = conn;
  1765             NS_RELEASE(temp);
  1767             // we check if the connection can be reused before even checking if
  1768             // it is a "matching" connection.
  1769             if (!conn->CanReuse()) {
  1770                 LOG(("   dropping stale connection: [conn=%x]\n", conn.get()));
  1771                 conn->Close(NS_ERROR_ABORT);
  1772                 conn = nullptr;
  1774             else {
  1775                 LOG(("   reusing connection [conn=%x]\n", conn.get()));
  1776                 conn->EndIdleMonitoring();
  1779             // If there are no idle connections left at all, we need to make
  1780             // sure that we are not pruning dead connections anymore.
  1781             ConditionallyStopPruneDeadConnectionsTimer();
  1783         if (conn) {
  1784             // This will update the class of the connection to be the class of
  1785             // the transaction dispatched on it.
  1786             AddActiveConn(conn, ent);
  1787             DispatchTransaction(ent, trans, conn);
  1788             return NS_OK;
  1792     // step 4: Maybe make a connection? 
  1793     if (!onlyReusedConnection && allowNewPipelines) {
  1794         nsresult rv = MakeNewConnection(ent, trans);
  1795         if (NS_SUCCEEDED(rv)) {
  1796             // this function returns NOT_AVAILABLE for asynchronous connects
  1797             return NS_ERROR_NOT_AVAILABLE;
  1800         if (rv != NS_ERROR_NOT_AVAILABLE) {
  1801             // not available return codes should try next step as they are
  1802             // not hard errors. Other codes should stop now
  1803             return rv;
  1807     // step 5
  1808     if (unusedSpdyPersistentConnection) {
  1809         // to avoid deadlocks, we need to throw away this perfectly valid SPDY
  1810         // connection to make room for a new one that can service a no KEEPALIVE
  1811         // request
  1812         unusedSpdyPersistentConnection->DontReuse();
  1815     // XXX: We dequeue and queue the same url here sometimes..
  1816 #ifdef WTF_DEBUG
  1817     nsHttpRequestHead *head = trans->RequestHead();
  1818     fprintf(stderr, "WTF: Queuing url %s%s\n",
  1819             ent->mConnInfo->Host(),
  1820             head ? head->RequestURI().BeginReading() : "<unknown?>");
  1821 #endif
  1824     return NS_ERROR_NOT_AVAILABLE;                /* queue it */
  1827 nsresult
  1828 nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
  1829                                          nsHttpTransaction *trans,
  1830                                          nsHttpConnection *conn)
  1832     uint32_t caps = trans->Caps();
  1833     int32_t priority = trans->Priority();
  1834     nsresult rv;
  1836     LOG(("nsHttpConnectionMgr::DispatchTransaction "
  1837          "[ci=%s trans=%x caps=%x conn=%x priority=%d]\n",
  1838          ent->mConnInfo->HashKey().get(), trans, caps, conn, priority));
  1840     // It is possible for a rate-paced transaction to be dispatched independent
  1841     // of the token bucket when the amount of parallelization has changed or
  1842     // when a muxed connection (e.g. spdy or pipelines) becomes available.
  1843     trans->CancelPacing(NS_OK);
  1845     if (conn->UsingSpdy()) {
  1846         LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s,"
  1847              "Connection host = %s\n",
  1848              trans->ConnectionInfo()->Host(),
  1849              conn->ConnectionInfo()->Host()));
  1850         rv = conn->Activate(trans, caps, priority);
  1851         MOZ_ASSERT(NS_SUCCEEDED(rv), "SPDY Cannot Fail Dispatch");
  1852         if (NS_SUCCEEDED(rv) && !trans->GetPendingTime().IsNull()) {
  1853             AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_SPDY,
  1854                 trans->GetPendingTime(), TimeStamp::Now());
  1855             trans->SetPendingTime(false);
  1857         return rv;
  1860     MOZ_ASSERT(conn && !conn->Transaction(),
  1861                "DispatchTranaction() on non spdy active connection");
  1863     if (!(caps & NS_HTTP_ALLOW_PIPELINING))
  1864         conn->Classify(nsAHttpTransaction::CLASS_SOLO);
  1865     else
  1866         conn->Classify(trans->Classification());
  1868     rv = DispatchAbstractTransaction(ent, trans, caps, conn, priority);
  1870     if (NS_SUCCEEDED(rv) && !trans->GetPendingTime().IsNull()) {
  1871         if (trans->UsesPipelining())
  1872             AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_HTTP_PIPELINES,
  1873                 trans->GetPendingTime(), TimeStamp::Now());
  1874         else
  1875             AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_HTTP,
  1876                 trans->GetPendingTime(), TimeStamp::Now());
  1877         trans->SetPendingTime(false);
  1879     return rv;
  1883 // Use this method for dispatching nsAHttpTransction's. It can only safely be
  1884 // used upon first use of a connection when NPN has not negotiated SPDY vs
  1885 // HTTP/1 yet as multiplexing onto an existing SPDY session requires a
  1886 // concrete nsHttpTransaction
  1887 nsresult
  1888 nsHttpConnectionMgr::DispatchAbstractTransaction(nsConnectionEntry *ent,
  1889                                                  nsAHttpTransaction *aTrans,
  1890                                                  uint32_t caps,
  1891                                                  nsHttpConnection *conn,
  1892                                                  int32_t priority)
  1894     MOZ_ASSERT(!conn->UsingSpdy(),
  1895                "Spdy Must Not Use DispatchAbstractTransaction");
  1896     LOG(("nsHttpConnectionMgr::DispatchAbstractTransaction "
  1897          "[ci=%s trans=%x caps=%x conn=%x]\n",
  1898          ent->mConnInfo->HashKey().get(), aTrans, caps, conn));
  1900     /* Use pipeline datastructure even if connection does not currently qualify
  1901        to pipeline this transaction because a different pipeline-eligible
  1902        transaction might be placed on the active connection. Make an exception
  1903        for CLASS_SOLO as that connection will never pipeline until it goes
  1904        quiescent */
  1906     nsRefPtr<nsAHttpTransaction> transaction;
  1907     nsresult rv;
  1908     if (conn->Classification() != nsAHttpTransaction::CLASS_SOLO) {
  1909         LOG(("   using pipeline datastructure.\n"));
  1910         nsRefPtr<nsHttpPipeline> pipeline;
  1911         rv = BuildPipeline(ent, aTrans, getter_AddRefs(pipeline));
  1912         if (!NS_SUCCEEDED(rv))
  1913             return rv;
  1914         transaction = pipeline;
  1915 #ifdef WTF_DEBUG
  1916         if (HasPipelines(ent) &&
  1917                 ent->mPendingQ.Length()+1 < mMaxOptimisticPipelinedRequests) {
  1918             fprintf(stderr, "WTF-new-bug: New pipeline created from %d idle conns for host %s with %d/%d pending\n",
  1919                     ent->mIdleConns.Length(), ent->mConnInfo->Host(), ent->mPendingQ.Length(),
  1920                     mMaxOptimisticPipelinedRequests);
  1921         } else {
  1922             fprintf(stderr, "WTF-new: New pipeline created from %d idle conns for host %s with %d/%d pending\n",
  1923                     ent->mIdleConns.Length(), ent->mConnInfo->Host(), ent->mPendingQ.Length(),
  1924                     mMaxOptimisticPipelinedRequests);
  1926 #endif
  1928     else {
  1929         LOG(("   not using pipeline datastructure due to class solo.\n"));
  1930         transaction = aTrans;
  1931 #ifdef WTF_TEST
  1932         nsHttpRequestHead *head = transaction->RequestHead();
  1933         fprintf(stderr, "WTF-order: Pipeline forbidden for url %s%s\n",
  1934                 ent->mConnInfo->Host(),
  1935                 head ? head->RequestURI().BeginReading() : "<unknown?>");
  1936 #endif
  1939     nsRefPtr<nsConnectionHandle> handle = new nsConnectionHandle(conn);
  1941     // give the transaction the indirect reference to the connection.
  1942     transaction->SetConnection(handle);
  1944     rv = conn->Activate(transaction, caps, priority);
  1945     if (NS_FAILED(rv)) {
  1946         LOG(("  conn->Activate failed [rv=%x]\n", rv));
  1947         ent->mActiveConns.RemoveElement(conn);
  1948         if (conn == ent->mYellowConnection)
  1949             ent->OnYellowComplete();
  1950         DecrementActiveConnCount(conn);
  1951         ConditionallyStopTimeoutTick();
  1953         // sever back references to connection, and do so without triggering
  1954         // a call to ReclaimConnection ;-)
  1955         transaction->SetConnection(nullptr);
  1956         NS_RELEASE(handle->mConn);
  1957         // destroy the connection
  1958         NS_RELEASE(conn);
  1961     // As transaction goes out of scope it will drop the last refernece to the
  1962     // pipeline if activation failed, in which case this will destroy
  1963     // the pipeline, which will cause each the transactions owned by the
  1964     // pipeline to be restarted.
  1966     return rv;
  1969 nsresult
  1970 nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
  1971                                    nsAHttpTransaction *firstTrans,
  1972                                    nsHttpPipeline **result)
  1974     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1976     /* form a pipeline here even if nothing is pending so that we
  1977        can stream-feed it as new transactions arrive */
  1979     /* the first transaction can go in unconditionally - 1 transaction
  1980        on a nsHttpPipeline object is not a real HTTP pipeline */
  1982     nsRefPtr<nsHttpPipeline> pipeline = new nsHttpPipeline();
  1983     pipeline->AddTransaction(firstTrans);
  1984     NS_ADDREF(*result = pipeline);
  1985     return NS_OK;
  1988 void
  1989 nsHttpConnectionMgr::ReportProxyTelemetry(nsConnectionEntry *ent)
  1991     enum { PROXY_NONE = 1, PROXY_HTTP = 2, PROXY_SOCKS = 3 };
  1993     if (!ent->mConnInfo->UsingProxy())
  1994         Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_NONE);
  1995     else if (ent->mConnInfo->UsingHttpProxy())
  1996         Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_HTTP);
  1997     else
  1998         Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_SOCKS);
  2001 nsresult
  2002 nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
  2004     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2006     // since "adds" and "cancels" are processed asynchronously and because
  2007     // various events might trigger an "add" directly on the socket thread,
  2008     // we must take care to avoid dispatching a transaction that has already
  2009     // been canceled (see bug 190001).
  2010     if (NS_FAILED(trans->Status())) {
  2011         LOG(("  transaction was canceled... dropping event!\n"));
  2012         return NS_OK;
  2015     trans->SetPendingTime();
  2017     nsresult rv = NS_OK;
  2018     nsHttpConnectionInfo *ci = trans->ConnectionInfo();
  2019     MOZ_ASSERT(ci);
  2021     nsConnectionEntry *ent = GetOrCreateConnectionEntry(ci);
  2023     // SPDY coalescing of hostnames means we might redirect from this
  2024     // connection entry onto the preferred one.
  2025     nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
  2026     if (preferredEntry && (preferredEntry != ent)) {
  2027         LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
  2028              "redirected via coalescing from %s to %s\n", trans,
  2029              ent->mConnInfo->Host(), preferredEntry->mConnInfo->Host()));
  2031         ent = preferredEntry;
  2034     ReportProxyTelemetry(ent);
  2036     // Check if the transaction already has a sticky reference to a connection.
  2037     // If so, then we can just use it directly by transferring its reference
  2038     // to the new connection variable instead of searching for a new one
  2040     nsAHttpConnection *wrappedConnection = trans->Connection();
  2041     nsRefPtr<nsHttpConnection> conn;
  2042     if (wrappedConnection)
  2043         conn = dont_AddRef(wrappedConnection->TakeHttpConnection());
  2045     if (conn) {
  2046         MOZ_ASSERT(trans->Caps() & NS_HTTP_STICKY_CONNECTION);
  2047         MOZ_ASSERT(((int32_t)ent->mActiveConns.IndexOf(conn)) != -1,
  2048                    "Sticky Connection Not In Active List");
  2049         LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
  2050              "sticky connection=%p\n", trans, conn.get()));
  2051         trans->SetConnection(nullptr);
  2052 #ifdef WTF_TEST
  2053         fprintf(stderr, "WTF-bad: Sticky connection status on 1 transaction to host %s\n",
  2054                 ent->mConnInfo->Host());
  2055 #endif
  2056         rv = DispatchTransaction(ent, trans, conn);
  2057         return rv;
  2058     } else {
  2059         // XXX: maybe check the queue first and directly call TryDispatch?
  2060         InsertTransactionSorted(ent->mPendingQ, trans);
  2061         NS_ADDREF(trans);
  2062         ProcessPendingQForEntry(ent, true);
  2063         return NS_OK;
  2068 void
  2069 nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
  2070                                    nsConnectionEntry *ent)
  2072     NS_ADDREF(conn);
  2073     ent->mActiveConns.AppendElement(conn);
  2074     mNumActiveConns++;
  2075     ActivateTimeoutTick();
  2078 void
  2079 nsHttpConnectionMgr::DecrementActiveConnCount(nsHttpConnection *conn)
  2081     mNumActiveConns--;
  2082     if (conn->EverUsedSpdy())
  2083         mNumSpdyActiveConns--;
  2086 void
  2087 nsHttpConnectionMgr::StartedConnect()
  2089     mNumActiveConns++;
  2090     ActivateTimeoutTick(); // likely disabled by RecvdConnect()
  2093 void
  2094 nsHttpConnectionMgr::RecvdConnect()
  2096     mNumActiveConns--;
  2097     ConditionallyStopTimeoutTick();
  2100 nsresult
  2101 nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
  2102                                      nsAHttpTransaction *trans,
  2103                                      uint32_t caps,
  2104                                      bool speculative)
  2106     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2108     nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans, caps);
  2109     if (speculative)
  2110         sock->SetSpeculative(true);
  2111     nsresult rv = sock->SetupPrimaryStreams();
  2112     NS_ENSURE_SUCCESS(rv, rv);
  2114     ent->mHalfOpens.AppendElement(sock);
  2115     mNumHalfOpenConns++;
  2116     return NS_OK;
  2119 // This function tries to dispatch the pending spdy transactions on
  2120 // the connection entry sent in as an argument. It will do so on the
  2121 // active spdy connection either in that same entry or in the
  2122 // redirected 'preferred' entry for the same coalescing hash key if
  2123 // coalescing is enabled.
  2125 void
  2126 nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
  2128     nsHttpConnection *conn = GetSpdyPreferredConn(ent);
  2129     if (!conn || !conn->CanDirectlyActivate())
  2130         return;
  2132     nsTArray<nsHttpTransaction*> leftovers;
  2133     uint32_t index;
  2135     // Dispatch all the transactions we can
  2136     for (index = 0;
  2137          index < ent->mPendingQ.Length() && conn->CanDirectlyActivate();
  2138          ++index) {
  2139         nsHttpTransaction *trans = ent->mPendingQ[index];
  2141         if (!(trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
  2142             trans->Caps() & NS_HTTP_DISALLOW_SPDY) {
  2143             leftovers.AppendElement(trans);
  2144             continue;
  2147         nsresult rv = DispatchTransaction(ent, trans, conn);
  2148         if (NS_FAILED(rv)) {
  2149             // this cannot happen, but if due to some bug it does then
  2150             // close the transaction
  2151             MOZ_ASSERT(false, "Dispatch SPDY Transaction");
  2152             LOG(("ProcessSpdyPendingQ Dispatch Transaction failed trans=%p\n",
  2153                     trans));
  2154             trans->Close(rv);
  2156         NS_RELEASE(trans);
  2159     // Slurp up the rest of the pending queue into our leftovers bucket (we
  2160     // might have some left if conn->CanDirectlyActivate returned false)
  2161     for (; index < ent->mPendingQ.Length(); ++index) {
  2162         nsHttpTransaction *trans = ent->mPendingQ[index];
  2163         leftovers.AppendElement(trans);
  2166     // Put the leftovers back in the pending queue and get rid of the
  2167     // transactions we dispatched
  2168     leftovers.SwapElements(ent->mPendingQ);
  2169     leftovers.Clear();
  2172 PLDHashOperator
  2173 nsHttpConnectionMgr::ProcessSpdyPendingQCB(const nsACString &key,
  2174                                            nsAutoPtr<nsConnectionEntry> &ent,
  2175                                            void *closure)
  2177     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
  2178     self->ProcessSpdyPendingQ(ent);
  2179     return PL_DHASH_NEXT;
  2182 void
  2183 nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ(int32_t, void *)
  2185     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2186     LOG(("nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ\n"));
  2187     mCT.Enumerate(ProcessSpdyPendingQCB, this);
  2190 nsHttpConnection *
  2191 nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
  2193     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2194     MOZ_ASSERT(ent);
  2196     nsConnectionEntry *preferred = GetSpdyPreferredEnt(ent);
  2198     // this entry is spdy-enabled if it is involved in a redirect
  2199     if (preferred)
  2200         // all new connections for this entry will use spdy too
  2201         ent->mUsingSpdy = true;
  2202     else
  2203         preferred = ent;
  2205     nsHttpConnection *conn = nullptr;
  2207     if (preferred->mUsingSpdy) {
  2208         for (uint32_t index = 0;
  2209              index < preferred->mActiveConns.Length();
  2210              ++index) {
  2211             if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
  2212                 conn = preferred->mActiveConns[index];
  2213                 break;
  2218     return conn;
  2221 //-----------------------------------------------------------------------------
  2223 void
  2224 nsHttpConnectionMgr::OnMsgShutdown(int32_t, void *param)
  2226     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2227     LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
  2229     mCT.Enumerate(ShutdownPassCB, this);
  2231     if (mTimeoutTick) {
  2232         mTimeoutTick->Cancel();
  2233         mTimeoutTick = nullptr;
  2234         mTimeoutTickArmed = false;
  2236     if (mTimer) {
  2237       mTimer->Cancel();
  2238       mTimer = nullptr;
  2241     // signal shutdown complete
  2242     nsRefPtr<nsIRunnable> runnable =
  2243         new nsConnEvent(this, &nsHttpConnectionMgr::OnMsgShutdownConfirm,
  2244                         0, param);
  2245     NS_DispatchToMainThread(runnable);
  2248 void
  2249 nsHttpConnectionMgr::OnMsgShutdownConfirm(int32_t priority, void *param)
  2251     MOZ_ASSERT(NS_IsMainThread());
  2252     LOG(("nsHttpConnectionMgr::OnMsgShutdownConfirm\n"));
  2254     bool *shutdown = static_cast<bool*>(param);
  2255     *shutdown = true;
  2258 void
  2259 nsHttpConnectionMgr::OnMsgNewTransaction(int32_t priority, void *param)
  2261     LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
  2263     nsHttpTransaction *trans = (nsHttpTransaction *) param;
  2264     trans->SetPriority(priority);
  2265     nsresult rv = ProcessNewTransaction(trans);
  2266     if (NS_FAILED(rv))
  2267         trans->Close(rv); // for whatever its worth
  2268     NS_RELEASE(trans);
  2271 void
  2272 nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, void *param)
  2274     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2275     LOG(("nsHttpConnectionMgr::OnMsgReschedTransaction [trans=%p]\n", param));
  2277     nsHttpTransaction *trans = (nsHttpTransaction *) param;
  2278     trans->SetPriority(priority);
  2280     nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
  2281                                                    nullptr, trans);
  2283     if (ent) {
  2284         int32_t index = ent->mPendingQ.IndexOf(trans);
  2285         if (index >= 0) {
  2286             ent->mPendingQ.RemoveElementAt(index);
  2287             InsertTransactionSorted(ent->mPendingQ, trans);
  2291     NS_RELEASE(trans);
  2294 void
  2295 nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, void *param)
  2297     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2298     LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
  2300     nsresult closeCode = static_cast<nsresult>(reason);
  2301     nsHttpTransaction *trans = (nsHttpTransaction *) param;
  2302     //
  2303     // if the transaction owns a connection and the transaction is not done,
  2304     // then ask the connection to close the transaction.  otherwise, close the
  2305     // transaction directly (removing it from the pending queue first).
  2306     //
  2307     nsAHttpConnection *conn = trans->Connection();
  2308     if (conn && !trans->IsDone()) {
  2309         conn->CloseTransaction(trans, closeCode);
  2310     } else {
  2311         nsConnectionEntry *ent =
  2312             LookupConnectionEntry(trans->ConnectionInfo(), nullptr, trans);
  2314         if (ent) {
  2315             int32_t index = ent->mPendingQ.IndexOf(trans);
  2316             if (index >= 0) {
  2317                 LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]"
  2318                      " found in pending queue\n", trans));
  2319                 ent->mPendingQ.RemoveElementAt(index);
  2320                 nsHttpTransaction *temp = trans;
  2321                 NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
  2324         trans->Close(closeCode);
  2326         // Cancel is a pretty strong signal that things might be hanging
  2327         // so we want to cancel any null transactions related to this connection
  2328         // entry. They are just optimizations, but they aren't hooked up to
  2329         // anything that might get canceled from the rest of gecko, so best
  2330         // to assume that's what was meant by the cancel we did receive if
  2331         // it only applied to something in the queue.
  2332         for (uint32_t index = 0;
  2333              ent && (index < ent->mActiveConns.Length());
  2334              ++index) {
  2335             nsHttpConnection *activeConn = ent->mActiveConns[index];
  2336             nsAHttpTransaction *liveTransaction = activeConn->Transaction();
  2337             if (liveTransaction && liveTransaction->IsNullTransaction()) {
  2338                 LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p] "
  2339                      "also canceling Null Transaction %p on conn %p\n",
  2340                      trans, liveTransaction, activeConn));
  2341                 activeConn->CloseTransaction(liveTransaction, closeCode);
  2345     NS_RELEASE(trans);
  2348 void
  2349 nsHttpConnectionMgr::OnMsgProcessPendingQ(int32_t, void *param)
  2351     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2352     nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param;
  2354     if (!ci) {
  2355         LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=nullptr]\n"));
  2356         // Try and dispatch everything
  2357         mCT.Enumerate(ProcessAllTransactionsCB, this);
  2358         return;
  2361     LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n",
  2362          ci->HashKey().get()));
  2364     // start by processing the queue identified by the given connection info.
  2365     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
  2366     if (!(ent && ProcessPendingQForEntry(ent, false))) {
  2367         // if we reach here, it means that we couldn't dispatch a transaction
  2368         // for the specified connection info.  walk the connection table...
  2369         mCT.Enumerate(ProcessOneTransactionCB, this);
  2372     NS_RELEASE(ci);
  2375 void
  2376 nsHttpConnectionMgr::OnMsgPruneDeadConnections(int32_t, void *)
  2378     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2379     LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
  2381     // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
  2382     mTimeOfNextWakeUp = UINT64_MAX;
  2384     // check canreuse() for all idle connections plus any active connections on
  2385     // connection entries that are using spdy.
  2386     if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
  2387         mCT.Enumerate(PruneDeadConnectionsCB, this);
  2390 void
  2391 nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup(int32_t, void *param)
  2393     LOG(("nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup\n"));
  2394     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2396     nsRefPtr<nsHttpConnectionInfo> ci =
  2397         dont_AddRef(static_cast<nsHttpConnectionInfo *>(param));
  2399     mCT.Enumerate(ClosePersistentConnectionsCB, this);
  2400     if (ci)
  2401         ResetIPFamilyPreference(ci);
  2404 void
  2405 nsHttpConnectionMgr::OnMsgReclaimConnection(int32_t, void *param)
  2407     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2408     LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
  2410     nsHttpConnection *conn = (nsHttpConnection *) param;
  2412     //
  2413     // 1) remove the connection from the active list
  2414     // 2) if keep-alive, add connection to idle list
  2415     // 3) post event to process the pending transaction queue
  2416     //
  2418     nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
  2419                                                    conn, nullptr);
  2420     nsHttpConnectionInfo *ci = nullptr;
  2422     if (!ent) {
  2423         // this should never happen
  2424         LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection ent == null\n"));
  2425         MOZ_ASSERT(false, "no connection entry");
  2426         NS_ADDREF(ci = conn->ConnectionInfo());
  2428     else {
  2429         NS_ADDREF(ci = ent->mConnInfo);
  2431         // If the connection is in the active list, remove that entry
  2432         // and the reference held by the mActiveConns list.
  2433         // This is never the final reference on conn as the event context
  2434         // is also holding one that is released at the end of this function.
  2436         if (ent->mUsingSpdy) {
  2437             // Spdy connections aren't reused in the traditional HTTP way in
  2438             // the idleconns list, they are actively multplexed as active
  2439             // conns. Even when they have 0 transactions on them they are
  2440             // considered active connections. So when one is reclaimed it
  2441             // is really complete and is meant to be shut down and not
  2442             // reused.
  2443             conn->DontReuse();
  2446         if (ent->mActiveConns.RemoveElement(conn)) {
  2447             if (conn == ent->mYellowConnection)
  2448                 ent->OnYellowComplete();
  2449             nsHttpConnection *temp = conn;
  2450             NS_RELEASE(temp);
  2451             DecrementActiveConnCount(conn);
  2452             ConditionallyStopTimeoutTick();
  2455         if (conn->CanReuse()) {
  2456             LOG(("  adding connection to idle list\n"));
  2457             // Keep The idle connection list sorted with the connections that
  2458             // have moved the largest data pipelines at the front because these
  2459             // connections have the largest cwnds on the server.
  2461             // The linear search is ok here because the number of idleconns
  2462             // in a single entry is generally limited to a small number (i.e. 6)
  2464             uint32_t idx;
  2465             for (idx = 0; idx < ent->mIdleConns.Length(); idx++) {
  2466                 nsHttpConnection *idleConn = ent->mIdleConns[idx];
  2467                 if (idleConn->MaxBytesRead() < conn->MaxBytesRead())
  2468                     break;
  2471             NS_ADDREF(conn);
  2472             ent->mIdleConns.InsertElementAt(idx, conn);
  2473             mNumIdleConns++;
  2474             conn->BeginIdleMonitoring();
  2476             // If the added connection was first idle connection or has shortest
  2477             // time to live among the watched connections, pruning dead
  2478             // connections needs to be done when it can't be reused anymore.
  2479             uint32_t timeToLive = conn->TimeToLive();
  2480             if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
  2481                 PruneDeadConnectionsAfter(timeToLive);
  2483         else {
  2484             LOG(("  connection cannot be reused; closing connection\n"));
  2485             conn->Close(NS_ERROR_ABORT);
  2489     OnMsgProcessPendingQ(0, ci); // releases |ci|
  2490     NS_RELEASE(conn);
  2493 void
  2494 nsHttpConnectionMgr::OnMsgCompleteUpgrade(int32_t, void *param)
  2496     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2497     nsCompleteUpgradeData *data = (nsCompleteUpgradeData *) param;
  2498     LOG(("nsHttpConnectionMgr::OnMsgCompleteUpgrade "
  2499          "this=%p conn=%p listener=%p\n", this, data->mConn.get(),
  2500          data->mUpgradeListener.get()));
  2502     nsCOMPtr<nsISocketTransport> socketTransport;
  2503     nsCOMPtr<nsIAsyncInputStream> socketIn;
  2504     nsCOMPtr<nsIAsyncOutputStream> socketOut;
  2506     nsresult rv;
  2507     rv = data->mConn->TakeTransport(getter_AddRefs(socketTransport),
  2508                                     getter_AddRefs(socketIn),
  2509                                     getter_AddRefs(socketOut));
  2511     if (NS_SUCCEEDED(rv))
  2512         data->mUpgradeListener->OnTransportAvailable(socketTransport,
  2513                                                      socketIn,
  2514                                                      socketOut);
  2515     delete data;
  2518 void
  2519 nsHttpConnectionMgr::OnMsgUpdateParam(int32_t, void *param)
  2521     uint16_t name  = (NS_PTR_TO_INT32(param) & 0xFFFF0000) >> 16;
  2522     uint16_t value =  NS_PTR_TO_INT32(param) & 0x0000FFFF;
  2524     switch (name) {
  2525     case MAX_CONNECTIONS:
  2526         mMaxConns = value;
  2527         break;
  2528     case MAX_PERSISTENT_CONNECTIONS_PER_HOST:
  2529         mMaxPersistConnsPerHost = value;
  2530         break;
  2531     case MAX_PERSISTENT_CONNECTIONS_PER_PROXY:
  2532         mMaxPersistConnsPerProxy = value;
  2533         break;
  2534     case MAX_REQUEST_DELAY:
  2535         mMaxRequestDelay = value;
  2536         break;
  2537     case MAX_PIPELINED_REQUESTS:
  2538         mMaxPipelinedRequests = value;
  2539         break;
  2540     case MAX_OPTIMISTIC_PIPELINED_REQUESTS:
  2541         mMaxOptimisticPipelinedRequests = value;
  2542         break;
  2543     default:
  2544         NS_NOTREACHED("unexpected parameter name");
  2548 // nsHttpConnectionMgr::nsConnectionEntry
  2549 nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
  2551     if (mSpdyPreferred)
  2552         gHttpHandler->ConnMgr()->RemoveSpdyPreferredEnt(mCoalescingKey);
  2554     NS_RELEASE(mConnInfo);
  2557 void
  2558 nsHttpConnectionMgr::OnMsgProcessFeedback(int32_t, void *param)
  2560     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2561     nsHttpPipelineFeedback *fb = (nsHttpPipelineFeedback *)param;
  2563     PipelineFeedbackInfo(fb->mConnInfo, fb->mInfo, fb->mConn, fb->mData);
  2564     delete fb;
  2567 // Read Timeout Tick handlers
  2569 void
  2570 nsHttpConnectionMgr::ActivateTimeoutTick()
  2572     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2573     LOG(("nsHttpConnectionMgr::ActivateTimeoutTick() "
  2574          "this=%p mTimeoutTick=%p\n"));
  2576     // The timer tick should be enabled if it is not already pending.
  2577     // Upon running the tick will rearm itself if there are active
  2578     // connections available.
  2580     if (mTimeoutTick && mTimeoutTickArmed) {
  2581         // make sure we get one iteration on a quick tick
  2582         if (mTimeoutTickNext > 1) {
  2583             mTimeoutTickNext = 1;
  2584             mTimeoutTick->SetDelay(1000);
  2586         return;
  2589     if (!mTimeoutTick) {
  2590         mTimeoutTick = do_CreateInstance(NS_TIMER_CONTRACTID);
  2591         if (!mTimeoutTick) {
  2592             NS_WARNING("failed to create timer for http timeout management");
  2593             return;
  2595         mTimeoutTick->SetTarget(mSocketThreadTarget);
  2598     MOZ_ASSERT(!mTimeoutTickArmed, "timer tick armed");
  2599     mTimeoutTickArmed = true;
  2600     mTimeoutTick->Init(this, 1000, nsITimer::TYPE_REPEATING_SLACK);
  2603 void
  2604 nsHttpConnectionMgr::TimeoutTick()
  2606     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2607     MOZ_ASSERT(mTimeoutTick, "no readtimeout tick");
  2609     LOG(("nsHttpConnectionMgr::TimeoutTick active=%d\n", mNumActiveConns));
  2610     // The next tick will be between 1 second and 1 hr
  2611     // Set it to the max value here, and the TimeoutTickCB()s can
  2612     // reduce it to their local needs.
  2613     mTimeoutTickNext = 3600; // 1hr
  2614     mCT.Enumerate(TimeoutTickCB, this);
  2615     if (mTimeoutTick) {
  2616         mTimeoutTickNext = std::max(mTimeoutTickNext, 1U);
  2617         mTimeoutTick->SetDelay(mTimeoutTickNext * 1000);
  2621 PLDHashOperator
  2622 nsHttpConnectionMgr::TimeoutTickCB(const nsACString &key,
  2623                                        nsAutoPtr<nsConnectionEntry> &ent,
  2624                                        void *closure)
  2626     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
  2628     LOG(("nsHttpConnectionMgr::TimeoutTickCB() this=%p host=%s "
  2629          "idle=%d active=%d half-len=%d pending=%d\n",
  2630          self, ent->mConnInfo->Host(), ent->mIdleConns.Length(),
  2631          ent->mActiveConns.Length(), ent->mHalfOpens.Length(),
  2632          ent->mPendingQ.Length()));
  2634     // first call the tick handler for each active connection
  2635     PRIntervalTime now = PR_IntervalNow();
  2636     for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
  2637         uint32_t connNextTimeout =  ent->mActiveConns[index]->ReadTimeoutTick(now);
  2638         self->mTimeoutTickNext = std::min(self->mTimeoutTickNext, connNextTimeout);
  2641     // now check for any stalled half open sockets
  2642     if (ent->mHalfOpens.Length()) {
  2643         TimeStamp now = TimeStamp::Now();
  2644         double maxConnectTime = gHttpHandler->ConnectTimeout();  /* in milliseconds */
  2646         for (uint32_t index = ent->mHalfOpens.Length(); index > 0; ) {
  2647             index--;
  2649             nsHalfOpenSocket *half = ent->mHalfOpens[index];
  2650             double delta = half->Duration(now);
  2651             // If the socket has timed out, close it so the waiting transaction
  2652             // will get the proper signal
  2653             if (delta > maxConnectTime) {
  2654                 LOG(("Force timeout of half open to %s after %.2fms.\n",
  2655                      ent->mConnInfo->HashKey().get(), delta));
  2656                 if (half->SocketTransport())
  2657                     half->SocketTransport()->Close(NS_ERROR_ABORT);
  2658                 if (half->BackupTransport())
  2659                     half->BackupTransport()->Close(NS_ERROR_ABORT);
  2662             // If this half open hangs around for 5 seconds after we've closed() it
  2663             // then just abandon the socket.
  2664             if (delta > maxConnectTime + 5000) {
  2665                 LOG(("Abandon half open to %s after %.2fms.\n",
  2666                      ent->mConnInfo->HashKey().get(), delta));
  2667                 half->Abandon();
  2671     if (ent->mHalfOpens.Length()) {
  2672         self->mTimeoutTickNext = 1;
  2674     return PL_DHASH_NEXT;
  2677 //-----------------------------------------------------------------------------
  2678 // nsHttpConnectionMgr::nsConnectionHandle
  2680 nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
  2682     if (mConn) {
  2683         gHttpHandler->ReclaimConnection(mConn);
  2684         NS_RELEASE(mConn);
  2688 NS_IMPL_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
  2690 nsHttpConnectionMgr::nsConnectionEntry *
  2691 nsHttpConnectionMgr::GetOrCreateConnectionEntry(nsHttpConnectionInfo *ci)
  2693     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
  2694     if (ent)
  2695         return ent;
  2697     nsHttpConnectionInfo *clone = ci->Clone();
  2698     ent = new nsConnectionEntry(clone);
  2699     mCT.Put(ci->HashKey(), ent);
  2700     return ent;
  2703 nsresult
  2704 nsHttpConnectionMgr::nsConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
  2705                                                             nsHttpRequestHead *req,
  2706                                                             nsHttpResponseHead *resp,
  2707                                                             bool *reset)
  2709     return mConn->OnHeadersAvailable(trans, req, resp, reset);
  2712 void
  2713 nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
  2715     mConn->CloseTransaction(trans, reason);
  2718 nsresult
  2719 nsHttpConnectionMgr::
  2720 nsConnectionHandle::TakeTransport(nsISocketTransport  **aTransport,
  2721                                   nsIAsyncInputStream **aInputStream,
  2722                                   nsIAsyncOutputStream **aOutputStream)
  2724     return mConn->TakeTransport(aTransport, aInputStream, aOutputStream);
  2727 void
  2728 nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param)
  2730     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2732     nsRefPtr<SpeculativeConnectArgs> args =
  2733         dont_AddRef(static_cast<SpeculativeConnectArgs *>(param));
  2735     LOG(("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s]\n",
  2736          args->mTrans->ConnectionInfo()->HashKey().get()));
  2738     nsConnectionEntry *ent =
  2739         GetOrCreateConnectionEntry(args->mTrans->ConnectionInfo());
  2741     // If spdy has previously made a preferred entry for this host via
  2742     // the ip pooling rules. If so, connect to the preferred host instead of
  2743     // the one directly passed in here.
  2744     nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
  2745     if (preferredEntry)
  2746         ent = preferredEntry;
  2748     uint32_t parallelSpeculativeConnectLimit =
  2749         gHttpHandler->ParallelSpeculativeConnectLimit();
  2750     bool ignorePossibleSpdyConnections = false;
  2751     bool ignoreIdle = false;
  2753     if (args->mOverridesOK) {
  2754         parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit;
  2755         ignorePossibleSpdyConnections = args->mIgnorePossibleSpdyConnections;
  2756         ignoreIdle = args->mIgnoreIdle;
  2759     if (ent->SupportsPipelining()) {
  2760         /* Only speculative connect if we're not pipelining and have no other pending
  2761          * unconnected half-opens.. */
  2762         if (ent->UnconnectedHalfOpens() == 0 && ent->mIdleConns.Length() == 0
  2763                 && !RestrictConnections(ent) && !HasPipelines(ent)
  2764                 && !AtActiveConnectionLimit(ent, args->mTrans->Caps())) {
  2765 #ifdef WTF_DEBUG
  2766             fprintf(stderr, "WTF: Creating speculative connection because we have no pipelines\n");
  2767 #endif
  2768             CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true);
  2770     } else if (mNumHalfOpenConns < parallelSpeculativeConnectLimit &&
  2771         ((ignoreIdle && (ent->mIdleConns.Length() < parallelSpeculativeConnectLimit)) ||
  2772          !ent->mIdleConns.Length()) &&
  2773         !RestrictConnections(ent, ignorePossibleSpdyConnections) &&
  2774         !AtActiveConnectionLimit(ent, args->mTrans->Caps())) {
  2775 #ifdef WTF_DEBUG
  2776             fprintf(stderr, "WTF: Creating speculative connection because we can't pipeline\n");
  2777 #endif
  2778         CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true);
  2779     } else {
  2780         LOG(("  Transport not created due to existing connection count\n"));
  2784 bool
  2785 nsHttpConnectionMgr::HasPipelines(nsConnectionEntry *ent)
  2787     uint32_t activeCount = ent->mActiveConns.Length();
  2789     if (!ent->SupportsPipelining()) {
  2790         return false;
  2793     for (uint32_t i = 0; i < activeCount; ++i) {
  2794         nsHttpConnection *conn = ent->mActiveConns[i];
  2795         if (!conn->SupportsPipelining())
  2796             continue;
  2798         nsAHttpTransaction *activeTrans = conn->Transaction();
  2800         if (activeTrans && !activeTrans->IsDone() &&
  2801             !NS_FAILED(activeTrans->Status()))
  2802             return true;
  2804     return false;
  2807 bool
  2808 nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
  2810     return mConn->IsPersistent();
  2813 bool
  2814 nsHttpConnectionMgr::nsConnectionHandle::IsReused()
  2816     return mConn->IsReused();
  2819 void
  2820 nsHttpConnectionMgr::nsConnectionHandle::DontReuse()
  2822     mConn->DontReuse();
  2825 nsresult
  2826 nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, uint32_t bufLen)
  2828     return mConn->PushBack(buf, bufLen);
  2832 //////////////////////// nsHalfOpenSocket
  2835 NS_IMPL_ISUPPORTS(nsHttpConnectionMgr::nsHalfOpenSocket,
  2836                   nsIOutputStreamCallback,
  2837                   nsITransportEventSink,
  2838                   nsIInterfaceRequestor,
  2839                   nsITimerCallback)
  2841 nsHttpConnectionMgr::
  2842 nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
  2843                                    nsAHttpTransaction *trans,
  2844                                    uint32_t caps)
  2845     : mEnt(ent),
  2846       mTransaction(trans),
  2847       mCaps(caps),
  2848       mSpeculative(false),
  2849       mHasConnected(false)
  2851     MOZ_ASSERT(ent && trans, "constructor with null arguments");
  2852     LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s]\n",
  2853          this, trans, ent->mConnInfo->Host()));
  2856 nsHttpConnectionMgr::nsHalfOpenSocket::~nsHalfOpenSocket()
  2858     MOZ_ASSERT(!mStreamOut);
  2859     MOZ_ASSERT(!mBackupStreamOut);
  2860     MOZ_ASSERT(!mSynTimer);
  2861     LOG(("Destroying nsHalfOpenSocket [this=%p]\n", this));
  2863     if (mEnt)
  2864         mEnt->RemoveHalfOpen(this);
  2867 nsresult
  2868 nsHttpConnectionMgr::
  2869 nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
  2870                                nsIAsyncInputStream **instream,
  2871                                nsIAsyncOutputStream **outstream,
  2872                                bool isBackup)
  2874     nsresult rv;
  2876     const char* types[1];
  2877     types[0] = (mEnt->mConnInfo->UsingSSL()) ?
  2878         "ssl" : gHttpHandler->DefaultSocketType();
  2879     uint32_t typeCount = (types[0] != nullptr);
  2881     nsCOMPtr<nsISocketTransport> socketTransport;
  2882     nsCOMPtr<nsISocketTransportService> sts;
  2884     sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
  2885     NS_ENSURE_SUCCESS(rv, rv);
  2887     rv = sts->CreateTransport(types, typeCount,
  2888                               nsDependentCString(mEnt->mConnInfo->Host()),
  2889                               mEnt->mConnInfo->Port(),
  2890                               mEnt->mConnInfo->ProxyInfo(),
  2891                               getter_AddRefs(socketTransport));
  2892     NS_ENSURE_SUCCESS(rv, rv);
  2894     uint32_t tmpFlags = 0;
  2895     if (mCaps & NS_HTTP_REFRESH_DNS)
  2896         tmpFlags = nsISocketTransport::BYPASS_CACHE;
  2898     if (mCaps & NS_HTTP_LOAD_ANONYMOUS)
  2899         tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
  2901     if (mEnt->mConnInfo->GetPrivate())
  2902         tmpFlags |= nsISocketTransport::NO_PERMANENT_STORAGE;
  2904     // For backup connections, we disable IPv6. That's because some users have
  2905     // broken IPv6 connectivity (leading to very long timeouts), and disabling
  2906     // IPv6 on the backup connection gives them a much better user experience
  2907     // with dual-stack hosts, though they still pay the 250ms delay for each new
  2908     // connection. This strategy is also known as "happy eyeballs".
  2909     if (mEnt->mPreferIPv6) {
  2910         tmpFlags |= nsISocketTransport::DISABLE_IPV4;
  2912     else if (mEnt->mPreferIPv4 ||
  2913              (isBackup && gHttpHandler->FastFallbackToIPv4())) {
  2914         tmpFlags |= nsISocketTransport::DISABLE_IPV6;
  2917     if (IsSpeculative()) {
  2918         tmpFlags |= nsISocketTransport::DISABLE_RFC1918;
  2921     socketTransport->SetConnectionFlags(tmpFlags);
  2923     socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
  2925     rv = socketTransport->SetEventSink(this, nullptr);
  2926     NS_ENSURE_SUCCESS(rv, rv);
  2928     rv = socketTransport->SetSecurityCallbacks(this);
  2929     NS_ENSURE_SUCCESS(rv, rv);
  2931     nsCOMPtr<nsIOutputStream> sout;
  2932     rv = socketTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,
  2933                                             0, 0,
  2934                                             getter_AddRefs(sout));
  2935     NS_ENSURE_SUCCESS(rv, rv);
  2937     nsCOMPtr<nsIInputStream> sin;
  2938     rv = socketTransport->OpenInputStream(nsITransport::OPEN_UNBUFFERED,
  2939                                            0, 0,
  2940                                            getter_AddRefs(sin));
  2941     NS_ENSURE_SUCCESS(rv, rv);
  2943     socketTransport.forget(transport);
  2944     CallQueryInterface(sin, instream);
  2945     CallQueryInterface(sout, outstream);
  2947     rv = (*outstream)->AsyncWait(this, 0, 0, nullptr);
  2948     if (NS_SUCCEEDED(rv))
  2949         gHttpHandler->ConnMgr()->StartedConnect();
  2951     return rv;
  2954 nsresult
  2955 nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams()
  2957     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  2959     nsresult rv;
  2961     mPrimarySynStarted = TimeStamp::Now();
  2962     rv = SetupStreams(getter_AddRefs(mSocketTransport),
  2963                       getter_AddRefs(mStreamIn),
  2964                       getter_AddRefs(mStreamOut),
  2965                       false);
  2966     LOG(("nsHalfOpenSocket::SetupPrimaryStream [this=%p ent=%s rv=%x]",
  2967          this, mEnt->mConnInfo->Host(), rv));
  2968     if (NS_FAILED(rv)) {
  2969         if (mStreamOut)
  2970             mStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
  2971         mStreamOut = nullptr;
  2972         mStreamIn = nullptr;
  2973         mSocketTransport = nullptr;
  2975     return rv;
  2978 nsresult
  2979 nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams()
  2981     mBackupSynStarted = TimeStamp::Now();
  2982     nsresult rv = SetupStreams(getter_AddRefs(mBackupTransport),
  2983                                getter_AddRefs(mBackupStreamIn),
  2984                                getter_AddRefs(mBackupStreamOut),
  2985                                true);
  2986     LOG(("nsHalfOpenSocket::SetupBackupStream [this=%p ent=%s rv=%x]",
  2987          this, mEnt->mConnInfo->Host(), rv));
  2988     if (NS_FAILED(rv)) {
  2989         if (mBackupStreamOut)
  2990             mBackupStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
  2991         mBackupStreamOut = nullptr;
  2992         mBackupStreamIn = nullptr;
  2993         mBackupTransport = nullptr;
  2995     return rv;
  2998 void
  2999 nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
  3001     uint16_t timeout = gHttpHandler->GetIdleSynTimeout();
  3002     MOZ_ASSERT(!mSynTimer, "timer already initd");
  3004     if (timeout && !mTransaction->IsDone()) {
  3005         // Setup the timer that will establish a backup socket
  3006         // if we do not get a writable event on the main one.
  3007         // We do this because a lost SYN takes a very long time
  3008         // to repair at the TCP level.
  3009         //
  3010         // Failure to setup the timer is something we can live with,
  3011         // so don't return an error in that case.
  3012         nsresult rv;
  3013         mSynTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
  3014         if (NS_SUCCEEDED(rv)) {
  3015             mSynTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
  3016             LOG(("nsHalfOpenSocket::SetupBackupTimer() [this=%p]", this));
  3019     else if (timeout) {
  3020         LOG(("nsHalfOpenSocket::SetupBackupTimer() [this=%p],"
  3021              " transaction already done!", this));
  3025 void
  3026 nsHttpConnectionMgr::nsHalfOpenSocket::CancelBackupTimer()
  3028     // If the syntimer is still armed, we can cancel it because no backup
  3029     // socket should be formed at this point
  3030     if (!mSynTimer)
  3031         return;
  3033     LOG(("nsHalfOpenSocket::CancelBackupTimer()"));
  3034     mSynTimer->Cancel();
  3035     mSynTimer = nullptr;
  3038 void
  3039 nsHttpConnectionMgr::nsHalfOpenSocket::Abandon()
  3041     LOG(("nsHalfOpenSocket::Abandon [this=%p ent=%s]",
  3042          this, mEnt->mConnInfo->Host()));
  3044     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  3046     nsRefPtr<nsHalfOpenSocket> deleteProtector(this);
  3048     // Tell socket (and backup socket) to forget the half open socket.
  3049     if (mSocketTransport) {
  3050         mSocketTransport->SetEventSink(nullptr, nullptr);
  3051         mSocketTransport->SetSecurityCallbacks(nullptr);
  3052         mSocketTransport = nullptr;
  3054     if (mBackupTransport) {
  3055         mBackupTransport->SetEventSink(nullptr, nullptr);
  3056         mBackupTransport->SetSecurityCallbacks(nullptr);
  3057         mBackupTransport = nullptr;
  3060     // Tell output stream (and backup) to forget the half open socket.
  3061     if (mStreamOut) {
  3062         gHttpHandler->ConnMgr()->RecvdConnect();
  3063         mStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
  3064         mStreamOut = nullptr;
  3066     if (mBackupStreamOut) {
  3067         gHttpHandler->ConnMgr()->RecvdConnect();
  3068         mBackupStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
  3069         mBackupStreamOut = nullptr;
  3072     // Lose references to input stream (and backup).
  3073     mStreamIn = mBackupStreamIn = nullptr;
  3075     // Stop the timer - we don't want any new backups.
  3076     CancelBackupTimer();
  3078     // Remove the half open from the connection entry.
  3079     if (mEnt)
  3080         mEnt->RemoveHalfOpen(this);
  3081     mEnt = nullptr;
  3084 double
  3085 nsHttpConnectionMgr::nsHalfOpenSocket::Duration(TimeStamp epoch)
  3087     if (mPrimarySynStarted.IsNull())
  3088         return 0;
  3090     return (epoch - mPrimarySynStarted).ToMilliseconds();
  3094 NS_IMETHODIMP // method for nsITimerCallback
  3095 nsHttpConnectionMgr::nsHalfOpenSocket::Notify(nsITimer *timer)
  3097     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  3098     MOZ_ASSERT(timer == mSynTimer, "wrong timer");
  3100     SetupBackupStreams();
  3102     mSynTimer = nullptr;
  3103     return NS_OK;
  3106 // method for nsIAsyncOutputStreamCallback
  3107 NS_IMETHODIMP
  3108 nsHttpConnectionMgr::
  3109 nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
  3111     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  3112     MOZ_ASSERT(out == mStreamOut || out == mBackupStreamOut,
  3113                "stream mismatch");
  3114     LOG(("nsHalfOpenSocket::OnOutputStreamReady [this=%p ent=%s %s]\n",
  3115          this, mEnt->mConnInfo->Host(),
  3116          out == mStreamOut ? "primary" : "backup"));
  3117     int32_t index;
  3118     nsresult rv;
  3120     gHttpHandler->ConnMgr()->RecvdConnect();
  3122     CancelBackupTimer();
  3124     // assign the new socket to the http connection
  3125     nsRefPtr<nsHttpConnection> conn = new nsHttpConnection();
  3126     LOG(("nsHalfOpenSocket::OnOutputStreamReady "
  3127          "Created new nshttpconnection %p\n", conn.get()));
  3129     // Some capabilities are needed before a transaciton actually gets
  3130     // scheduled (e.g. how to negotiate false start)
  3131     conn->SetTransactionCaps(mTransaction->Caps());
  3133     NetAddr peeraddr;
  3134     nsCOMPtr<nsIInterfaceRequestor> callbacks;
  3135     mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
  3136     if (out == mStreamOut) {
  3137         TimeDuration rtt = TimeStamp::Now() - mPrimarySynStarted;
  3138         rv = conn->Init(mEnt->mConnInfo,
  3139                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
  3140                         mSocketTransport, mStreamIn, mStreamOut,
  3141                         callbacks,
  3142                         PR_MillisecondsToInterval(
  3143                           static_cast<uint32_t>(rtt.ToMilliseconds())));
  3145         if (NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr)))
  3146             mEnt->RecordIPFamilyPreference(peeraddr.raw.family);
  3148         // The nsHttpConnection object now owns these streams and sockets
  3149         mStreamOut = nullptr;
  3150         mStreamIn = nullptr;
  3151         mSocketTransport = nullptr;
  3153     else {
  3154         TimeDuration rtt = TimeStamp::Now() - mBackupSynStarted;
  3155         rv = conn->Init(mEnt->mConnInfo,
  3156                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
  3157                         mBackupTransport, mBackupStreamIn, mBackupStreamOut,
  3158                         callbacks,
  3159                         PR_MillisecondsToInterval(
  3160                           static_cast<uint32_t>(rtt.ToMilliseconds())));
  3162         if (NS_SUCCEEDED(mBackupTransport->GetPeerAddr(&peeraddr)))
  3163             mEnt->RecordIPFamilyPreference(peeraddr.raw.family);
  3165         // The nsHttpConnection object now owns these streams and sockets
  3166         mBackupStreamOut = nullptr;
  3167         mBackupStreamIn = nullptr;
  3168         mBackupTransport = nullptr;
  3171     if (NS_FAILED(rv)) {
  3172         LOG(("nsHalfOpenSocket::OnOutputStreamReady "
  3173              "conn->init (%p) failed %x\n", conn.get(), rv));
  3174         return rv;
  3177     // This half-open socket has created a connection.  This flag excludes it
  3178     // from counter of actual connections used for checking limits.
  3179     mHasConnected = true;
  3181     // if this is still in the pending list, remove it and dispatch it
  3182     index = mEnt->mPendingQ.IndexOf(mTransaction);
  3183     if (index != -1) {
  3184         MOZ_ASSERT(!mSpeculative,
  3185                    "Speculative Half Open found mTranscation");
  3186         nsRefPtr<nsHttpTransaction> temp = dont_AddRef(mEnt->mPendingQ[index]);
  3187         mEnt->mPendingQ.RemoveElementAt(index);
  3188         gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
  3189 #ifdef WTF_DEBUG
  3190         fprintf(stderr, "WTF: Speculative half-opened connection is now ready for %s (pipelines %d)\n",
  3191                 mEnt->mConnInfo->Host(), mEnt->SupportsPipelining());
  3192 #endif
  3193         rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, temp, conn);
  3195     else {
  3196         // this transaction was dispatched off the pending q before all the
  3197         // sockets established themselves.
  3199         // After about 1 second allow for the possibility of restarting a
  3200         // transaction due to server close. Keep at sub 1 second as that is the
  3201         // minimum granularity we can expect a server to be timing out with.
  3202         conn->SetIsReusedAfter(950);
  3204         // if we are using ssl and no other transactions are waiting right now,
  3205         // then form a null transaction to drive the SSL handshake to
  3206         // completion. Afterwards the connection will be 100% ready for the next
  3207         // transaction to use it. Make an exception for SSL over HTTP proxy as the
  3208         // NullHttpTransaction does not know how to drive CONNECT.
  3209         if (mEnt->mConnInfo->UsingSSL() && !mEnt->mPendingQ.Length() &&
  3210             !mEnt->mConnInfo->UsingHttpProxy()) {
  3211             LOG(("nsHalfOpenSocket::OnOutputStreamReady null transaction will "
  3212                  "be used to finish SSL handshake on conn %p\n", conn.get()));
  3213             nsRefPtr<NullHttpTransaction>  trans =
  3214                 new NullHttpTransaction(mEnt->mConnInfo,
  3215                                         callbacks,
  3216                                         mCaps & ~NS_HTTP_ALLOW_PIPELINING);
  3218             gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
  3219             conn->Classify(nsAHttpTransaction::CLASS_SOLO);
  3220             rv = gHttpHandler->ConnMgr()->
  3221                 DispatchAbstractTransaction(mEnt, trans, mCaps, conn, 0);
  3223         else {
  3224             // otherwise just put this in the persistent connection pool
  3225             LOG(("nsHalfOpenSocket::OnOutputStreamReady no transaction match "
  3226                  "returning conn %p to pool\n", conn.get()));
  3227             nsRefPtr<nsHttpConnection> copy(conn);
  3228             // forget() to effectively addref because onmsg*() will drop a ref
  3229             gHttpHandler->ConnMgr()->OnMsgReclaimConnection(
  3230                 0, conn.forget().take());
  3234     return rv;
  3237 // method for nsITransportEventSink
  3238 NS_IMETHODIMP
  3239 nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
  3240                                                          nsresult status,
  3241                                                          uint64_t progress,
  3242                                                          uint64_t progressMax)
  3244     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  3246     if (mTransaction)
  3247         mTransaction->OnTransportStatus(trans, status, progress);
  3249     if (trans != mSocketTransport)
  3250         return NS_OK;
  3252     // if we are doing spdy coalescing and haven't recorded the ip address
  3253     // for this entry before then make the hash key if our dns lookup
  3254     // just completed. We can't do coalescing if using a proxy because the
  3255     // ip addresses are not available to the client.
  3257     if (status == NS_NET_STATUS_CONNECTED_TO &&
  3258         gHttpHandler->IsSpdyEnabled() &&
  3259         gHttpHandler->CoalesceSpdy() &&
  3260         mEnt && mEnt->mConnInfo && mEnt->mConnInfo->UsingSSL() &&
  3261         !mEnt->mConnInfo->UsingProxy() &&
  3262         mEnt->mCoalescingKey.IsEmpty()) {
  3264         NetAddr addr;
  3265         nsresult rv = mSocketTransport->GetPeerAddr(&addr);
  3266         if (NS_SUCCEEDED(rv)) {
  3267             mEnt->mCoalescingKey.SetCapacity(kIPv6CStrBufSize + 26);
  3268             NetAddrToString(&addr, mEnt->mCoalescingKey.BeginWriting(), kIPv6CStrBufSize);
  3269             mEnt->mCoalescingKey.SetLength(
  3270                 strlen(mEnt->mCoalescingKey.BeginReading()));
  3272             if (mEnt->mConnInfo->GetAnonymous())
  3273                 mEnt->mCoalescingKey.AppendLiteral("~A:");
  3274             else
  3275                 mEnt->mCoalescingKey.AppendLiteral("~.:");
  3276             mEnt->mCoalescingKey.AppendInt(mEnt->mConnInfo->Port());
  3278             LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus "
  3279                  "STATUS_CONNECTED_TO Established New Coalescing Key for host "
  3280                  "%s [%s]", mEnt->mConnInfo->Host(),
  3281                  mEnt->mCoalescingKey.get()));
  3283             gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(mEnt);
  3287     switch (status) {
  3288     case NS_NET_STATUS_CONNECTING_TO:
  3289         // Passed DNS resolution, now trying to connect, start the backup timer
  3290         // only prevent creating another backup transport.
  3291         // We also check for mEnt presence to not instantiate the timer after
  3292         // this half open socket has already been abandoned.  It may happen
  3293         // when we get this notification right between main-thread calls to
  3294         // nsHttpConnectionMgr::Shutdown and nsSocketTransportService::Shutdown
  3295         // where the first abandones all half open socket instances and only
  3296         // after that the second stops the socket thread.
  3297         if (mEnt && !mBackupTransport && !mSynTimer)
  3298             SetupBackupTimer();
  3299         break;
  3301     case NS_NET_STATUS_CONNECTED_TO:
  3302         // TCP connection's up, now transfer or SSL negotiantion starts,
  3303         // no need for backup socket
  3304         CancelBackupTimer();
  3305         break;
  3307     default:
  3308         break;
  3311     return NS_OK;
  3314 // method for nsIInterfaceRequestor
  3315 NS_IMETHODIMP
  3316 nsHttpConnectionMgr::nsHalfOpenSocket::GetInterface(const nsIID &iid,
  3317                                                     void **result)
  3319     if (mTransaction) {
  3320         nsCOMPtr<nsIInterfaceRequestor> callbacks;
  3321         mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
  3322         if (callbacks)
  3323             return callbacks->GetInterface(iid, result);
  3325     return NS_ERROR_NO_INTERFACE;
  3329 nsHttpConnection *
  3330 nsHttpConnectionMgr::nsConnectionHandle::TakeHttpConnection()
  3332     // return our connection object to the caller and clear it internally
  3333     // do not drop our reference - the caller now owns it.
  3335     MOZ_ASSERT(mConn);
  3336     nsHttpConnection *conn = mConn;
  3337     mConn = nullptr;
  3338     return conn;
  3341 uint32_t
  3342 nsHttpConnectionMgr::nsConnectionHandle::CancelPipeline(nsresult reason)
  3344     // no pipeline to cancel
  3345     return 0;
  3348 nsAHttpTransaction::Classifier
  3349 nsHttpConnectionMgr::nsConnectionHandle::Classification()
  3351     if (mConn)
  3352         return mConn->Classification();
  3354     LOG(("nsConnectionHandle::Classification this=%p "
  3355          "has null mConn using CLASS_SOLO default", this));
  3356     return nsAHttpTransaction::CLASS_SOLO;
  3359 // nsConnectionEntry
  3361 nsHttpConnectionMgr::
  3362 nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci)
  3363     : mConnInfo(ci)
  3364     , mPipelineState(PS_YELLOW)
  3365     , mYellowGoodEvents(0)
  3366     , mYellowBadEvents(0)
  3367     , mYellowConnection(nullptr)
  3368     , mGreenDepth(kPipelineOpen)
  3369     , mPipeliningPenalty(0)
  3370     , mSpdyCWND(0)
  3371     , mUsingSpdy(false)
  3372     , mTestedSpdy(false)
  3373     , mSpdyPreferred(false)
  3374     , mPreferIPv4(false)
  3375     , mPreferIPv6(false)
  3377     NS_ADDREF(mConnInfo);
  3379     // Randomize the pipeline depth (3..12)
  3380     mGreenDepth = gHttpHandler->GetMaxOptimisticPipelinedRequests()
  3381                   + rand() % (gHttpHandler->GetMaxPipelinedRequests()
  3382                               - gHttpHandler->GetMaxOptimisticPipelinedRequests());
  3384     if (gHttpHandler->GetPipelineAggressive()) {
  3385         mPipelineState = PS_GREEN;
  3388     mInitialGreenDepth = mGreenDepth;
  3389     memset(mPipeliningClassPenalty, 0, sizeof(int16_t) * nsAHttpTransaction::CLASS_MAX);
  3392 bool
  3393 nsHttpConnectionMgr::nsConnectionEntry::SupportsPipelining()
  3395     return mPipelineState != nsHttpConnectionMgr::PS_RED;
  3398 nsHttpConnectionMgr::PipeliningState
  3399 nsHttpConnectionMgr::nsConnectionEntry::PipelineState()
  3401     return mPipelineState;
  3404 void
  3405 nsHttpConnectionMgr::
  3406 nsConnectionEntry::OnPipelineFeedbackInfo(
  3407     nsHttpConnectionMgr::PipelineFeedbackInfoType info,
  3408     nsHttpConnection *conn,
  3409     uint32_t data)
  3411     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  3413     if (mPipelineState == PS_YELLOW) {
  3414         if (info & kPipelineInfoTypeBad)
  3415             mYellowBadEvents++;
  3416         else if (info & (kPipelineInfoTypeNeutral | kPipelineInfoTypeGood))
  3417             mYellowGoodEvents++;
  3420     if (mPipelineState == PS_GREEN && info == GoodCompletedOK) {
  3421         int32_t depth = data;
  3422         LOG(("Transaction completed at pipeline depth of %d. Host = %s\n",
  3423              depth, mConnInfo->Host()));
  3425         // Don't set this. We want to keep our initial random value..
  3426         //if (depth >= 3)
  3427         //    mGreenDepth = kPipelineUnlimited;
  3430     nsAHttpTransaction::Classifier classification;
  3431     if (conn)
  3432         classification = conn->Classification();
  3433     else if (info == BadInsufficientFraming ||
  3434              info == BadUnexpectedLarge)
  3435         classification = (nsAHttpTransaction::Classifier) data;
  3436     else
  3437         classification = nsAHttpTransaction::CLASS_SOLO;
  3439     if (gHttpHandler->GetPipelineAggressive() &&
  3440         info & kPipelineInfoTypeBad &&
  3441         info != BadExplicitClose &&
  3442         info != RedVersionTooLow &&
  3443         info != RedBannedServer &&
  3444         info != RedCorruptedContent &&
  3445         info != BadInsufficientFraming) {
  3446         LOG(("minor negative feedback ignored "
  3447              "because of pipeline aggressive mode"));
  3449     else if (info & kPipelineInfoTypeBad) {
  3450         if ((info & kPipelineInfoTypeRed) && (mPipelineState != PS_RED)) {
  3451             LOG(("transition to red from %d. Host = %s.\n",
  3452                  mPipelineState, mConnInfo->Host()));
  3453             mPipelineState = PS_RED;
  3454             mPipeliningPenalty = 0;
  3455 #ifdef WTF_TEST
  3456             fprintf(stderr, "WTF-bad: Red pipeline status disabled host %s\n",
  3457                     mConnInfo->Host());
  3458 #endif
  3462         if (mLastCreditTime.IsNull())
  3463             mLastCreditTime = TimeStamp::Now();
  3465         // Red* events impact the host globally via mPipeliningPenalty, while
  3466         // Bad* events impact the per class penalty.
  3468         // The individual penalties should be < 16bit-signed-maxint - 25000
  3469         // (approx 7500). Penalties are paid-off either when something promising
  3470         // happens (a successful transaction, or promising headers) or when
  3471         // time goes by at a rate of 1 penalty point every 16 seconds.
  3473         switch (info) {
  3474         case RedVersionTooLow:
  3475             mPipeliningPenalty += 1000;
  3476             break;
  3477         case RedBannedServer:
  3478             mPipeliningPenalty += 7000;
  3479             break;
  3480         case RedCorruptedContent:
  3481             mPipeliningPenalty += 7000;
  3482             break;
  3483         case RedCanceledPipeline:
  3484             mPipeliningPenalty += 60;
  3485             break;
  3486         case BadExplicitClose:
  3487             mPipeliningClassPenalty[classification] += 250;
  3488             break;
  3489         case BadSlowReadMinor:
  3490             mPipeliningClassPenalty[classification] += 5;
  3491             break;
  3492         case BadSlowReadMajor:
  3493             mPipeliningClassPenalty[classification] += 25;
  3494             break;
  3495         case BadInsufficientFraming:
  3496             mPipeliningClassPenalty[classification] += 7000;
  3497             break;
  3498         case BadUnexpectedLarge:
  3499             mPipeliningClassPenalty[classification] += 120;
  3500             break;
  3502         default:
  3503             MOZ_ASSERT(false, "Unknown Bad/Red Pipeline Feedback Event");
  3506         const int16_t kPenalty = 25000;
  3507         mPipeliningPenalty = std::min(mPipeliningPenalty, kPenalty);
  3508         mPipeliningClassPenalty[classification] =
  3509           std::min(mPipeliningClassPenalty[classification], kPenalty);
  3511         LOG(("Assessing red penalty to %s class %d for event %d. "
  3512              "Penalty now %d, throttle[%d] = %d\n", mConnInfo->Host(),
  3513              classification, info, mPipeliningPenalty, classification,
  3514              mPipeliningClassPenalty[classification]));
  3516     else {
  3517         // hand out credits for neutral and good events such as
  3518         // "headers look ok" events
  3520         mPipeliningPenalty = std::max(mPipeliningPenalty - 1, 0);
  3521         mPipeliningClassPenalty[classification] = std::max(mPipeliningClassPenalty[classification] - 1, 0);
  3524     if (mPipelineState == PS_RED && !mPipeliningPenalty)
  3526         LOG(("transition %s to yellow\n", mConnInfo->Host()));
  3527         mPipelineState = PS_YELLOW;
  3528         mYellowConnection = nullptr;
  3532 void
  3533 nsHttpConnectionMgr::
  3534 nsConnectionEntry::SetYellowConnection(nsHttpConnection *conn)
  3536     MOZ_ASSERT(!mYellowConnection && mPipelineState == PS_YELLOW,
  3537                "yellow connection already set or state is not yellow");
  3538     mYellowConnection = conn;
  3539     mYellowGoodEvents = mYellowBadEvents = 0;
  3542 void
  3543 nsHttpConnectionMgr::
  3544 nsConnectionEntry::OnYellowComplete()
  3546     if (mPipelineState == PS_YELLOW) {
  3547         if (mYellowGoodEvents && !mYellowBadEvents) {
  3548             LOG(("transition %s to green\n", mConnInfo->Host()));
  3549             mPipelineState = PS_GREEN;
  3550             mGreenDepth = mInitialGreenDepth;
  3552         else {
  3553             // The purpose of the yellow state is to witness at least
  3554             // one successful pipelined transaction without seeing any
  3555             // kind of negative feedback before opening the flood gates.
  3556             // If we haven't confirmed that, then transfer back to red.
  3557             LOG(("transition %s to red from yellow return\n",
  3558                  mConnInfo->Host()));
  3559             mPipelineState = PS_RED;
  3563     mYellowConnection = nullptr;
  3566 void
  3567 nsHttpConnectionMgr::
  3568 nsConnectionEntry::CreditPenalty()
  3570     if (mLastCreditTime.IsNull())
  3571         return;
  3573     // Decrease penalty values by 1 for every 16 seconds
  3574     // (i.e 3.7 per minute, or 1000 every 4h20m)
  3576     TimeStamp now = TimeStamp::Now();
  3577     TimeDuration elapsedTime = now - mLastCreditTime;
  3578     uint32_t creditsEarned =
  3579         static_cast<uint32_t>(elapsedTime.ToSeconds()) >> 4;
  3581     bool failed = false;
  3582     if (creditsEarned > 0) {
  3583         mPipeliningPenalty =
  3584             std::max(int32_t(mPipeliningPenalty - creditsEarned), 0);
  3585         if (mPipeliningPenalty > 0)
  3586             failed = true;
  3588         for (int32_t i = 0; i < nsAHttpTransaction::CLASS_MAX; ++i) {
  3589             mPipeliningClassPenalty[i]  =
  3590                 std::max(int32_t(mPipeliningClassPenalty[i] - creditsEarned), 0);
  3591             failed = failed || (mPipeliningClassPenalty[i] > 0);
  3594         // update last credit mark to reflect elapsed time
  3595         mLastCreditTime += TimeDuration::FromSeconds(creditsEarned << 4);
  3597     else {
  3598         failed = true;                         /* just assume this */
  3601     // If we are no longer red then clear the credit counter - you only
  3602     // get credits for time spent in the red state
  3603     if (!failed)
  3604         mLastCreditTime = TimeStamp();    /* reset to null timestamp */
  3606     if (mPipelineState == PS_RED && !mPipeliningPenalty)
  3608         LOG(("transition %s to yellow based on time credit\n",
  3609              mConnInfo->Host()));
  3610         mPipelineState = PS_YELLOW;
  3611         mYellowConnection = nullptr;
  3615 uint32_t
  3616 nsHttpConnectionMgr::
  3617 nsConnectionEntry::MaxPipelineDepth(nsAHttpTransaction::Classifier aClass)
  3619     // Still subject to configuration limit no matter return value
  3621     if ((mPipelineState == PS_RED) || (mPipeliningClassPenalty[aClass] > 0))
  3622         return 0;
  3624     if (mPipelineState == PS_YELLOW)
  3625         return kPipelineRestricted;
  3627     return mGreenDepth;
  3630 PLDHashOperator
  3631 nsHttpConnectionMgr::ReadConnectionEntry(const nsACString &key,
  3632                 nsAutoPtr<nsConnectionEntry> &ent,
  3633                 void *aArg)
  3635     if (ent->mConnInfo->GetPrivate())
  3636         return PL_DHASH_NEXT;
  3638     nsTArray<HttpRetParams> *args = static_cast<nsTArray<HttpRetParams> *> (aArg);
  3639     HttpRetParams data;
  3640     data.host = ent->mConnInfo->Host();
  3641     data.port = ent->mConnInfo->Port();
  3642     for (uint32_t i = 0; i < ent->mActiveConns.Length(); i++) {
  3643         HttpConnInfo info;
  3644         info.ttl = ent->mActiveConns[i]->TimeToLive();
  3645         info.rtt = ent->mActiveConns[i]->Rtt();
  3646         if (ent->mActiveConns[i]->UsingSpdy())
  3647             info.SetHTTP2ProtocolVersion(ent->mActiveConns[i]->GetSpdyVersion());
  3648         else
  3649             info.SetHTTP1ProtocolVersion(ent->mActiveConns[i]->GetLastHttpResponseVersion());
  3651         data.active.AppendElement(info);
  3653     for (uint32_t i = 0; i < ent->mIdleConns.Length(); i++) {
  3654         HttpConnInfo info;
  3655         info.ttl = ent->mIdleConns[i]->TimeToLive();
  3656         info.rtt = ent->mIdleConns[i]->Rtt();
  3657         info.SetHTTP1ProtocolVersion(ent->mIdleConns[i]->GetLastHttpResponseVersion());
  3658         data.idle.AppendElement(info);
  3660     for(uint32_t i = 0; i < ent->mHalfOpens.Length(); i++) {
  3661         HalfOpenSockets hSocket;
  3662         hSocket.speculative = ent->mHalfOpens[i]->IsSpeculative();
  3663         data.halfOpens.AppendElement(hSocket);
  3665     data.spdy = ent->mUsingSpdy;
  3666     data.ssl = ent->mConnInfo->UsingSSL();
  3667     args->AppendElement(data);
  3668     return PL_DHASH_NEXT;
  3671 bool
  3672 nsHttpConnectionMgr::GetConnectionData(nsTArray<HttpRetParams> *aArg)
  3674     mCT.Enumerate(ReadConnectionEntry, aArg);
  3675     return true;
  3678 void
  3679 nsHttpConnectionMgr::ResetIPFamilyPreference(nsHttpConnectionInfo *ci)
  3681     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  3682     nsConnectionEntry *ent = LookupConnectionEntry(ci, nullptr, nullptr);
  3683     if (ent)
  3684         ent->ResetIPFamilyPreference();
  3687 uint32_t
  3688 nsHttpConnectionMgr::
  3689 nsConnectionEntry::UnconnectedHalfOpens()
  3691     uint32_t unconnectedHalfOpens = 0;
  3692     for (uint32_t i = 0; i < mHalfOpens.Length(); ++i) {
  3693         if (!mHalfOpens[i]->HasConnected())
  3694             ++unconnectedHalfOpens;
  3696     return unconnectedHalfOpens;
  3699 void
  3700 nsHttpConnectionMgr::
  3701 nsConnectionEntry::RemoveHalfOpen(nsHalfOpenSocket *halfOpen)
  3703     // A failure to create the transport object at all
  3704     // will result in it not being present in the halfopen table
  3705     // so ignore failures of RemoveElement()
  3706     mHalfOpens.RemoveElement(halfOpen);
  3707     gHttpHandler->ConnMgr()->mNumHalfOpenConns--;
  3709     if (!UnconnectedHalfOpens())
  3710         // perhaps this reverted RestrictConnections()
  3711         // use the PostEvent version of processpendingq to avoid
  3712         // altering the pending q vector from an arbitrary stack
  3713         gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo);
  3716 void
  3717 nsHttpConnectionMgr::
  3718 nsConnectionEntry::RecordIPFamilyPreference(uint16_t family)
  3720   if (family == PR_AF_INET && !mPreferIPv6)
  3721     mPreferIPv4 = true;
  3723   if (family == PR_AF_INET6 && !mPreferIPv4)
  3724     mPreferIPv6 = true;
  3727 void
  3728 nsHttpConnectionMgr::
  3729 nsConnectionEntry::ResetIPFamilyPreference()
  3731   mPreferIPv4 = false;
  3732   mPreferIPv6 = false;
  3735 } // namespace mozilla::net
  3736 } // namespace mozilla

mercurial