netwerk/protocol/http/nsHttpConnectionMgr.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial