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