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