netwerk/protocol/http/nsHttpConnectionMgr.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3736 @@
     1.4 +/* vim:set ts=4 sw=4 sts=4 et cin: */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +// HttpLog.h should generally be included first
    1.10 +#include "HttpLog.h"
    1.11 +
    1.12 +// Log on level :5, instead of default :4.
    1.13 +#undef LOG
    1.14 +#define LOG(args) LOG5(args)
    1.15 +#undef LOG_ENABLED
    1.16 +#define LOG_ENABLED() LOG5_ENABLED()
    1.17 +
    1.18 +#include "nsHttpConnectionMgr.h"
    1.19 +#include "nsHttpConnection.h"
    1.20 +#include "nsHttpPipeline.h"
    1.21 +#include "nsHttpHandler.h"
    1.22 +#include "nsIHttpChannelInternal.h"
    1.23 +#include "nsNetCID.h"
    1.24 +#include "nsCOMPtr.h"
    1.25 +#include "nsNetUtil.h"
    1.26 +#include "mozilla/net/DNS.h"
    1.27 +#include "nsISocketTransport.h"
    1.28 +#include "nsISSLSocketControl.h"
    1.29 +#include "mozilla/Telemetry.h"
    1.30 +#include "mozilla/net/DashboardTypes.h"
    1.31 +#include "NullHttpTransaction.h"
    1.32 +#include "nsITransport.h"
    1.33 +#include "nsISocketTransportService.h"
    1.34 +#include <algorithm>
    1.35 +#include "Http2Compression.h"
    1.36 +#include "mozilla/ChaosMode.h"
    1.37 +#include "mozilla/unused.h"
    1.38 +#include <stdlib.h>
    1.39 +#include "nsHttpRequestHead.h"
    1.40 +
    1.41 +// defined by the socket transport service while active
    1.42 +extern PRThread *gSocketThread;
    1.43 +
    1.44 +namespace mozilla {
    1.45 +namespace net {
    1.46 +
    1.47 +//-----------------------------------------------------------------------------
    1.48 +
    1.49 +NS_IMPL_ISUPPORTS(nsHttpConnectionMgr, nsIObserver)
    1.50 +
    1.51 +static void
    1.52 +InsertTransactionSorted(nsTArray<nsHttpTransaction*> &pendingQ, nsHttpTransaction *trans)
    1.53 +{
    1.54 +    // insert into queue with smallest valued number first.  search in reverse
    1.55 +    // order under the assumption that many of the existing transactions will
    1.56 +    // have the same priority (usually 0).
    1.57 +    uint32_t len = pendingQ.Length();
    1.58 +
    1.59 +    if (pendingQ.IsEmpty()) {
    1.60 +        pendingQ.InsertElementAt(0, trans);
    1.61 +        return;
    1.62 +    }
    1.63 +
    1.64 +    pendingQ.InsertElementAt(0, trans);
    1.65 +
    1.66 +    // FIXME: Refactor into standalone helper (for nsHttpPipeline)
    1.67 +    // Or at least simplify this function if this shuffle ends up
    1.68 +    // being an improvement.
    1.69 +    uint32_t i = 0;
    1.70 +    for (i=0; i < len; ++i) {
    1.71 +        uint32_t ridx = rand() % len;
    1.72 +
    1.73 +        nsHttpTransaction *tmp = pendingQ[i];
    1.74 +        pendingQ[i] = pendingQ[ridx];
    1.75 +        pendingQ[ridx] = tmp;
    1.76 +    }
    1.77 +}
    1.78 +
    1.79 +//-----------------------------------------------------------------------------
    1.80 +
    1.81 +nsHttpConnectionMgr::nsHttpConnectionMgr()
    1.82 +    : mReentrantMonitor("nsHttpConnectionMgr.mReentrantMonitor")
    1.83 +    , mMaxConns(0)
    1.84 +    , mMaxPersistConnsPerHost(0)
    1.85 +    , mMaxPersistConnsPerProxy(0)
    1.86 +    , mIsShuttingDown(false)
    1.87 +    , mNumActiveConns(0)
    1.88 +    , mNumIdleConns(0)
    1.89 +    , mNumSpdyActiveConns(0)
    1.90 +    , mNumHalfOpenConns(0)
    1.91 +    , mTimeOfNextWakeUp(UINT64_MAX)
    1.92 +    , mTimeoutTickArmed(false)
    1.93 +    , mTimeoutTickNext(1)
    1.94 +{
    1.95 +    LOG(("Creating nsHttpConnectionMgr @%x\n", this));
    1.96 +}
    1.97 +
    1.98 +nsHttpConnectionMgr::~nsHttpConnectionMgr()
    1.99 +{
   1.100 +    LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
   1.101 +    if (mTimeoutTick)
   1.102 +        mTimeoutTick->Cancel();
   1.103 +}
   1.104 +
   1.105 +nsresult
   1.106 +nsHttpConnectionMgr::EnsureSocketThreadTarget()
   1.107 +{
   1.108 +    nsresult rv;
   1.109 +    nsCOMPtr<nsIEventTarget> sts;
   1.110 +    nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   1.111 +    if (NS_SUCCEEDED(rv))
   1.112 +        sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
   1.113 +
   1.114 +    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   1.115 +
   1.116 +    // do nothing if already initialized or if we've shut down
   1.117 +    if (mSocketThreadTarget || mIsShuttingDown)
   1.118 +        return NS_OK;
   1.119 +
   1.120 +    mSocketThreadTarget = sts;
   1.121 +
   1.122 +    return rv;
   1.123 +}
   1.124 +
   1.125 +nsresult
   1.126 +nsHttpConnectionMgr::Init(uint16_t maxConns,
   1.127 +                          uint16_t maxPersistConnsPerHost,
   1.128 +                          uint16_t maxPersistConnsPerProxy,
   1.129 +                          uint16_t maxRequestDelay,
   1.130 +                          uint16_t maxPipelinedRequests,
   1.131 +                          uint16_t maxOptimisticPipelinedRequests)
   1.132 +{
   1.133 +    LOG(("nsHttpConnectionMgr::Init\n"));
   1.134 +
   1.135 +    {
   1.136 +        ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   1.137 +
   1.138 +        mMaxConns = maxConns;
   1.139 +        mMaxPersistConnsPerHost = maxPersistConnsPerHost;
   1.140 +        mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
   1.141 +        mMaxRequestDelay = maxRequestDelay;
   1.142 +        mMaxPipelinedRequests = maxPipelinedRequests;
   1.143 +        mMaxOptimisticPipelinedRequests = maxOptimisticPipelinedRequests;
   1.144 +
   1.145 +        mIsShuttingDown = false;
   1.146 +    }
   1.147 +
   1.148 +    return EnsureSocketThreadTarget();
   1.149 +}
   1.150 +
   1.151 +nsresult
   1.152 +nsHttpConnectionMgr::Shutdown()
   1.153 +{
   1.154 +    LOG(("nsHttpConnectionMgr::Shutdown\n"));
   1.155 +
   1.156 +    bool shutdown = false;
   1.157 +    {
   1.158 +        ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   1.159 +
   1.160 +        // do nothing if already shutdown
   1.161 +        if (!mSocketThreadTarget)
   1.162 +            return NS_OK;
   1.163 +
   1.164 +        nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgShutdown,
   1.165 +                                0, &shutdown);
   1.166 +
   1.167 +        // release our reference to the STS to prevent further events
   1.168 +        // from being posted.  this is how we indicate that we are
   1.169 +        // shutting down.
   1.170 +        mIsShuttingDown = true;
   1.171 +        mSocketThreadTarget = 0;
   1.172 +
   1.173 +        if (NS_FAILED(rv)) {
   1.174 +            NS_WARNING("unable to post SHUTDOWN message");
   1.175 +            return rv;
   1.176 +        }
   1.177 +    }
   1.178 +
   1.179 +    // wait for shutdown event to complete
   1.180 +    while (!shutdown)
   1.181 +        NS_ProcessNextEvent(NS_GetCurrentThread());
   1.182 +    Http2CompressionCleanup();
   1.183 +
   1.184 +    return NS_OK;
   1.185 +}
   1.186 +
   1.187 +nsresult
   1.188 +nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, int32_t iparam, void *vparam)
   1.189 +{
   1.190 +    EnsureSocketThreadTarget();
   1.191 +
   1.192 +    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   1.193 +
   1.194 +    nsresult rv;
   1.195 +    if (!mSocketThreadTarget) {
   1.196 +        NS_WARNING("cannot post event if not initialized");
   1.197 +        rv = NS_ERROR_NOT_INITIALIZED;
   1.198 +    }
   1.199 +    else {
   1.200 +        nsRefPtr<nsIRunnable> event = new nsConnEvent(this, handler, iparam, vparam);
   1.201 +        rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
   1.202 +    }
   1.203 +    return rv;
   1.204 +}
   1.205 +
   1.206 +void
   1.207 +nsHttpConnectionMgr::PruneDeadConnectionsAfter(uint32_t timeInSeconds)
   1.208 +{
   1.209 +    LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));
   1.210 +
   1.211 +    if(!mTimer)
   1.212 +        mTimer = do_CreateInstance("@mozilla.org/timer;1");
   1.213 +
   1.214 +    // failure to create a timer is not a fatal error, but idle connections
   1.215 +    // will not be cleaned up until we try to use them.
   1.216 +    if (mTimer) {
   1.217 +        mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
   1.218 +        mTimer->Init(this, timeInSeconds*1000, nsITimer::TYPE_ONE_SHOT);
   1.219 +    } else {
   1.220 +        NS_WARNING("failed to create: timer for pruning the dead connections!");
   1.221 +    }
   1.222 +}
   1.223 +
   1.224 +void
   1.225 +nsHttpConnectionMgr::ConditionallyStopPruneDeadConnectionsTimer()
   1.226 +{
   1.227 +    // Leave the timer in place if there are connections that potentially
   1.228 +    // need management
   1.229 +    if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
   1.230 +        return;
   1.231 +
   1.232 +    LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));
   1.233 +
   1.234 +    // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
   1.235 +    mTimeOfNextWakeUp = UINT64_MAX;
   1.236 +    if (mTimer) {
   1.237 +        mTimer->Cancel();
   1.238 +        mTimer = nullptr;
   1.239 +    }
   1.240 +}
   1.241 +
   1.242 +void
   1.243 +nsHttpConnectionMgr::ConditionallyStopTimeoutTick()
   1.244 +{
   1.245 +    LOG(("nsHttpConnectionMgr::ConditionallyStopTimeoutTick "
   1.246 +         "armed=%d active=%d\n", mTimeoutTickArmed, mNumActiveConns));
   1.247 +
   1.248 +    if (!mTimeoutTickArmed)
   1.249 +        return;
   1.250 +
   1.251 +    if (mNumActiveConns)
   1.252 +        return;
   1.253 +
   1.254 +    LOG(("nsHttpConnectionMgr::ConditionallyStopTimeoutTick stop==true\n"));
   1.255 +
   1.256 +    mTimeoutTick->Cancel();
   1.257 +    mTimeoutTickArmed = false;
   1.258 +}
   1.259 +
   1.260 +//-----------------------------------------------------------------------------
   1.261 +// nsHttpConnectionMgr::nsIObserver
   1.262 +//-----------------------------------------------------------------------------
   1.263 +
   1.264 +NS_IMETHODIMP
   1.265 +nsHttpConnectionMgr::Observe(nsISupports *subject,
   1.266 +                             const char *topic,
   1.267 +                             const char16_t *data)
   1.268 +{
   1.269 +    LOG(("nsHttpConnectionMgr::Observe [topic=\"%s\"]\n", topic));
   1.270 +
   1.271 +    if (0 == strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
   1.272 +        nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
   1.273 +        if (timer == mTimer) {
   1.274 +            PruneDeadConnections();
   1.275 +        }
   1.276 +        else if (timer == mTimeoutTick) {
   1.277 +            TimeoutTick();
   1.278 +        }
   1.279 +        else {
   1.280 +            MOZ_ASSERT(false, "unexpected timer-callback");
   1.281 +            LOG(("Unexpected timer object\n"));
   1.282 +            return NS_ERROR_UNEXPECTED;
   1.283 +        }
   1.284 +    }
   1.285 +
   1.286 +    return NS_OK;
   1.287 +}
   1.288 +
   1.289 +
   1.290 +//-----------------------------------------------------------------------------
   1.291 +
   1.292 +nsresult
   1.293 +nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, int32_t priority)
   1.294 +{
   1.295 +    LOG(("nsHttpConnectionMgr::AddTransaction [trans=%x %d]\n", trans, priority));
   1.296 +
   1.297 +    NS_ADDREF(trans);
   1.298 +    nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
   1.299 +    if (NS_FAILED(rv))
   1.300 +        NS_RELEASE(trans);
   1.301 +    return rv;
   1.302 +}
   1.303 +
   1.304 +nsresult
   1.305 +nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, int32_t priority)
   1.306 +{
   1.307 +    LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%x %d]\n", trans, priority));
   1.308 +
   1.309 +    NS_ADDREF(trans);
   1.310 +    nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority, trans);
   1.311 +    if (NS_FAILED(rv))
   1.312 +        NS_RELEASE(trans);
   1.313 +    return rv;
   1.314 +}
   1.315 +
   1.316 +nsresult
   1.317 +nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
   1.318 +{
   1.319 +    LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%x reason=%x]\n", trans, reason));
   1.320 +
   1.321 +    NS_ADDREF(trans);
   1.322 +    nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction,
   1.323 +                            static_cast<int32_t>(reason), trans);
   1.324 +    if (NS_FAILED(rv))
   1.325 +        NS_RELEASE(trans);
   1.326 +    return rv;
   1.327 +}
   1.328 +
   1.329 +nsresult
   1.330 +nsHttpConnectionMgr::PruneDeadConnections()
   1.331 +{
   1.332 +    return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
   1.333 +}
   1.334 +
   1.335 +nsresult
   1.336 +nsHttpConnectionMgr::DoShiftReloadConnectionCleanup(nsHttpConnectionInfo *aCI)
   1.337 +{
   1.338 +    nsRefPtr<nsHttpConnectionInfo> connInfo(aCI);
   1.339 +
   1.340 +    nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup,
   1.341 +                            0, connInfo);
   1.342 +    if (NS_SUCCEEDED(rv))
   1.343 +        unused << connInfo.forget();
   1.344 +    return rv;
   1.345 +}
   1.346 +
   1.347 +class SpeculativeConnectArgs
   1.348 +{
   1.349 +public:
   1.350 +    SpeculativeConnectArgs() { mOverridesOK = false; }
   1.351 +    virtual ~SpeculativeConnectArgs() {}
   1.352 +
   1.353 +    // Added manually so we can use nsRefPtr without inheriting from
   1.354 +    // nsISupports
   1.355 +    NS_IMETHOD_(MozExternalRefCountType) AddRef(void);
   1.356 +    NS_IMETHOD_(MozExternalRefCountType) Release(void);
   1.357 +
   1.358 +public: // intentional!
   1.359 +    nsRefPtr<NullHttpTransaction> mTrans;
   1.360 +
   1.361 +    bool mOverridesOK;
   1.362 +    uint32_t mParallelSpeculativeConnectLimit;
   1.363 +    bool mIgnoreIdle;
   1.364 +    bool mIgnorePossibleSpdyConnections;
   1.365 +
   1.366 +    // As above, added manually so we can use nsRefPtr without inheriting from
   1.367 +    // nsISupports
   1.368 +protected:
   1.369 +    ThreadSafeAutoRefCnt mRefCnt;
   1.370 +    NS_DECL_OWNINGTHREAD
   1.371 +};
   1.372 +
   1.373 +NS_IMPL_ADDREF(SpeculativeConnectArgs)
   1.374 +NS_IMPL_RELEASE(SpeculativeConnectArgs)
   1.375 +
   1.376 +nsresult
   1.377 +nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci,
   1.378 +                                        nsIInterfaceRequestor *callbacks,
   1.379 +                                        uint32_t caps)
   1.380 +{
   1.381 +    MOZ_ASSERT(NS_IsMainThread(), "nsHttpConnectionMgr::SpeculativeConnect called off main thread!");
   1.382 +
   1.383 +    LOG(("nsHttpConnectionMgr::SpeculativeConnect [ci=%s]\n",
   1.384 +         ci->HashKey().get()));
   1.385 +
   1.386 +    // Hosts that are Local IP Literals should not be speculatively
   1.387 +    // connected - Bug 853423.
   1.388 +    if (ci && ci->HostIsLocalIPLiteral()) {
   1.389 +        LOG(("nsHttpConnectionMgr::SpeculativeConnect skipping RFC1918 "
   1.390 +             "address [%s]", ci->Host()));
   1.391 +        return NS_OK;
   1.392 +    }
   1.393 +
   1.394 +    nsRefPtr<SpeculativeConnectArgs> args = new SpeculativeConnectArgs();
   1.395 +
   1.396 +    // Wrap up the callbacks and the target to ensure they're released on the target
   1.397 +    // thread properly.
   1.398 +    nsCOMPtr<nsIInterfaceRequestor> wrappedCallbacks;
   1.399 +    NS_NewInterfaceRequestorAggregation(callbacks, nullptr, getter_AddRefs(wrappedCallbacks));
   1.400 +
   1.401 +    caps |= ci->GetAnonymous() ? NS_HTTP_LOAD_ANONYMOUS : 0;
   1.402 +    args->mTrans = new NullHttpTransaction(ci, wrappedCallbacks, caps);
   1.403 +
   1.404 +    nsCOMPtr<nsISpeculativeConnectionOverrider> overrider =
   1.405 +        do_GetInterface(callbacks);
   1.406 +    if (overrider) {
   1.407 +        args->mOverridesOK = true;
   1.408 +        overrider->GetParallelSpeculativeConnectLimit(
   1.409 +            &args->mParallelSpeculativeConnectLimit);
   1.410 +        overrider->GetIgnoreIdle(&args->mIgnoreIdle);
   1.411 +        overrider->GetIgnorePossibleSpdyConnections(
   1.412 +            &args->mIgnorePossibleSpdyConnections);
   1.413 +    }
   1.414 +
   1.415 +    nsresult rv =
   1.416 +        PostEvent(&nsHttpConnectionMgr::OnMsgSpeculativeConnect, 0, args);
   1.417 +    if (NS_SUCCEEDED(rv))
   1.418 +        unused << args.forget();
   1.419 +    return rv;
   1.420 +}
   1.421 +
   1.422 +nsresult
   1.423 +nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget **target)
   1.424 +{
   1.425 +    EnsureSocketThreadTarget();
   1.426 +
   1.427 +    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   1.428 +    NS_IF_ADDREF(*target = mSocketThreadTarget);
   1.429 +    return NS_OK;
   1.430 +}
   1.431 +
   1.432 +nsresult
   1.433 +nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
   1.434 +{
   1.435 +    LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
   1.436 +
   1.437 +    NS_ADDREF(conn);
   1.438 +    nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
   1.439 +    if (NS_FAILED(rv))
   1.440 +        NS_RELEASE(conn);
   1.441 +    return rv;
   1.442 +}
   1.443 +
   1.444 +// A structure used to marshall 2 pointers across the various necessary
   1.445 +// threads to complete an HTTP upgrade.
   1.446 +class nsCompleteUpgradeData
   1.447 +{
   1.448 +public:
   1.449 +nsCompleteUpgradeData(nsAHttpConnection *aConn,
   1.450 +                      nsIHttpUpgradeListener *aListener)
   1.451 +    : mConn(aConn), mUpgradeListener(aListener) {}
   1.452 +
   1.453 +    nsRefPtr<nsAHttpConnection> mConn;
   1.454 +    nsCOMPtr<nsIHttpUpgradeListener> mUpgradeListener;
   1.455 +};
   1.456 +
   1.457 +nsresult
   1.458 +nsHttpConnectionMgr::CompleteUpgrade(nsAHttpConnection *aConn,
   1.459 +                                     nsIHttpUpgradeListener *aUpgradeListener)
   1.460 +{
   1.461 +    nsCompleteUpgradeData *data =
   1.462 +        new nsCompleteUpgradeData(aConn, aUpgradeListener);
   1.463 +    nsresult rv;
   1.464 +    rv = PostEvent(&nsHttpConnectionMgr::OnMsgCompleteUpgrade, 0, data);
   1.465 +    if (NS_FAILED(rv))
   1.466 +        delete data;
   1.467 +    return rv;
   1.468 +}
   1.469 +
   1.470 +nsresult
   1.471 +nsHttpConnectionMgr::UpdateParam(nsParamName name, uint16_t value)
   1.472 +{
   1.473 +    uint32_t param = (uint32_t(name) << 16) | uint32_t(value);
   1.474 +    return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam, 0,
   1.475 +                     (void *)(uintptr_t) param);
   1.476 +}
   1.477 +
   1.478 +nsresult
   1.479 +nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
   1.480 +{
   1.481 +    LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
   1.482 +
   1.483 +    NS_ADDREF(ci);
   1.484 +    nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
   1.485 +    if (NS_FAILED(rv))
   1.486 +        NS_RELEASE(ci);
   1.487 +    return rv;
   1.488 +}
   1.489 +
   1.490 +nsresult
   1.491 +nsHttpConnectionMgr::ProcessPendingQ()
   1.492 +{
   1.493 +    LOG(("nsHttpConnectionMgr::ProcessPendingQ [All CI]\n"));
   1.494 +    return PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, nullptr);
   1.495 +}
   1.496 +
   1.497 +void
   1.498 +nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket(int32_t, void *param)
   1.499 +{
   1.500 +    nsRefPtr<EventTokenBucket> tokenBucket =
   1.501 +        dont_AddRef(static_cast<EventTokenBucket *>(param));
   1.502 +    gHttpHandler->SetRequestTokenBucket(tokenBucket);
   1.503 +}
   1.504 +
   1.505 +nsresult
   1.506 +nsHttpConnectionMgr::UpdateRequestTokenBucket(EventTokenBucket *aBucket)
   1.507 +{
   1.508 +    nsRefPtr<EventTokenBucket> bucket(aBucket);
   1.509 +
   1.510 +    // Call From main thread when a new EventTokenBucket has been made in order
   1.511 +    // to post the new value to the socket thread.
   1.512 +    nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgUpdateRequestTokenBucket,
   1.513 +                            0, bucket);
   1.514 +    if (NS_SUCCEEDED(rv))
   1.515 +        unused << bucket.forget();
   1.516 +    return rv;
   1.517 +}
   1.518 +
   1.519 +// Given a nsHttpConnectionInfo find the connection entry object that
   1.520 +// contains either the nshttpconnection or nshttptransaction parameter.
   1.521 +// Normally this is done by the hashkey lookup of connectioninfo,
   1.522 +// but if spdy coalescing is in play it might be found in a redirected
   1.523 +// entry
   1.524 +nsHttpConnectionMgr::nsConnectionEntry *
   1.525 +nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
   1.526 +                                           nsHttpConnection *conn,
   1.527 +                                           nsHttpTransaction *trans)
   1.528 +{
   1.529 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.530 +    if (!ci)
   1.531 +        return nullptr;
   1.532 +
   1.533 +    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
   1.534 +
   1.535 +    // If there is no sign of coalescing (or it is disabled) then just
   1.536 +    // return the primary hash lookup
   1.537 +    if (!ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty())
   1.538 +        return ent;
   1.539 +
   1.540 +    // If there is no preferred coalescing entry for this host (or the
   1.541 +    // preferred entry is the one that matched the mCT hash lookup) then
   1.542 +    // there is only option
   1.543 +    nsConnectionEntry *preferred = mSpdyPreferredHash.Get(ent->mCoalescingKey);
   1.544 +    if (!preferred || (preferred == ent))
   1.545 +        return ent;
   1.546 +
   1.547 +    if (conn) {
   1.548 +        // The connection could be either in preferred or ent. It is most
   1.549 +        // likely the only active connection in preferred - so start with that.
   1.550 +        if (preferred->mActiveConns.Contains(conn))
   1.551 +            return preferred;
   1.552 +        if (preferred->mIdleConns.Contains(conn))
   1.553 +            return preferred;
   1.554 +    }
   1.555 +
   1.556 +    if (trans && preferred->mPendingQ.Contains(trans))
   1.557 +        return preferred;
   1.558 +
   1.559 +    // Neither conn nor trans found in preferred, use the default entry
   1.560 +    return ent;
   1.561 +}
   1.562 +
   1.563 +nsresult
   1.564 +nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
   1.565 +{
   1.566 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.567 +    LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p",
   1.568 +         this, conn));
   1.569 +
   1.570 +    if (!conn->ConnectionInfo())
   1.571 +        return NS_ERROR_UNEXPECTED;
   1.572 +
   1.573 +    nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
   1.574 +                                                   conn, nullptr);
   1.575 +
   1.576 +    if (!ent || !ent->mIdleConns.RemoveElement(conn))
   1.577 +        return NS_ERROR_UNEXPECTED;
   1.578 +
   1.579 +    conn->Close(NS_ERROR_ABORT);
   1.580 +    NS_RELEASE(conn);
   1.581 +    mNumIdleConns--;
   1.582 +    ConditionallyStopPruneDeadConnectionsTimer();
   1.583 +    return NS_OK;
   1.584 +}
   1.585 +
   1.586 +// This function lets a connection, after completing the NPN phase,
   1.587 +// report whether or not it is using spdy through the usingSpdy
   1.588 +// argument. It would not be necessary if NPN were driven out of
   1.589 +// the connection manager. The connection entry associated with the
   1.590 +// connection is then updated to indicate whether or not we want to use
   1.591 +// spdy with that host and update the preliminary preferred host
   1.592 +// entries used for de-sharding hostsnames.
   1.593 +void
   1.594 +nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
   1.595 +                                          bool usingSpdy)
   1.596 +{
   1.597 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.598 +
   1.599 +    nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
   1.600 +                                                   conn, nullptr);
   1.601 +
   1.602 +    if (!ent)
   1.603 +        return;
   1.604 +
   1.605 +    ent->mTestedSpdy = true;
   1.606 +
   1.607 +    if (!usingSpdy)
   1.608 +        return;
   1.609 +
   1.610 +    ent->mUsingSpdy = true;
   1.611 +    mNumSpdyActiveConns++;
   1.612 +
   1.613 +    uint32_t ttl = conn->TimeToLive();
   1.614 +    uint64_t timeOfExpire = NowInSeconds() + ttl;
   1.615 +    if (!mTimer || timeOfExpire < mTimeOfNextWakeUp)
   1.616 +        PruneDeadConnectionsAfter(ttl);
   1.617 +
   1.618 +    // Lookup preferred directly from the hash instead of using
   1.619 +    // GetSpdyPreferredEnt() because we want to avoid the cert compatibility
   1.620 +    // check at this point because the cert is never part of the hash
   1.621 +    // lookup. Filtering on that has to be done at the time of use
   1.622 +    // rather than the time of registration (i.e. now).
   1.623 +    nsConnectionEntry *joinedConnection;
   1.624 +    nsConnectionEntry *preferred =
   1.625 +        mSpdyPreferredHash.Get(ent->mCoalescingKey);
   1.626 +
   1.627 +    LOG(("ReportSpdyConnection %s %s ent=%p preferred=%p\n",
   1.628 +         ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
   1.629 +         ent, preferred));
   1.630 +
   1.631 +    if (!preferred) {
   1.632 +        if (!ent->mCoalescingKey.IsEmpty()) {
   1.633 +            mSpdyPreferredHash.Put(ent->mCoalescingKey, ent);
   1.634 +            ent->mSpdyPreferred = true;
   1.635 +            preferred = ent;
   1.636 +        }
   1.637 +    } else if ((preferred != ent) &&
   1.638 +               (joinedConnection = GetSpdyPreferredEnt(ent)) &&
   1.639 +               (joinedConnection != ent)) {
   1.640 +        //
   1.641 +        // A connection entry (e.g. made with a different hostname) with
   1.642 +        // the same IP address is preferred for future transactions over this
   1.643 +        // connection entry. Gracefully close down the connection to help
   1.644 +        // new transactions migrate over.
   1.645 +
   1.646 +        LOG(("ReportSpdyConnection graceful close of conn=%p ent=%p to "
   1.647 +                 "migrate to preferred\n", conn, ent));
   1.648 +
   1.649 +        conn->DontReuse();
   1.650 +    } else if (preferred != ent) {
   1.651 +        LOG (("ReportSpdyConnection preferred host may be in false start or "
   1.652 +              "may have insufficient cert. Leave mapping in place but do not "
   1.653 +              "abandon this connection yet."));
   1.654 +    }
   1.655 +
   1.656 +    PostEvent(&nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ);
   1.657 +}
   1.658 +
   1.659 +void
   1.660 +nsHttpConnectionMgr::ReportSpdyCWNDSetting(nsHttpConnectionInfo *ci,
   1.661 +                                           uint32_t cwndValue)
   1.662 +{
   1.663 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.664 +
   1.665 +    if (!gHttpHandler->UseSpdyPersistentSettings())
   1.666 +        return;
   1.667 +
   1.668 +    if (!ci)
   1.669 +        return;
   1.670 +
   1.671 +    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
   1.672 +    if (!ent)
   1.673 +        return;
   1.674 +
   1.675 +    ent = GetSpdyPreferredEnt(ent);
   1.676 +    if (!ent) // just to be thorough - but that map should always exist
   1.677 +        return;
   1.678 +
   1.679 +    cwndValue = std::max(2U, cwndValue);
   1.680 +    cwndValue = std::min(128U, cwndValue);
   1.681 +
   1.682 +    ent->mSpdyCWND = cwndValue;
   1.683 +    ent->mSpdyCWNDTimeStamp = TimeStamp::Now();
   1.684 +    return;
   1.685 +}
   1.686 +
   1.687 +// a value of 0 means no setting is available
   1.688 +uint32_t
   1.689 +nsHttpConnectionMgr::GetSpdyCWNDSetting(nsHttpConnectionInfo *ci)
   1.690 +{
   1.691 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.692 +
   1.693 +    if (!gHttpHandler->UseSpdyPersistentSettings())
   1.694 +        return 0;
   1.695 +
   1.696 +    if (!ci)
   1.697 +        return 0;
   1.698 +
   1.699 +    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
   1.700 +    if (!ent)
   1.701 +        return 0;
   1.702 +
   1.703 +    ent = GetSpdyPreferredEnt(ent);
   1.704 +    if (!ent) // just to be thorough - but that map should always exist
   1.705 +        return 0;
   1.706 +
   1.707 +    if (ent->mSpdyCWNDTimeStamp.IsNull())
   1.708 +        return 0;
   1.709 +
   1.710 +    // For privacy tracking reasons, and the fact that CWND is not
   1.711 +    // meaningful after some time, we don't honor stored CWND after 8
   1.712 +    // hours.
   1.713 +    TimeDuration age = TimeStamp::Now() - ent->mSpdyCWNDTimeStamp;
   1.714 +    if (age.ToMilliseconds() > (1000 * 60 * 60 * 8))
   1.715 +        return 0;
   1.716 +
   1.717 +    return ent->mSpdyCWND;
   1.718 +}
   1.719 +
   1.720 +nsHttpConnectionMgr::nsConnectionEntry *
   1.721 +nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
   1.722 +{
   1.723 +    if (!gHttpHandler->IsSpdyEnabled() ||
   1.724 +        !gHttpHandler->CoalesceSpdy() ||
   1.725 +        aOriginalEntry->mCoalescingKey.IsEmpty())
   1.726 +        return nullptr;
   1.727 +
   1.728 +    nsConnectionEntry *preferred =
   1.729 +        mSpdyPreferredHash.Get(aOriginalEntry->mCoalescingKey);
   1.730 +
   1.731 +    // if there is no redirection no cert validation is required
   1.732 +    if (preferred == aOriginalEntry)
   1.733 +        return aOriginalEntry;
   1.734 +
   1.735 +    // if there is no preferred host or it is no longer using spdy
   1.736 +    // then skip pooling
   1.737 +    if (!preferred || !preferred->mUsingSpdy)
   1.738 +        return nullptr;
   1.739 +
   1.740 +    // if there is not an active spdy session in this entry then
   1.741 +    // we cannot pool because the cert upon activation may not
   1.742 +    // be the same as the old one. Active sessions are prohibited
   1.743 +    // from changing certs.
   1.744 +
   1.745 +    nsHttpConnection *activeSpdy = nullptr;
   1.746 +
   1.747 +    for (uint32_t index = 0; index < preferred->mActiveConns.Length(); ++index) {
   1.748 +        if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
   1.749 +            activeSpdy = preferred->mActiveConns[index];
   1.750 +            break;
   1.751 +        }
   1.752 +    }
   1.753 +
   1.754 +    if (!activeSpdy) {
   1.755 +        // remove the preferred status of this entry if it cannot be
   1.756 +        // used for pooling.
   1.757 +        preferred->mSpdyPreferred = false;
   1.758 +        RemoveSpdyPreferredEnt(preferred->mCoalescingKey);
   1.759 +        LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
   1.760 +             "preferred host mapping %s to %s removed due to inactivity.\n",
   1.761 +             aOriginalEntry->mConnInfo->Host(),
   1.762 +             preferred->mConnInfo->Host()));
   1.763 +
   1.764 +        return nullptr;
   1.765 +    }
   1.766 +
   1.767 +    // Check that the server cert supports redirection
   1.768 +    nsresult rv;
   1.769 +    bool isJoined = false;
   1.770 +
   1.771 +    nsCOMPtr<nsISupports> securityInfo;
   1.772 +    nsCOMPtr<nsISSLSocketControl> sslSocketControl;
   1.773 +    nsAutoCString negotiatedNPN;
   1.774 +
   1.775 +    activeSpdy->GetSecurityInfo(getter_AddRefs(securityInfo));
   1.776 +    if (!securityInfo) {
   1.777 +        NS_WARNING("cannot obtain spdy security info");
   1.778 +        return nullptr;
   1.779 +    }
   1.780 +
   1.781 +    sslSocketControl = do_QueryInterface(securityInfo, &rv);
   1.782 +    if (NS_FAILED(rv)) {
   1.783 +        NS_WARNING("sslSocketControl QI Failed");
   1.784 +        return nullptr;
   1.785 +    }
   1.786 +
   1.787 +    if (gHttpHandler->SpdyInfo()->ProtocolEnabled(0))
   1.788 +        rv = sslSocketControl->JoinConnection(gHttpHandler->SpdyInfo()->VersionString[0],
   1.789 +                                              aOriginalEntry->mConnInfo->GetHost(),
   1.790 +                                              aOriginalEntry->mConnInfo->Port(),
   1.791 +                                              &isJoined);
   1.792 +    else
   1.793 +        rv = NS_OK;                               /* simulate failed join */
   1.794 +
   1.795 +    // JoinConnection() may have failed due to spdy version level. Try the other
   1.796 +    // level we support (if any)
   1.797 +    if (NS_SUCCEEDED(rv) && !isJoined && gHttpHandler->SpdyInfo()->ProtocolEnabled(1)) {
   1.798 +        rv = sslSocketControl->JoinConnection(gHttpHandler->SpdyInfo()->VersionString[1],
   1.799 +                                              aOriginalEntry->mConnInfo->GetHost(),
   1.800 +                                              aOriginalEntry->mConnInfo->Port(),
   1.801 +                                              &isJoined);
   1.802 +    }
   1.803 +
   1.804 +    if (NS_FAILED(rv) || !isJoined) {
   1.805 +        LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
   1.806 +             "Host %s cannot be confirmed to be joined "
   1.807 +             "with %s connections. rv=%x isJoined=%d",
   1.808 +             preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
   1.809 +             rv, isJoined));
   1.810 +        Telemetry::Accumulate(Telemetry::SPDY_NPN_JOIN, false);
   1.811 +        return nullptr;
   1.812 +    }
   1.813 +
   1.814 +    // IP pooling confirmed
   1.815 +    LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
   1.816 +         "Host %s has cert valid for %s connections, "
   1.817 +         "so %s will be coalesced with %s",
   1.818 +         preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
   1.819 +         aOriginalEntry->mConnInfo->Host(), preferred->mConnInfo->Host()));
   1.820 +    Telemetry::Accumulate(Telemetry::SPDY_NPN_JOIN, true);
   1.821 +    return preferred;
   1.822 +}
   1.823 +
   1.824 +void
   1.825 +nsHttpConnectionMgr::RemoveSpdyPreferredEnt(nsACString &aHashKey)
   1.826 +{
   1.827 +    if (aHashKey.IsEmpty())
   1.828 +        return;
   1.829 +
   1.830 +    mSpdyPreferredHash.Remove(aHashKey);
   1.831 +}
   1.832 +
   1.833 +//-----------------------------------------------------------------------------
   1.834 +// enumeration callbacks
   1.835 +
   1.836 +PLDHashOperator
   1.837 +nsHttpConnectionMgr::ProcessOneTransactionCB(const nsACString &key,
   1.838 +                                             nsAutoPtr<nsConnectionEntry> &ent,
   1.839 +                                             void *closure)
   1.840 +{
   1.841 +    nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
   1.842 +
   1.843 +    if (self->ProcessPendingQForEntry(ent, false))
   1.844 +        return PL_DHASH_STOP;
   1.845 +
   1.846 +    return PL_DHASH_NEXT;
   1.847 +}
   1.848 +
   1.849 +PLDHashOperator
   1.850 +nsHttpConnectionMgr::ProcessAllTransactionsCB(const nsACString &key,
   1.851 +                                              nsAutoPtr<nsConnectionEntry> &ent,
   1.852 +                                              void *closure)
   1.853 +{
   1.854 +    nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
   1.855 +    self->ProcessPendingQForEntry(ent, true);
   1.856 +    return PL_DHASH_NEXT;
   1.857 +}
   1.858 +
   1.859 +// If the global number of connections is preventing the opening of
   1.860 +// new connections to a host without idle connections, then
   1.861 +// close them regardless of their TTL
   1.862 +PLDHashOperator
   1.863 +nsHttpConnectionMgr::PurgeExcessIdleConnectionsCB(const nsACString &key,
   1.864 +                                                  nsAutoPtr<nsConnectionEntry> &ent,
   1.865 +                                                  void *closure)
   1.866 +{
   1.867 +    nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
   1.868 +
   1.869 +    while (self->mNumIdleConns + self->mNumActiveConns + 1 >= self->mMaxConns) {
   1.870 +        if (!ent->mIdleConns.Length()) {
   1.871 +            // There are no idle conns left in this connection entry
   1.872 +            return PL_DHASH_NEXT;
   1.873 +        }
   1.874 +        nsHttpConnection *conn = ent->mIdleConns[0];
   1.875 +        ent->mIdleConns.RemoveElementAt(0);
   1.876 +        conn->Close(NS_ERROR_ABORT);
   1.877 +        NS_RELEASE(conn);
   1.878 +        self->mNumIdleConns--;
   1.879 +        self->ConditionallyStopPruneDeadConnectionsTimer();
   1.880 +    }
   1.881 +    return PL_DHASH_STOP;
   1.882 +}
   1.883 +
   1.884 +// If the global number of connections is preventing the opening of
   1.885 +// new connections to a host without idle connections, then
   1.886 +// close any spdy asap
   1.887 +PLDHashOperator
   1.888 +nsHttpConnectionMgr::PurgeExcessSpdyConnectionsCB(const nsACString &key,
   1.889 +                                                  nsAutoPtr<nsConnectionEntry> &ent,
   1.890 +                                                  void *closure)
   1.891 +{
   1.892 +    if (!ent->mUsingSpdy)
   1.893 +        return PL_DHASH_NEXT;
   1.894 +
   1.895 +    nsHttpConnectionMgr *self = static_cast<nsHttpConnectionMgr *>(closure);
   1.896 +    for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
   1.897 +        nsHttpConnection *conn = ent->mActiveConns[index];
   1.898 +        if (conn->UsingSpdy() && conn->CanReuse()) {
   1.899 +            conn->DontReuse();
   1.900 +            // stop on <= (particularly =) beacuse this dontreuse causes async close
   1.901 +            if (self->mNumIdleConns + self->mNumActiveConns + 1 <= self->mMaxConns)
   1.902 +                return PL_DHASH_STOP;
   1.903 +        }
   1.904 +    }
   1.905 +    return PL_DHASH_NEXT;
   1.906 +}
   1.907 +
   1.908 +PLDHashOperator
   1.909 +nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key,
   1.910 +                                            nsAutoPtr<nsConnectionEntry> &ent,
   1.911 +                                            void *closure)
   1.912 +{
   1.913 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   1.914 +    nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
   1.915 +
   1.916 +    LOG(("  pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
   1.917 +
   1.918 +    // Find out how long it will take for next idle connection to not be reusable
   1.919 +    // anymore.
   1.920 +    uint32_t timeToNextExpire = UINT32_MAX;
   1.921 +    int32_t count = ent->mIdleConns.Length();
   1.922 +    if (count > 0) {
   1.923 +        for (int32_t i=count-1; i>=0; --i) {
   1.924 +            nsHttpConnection *conn = ent->mIdleConns[i];
   1.925 +            if (!conn->CanReuse()) {
   1.926 +                ent->mIdleConns.RemoveElementAt(i);
   1.927 +                conn->Close(NS_ERROR_ABORT);
   1.928 +                NS_RELEASE(conn);
   1.929 +                self->mNumIdleConns--;
   1.930 +            } else {
   1.931 +                timeToNextExpire = std::min(timeToNextExpire, conn->TimeToLive());
   1.932 +            }
   1.933 +        }
   1.934 +    }
   1.935 +
   1.936 +    if (ent->mUsingSpdy) {
   1.937 +        for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
   1.938 +            nsHttpConnection *conn = ent->mActiveConns[index];
   1.939 +            if (conn->UsingSpdy()) {
   1.940 +                if (!conn->CanReuse()) {
   1.941 +                    // marking it dont reuse will create an active tear down if
   1.942 +                    // the spdy session is idle.
   1.943 +                    conn->DontReuse();
   1.944 +                }
   1.945 +                else {
   1.946 +                    timeToNextExpire = std::min(timeToNextExpire,
   1.947 +                                              conn->TimeToLive());
   1.948 +                }
   1.949 +            }
   1.950 +        }
   1.951 +    }
   1.952 +
   1.953 +    // If time to next expire found is shorter than time to next wake-up, we need to
   1.954 +    // change the time for next wake-up.
   1.955 +    if (timeToNextExpire != UINT32_MAX) {
   1.956 +        uint32_t now = NowInSeconds();
   1.957 +        uint64_t timeOfNextExpire = now + timeToNextExpire;
   1.958 +        // If pruning of dead connections is not already scheduled to happen
   1.959 +        // or time found for next connection to expire is is before
   1.960 +        // mTimeOfNextWakeUp, we need to schedule the pruning to happen
   1.961 +        // after timeToNextExpire.
   1.962 +        if (!self->mTimer || timeOfNextExpire < self->mTimeOfNextWakeUp) {
   1.963 +            self->PruneDeadConnectionsAfter(timeToNextExpire);
   1.964 +        }
   1.965 +    } else {
   1.966 +        self->ConditionallyStopPruneDeadConnectionsTimer();
   1.967 +    }
   1.968 +
   1.969 +    // if this entry is empty, we have too many entries,
   1.970 +    // and this doesn't represent some painfully determined
   1.971 +    // red condition, then we can clean it up and restart from
   1.972 +    // yellow
   1.973 +    if (ent->PipelineState()       != PS_RED &&
   1.974 +        self->mCT.Count()          >  125 &&
   1.975 +        ent->mIdleConns.Length()   == 0 &&
   1.976 +        ent->mActiveConns.Length() == 0 &&
   1.977 +        ent->mHalfOpens.Length()   == 0 &&
   1.978 +        ent->mPendingQ.Length()    == 0 &&
   1.979 +        ((!ent->mTestedSpdy && !ent->mUsingSpdy) ||
   1.980 +         !gHttpHandler->IsSpdyEnabled() ||
   1.981 +         self->mCT.Count() > 300)) {
   1.982 +        LOG(("    removing empty connection entry\n"));
   1.983 +        return PL_DHASH_REMOVE;
   1.984 +    }
   1.985 +
   1.986 +    // otherwise use this opportunity to compact our arrays...
   1.987 +    ent->mIdleConns.Compact();
   1.988 +    ent->mActiveConns.Compact();
   1.989 +    ent->mPendingQ.Compact();
   1.990 +
   1.991 +    return PL_DHASH_NEXT;
   1.992 +}
   1.993 +
   1.994 +PLDHashOperator
   1.995 +nsHttpConnectionMgr::ShutdownPassCB(const nsACString &key,
   1.996 +                                    nsAutoPtr<nsConnectionEntry> &ent,
   1.997 +                                    void *closure)
   1.998 +{
   1.999 +    nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
  1.1000 +
  1.1001 +    nsHttpTransaction *trans;
  1.1002 +    nsHttpConnection *conn;
  1.1003 +
  1.1004 +    // close all active connections
  1.1005 +    while (ent->mActiveConns.Length()) {
  1.1006 +        conn = ent->mActiveConns[0];
  1.1007 +
  1.1008 +        ent->mActiveConns.RemoveElementAt(0);
  1.1009 +        self->DecrementActiveConnCount(conn);
  1.1010 +
  1.1011 +        conn->Close(NS_ERROR_ABORT);
  1.1012 +        NS_RELEASE(conn);
  1.1013 +    }
  1.1014 +
  1.1015 +    // close all idle connections
  1.1016 +    while (ent->mIdleConns.Length()) {
  1.1017 +        conn = ent->mIdleConns[0];
  1.1018 +
  1.1019 +        ent->mIdleConns.RemoveElementAt(0);
  1.1020 +        self->mNumIdleConns--;
  1.1021 +
  1.1022 +        conn->Close(NS_ERROR_ABORT);
  1.1023 +        NS_RELEASE(conn);
  1.1024 +    }
  1.1025 +    // If all idle connections are removed,
  1.1026 +    // we can stop pruning dead connections.
  1.1027 +    self->ConditionallyStopPruneDeadConnectionsTimer();
  1.1028 +
  1.1029 +    // close all pending transactions
  1.1030 +    while (ent->mPendingQ.Length()) {
  1.1031 +        trans = ent->mPendingQ[0];
  1.1032 +
  1.1033 +        ent->mPendingQ.RemoveElementAt(0);
  1.1034 +
  1.1035 +        trans->Close(NS_ERROR_ABORT);
  1.1036 +        NS_RELEASE(trans);
  1.1037 +    }
  1.1038 +
  1.1039 +    // close all half open tcp connections
  1.1040 +    for (int32_t i = ((int32_t) ent->mHalfOpens.Length()) - 1; i >= 0; i--)
  1.1041 +        ent->mHalfOpens[i]->Abandon();
  1.1042 +
  1.1043 +    return PL_DHASH_REMOVE;
  1.1044 +}
  1.1045 +
  1.1046 +//-----------------------------------------------------------------------------
  1.1047 +
  1.1048 +bool
  1.1049 +nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent, bool considerAll)
  1.1050 +{
  1.1051 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.1052 +
  1.1053 +    LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
  1.1054 +         ent->mConnInfo->HashKey().get()));
  1.1055 +
  1.1056 +    ProcessSpdyPendingQ(ent);
  1.1057 +
  1.1058 +    nsHttpTransaction *trans;
  1.1059 +    nsresult rv;
  1.1060 +    bool dispatchedSuccessfully = false;
  1.1061 +    int dispatchCount = 0;
  1.1062 +#ifdef WTF_DEBUG
  1.1063 +    uint32_t total = ent->mPendingQ.Length();
  1.1064 +#endif
  1.1065 +
  1.1066 +    // if !considerAll iterate the pending list until one is dispatched successfully.
  1.1067 +    // Keep iterating afterwards only until a transaction fails to dispatch.
  1.1068 +    // if considerAll == true then try and dispatch all items.
  1.1069 +    for (uint32_t i = 0; i < ent->mPendingQ.Length(); ) {
  1.1070 +        trans = ent->mPendingQ[i];
  1.1071 +
  1.1072 +        // When this entry has already established a half-open
  1.1073 +        // connection, we want to prevent any duplicate half-open
  1.1074 +        // connections from being established and bound to this
  1.1075 +        // transaction.
  1.1076 +        bool alreadyHalfOpen = false;
  1.1077 +        if (ent->SupportsPipelining()) {
  1.1078 +            alreadyHalfOpen = (ent->UnconnectedHalfOpens() > 0);
  1.1079 +        } else {
  1.1080 +            for (int32_t j = 0; j < ((int32_t) ent->mHalfOpens.Length()); ++j) {
  1.1081 +                if (ent->mHalfOpens[j]->Transaction() == trans) {
  1.1082 +                    alreadyHalfOpen = true;
  1.1083 +                    break;
  1.1084 +                }
  1.1085 +            }
  1.1086 +        }
  1.1087 +
  1.1088 +        rv = TryDispatchTransaction(ent, alreadyHalfOpen, trans);
  1.1089 +        if (NS_SUCCEEDED(rv) || (rv != NS_ERROR_NOT_AVAILABLE)) {
  1.1090 +            if (NS_SUCCEEDED(rv))
  1.1091 +                LOG(("  dispatching pending transaction...\n"));
  1.1092 +            else
  1.1093 +                LOG(("  removing pending transaction based on "
  1.1094 +                     "TryDispatchTransaction returning hard error %x\n", rv));
  1.1095 +
  1.1096 +            if (ent->mPendingQ.RemoveElement(trans)) {
  1.1097 +                dispatchedSuccessfully = true;
  1.1098 +                dispatchCount++;
  1.1099 +                NS_RELEASE(trans);
  1.1100 +                continue; // dont ++i as we just made the array shorter
  1.1101 +            }
  1.1102 +
  1.1103 +            LOG(("  transaction not found in pending queue\n"));
  1.1104 +        }
  1.1105 +
  1.1106 +        // We want to keep walking the dispatch table to ensure requests
  1.1107 +        // get combined properly.
  1.1108 +        //if (dispatchedSuccessfully && !considerAll)
  1.1109 +        //    break;
  1.1110 +
  1.1111 +        ++i;
  1.1112 +    }
  1.1113 +
  1.1114 +#ifdef WTF_DEBUG
  1.1115 +    if (dispatchedSuccessfully) {
  1.1116 +        fprintf(stderr, "WTF-queue: Dispatched %d/%d pending transactions for %s\n",
  1.1117 +                dispatchCount, total, ent->mConnInfo->Host());
  1.1118 +        return true;
  1.1119 +    }
  1.1120 +#endif
  1.1121 +
  1.1122 +    return dispatchedSuccessfully;
  1.1123 +}
  1.1124 +
  1.1125 +bool
  1.1126 +nsHttpConnectionMgr::ProcessPendingQForEntry(nsHttpConnectionInfo *ci)
  1.1127 +{
  1.1128 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.1129 +
  1.1130 +    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
  1.1131 +    if (ent)
  1.1132 +        return ProcessPendingQForEntry(ent, false);
  1.1133 +    return false;
  1.1134 +}
  1.1135 +
  1.1136 +bool
  1.1137 +nsHttpConnectionMgr::SupportsPipelining(nsHttpConnectionInfo *ci)
  1.1138 +{
  1.1139 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.1140 +
  1.1141 +    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
  1.1142 +    if (ent)
  1.1143 +        return ent->SupportsPipelining();
  1.1144 +    return false;
  1.1145 +}
  1.1146 +
  1.1147 +// nsHttpPipelineFeedback used to hold references across events
  1.1148 +
  1.1149 +class nsHttpPipelineFeedback
  1.1150 +{
  1.1151 +public:
  1.1152 +    nsHttpPipelineFeedback(nsHttpConnectionInfo *ci,
  1.1153 +                           nsHttpConnectionMgr::PipelineFeedbackInfoType info,
  1.1154 +                           nsHttpConnection *conn, uint32_t data)
  1.1155 +        : mConnInfo(ci)
  1.1156 +        , mConn(conn)
  1.1157 +        , mInfo(info)
  1.1158 +        , mData(data)
  1.1159 +        {
  1.1160 +        }
  1.1161 +
  1.1162 +    ~nsHttpPipelineFeedback()
  1.1163 +    {
  1.1164 +    }
  1.1165 +
  1.1166 +    nsRefPtr<nsHttpConnectionInfo> mConnInfo;
  1.1167 +    nsRefPtr<nsHttpConnection> mConn;
  1.1168 +    nsHttpConnectionMgr::PipelineFeedbackInfoType mInfo;
  1.1169 +    uint32_t mData;
  1.1170 +};
  1.1171 +
  1.1172 +void
  1.1173 +nsHttpConnectionMgr::PipelineFeedbackInfo(nsHttpConnectionInfo *ci,
  1.1174 +                                          PipelineFeedbackInfoType info,
  1.1175 +                                          nsHttpConnection *conn,
  1.1176 +                                          uint32_t data)
  1.1177 +{
  1.1178 +    if (!ci)
  1.1179 +        return;
  1.1180 +
  1.1181 +    // Post this to the socket thread if we are not running there already
  1.1182 +    if (PR_GetCurrentThread() != gSocketThread) {
  1.1183 +        nsHttpPipelineFeedback *fb = new nsHttpPipelineFeedback(ci, info,
  1.1184 +                                                                conn, data);
  1.1185 +
  1.1186 +        nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessFeedback,
  1.1187 +                                0, fb);
  1.1188 +        if (NS_FAILED(rv))
  1.1189 +            delete fb;
  1.1190 +        return;
  1.1191 +    }
  1.1192 +
  1.1193 +    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
  1.1194 +
  1.1195 +    if (ent)
  1.1196 +        ent->OnPipelineFeedbackInfo(info, conn, data);
  1.1197 +}
  1.1198 +
  1.1199 +void
  1.1200 +nsHttpConnectionMgr::ReportFailedToProcess(nsIURI *uri)
  1.1201 +{
  1.1202 +    MOZ_ASSERT(uri);
  1.1203 +
  1.1204 +    nsAutoCString host;
  1.1205 +    int32_t port = -1;
  1.1206 +    nsAutoCString username;
  1.1207 +    bool usingSSL = false;
  1.1208 +    bool isHttp = false;
  1.1209 +
  1.1210 +    nsresult rv = uri->SchemeIs("https", &usingSSL);
  1.1211 +    if (NS_SUCCEEDED(rv) && usingSSL)
  1.1212 +        isHttp = true;
  1.1213 +    if (NS_SUCCEEDED(rv) && !isHttp)
  1.1214 +        rv = uri->SchemeIs("http", &isHttp);
  1.1215 +    if (NS_SUCCEEDED(rv))
  1.1216 +        rv = uri->GetAsciiHost(host);
  1.1217 +    if (NS_SUCCEEDED(rv))
  1.1218 +        rv = uri->GetPort(&port);
  1.1219 +    if (NS_SUCCEEDED(rv))
  1.1220 +        uri->GetUsername(username);
  1.1221 +    if (NS_FAILED(rv) || !isHttp || host.IsEmpty())
  1.1222 +        return;
  1.1223 +
  1.1224 +    // report the event for all the permutations of anonymous and
  1.1225 +    // private versions of this host
  1.1226 +    nsRefPtr<nsHttpConnectionInfo> ci =
  1.1227 +        new nsHttpConnectionInfo(host, port, username, nullptr, usingSSL);
  1.1228 +    ci->SetAnonymous(false);
  1.1229 +    ci->SetPrivate(false);
  1.1230 +    PipelineFeedbackInfo(ci, RedCorruptedContent, nullptr, 0);
  1.1231 +
  1.1232 +    ci = ci->Clone();
  1.1233 +    ci->SetAnonymous(false);
  1.1234 +    ci->SetPrivate(true);
  1.1235 +    PipelineFeedbackInfo(ci, RedCorruptedContent, nullptr, 0);
  1.1236 +
  1.1237 +    ci = ci->Clone();
  1.1238 +    ci->SetAnonymous(true);
  1.1239 +    ci->SetPrivate(false);
  1.1240 +    PipelineFeedbackInfo(ci, RedCorruptedContent, nullptr, 0);
  1.1241 +
  1.1242 +    ci = ci->Clone();
  1.1243 +    ci->SetAnonymous(true);
  1.1244 +    ci->SetPrivate(true);
  1.1245 +    PipelineFeedbackInfo(ci, RedCorruptedContent, nullptr, 0);
  1.1246 +}
  1.1247 +
  1.1248 +// we're at the active connection limit if any one of the following conditions is true:
  1.1249 +//  (1) at max-connections
  1.1250 +//  (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
  1.1251 +//  (3) keep-alive disabled and at max-connections-per-server
  1.1252 +bool
  1.1253 +nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, uint32_t caps)
  1.1254 +{
  1.1255 +    nsHttpConnectionInfo *ci = ent->mConnInfo;
  1.1256 +
  1.1257 +    LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
  1.1258 +        ci->HashKey().get(), caps));
  1.1259 +
  1.1260 +    // update maxconns if potentially limited by the max socket count
  1.1261 +    // this requires a dynamic reduction in the max socket count to a point
  1.1262 +    // lower than the max-connections pref.
  1.1263 +    uint32_t maxSocketCount = gHttpHandler->MaxSocketCount();
  1.1264 +    if (mMaxConns > maxSocketCount) {
  1.1265 +        mMaxConns = maxSocketCount;
  1.1266 +        LOG(("nsHttpConnectionMgr %p mMaxConns dynamically reduced to %u",
  1.1267 +             this, mMaxConns));
  1.1268 +    }
  1.1269 +
  1.1270 +    // If there are more active connections than the global limit, then we're
  1.1271 +    // done. Purging idle connections won't get us below it.
  1.1272 +    if (mNumActiveConns >= mMaxConns) {
  1.1273 +        LOG(("  num active conns == max conns\n"));
  1.1274 +        return true;
  1.1275 +    }
  1.1276 +
  1.1277 +    // Add in the in-progress tcp connections, we will assume they are
  1.1278 +    // keepalive enabled.
  1.1279 +    // Exclude half-open's that has already created a usable connection.
  1.1280 +    // This prevents the limit being stuck on ipv6 connections that
  1.1281 +    // eventually time out after typical 21 seconds of no ACK+SYN reply.
  1.1282 +    uint32_t totalCount =
  1.1283 +        ent->mActiveConns.Length() + ent->UnconnectedHalfOpens();
  1.1284 +
  1.1285 +    uint16_t maxPersistConns;
  1.1286 +
  1.1287 +    if (ci->UsingHttpProxy() && !ci->UsingConnect())
  1.1288 +        maxPersistConns = mMaxPersistConnsPerProxy;
  1.1289 +    else
  1.1290 +        maxPersistConns = mMaxPersistConnsPerHost;
  1.1291 +
  1.1292 +    LOG(("   connection count = %d, limit %d\n", totalCount, maxPersistConns));
  1.1293 +
  1.1294 +    // use >= just to be safe
  1.1295 +    bool result = (totalCount >= maxPersistConns);
  1.1296 +    LOG(("  result: %s", result ? "true" : "false"));
  1.1297 +    return result;
  1.1298 +}
  1.1299 +
  1.1300 +void
  1.1301 +nsHttpConnectionMgr::ClosePersistentConnections(nsConnectionEntry *ent)
  1.1302 +{
  1.1303 +    LOG(("nsHttpConnectionMgr::ClosePersistentConnections [ci=%s]\n",
  1.1304 +         ent->mConnInfo->HashKey().get()));
  1.1305 +    while (ent->mIdleConns.Length()) {
  1.1306 +        nsHttpConnection *conn = ent->mIdleConns[0];
  1.1307 +        ent->mIdleConns.RemoveElementAt(0);
  1.1308 +        mNumIdleConns--;
  1.1309 +        conn->Close(NS_ERROR_ABORT);
  1.1310 +        NS_RELEASE(conn);
  1.1311 +    }
  1.1312 +
  1.1313 +    int32_t activeCount = ent->mActiveConns.Length();
  1.1314 +    for (int32_t i=0; i < activeCount; i++)
  1.1315 +        ent->mActiveConns[i]->DontReuse();
  1.1316 +}
  1.1317 +
  1.1318 +PLDHashOperator
  1.1319 +nsHttpConnectionMgr::ClosePersistentConnectionsCB(const nsACString &key,
  1.1320 +                                                  nsAutoPtr<nsConnectionEntry> &ent,
  1.1321 +                                                  void *closure)
  1.1322 +{
  1.1323 +    nsHttpConnectionMgr *self = static_cast<nsHttpConnectionMgr *>(closure);
  1.1324 +    self->ClosePersistentConnections(ent);
  1.1325 +    return PL_DHASH_NEXT;
  1.1326 +}
  1.1327 +
  1.1328 +bool
  1.1329 +nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent,
  1.1330 +                                         bool ignorePossibleSpdyConnections)
  1.1331 +{
  1.1332 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.1333 +
  1.1334 +    // If this host is trying to negotiate a SPDY session right now,
  1.1335 +    // don't create any new ssl connections until the result of the
  1.1336 +    // negotiation is known.
  1.1337 +
  1.1338 +    bool doRestrict = ent->mConnInfo->UsingSSL() &&
  1.1339 +        gHttpHandler->IsSpdyEnabled() &&
  1.1340 +        ((!ent->mTestedSpdy && !ignorePossibleSpdyConnections) ||
  1.1341 +         ent->mUsingSpdy) &&
  1.1342 +        (ent->mHalfOpens.Length() || ent->mActiveConns.Length());
  1.1343 +
  1.1344 +    // If there are no restrictions, we are done
  1.1345 +    if (!doRestrict)
  1.1346 +        return false;
  1.1347 +
  1.1348 +    // If the restriction is based on a tcp handshake in progress
  1.1349 +    // let that connect and then see if it was SPDY or not
  1.1350 +    if (ent->UnconnectedHalfOpens() && !ignorePossibleSpdyConnections)
  1.1351 +        return true;
  1.1352 +
  1.1353 +    // There is a concern that a host is using a mix of HTTP/1 and SPDY.
  1.1354 +    // In that case we don't want to restrict connections just because
  1.1355 +    // there is a single active HTTP/1 session in use.
  1.1356 +    if (ent->mUsingSpdy && ent->mActiveConns.Length()) {
  1.1357 +        bool confirmedRestrict = false;
  1.1358 +        for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
  1.1359 +            nsHttpConnection *conn = ent->mActiveConns[index];
  1.1360 +            if (!conn->ReportedNPN() || conn->CanDirectlyActivate()) {
  1.1361 +                confirmedRestrict = true;
  1.1362 +                break;
  1.1363 +            }
  1.1364 +        }
  1.1365 +        doRestrict = confirmedRestrict;
  1.1366 +        if (!confirmedRestrict) {
  1.1367 +            LOG(("nsHttpConnectionMgr spdy connection restriction to "
  1.1368 +                 "%s bypassed.\n", ent->mConnInfo->Host()));
  1.1369 +        }
  1.1370 +    }
  1.1371 +    return doRestrict;
  1.1372 +}
  1.1373 +
  1.1374 +// returns NS_OK if a connection was started
  1.1375 +// return NS_ERROR_NOT_AVAILABLE if a new connection cannot be made due to
  1.1376 +//        ephemeral limits
  1.1377 +// returns other NS_ERROR on hard failure conditions
  1.1378 +nsresult
  1.1379 +nsHttpConnectionMgr::MakeNewConnection(nsConnectionEntry *ent,
  1.1380 +                                       nsHttpTransaction *trans)
  1.1381 +{
  1.1382 +    LOG(("nsHttpConnectionMgr::MakeNewConnection %p ent=%p trans=%p",
  1.1383 +         this, ent, trans));
  1.1384 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.1385 +
  1.1386 +    uint32_t halfOpenLength = ent->mHalfOpens.Length();
  1.1387 +    for (uint32_t i = 0; i < halfOpenLength; i++) {
  1.1388 +        if (ent->mHalfOpens[i]->IsSpeculative()) {
  1.1389 +            // We've found a speculative connection in the half
  1.1390 +            // open list. Remove the speculative bit from it and that
  1.1391 +            // connection can later be used for this transaction
  1.1392 +            // (or another one in the pending queue) - we don't
  1.1393 +            // need to open a new connection here.
  1.1394 +            LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s]\n"
  1.1395 +                 "Found a speculative half open connection\n",
  1.1396 +                 ent->mConnInfo->HashKey().get()));
  1.1397 +            ent->mHalfOpens[i]->SetSpeculative(false);
  1.1398 +
  1.1399 +            // return OK because we have essentially opened a new connection
  1.1400 +            // by converting a speculative half-open to general use
  1.1401 +            return NS_OK;
  1.1402 +        }
  1.1403 +    }
  1.1404 +
  1.1405 +    // If this host is trying to negotiate a SPDY session right now,
  1.1406 +    // don't create any new connections until the result of the
  1.1407 +    // negotiation is known.
  1.1408 +    if (!(trans->Caps() & NS_HTTP_DISALLOW_SPDY) &&
  1.1409 +        (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) &&
  1.1410 +        RestrictConnections(ent)) {
  1.1411 +        LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s] "
  1.1412 +             "Not Available Due to RestrictConnections()\n",
  1.1413 +             ent->mConnInfo->HashKey().get()));
  1.1414 +        return NS_ERROR_NOT_AVAILABLE;
  1.1415 +    }
  1.1416 +
  1.1417 +    // We need to make a new connection. If that is going to exceed the
  1.1418 +    // global connection limit then try and free up some room by closing
  1.1419 +    // an idle connection to another host. We know it won't select "ent"
  1.1420 +    // beacuse we have already determined there are no idle connections
  1.1421 +    // to our destination
  1.1422 +
  1.1423 +    if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) && mNumIdleConns)
  1.1424 +        mCT.Enumerate(PurgeExcessIdleConnectionsCB, this);
  1.1425 +
  1.1426 +    if ((mNumIdleConns + mNumActiveConns + 1 >= mMaxConns) &&
  1.1427 +        mNumActiveConns && gHttpHandler->IsSpdyEnabled())
  1.1428 +        mCT.Enumerate(PurgeExcessSpdyConnectionsCB, this);
  1.1429 +
  1.1430 +    if (AtActiveConnectionLimit(ent, trans->Caps()))
  1.1431 +        return NS_ERROR_NOT_AVAILABLE;
  1.1432 +
  1.1433 +#ifdef WTF_DEBUG
  1.1434 +        fprintf(stderr, "WTF: MakeNewConnection() is creating a transport (pipelines %d) for host %s\n",
  1.1435 +                ent->SupportsPipelining(), ent->mConnInfo->Host());
  1.1436 +#endif
  1.1437 +    nsresult rv = CreateTransport(ent, trans, trans->Caps(), false);
  1.1438 +    if (NS_FAILED(rv)) {
  1.1439 +        /* hard failure */
  1.1440 +        LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s trans = %p] "
  1.1441 +             "CreateTransport() hard failure.\n",
  1.1442 +             ent->mConnInfo->HashKey().get(), trans));
  1.1443 +        trans->Close(rv);
  1.1444 +        if (rv == NS_ERROR_NOT_AVAILABLE)
  1.1445 +            rv = NS_ERROR_FAILURE;
  1.1446 +        return rv;
  1.1447 +    }
  1.1448 +
  1.1449 +    return NS_OK;
  1.1450 +}
  1.1451 +
  1.1452 +bool
  1.1453 +nsHttpConnectionMgr::AddToBestPipeline(nsConnectionEntry *ent,
  1.1454 +                                           nsHttpTransaction *trans,
  1.1455 +                                           nsHttpTransaction::Classifier classification,
  1.1456 +                                           uint16_t depthLimit)
  1.1457 +{
  1.1458 +    if (classification == nsAHttpTransaction::CLASS_SOLO)
  1.1459 +        return false;
  1.1460 +
  1.1461 +    uint32_t maxdepth = ent->MaxPipelineDepth(classification);
  1.1462 +    if (maxdepth == 0) {
  1.1463 +        ent->CreditPenalty();
  1.1464 +        maxdepth = ent->MaxPipelineDepth(classification);
  1.1465 +    }
  1.1466 +
  1.1467 +    if (ent->PipelineState() == PS_RED)
  1.1468 +        return false;
  1.1469 +
  1.1470 +    if (ent->PipelineState() == PS_YELLOW && ent->mYellowConnection)
  1.1471 +        return false;
  1.1472 +
  1.1473 +    // The maximum depth of a pipeline in yellow is 1 pipeline of
  1.1474 +    // depth 2 for entire CI. When that transaction completes successfully
  1.1475 +    // we transition to green and that expands the allowed depth
  1.1476 +    // to any number of pipelines of up to depth 4.  When a transaction
  1.1477 +    // queued at position 3 or deeper succeeds we open it all the way
  1.1478 +    // up to depths limited only by configuration. The staggered start
  1.1479 +    // in green is simply because a successful yellow test of depth=2
  1.1480 +    // might really just be a race condition (i.e. depth=1 from the
  1.1481 +    // server's point of view), while depth=3 is a stronger indicator -
  1.1482 +    // keeping the pipelines to a modest depth during that period limits
  1.1483 +    // the damage if something is going to go wrong.
  1.1484 +
  1.1485 +    maxdepth = std::min<uint32_t>(maxdepth, depthLimit);
  1.1486 +
  1.1487 +    if (maxdepth < 2)
  1.1488 +        return false;
  1.1489 +
  1.1490 +    // Find out how many requests of this class we have
  1.1491 +    uint32_t sameClass = 0;
  1.1492 +    uint32_t allClasses = ent->mPendingQ.Length();
  1.1493 +    for (uint32_t i = 0; i < allClasses; ++i) {
  1.1494 +        if (trans != ent->mPendingQ[i] &&
  1.1495 +            classification == ent->mPendingQ[i]->Classification()) {
  1.1496 +            sameClass++;
  1.1497 +        }
  1.1498 +    }
  1.1499 +
  1.1500 +    nsAHttpTransaction *activeTrans;
  1.1501 +    nsHttpPipeline *pipeline;
  1.1502 +    nsHttpConnection *bestConn = nullptr;
  1.1503 +    uint32_t activeCount = ent->mActiveConns.Length();
  1.1504 +    uint32_t pipelineDepth;
  1.1505 +    uint32_t requestLen;
  1.1506 +    uint32_t totalDepth = 0;
  1.1507 +
  1.1508 +    // Now, try to find the best pipeline
  1.1509 +    nsTArray<nsHttpConnection *> validConns;
  1.1510 +    nsTArray<nsHttpConnection *> betterConns;
  1.1511 +    nsTArray<nsHttpConnection *> bestConns;
  1.1512 +    uint32_t numPipelines = 0;
  1.1513 +
  1.1514 +    for (uint32_t i = 0; i < activeCount; ++i) {
  1.1515 +        nsHttpConnection *conn = ent->mActiveConns[i];
  1.1516 +
  1.1517 +        if (!conn->SupportsPipelining())
  1.1518 +            continue;
  1.1519 +
  1.1520 +        activeTrans = conn->Transaction();
  1.1521 +
  1.1522 +        if (!activeTrans ||
  1.1523 +            activeTrans->IsDone() ||
  1.1524 +            NS_FAILED(activeTrans->Status()))
  1.1525 +            continue;
  1.1526 +
  1.1527 +        pipeline = activeTrans->QueryPipeline();
  1.1528 +        if (!pipeline)
  1.1529 +            continue;
  1.1530 +
  1.1531 +        numPipelines++;
  1.1532 +
  1.1533 +        pipelineDepth = activeTrans->PipelineDepth();
  1.1534 +        requestLen = pipeline->RequestDepth();
  1.1535 +
  1.1536 +        totalDepth += pipelineDepth;
  1.1537 +
  1.1538 +        // If we're within striking distance of our pipeline
  1.1539 +        // packaging goal, give a little slack on the depth
  1.1540 +        // limit to allow us to try to get there. Don't give
  1.1541 +        // too much slack, though, or we'll tend to have
  1.1542 +        // request packages of the same size when we have
  1.1543 +        // many content elements appear at once.
  1.1544 +        if (maxdepth +
  1.1545 +              PR_MIN(mMaxOptimisticPipelinedRequests,
  1.1546 +                     requestLen + allClasses)
  1.1547 +              <= pipelineDepth)
  1.1548 +            continue;
  1.1549 +
  1.1550 +        validConns.AppendElement(conn);
  1.1551 +
  1.1552 +        // Prefer a pipeline that either has at least two requests
  1.1553 +        // queued already, or for which we can add multiple requests
  1.1554 +        if (requestLen + allClasses < mMaxOptimisticPipelinedRequests)
  1.1555 +            continue;
  1.1556 +
  1.1557 +        betterConns.AppendElement(conn);
  1.1558 +
  1.1559 +        // Prefer a pipeline with the same classification if 
  1.1560 +        // our current classes will put it over the line
  1.1561 +        if (conn->Classification() != classification)
  1.1562 +            continue;
  1.1563 +        if (requestLen + sameClass < mMaxOptimisticPipelinedRequests)
  1.1564 +            continue;
  1.1565 +
  1.1566 +        bestConns.AppendElement(conn);
  1.1567 +    }
  1.1568 +
  1.1569 +    const char *type;
  1.1570 +    if (bestConns.Length()) {
  1.1571 +        type = "best";
  1.1572 +        bestConn = bestConns[rand()%bestConns.Length()];
  1.1573 +    } else if (betterConns.Length()) {
  1.1574 +        type = "better";
  1.1575 +        bestConn = betterConns[rand()%betterConns.Length()];
  1.1576 +    } else if (validConns.Length() && totalDepth == 0) {
  1.1577 +        // We only use valid conns if it's a last resort
  1.1578 +        // (No other requests are pending or in flight)
  1.1579 +        type = "valid";
  1.1580 +        bestConn = validConns[rand()%validConns.Length()];
  1.1581 +    } else {
  1.1582 +        return false;
  1.1583 +    }
  1.1584 +
  1.1585 +    activeTrans = bestConn->Transaction();
  1.1586 +    nsresult rv = activeTrans->AddTransaction(trans);
  1.1587 +    if (NS_FAILED(rv))
  1.1588 +        return false;
  1.1589 +
  1.1590 +    LOG(("   scheduling trans %p on pipeline at position %d, type %s\n",
  1.1591 +         trans, trans->PipelinePosition(), type));
  1.1592 +
  1.1593 +#ifdef WTF_DEBUG
  1.1594 +    pipeline = activeTrans->QueryPipeline();
  1.1595 +    fprintf(stderr,
  1.1596 +            "WTF-depth: Added trans to %s of %d/%d/%d/%d pipelines. Request len %d/%d/%d for %s\n",
  1.1597 +            type, bestConns.Length(), betterConns.Length(), validConns.Length(),
  1.1598 +            numPipelines, pipeline->RequestDepth(), activeTrans->PipelineDepth(),
  1.1599 +            maxdepth, ent->mConnInfo->Host());
  1.1600 +#endif
  1.1601 +
  1.1602 +    if ((ent->PipelineState() == PS_YELLOW) && (trans->PipelinePosition() > 1))
  1.1603 +        ent->SetYellowConnection(bestConn);
  1.1604 +
  1.1605 +    if (!trans->GetPendingTime().IsNull()) {
  1.1606 +        if (trans->UsesPipelining())
  1.1607 +            AccumulateTimeDelta(
  1.1608 +                Telemetry::TRANSACTION_WAIT_TIME_HTTP_PIPELINES,
  1.1609 +                trans->GetPendingTime(), TimeStamp::Now());
  1.1610 +        else
  1.1611 +            AccumulateTimeDelta(
  1.1612 +                Telemetry::TRANSACTION_WAIT_TIME_HTTP,
  1.1613 +                trans->GetPendingTime(), TimeStamp::Now());
  1.1614 +        trans->SetPendingTime(false);
  1.1615 +    }
  1.1616 +    return true;
  1.1617 +}
  1.1618 +
  1.1619 +bool
  1.1620 +nsHttpConnectionMgr::IsUnderPressure(nsConnectionEntry *ent,
  1.1621 +                                   nsHttpTransaction::Classifier classification)
  1.1622 +{
  1.1623 +    // A connection entry is declared to be "under pressure" if most of the
  1.1624 +    // allowed parallel connections are already used up. In that case we want to
  1.1625 +    // favor existing pipelines over more parallelism so as to reserve any
  1.1626 +    // unused parallel connections for types that don't have existing pipelines.
  1.1627 +    //
  1.1628 +    // The definition of connection pressure is a pretty liberal one here - that
  1.1629 +    // is why we are using the more restrictive maxPersist* counters.
  1.1630 +    //
  1.1631 +    // Pipelines are also favored when the requested classification is already
  1.1632 +    // using 3 or more of the connections. Failure to do this could result in
  1.1633 +    // one class (e.g. images) establishing self replenishing queues on all the
  1.1634 +    // connections that would starve the other transaction types.
  1.1635 +
  1.1636 +    int32_t currentConns = ent->mActiveConns.Length();
  1.1637 +    int32_t maxConns =
  1.1638 +        (ent->mConnInfo->UsingHttpProxy() && !ent->mConnInfo->UsingConnect()) ?
  1.1639 +        mMaxPersistConnsPerProxy : mMaxPersistConnsPerHost;
  1.1640 +
  1.1641 +    // Leave room for at least 3 distinct types to operate concurrently,
  1.1642 +    // this satisfies the typical {html, js/css, img} page.
  1.1643 +    if (currentConns >= (maxConns - 2))
  1.1644 +        return true;                           /* prefer pipeline */
  1.1645 +
  1.1646 +    int32_t sameClass = 0;
  1.1647 +    for (int32_t i = 0; i < currentConns; ++i)
  1.1648 +        if (classification == ent->mActiveConns[i]->Classification())
  1.1649 +            if (++sameClass == 3)
  1.1650 +                return true;                   /* prefer pipeline */
  1.1651 +
  1.1652 +    return false;                              /* normal behavior */
  1.1653 +}
  1.1654 +
  1.1655 +// returns OK if a connection is found for the transaction
  1.1656 +//   and the transaction is started.
  1.1657 +// returns ERROR_NOT_AVAILABLE if no connection can be found and it
  1.1658 +//   should be queued until circumstances change
  1.1659 +// returns other ERROR when transaction has a hard failure and should
  1.1660 +//   not remain in the pending queue
  1.1661 +nsresult
  1.1662 +nsHttpConnectionMgr::TryDispatchTransaction(nsConnectionEntry *ent,
  1.1663 +                                            bool onlyReusedConnection,
  1.1664 +                                            nsHttpTransaction *trans)
  1.1665 +{
  1.1666 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.1667 +    LOG(("nsHttpConnectionMgr::TryDispatchTransaction without conn "
  1.1668 +         "[ci=%s caps=%x]\n",
  1.1669 +         ent->mConnInfo->HashKey().get(), uint32_t(trans->Caps())));
  1.1670 +
  1.1671 +    nsHttpTransaction::Classifier classification = trans->Classification();
  1.1672 +    uint32_t caps = trans->Caps();
  1.1673 +
  1.1674 +    bool allowNewPipelines = true;
  1.1675 +
  1.1676 +    // no keep-alive means no pipelines either
  1.1677 +    if (!(caps & NS_HTTP_ALLOW_KEEPALIVE))
  1.1678 +        caps = caps & ~NS_HTTP_ALLOW_PIPELINING;
  1.1679 +
  1.1680 +    nsRefPtr<nsHttpConnection> unusedSpdyPersistentConnection;
  1.1681 +
  1.1682 +    // step 0
  1.1683 +    // look for existing spdy connection - that's always best because it is
  1.1684 +    // essentially pipelining without head of line blocking
  1.1685 +
  1.1686 +    if (!(caps & NS_HTTP_DISALLOW_SPDY) && gHttpHandler->IsSpdyEnabled()) {
  1.1687 +        nsRefPtr<nsHttpConnection> conn = GetSpdyPreferredConn(ent);
  1.1688 +        if (conn) {
  1.1689 +            if ((caps & NS_HTTP_ALLOW_KEEPALIVE) || !conn->IsExperienced()) {
  1.1690 +                LOG(("   dispatch to spdy: [conn=%x]\n", conn.get()));
  1.1691 +                trans->RemoveDispatchedAsBlocking();  /* just in case */
  1.1692 +                DispatchTransaction(ent, trans, conn);
  1.1693 +                return NS_OK;
  1.1694 +            }
  1.1695 +            unusedSpdyPersistentConnection = conn;
  1.1696 +        }
  1.1697 +    }
  1.1698 +
  1.1699 +    // If this is not a blocking transaction and the loadgroup for it is
  1.1700 +    // currently processing one or more blocking transactions then we
  1.1701 +    // need to just leave it in the queue until those are complete unless it is
  1.1702 +    // explicitly marked as unblocked.
  1.1703 +    if (!(caps & NS_HTTP_LOAD_AS_BLOCKING)) {
  1.1704 +        if (!(caps & NS_HTTP_LOAD_UNBLOCKED)) {
  1.1705 +            nsILoadGroupConnectionInfo *loadGroupCI = trans->LoadGroupConnectionInfo();
  1.1706 +            if (loadGroupCI) {
  1.1707 +                uint32_t blockers = 0;
  1.1708 +                if (NS_SUCCEEDED(loadGroupCI->GetBlockingTransactionCount(&blockers)) &&
  1.1709 +                    blockers) {
  1.1710 +                    // need to wait for blockers to clear
  1.1711 +                    LOG(("   blocked by load group: [blockers=%d]\n", blockers));
  1.1712 +                    return NS_ERROR_NOT_AVAILABLE;
  1.1713 +                }
  1.1714 +            }
  1.1715 +        }
  1.1716 +    }
  1.1717 +    else {
  1.1718 +        // Mark the transaction and its load group as blocking right now to prevent
  1.1719 +        // other transactions from being reordered in the queue due to slow syns.
  1.1720 +        trans->DispatchedAsBlocking();
  1.1721 +    }
  1.1722 +
  1.1723 +    // step 1: Try a pipeline
  1.1724 +    if (caps & NS_HTTP_ALLOW_PIPELINING &&
  1.1725 +        AddToBestPipeline(ent, trans, classification,
  1.1726 +                          mMaxPipelinedRequests)) {
  1.1727 +        return NS_OK;
  1.1728 +    }
  1.1729 +
  1.1730 +    // XXX: Kill this block? It's new.. but it may be needed for SPDY
  1.1731 +    // Subject most transactions at high parallelism to rate pacing.
  1.1732 +    // It will only be actually submitted to the
  1.1733 +    // token bucket once, and if possible it is granted admission synchronously.
  1.1734 +    // It is important to leave a transaction in the pending queue when blocked by
  1.1735 +    // pacing so it can be found on cancel if necessary.
  1.1736 +    // Transactions that cause blocking or bypass it (e.g. js/css) are not rate
  1.1737 +    // limited.
  1.1738 +    if (gHttpHandler->UseRequestTokenBucket() &&
  1.1739 +        (mNumActiveConns >= mNumSpdyActiveConns) && // just check for robustness sake
  1.1740 +        ((mNumActiveConns - mNumSpdyActiveConns) >= gHttpHandler->RequestTokenBucketMinParallelism()) &&
  1.1741 +        !(caps & (NS_HTTP_LOAD_AS_BLOCKING | NS_HTTP_LOAD_UNBLOCKED))) {
  1.1742 +        if (!trans->TryToRunPacedRequest()) {
  1.1743 +            LOG(("   blocked due to rate pacing\n"));
  1.1744 +            return NS_ERROR_NOT_AVAILABLE;
  1.1745 +        }
  1.1746 +    }
  1.1747 +
  1.1748 +    // Step 2: Decide if we should forbid new pipeline creation.
  1.1749 +    //
  1.1750 +    // FIXME: We repurposed mMaxOptimisticPipelinedRequests here to mean:
  1.1751 +    // "Don't make a new pipeline until you have this many requests pending and 
  1.1752 +    // no potential connections to put them on". It might be nice to give this
  1.1753 +    // its own pref..
  1.1754 +    if (HasPipelines(ent) &&
  1.1755 +            ent->mPendingQ.Length() < mMaxOptimisticPipelinedRequests &&
  1.1756 +            trans->Classification() != nsAHttpTransaction::CLASS_SOLO &&
  1.1757 +            caps & NS_HTTP_ALLOW_PIPELINING)
  1.1758 +        allowNewPipelines = false;
  1.1759 +
  1.1760 +    // step 3: consider an idle persistent connection
  1.1761 +    if (allowNewPipelines && (caps & NS_HTTP_ALLOW_KEEPALIVE)) {
  1.1762 +        nsRefPtr<nsHttpConnection> conn;
  1.1763 +        while (!conn && (ent->mIdleConns.Length() > 0)) {
  1.1764 +            conn = ent->mIdleConns[0];
  1.1765 +            ent->mIdleConns.RemoveElementAt(0);
  1.1766 +            mNumIdleConns--;
  1.1767 +            nsHttpConnection *temp = conn;
  1.1768 +            NS_RELEASE(temp);
  1.1769 +
  1.1770 +            // we check if the connection can be reused before even checking if
  1.1771 +            // it is a "matching" connection.
  1.1772 +            if (!conn->CanReuse()) {
  1.1773 +                LOG(("   dropping stale connection: [conn=%x]\n", conn.get()));
  1.1774 +                conn->Close(NS_ERROR_ABORT);
  1.1775 +                conn = nullptr;
  1.1776 +            }
  1.1777 +            else {
  1.1778 +                LOG(("   reusing connection [conn=%x]\n", conn.get()));
  1.1779 +                conn->EndIdleMonitoring();
  1.1780 +            }
  1.1781 +
  1.1782 +            // If there are no idle connections left at all, we need to make
  1.1783 +            // sure that we are not pruning dead connections anymore.
  1.1784 +            ConditionallyStopPruneDeadConnectionsTimer();
  1.1785 +        }
  1.1786 +        if (conn) {
  1.1787 +            // This will update the class of the connection to be the class of
  1.1788 +            // the transaction dispatched on it.
  1.1789 +            AddActiveConn(conn, ent);
  1.1790 +            DispatchTransaction(ent, trans, conn);
  1.1791 +            return NS_OK;
  1.1792 +        }
  1.1793 +    }
  1.1794 +
  1.1795 +    // step 4: Maybe make a connection? 
  1.1796 +    if (!onlyReusedConnection && allowNewPipelines) {
  1.1797 +        nsresult rv = MakeNewConnection(ent, trans);
  1.1798 +        if (NS_SUCCEEDED(rv)) {
  1.1799 +            // this function returns NOT_AVAILABLE for asynchronous connects
  1.1800 +            return NS_ERROR_NOT_AVAILABLE;
  1.1801 +        }
  1.1802 +
  1.1803 +        if (rv != NS_ERROR_NOT_AVAILABLE) {
  1.1804 +            // not available return codes should try next step as they are
  1.1805 +            // not hard errors. Other codes should stop now
  1.1806 +            return rv;
  1.1807 +        }
  1.1808 +    }
  1.1809 +
  1.1810 +    // step 5
  1.1811 +    if (unusedSpdyPersistentConnection) {
  1.1812 +        // to avoid deadlocks, we need to throw away this perfectly valid SPDY
  1.1813 +        // connection to make room for a new one that can service a no KEEPALIVE
  1.1814 +        // request
  1.1815 +        unusedSpdyPersistentConnection->DontReuse();
  1.1816 +    }
  1.1817 +
  1.1818 +    // XXX: We dequeue and queue the same url here sometimes..
  1.1819 +#ifdef WTF_DEBUG
  1.1820 +    nsHttpRequestHead *head = trans->RequestHead();
  1.1821 +    fprintf(stderr, "WTF: Queuing url %s%s\n",
  1.1822 +            ent->mConnInfo->Host(),
  1.1823 +            head ? head->RequestURI().BeginReading() : "<unknown?>");
  1.1824 +#endif
  1.1825 +  
  1.1826 +
  1.1827 +    return NS_ERROR_NOT_AVAILABLE;                /* queue it */
  1.1828 +}
  1.1829 +
  1.1830 +nsresult
  1.1831 +nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
  1.1832 +                                         nsHttpTransaction *trans,
  1.1833 +                                         nsHttpConnection *conn)
  1.1834 +{
  1.1835 +    uint32_t caps = trans->Caps();
  1.1836 +    int32_t priority = trans->Priority();
  1.1837 +    nsresult rv;
  1.1838 +
  1.1839 +    LOG(("nsHttpConnectionMgr::DispatchTransaction "
  1.1840 +         "[ci=%s trans=%x caps=%x conn=%x priority=%d]\n",
  1.1841 +         ent->mConnInfo->HashKey().get(), trans, caps, conn, priority));
  1.1842 +
  1.1843 +    // It is possible for a rate-paced transaction to be dispatched independent
  1.1844 +    // of the token bucket when the amount of parallelization has changed or
  1.1845 +    // when a muxed connection (e.g. spdy or pipelines) becomes available.
  1.1846 +    trans->CancelPacing(NS_OK);
  1.1847 +
  1.1848 +    if (conn->UsingSpdy()) {
  1.1849 +        LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s,"
  1.1850 +             "Connection host = %s\n",
  1.1851 +             trans->ConnectionInfo()->Host(),
  1.1852 +             conn->ConnectionInfo()->Host()));
  1.1853 +        rv = conn->Activate(trans, caps, priority);
  1.1854 +        MOZ_ASSERT(NS_SUCCEEDED(rv), "SPDY Cannot Fail Dispatch");
  1.1855 +        if (NS_SUCCEEDED(rv) && !trans->GetPendingTime().IsNull()) {
  1.1856 +            AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_SPDY,
  1.1857 +                trans->GetPendingTime(), TimeStamp::Now());
  1.1858 +            trans->SetPendingTime(false);
  1.1859 +        }
  1.1860 +        return rv;
  1.1861 +    }
  1.1862 +
  1.1863 +    MOZ_ASSERT(conn && !conn->Transaction(),
  1.1864 +               "DispatchTranaction() on non spdy active connection");
  1.1865 +
  1.1866 +    if (!(caps & NS_HTTP_ALLOW_PIPELINING))
  1.1867 +        conn->Classify(nsAHttpTransaction::CLASS_SOLO);
  1.1868 +    else
  1.1869 +        conn->Classify(trans->Classification());
  1.1870 +
  1.1871 +    rv = DispatchAbstractTransaction(ent, trans, caps, conn, priority);
  1.1872 +
  1.1873 +    if (NS_SUCCEEDED(rv) && !trans->GetPendingTime().IsNull()) {
  1.1874 +        if (trans->UsesPipelining())
  1.1875 +            AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_HTTP_PIPELINES,
  1.1876 +                trans->GetPendingTime(), TimeStamp::Now());
  1.1877 +        else
  1.1878 +            AccumulateTimeDelta(Telemetry::TRANSACTION_WAIT_TIME_HTTP,
  1.1879 +                trans->GetPendingTime(), TimeStamp::Now());
  1.1880 +        trans->SetPendingTime(false);
  1.1881 +    }
  1.1882 +    return rv;
  1.1883 +}
  1.1884 +
  1.1885 +
  1.1886 +// Use this method for dispatching nsAHttpTransction's. It can only safely be
  1.1887 +// used upon first use of a connection when NPN has not negotiated SPDY vs
  1.1888 +// HTTP/1 yet as multiplexing onto an existing SPDY session requires a
  1.1889 +// concrete nsHttpTransaction
  1.1890 +nsresult
  1.1891 +nsHttpConnectionMgr::DispatchAbstractTransaction(nsConnectionEntry *ent,
  1.1892 +                                                 nsAHttpTransaction *aTrans,
  1.1893 +                                                 uint32_t caps,
  1.1894 +                                                 nsHttpConnection *conn,
  1.1895 +                                                 int32_t priority)
  1.1896 +{
  1.1897 +    MOZ_ASSERT(!conn->UsingSpdy(),
  1.1898 +               "Spdy Must Not Use DispatchAbstractTransaction");
  1.1899 +    LOG(("nsHttpConnectionMgr::DispatchAbstractTransaction "
  1.1900 +         "[ci=%s trans=%x caps=%x conn=%x]\n",
  1.1901 +         ent->mConnInfo->HashKey().get(), aTrans, caps, conn));
  1.1902 +
  1.1903 +    /* Use pipeline datastructure even if connection does not currently qualify
  1.1904 +       to pipeline this transaction because a different pipeline-eligible
  1.1905 +       transaction might be placed on the active connection. Make an exception
  1.1906 +       for CLASS_SOLO as that connection will never pipeline until it goes
  1.1907 +       quiescent */
  1.1908 +
  1.1909 +    nsRefPtr<nsAHttpTransaction> transaction;
  1.1910 +    nsresult rv;
  1.1911 +    if (conn->Classification() != nsAHttpTransaction::CLASS_SOLO) {
  1.1912 +        LOG(("   using pipeline datastructure.\n"));
  1.1913 +        nsRefPtr<nsHttpPipeline> pipeline;
  1.1914 +        rv = BuildPipeline(ent, aTrans, getter_AddRefs(pipeline));
  1.1915 +        if (!NS_SUCCEEDED(rv))
  1.1916 +            return rv;
  1.1917 +        transaction = pipeline;
  1.1918 +#ifdef WTF_DEBUG
  1.1919 +        if (HasPipelines(ent) &&
  1.1920 +                ent->mPendingQ.Length()+1 < mMaxOptimisticPipelinedRequests) {
  1.1921 +            fprintf(stderr, "WTF-new-bug: New pipeline created from %d idle conns for host %s with %d/%d pending\n",
  1.1922 +                    ent->mIdleConns.Length(), ent->mConnInfo->Host(), ent->mPendingQ.Length(),
  1.1923 +                    mMaxOptimisticPipelinedRequests);
  1.1924 +        } else {
  1.1925 +            fprintf(stderr, "WTF-new: New pipeline created from %d idle conns for host %s with %d/%d pending\n",
  1.1926 +                    ent->mIdleConns.Length(), ent->mConnInfo->Host(), ent->mPendingQ.Length(),
  1.1927 +                    mMaxOptimisticPipelinedRequests);
  1.1928 +        }
  1.1929 +#endif
  1.1930 +    }
  1.1931 +    else {
  1.1932 +        LOG(("   not using pipeline datastructure due to class solo.\n"));
  1.1933 +        transaction = aTrans;
  1.1934 +#ifdef WTF_TEST
  1.1935 +        nsHttpRequestHead *head = transaction->RequestHead();
  1.1936 +        fprintf(stderr, "WTF-order: Pipeline forbidden for url %s%s\n",
  1.1937 +                ent->mConnInfo->Host(),
  1.1938 +                head ? head->RequestURI().BeginReading() : "<unknown?>");
  1.1939 +#endif
  1.1940 +    }
  1.1941 +
  1.1942 +    nsRefPtr<nsConnectionHandle> handle = new nsConnectionHandle(conn);
  1.1943 +
  1.1944 +    // give the transaction the indirect reference to the connection.
  1.1945 +    transaction->SetConnection(handle);
  1.1946 +
  1.1947 +    rv = conn->Activate(transaction, caps, priority);
  1.1948 +    if (NS_FAILED(rv)) {
  1.1949 +        LOG(("  conn->Activate failed [rv=%x]\n", rv));
  1.1950 +        ent->mActiveConns.RemoveElement(conn);
  1.1951 +        if (conn == ent->mYellowConnection)
  1.1952 +            ent->OnYellowComplete();
  1.1953 +        DecrementActiveConnCount(conn);
  1.1954 +        ConditionallyStopTimeoutTick();
  1.1955 +
  1.1956 +        // sever back references to connection, and do so without triggering
  1.1957 +        // a call to ReclaimConnection ;-)
  1.1958 +        transaction->SetConnection(nullptr);
  1.1959 +        NS_RELEASE(handle->mConn);
  1.1960 +        // destroy the connection
  1.1961 +        NS_RELEASE(conn);
  1.1962 +    }
  1.1963 +
  1.1964 +    // As transaction goes out of scope it will drop the last refernece to the
  1.1965 +    // pipeline if activation failed, in which case this will destroy
  1.1966 +    // the pipeline, which will cause each the transactions owned by the
  1.1967 +    // pipeline to be restarted.
  1.1968 +
  1.1969 +    return rv;
  1.1970 +}
  1.1971 +
  1.1972 +nsresult
  1.1973 +nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
  1.1974 +                                   nsAHttpTransaction *firstTrans,
  1.1975 +                                   nsHttpPipeline **result)
  1.1976 +{
  1.1977 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.1978 +
  1.1979 +    /* form a pipeline here even if nothing is pending so that we
  1.1980 +       can stream-feed it as new transactions arrive */
  1.1981 +
  1.1982 +    /* the first transaction can go in unconditionally - 1 transaction
  1.1983 +       on a nsHttpPipeline object is not a real HTTP pipeline */
  1.1984 +
  1.1985 +    nsRefPtr<nsHttpPipeline> pipeline = new nsHttpPipeline();
  1.1986 +    pipeline->AddTransaction(firstTrans);
  1.1987 +    NS_ADDREF(*result = pipeline);
  1.1988 +    return NS_OK;
  1.1989 +}
  1.1990 +
  1.1991 +void
  1.1992 +nsHttpConnectionMgr::ReportProxyTelemetry(nsConnectionEntry *ent)
  1.1993 +{
  1.1994 +    enum { PROXY_NONE = 1, PROXY_HTTP = 2, PROXY_SOCKS = 3 };
  1.1995 +
  1.1996 +    if (!ent->mConnInfo->UsingProxy())
  1.1997 +        Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_NONE);
  1.1998 +    else if (ent->mConnInfo->UsingHttpProxy())
  1.1999 +        Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_HTTP);
  1.2000 +    else
  1.2001 +        Telemetry::Accumulate(Telemetry::HTTP_PROXY_TYPE, PROXY_SOCKS);
  1.2002 +}
  1.2003 +
  1.2004 +nsresult
  1.2005 +nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
  1.2006 +{
  1.2007 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2008 +
  1.2009 +    // since "adds" and "cancels" are processed asynchronously and because
  1.2010 +    // various events might trigger an "add" directly on the socket thread,
  1.2011 +    // we must take care to avoid dispatching a transaction that has already
  1.2012 +    // been canceled (see bug 190001).
  1.2013 +    if (NS_FAILED(trans->Status())) {
  1.2014 +        LOG(("  transaction was canceled... dropping event!\n"));
  1.2015 +        return NS_OK;
  1.2016 +    }
  1.2017 +
  1.2018 +    trans->SetPendingTime();
  1.2019 +
  1.2020 +    nsresult rv = NS_OK;
  1.2021 +    nsHttpConnectionInfo *ci = trans->ConnectionInfo();
  1.2022 +    MOZ_ASSERT(ci);
  1.2023 +
  1.2024 +    nsConnectionEntry *ent = GetOrCreateConnectionEntry(ci);
  1.2025 +
  1.2026 +    // SPDY coalescing of hostnames means we might redirect from this
  1.2027 +    // connection entry onto the preferred one.
  1.2028 +    nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
  1.2029 +    if (preferredEntry && (preferredEntry != ent)) {
  1.2030 +        LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
  1.2031 +             "redirected via coalescing from %s to %s\n", trans,
  1.2032 +             ent->mConnInfo->Host(), preferredEntry->mConnInfo->Host()));
  1.2033 +
  1.2034 +        ent = preferredEntry;
  1.2035 +    }
  1.2036 +
  1.2037 +    ReportProxyTelemetry(ent);
  1.2038 +
  1.2039 +    // Check if the transaction already has a sticky reference to a connection.
  1.2040 +    // If so, then we can just use it directly by transferring its reference
  1.2041 +    // to the new connection variable instead of searching for a new one
  1.2042 +
  1.2043 +    nsAHttpConnection *wrappedConnection = trans->Connection();
  1.2044 +    nsRefPtr<nsHttpConnection> conn;
  1.2045 +    if (wrappedConnection)
  1.2046 +        conn = dont_AddRef(wrappedConnection->TakeHttpConnection());
  1.2047 +
  1.2048 +    if (conn) {
  1.2049 +        MOZ_ASSERT(trans->Caps() & NS_HTTP_STICKY_CONNECTION);
  1.2050 +        MOZ_ASSERT(((int32_t)ent->mActiveConns.IndexOf(conn)) != -1,
  1.2051 +                   "Sticky Connection Not In Active List");
  1.2052 +        LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
  1.2053 +             "sticky connection=%p\n", trans, conn.get()));
  1.2054 +        trans->SetConnection(nullptr);
  1.2055 +#ifdef WTF_TEST
  1.2056 +        fprintf(stderr, "WTF-bad: Sticky connection status on 1 transaction to host %s\n",
  1.2057 +                ent->mConnInfo->Host());
  1.2058 +#endif
  1.2059 +        rv = DispatchTransaction(ent, trans, conn);
  1.2060 +        return rv;
  1.2061 +    } else {
  1.2062 +        // XXX: maybe check the queue first and directly call TryDispatch?
  1.2063 +        InsertTransactionSorted(ent->mPendingQ, trans);
  1.2064 +        NS_ADDREF(trans);
  1.2065 +        ProcessPendingQForEntry(ent, true);
  1.2066 +        return NS_OK;
  1.2067 +    }
  1.2068 +}
  1.2069 +
  1.2070 +
  1.2071 +void
  1.2072 +nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
  1.2073 +                                   nsConnectionEntry *ent)
  1.2074 +{
  1.2075 +    NS_ADDREF(conn);
  1.2076 +    ent->mActiveConns.AppendElement(conn);
  1.2077 +    mNumActiveConns++;
  1.2078 +    ActivateTimeoutTick();
  1.2079 +}
  1.2080 +
  1.2081 +void
  1.2082 +nsHttpConnectionMgr::DecrementActiveConnCount(nsHttpConnection *conn)
  1.2083 +{
  1.2084 +    mNumActiveConns--;
  1.2085 +    if (conn->EverUsedSpdy())
  1.2086 +        mNumSpdyActiveConns--;
  1.2087 +}
  1.2088 +
  1.2089 +void
  1.2090 +nsHttpConnectionMgr::StartedConnect()
  1.2091 +{
  1.2092 +    mNumActiveConns++;
  1.2093 +    ActivateTimeoutTick(); // likely disabled by RecvdConnect()
  1.2094 +}
  1.2095 +
  1.2096 +void
  1.2097 +nsHttpConnectionMgr::RecvdConnect()
  1.2098 +{
  1.2099 +    mNumActiveConns--;
  1.2100 +    ConditionallyStopTimeoutTick();
  1.2101 +}
  1.2102 +
  1.2103 +nsresult
  1.2104 +nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
  1.2105 +                                     nsAHttpTransaction *trans,
  1.2106 +                                     uint32_t caps,
  1.2107 +                                     bool speculative)
  1.2108 +{
  1.2109 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2110 +
  1.2111 +    nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans, caps);
  1.2112 +    if (speculative)
  1.2113 +        sock->SetSpeculative(true);
  1.2114 +    nsresult rv = sock->SetupPrimaryStreams();
  1.2115 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2116 +
  1.2117 +    ent->mHalfOpens.AppendElement(sock);
  1.2118 +    mNumHalfOpenConns++;
  1.2119 +    return NS_OK;
  1.2120 +}
  1.2121 +
  1.2122 +// This function tries to dispatch the pending spdy transactions on
  1.2123 +// the connection entry sent in as an argument. It will do so on the
  1.2124 +// active spdy connection either in that same entry or in the
  1.2125 +// redirected 'preferred' entry for the same coalescing hash key if
  1.2126 +// coalescing is enabled.
  1.2127 +
  1.2128 +void
  1.2129 +nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
  1.2130 +{
  1.2131 +    nsHttpConnection *conn = GetSpdyPreferredConn(ent);
  1.2132 +    if (!conn || !conn->CanDirectlyActivate())
  1.2133 +        return;
  1.2134 +
  1.2135 +    nsTArray<nsHttpTransaction*> leftovers;
  1.2136 +    uint32_t index;
  1.2137 +
  1.2138 +    // Dispatch all the transactions we can
  1.2139 +    for (index = 0;
  1.2140 +         index < ent->mPendingQ.Length() && conn->CanDirectlyActivate();
  1.2141 +         ++index) {
  1.2142 +        nsHttpTransaction *trans = ent->mPendingQ[index];
  1.2143 +
  1.2144 +        if (!(trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
  1.2145 +            trans->Caps() & NS_HTTP_DISALLOW_SPDY) {
  1.2146 +            leftovers.AppendElement(trans);
  1.2147 +            continue;
  1.2148 +        }
  1.2149 +
  1.2150 +        nsresult rv = DispatchTransaction(ent, trans, conn);
  1.2151 +        if (NS_FAILED(rv)) {
  1.2152 +            // this cannot happen, but if due to some bug it does then
  1.2153 +            // close the transaction
  1.2154 +            MOZ_ASSERT(false, "Dispatch SPDY Transaction");
  1.2155 +            LOG(("ProcessSpdyPendingQ Dispatch Transaction failed trans=%p\n",
  1.2156 +                    trans));
  1.2157 +            trans->Close(rv);
  1.2158 +        }
  1.2159 +        NS_RELEASE(trans);
  1.2160 +    }
  1.2161 +
  1.2162 +    // Slurp up the rest of the pending queue into our leftovers bucket (we
  1.2163 +    // might have some left if conn->CanDirectlyActivate returned false)
  1.2164 +    for (; index < ent->mPendingQ.Length(); ++index) {
  1.2165 +        nsHttpTransaction *trans = ent->mPendingQ[index];
  1.2166 +        leftovers.AppendElement(trans);
  1.2167 +    }
  1.2168 +
  1.2169 +    // Put the leftovers back in the pending queue and get rid of the
  1.2170 +    // transactions we dispatched
  1.2171 +    leftovers.SwapElements(ent->mPendingQ);
  1.2172 +    leftovers.Clear();
  1.2173 +}
  1.2174 +
  1.2175 +PLDHashOperator
  1.2176 +nsHttpConnectionMgr::ProcessSpdyPendingQCB(const nsACString &key,
  1.2177 +                                           nsAutoPtr<nsConnectionEntry> &ent,
  1.2178 +                                           void *closure)
  1.2179 +{
  1.2180 +    nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
  1.2181 +    self->ProcessSpdyPendingQ(ent);
  1.2182 +    return PL_DHASH_NEXT;
  1.2183 +}
  1.2184 +
  1.2185 +void
  1.2186 +nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ(int32_t, void *)
  1.2187 +{
  1.2188 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2189 +    LOG(("nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ\n"));
  1.2190 +    mCT.Enumerate(ProcessSpdyPendingQCB, this);
  1.2191 +}
  1.2192 +
  1.2193 +nsHttpConnection *
  1.2194 +nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
  1.2195 +{
  1.2196 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2197 +    MOZ_ASSERT(ent);
  1.2198 +
  1.2199 +    nsConnectionEntry *preferred = GetSpdyPreferredEnt(ent);
  1.2200 +
  1.2201 +    // this entry is spdy-enabled if it is involved in a redirect
  1.2202 +    if (preferred)
  1.2203 +        // all new connections for this entry will use spdy too
  1.2204 +        ent->mUsingSpdy = true;
  1.2205 +    else
  1.2206 +        preferred = ent;
  1.2207 +
  1.2208 +    nsHttpConnection *conn = nullptr;
  1.2209 +
  1.2210 +    if (preferred->mUsingSpdy) {
  1.2211 +        for (uint32_t index = 0;
  1.2212 +             index < preferred->mActiveConns.Length();
  1.2213 +             ++index) {
  1.2214 +            if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
  1.2215 +                conn = preferred->mActiveConns[index];
  1.2216 +                break;
  1.2217 +            }
  1.2218 +        }
  1.2219 +    }
  1.2220 +
  1.2221 +    return conn;
  1.2222 +}
  1.2223 +
  1.2224 +//-----------------------------------------------------------------------------
  1.2225 +
  1.2226 +void
  1.2227 +nsHttpConnectionMgr::OnMsgShutdown(int32_t, void *param)
  1.2228 +{
  1.2229 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2230 +    LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
  1.2231 +
  1.2232 +    mCT.Enumerate(ShutdownPassCB, this);
  1.2233 +
  1.2234 +    if (mTimeoutTick) {
  1.2235 +        mTimeoutTick->Cancel();
  1.2236 +        mTimeoutTick = nullptr;
  1.2237 +        mTimeoutTickArmed = false;
  1.2238 +    }
  1.2239 +    if (mTimer) {
  1.2240 +      mTimer->Cancel();
  1.2241 +      mTimer = nullptr;
  1.2242 +    }
  1.2243 +
  1.2244 +    // signal shutdown complete
  1.2245 +    nsRefPtr<nsIRunnable> runnable =
  1.2246 +        new nsConnEvent(this, &nsHttpConnectionMgr::OnMsgShutdownConfirm,
  1.2247 +                        0, param);
  1.2248 +    NS_DispatchToMainThread(runnable);
  1.2249 +}
  1.2250 +
  1.2251 +void
  1.2252 +nsHttpConnectionMgr::OnMsgShutdownConfirm(int32_t priority, void *param)
  1.2253 +{
  1.2254 +    MOZ_ASSERT(NS_IsMainThread());
  1.2255 +    LOG(("nsHttpConnectionMgr::OnMsgShutdownConfirm\n"));
  1.2256 +
  1.2257 +    bool *shutdown = static_cast<bool*>(param);
  1.2258 +    *shutdown = true;
  1.2259 +}
  1.2260 +
  1.2261 +void
  1.2262 +nsHttpConnectionMgr::OnMsgNewTransaction(int32_t priority, void *param)
  1.2263 +{
  1.2264 +    LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
  1.2265 +
  1.2266 +    nsHttpTransaction *trans = (nsHttpTransaction *) param;
  1.2267 +    trans->SetPriority(priority);
  1.2268 +    nsresult rv = ProcessNewTransaction(trans);
  1.2269 +    if (NS_FAILED(rv))
  1.2270 +        trans->Close(rv); // for whatever its worth
  1.2271 +    NS_RELEASE(trans);
  1.2272 +}
  1.2273 +
  1.2274 +void
  1.2275 +nsHttpConnectionMgr::OnMsgReschedTransaction(int32_t priority, void *param)
  1.2276 +{
  1.2277 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2278 +    LOG(("nsHttpConnectionMgr::OnMsgReschedTransaction [trans=%p]\n", param));
  1.2279 +
  1.2280 +    nsHttpTransaction *trans = (nsHttpTransaction *) param;
  1.2281 +    trans->SetPriority(priority);
  1.2282 +
  1.2283 +    nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
  1.2284 +                                                   nullptr, trans);
  1.2285 +
  1.2286 +    if (ent) {
  1.2287 +        int32_t index = ent->mPendingQ.IndexOf(trans);
  1.2288 +        if (index >= 0) {
  1.2289 +            ent->mPendingQ.RemoveElementAt(index);
  1.2290 +            InsertTransactionSorted(ent->mPendingQ, trans);
  1.2291 +        }
  1.2292 +    }
  1.2293 +
  1.2294 +    NS_RELEASE(trans);
  1.2295 +}
  1.2296 +
  1.2297 +void
  1.2298 +nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, void *param)
  1.2299 +{
  1.2300 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2301 +    LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
  1.2302 +
  1.2303 +    nsresult closeCode = static_cast<nsresult>(reason);
  1.2304 +    nsHttpTransaction *trans = (nsHttpTransaction *) param;
  1.2305 +    //
  1.2306 +    // if the transaction owns a connection and the transaction is not done,
  1.2307 +    // then ask the connection to close the transaction.  otherwise, close the
  1.2308 +    // transaction directly (removing it from the pending queue first).
  1.2309 +    //
  1.2310 +    nsAHttpConnection *conn = trans->Connection();
  1.2311 +    if (conn && !trans->IsDone()) {
  1.2312 +        conn->CloseTransaction(trans, closeCode);
  1.2313 +    } else {
  1.2314 +        nsConnectionEntry *ent =
  1.2315 +            LookupConnectionEntry(trans->ConnectionInfo(), nullptr, trans);
  1.2316 +
  1.2317 +        if (ent) {
  1.2318 +            int32_t index = ent->mPendingQ.IndexOf(trans);
  1.2319 +            if (index >= 0) {
  1.2320 +                LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]"
  1.2321 +                     " found in pending queue\n", trans));
  1.2322 +                ent->mPendingQ.RemoveElementAt(index);
  1.2323 +                nsHttpTransaction *temp = trans;
  1.2324 +                NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
  1.2325 +            }
  1.2326 +        }
  1.2327 +        trans->Close(closeCode);
  1.2328 +
  1.2329 +        // Cancel is a pretty strong signal that things might be hanging
  1.2330 +        // so we want to cancel any null transactions related to this connection
  1.2331 +        // entry. They are just optimizations, but they aren't hooked up to
  1.2332 +        // anything that might get canceled from the rest of gecko, so best
  1.2333 +        // to assume that's what was meant by the cancel we did receive if
  1.2334 +        // it only applied to something in the queue.
  1.2335 +        for (uint32_t index = 0;
  1.2336 +             ent && (index < ent->mActiveConns.Length());
  1.2337 +             ++index) {
  1.2338 +            nsHttpConnection *activeConn = ent->mActiveConns[index];
  1.2339 +            nsAHttpTransaction *liveTransaction = activeConn->Transaction();
  1.2340 +            if (liveTransaction && liveTransaction->IsNullTransaction()) {
  1.2341 +                LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p] "
  1.2342 +                     "also canceling Null Transaction %p on conn %p\n",
  1.2343 +                     trans, liveTransaction, activeConn));
  1.2344 +                activeConn->CloseTransaction(liveTransaction, closeCode);
  1.2345 +            }
  1.2346 +        }
  1.2347 +    }
  1.2348 +    NS_RELEASE(trans);
  1.2349 +}
  1.2350 +
  1.2351 +void
  1.2352 +nsHttpConnectionMgr::OnMsgProcessPendingQ(int32_t, void *param)
  1.2353 +{
  1.2354 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2355 +    nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param;
  1.2356 +
  1.2357 +    if (!ci) {
  1.2358 +        LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=nullptr]\n"));
  1.2359 +        // Try and dispatch everything
  1.2360 +        mCT.Enumerate(ProcessAllTransactionsCB, this);
  1.2361 +        return;
  1.2362 +    }
  1.2363 +
  1.2364 +    LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n",
  1.2365 +         ci->HashKey().get()));
  1.2366 +
  1.2367 +    // start by processing the queue identified by the given connection info.
  1.2368 +    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
  1.2369 +    if (!(ent && ProcessPendingQForEntry(ent, false))) {
  1.2370 +        // if we reach here, it means that we couldn't dispatch a transaction
  1.2371 +        // for the specified connection info.  walk the connection table...
  1.2372 +        mCT.Enumerate(ProcessOneTransactionCB, this);
  1.2373 +    }
  1.2374 +
  1.2375 +    NS_RELEASE(ci);
  1.2376 +}
  1.2377 +
  1.2378 +void
  1.2379 +nsHttpConnectionMgr::OnMsgPruneDeadConnections(int32_t, void *)
  1.2380 +{
  1.2381 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2382 +    LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
  1.2383 +
  1.2384 +    // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
  1.2385 +    mTimeOfNextWakeUp = UINT64_MAX;
  1.2386 +
  1.2387 +    // check canreuse() for all idle connections plus any active connections on
  1.2388 +    // connection entries that are using spdy.
  1.2389 +    if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
  1.2390 +        mCT.Enumerate(PruneDeadConnectionsCB, this);
  1.2391 +}
  1.2392 +
  1.2393 +void
  1.2394 +nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup(int32_t, void *param)
  1.2395 +{
  1.2396 +    LOG(("nsHttpConnectionMgr::OnMsgDoShiftReloadConnectionCleanup\n"));
  1.2397 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2398 +
  1.2399 +    nsRefPtr<nsHttpConnectionInfo> ci =
  1.2400 +        dont_AddRef(static_cast<nsHttpConnectionInfo *>(param));
  1.2401 +
  1.2402 +    mCT.Enumerate(ClosePersistentConnectionsCB, this);
  1.2403 +    if (ci)
  1.2404 +        ResetIPFamilyPreference(ci);
  1.2405 +}
  1.2406 +
  1.2407 +void
  1.2408 +nsHttpConnectionMgr::OnMsgReclaimConnection(int32_t, void *param)
  1.2409 +{
  1.2410 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2411 +    LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
  1.2412 +
  1.2413 +    nsHttpConnection *conn = (nsHttpConnection *) param;
  1.2414 +
  1.2415 +    //
  1.2416 +    // 1) remove the connection from the active list
  1.2417 +    // 2) if keep-alive, add connection to idle list
  1.2418 +    // 3) post event to process the pending transaction queue
  1.2419 +    //
  1.2420 +
  1.2421 +    nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
  1.2422 +                                                   conn, nullptr);
  1.2423 +    nsHttpConnectionInfo *ci = nullptr;
  1.2424 +
  1.2425 +    if (!ent) {
  1.2426 +        // this should never happen
  1.2427 +        LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection ent == null\n"));
  1.2428 +        MOZ_ASSERT(false, "no connection entry");
  1.2429 +        NS_ADDREF(ci = conn->ConnectionInfo());
  1.2430 +    }
  1.2431 +    else {
  1.2432 +        NS_ADDREF(ci = ent->mConnInfo);
  1.2433 +
  1.2434 +        // If the connection is in the active list, remove that entry
  1.2435 +        // and the reference held by the mActiveConns list.
  1.2436 +        // This is never the final reference on conn as the event context
  1.2437 +        // is also holding one that is released at the end of this function.
  1.2438 +
  1.2439 +        if (ent->mUsingSpdy) {
  1.2440 +            // Spdy connections aren't reused in the traditional HTTP way in
  1.2441 +            // the idleconns list, they are actively multplexed as active
  1.2442 +            // conns. Even when they have 0 transactions on them they are
  1.2443 +            // considered active connections. So when one is reclaimed it
  1.2444 +            // is really complete and is meant to be shut down and not
  1.2445 +            // reused.
  1.2446 +            conn->DontReuse();
  1.2447 +        }
  1.2448 +
  1.2449 +        if (ent->mActiveConns.RemoveElement(conn)) {
  1.2450 +            if (conn == ent->mYellowConnection)
  1.2451 +                ent->OnYellowComplete();
  1.2452 +            nsHttpConnection *temp = conn;
  1.2453 +            NS_RELEASE(temp);
  1.2454 +            DecrementActiveConnCount(conn);
  1.2455 +            ConditionallyStopTimeoutTick();
  1.2456 +        }
  1.2457 +
  1.2458 +        if (conn->CanReuse()) {
  1.2459 +            LOG(("  adding connection to idle list\n"));
  1.2460 +            // Keep The idle connection list sorted with the connections that
  1.2461 +            // have moved the largest data pipelines at the front because these
  1.2462 +            // connections have the largest cwnds on the server.
  1.2463 +
  1.2464 +            // The linear search is ok here because the number of idleconns
  1.2465 +            // in a single entry is generally limited to a small number (i.e. 6)
  1.2466 +
  1.2467 +            uint32_t idx;
  1.2468 +            for (idx = 0; idx < ent->mIdleConns.Length(); idx++) {
  1.2469 +                nsHttpConnection *idleConn = ent->mIdleConns[idx];
  1.2470 +                if (idleConn->MaxBytesRead() < conn->MaxBytesRead())
  1.2471 +                    break;
  1.2472 +            }
  1.2473 +
  1.2474 +            NS_ADDREF(conn);
  1.2475 +            ent->mIdleConns.InsertElementAt(idx, conn);
  1.2476 +            mNumIdleConns++;
  1.2477 +            conn->BeginIdleMonitoring();
  1.2478 +
  1.2479 +            // If the added connection was first idle connection or has shortest
  1.2480 +            // time to live among the watched connections, pruning dead
  1.2481 +            // connections needs to be done when it can't be reused anymore.
  1.2482 +            uint32_t timeToLive = conn->TimeToLive();
  1.2483 +            if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
  1.2484 +                PruneDeadConnectionsAfter(timeToLive);
  1.2485 +        }
  1.2486 +        else {
  1.2487 +            LOG(("  connection cannot be reused; closing connection\n"));
  1.2488 +            conn->Close(NS_ERROR_ABORT);
  1.2489 +        }
  1.2490 +    }
  1.2491 +
  1.2492 +    OnMsgProcessPendingQ(0, ci); // releases |ci|
  1.2493 +    NS_RELEASE(conn);
  1.2494 +}
  1.2495 +
  1.2496 +void
  1.2497 +nsHttpConnectionMgr::OnMsgCompleteUpgrade(int32_t, void *param)
  1.2498 +{
  1.2499 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2500 +    nsCompleteUpgradeData *data = (nsCompleteUpgradeData *) param;
  1.2501 +    LOG(("nsHttpConnectionMgr::OnMsgCompleteUpgrade "
  1.2502 +         "this=%p conn=%p listener=%p\n", this, data->mConn.get(),
  1.2503 +         data->mUpgradeListener.get()));
  1.2504 +
  1.2505 +    nsCOMPtr<nsISocketTransport> socketTransport;
  1.2506 +    nsCOMPtr<nsIAsyncInputStream> socketIn;
  1.2507 +    nsCOMPtr<nsIAsyncOutputStream> socketOut;
  1.2508 +
  1.2509 +    nsresult rv;
  1.2510 +    rv = data->mConn->TakeTransport(getter_AddRefs(socketTransport),
  1.2511 +                                    getter_AddRefs(socketIn),
  1.2512 +                                    getter_AddRefs(socketOut));
  1.2513 +
  1.2514 +    if (NS_SUCCEEDED(rv))
  1.2515 +        data->mUpgradeListener->OnTransportAvailable(socketTransport,
  1.2516 +                                                     socketIn,
  1.2517 +                                                     socketOut);
  1.2518 +    delete data;
  1.2519 +}
  1.2520 +
  1.2521 +void
  1.2522 +nsHttpConnectionMgr::OnMsgUpdateParam(int32_t, void *param)
  1.2523 +{
  1.2524 +    uint16_t name  = (NS_PTR_TO_INT32(param) & 0xFFFF0000) >> 16;
  1.2525 +    uint16_t value =  NS_PTR_TO_INT32(param) & 0x0000FFFF;
  1.2526 +
  1.2527 +    switch (name) {
  1.2528 +    case MAX_CONNECTIONS:
  1.2529 +        mMaxConns = value;
  1.2530 +        break;
  1.2531 +    case MAX_PERSISTENT_CONNECTIONS_PER_HOST:
  1.2532 +        mMaxPersistConnsPerHost = value;
  1.2533 +        break;
  1.2534 +    case MAX_PERSISTENT_CONNECTIONS_PER_PROXY:
  1.2535 +        mMaxPersistConnsPerProxy = value;
  1.2536 +        break;
  1.2537 +    case MAX_REQUEST_DELAY:
  1.2538 +        mMaxRequestDelay = value;
  1.2539 +        break;
  1.2540 +    case MAX_PIPELINED_REQUESTS:
  1.2541 +        mMaxPipelinedRequests = value;
  1.2542 +        break;
  1.2543 +    case MAX_OPTIMISTIC_PIPELINED_REQUESTS:
  1.2544 +        mMaxOptimisticPipelinedRequests = value;
  1.2545 +        break;
  1.2546 +    default:
  1.2547 +        NS_NOTREACHED("unexpected parameter name");
  1.2548 +    }
  1.2549 +}
  1.2550 +
  1.2551 +// nsHttpConnectionMgr::nsConnectionEntry
  1.2552 +nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
  1.2553 +{
  1.2554 +    if (mSpdyPreferred)
  1.2555 +        gHttpHandler->ConnMgr()->RemoveSpdyPreferredEnt(mCoalescingKey);
  1.2556 +
  1.2557 +    NS_RELEASE(mConnInfo);
  1.2558 +}
  1.2559 +
  1.2560 +void
  1.2561 +nsHttpConnectionMgr::OnMsgProcessFeedback(int32_t, void *param)
  1.2562 +{
  1.2563 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2564 +    nsHttpPipelineFeedback *fb = (nsHttpPipelineFeedback *)param;
  1.2565 +
  1.2566 +    PipelineFeedbackInfo(fb->mConnInfo, fb->mInfo, fb->mConn, fb->mData);
  1.2567 +    delete fb;
  1.2568 +}
  1.2569 +
  1.2570 +// Read Timeout Tick handlers
  1.2571 +
  1.2572 +void
  1.2573 +nsHttpConnectionMgr::ActivateTimeoutTick()
  1.2574 +{
  1.2575 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2576 +    LOG(("nsHttpConnectionMgr::ActivateTimeoutTick() "
  1.2577 +         "this=%p mTimeoutTick=%p\n"));
  1.2578 +
  1.2579 +    // The timer tick should be enabled if it is not already pending.
  1.2580 +    // Upon running the tick will rearm itself if there are active
  1.2581 +    // connections available.
  1.2582 +
  1.2583 +    if (mTimeoutTick && mTimeoutTickArmed) {
  1.2584 +        // make sure we get one iteration on a quick tick
  1.2585 +        if (mTimeoutTickNext > 1) {
  1.2586 +            mTimeoutTickNext = 1;
  1.2587 +            mTimeoutTick->SetDelay(1000);
  1.2588 +        }
  1.2589 +        return;
  1.2590 +    }
  1.2591 +
  1.2592 +    if (!mTimeoutTick) {
  1.2593 +        mTimeoutTick = do_CreateInstance(NS_TIMER_CONTRACTID);
  1.2594 +        if (!mTimeoutTick) {
  1.2595 +            NS_WARNING("failed to create timer for http timeout management");
  1.2596 +            return;
  1.2597 +        }
  1.2598 +        mTimeoutTick->SetTarget(mSocketThreadTarget);
  1.2599 +    }
  1.2600 +
  1.2601 +    MOZ_ASSERT(!mTimeoutTickArmed, "timer tick armed");
  1.2602 +    mTimeoutTickArmed = true;
  1.2603 +    mTimeoutTick->Init(this, 1000, nsITimer::TYPE_REPEATING_SLACK);
  1.2604 +}
  1.2605 +
  1.2606 +void
  1.2607 +nsHttpConnectionMgr::TimeoutTick()
  1.2608 +{
  1.2609 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2610 +    MOZ_ASSERT(mTimeoutTick, "no readtimeout tick");
  1.2611 +
  1.2612 +    LOG(("nsHttpConnectionMgr::TimeoutTick active=%d\n", mNumActiveConns));
  1.2613 +    // The next tick will be between 1 second and 1 hr
  1.2614 +    // Set it to the max value here, and the TimeoutTickCB()s can
  1.2615 +    // reduce it to their local needs.
  1.2616 +    mTimeoutTickNext = 3600; // 1hr
  1.2617 +    mCT.Enumerate(TimeoutTickCB, this);
  1.2618 +    if (mTimeoutTick) {
  1.2619 +        mTimeoutTickNext = std::max(mTimeoutTickNext, 1U);
  1.2620 +        mTimeoutTick->SetDelay(mTimeoutTickNext * 1000);
  1.2621 +    }
  1.2622 +}
  1.2623 +
  1.2624 +PLDHashOperator
  1.2625 +nsHttpConnectionMgr::TimeoutTickCB(const nsACString &key,
  1.2626 +                                       nsAutoPtr<nsConnectionEntry> &ent,
  1.2627 +                                       void *closure)
  1.2628 +{
  1.2629 +    nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
  1.2630 +
  1.2631 +    LOG(("nsHttpConnectionMgr::TimeoutTickCB() this=%p host=%s "
  1.2632 +         "idle=%d active=%d half-len=%d pending=%d\n",
  1.2633 +         self, ent->mConnInfo->Host(), ent->mIdleConns.Length(),
  1.2634 +         ent->mActiveConns.Length(), ent->mHalfOpens.Length(),
  1.2635 +         ent->mPendingQ.Length()));
  1.2636 +
  1.2637 +    // first call the tick handler for each active connection
  1.2638 +    PRIntervalTime now = PR_IntervalNow();
  1.2639 +    for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) {
  1.2640 +        uint32_t connNextTimeout =  ent->mActiveConns[index]->ReadTimeoutTick(now);
  1.2641 +        self->mTimeoutTickNext = std::min(self->mTimeoutTickNext, connNextTimeout);
  1.2642 +    }
  1.2643 +
  1.2644 +    // now check for any stalled half open sockets
  1.2645 +    if (ent->mHalfOpens.Length()) {
  1.2646 +        TimeStamp now = TimeStamp::Now();
  1.2647 +        double maxConnectTime = gHttpHandler->ConnectTimeout();  /* in milliseconds */
  1.2648 +
  1.2649 +        for (uint32_t index = ent->mHalfOpens.Length(); index > 0; ) {
  1.2650 +            index--;
  1.2651 +
  1.2652 +            nsHalfOpenSocket *half = ent->mHalfOpens[index];
  1.2653 +            double delta = half->Duration(now);
  1.2654 +            // If the socket has timed out, close it so the waiting transaction
  1.2655 +            // will get the proper signal
  1.2656 +            if (delta > maxConnectTime) {
  1.2657 +                LOG(("Force timeout of half open to %s after %.2fms.\n",
  1.2658 +                     ent->mConnInfo->HashKey().get(), delta));
  1.2659 +                if (half->SocketTransport())
  1.2660 +                    half->SocketTransport()->Close(NS_ERROR_ABORT);
  1.2661 +                if (half->BackupTransport())
  1.2662 +                    half->BackupTransport()->Close(NS_ERROR_ABORT);
  1.2663 +            }
  1.2664 +
  1.2665 +            // If this half open hangs around for 5 seconds after we've closed() it
  1.2666 +            // then just abandon the socket.
  1.2667 +            if (delta > maxConnectTime + 5000) {
  1.2668 +                LOG(("Abandon half open to %s after %.2fms.\n",
  1.2669 +                     ent->mConnInfo->HashKey().get(), delta));
  1.2670 +                half->Abandon();
  1.2671 +            }
  1.2672 +        }
  1.2673 +    }
  1.2674 +    if (ent->mHalfOpens.Length()) {
  1.2675 +        self->mTimeoutTickNext = 1;
  1.2676 +    }
  1.2677 +    return PL_DHASH_NEXT;
  1.2678 +}
  1.2679 +
  1.2680 +//-----------------------------------------------------------------------------
  1.2681 +// nsHttpConnectionMgr::nsConnectionHandle
  1.2682 +
  1.2683 +nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
  1.2684 +{
  1.2685 +    if (mConn) {
  1.2686 +        gHttpHandler->ReclaimConnection(mConn);
  1.2687 +        NS_RELEASE(mConn);
  1.2688 +    }
  1.2689 +}
  1.2690 +
  1.2691 +NS_IMPL_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
  1.2692 +
  1.2693 +nsHttpConnectionMgr::nsConnectionEntry *
  1.2694 +nsHttpConnectionMgr::GetOrCreateConnectionEntry(nsHttpConnectionInfo *ci)
  1.2695 +{
  1.2696 +    nsConnectionEntry *ent = mCT.Get(ci->HashKey());
  1.2697 +    if (ent)
  1.2698 +        return ent;
  1.2699 +
  1.2700 +    nsHttpConnectionInfo *clone = ci->Clone();
  1.2701 +    ent = new nsConnectionEntry(clone);
  1.2702 +    mCT.Put(ci->HashKey(), ent);
  1.2703 +    return ent;
  1.2704 +}
  1.2705 +
  1.2706 +nsresult
  1.2707 +nsHttpConnectionMgr::nsConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
  1.2708 +                                                            nsHttpRequestHead *req,
  1.2709 +                                                            nsHttpResponseHead *resp,
  1.2710 +                                                            bool *reset)
  1.2711 +{
  1.2712 +    return mConn->OnHeadersAvailable(trans, req, resp, reset);
  1.2713 +}
  1.2714 +
  1.2715 +void
  1.2716 +nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
  1.2717 +{
  1.2718 +    mConn->CloseTransaction(trans, reason);
  1.2719 +}
  1.2720 +
  1.2721 +nsresult
  1.2722 +nsHttpConnectionMgr::
  1.2723 +nsConnectionHandle::TakeTransport(nsISocketTransport  **aTransport,
  1.2724 +                                  nsIAsyncInputStream **aInputStream,
  1.2725 +                                  nsIAsyncOutputStream **aOutputStream)
  1.2726 +{
  1.2727 +    return mConn->TakeTransport(aTransport, aInputStream, aOutputStream);
  1.2728 +}
  1.2729 +
  1.2730 +void
  1.2731 +nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param)
  1.2732 +{
  1.2733 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2734 +
  1.2735 +    nsRefPtr<SpeculativeConnectArgs> args =
  1.2736 +        dont_AddRef(static_cast<SpeculativeConnectArgs *>(param));
  1.2737 +
  1.2738 +    LOG(("nsHttpConnectionMgr::OnMsgSpeculativeConnect [ci=%s]\n",
  1.2739 +         args->mTrans->ConnectionInfo()->HashKey().get()));
  1.2740 +
  1.2741 +    nsConnectionEntry *ent =
  1.2742 +        GetOrCreateConnectionEntry(args->mTrans->ConnectionInfo());
  1.2743 +
  1.2744 +    // If spdy has previously made a preferred entry for this host via
  1.2745 +    // the ip pooling rules. If so, connect to the preferred host instead of
  1.2746 +    // the one directly passed in here.
  1.2747 +    nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
  1.2748 +    if (preferredEntry)
  1.2749 +        ent = preferredEntry;
  1.2750 +
  1.2751 +    uint32_t parallelSpeculativeConnectLimit =
  1.2752 +        gHttpHandler->ParallelSpeculativeConnectLimit();
  1.2753 +    bool ignorePossibleSpdyConnections = false;
  1.2754 +    bool ignoreIdle = false;
  1.2755 +
  1.2756 +    if (args->mOverridesOK) {
  1.2757 +        parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit;
  1.2758 +        ignorePossibleSpdyConnections = args->mIgnorePossibleSpdyConnections;
  1.2759 +        ignoreIdle = args->mIgnoreIdle;
  1.2760 +    }
  1.2761 +
  1.2762 +    if (ent->SupportsPipelining()) {
  1.2763 +        /* Only speculative connect if we're not pipelining and have no other pending
  1.2764 +         * unconnected half-opens.. */
  1.2765 +        if (ent->UnconnectedHalfOpens() == 0 && ent->mIdleConns.Length() == 0
  1.2766 +                && !RestrictConnections(ent) && !HasPipelines(ent)
  1.2767 +                && !AtActiveConnectionLimit(ent, args->mTrans->Caps())) {
  1.2768 +#ifdef WTF_DEBUG
  1.2769 +            fprintf(stderr, "WTF: Creating speculative connection because we have no pipelines\n");
  1.2770 +#endif
  1.2771 +            CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true);
  1.2772 +        }
  1.2773 +    } else if (mNumHalfOpenConns < parallelSpeculativeConnectLimit &&
  1.2774 +        ((ignoreIdle && (ent->mIdleConns.Length() < parallelSpeculativeConnectLimit)) ||
  1.2775 +         !ent->mIdleConns.Length()) &&
  1.2776 +        !RestrictConnections(ent, ignorePossibleSpdyConnections) &&
  1.2777 +        !AtActiveConnectionLimit(ent, args->mTrans->Caps())) {
  1.2778 +#ifdef WTF_DEBUG
  1.2779 +            fprintf(stderr, "WTF: Creating speculative connection because we can't pipeline\n");
  1.2780 +#endif
  1.2781 +        CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true);
  1.2782 +    } else {
  1.2783 +        LOG(("  Transport not created due to existing connection count\n"));
  1.2784 +    }
  1.2785 +}
  1.2786 +
  1.2787 +bool
  1.2788 +nsHttpConnectionMgr::HasPipelines(nsConnectionEntry *ent)
  1.2789 +{
  1.2790 +    uint32_t activeCount = ent->mActiveConns.Length();
  1.2791 +
  1.2792 +    if (!ent->SupportsPipelining()) {
  1.2793 +        return false;
  1.2794 +    }
  1.2795 +
  1.2796 +    for (uint32_t i = 0; i < activeCount; ++i) {
  1.2797 +        nsHttpConnection *conn = ent->mActiveConns[i];
  1.2798 +        if (!conn->SupportsPipelining())
  1.2799 +            continue;
  1.2800 +
  1.2801 +        nsAHttpTransaction *activeTrans = conn->Transaction();
  1.2802 +
  1.2803 +        if (activeTrans && !activeTrans->IsDone() &&
  1.2804 +            !NS_FAILED(activeTrans->Status()))
  1.2805 +            return true;
  1.2806 +    }
  1.2807 +    return false;
  1.2808 +}
  1.2809 + 
  1.2810 +bool
  1.2811 +nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
  1.2812 +{
  1.2813 +    return mConn->IsPersistent();
  1.2814 +}
  1.2815 +
  1.2816 +bool
  1.2817 +nsHttpConnectionMgr::nsConnectionHandle::IsReused()
  1.2818 +{
  1.2819 +    return mConn->IsReused();
  1.2820 +}
  1.2821 +
  1.2822 +void
  1.2823 +nsHttpConnectionMgr::nsConnectionHandle::DontReuse()
  1.2824 +{
  1.2825 +    mConn->DontReuse();
  1.2826 +}
  1.2827 +
  1.2828 +nsresult
  1.2829 +nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, uint32_t bufLen)
  1.2830 +{
  1.2831 +    return mConn->PushBack(buf, bufLen);
  1.2832 +}
  1.2833 +
  1.2834 +
  1.2835 +//////////////////////// nsHalfOpenSocket
  1.2836 +
  1.2837 +
  1.2838 +NS_IMPL_ISUPPORTS(nsHttpConnectionMgr::nsHalfOpenSocket,
  1.2839 +                  nsIOutputStreamCallback,
  1.2840 +                  nsITransportEventSink,
  1.2841 +                  nsIInterfaceRequestor,
  1.2842 +                  nsITimerCallback)
  1.2843 +
  1.2844 +nsHttpConnectionMgr::
  1.2845 +nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
  1.2846 +                                   nsAHttpTransaction *trans,
  1.2847 +                                   uint32_t caps)
  1.2848 +    : mEnt(ent),
  1.2849 +      mTransaction(trans),
  1.2850 +      mCaps(caps),
  1.2851 +      mSpeculative(false),
  1.2852 +      mHasConnected(false)
  1.2853 +{
  1.2854 +    MOZ_ASSERT(ent && trans, "constructor with null arguments");
  1.2855 +    LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s]\n",
  1.2856 +         this, trans, ent->mConnInfo->Host()));
  1.2857 +}
  1.2858 +
  1.2859 +nsHttpConnectionMgr::nsHalfOpenSocket::~nsHalfOpenSocket()
  1.2860 +{
  1.2861 +    MOZ_ASSERT(!mStreamOut);
  1.2862 +    MOZ_ASSERT(!mBackupStreamOut);
  1.2863 +    MOZ_ASSERT(!mSynTimer);
  1.2864 +    LOG(("Destroying nsHalfOpenSocket [this=%p]\n", this));
  1.2865 +
  1.2866 +    if (mEnt)
  1.2867 +        mEnt->RemoveHalfOpen(this);
  1.2868 +}
  1.2869 +
  1.2870 +nsresult
  1.2871 +nsHttpConnectionMgr::
  1.2872 +nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
  1.2873 +                               nsIAsyncInputStream **instream,
  1.2874 +                               nsIAsyncOutputStream **outstream,
  1.2875 +                               bool isBackup)
  1.2876 +{
  1.2877 +    nsresult rv;
  1.2878 +
  1.2879 +    const char* types[1];
  1.2880 +    types[0] = (mEnt->mConnInfo->UsingSSL()) ?
  1.2881 +        "ssl" : gHttpHandler->DefaultSocketType();
  1.2882 +    uint32_t typeCount = (types[0] != nullptr);
  1.2883 +
  1.2884 +    nsCOMPtr<nsISocketTransport> socketTransport;
  1.2885 +    nsCOMPtr<nsISocketTransportService> sts;
  1.2886 +
  1.2887 +    sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
  1.2888 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2889 +
  1.2890 +    rv = sts->CreateTransport(types, typeCount,
  1.2891 +                              nsDependentCString(mEnt->mConnInfo->Host()),
  1.2892 +                              mEnt->mConnInfo->Port(),
  1.2893 +                              mEnt->mConnInfo->ProxyInfo(),
  1.2894 +                              getter_AddRefs(socketTransport));
  1.2895 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2896 +
  1.2897 +    uint32_t tmpFlags = 0;
  1.2898 +    if (mCaps & NS_HTTP_REFRESH_DNS)
  1.2899 +        tmpFlags = nsISocketTransport::BYPASS_CACHE;
  1.2900 +
  1.2901 +    if (mCaps & NS_HTTP_LOAD_ANONYMOUS)
  1.2902 +        tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
  1.2903 +
  1.2904 +    if (mEnt->mConnInfo->GetPrivate())
  1.2905 +        tmpFlags |= nsISocketTransport::NO_PERMANENT_STORAGE;
  1.2906 +
  1.2907 +    // For backup connections, we disable IPv6. That's because some users have
  1.2908 +    // broken IPv6 connectivity (leading to very long timeouts), and disabling
  1.2909 +    // IPv6 on the backup connection gives them a much better user experience
  1.2910 +    // with dual-stack hosts, though they still pay the 250ms delay for each new
  1.2911 +    // connection. This strategy is also known as "happy eyeballs".
  1.2912 +    if (mEnt->mPreferIPv6) {
  1.2913 +        tmpFlags |= nsISocketTransport::DISABLE_IPV4;
  1.2914 +    }
  1.2915 +    else if (mEnt->mPreferIPv4 ||
  1.2916 +             (isBackup && gHttpHandler->FastFallbackToIPv4())) {
  1.2917 +        tmpFlags |= nsISocketTransport::DISABLE_IPV6;
  1.2918 +    }
  1.2919 +
  1.2920 +    if (IsSpeculative()) {
  1.2921 +        tmpFlags |= nsISocketTransport::DISABLE_RFC1918;
  1.2922 +    }
  1.2923 +
  1.2924 +    socketTransport->SetConnectionFlags(tmpFlags);
  1.2925 +
  1.2926 +    socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
  1.2927 +
  1.2928 +    rv = socketTransport->SetEventSink(this, nullptr);
  1.2929 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2930 +
  1.2931 +    rv = socketTransport->SetSecurityCallbacks(this);
  1.2932 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2933 +
  1.2934 +    nsCOMPtr<nsIOutputStream> sout;
  1.2935 +    rv = socketTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,
  1.2936 +                                            0, 0,
  1.2937 +                                            getter_AddRefs(sout));
  1.2938 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2939 +
  1.2940 +    nsCOMPtr<nsIInputStream> sin;
  1.2941 +    rv = socketTransport->OpenInputStream(nsITransport::OPEN_UNBUFFERED,
  1.2942 +                                           0, 0,
  1.2943 +                                           getter_AddRefs(sin));
  1.2944 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2945 +
  1.2946 +    socketTransport.forget(transport);
  1.2947 +    CallQueryInterface(sin, instream);
  1.2948 +    CallQueryInterface(sout, outstream);
  1.2949 +
  1.2950 +    rv = (*outstream)->AsyncWait(this, 0, 0, nullptr);
  1.2951 +    if (NS_SUCCEEDED(rv))
  1.2952 +        gHttpHandler->ConnMgr()->StartedConnect();
  1.2953 +
  1.2954 +    return rv;
  1.2955 +}
  1.2956 +
  1.2957 +nsresult
  1.2958 +nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams()
  1.2959 +{
  1.2960 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.2961 +
  1.2962 +    nsresult rv;
  1.2963 +
  1.2964 +    mPrimarySynStarted = TimeStamp::Now();
  1.2965 +    rv = SetupStreams(getter_AddRefs(mSocketTransport),
  1.2966 +                      getter_AddRefs(mStreamIn),
  1.2967 +                      getter_AddRefs(mStreamOut),
  1.2968 +                      false);
  1.2969 +    LOG(("nsHalfOpenSocket::SetupPrimaryStream [this=%p ent=%s rv=%x]",
  1.2970 +         this, mEnt->mConnInfo->Host(), rv));
  1.2971 +    if (NS_FAILED(rv)) {
  1.2972 +        if (mStreamOut)
  1.2973 +            mStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
  1.2974 +        mStreamOut = nullptr;
  1.2975 +        mStreamIn = nullptr;
  1.2976 +        mSocketTransport = nullptr;
  1.2977 +    }
  1.2978 +    return rv;
  1.2979 +}
  1.2980 +
  1.2981 +nsresult
  1.2982 +nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams()
  1.2983 +{
  1.2984 +    mBackupSynStarted = TimeStamp::Now();
  1.2985 +    nsresult rv = SetupStreams(getter_AddRefs(mBackupTransport),
  1.2986 +                               getter_AddRefs(mBackupStreamIn),
  1.2987 +                               getter_AddRefs(mBackupStreamOut),
  1.2988 +                               true);
  1.2989 +    LOG(("nsHalfOpenSocket::SetupBackupStream [this=%p ent=%s rv=%x]",
  1.2990 +         this, mEnt->mConnInfo->Host(), rv));
  1.2991 +    if (NS_FAILED(rv)) {
  1.2992 +        if (mBackupStreamOut)
  1.2993 +            mBackupStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
  1.2994 +        mBackupStreamOut = nullptr;
  1.2995 +        mBackupStreamIn = nullptr;
  1.2996 +        mBackupTransport = nullptr;
  1.2997 +    }
  1.2998 +    return rv;
  1.2999 +}
  1.3000 +
  1.3001 +void
  1.3002 +nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
  1.3003 +{
  1.3004 +    uint16_t timeout = gHttpHandler->GetIdleSynTimeout();
  1.3005 +    MOZ_ASSERT(!mSynTimer, "timer already initd");
  1.3006 +
  1.3007 +    if (timeout && !mTransaction->IsDone()) {
  1.3008 +        // Setup the timer that will establish a backup socket
  1.3009 +        // if we do not get a writable event on the main one.
  1.3010 +        // We do this because a lost SYN takes a very long time
  1.3011 +        // to repair at the TCP level.
  1.3012 +        //
  1.3013 +        // Failure to setup the timer is something we can live with,
  1.3014 +        // so don't return an error in that case.
  1.3015 +        nsresult rv;
  1.3016 +        mSynTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
  1.3017 +        if (NS_SUCCEEDED(rv)) {
  1.3018 +            mSynTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
  1.3019 +            LOG(("nsHalfOpenSocket::SetupBackupTimer() [this=%p]", this));
  1.3020 +        }
  1.3021 +    }
  1.3022 +    else if (timeout) {
  1.3023 +        LOG(("nsHalfOpenSocket::SetupBackupTimer() [this=%p],"
  1.3024 +             " transaction already done!", this));
  1.3025 +    }
  1.3026 +}
  1.3027 +
  1.3028 +void
  1.3029 +nsHttpConnectionMgr::nsHalfOpenSocket::CancelBackupTimer()
  1.3030 +{
  1.3031 +    // If the syntimer is still armed, we can cancel it because no backup
  1.3032 +    // socket should be formed at this point
  1.3033 +    if (!mSynTimer)
  1.3034 +        return;
  1.3035 +
  1.3036 +    LOG(("nsHalfOpenSocket::CancelBackupTimer()"));
  1.3037 +    mSynTimer->Cancel();
  1.3038 +    mSynTimer = nullptr;
  1.3039 +}
  1.3040 +
  1.3041 +void
  1.3042 +nsHttpConnectionMgr::nsHalfOpenSocket::Abandon()
  1.3043 +{
  1.3044 +    LOG(("nsHalfOpenSocket::Abandon [this=%p ent=%s]",
  1.3045 +         this, mEnt->mConnInfo->Host()));
  1.3046 +
  1.3047 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.3048 +
  1.3049 +    nsRefPtr<nsHalfOpenSocket> deleteProtector(this);
  1.3050 +
  1.3051 +    // Tell socket (and backup socket) to forget the half open socket.
  1.3052 +    if (mSocketTransport) {
  1.3053 +        mSocketTransport->SetEventSink(nullptr, nullptr);
  1.3054 +        mSocketTransport->SetSecurityCallbacks(nullptr);
  1.3055 +        mSocketTransport = nullptr;
  1.3056 +    }
  1.3057 +    if (mBackupTransport) {
  1.3058 +        mBackupTransport->SetEventSink(nullptr, nullptr);
  1.3059 +        mBackupTransport->SetSecurityCallbacks(nullptr);
  1.3060 +        mBackupTransport = nullptr;
  1.3061 +    }
  1.3062 +
  1.3063 +    // Tell output stream (and backup) to forget the half open socket.
  1.3064 +    if (mStreamOut) {
  1.3065 +        gHttpHandler->ConnMgr()->RecvdConnect();
  1.3066 +        mStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
  1.3067 +        mStreamOut = nullptr;
  1.3068 +    }
  1.3069 +    if (mBackupStreamOut) {
  1.3070 +        gHttpHandler->ConnMgr()->RecvdConnect();
  1.3071 +        mBackupStreamOut->AsyncWait(nullptr, 0, 0, nullptr);
  1.3072 +        mBackupStreamOut = nullptr;
  1.3073 +    }
  1.3074 +
  1.3075 +    // Lose references to input stream (and backup).
  1.3076 +    mStreamIn = mBackupStreamIn = nullptr;
  1.3077 +
  1.3078 +    // Stop the timer - we don't want any new backups.
  1.3079 +    CancelBackupTimer();
  1.3080 +
  1.3081 +    // Remove the half open from the connection entry.
  1.3082 +    if (mEnt)
  1.3083 +        mEnt->RemoveHalfOpen(this);
  1.3084 +    mEnt = nullptr;
  1.3085 +}
  1.3086 +
  1.3087 +double
  1.3088 +nsHttpConnectionMgr::nsHalfOpenSocket::Duration(TimeStamp epoch)
  1.3089 +{
  1.3090 +    if (mPrimarySynStarted.IsNull())
  1.3091 +        return 0;
  1.3092 +
  1.3093 +    return (epoch - mPrimarySynStarted).ToMilliseconds();
  1.3094 +}
  1.3095 +
  1.3096 +
  1.3097 +NS_IMETHODIMP // method for nsITimerCallback
  1.3098 +nsHttpConnectionMgr::nsHalfOpenSocket::Notify(nsITimer *timer)
  1.3099 +{
  1.3100 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.3101 +    MOZ_ASSERT(timer == mSynTimer, "wrong timer");
  1.3102 +
  1.3103 +    SetupBackupStreams();
  1.3104 +
  1.3105 +    mSynTimer = nullptr;
  1.3106 +    return NS_OK;
  1.3107 +}
  1.3108 +
  1.3109 +// method for nsIAsyncOutputStreamCallback
  1.3110 +NS_IMETHODIMP
  1.3111 +nsHttpConnectionMgr::
  1.3112 +nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
  1.3113 +{
  1.3114 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.3115 +    MOZ_ASSERT(out == mStreamOut || out == mBackupStreamOut,
  1.3116 +               "stream mismatch");
  1.3117 +    LOG(("nsHalfOpenSocket::OnOutputStreamReady [this=%p ent=%s %s]\n",
  1.3118 +         this, mEnt->mConnInfo->Host(),
  1.3119 +         out == mStreamOut ? "primary" : "backup"));
  1.3120 +    int32_t index;
  1.3121 +    nsresult rv;
  1.3122 +
  1.3123 +    gHttpHandler->ConnMgr()->RecvdConnect();
  1.3124 +
  1.3125 +    CancelBackupTimer();
  1.3126 +
  1.3127 +    // assign the new socket to the http connection
  1.3128 +    nsRefPtr<nsHttpConnection> conn = new nsHttpConnection();
  1.3129 +    LOG(("nsHalfOpenSocket::OnOutputStreamReady "
  1.3130 +         "Created new nshttpconnection %p\n", conn.get()));
  1.3131 +
  1.3132 +    // Some capabilities are needed before a transaciton actually gets
  1.3133 +    // scheduled (e.g. how to negotiate false start)
  1.3134 +    conn->SetTransactionCaps(mTransaction->Caps());
  1.3135 +
  1.3136 +    NetAddr peeraddr;
  1.3137 +    nsCOMPtr<nsIInterfaceRequestor> callbacks;
  1.3138 +    mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
  1.3139 +    if (out == mStreamOut) {
  1.3140 +        TimeDuration rtt = TimeStamp::Now() - mPrimarySynStarted;
  1.3141 +        rv = conn->Init(mEnt->mConnInfo,
  1.3142 +                        gHttpHandler->ConnMgr()->mMaxRequestDelay,
  1.3143 +                        mSocketTransport, mStreamIn, mStreamOut,
  1.3144 +                        callbacks,
  1.3145 +                        PR_MillisecondsToInterval(
  1.3146 +                          static_cast<uint32_t>(rtt.ToMilliseconds())));
  1.3147 +
  1.3148 +        if (NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr)))
  1.3149 +            mEnt->RecordIPFamilyPreference(peeraddr.raw.family);
  1.3150 +
  1.3151 +        // The nsHttpConnection object now owns these streams and sockets
  1.3152 +        mStreamOut = nullptr;
  1.3153 +        mStreamIn = nullptr;
  1.3154 +        mSocketTransport = nullptr;
  1.3155 +    }
  1.3156 +    else {
  1.3157 +        TimeDuration rtt = TimeStamp::Now() - mBackupSynStarted;
  1.3158 +        rv = conn->Init(mEnt->mConnInfo,
  1.3159 +                        gHttpHandler->ConnMgr()->mMaxRequestDelay,
  1.3160 +                        mBackupTransport, mBackupStreamIn, mBackupStreamOut,
  1.3161 +                        callbacks,
  1.3162 +                        PR_MillisecondsToInterval(
  1.3163 +                          static_cast<uint32_t>(rtt.ToMilliseconds())));
  1.3164 +
  1.3165 +        if (NS_SUCCEEDED(mBackupTransport->GetPeerAddr(&peeraddr)))
  1.3166 +            mEnt->RecordIPFamilyPreference(peeraddr.raw.family);
  1.3167 +
  1.3168 +        // The nsHttpConnection object now owns these streams and sockets
  1.3169 +        mBackupStreamOut = nullptr;
  1.3170 +        mBackupStreamIn = nullptr;
  1.3171 +        mBackupTransport = nullptr;
  1.3172 +    }
  1.3173 +
  1.3174 +    if (NS_FAILED(rv)) {
  1.3175 +        LOG(("nsHalfOpenSocket::OnOutputStreamReady "
  1.3176 +             "conn->init (%p) failed %x\n", conn.get(), rv));
  1.3177 +        return rv;
  1.3178 +    }
  1.3179 +
  1.3180 +    // This half-open socket has created a connection.  This flag excludes it
  1.3181 +    // from counter of actual connections used for checking limits.
  1.3182 +    mHasConnected = true;
  1.3183 +
  1.3184 +    // if this is still in the pending list, remove it and dispatch it
  1.3185 +    index = mEnt->mPendingQ.IndexOf(mTransaction);
  1.3186 +    if (index != -1) {
  1.3187 +        MOZ_ASSERT(!mSpeculative,
  1.3188 +                   "Speculative Half Open found mTranscation");
  1.3189 +        nsRefPtr<nsHttpTransaction> temp = dont_AddRef(mEnt->mPendingQ[index]);
  1.3190 +        mEnt->mPendingQ.RemoveElementAt(index);
  1.3191 +        gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
  1.3192 +#ifdef WTF_DEBUG
  1.3193 +        fprintf(stderr, "WTF: Speculative half-opened connection is now ready for %s (pipelines %d)\n",
  1.3194 +                mEnt->mConnInfo->Host(), mEnt->SupportsPipelining());
  1.3195 +#endif
  1.3196 +        rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, temp, conn);
  1.3197 +    }
  1.3198 +    else {
  1.3199 +        // this transaction was dispatched off the pending q before all the
  1.3200 +        // sockets established themselves.
  1.3201 +
  1.3202 +        // After about 1 second allow for the possibility of restarting a
  1.3203 +        // transaction due to server close. Keep at sub 1 second as that is the
  1.3204 +        // minimum granularity we can expect a server to be timing out with.
  1.3205 +        conn->SetIsReusedAfter(950);
  1.3206 +
  1.3207 +        // if we are using ssl and no other transactions are waiting right now,
  1.3208 +        // then form a null transaction to drive the SSL handshake to
  1.3209 +        // completion. Afterwards the connection will be 100% ready for the next
  1.3210 +        // transaction to use it. Make an exception for SSL over HTTP proxy as the
  1.3211 +        // NullHttpTransaction does not know how to drive CONNECT.
  1.3212 +        if (mEnt->mConnInfo->UsingSSL() && !mEnt->mPendingQ.Length() &&
  1.3213 +            !mEnt->mConnInfo->UsingHttpProxy()) {
  1.3214 +            LOG(("nsHalfOpenSocket::OnOutputStreamReady null transaction will "
  1.3215 +                 "be used to finish SSL handshake on conn %p\n", conn.get()));
  1.3216 +            nsRefPtr<NullHttpTransaction>  trans =
  1.3217 +                new NullHttpTransaction(mEnt->mConnInfo,
  1.3218 +                                        callbacks,
  1.3219 +                                        mCaps & ~NS_HTTP_ALLOW_PIPELINING);
  1.3220 +
  1.3221 +            gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
  1.3222 +            conn->Classify(nsAHttpTransaction::CLASS_SOLO);
  1.3223 +            rv = gHttpHandler->ConnMgr()->
  1.3224 +                DispatchAbstractTransaction(mEnt, trans, mCaps, conn, 0);
  1.3225 +        }
  1.3226 +        else {
  1.3227 +            // otherwise just put this in the persistent connection pool
  1.3228 +            LOG(("nsHalfOpenSocket::OnOutputStreamReady no transaction match "
  1.3229 +                 "returning conn %p to pool\n", conn.get()));
  1.3230 +            nsRefPtr<nsHttpConnection> copy(conn);
  1.3231 +            // forget() to effectively addref because onmsg*() will drop a ref
  1.3232 +            gHttpHandler->ConnMgr()->OnMsgReclaimConnection(
  1.3233 +                0, conn.forget().take());
  1.3234 +        }
  1.3235 +    }
  1.3236 +
  1.3237 +    return rv;
  1.3238 +}
  1.3239 +
  1.3240 +// method for nsITransportEventSink
  1.3241 +NS_IMETHODIMP
  1.3242 +nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
  1.3243 +                                                         nsresult status,
  1.3244 +                                                         uint64_t progress,
  1.3245 +                                                         uint64_t progressMax)
  1.3246 +{
  1.3247 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.3248 +
  1.3249 +    if (mTransaction)
  1.3250 +        mTransaction->OnTransportStatus(trans, status, progress);
  1.3251 +
  1.3252 +    if (trans != mSocketTransport)
  1.3253 +        return NS_OK;
  1.3254 +
  1.3255 +    // if we are doing spdy coalescing and haven't recorded the ip address
  1.3256 +    // for this entry before then make the hash key if our dns lookup
  1.3257 +    // just completed. We can't do coalescing if using a proxy because the
  1.3258 +    // ip addresses are not available to the client.
  1.3259 +
  1.3260 +    if (status == NS_NET_STATUS_CONNECTED_TO &&
  1.3261 +        gHttpHandler->IsSpdyEnabled() &&
  1.3262 +        gHttpHandler->CoalesceSpdy() &&
  1.3263 +        mEnt && mEnt->mConnInfo && mEnt->mConnInfo->UsingSSL() &&
  1.3264 +        !mEnt->mConnInfo->UsingProxy() &&
  1.3265 +        mEnt->mCoalescingKey.IsEmpty()) {
  1.3266 +
  1.3267 +        NetAddr addr;
  1.3268 +        nsresult rv = mSocketTransport->GetPeerAddr(&addr);
  1.3269 +        if (NS_SUCCEEDED(rv)) {
  1.3270 +            mEnt->mCoalescingKey.SetCapacity(kIPv6CStrBufSize + 26);
  1.3271 +            NetAddrToString(&addr, mEnt->mCoalescingKey.BeginWriting(), kIPv6CStrBufSize);
  1.3272 +            mEnt->mCoalescingKey.SetLength(
  1.3273 +                strlen(mEnt->mCoalescingKey.BeginReading()));
  1.3274 +
  1.3275 +            if (mEnt->mConnInfo->GetAnonymous())
  1.3276 +                mEnt->mCoalescingKey.AppendLiteral("~A:");
  1.3277 +            else
  1.3278 +                mEnt->mCoalescingKey.AppendLiteral("~.:");
  1.3279 +            mEnt->mCoalescingKey.AppendInt(mEnt->mConnInfo->Port());
  1.3280 +
  1.3281 +            LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus "
  1.3282 +                 "STATUS_CONNECTED_TO Established New Coalescing Key for host "
  1.3283 +                 "%s [%s]", mEnt->mConnInfo->Host(),
  1.3284 +                 mEnt->mCoalescingKey.get()));
  1.3285 +
  1.3286 +            gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(mEnt);
  1.3287 +        }
  1.3288 +    }
  1.3289 +
  1.3290 +    switch (status) {
  1.3291 +    case NS_NET_STATUS_CONNECTING_TO:
  1.3292 +        // Passed DNS resolution, now trying to connect, start the backup timer
  1.3293 +        // only prevent creating another backup transport.
  1.3294 +        // We also check for mEnt presence to not instantiate the timer after
  1.3295 +        // this half open socket has already been abandoned.  It may happen
  1.3296 +        // when we get this notification right between main-thread calls to
  1.3297 +        // nsHttpConnectionMgr::Shutdown and nsSocketTransportService::Shutdown
  1.3298 +        // where the first abandones all half open socket instances and only
  1.3299 +        // after that the second stops the socket thread.
  1.3300 +        if (mEnt && !mBackupTransport && !mSynTimer)
  1.3301 +            SetupBackupTimer();
  1.3302 +        break;
  1.3303 +
  1.3304 +    case NS_NET_STATUS_CONNECTED_TO:
  1.3305 +        // TCP connection's up, now transfer or SSL negotiantion starts,
  1.3306 +        // no need for backup socket
  1.3307 +        CancelBackupTimer();
  1.3308 +        break;
  1.3309 +
  1.3310 +    default:
  1.3311 +        break;
  1.3312 +    }
  1.3313 +
  1.3314 +    return NS_OK;
  1.3315 +}
  1.3316 +
  1.3317 +// method for nsIInterfaceRequestor
  1.3318 +NS_IMETHODIMP
  1.3319 +nsHttpConnectionMgr::nsHalfOpenSocket::GetInterface(const nsIID &iid,
  1.3320 +                                                    void **result)
  1.3321 +{
  1.3322 +    if (mTransaction) {
  1.3323 +        nsCOMPtr<nsIInterfaceRequestor> callbacks;
  1.3324 +        mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
  1.3325 +        if (callbacks)
  1.3326 +            return callbacks->GetInterface(iid, result);
  1.3327 +    }
  1.3328 +    return NS_ERROR_NO_INTERFACE;
  1.3329 +}
  1.3330 +
  1.3331 +
  1.3332 +nsHttpConnection *
  1.3333 +nsHttpConnectionMgr::nsConnectionHandle::TakeHttpConnection()
  1.3334 +{
  1.3335 +    // return our connection object to the caller and clear it internally
  1.3336 +    // do not drop our reference - the caller now owns it.
  1.3337 +
  1.3338 +    MOZ_ASSERT(mConn);
  1.3339 +    nsHttpConnection *conn = mConn;
  1.3340 +    mConn = nullptr;
  1.3341 +    return conn;
  1.3342 +}
  1.3343 +
  1.3344 +uint32_t
  1.3345 +nsHttpConnectionMgr::nsConnectionHandle::CancelPipeline(nsresult reason)
  1.3346 +{
  1.3347 +    // no pipeline to cancel
  1.3348 +    return 0;
  1.3349 +}
  1.3350 +
  1.3351 +nsAHttpTransaction::Classifier
  1.3352 +nsHttpConnectionMgr::nsConnectionHandle::Classification()
  1.3353 +{
  1.3354 +    if (mConn)
  1.3355 +        return mConn->Classification();
  1.3356 +
  1.3357 +    LOG(("nsConnectionHandle::Classification this=%p "
  1.3358 +         "has null mConn using CLASS_SOLO default", this));
  1.3359 +    return nsAHttpTransaction::CLASS_SOLO;
  1.3360 +}
  1.3361 +
  1.3362 +// nsConnectionEntry
  1.3363 +
  1.3364 +nsHttpConnectionMgr::
  1.3365 +nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci)
  1.3366 +    : mConnInfo(ci)
  1.3367 +    , mPipelineState(PS_YELLOW)
  1.3368 +    , mYellowGoodEvents(0)
  1.3369 +    , mYellowBadEvents(0)
  1.3370 +    , mYellowConnection(nullptr)
  1.3371 +    , mGreenDepth(kPipelineOpen)
  1.3372 +    , mPipeliningPenalty(0)
  1.3373 +    , mSpdyCWND(0)
  1.3374 +    , mUsingSpdy(false)
  1.3375 +    , mTestedSpdy(false)
  1.3376 +    , mSpdyPreferred(false)
  1.3377 +    , mPreferIPv4(false)
  1.3378 +    , mPreferIPv6(false)
  1.3379 +{
  1.3380 +    NS_ADDREF(mConnInfo);
  1.3381 +
  1.3382 +    // Randomize the pipeline depth (3..12)
  1.3383 +    mGreenDepth = gHttpHandler->GetMaxOptimisticPipelinedRequests()
  1.3384 +                  + rand() % (gHttpHandler->GetMaxPipelinedRequests()
  1.3385 +                              - gHttpHandler->GetMaxOptimisticPipelinedRequests());
  1.3386 +
  1.3387 +    if (gHttpHandler->GetPipelineAggressive()) {
  1.3388 +        mPipelineState = PS_GREEN;
  1.3389 +    }
  1.3390 +
  1.3391 +    mInitialGreenDepth = mGreenDepth;
  1.3392 +    memset(mPipeliningClassPenalty, 0, sizeof(int16_t) * nsAHttpTransaction::CLASS_MAX);
  1.3393 +}
  1.3394 +
  1.3395 +bool
  1.3396 +nsHttpConnectionMgr::nsConnectionEntry::SupportsPipelining()
  1.3397 +{
  1.3398 +    return mPipelineState != nsHttpConnectionMgr::PS_RED;
  1.3399 +}
  1.3400 +
  1.3401 +nsHttpConnectionMgr::PipeliningState
  1.3402 +nsHttpConnectionMgr::nsConnectionEntry::PipelineState()
  1.3403 +{
  1.3404 +    return mPipelineState;
  1.3405 +}
  1.3406 +
  1.3407 +void
  1.3408 +nsHttpConnectionMgr::
  1.3409 +nsConnectionEntry::OnPipelineFeedbackInfo(
  1.3410 +    nsHttpConnectionMgr::PipelineFeedbackInfoType info,
  1.3411 +    nsHttpConnection *conn,
  1.3412 +    uint32_t data)
  1.3413 +{
  1.3414 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.3415 +
  1.3416 +    if (mPipelineState == PS_YELLOW) {
  1.3417 +        if (info & kPipelineInfoTypeBad)
  1.3418 +            mYellowBadEvents++;
  1.3419 +        else if (info & (kPipelineInfoTypeNeutral | kPipelineInfoTypeGood))
  1.3420 +            mYellowGoodEvents++;
  1.3421 +    }
  1.3422 +
  1.3423 +    if (mPipelineState == PS_GREEN && info == GoodCompletedOK) {
  1.3424 +        int32_t depth = data;
  1.3425 +        LOG(("Transaction completed at pipeline depth of %d. Host = %s\n",
  1.3426 +             depth, mConnInfo->Host()));
  1.3427 +
  1.3428 +        // Don't set this. We want to keep our initial random value..
  1.3429 +        //if (depth >= 3)
  1.3430 +        //    mGreenDepth = kPipelineUnlimited;
  1.3431 +    }
  1.3432 +
  1.3433 +    nsAHttpTransaction::Classifier classification;
  1.3434 +    if (conn)
  1.3435 +        classification = conn->Classification();
  1.3436 +    else if (info == BadInsufficientFraming ||
  1.3437 +             info == BadUnexpectedLarge)
  1.3438 +        classification = (nsAHttpTransaction::Classifier) data;
  1.3439 +    else
  1.3440 +        classification = nsAHttpTransaction::CLASS_SOLO;
  1.3441 +
  1.3442 +    if (gHttpHandler->GetPipelineAggressive() &&
  1.3443 +        info & kPipelineInfoTypeBad &&
  1.3444 +        info != BadExplicitClose &&
  1.3445 +        info != RedVersionTooLow &&
  1.3446 +        info != RedBannedServer &&
  1.3447 +        info != RedCorruptedContent &&
  1.3448 +        info != BadInsufficientFraming) {
  1.3449 +        LOG(("minor negative feedback ignored "
  1.3450 +             "because of pipeline aggressive mode"));
  1.3451 +    }
  1.3452 +    else if (info & kPipelineInfoTypeBad) {
  1.3453 +        if ((info & kPipelineInfoTypeRed) && (mPipelineState != PS_RED)) {
  1.3454 +            LOG(("transition to red from %d. Host = %s.\n",
  1.3455 +                 mPipelineState, mConnInfo->Host()));
  1.3456 +            mPipelineState = PS_RED;
  1.3457 +            mPipeliningPenalty = 0;
  1.3458 +#ifdef WTF_TEST
  1.3459 +            fprintf(stderr, "WTF-bad: Red pipeline status disabled host %s\n",
  1.3460 +                    mConnInfo->Host());
  1.3461 +#endif
  1.3462 +
  1.3463 +        }
  1.3464 +
  1.3465 +        if (mLastCreditTime.IsNull())
  1.3466 +            mLastCreditTime = TimeStamp::Now();
  1.3467 +
  1.3468 +        // Red* events impact the host globally via mPipeliningPenalty, while
  1.3469 +        // Bad* events impact the per class penalty.
  1.3470 +
  1.3471 +        // The individual penalties should be < 16bit-signed-maxint - 25000
  1.3472 +        // (approx 7500). Penalties are paid-off either when something promising
  1.3473 +        // happens (a successful transaction, or promising headers) or when
  1.3474 +        // time goes by at a rate of 1 penalty point every 16 seconds.
  1.3475 +
  1.3476 +        switch (info) {
  1.3477 +        case RedVersionTooLow:
  1.3478 +            mPipeliningPenalty += 1000;
  1.3479 +            break;
  1.3480 +        case RedBannedServer:
  1.3481 +            mPipeliningPenalty += 7000;
  1.3482 +            break;
  1.3483 +        case RedCorruptedContent:
  1.3484 +            mPipeliningPenalty += 7000;
  1.3485 +            break;
  1.3486 +        case RedCanceledPipeline:
  1.3487 +            mPipeliningPenalty += 60;
  1.3488 +            break;
  1.3489 +        case BadExplicitClose:
  1.3490 +            mPipeliningClassPenalty[classification] += 250;
  1.3491 +            break;
  1.3492 +        case BadSlowReadMinor:
  1.3493 +            mPipeliningClassPenalty[classification] += 5;
  1.3494 +            break;
  1.3495 +        case BadSlowReadMajor:
  1.3496 +            mPipeliningClassPenalty[classification] += 25;
  1.3497 +            break;
  1.3498 +        case BadInsufficientFraming:
  1.3499 +            mPipeliningClassPenalty[classification] += 7000;
  1.3500 +            break;
  1.3501 +        case BadUnexpectedLarge:
  1.3502 +            mPipeliningClassPenalty[classification] += 120;
  1.3503 +            break;
  1.3504 +
  1.3505 +        default:
  1.3506 +            MOZ_ASSERT(false, "Unknown Bad/Red Pipeline Feedback Event");
  1.3507 +        }
  1.3508 +
  1.3509 +        const int16_t kPenalty = 25000;
  1.3510 +        mPipeliningPenalty = std::min(mPipeliningPenalty, kPenalty);
  1.3511 +        mPipeliningClassPenalty[classification] =
  1.3512 +          std::min(mPipeliningClassPenalty[classification], kPenalty);
  1.3513 +
  1.3514 +        LOG(("Assessing red penalty to %s class %d for event %d. "
  1.3515 +             "Penalty now %d, throttle[%d] = %d\n", mConnInfo->Host(),
  1.3516 +             classification, info, mPipeliningPenalty, classification,
  1.3517 +             mPipeliningClassPenalty[classification]));
  1.3518 +    }
  1.3519 +    else {
  1.3520 +        // hand out credits for neutral and good events such as
  1.3521 +        // "headers look ok" events
  1.3522 +
  1.3523 +        mPipeliningPenalty = std::max(mPipeliningPenalty - 1, 0);
  1.3524 +        mPipeliningClassPenalty[classification] = std::max(mPipeliningClassPenalty[classification] - 1, 0);
  1.3525 +    }
  1.3526 +
  1.3527 +    if (mPipelineState == PS_RED && !mPipeliningPenalty)
  1.3528 +    {
  1.3529 +        LOG(("transition %s to yellow\n", mConnInfo->Host()));
  1.3530 +        mPipelineState = PS_YELLOW;
  1.3531 +        mYellowConnection = nullptr;
  1.3532 +    }
  1.3533 +}
  1.3534 +
  1.3535 +void
  1.3536 +nsHttpConnectionMgr::
  1.3537 +nsConnectionEntry::SetYellowConnection(nsHttpConnection *conn)
  1.3538 +{
  1.3539 +    MOZ_ASSERT(!mYellowConnection && mPipelineState == PS_YELLOW,
  1.3540 +               "yellow connection already set or state is not yellow");
  1.3541 +    mYellowConnection = conn;
  1.3542 +    mYellowGoodEvents = mYellowBadEvents = 0;
  1.3543 +}
  1.3544 +
  1.3545 +void
  1.3546 +nsHttpConnectionMgr::
  1.3547 +nsConnectionEntry::OnYellowComplete()
  1.3548 +{
  1.3549 +    if (mPipelineState == PS_YELLOW) {
  1.3550 +        if (mYellowGoodEvents && !mYellowBadEvents) {
  1.3551 +            LOG(("transition %s to green\n", mConnInfo->Host()));
  1.3552 +            mPipelineState = PS_GREEN;
  1.3553 +            mGreenDepth = mInitialGreenDepth;
  1.3554 +        }
  1.3555 +        else {
  1.3556 +            // The purpose of the yellow state is to witness at least
  1.3557 +            // one successful pipelined transaction without seeing any
  1.3558 +            // kind of negative feedback before opening the flood gates.
  1.3559 +            // If we haven't confirmed that, then transfer back to red.
  1.3560 +            LOG(("transition %s to red from yellow return\n",
  1.3561 +                 mConnInfo->Host()));
  1.3562 +            mPipelineState = PS_RED;
  1.3563 +        }
  1.3564 +    }
  1.3565 +
  1.3566 +    mYellowConnection = nullptr;
  1.3567 +}
  1.3568 +
  1.3569 +void
  1.3570 +nsHttpConnectionMgr::
  1.3571 +nsConnectionEntry::CreditPenalty()
  1.3572 +{
  1.3573 +    if (mLastCreditTime.IsNull())
  1.3574 +        return;
  1.3575 +
  1.3576 +    // Decrease penalty values by 1 for every 16 seconds
  1.3577 +    // (i.e 3.7 per minute, or 1000 every 4h20m)
  1.3578 +
  1.3579 +    TimeStamp now = TimeStamp::Now();
  1.3580 +    TimeDuration elapsedTime = now - mLastCreditTime;
  1.3581 +    uint32_t creditsEarned =
  1.3582 +        static_cast<uint32_t>(elapsedTime.ToSeconds()) >> 4;
  1.3583 +
  1.3584 +    bool failed = false;
  1.3585 +    if (creditsEarned > 0) {
  1.3586 +        mPipeliningPenalty =
  1.3587 +            std::max(int32_t(mPipeliningPenalty - creditsEarned), 0);
  1.3588 +        if (mPipeliningPenalty > 0)
  1.3589 +            failed = true;
  1.3590 +
  1.3591 +        for (int32_t i = 0; i < nsAHttpTransaction::CLASS_MAX; ++i) {
  1.3592 +            mPipeliningClassPenalty[i]  =
  1.3593 +                std::max(int32_t(mPipeliningClassPenalty[i] - creditsEarned), 0);
  1.3594 +            failed = failed || (mPipeliningClassPenalty[i] > 0);
  1.3595 +        }
  1.3596 +
  1.3597 +        // update last credit mark to reflect elapsed time
  1.3598 +        mLastCreditTime += TimeDuration::FromSeconds(creditsEarned << 4);
  1.3599 +    }
  1.3600 +    else {
  1.3601 +        failed = true;                         /* just assume this */
  1.3602 +    }
  1.3603 +
  1.3604 +    // If we are no longer red then clear the credit counter - you only
  1.3605 +    // get credits for time spent in the red state
  1.3606 +    if (!failed)
  1.3607 +        mLastCreditTime = TimeStamp();    /* reset to null timestamp */
  1.3608 +
  1.3609 +    if (mPipelineState == PS_RED && !mPipeliningPenalty)
  1.3610 +    {
  1.3611 +        LOG(("transition %s to yellow based on time credit\n",
  1.3612 +             mConnInfo->Host()));
  1.3613 +        mPipelineState = PS_YELLOW;
  1.3614 +        mYellowConnection = nullptr;
  1.3615 +    }
  1.3616 +}
  1.3617 +
  1.3618 +uint32_t
  1.3619 +nsHttpConnectionMgr::
  1.3620 +nsConnectionEntry::MaxPipelineDepth(nsAHttpTransaction::Classifier aClass)
  1.3621 +{
  1.3622 +    // Still subject to configuration limit no matter return value
  1.3623 +
  1.3624 +    if ((mPipelineState == PS_RED) || (mPipeliningClassPenalty[aClass] > 0))
  1.3625 +        return 0;
  1.3626 +
  1.3627 +    if (mPipelineState == PS_YELLOW)
  1.3628 +        return kPipelineRestricted;
  1.3629 +
  1.3630 +    return mGreenDepth;
  1.3631 +}
  1.3632 +
  1.3633 +PLDHashOperator
  1.3634 +nsHttpConnectionMgr::ReadConnectionEntry(const nsACString &key,
  1.3635 +                nsAutoPtr<nsConnectionEntry> &ent,
  1.3636 +                void *aArg)
  1.3637 +{
  1.3638 +    if (ent->mConnInfo->GetPrivate())
  1.3639 +        return PL_DHASH_NEXT;
  1.3640 +
  1.3641 +    nsTArray<HttpRetParams> *args = static_cast<nsTArray<HttpRetParams> *> (aArg);
  1.3642 +    HttpRetParams data;
  1.3643 +    data.host = ent->mConnInfo->Host();
  1.3644 +    data.port = ent->mConnInfo->Port();
  1.3645 +    for (uint32_t i = 0; i < ent->mActiveConns.Length(); i++) {
  1.3646 +        HttpConnInfo info;
  1.3647 +        info.ttl = ent->mActiveConns[i]->TimeToLive();
  1.3648 +        info.rtt = ent->mActiveConns[i]->Rtt();
  1.3649 +        if (ent->mActiveConns[i]->UsingSpdy())
  1.3650 +            info.SetHTTP2ProtocolVersion(ent->mActiveConns[i]->GetSpdyVersion());
  1.3651 +        else
  1.3652 +            info.SetHTTP1ProtocolVersion(ent->mActiveConns[i]->GetLastHttpResponseVersion());
  1.3653 +
  1.3654 +        data.active.AppendElement(info);
  1.3655 +    }
  1.3656 +    for (uint32_t i = 0; i < ent->mIdleConns.Length(); i++) {
  1.3657 +        HttpConnInfo info;
  1.3658 +        info.ttl = ent->mIdleConns[i]->TimeToLive();
  1.3659 +        info.rtt = ent->mIdleConns[i]->Rtt();
  1.3660 +        info.SetHTTP1ProtocolVersion(ent->mIdleConns[i]->GetLastHttpResponseVersion());
  1.3661 +        data.idle.AppendElement(info);
  1.3662 +    }
  1.3663 +    for(uint32_t i = 0; i < ent->mHalfOpens.Length(); i++) {
  1.3664 +        HalfOpenSockets hSocket;
  1.3665 +        hSocket.speculative = ent->mHalfOpens[i]->IsSpeculative();
  1.3666 +        data.halfOpens.AppendElement(hSocket);
  1.3667 +    }
  1.3668 +    data.spdy = ent->mUsingSpdy;
  1.3669 +    data.ssl = ent->mConnInfo->UsingSSL();
  1.3670 +    args->AppendElement(data);
  1.3671 +    return PL_DHASH_NEXT;
  1.3672 +}
  1.3673 +
  1.3674 +bool
  1.3675 +nsHttpConnectionMgr::GetConnectionData(nsTArray<HttpRetParams> *aArg)
  1.3676 +{
  1.3677 +    mCT.Enumerate(ReadConnectionEntry, aArg);
  1.3678 +    return true;
  1.3679 +}
  1.3680 +
  1.3681 +void
  1.3682 +nsHttpConnectionMgr::ResetIPFamilyPreference(nsHttpConnectionInfo *ci)
  1.3683 +{
  1.3684 +    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
  1.3685 +    nsConnectionEntry *ent = LookupConnectionEntry(ci, nullptr, nullptr);
  1.3686 +    if (ent)
  1.3687 +        ent->ResetIPFamilyPreference();
  1.3688 +}
  1.3689 +
  1.3690 +uint32_t
  1.3691 +nsHttpConnectionMgr::
  1.3692 +nsConnectionEntry::UnconnectedHalfOpens()
  1.3693 +{
  1.3694 +    uint32_t unconnectedHalfOpens = 0;
  1.3695 +    for (uint32_t i = 0; i < mHalfOpens.Length(); ++i) {
  1.3696 +        if (!mHalfOpens[i]->HasConnected())
  1.3697 +            ++unconnectedHalfOpens;
  1.3698 +    }
  1.3699 +    return unconnectedHalfOpens;
  1.3700 +}
  1.3701 +
  1.3702 +void
  1.3703 +nsHttpConnectionMgr::
  1.3704 +nsConnectionEntry::RemoveHalfOpen(nsHalfOpenSocket *halfOpen)
  1.3705 +{
  1.3706 +    // A failure to create the transport object at all
  1.3707 +    // will result in it not being present in the halfopen table
  1.3708 +    // so ignore failures of RemoveElement()
  1.3709 +    mHalfOpens.RemoveElement(halfOpen);
  1.3710 +    gHttpHandler->ConnMgr()->mNumHalfOpenConns--;
  1.3711 +
  1.3712 +    if (!UnconnectedHalfOpens())
  1.3713 +        // perhaps this reverted RestrictConnections()
  1.3714 +        // use the PostEvent version of processpendingq to avoid
  1.3715 +        // altering the pending q vector from an arbitrary stack
  1.3716 +        gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo);
  1.3717 +}
  1.3718 +
  1.3719 +void
  1.3720 +nsHttpConnectionMgr::
  1.3721 +nsConnectionEntry::RecordIPFamilyPreference(uint16_t family)
  1.3722 +{
  1.3723 +  if (family == PR_AF_INET && !mPreferIPv6)
  1.3724 +    mPreferIPv4 = true;
  1.3725 +
  1.3726 +  if (family == PR_AF_INET6 && !mPreferIPv4)
  1.3727 +    mPreferIPv6 = true;
  1.3728 +}
  1.3729 +
  1.3730 +void
  1.3731 +nsHttpConnectionMgr::
  1.3732 +nsConnectionEntry::ResetIPFamilyPreference()
  1.3733 +{
  1.3734 +  mPreferIPv4 = false;
  1.3735 +  mPreferIPv6 = false;
  1.3736 +}
  1.3737 +
  1.3738 +} // namespace mozilla::net
  1.3739 +} // namespace mozilla

mercurial