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