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