Coverage Report

Created: 2018-09-25 14:53

/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