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