Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/netwerk/protocol/http/nsHttpConnection.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* vim:set ts=4 sw=4 sts=4 et cin: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
// HttpLog.h should generally be included first
8
#include "HttpLog.h"
9
10
// Log on level :5, instead of default :4.
11
#undef LOG
12
0
#define LOG(args) LOG5(args)
13
#undef LOG_ENABLED
14
#define LOG_ENABLED() LOG5_ENABLED()
15
16
0
#define TLS_EARLY_DATA_NOT_AVAILABLE 0
17
0
#define TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED 1
18
0
#define TLS_EARLY_DATA_AVAILABLE_AND_USED 2
19
20
0
#define ESNI_SUCCESSFUL 0
21
0
#define ESNI_FAILED 1
22
0
#define NO_ESNI_SUCCESSFUL 2
23
0
#define NO_ESNI_FAILED 3
24
25
#include "ASpdySession.h"
26
#include "mozilla/ChaosMode.h"
27
#include "mozilla/Telemetry.h"
28
#include "nsHttpConnection.h"
29
#include "nsHttpHandler.h"
30
#include "nsHttpRequestHead.h"
31
#include "nsHttpResponseHead.h"
32
#include "nsIClassOfService.h"
33
#include "nsIOService.h"
34
#include "nsISocketTransport.h"
35
#include "nsSocketTransportService2.h"
36
#include "nsISSLSocketControl.h"
37
#include "nsISupportsPriority.h"
38
#include "nsPreloadedStream.h"
39
#include "nsProxyRelease.h"
40
#include "nsSocketTransport2.h"
41
#include "nsStringStream.h"
42
#include "pkix/pkixnss.h"
43
#include "sslt.h"
44
#include "NSSErrorsService.h"
45
#include "TunnelUtils.h"
46
#include "TCPFastOpenLayer.h"
47
48
namespace mozilla {
49
namespace net {
50
51
//-----------------------------------------------------------------------------
52
// nsHttpConnection <public>
53
//-----------------------------------------------------------------------------
54
55
nsHttpConnection::nsHttpConnection()
56
    : mSocketInCondition(NS_ERROR_NOT_INITIALIZED)
57
    , mSocketOutCondition(NS_ERROR_NOT_INITIALIZED)
58
    , mTransaction(nullptr)
59
    , mHttpHandler(gHttpHandler)
60
    , mCallbacksLock("nsHttpConnection::mCallbacksLock")
61
    , mLastReadTime(0)
62
    , mLastWriteTime(0)
63
    , mMaxHangTime(0)
64
    , mConsiderReusedAfterInterval(0)
65
    , mConsiderReusedAfterEpoch(0)
66
    , mCurrentBytesRead(0)
67
    , mMaxBytesRead(0)
68
    , mTotalBytesRead(0)
69
    , mTotalBytesWritten(0)
70
    , mContentBytesWritten(0)
71
    , mRtt(0)
72
    , mUrgentStartPreferred(false)
73
    , mUrgentStartPreferredKnown(false)
74
    , mConnectedTransport(false)
75
    , mKeepAlive(true) // assume to keep-alive by default
76
    , mKeepAliveMask(true)
77
    , mDontReuse(false)
78
    , mIsReused(false)
79
    , mCompletedProxyConnect(false)
80
    , mLastTransactionExpectedNoContent(false)
81
    , mIdleMonitoring(false)
82
    , mProxyConnectInProgress(false)
83
    , mExperienced(false)
84
    , mInSpdyTunnel(false)
85
    , mForcePlainText(false)
86
    , mTrafficCount(0)
87
    , mTrafficStamp(false)
88
    , mHttp1xTransactionCount(0)
89
    , mRemainingConnectionUses(0xffffffff)
90
    , mNPNComplete(false)
91
    , mSetupSSLCalled(false)
92
    , mUsingSpdyVersion(SpdyVersion::NONE)
93
    , mPriority(nsISupportsPriority::PRIORITY_NORMAL)
94
    , mReportedSpdy(false)
95
    , mEverUsedSpdy(false)
96
    , mLastHttpResponseVersion(HttpVersion::v1_1)
97
    , mTransactionCaps(0)
98
    , mDefaultTimeoutFactor(1)
99
    , mResponseTimeoutEnabled(false)
100
    , mTCPKeepaliveConfig(kTCPKeepaliveDisabled)
101
    , mForceSendPending(false)
102
    , m0RTTChecked(false)
103
    , mWaitingFor0RTTResponse(false)
104
    , mContentBytesWritten0RTT(0)
105
    , mEarlyDataNegotiated(false)
106
    , mDid0RTTSpdy(false)
107
    , mFastOpen(false)
108
    , mFastOpenStatus(TFO_NOT_SET)
109
    , mForceSendDuringFastOpenPending(false)
110
    , mReceivedSocketWouldBlockDuringFastOpen(false)
111
    , mCheckNetworkStallsWithTFO(false)
112
    , mLastRequestBytesSentTime(0)
113
    , mBootstrappedTimingsSet(false)
114
0
{
115
0
    LOG(("Creating nsHttpConnection @%p\n", this));
116
0
117
0
    // the default timeout is for when this connection has not yet processed a
118
0
    // transaction
119
0
    static const PRIntervalTime k5Sec = PR_SecondsToInterval(5);
120
0
    mIdleTimeout =
121
0
        (k5Sec < gHttpHandler->IdleTimeout()) ? k5Sec : gHttpHandler->IdleTimeout();
122
0
}
123
124
nsHttpConnection::~nsHttpConnection()
125
0
{
126
0
    LOG(("Destroying nsHttpConnection @%p\n", this));
127
0
128
0
    if (!mEverUsedSpdy) {
129
0
        LOG(("nsHttpConnection %p performed %d HTTP/1.x transactions\n",
130
0
             this, mHttp1xTransactionCount));
131
0
        Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_CONN,
132
0
                              mHttp1xTransactionCount);
133
0
        nsHttpConnectionInfo *ci = nullptr;
134
0
        if (mTransaction) {
135
0
            ci = mTransaction->ConnectionInfo();
136
0
        }
137
0
        if (!ci) {
138
0
            ci = mConnInfo;
139
0
        }
140
0
141
0
        MOZ_ASSERT(ci);
142
0
        if (ci->GetTrrUsed()) {
143
0
            Telemetry::Accumulate(Telemetry::DNS_TRR_REQUEST_PER_CONN,
144
0
                                  mHttp1xTransactionCount);
145
0
        }
146
0
    }
147
0
148
0
    if (mTotalBytesRead) {
149
0
        uint32_t totalKBRead = static_cast<uint32_t>(mTotalBytesRead >> 10);
150
0
        LOG(("nsHttpConnection %p read %dkb on connection spdy=%d\n",
151
0
             this, totalKBRead, mEverUsedSpdy));
152
0
        Telemetry::Accumulate(mEverUsedSpdy ?
153
0
                              Telemetry::SPDY_KBREAD_PER_CONN :
154
0
                              Telemetry::HTTP_KBREAD_PER_CONN,
155
0
                              totalKBRead);
156
0
    }
157
0
    if (mForceSendTimer) {
158
0
        mForceSendTimer->Cancel();
159
0
        mForceSendTimer = nullptr;
160
0
    }
161
0
162
0
    if ((mFastOpenStatus != TFO_FAILED) &&
163
0
        (mFastOpenStatus != TFO_HTTP) &&
164
0
        (((mFastOpenStatus > TFO_DISABLED_CONNECT) && (mFastOpenStatus < TFO_BACKUP_CONN)) ||
165
0
         gHttpHandler->UseFastOpen())) {
166
0
        // TFO_FAILED will be reported in the replacement connection with more
167
0
        // details.
168
0
        // Otherwise report only if TFO is enabled and supported.
169
0
        // If TFO is disabled, report only connections ha cause it to be disabled, e.g. TFO_FAILED_NET_TIMEOUT, etc.
170
0
        Telemetry::Accumulate(Telemetry::TCP_FAST_OPEN_3, mFastOpenStatus);
171
0
    }
172
0
}
173
174
nsresult
175
nsHttpConnection::Init(nsHttpConnectionInfo *info,
176
                       uint16_t maxHangTime,
177
                       nsISocketTransport *transport,
178
                       nsIAsyncInputStream *instream,
179
                       nsIAsyncOutputStream *outstream,
180
                       bool connectedTransport,
181
                       nsIInterfaceRequestor *callbacks,
182
                       PRIntervalTime rtt)
183
0
{
184
0
    LOG(("nsHttpConnection::Init this=%p sockettransport=%p", this, transport));
185
0
    NS_ENSURE_ARG_POINTER(info);
186
0
    NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
187
0
188
0
    mConnectedTransport = connectedTransport;
189
0
    mConnInfo = info;
190
0
    MOZ_ASSERT(mConnInfo);
191
0
    mLastWriteTime = mLastReadTime = PR_IntervalNow();
192
0
    mRtt = rtt;
193
0
    mMaxHangTime = PR_SecondsToInterval(maxHangTime);
194
0
195
0
    mSocketTransport = transport;
196
0
    mSocketIn = instream;
197
0
    mSocketOut = outstream;
198
0
199
0
    // See explanation for non-strictness of this operation in SetSecurityCallbacks.
200
0
    mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>(
201
0
      "nsHttpConnection::mCallbacks", callbacks, false);
202
0
203
0
    mSocketTransport->SetEventSink(this, nullptr);
204
0
    mSocketTransport->SetSecurityCallbacks(this);
205
0
206
0
    return NS_OK;
207
0
}
208
209
nsresult
210
nsHttpConnection::TryTakeSubTransactions(nsTArray<RefPtr<nsAHttpTransaction> > &list)
211
0
{
212
0
    nsresult rv = mTransaction->TakeSubTransactions(list);
213
0
214
0
    if (rv == NS_ERROR_ALREADY_OPENED) {
215
0
        // Has the interface for TakeSubTransactions() changed?
216
0
        LOG(("TakeSubTransactions somehow called after "
217
0
             "nsAHttpTransaction began processing\n"));
218
0
        MOZ_ASSERT(false,
219
0
                   "TakeSubTransactions somehow called after "
220
0
                   "nsAHttpTransaction began processing");
221
0
        mTransaction->Close(NS_ERROR_ABORT);
222
0
        return rv;
223
0
    }
224
0
225
0
    if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
226
0
        // Has the interface for TakeSubTransactions() changed?
227
0
        LOG(("unexpected rv from nnsAHttpTransaction::TakeSubTransactions()"));
228
0
        MOZ_ASSERT(false,
229
0
                   "unexpected result from "
230
0
                   "nsAHttpTransaction::TakeSubTransactions()");
231
0
        mTransaction->Close(NS_ERROR_ABORT);
232
0
        return rv;
233
0
    }
234
0
235
0
    return rv;
236
0
}
237
238
nsresult
239
nsHttpConnection::MoveTransactionsToSpdy(nsresult status, nsTArray<RefPtr<nsAHttpTransaction> > &list)
240
0
{
241
0
    if (NS_FAILED(status)) { // includes NS_ERROR_NOT_IMPLEMENTED
242
0
        MOZ_ASSERT(list.IsEmpty(), "sub transaction list not empty");
243
0
244
0
        // This is ok - treat mTransaction as a single real request.
245
0
        // Wrap the old http transaction into the new spdy session
246
0
        // as the first stream.
247
0
        LOG(("nsHttpConnection::MoveTransactionsToSpdy moves single transaction %p "
248
0
             "into SpdySession %p\n", mTransaction.get(), mSpdySession.get()));
249
0
        nsresult rv = AddTransaction(mTransaction, mPriority);
250
0
        if (NS_FAILED(rv)) {
251
0
            return rv;
252
0
        }
253
0
    } else {
254
0
        int32_t count = list.Length();
255
0
256
0
        LOG(("nsHttpConnection::MoveTransactionsToSpdy moving transaction list len=%d "
257
0
             "into SpdySession %p\n", count, mSpdySession.get()));
258
0
259
0
        if (!count) {
260
0
            mTransaction->Close(NS_ERROR_ABORT);
261
0
            return NS_ERROR_ABORT;
262
0
        }
263
0
264
0
        for (int32_t index = 0; index < count; ++index) {
265
0
            nsresult rv = AddTransaction(list[index], mPriority);
266
0
            if (NS_FAILED(rv)) {
267
0
                return rv;
268
0
            }
269
0
        }
270
0
    }
271
0
272
0
    return NS_OK;
273
0
}
274
275
void
276
nsHttpConnection::Start0RTTSpdy(SpdyVersion spdyVersion)
277
0
{
278
0
    LOG(("nsHttpConnection::Start0RTTSpdy [this=%p]", this));
279
0
280
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
281
0
282
0
    mDid0RTTSpdy = true;
283
0
    mUsingSpdyVersion = spdyVersion;
284
0
    mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport,
285
0
                                                true);
286
0
287
0
    nsTArray<RefPtr<nsAHttpTransaction> > list;
288
0
    nsresult rv = TryTakeSubTransactions(list);
289
0
    if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
290
0
        LOG(("nsHttpConnection::Start0RTTSpdy [this=%p] failed taking "
291
0
             "subtransactions rv=%" PRIx32 , this, static_cast<uint32_t>(rv)));
292
0
        return;
293
0
    }
294
0
295
0
    rv = MoveTransactionsToSpdy(rv, list);
296
0
    if (NS_FAILED(rv)) {
297
0
        LOG(("nsHttpConnection::Start0RTTSpdy [this=%p] failed moving "
298
0
             "transactions rv=%" PRIx32 , this, static_cast<uint32_t>(rv)));
299
0
        return;
300
0
    }
301
0
302
0
    mTransaction = mSpdySession;
303
0
}
304
305
void
306
nsHttpConnection::StartSpdy(nsISSLSocketControl *sslControl, SpdyVersion spdyVersion)
307
0
{
308
0
    LOG(("nsHttpConnection::StartSpdy [this=%p, mDid0RTTSpdy=%d]\n", this, mDid0RTTSpdy));
309
0
310
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
311
0
    MOZ_ASSERT(!mSpdySession || mDid0RTTSpdy);
312
0
313
0
    mUsingSpdyVersion = spdyVersion;
314
0
    mEverUsedSpdy = true;
315
0
    if (sslControl) {
316
0
        sslControl->SetDenyClientCert(true);
317
0
    }
318
0
319
0
    if (!mDid0RTTSpdy) {
320
0
        mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport,
321
0
                                                    false);
322
0
    }
323
0
324
0
    if (!mReportedSpdy) {
325
0
        mReportedSpdy = true;
326
0
        gHttpHandler->ConnMgr()->ReportSpdyConnection(this, true);
327
0
    }
328
0
329
0
    // Setting the connection as reused allows some transactions that fail
330
0
    // with NS_ERROR_NET_RESET to be restarted and SPDY uses that code
331
0
    // to handle clean rejections (such as those that arrived after
332
0
    // a server goaway was generated).
333
0
    mIsReused = true;
334
0
335
0
    // If mTransaction is a muxed object it might represent
336
0
    // several requests. If so, we need to unpack that and
337
0
    // pack them all into a new spdy session.
338
0
339
0
    nsTArray<RefPtr<nsAHttpTransaction> > list;
340
0
    nsresult status = NS_OK;
341
0
    if (!mDid0RTTSpdy) {
342
0
        status = TryTakeSubTransactions(list);
343
0
344
0
        if (NS_FAILED(status) && status != NS_ERROR_NOT_IMPLEMENTED) {
345
0
            return;
346
0
        }
347
0
    }
348
0
349
0
    if (NeedSpdyTunnel()) {
350
0
        LOG3(("nsHttpConnection::StartSpdy %p Connecting To a HTTP/2 "
351
0
              "Proxy and Need Connect", this));
352
0
        MOZ_ASSERT(mProxyConnectStream);
353
0
354
0
        mProxyConnectStream = nullptr;
355
0
        mCompletedProxyConnect = true;
356
0
        mProxyConnectInProgress = false;
357
0
    }
358
0
359
0
    nsresult rv = NS_OK;
360
0
    bool spdyProxy = mConnInfo->UsingHttpsProxy() && !mTLSFilter;
361
0
    if (spdyProxy) {
362
0
        RefPtr<nsHttpConnectionInfo> wildCardProxyCi;
363
0
        rv = mConnInfo->CreateWildCard(getter_AddRefs(wildCardProxyCi));
364
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
365
0
        gHttpHandler->ConnMgr()->MoveToWildCardConnEntry(mConnInfo,
366
0
                                                         wildCardProxyCi, this);
367
0
        mConnInfo = wildCardProxyCi;
368
0
        MOZ_ASSERT(mConnInfo);
369
0
    }
370
0
371
0
    if (!mDid0RTTSpdy) {
372
0
        rv = MoveTransactionsToSpdy(status, list);
373
0
        if (NS_FAILED(rv)) {
374
0
            return;
375
0
        }
376
0
    }
377
0
378
0
    // Disable TCP Keepalives - use SPDY ping instead.
379
0
    rv = DisableTCPKeepalives();
380
0
    if (NS_FAILED(rv)) {
381
0
        LOG(("nsHttpConnection::StartSpdy [%p] DisableTCPKeepalives failed "
382
0
             "rv[0x%" PRIx32 "]", this, static_cast<uint32_t>(rv)));
383
0
    }
384
0
385
0
    mIdleTimeout = gHttpHandler->SpdyTimeout() * mDefaultTimeoutFactor;
386
0
387
0
    if (!mTLSFilter) {
388
0
        mTransaction = mSpdySession;
389
0
    } else {
390
0
        rv = mTLSFilter->SetProxiedTransaction(mSpdySession);
391
0
        if (NS_FAILED(rv)) {
392
0
            LOG(("nsHttpConnection::StartSpdy [%p] SetProxiedTransaction failed"
393
0
                 " rv[0x%x]", this, static_cast<uint32_t>(rv)));
394
0
        }
395
0
    }
396
0
    if (mDontReuse) {
397
0
        mSpdySession->DontReuse();
398
0
    }
399
0
}
400
401
bool
402
nsHttpConnection::EnsureNPNComplete(nsresult &aOut0RTTWriteHandshakeValue,
403
                                    uint32_t &aOut0RTTBytesWritten)
404
0
{
405
0
    // If for some reason the components to check on NPN aren't available,
406
0
    // this function will just return true to continue on and disable SPDY
407
0
408
0
    aOut0RTTWriteHandshakeValue = NS_OK;
409
0
    aOut0RTTBytesWritten = 0;
410
0
411
0
    MOZ_ASSERT(mSocketTransport);
412
0
    if (!mSocketTransport) {
413
0
        // this cannot happen
414
0
        mNPNComplete = true;
415
0
        return true;
416
0
    }
417
0
418
0
    if (mNPNComplete) {
419
0
        return true;
420
0
    }
421
0
422
0
    nsresult rv = NS_OK;
423
0
    nsCOMPtr<nsISupports> securityInfo;
424
0
    nsCOMPtr<nsISSLSocketControl> ssl;
425
0
    nsAutoCString negotiatedNPN;
426
0
    // This is neede for telemetry
427
0
    bool handshakeSucceeded = false;
428
0
429
0
    GetSecurityInfo(getter_AddRefs(securityInfo));
430
0
    if (!securityInfo) {
431
0
        goto npnComplete;
432
0
    }
433
0
434
0
    ssl = do_QueryInterface(securityInfo, &rv);
435
0
    if (NS_FAILED(rv))
436
0
        goto npnComplete;
437
0
438
0
    if (!m0RTTChecked) {
439
0
        // We reuse m0RTTChecked. We want to send this status only once.
440
0
        mTransaction->OnTransportStatus(mSocketTransport,
441
0
                                        NS_NET_STATUS_TLS_HANDSHAKE_STARTING,
442
0
                                        0);
443
0
    }
444
0
445
0
    rv = ssl->GetNegotiatedNPN(negotiatedNPN);
446
0
    if (!m0RTTChecked && (rv == NS_ERROR_NOT_CONNECTED) &&
447
0
        !mConnInfo->UsingProxy()) {
448
0
        // There is no ALPN info (yet!). We need to consider doing 0RTT. We
449
0
        // will do so if there is ALPN information from a previous session
450
0
        // (AlpnEarlySelection), we are using HTTP/1, and the request data can
451
0
        // be safely retried.
452
0
        m0RTTChecked = true;
453
0
        nsresult rvEarlyAlpn = ssl->GetAlpnEarlySelection(mEarlyNegotiatedALPN);
454
0
        if (NS_FAILED(rvEarlyAlpn)) {
455
0
            // if ssl->DriveHandshake() has never been called the value
456
0
            // for AlpnEarlySelection is still not set. So call it here and
457
0
            // check again.
458
0
            LOG(("nsHttpConnection::EnsureNPNComplete %p - "
459
0
                 "early selected alpn not available, we will try one more time.",
460
0
                 this));
461
0
            // Let's do DriveHandshake again.
462
0
            rv = ssl->DriveHandshake();
463
0
            if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
464
0
                goto npnComplete;
465
0
            }
466
0
467
0
            // Check NegotiatedNPN first.
468
0
            rv = ssl->GetNegotiatedNPN(negotiatedNPN);
469
0
            if (rv == NS_ERROR_NOT_CONNECTED) {
470
0
                rvEarlyAlpn = ssl->GetAlpnEarlySelection(mEarlyNegotiatedALPN);
471
0
            }
472
0
        }
473
0
474
0
        if (NS_FAILED(rvEarlyAlpn)) {
475
0
            LOG(("nsHttpConnection::EnsureNPNComplete %p - "
476
0
                 "early selected alpn not available", this));
477
0
            mEarlyDataNegotiated = false;
478
0
        } else {
479
0
            LOG(("nsHttpConnection::EnsureNPNComplete %p -"
480
0
                 "early selected alpn: %s", this, mEarlyNegotiatedALPN.get()));
481
0
            uint32_t infoIndex;
482
0
            const SpdyInformation *info = gHttpHandler->SpdyInfo();
483
0
            if (NS_FAILED(info->GetNPNIndex(mEarlyNegotiatedALPN, &infoIndex))) {
484
0
                // This is the HTTP/1 case.
485
0
                // Check if early-data is allowed for this transaction.
486
0
                if (mTransaction->Do0RTT()) {
487
0
                    LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - We "
488
0
                         "can do 0RTT (http/1)!", this));
489
0
                    mWaitingFor0RTTResponse = true;
490
0
                }
491
0
            } else {
492
0
                // We have h2, we can at least 0-RTT the preamble and opening
493
0
                // SETTINGS, etc, and maybe some of the first request
494
0
                LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - Starting "
495
0
                     "0RTT for h2!", this));
496
0
                mWaitingFor0RTTResponse = true;
497
0
                Start0RTTSpdy(info->Version[infoIndex]);
498
0
            }
499
0
            mEarlyDataNegotiated = true;
500
0
        }
501
0
    }
502
0
503
0
    if (rv == NS_ERROR_NOT_CONNECTED) {
504
0
        if (mWaitingFor0RTTResponse) {
505
0
            aOut0RTTWriteHandshakeValue = mTransaction->ReadSegments(this,
506
0
                nsIOService::gDefaultSegmentSize, &aOut0RTTBytesWritten);
507
0
            if (NS_FAILED(aOut0RTTWriteHandshakeValue) &&
508
0
                aOut0RTTWriteHandshakeValue != NS_BASE_STREAM_WOULD_BLOCK) {
509
0
                goto npnComplete;
510
0
            }
511
0
            LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - written %d "
512
0
                 "bytes during 0RTT", this, aOut0RTTBytesWritten));
513
0
            mContentBytesWritten0RTT += aOut0RTTBytesWritten;
514
0
            if (mSocketOutCondition == NS_BASE_STREAM_WOULD_BLOCK) {
515
0
                mReceivedSocketWouldBlockDuringFastOpen = true;
516
0
            }
517
0
        }
518
0
519
0
        rv = ssl->DriveHandshake();
520
0
        if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
521
0
            goto npnComplete;
522
0
        }
523
0
524
0
        return false;
525
0
    }
526
0
527
0
    if (NS_SUCCEEDED(rv)) {
528
0
        LOG(("nsHttpConnection::EnsureNPNComplete %p [%s] negotiated to '%s'%s\n",
529
0
             this, mConnInfo->HashKey().get(), negotiatedNPN.get(),
530
0
             mTLSFilter ? " [Double Tunnel]" : ""));
531
0
532
0
        handshakeSucceeded = true;
533
0
534
0
        int16_t tlsVersion;
535
0
        ssl->GetSSLVersionUsed(&tlsVersion);
536
0
        mConnInfo->SetLessThanTls13((tlsVersion < nsISSLSocketControl::TLS_VERSION_1_3) &&
537
0
                                    (tlsVersion != nsISSLSocketControl::SSL_VERSION_UNKNOWN));
538
0
539
0
        bool earlyDataAccepted = false;
540
0
        if (mWaitingFor0RTTResponse) {
541
0
            // Check if early data has been accepted.
542
0
            nsresult rvEarlyData = ssl->GetEarlyDataAccepted(&earlyDataAccepted);
543
0
            LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - early data "
544
0
                 "that was sent during 0RTT %s been accepted [rv=%" PRIx32 "].",
545
0
                 this, earlyDataAccepted ? "has" : "has not", static_cast<uint32_t>(rv)));
546
0
547
0
            if (NS_FAILED(rvEarlyData) ||
548
0
                NS_FAILED(mTransaction->Finish0RTT(!earlyDataAccepted, negotiatedNPN != mEarlyNegotiatedALPN))) {
549
0
                LOG(("nsHttpConection::EnsureNPNComplete [this=%p] closing transaction %p", this, mTransaction.get()));
550
0
                mTransaction->Close(NS_ERROR_NET_RESET);
551
0
                goto npnComplete;
552
0
            }
553
0
        }
554
0
555
0
        // Send the 0RTT telemetry only for tls1.3
556
0
        if (tlsVersion > nsISSLSocketControl::TLS_VERSION_1_2) {
557
0
            Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_NEGOTIATED,
558
0
                (!mEarlyDataNegotiated) ? TLS_EARLY_DATA_NOT_AVAILABLE
559
0
                    : ((mWaitingFor0RTTResponse) ? TLS_EARLY_DATA_AVAILABLE_AND_USED
560
0
                                                 : TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED));
561
0
            if (mWaitingFor0RTTResponse) {
562
0
                Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_ACCEPTED,
563
0
                                      earlyDataAccepted);
564
0
            }
565
0
            if (earlyDataAccepted) {
566
0
                Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_BYTES_WRITTEN,
567
0
                                      mContentBytesWritten0RTT);
568
0
            }
569
0
        }
570
0
        mWaitingFor0RTTResponse = false;
571
0
572
0
        if (!earlyDataAccepted) {
573
0
            LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] early data not accepted", this));
574
0
            if (mTransaction->QueryNullTransaction() &&
575
0
                (mBootstrappedTimings.secureConnectionStart.IsNull() ||
576
0
                 mBootstrappedTimings.tcpConnectEnd.IsNull())) {
577
0
                // if TFO is used some socket event will be sent after
578
0
                // mBootstrappedTimings has been set. therefore we should
579
0
                // update them.
580
0
                mBootstrappedTimings.secureConnectionStart =
581
0
                    mTransaction->QueryNullTransaction()->GetSecureConnectionStart();
582
0
                mBootstrappedTimings.tcpConnectEnd =
583
0
                    mTransaction->QueryNullTransaction()->GetTcpConnectEnd();
584
0
            }
585
0
            uint32_t infoIndex;
586
0
            const SpdyInformation *info = gHttpHandler->SpdyInfo();
587
0
            if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) {
588
0
                StartSpdy(ssl, info->Version[infoIndex]);
589
0
            }
590
0
        } else {
591
0
          LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - %" PRId64 " bytes "
592
0
               "has been sent during 0RTT.", this, mContentBytesWritten0RTT));
593
0
          mContentBytesWritten = mContentBytesWritten0RTT;
594
0
          if (mSpdySession) {
595
0
              // We had already started 0RTT-spdy, now we need to fully set up
596
0
              // spdy, since we know we're sticking with it.
597
0
              LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - finishing "
598
0
                   "StartSpdy for 0rtt spdy session %p", this, mSpdySession.get()));
599
0
              StartSpdy(ssl, mSpdySession->SpdyVersion());
600
0
          }
601
0
        }
602
0
603
0
        Telemetry::Accumulate(Telemetry::SPDY_NPN_CONNECT, UsingSpdy());
604
0
    }
605
0
606
0
npnComplete:
607
0
    LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] setting complete to true", this));
608
0
    mNPNComplete = true;
609
0
610
0
    mTransaction->OnTransportStatus(mSocketTransport,
611
0
                                    NS_NET_STATUS_TLS_HANDSHAKE_ENDED, 0);
612
0
613
0
    // this is happening after the bootstrap was originally written to. so update it.
614
0
    if (mTransaction->QueryNullTransaction() &&
615
0
        (mBootstrappedTimings.secureConnectionStart.IsNull() ||
616
0
         mBootstrappedTimings.tcpConnectEnd.IsNull())) {
617
0
        // if TFO is used some socket event will be sent after
618
0
        // mBootstrappedTimings has been set. therefore we should
619
0
        // update them.
620
0
        mBootstrappedTimings.secureConnectionStart =
621
0
            mTransaction->QueryNullTransaction()->GetSecureConnectionStart();
622
0
        mBootstrappedTimings.tcpConnectEnd =
623
0
            mTransaction->QueryNullTransaction()->GetTcpConnectEnd();
624
0
    }
625
0
626
0
    if (securityInfo) {
627
0
        mBootstrappedTimings.connectEnd = TimeStamp::Now();
628
0
    }
629
0
630
0
    if (mWaitingFor0RTTResponse) {
631
0
        // Didn't get 0RTT OK, back out of the "attempting 0RTT" state
632
0
        mWaitingFor0RTTResponse = false;
633
0
        LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] 0rtt failed", this));
634
0
        if (NS_FAILED(mTransaction->Finish0RTT(true, negotiatedNPN != mEarlyNegotiatedALPN))) {
635
0
            mTransaction->Close(NS_ERROR_NET_RESET);
636
0
        }
637
0
        mContentBytesWritten0RTT = 0;
638
0
    }
639
0
640
0
    if (mDid0RTTSpdy && negotiatedNPN != mEarlyNegotiatedALPN) {
641
0
        // Reset the work done by Start0RTTSpdy
642
0
        LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] resetting Start0RTTSpdy", this));
643
0
        mUsingSpdyVersion = SpdyVersion::NONE;
644
0
        mTransaction = nullptr;
645
0
        mSpdySession = nullptr;
646
0
        // We have to reset this here, just in case we end up starting spdy again,
647
0
        // so it can actually do everything it needs to do.
648
0
        mDid0RTTSpdy = false;
649
0
    }
650
0
651
0
    if (ssl) {
652
0
        // Telemetry for tls failure rate with and without esni;
653
0
        bool esni;
654
0
        mSocketTransport->GetEsniUsed(&esni);
655
0
        Telemetry::Accumulate(Telemetry::ESNI_NOESNI_TLS_SUCCESS_RATE,
656
0
                              (esni) ? ((handshakeSucceeded) ? ESNI_SUCCESSFUL : ESNI_FAILED)
657
0
                                     : ((handshakeSucceeded) ? NO_ESNI_SUCCESSFUL : NO_ESNI_FAILED));
658
0
    }
659
0
660
0
    if (rv == psm::GetXPCOMFromNSSError(mozilla::pkix::MOZILLA_PKIX_ERROR_MITM_DETECTED)) {
661
0
        gSocketTransportService->SetNotTrustedMitmDetected();
662
0
    }
663
0
    return true;
664
0
}
665
666
void
667
nsHttpConnection::OnTunnelNudged(TLSFilterTransaction *trans)
668
0
{
669
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
670
0
    LOG(("nsHttpConnection::OnTunnelNudged %p\n", this));
671
0
    if (trans != mTLSFilter) {
672
0
        return;
673
0
    }
674
0
    LOG(("nsHttpConnection::OnTunnelNudged %p Calling OnSocketWritable\n", this));
675
0
    Unused << OnSocketWritable();
676
0
}
677
678
// called on the socket thread
679
nsresult
680
nsHttpConnection::Activate(nsAHttpTransaction *trans, uint32_t caps, int32_t pri)
681
0
{
682
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
683
0
    LOG(("nsHttpConnection::Activate [this=%p trans=%p caps=%x]\n",
684
0
         this, trans, caps));
685
0
686
0
    if (!mExperienced && !trans->IsNullTransaction()) {
687
0
        if (!mFastOpen) {
688
0
            mExperienced = true;
689
0
        }
690
0
        if (mBootstrappedTimingsSet) {
691
0
            mBootstrappedTimingsSet = false;
692
0
            nsHttpTransaction *hTrans = trans->QueryHttpTransaction();
693
0
            if (hTrans) {
694
0
                hTrans->BootstrapTimings(mBootstrappedTimings);
695
0
                SetUrgentStartPreferred(hTrans->ClassOfService() & nsIClassOfService::UrgentStart);
696
0
            }
697
0
        }
698
0
        mBootstrappedTimings = TimingStruct();
699
0
    }
700
0
701
0
    if (caps & NS_HTTP_LARGE_KEEPALIVE) {
702
0
        mDefaultTimeoutFactor = 10; // don't ever lower
703
0
    }
704
0
705
0
    mTransactionCaps = caps;
706
0
    mPriority = pri;
707
0
    if (mTransaction && (mUsingSpdyVersion != SpdyVersion::NONE)) {
708
0
        return AddTransaction(trans, pri);
709
0
    }
710
0
711
0
    NS_ENSURE_ARG_POINTER(trans);
712
0
    NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
713
0
714
0
    // If TCP fast Open has been used and conection was idle for some time
715
0
    // we will be cautious and watch out for bug 1395494.
716
0
    if (mNPNComplete && (mFastOpenStatus == TFO_DATA_SENT) &&
717
0
        gHttpHandler->CheckIfConnectionIsStalledOnlyIfIdleForThisAmountOfSeconds() &&
718
0
        IdleTime() >= gHttpHandler->CheckIfConnectionIsStalledOnlyIfIdleForThisAmountOfSeconds()) {
719
0
        // If a connection was using the TCP FastOpen and it was idle for a
720
0
        // long time we should check for stalls like bug 1395494.
721
0
        mCheckNetworkStallsWithTFO = true;
722
0
        // Also reset last write. We should start measuring a stall time only
723
0
        // after we really write a request to the network.
724
0
        mLastRequestBytesSentTime = 0;
725
0
    }
726
0
    // reset the read timers to wash away any idle time
727
0
    mLastWriteTime = mLastReadTime = PR_IntervalNow();
728
0
729
0
    // Connection failures are Activated() just like regular transacions.
730
0
    // If we don't have a confirmation of a connected socket then test it
731
0
    // with a write() to get relevant error code.
732
0
    if (!mConnectedTransport) {
733
0
        uint32_t count;
734
0
        mSocketOutCondition = NS_ERROR_FAILURE;
735
0
        if (mSocketOut) {
736
0
            mSocketOutCondition = mSocketOut->Write("", 0, &count);
737
0
        }
738
0
        if (NS_FAILED(mSocketOutCondition) &&
739
0
            mSocketOutCondition != NS_BASE_STREAM_WOULD_BLOCK) {
740
0
            LOG(("nsHttpConnection::Activate [this=%p] Bad Socket %" PRIx32 "\n",
741
0
                 this, static_cast<uint32_t>(mSocketOutCondition)));
742
0
            mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
743
0
            mTransaction = trans;
744
0
            CloseTransaction(mTransaction, mSocketOutCondition);
745
0
            return mSocketOutCondition;
746
0
        }
747
0
    }
748
0
749
0
    // Update security callbacks
750
0
    nsCOMPtr<nsIInterfaceRequestor> callbacks;
751
0
    trans->GetSecurityCallbacks(getter_AddRefs(callbacks));
752
0
    SetSecurityCallbacks(callbacks);
753
0
    SetupSSL();
754
0
755
0
    // take ownership of the transaction
756
0
    mTransaction = trans;
757
0
758
0
    MOZ_ASSERT(!mIdleMonitoring, "Activating a connection with an Idle Monitor");
759
0
    mIdleMonitoring = false;
760
0
761
0
    // set mKeepAlive according to what will be requested
762
0
    mKeepAliveMask = mKeepAlive = (caps & NS_HTTP_ALLOW_KEEPALIVE);
763
0
764
0
    // need to handle HTTP CONNECT tunnels if this is the first time if
765
0
    // we are tunneling through a proxy
766
0
    nsresult rv = NS_OK;
767
0
    if (mTransaction->ConnectionInfo()->UsingConnect() && !mCompletedProxyConnect) {
768
0
        rv = SetupProxyConnect();
769
0
        if (NS_FAILED(rv))
770
0
            goto failed_activation;
771
0
        mProxyConnectInProgress = true;
772
0
    }
773
0
774
0
    // Clear the per activation counter
775
0
    mCurrentBytesRead = 0;
776
0
777
0
    // The overflow state is not needed between activations
778
0
    mInputOverflow = nullptr;
779
0
780
0
    mResponseTimeoutEnabled = gHttpHandler->ResponseTimeoutEnabled() &&
781
0
                              mTransaction->ResponseTimeout() > 0 &&
782
0
                              mTransaction->ResponseTimeoutEnabled();
783
0
784
0
    rv = StartShortLivedTCPKeepalives();
785
0
    if (NS_FAILED(rv)) {
786
0
        LOG(("nsHttpConnection::Activate [%p] "
787
0
             "StartShortLivedTCPKeepalives failed rv[0x%" PRIx32 "]",
788
0
             this, static_cast<uint32_t>(rv)));
789
0
    }
790
0
791
0
    if (mTLSFilter) {
792
0
        rv = mTLSFilter->SetProxiedTransaction(trans);
793
0
        NS_ENSURE_SUCCESS(rv, rv);
794
0
        mTransaction = mTLSFilter;
795
0
    }
796
0
797
0
    trans->OnActivated();
798
0
799
0
    rv = OnOutputStreamReady(mSocketOut);
800
0
801
0
failed_activation:
802
0
    if (NS_FAILED(rv)) {
803
0
        mTransaction = nullptr;
804
0
    }
805
0
806
0
    return rv;
807
0
}
808
809
void
810
nsHttpConnection::SetupSSL()
811
0
{
812
0
    LOG(("nsHttpConnection::SetupSSL %p caps=0x%X %s\n",
813
0
         this, mTransactionCaps, mConnInfo->HashKey().get()));
814
0
815
0
    if (mSetupSSLCalled) // do only once
816
0
        return;
817
0
    mSetupSSLCalled = true;
818
0
819
0
    if (mNPNComplete)
820
0
        return;
821
0
822
0
    // we flip this back to false if SetNPNList succeeds at the end
823
0
    // of this function
824
0
    mNPNComplete = true;
825
0
826
0
    if (!mConnInfo->FirstHopSSL() || mForcePlainText) {
827
0
        return;
828
0
    }
829
0
830
0
    // if we are connected to the proxy with TLS, start the TLS
831
0
    // flow immediately without waiting for a CONNECT sequence.
832
0
    DebugOnly<nsresult> rv;
833
0
    if (mInSpdyTunnel) {
834
0
        rv = InitSSLParams(false, true);
835
0
    } else {
836
0
        bool usingHttpsProxy = mConnInfo->UsingHttpsProxy();
837
0
        rv = InitSSLParams(usingHttpsProxy, usingHttpsProxy);
838
0
    }
839
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
840
0
}
841
842
// The naming of NPN is historical - this function creates the basic
843
// offer list for both NPN and ALPN. ALPN validation callbacks are made
844
// now before the handshake is complete, and NPN validation callbacks
845
// are made during the handshake.
846
nsresult
847
nsHttpConnection::SetupNPNList(nsISSLSocketControl *ssl, uint32_t caps)
848
0
{
849
0
    nsTArray<nsCString> protocolArray;
850
0
851
0
    nsCString npnToken = mConnInfo->GetNPNToken();
852
0
    if (npnToken.IsEmpty()) {
853
0
        // The first protocol is used as the fallback if none of the
854
0
        // protocols supported overlap with the server's list.
855
0
        // When using ALPN the advertised preferences are protocolArray indicies
856
0
        // {1, .., N, 0} in decreasing order.
857
0
        // For NPN, In the case of overlap, matching priority is driven by
858
0
        // the order of the server's advertisement - with index 0 used when
859
0
        // there is no match.
860
0
        protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
861
0
862
0
        if (gHttpHandler->IsSpdyEnabled() &&
863
0
            !(caps & NS_HTTP_DISALLOW_SPDY)) {
864
0
            LOG(("nsHttpConnection::SetupSSL Allow SPDY NPN selection"));
865
0
            const SpdyInformation *info = gHttpHandler->SpdyInfo();
866
0
            for (uint32_t index = SpdyInformation::kCount; index > 0; --index) {
867
0
                if (info->ProtocolEnabled(index - 1) &&
868
0
                    info->ALPNCallbacks[index - 1](ssl)) {
869
0
                    protocolArray.AppendElement(info->VersionString[index - 1]);
870
0
                }
871
0
            }
872
0
        }
873
0
    } else {
874
0
        LOG(("nsHttpConnection::SetupSSL limiting NPN selection to %s",
875
0
             npnToken.get()));
876
0
        protocolArray.AppendElement(npnToken);
877
0
    }
878
0
879
0
    nsresult rv = ssl->SetNPNList(protocolArray);
880
0
    LOG(("nsHttpConnection::SetupNPNList %p %" PRIx32 "\n",
881
0
         this, static_cast<uint32_t>(rv)));
882
0
    return rv;
883
0
}
884
885
nsresult
886
nsHttpConnection::AddTransaction(nsAHttpTransaction *httpTransaction,
887
                                 int32_t priority)
888
0
{
889
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
890
0
    MOZ_ASSERT(mSpdySession && (mUsingSpdyVersion != SpdyVersion::NONE),
891
0
               "AddTransaction to live http connection without spdy");
892
0
893
0
    // If this is a wild card nshttpconnection (i.e. a spdy proxy) then
894
0
    // it is important to start the stream using the specific connection
895
0
    // info of the transaction to ensure it is routed on the right tunnel
896
0
897
0
    nsHttpConnectionInfo *transCI = httpTransaction->ConnectionInfo();
898
0
899
0
    bool needTunnel = transCI->UsingHttpsProxy();
900
0
    needTunnel = needTunnel && !mTLSFilter;
901
0
    needTunnel = needTunnel && transCI->UsingConnect();
902
0
    needTunnel = needTunnel && httpTransaction->QueryHttpTransaction();
903
0
904
0
    LOG(("nsHttpConnection::AddTransaction for SPDY%s",
905
0
         needTunnel ? " over tunnel" : ""));
906
0
907
0
    if (!mSpdySession->AddStream(httpTransaction, priority,
908
0
                                 needTunnel, mCallbacks)) {
909
0
        MOZ_ASSERT(false); // this cannot happen!
910
0
        httpTransaction->Close(NS_ERROR_ABORT);
911
0
        return NS_ERROR_FAILURE;
912
0
    }
913
0
914
0
    Unused << ResumeSend();
915
0
    return NS_OK;
916
0
}
917
918
void
919
nsHttpConnection::Close(nsresult reason, bool aIsShutdown)
920
0
{
921
0
    LOG(("nsHttpConnection::Close [this=%p reason=%" PRIx32 "]\n",
922
0
         this, static_cast<uint32_t>(reason)));
923
0
924
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
925
0
926
0
    // Ensure TCP keepalive timer is stopped.
927
0
    if (mTCPKeepaliveTransitionTimer) {
928
0
        mTCPKeepaliveTransitionTimer->Cancel();
929
0
        mTCPKeepaliveTransitionTimer = nullptr;
930
0
    }
931
0
    if (mForceSendTimer) {
932
0
        mForceSendTimer->Cancel();
933
0
        mForceSendTimer = nullptr;
934
0
    }
935
0
936
0
    if (NS_FAILED(reason)) {
937
0
        if (mIdleMonitoring)
938
0
            EndIdleMonitoring();
939
0
940
0
        mTLSFilter = nullptr;
941
0
942
0
        // The connection and security errors clear out alt-svc mappings
943
0
        // in case any previously validated ones are now invalid
944
0
        if (((reason == NS_ERROR_NET_RESET) ||
945
0
             (NS_ERROR_GET_MODULE(reason) == NS_ERROR_MODULE_SECURITY))
946
0
            && mConnInfo && !(mTransactionCaps & NS_HTTP_ERROR_SOFTLY)) {
947
0
            gHttpHandler->ConnMgr()->ClearHostMapping(mConnInfo);
948
0
        }
949
0
950
0
        if (mSocketTransport) {
951
0
            mSocketTransport->SetEventSink(nullptr, nullptr);
952
0
953
0
            // If there are bytes sitting in the input queue then read them
954
0
            // into a junk buffer to avoid generating a tcp rst by closing a
955
0
            // socket with data pending. TLS is a classic case of this where
956
0
            // a Alert record might be superfulous to a clean HTTP/SPDY shutdown.
957
0
            // Never block to do this and limit it to a small amount of data.
958
0
            // During shutdown just be fast!
959
0
            if (mSocketIn && !aIsShutdown) {
960
0
                char buffer[4000];
961
0
                uint32_t count, total = 0;
962
0
                nsresult rv;
963
0
                do {
964
0
                    rv = mSocketIn->Read(buffer, 4000, &count);
965
0
                    if (NS_SUCCEEDED(rv))
966
0
                        total += count;
967
0
                }
968
0
                while (NS_SUCCEEDED(rv) && count > 0 && total < 64000);
969
0
                LOG(("nsHttpConnection::Close drained %d bytes\n", total));
970
0
            }
971
0
972
0
            mSocketTransport->SetSecurityCallbacks(nullptr);
973
0
            mSocketTransport->Close(reason);
974
0
            if (mSocketOut)
975
0
                mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
976
0
        }
977
0
        mKeepAlive = false;
978
0
    }
979
0
}
980
981
// called on the socket thread
982
nsresult
983
nsHttpConnection::InitSSLParams(bool connectingToProxy, bool proxyStartSSL)
984
0
{
985
0
    LOG(("nsHttpConnection::InitSSLParams [this=%p] connectingToProxy=%d\n",
986
0
         this, connectingToProxy));
987
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
988
0
989
0
    nsresult rv;
990
0
    nsCOMPtr<nsISupports> securityInfo;
991
0
    GetSecurityInfo(getter_AddRefs(securityInfo));
992
0
    if (!securityInfo) {
993
0
        return NS_ERROR_FAILURE;
994
0
    }
995
0
996
0
    nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
997
0
    if (NS_FAILED(rv)){
998
0
        return rv;
999
0
    }
1000
0
1001
0
    if (proxyStartSSL) {
1002
0
        rv = ssl->ProxyStartSSL();
1003
0
        if (NS_FAILED(rv)){
1004
0
            return rv;
1005
0
        }
1006
0
    }
1007
0
1008
0
    if (NS_SUCCEEDED(SetupNPNList(ssl, mTransactionCaps))) {
1009
0
        LOG(("InitSSLParams Setting up SPDY Negotiation OK"));
1010
0
        mNPNComplete = false;
1011
0
    }
1012
0
1013
0
    return NS_OK;
1014
0
}
1015
1016
void
1017
nsHttpConnection::DontReuse()
1018
0
{
1019
0
    LOG(("nsHttpConnection::DontReuse %p spdysession=%p\n", this, mSpdySession.get()));
1020
0
    mKeepAliveMask = false;
1021
0
    mKeepAlive = false;
1022
0
    mDontReuse = true;
1023
0
    mIdleTimeout = 0;
1024
0
    if (mSpdySession)
1025
0
        mSpdySession->DontReuse();
1026
0
}
1027
1028
bool
1029
nsHttpConnection::TestJoinConnection(const nsACString &hostname, int32_t port)
1030
0
{
1031
0
    if (mSpdySession && CanDirectlyActivate()) {
1032
0
        return mSpdySession->TestJoinConnection(hostname, port);
1033
0
    }
1034
0
    return false;
1035
0
}
1036
1037
bool
1038
nsHttpConnection::JoinConnection(const nsACString &hostname, int32_t port)
1039
0
{
1040
0
    if (mSpdySession && CanDirectlyActivate()) {
1041
0
        return mSpdySession->JoinConnection(hostname, port);
1042
0
    }
1043
0
    return false;
1044
0
}
1045
1046
bool
1047
nsHttpConnection::CanReuse()
1048
0
{
1049
0
    if (mDontReuse || !mRemainingConnectionUses) {
1050
0
        return false;
1051
0
    }
1052
0
1053
0
    if ((mTransaction ? (mTransaction->IsDone() ? 0U : 1U) : 0U) >=
1054
0
        mRemainingConnectionUses) {
1055
0
        return false;
1056
0
    }
1057
0
1058
0
    bool canReuse;
1059
0
    if (mSpdySession) {
1060
0
        canReuse = mSpdySession->CanReuse();
1061
0
    } else {
1062
0
        canReuse = IsKeepAlive();
1063
0
    }
1064
0
    canReuse = canReuse && (IdleTime() < mIdleTimeout) && IsAlive();
1065
0
1066
0
    // An idle persistent connection should not have data waiting to be read
1067
0
    // before a request is sent. Data here is likely a 408 timeout response
1068
0
    // which we would deal with later on through the restart logic, but that
1069
0
    // path is more expensive than just closing the socket now.
1070
0
1071
0
    uint64_t dataSize;
1072
0
    if (canReuse && mSocketIn && (mUsingSpdyVersion == SpdyVersion::NONE) &&
1073
0
        mHttp1xTransactionCount &&
1074
0
        NS_SUCCEEDED(mSocketIn->Available(&dataSize)) && dataSize) {
1075
0
        LOG(("nsHttpConnection::CanReuse %p %s"
1076
0
             "Socket not reusable because read data pending (%" PRIu64 ") on it.\n",
1077
0
             this, mConnInfo->Origin(), dataSize));
1078
0
        canReuse = false;
1079
0
    }
1080
0
    return canReuse;
1081
0
}
1082
1083
bool
1084
nsHttpConnection::CanDirectlyActivate()
1085
0
{
1086
0
    // return true if a new transaction can be addded to ths connection at any
1087
0
    // time through Activate(). In practice this means this is a healthy SPDY
1088
0
    // connection with room for more concurrent streams.
1089
0
1090
0
    return UsingSpdy() && CanReuse() &&
1091
0
        mSpdySession && mSpdySession->RoomForMoreStreams();
1092
0
}
1093
1094
PRIntervalTime
1095
nsHttpConnection::IdleTime()
1096
0
{
1097
0
    return mSpdySession ?
1098
0
        mSpdySession->IdleTime() : (PR_IntervalNow() - mLastReadTime);
1099
0
}
1100
1101
// returns the number of seconds left before the allowable idle period
1102
// expires, or 0 if the period has already expied.
1103
uint32_t
1104
nsHttpConnection::TimeToLive()
1105
0
{
1106
0
    LOG(("nsHttpConnection::TTL: %p %s idle %d timeout %d\n",
1107
0
         this, mConnInfo->Origin(), IdleTime(), mIdleTimeout));
1108
0
1109
0
    if (IdleTime() >= mIdleTimeout) {
1110
0
        return 0;
1111
0
    }
1112
0
1113
0
    uint32_t timeToLive = PR_IntervalToSeconds(mIdleTimeout - IdleTime());
1114
0
1115
0
    // a positive amount of time can be rounded to 0. Because 0 is used
1116
0
    // as the expiration signal, round all values from 0 to 1 up to 1.
1117
0
    if (!timeToLive) {
1118
0
        timeToLive = 1;
1119
0
    }
1120
0
    return timeToLive;
1121
0
}
1122
1123
bool
1124
nsHttpConnection::IsAlive()
1125
0
{
1126
0
    if (!mSocketTransport || !mConnectedTransport)
1127
0
        return false;
1128
0
1129
0
    // SocketTransport::IsAlive can run the SSL state machine, so make sure
1130
0
    // the NPN options are set before that happens.
1131
0
    SetupSSL();
1132
0
1133
0
    bool alive;
1134
0
    nsresult rv = mSocketTransport->IsAlive(&alive);
1135
0
    if (NS_FAILED(rv))
1136
0
        alive = false;
1137
0
1138
0
//#define TEST_RESTART_LOGIC
1139
#ifdef TEST_RESTART_LOGIC
1140
    if (!alive) {
1141
        LOG(("pretending socket is still alive to test restart logic\n"));
1142
        alive = true;
1143
    }
1144
#endif
1145
1146
0
    return alive;
1147
0
}
1148
1149
void
1150
nsHttpConnection::SetUrgentStartPreferred(bool urgent)
1151
0
{
1152
0
  if (mExperienced && !mUrgentStartPreferredKnown) {
1153
0
    // Set only according the first ever dispatched non-null transaction
1154
0
    mUrgentStartPreferredKnown = true;
1155
0
    mUrgentStartPreferred = urgent;
1156
0
    LOG(("nsHttpConnection::SetUrgentStartPreferred [this=%p urgent=%d]", this, urgent));
1157
0
  }
1158
0
}
1159
1160
//----------------------------------------------------------------------------
1161
// nsHttpConnection::nsAHttpConnection compatible methods
1162
//----------------------------------------------------------------------------
1163
1164
nsresult
1165
nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
1166
                                     nsHttpRequestHead *requestHead,
1167
                                     nsHttpResponseHead *responseHead,
1168
                                     bool *reset)
1169
0
{
1170
0
    LOG(("nsHttpConnection::OnHeadersAvailable [this=%p trans=%p response-head=%p]\n",
1171
0
        this, trans, responseHead));
1172
0
1173
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1174
0
    NS_ENSURE_ARG_POINTER(trans);
1175
0
    MOZ_ASSERT(responseHead, "No response head?");
1176
0
1177
0
    if (mInSpdyTunnel) {
1178
0
        DebugOnly<nsresult> rv =
1179
0
            responseHead->SetHeader(nsHttp::X_Firefox_Spdy_Proxy,
1180
0
                                    NS_LITERAL_CSTRING("true"));
1181
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
1182
0
    }
1183
0
1184
0
    // we won't change our keep-alive policy unless the server has explicitly
1185
0
    // told us to do so.
1186
0
1187
0
    // inspect the connection headers for keep-alive info provided the
1188
0
    // transaction completed successfully. In the case of a non-sensical close
1189
0
    // and keep-alive favor the close out of conservatism.
1190
0
1191
0
    bool explicitKeepAlive = false;
1192
0
    bool explicitClose = responseHead->HasHeaderValue(nsHttp::Connection, "close") ||
1193
0
        responseHead->HasHeaderValue(nsHttp::Proxy_Connection, "close");
1194
0
    if (!explicitClose)
1195
0
        explicitKeepAlive = responseHead->HasHeaderValue(nsHttp::Connection, "keep-alive") ||
1196
0
            responseHead->HasHeaderValue(nsHttp::Proxy_Connection, "keep-alive");
1197
0
1198
0
    // deal with 408 Server Timeouts
1199
0
    uint16_t responseStatus = responseHead->Status();
1200
0
    static const PRIntervalTime k1000ms  = PR_MillisecondsToInterval(1000);
1201
0
    if (responseStatus == 408) {
1202
0
        // If this error could be due to a persistent connection reuse then
1203
0
        // we pass an error code of NS_ERROR_NET_RESET to
1204
0
        // trigger the transaction 'restart' mechanism.  We tell it to reset its
1205
0
        // response headers so that it will be ready to receive the new response.
1206
0
        if (mIsReused && ((PR_IntervalNow() - mLastWriteTime) < k1000ms)) {
1207
0
            Close(NS_ERROR_NET_RESET);
1208
0
            *reset = true;
1209
0
            return NS_OK;
1210
0
        }
1211
0
1212
0
        // timeouts that are not caused by persistent connection reuse should
1213
0
        // not be retried for browser compatibility reasons. bug 907800. The
1214
0
        // server driven close is implicit in the 408.
1215
0
        explicitClose = true;
1216
0
        explicitKeepAlive = false;
1217
0
    }
1218
0
1219
0
    if ((responseHead->Version() < HttpVersion::v1_1) ||
1220
0
        (requestHead->Version() < HttpVersion::v1_1)) {
1221
0
        // HTTP/1.0 connections are by default NOT persistent
1222
0
        if (explicitKeepAlive)
1223
0
            mKeepAlive = true;
1224
0
        else
1225
0
            mKeepAlive = false;
1226
0
    }
1227
0
    else {
1228
0
        // HTTP/1.1 connections are by default persistent
1229
0
        mKeepAlive = !explicitClose;
1230
0
    }
1231
0
    mKeepAliveMask = mKeepAlive;
1232
0
1233
0
    // if this connection is persistent, then the server may send a "Keep-Alive"
1234
0
    // header specifying the maximum number of times the connection can be
1235
0
    // reused as well as the maximum amount of time the connection can be idle
1236
0
    // before the server will close it.  we ignore the max reuse count, because
1237
0
    // a "keep-alive" connection is by definition capable of being reused, and
1238
0
    // we only care about being able to reuse it once.  if a timeout is not
1239
0
    // specified then we use our advertized timeout value.
1240
0
    bool foundKeepAliveMax = false;
1241
0
    if (mKeepAlive) {
1242
0
        nsAutoCString keepAlive;
1243
0
        Unused << responseHead->GetHeader(nsHttp::Keep_Alive, keepAlive);
1244
0
1245
0
        if (mUsingSpdyVersion == SpdyVersion::NONE) {
1246
0
            const char *cp = PL_strcasestr(keepAlive.get(), "timeout=");
1247
0
            if (cp)
1248
0
                mIdleTimeout = PR_SecondsToInterval((uint32_t) atoi(cp + 8));
1249
0
            else
1250
0
                mIdleTimeout = gHttpHandler->IdleTimeout() * mDefaultTimeoutFactor;
1251
0
1252
0
            cp = PL_strcasestr(keepAlive.get(), "max=");
1253
0
            if (cp) {
1254
0
                int maxUses = atoi(cp + 4);
1255
0
                if (maxUses > 0) {
1256
0
                    foundKeepAliveMax = true;
1257
0
                    mRemainingConnectionUses = static_cast<uint32_t>(maxUses);
1258
0
                }
1259
0
            }
1260
0
        }
1261
0
1262
0
        LOG(("Connection can be reused [this=%p idle-timeout=%usec]\n",
1263
0
             this, PR_IntervalToSeconds(mIdleTimeout)));
1264
0
    }
1265
0
1266
0
    if (!foundKeepAliveMax && mRemainingConnectionUses && (mUsingSpdyVersion == SpdyVersion::NONE))
1267
0
        --mRemainingConnectionUses;
1268
0
1269
0
    // If we're doing a proxy connect, we need to check whether or not
1270
0
    // it was successful.  If so, we have to reset the transaction and step-up
1271
0
    // the socket connection if using SSL. Finally, we have to wake up the
1272
0
    // socket write request.
1273
0
    if (mProxyConnectStream) {
1274
0
        MOZ_ASSERT(mUsingSpdyVersion == SpdyVersion::NONE,
1275
0
                   "SPDY NPN Complete while using proxy connect stream");
1276
0
        mProxyConnectStream = nullptr;
1277
0
        bool isHttps =
1278
0
            mTransaction ? mTransaction->ConnectionInfo()->EndToEndSSL() :
1279
0
            mConnInfo->EndToEndSSL();
1280
0
1281
0
        if (responseStatus == 200) {
1282
0
            LOG(("proxy CONNECT succeeded! endtoendssl=%d\n", isHttps));
1283
0
            *reset = true;
1284
0
            nsresult rv;
1285
0
            if (isHttps) {
1286
0
                if (mConnInfo->UsingHttpsProxy()) {
1287
0
                    LOG(("%p new TLSFilterTransaction %s %d\n",
1288
0
                         this, mConnInfo->Origin(), mConnInfo->OriginPort()));
1289
0
                    SetupSecondaryTLS();
1290
0
                }
1291
0
1292
0
                rv = InitSSLParams(false, true);
1293
0
                LOG(("InitSSLParams [rv=%" PRIx32 "]\n", static_cast<uint32_t>(rv)));
1294
0
            }
1295
0
            mCompletedProxyConnect = true;
1296
0
            mProxyConnectInProgress = false;
1297
0
            rv = mSocketOut->AsyncWait(this, 0, 0, nullptr);
1298
0
            // XXX what if this fails -- need to handle this error
1299
0
            MOZ_ASSERT(NS_SUCCEEDED(rv), "mSocketOut->AsyncWait failed");
1300
0
        }
1301
0
        else {
1302
0
            LOG(("proxy CONNECT failed! endtoendssl=%d\n", isHttps));
1303
0
            mTransaction->SetProxyConnectFailed();
1304
0
        }
1305
0
    }
1306
0
1307
0
    nsAutoCString upgradeReq;
1308
0
    bool hasUpgradeReq = NS_SUCCEEDED(requestHead->GetHeader(nsHttp::Upgrade,
1309
0
                                                             upgradeReq));
1310
0
    // Don't use persistent connection for Upgrade unless there's an auth failure:
1311
0
    // some proxies expect to see auth response on persistent connection.
1312
0
    if (hasUpgradeReq && responseStatus != 401 && responseStatus != 407) {
1313
0
        LOG(("HTTP Upgrade in play - disable keepalive\n"));
1314
0
        DontReuse();
1315
0
    }
1316
0
1317
0
    if (responseStatus == 101) {
1318
0
        nsAutoCString upgradeResp;
1319
0
        bool hasUpgradeResp = NS_SUCCEEDED(responseHead->GetHeader(
1320
0
                                                nsHttp::Upgrade,
1321
0
                                                upgradeResp));
1322
0
        if (!hasUpgradeReq || !hasUpgradeResp ||
1323
0
            !nsHttp::FindToken(upgradeResp.get(), upgradeReq.get(),
1324
0
                               HTTP_HEADER_VALUE_SEPS)) {
1325
0
            LOG(("HTTP 101 Upgrade header mismatch req = %s, resp = %s\n",
1326
0
                 upgradeReq.get(),
1327
0
                 !upgradeResp.IsEmpty() ? upgradeResp.get() :
1328
0
                     "RESPONSE's nsHttp::Upgrade is empty"));
1329
0
            Close(NS_ERROR_ABORT);
1330
0
        }
1331
0
        else {
1332
0
            LOG(("HTTP Upgrade Response to %s\n", upgradeResp.get()));
1333
0
        }
1334
0
    }
1335
0
1336
0
    mLastHttpResponseVersion = responseHead->Version();
1337
0
1338
0
    return NS_OK;
1339
0
}
1340
1341
bool
1342
nsHttpConnection::IsReused()
1343
0
{
1344
0
    if (mIsReused)
1345
0
        return true;
1346
0
    if (!mConsiderReusedAfterInterval)
1347
0
        return false;
1348
0
1349
0
    // ReusedAfter allows a socket to be consider reused only after a certain
1350
0
    // interval of time has passed
1351
0
    return (PR_IntervalNow() - mConsiderReusedAfterEpoch) >=
1352
0
        mConsiderReusedAfterInterval;
1353
0
}
1354
1355
void
1356
nsHttpConnection::SetIsReusedAfter(uint32_t afterMilliseconds)
1357
0
{
1358
0
    mConsiderReusedAfterEpoch = PR_IntervalNow();
1359
0
    mConsiderReusedAfterInterval = PR_MillisecondsToInterval(afterMilliseconds);
1360
0
}
1361
1362
nsresult
1363
nsHttpConnection::TakeTransport(nsISocketTransport  **aTransport,
1364
                                nsIAsyncInputStream **aInputStream,
1365
                                nsIAsyncOutputStream **aOutputStream)
1366
0
{
1367
0
    if (mUsingSpdyVersion != SpdyVersion::NONE)
1368
0
        return NS_ERROR_FAILURE;
1369
0
    if (mTransaction && !mTransaction->IsDone())
1370
0
        return NS_ERROR_IN_PROGRESS;
1371
0
    if (!(mSocketTransport && mSocketIn && mSocketOut))
1372
0
        return NS_ERROR_NOT_INITIALIZED;
1373
0
1374
0
    if (mInputOverflow)
1375
0
        mSocketIn = mInputOverflow.forget();
1376
0
1377
0
    // Change TCP Keepalive frequency to long-lived if currently short-lived.
1378
0
    if (mTCPKeepaliveConfig == kTCPKeepaliveShortLivedConfig) {
1379
0
        if (mTCPKeepaliveTransitionTimer) {
1380
0
            mTCPKeepaliveTransitionTimer->Cancel();
1381
0
            mTCPKeepaliveTransitionTimer = nullptr;
1382
0
        }
1383
0
        nsresult rv = StartLongLivedTCPKeepalives();
1384
0
        LOG(("nsHttpConnection::TakeTransport [%p] calling "
1385
0
             "StartLongLivedTCPKeepalives", this));
1386
0
        if (NS_FAILED(rv)) {
1387
0
            LOG(("nsHttpConnection::TakeTransport [%p] "
1388
0
                 "StartLongLivedTCPKeepalives failed rv[0x%" PRIx32 "]",
1389
0
                 this, static_cast<uint32_t>(rv)));
1390
0
        }
1391
0
    }
1392
0
1393
0
    mSocketTransport->SetSecurityCallbacks(nullptr);
1394
0
    mSocketTransport->SetEventSink(nullptr, nullptr);
1395
0
1396
0
    // The nsHttpConnection will go away soon, so if there is a TLS Filter
1397
0
    // being used (e.g. for wss CONNECT tunnel from a proxy connected to
1398
0
    // via https) that filter needs to take direct control of the
1399
0
    // streams
1400
0
    if (mTLSFilter) {
1401
0
        nsCOMPtr<nsIAsyncInputStream>  ref1(mSocketIn);
1402
0
        nsCOMPtr<nsIAsyncOutputStream> ref2(mSocketOut);
1403
0
        mTLSFilter->newIODriver(ref1, ref2,
1404
0
                                getter_AddRefs(mSocketIn),
1405
0
                                getter_AddRefs(mSocketOut));
1406
0
        mTLSFilter = nullptr;
1407
0
    }
1408
0
1409
0
    mSocketTransport.forget(aTransport);
1410
0
    mSocketIn.forget(aInputStream);
1411
0
    mSocketOut.forget(aOutputStream);
1412
0
1413
0
    return NS_OK;
1414
0
}
1415
1416
uint32_t
1417
nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
1418
0
{
1419
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1420
0
1421
0
    // make sure timer didn't tick before Activate()
1422
0
    if (!mTransaction)
1423
0
        return UINT32_MAX;
1424
0
1425
0
    // Spdy implements some timeout handling using the SPDY ping frame.
1426
0
    if (mSpdySession) {
1427
0
        return mSpdySession->ReadTimeoutTick(now);
1428
0
    }
1429
0
1430
0
    uint32_t nextTickAfter = UINT32_MAX;
1431
0
    // Timeout if the response is taking too long to arrive.
1432
0
    if (mResponseTimeoutEnabled) {
1433
0
        NS_WARNING_ASSERTION(
1434
0
            gHttpHandler->ResponseTimeoutEnabled(),
1435
0
            "Timing out a response, but response timeout is disabled!");
1436
0
1437
0
        PRIntervalTime initialResponseDelta = now - mLastWriteTime;
1438
0
1439
0
        if (initialResponseDelta > mTransaction->ResponseTimeout()) {
1440
0
            LOG(("canceling transaction: no response for %ums: timeout is %dms\n",
1441
0
                 PR_IntervalToMilliseconds(initialResponseDelta),
1442
0
                 PR_IntervalToMilliseconds(mTransaction->ResponseTimeout())));
1443
0
1444
0
            mResponseTimeoutEnabled = false;
1445
0
1446
0
            // This will also close the connection
1447
0
            CloseTransaction(mTransaction, NS_ERROR_NET_TIMEOUT);
1448
0
            return UINT32_MAX;
1449
0
        }
1450
0
        nextTickAfter = PR_IntervalToSeconds(mTransaction->ResponseTimeout()) -
1451
0
                        PR_IntervalToSeconds(initialResponseDelta);
1452
0
        nextTickAfter = std::max(nextTickAfter, 1U);
1453
0
    }
1454
0
1455
0
    // Check for the TCP Fast Open related stalls.
1456
0
    if (mCheckNetworkStallsWithTFO && mLastRequestBytesSentTime) {
1457
0
        PRIntervalTime initialResponseDelta = now - mLastRequestBytesSentTime;
1458
0
        if (initialResponseDelta >= gHttpHandler->FastOpenStallsTimeout()) {
1459
0
            gHttpHandler->IncrementFastOpenStallsCounter();
1460
0
            mCheckNetworkStallsWithTFO = false;
1461
0
        } else {
1462
0
            uint32_t next = PR_IntervalToSeconds(gHttpHandler->FastOpenStallsTimeout()) -
1463
0
                            PR_IntervalToSeconds(initialResponseDelta);
1464
0
            nextTickAfter = std::min(nextTickAfter, next);
1465
0
        }
1466
0
    }
1467
0
1468
0
    if (!mNPNComplete) {
1469
0
      // We can reuse mLastWriteTime here, because it is set when the
1470
0
      // connection is activated and only change when a transaction
1471
0
      // succesfullu write to the socket and this can only happen after
1472
0
      // the TLS handshake is done.
1473
0
      PRIntervalTime initialTLSDelta = now - mLastWriteTime;
1474
0
      if (initialTLSDelta > PR_MillisecondsToInterval(gHttpHandler->TLSHandshakeTimeout())) {
1475
0
        LOG(("canceling transaction: tls handshake takes too long: tls handshake "
1476
0
             "last %ums, timeout is %dms.",
1477
0
             PR_IntervalToMilliseconds(initialTLSDelta),
1478
0
             gHttpHandler->TLSHandshakeTimeout()));
1479
0
1480
0
        // This will also close the connection
1481
0
        CloseTransaction(mTransaction, NS_ERROR_NET_TIMEOUT);
1482
0
        return UINT32_MAX;
1483
0
      }
1484
0
    }
1485
0
1486
0
    return nextTickAfter;
1487
0
}
1488
1489
void
1490
nsHttpConnection::UpdateTCPKeepalive(nsITimer *aTimer, void *aClosure)
1491
0
{
1492
0
    MOZ_ASSERT(aTimer);
1493
0
    MOZ_ASSERT(aClosure);
1494
0
1495
0
    nsHttpConnection *self = static_cast<nsHttpConnection*>(aClosure);
1496
0
1497
0
    if (NS_WARN_IF(self->mUsingSpdyVersion != SpdyVersion::NONE)) {
1498
0
        return;
1499
0
    }
1500
0
1501
0
    // Do not reduce keepalive probe frequency for idle connections.
1502
0
    if (self->mIdleMonitoring) {
1503
0
        return;
1504
0
    }
1505
0
1506
0
    nsresult rv = self->StartLongLivedTCPKeepalives();
1507
0
    if (NS_FAILED(rv)) {
1508
0
        LOG(("nsHttpConnection::UpdateTCPKeepalive [%p] "
1509
0
             "StartLongLivedTCPKeepalives failed rv[0x%" PRIx32 "]",
1510
0
             self, static_cast<uint32_t>(rv)));
1511
0
    }
1512
0
}
1513
1514
void
1515
nsHttpConnection::GetSecurityInfo(nsISupports **secinfo)
1516
0
{
1517
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1518
0
    LOG(("nsHttpConnection::GetSecurityInfo trans=%p tlsfilter=%p socket=%p\n",
1519
0
         mTransaction.get(), mTLSFilter.get(), mSocketTransport.get()));
1520
0
1521
0
    if (mTransaction &&
1522
0
        NS_SUCCEEDED(mTransaction->GetTransactionSecurityInfo(secinfo))) {
1523
0
        return;
1524
0
    }
1525
0
1526
0
    if (mTLSFilter &&
1527
0
        NS_SUCCEEDED(mTLSFilter->GetTransactionSecurityInfo(secinfo))) {
1528
0
        return;
1529
0
    }
1530
0
1531
0
    if (mSocketTransport &&
1532
0
        NS_SUCCEEDED(mSocketTransport->GetSecurityInfo(secinfo))) {
1533
0
        return;
1534
0
    }
1535
0
1536
0
    *secinfo = nullptr;
1537
0
}
1538
1539
void
1540
nsHttpConnection::SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks)
1541
0
{
1542
0
    MutexAutoLock lock(mCallbacksLock);
1543
0
    // This is called both on and off the main thread. For JS-implemented
1544
0
    // callbacks, we requires that the call happen on the main thread, but
1545
0
    // for C++-implemented callbacks we don't care. Use a pointer holder with
1546
0
    // strict checking disabled.
1547
0
    mCallbacks = new nsMainThreadPtrHolder<nsIInterfaceRequestor>(
1548
0
      "nsHttpConnection::mCallbacks", aCallbacks, false);
1549
0
}
1550
1551
nsresult
1552
nsHttpConnection::PushBack(const char *data, uint32_t length)
1553
0
{
1554
0
    LOG(("nsHttpConnection::PushBack [this=%p, length=%d]\n", this, length));
1555
0
1556
0
    if (mInputOverflow) {
1557
0
        NS_ERROR("nsHttpConnection::PushBack only one buffer supported");
1558
0
        return NS_ERROR_UNEXPECTED;
1559
0
    }
1560
0
1561
0
    mInputOverflow = new nsPreloadedStream(mSocketIn, data, length);
1562
0
    return NS_OK;
1563
0
}
1564
1565
class HttpConnectionForceIO : public Runnable
1566
{
1567
public:
1568
  HttpConnectionForceIO(nsHttpConnection* aConn,
1569
                        bool doRecv,
1570
                        bool isFastOpenForce)
1571
    : Runnable("net::HttpConnectionForceIO")
1572
    , mConn(aConn)
1573
    , mDoRecv(doRecv)
1574
    , mIsFastOpenForce(isFastOpenForce)
1575
0
  {
1576
0
  }
1577
1578
  NS_IMETHOD Run() override
1579
0
  {
1580
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1581
0
1582
0
    if (mDoRecv) {
1583
0
      if (!mConn->mSocketIn)
1584
0
        return NS_OK;
1585
0
      return mConn->OnInputStreamReady(mConn->mSocketIn);
1586
0
    }
1587
0
1588
0
    // This runnable will be called when the ForceIO timer expires
1589
0
    // (mIsFastOpenForce==false) or during the TCP Fast Open to force
1590
0
    // writes (mIsFastOpenForce==true).
1591
0
    if (mIsFastOpenForce && !mConn->mWaitingFor0RTTResponse) {
1592
0
      // If we have exit the TCP Fast Open in the meantime we can skip
1593
0
      // this.
1594
0
      return NS_OK;
1595
0
    }
1596
0
    if (!mIsFastOpenForce) {
1597
0
      MOZ_ASSERT(mConn->mForceSendPending);
1598
0
      mConn->mForceSendPending = false;
1599
0
    }
1600
0
1601
0
    if (!mConn->mSocketOut) {
1602
0
      return NS_OK;
1603
0
    }
1604
0
    return mConn->OnOutputStreamReady(mConn->mSocketOut);
1605
0
    }
1606
private:
1607
    RefPtr<nsHttpConnection> mConn;
1608
    bool mDoRecv;
1609
    bool mIsFastOpenForce;
1610
};
1611
1612
nsresult
1613
nsHttpConnection::ResumeSend()
1614
0
{
1615
0
    LOG(("nsHttpConnection::ResumeSend [this=%p]\n", this));
1616
0
1617
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1618
0
1619
0
    if (mSocketOut) {
1620
0
        nsresult rv = mSocketOut->AsyncWait(this, 0, 0, nullptr);
1621
0
        LOG(("nsHttpConnection::ResumeSend [this=%p] "
1622
0
             "mWaitingFor0RTTResponse=%d mForceSendDuringFastOpenPending=%d "
1623
0
             "mReceivedSocketWouldBlockDuringFastOpen=%d\n",
1624
0
             this, mWaitingFor0RTTResponse, mForceSendDuringFastOpenPending,
1625
0
             mReceivedSocketWouldBlockDuringFastOpen));
1626
0
        if (mWaitingFor0RTTResponse && !mForceSendDuringFastOpenPending &&
1627
0
            !mReceivedSocketWouldBlockDuringFastOpen &&
1628
0
            NS_SUCCEEDED(rv)) {
1629
0
            // During TCP Fast Open, poll does not work properly so we will
1630
0
            // trigger writes manually.
1631
0
            mForceSendDuringFastOpenPending = true;
1632
0
            NS_DispatchToCurrentThread(new HttpConnectionForceIO(this, false, true));
1633
0
        }
1634
0
        return rv;
1635
0
    }
1636
0
1637
0
    MOZ_ASSERT_UNREACHABLE("no socket output stream");
1638
0
    return NS_ERROR_UNEXPECTED;
1639
0
}
1640
1641
nsresult
1642
nsHttpConnection::ResumeRecv()
1643
0
{
1644
0
    LOG(("nsHttpConnection::ResumeRecv [this=%p]\n", this));
1645
0
1646
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1647
0
1648
0
    if (mFastOpen) {
1649
0
        LOG(("nsHttpConnection::ResumeRecv - do not waiting for read during "
1650
0
             "fast open! [this=%p]\n", this));
1651
0
        return NS_OK;
1652
0
    }
1653
0
1654
0
    // the mLastReadTime timestamp is used for finding slowish readers
1655
0
    // and can be pretty sensitive. For that reason we actually reset it
1656
0
    // when we ask to read (resume recv()) so that when we get called back
1657
0
    // with actual read data in OnSocketReadable() we are only measuring
1658
0
    // the latency between those two acts and not all the processing that
1659
0
    // may get done before the ResumeRecv() call
1660
0
    mLastReadTime = PR_IntervalNow();
1661
0
1662
0
    if (mSocketIn)
1663
0
        return mSocketIn->AsyncWait(this, 0, 0, nullptr);
1664
0
1665
0
    MOZ_ASSERT_UNREACHABLE("no socket input stream");
1666
0
    return NS_ERROR_UNEXPECTED;
1667
0
}
1668
1669
void
1670
nsHttpConnection::ForceSendIO(nsITimer *aTimer, void *aClosure)
1671
0
{
1672
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1673
0
    nsHttpConnection *self = static_cast<nsHttpConnection *>(aClosure);
1674
0
    MOZ_ASSERT(aTimer == self->mForceSendTimer);
1675
0
    self->mForceSendTimer = nullptr;
1676
0
    NS_DispatchToCurrentThread(new HttpConnectionForceIO(self, false, false));
1677
0
}
1678
1679
nsresult
1680
nsHttpConnection::MaybeForceSendIO()
1681
0
{
1682
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1683
0
    // due to bug 1213084 sometimes real I/O events do not get serviced when
1684
0
    // NSPR derived I/O events are ready and this can cause a deadlock with
1685
0
    // https over https proxying. Normally we would expect the write callback to
1686
0
    // be invoked before this timer goes off, but set it at the old windows
1687
0
    // tick interval (kForceDelay) as a backup for those circumstances.
1688
0
    static const uint32_t kForceDelay = 17; //ms
1689
0
1690
0
    if (mForceSendPending) {
1691
0
        return NS_OK;
1692
0
    }
1693
0
    MOZ_ASSERT(!mForceSendTimer);
1694
0
    mForceSendPending = true;
1695
0
    return NS_NewTimerWithFuncCallback(
1696
0
        getter_AddRefs(mForceSendTimer),
1697
0
        nsHttpConnection::ForceSendIO,
1698
0
        this,
1699
0
        kForceDelay,
1700
0
        nsITimer::TYPE_ONE_SHOT,
1701
0
        "net::nsHttpConnection::MaybeForceSendIO");
1702
0
}
1703
1704
// trigger an asynchronous read
1705
nsresult
1706
nsHttpConnection::ForceRecv()
1707
0
{
1708
0
    LOG(("nsHttpConnection::ForceRecv [this=%p]\n", this));
1709
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1710
0
1711
0
    return NS_DispatchToCurrentThread(new HttpConnectionForceIO(this, true, false));
1712
0
}
1713
1714
// trigger an asynchronous write
1715
nsresult
1716
nsHttpConnection::ForceSend()
1717
0
{
1718
0
    LOG(("nsHttpConnection::ForceSend [this=%p]\n", this));
1719
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1720
0
1721
0
    if (mTLSFilter) {
1722
0
        return mTLSFilter->NudgeTunnel(this);
1723
0
    }
1724
0
    return MaybeForceSendIO();
1725
0
}
1726
1727
void
1728
nsHttpConnection::BeginIdleMonitoring()
1729
0
{
1730
0
    LOG(("nsHttpConnection::BeginIdleMonitoring [this=%p]\n", this));
1731
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1732
0
    MOZ_ASSERT(!mTransaction, "BeginIdleMonitoring() while active");
1733
0
    MOZ_ASSERT(mUsingSpdyVersion == SpdyVersion::NONE, "Idle monitoring of spdy not allowed");
1734
0
1735
0
    LOG(("Entering Idle Monitoring Mode [this=%p]", this));
1736
0
    mIdleMonitoring = true;
1737
0
    if (mSocketIn)
1738
0
        mSocketIn->AsyncWait(this, 0, 0, nullptr);
1739
0
}
1740
1741
void
1742
nsHttpConnection::EndIdleMonitoring()
1743
0
{
1744
0
    LOG(("nsHttpConnection::EndIdleMonitoring [this=%p]\n", this));
1745
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1746
0
    MOZ_ASSERT(!mTransaction, "EndIdleMonitoring() while active");
1747
0
1748
0
    if (mIdleMonitoring) {
1749
0
        LOG(("Leaving Idle Monitoring Mode [this=%p]", this));
1750
0
        mIdleMonitoring = false;
1751
0
        if (mSocketIn)
1752
0
            mSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
1753
0
    }
1754
0
}
1755
1756
HttpVersion
1757
nsHttpConnection::Version()
1758
0
{
1759
0
    if (mUsingSpdyVersion != SpdyVersion::NONE) {
1760
0
        return nsHttp::GetHttpVersionFromSpdy(mUsingSpdyVersion);
1761
0
    }
1762
0
    return mLastHttpResponseVersion;
1763
0
}
1764
1765
//-----------------------------------------------------------------------------
1766
// nsHttpConnection <private>
1767
//-----------------------------------------------------------------------------
1768
1769
void
1770
nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason,
1771
                                   bool aIsShutdown)
1772
0
{
1773
0
    LOG(("nsHttpConnection::CloseTransaction[this=%p trans=%p reason=%" PRIx32 "]\n",
1774
0
         this, trans, static_cast<uint32_t>(reason)));
1775
0
1776
0
    MOZ_ASSERT((trans == mTransaction) ||
1777
0
               (mTLSFilter && mTLSFilter->Transaction() == trans));
1778
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
1779
0
1780
0
    if (mCurrentBytesRead > mMaxBytesRead)
1781
0
        mMaxBytesRead = mCurrentBytesRead;
1782
0
1783
0
    // mask this error code because its not a real error.
1784
0
    if (reason == NS_BASE_STREAM_CLOSED)
1785
0
        reason = NS_OK;
1786
0
1787
0
    if (mUsingSpdyVersion != SpdyVersion::NONE) {
1788
0
        DontReuse();
1789
0
        // if !mSpdySession then mUsingSpdyVersion must be false for canreuse()
1790
0
        mUsingSpdyVersion = SpdyVersion::NONE;
1791
0
        mSpdySession = nullptr;
1792
0
    }
1793
0
1794
0
    if (mTransaction) {
1795
0
        mHttp1xTransactionCount += mTransaction->Http1xTransactionCount();
1796
0
1797
0
        mTransaction->Close(reason);
1798
0
        mTransaction = nullptr;
1799
0
    }
1800
0
1801
0
    {
1802
0
        MutexAutoLock lock(mCallbacksLock);
1803
0
        mCallbacks = nullptr;
1804
0
    }
1805
0
1806
0
    if (NS_FAILED(reason) && (reason != NS_BINDING_RETARGETED)) {
1807
0
        Close(reason, aIsShutdown);
1808
0
    }
1809
0
1810
0
    // flag the connection as reused here for convenience sake.  certainly
1811
0
    // it might be going away instead ;-)
1812
0
    mIsReused = true;
1813
0
}
1814
1815
nsresult
1816
nsHttpConnection::ReadFromStream(nsIInputStream *input,
1817
                                 void *closure,
1818
                                 const char *buf,
1819
                                 uint32_t offset,
1820
                                 uint32_t count,
1821
                                 uint32_t *countRead)
1822
0
{
1823
0
    // thunk for nsIInputStream instance
1824
0
    nsHttpConnection *conn = (nsHttpConnection *) closure;
1825
0
    return conn->OnReadSegment(buf, count, countRead);
1826
0
}
1827
1828
nsresult
1829
nsHttpConnection::OnReadSegment(const char *buf,
1830
                                uint32_t count,
1831
                                uint32_t *countRead)
1832
0
{
1833
0
    if (count == 0) {
1834
0
        // some ReadSegments implementations will erroneously call the writer
1835
0
        // to consume 0 bytes worth of data.  we must protect against this case
1836
0
        // or else we'd end up closing the socket prematurely.
1837
0
        NS_ERROR("bad ReadSegments implementation");
1838
0
        return NS_ERROR_FAILURE; // stop iterating
1839
0
    }
1840
0
1841
0
    nsresult rv = mSocketOut->Write(buf, count, countRead);
1842
0
    if (NS_FAILED(rv))
1843
0
        mSocketOutCondition = rv;
1844
0
    else if (*countRead == 0)
1845
0
        mSocketOutCondition = NS_BASE_STREAM_CLOSED;
1846
0
    else {
1847
0
        mLastWriteTime = PR_IntervalNow();
1848
0
        mSocketOutCondition = NS_OK; // reset condition
1849
0
        if (!mProxyConnectInProgress)
1850
0
            mTotalBytesWritten += *countRead;
1851
0
    }
1852
0
1853
0
    return mSocketOutCondition;
1854
0
}
1855
1856
nsresult
1857
nsHttpConnection::OnSocketWritable()
1858
0
{
1859
0
    LOG(("nsHttpConnection::OnSocketWritable [this=%p] host=%s\n",
1860
0
         this, mConnInfo->Origin()));
1861
0
1862
0
    nsresult rv;
1863
0
    uint32_t transactionBytes;
1864
0
    bool again = true;
1865
0
1866
0
    // Prevent STS thread from being blocked by single OnOutputStreamReady callback.
1867
0
    const uint32_t maxWriteAttempts = 128;
1868
0
    uint32_t writeAttempts = 0;
1869
0
1870
0
    mForceSendDuringFastOpenPending = false;
1871
0
1872
0
    do {
1873
0
        ++writeAttempts;
1874
0
        rv = mSocketOutCondition = NS_OK;
1875
0
        transactionBytes = 0;
1876
0
1877
0
        // The SSL handshake must be completed before the transaction->readsegments()
1878
0
        // processing can proceed because we need to know how to format the
1879
0
        // request differently for http/1, http/2, spdy, etc.. and that is
1880
0
        // negotiated with NPN/ALPN in the SSL handshake.
1881
0
1882
0
        if (mConnInfo->UsingHttpsProxy() &&
1883
0
            !EnsureNPNComplete(rv, transactionBytes)) {
1884
0
            MOZ_ASSERT(!transactionBytes);
1885
0
            mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
1886
0
        } else if (mProxyConnectStream) {
1887
0
            // If we're need an HTTP/1 CONNECT tunnel through a proxy
1888
0
            // send it before doing the SSL handshake
1889
0
            LOG(("  writing CONNECT request stream\n"));
1890
0
            rv = mProxyConnectStream->ReadSegments(ReadFromStream, this,
1891
0
                                                   nsIOService::gDefaultSegmentSize,
1892
0
                                                   &transactionBytes);
1893
0
        } else if (!EnsureNPNComplete(rv, transactionBytes)) {
1894
0
            if (NS_SUCCEEDED(rv) && !transactionBytes &&
1895
0
                NS_SUCCEEDED(mSocketOutCondition)) {
1896
0
                mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
1897
0
            }
1898
0
        } else if (!mTransaction) {
1899
0
            rv = NS_ERROR_FAILURE;
1900
0
            LOG(("  No Transaction In OnSocketWritable\n"));
1901
0
        } else if (NS_SUCCEEDED(rv)) {
1902
0
1903
0
            // for non spdy sessions let the connection manager know
1904
0
            if (!mReportedSpdy) {
1905
0
                mReportedSpdy = true;
1906
0
                MOZ_ASSERT(!mEverUsedSpdy);
1907
0
                gHttpHandler->ConnMgr()->ReportSpdyConnection(this, false);
1908
0
            }
1909
0
1910
0
            LOG(("  writing transaction request stream\n"));
1911
0
            mProxyConnectInProgress = false;
1912
0
            rv = mTransaction->ReadSegmentsAgain(this, nsIOService::gDefaultSegmentSize,
1913
0
                                                 &transactionBytes, &again);
1914
0
            mContentBytesWritten += transactionBytes;
1915
0
        }
1916
0
1917
0
        LOG(("nsHttpConnection::OnSocketWritable %p "
1918
0
             "ReadSegments returned [rv=%" PRIx32 " read=%u "
1919
0
             "sock-cond=%" PRIx32 " again=%d]\n",
1920
0
             this, static_cast<uint32_t>(rv), transactionBytes,
1921
0
             static_cast<uint32_t>(mSocketOutCondition), again));
1922
0
1923
0
        // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
1924
0
        if (rv == NS_BASE_STREAM_CLOSED && !mTransaction->IsDone()) {
1925
0
            rv = NS_OK;
1926
0
            transactionBytes = 0;
1927
0
        }
1928
0
1929
0
        if (!again && (mFastOpen || mWaitingFor0RTTResponse)) {
1930
0
            // Continue waiting;
1931
0
            rv = mSocketOut->AsyncWait(this, 0, 0, nullptr);
1932
0
        }
1933
0
        if (NS_FAILED(rv)) {
1934
0
            // if the transaction didn't want to write any more data, then
1935
0
            // wait for the transaction to call ResumeSend.
1936
0
            if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
1937
0
                rv = NS_OK;
1938
0
                if (mFastOpen || mWaitingFor0RTTResponse) {
1939
0
                    // Continue waiting;
1940
0
                    rv = mSocketOut->AsyncWait(this, 0, 0, nullptr);
1941
0
                }
1942
0
            }
1943
0
            again = false;
1944
0
        } else if (NS_FAILED(mSocketOutCondition)) {
1945
0
            if (mSocketOutCondition == NS_BASE_STREAM_WOULD_BLOCK) {
1946
0
                if (mTLSFilter) {
1947
0
                    LOG(("  blocked tunnel (handshake?)\n"));
1948
0
                    rv = mTLSFilter->NudgeTunnel(this);
1949
0
                } else {
1950
0
                    rv = mSocketOut->AsyncWait(this, 0, 0, nullptr); // continue writing
1951
0
                }
1952
0
            } else {
1953
0
                rv = mSocketOutCondition;
1954
0
            }
1955
0
            again = false;
1956
0
        } else if (!transactionBytes) {
1957
0
            rv = NS_OK;
1958
0
1959
0
            if (mWaitingFor0RTTResponse || mFastOpen) {
1960
0
                // Wait for tls handshake to finish or waiting for connect.
1961
0
                rv = mSocketOut->AsyncWait(this, 0, 0, nullptr);
1962
0
            } else if (mTransaction) { // in case the ReadSegments stack called CloseTransaction()
1963
0
                //
1964
0
                // at this point we've written out the entire transaction, and now we
1965
0
                // must wait for the server's response.  we manufacture a status message
1966
0
                // here to reflect the fact that we are waiting.  this message will be
1967
0
                // trumped (overwritten) if the server responds quickly.
1968
0
                //
1969
0
                mTransaction->OnTransportStatus(mSocketTransport,
1970
0
                                                NS_NET_STATUS_WAITING_FOR,
1971
0
                                                0);
1972
0
                if (mCheckNetworkStallsWithTFO) {
1973
0
                    mLastRequestBytesSentTime = PR_IntervalNow();
1974
0
                }
1975
0
1976
0
                rv = ResumeRecv(); // start reading
1977
0
            }
1978
0
            again = false;
1979
0
        } else if (writeAttempts >= maxWriteAttempts) {
1980
0
            LOG(("  yield for other transactions\n"));
1981
0
            rv = mSocketOut->AsyncWait(this, 0, 0, nullptr); // continue writing
1982
0
            again = false;
1983
0
        }
1984
0
        // write more to the socket until error or end-of-request...
1985
0
    } while (again && gHttpHandler->Active());
1986
0
1987
0
    return rv;
1988
0
}
1989
1990
nsresult
1991
nsHttpConnection::OnWriteSegment(char *buf,
1992
                                 uint32_t count,
1993
                                 uint32_t *countWritten)
1994
0
{
1995
0
    if (count == 0) {
1996
0
        // some WriteSegments implementations will erroneously call the reader
1997
0
        // to provide 0 bytes worth of data.  we must protect against this case
1998
0
        // or else we'd end up closing the socket prematurely.
1999
0
        NS_ERROR("bad WriteSegments implementation");
2000
0
        return NS_ERROR_FAILURE; // stop iterating
2001
0
    }
2002
0
2003
0
    if (ChaosMode::isActive(ChaosFeature::IOAmounts) &&
2004
0
        ChaosMode::randomUint32LessThan(2)) {
2005
0
        // read 1...count bytes
2006
0
        count = ChaosMode::randomUint32LessThan(count) + 1;
2007
0
    }
2008
0
2009
0
    nsresult rv = mSocketIn->Read(buf, count, countWritten);
2010
0
    if (NS_FAILED(rv))
2011
0
        mSocketInCondition = rv;
2012
0
    else if (*countWritten == 0)
2013
0
        mSocketInCondition = NS_BASE_STREAM_CLOSED;
2014
0
    else
2015
0
        mSocketInCondition = NS_OK; // reset condition
2016
0
2017
0
    mCheckNetworkStallsWithTFO = false;
2018
0
2019
0
    return mSocketInCondition;
2020
0
}
2021
2022
nsresult
2023
nsHttpConnection::OnSocketReadable()
2024
0
{
2025
0
    LOG(("nsHttpConnection::OnSocketReadable [this=%p]\n", this));
2026
0
2027
0
    PRIntervalTime now = PR_IntervalNow();
2028
0
    PRIntervalTime delta = now - mLastReadTime;
2029
0
2030
0
    // Reset mResponseTimeoutEnabled to stop response timeout checks.
2031
0
    mResponseTimeoutEnabled = false;
2032
0
2033
0
    if (mKeepAliveMask && (delta >= mMaxHangTime)) {
2034
0
        LOG(("max hang time exceeded!\n"));
2035
0
        // give the handler a chance to create a new persistent connection to
2036
0
        // this host if we've been busy for too long.
2037
0
        mKeepAliveMask = false;
2038
0
        Unused << gHttpHandler->ProcessPendingQ(mConnInfo);
2039
0
    }
2040
0
2041
0
    // Reduce the estimate of the time since last read by up to 1 RTT to
2042
0
    // accommodate exhausted sender TCP congestion windows or minor I/O delays.
2043
0
    mLastReadTime = now;
2044
0
2045
0
    nsresult rv;
2046
0
    uint32_t n;
2047
0
    bool again = true;
2048
0
2049
0
    do {
2050
0
        if (!mProxyConnectInProgress && !mNPNComplete) {
2051
0
            // Unless we are setting up a tunnel via CONNECT, prevent reading
2052
0
            // from the socket until the results of NPN
2053
0
            // negotiation are known (which is determined from the write path).
2054
0
            // If the server speaks SPDY it is likely the readable data here is
2055
0
            // a spdy settings frame and without NPN it would be misinterpreted
2056
0
            // as HTTP/*
2057
0
2058
0
            LOG(("nsHttpConnection::OnSocketReadable %p return due to inactive "
2059
0
                 "tunnel setup but incomplete NPN state\n", this));
2060
0
            rv = NS_OK;
2061
0
            break;
2062
0
        }
2063
0
2064
0
        mSocketInCondition = NS_OK;
2065
0
        rv = mTransaction->
2066
0
            WriteSegmentsAgain(this, nsIOService::gDefaultSegmentSize, &n, &again);
2067
0
        LOG(("nsHttpConnection::OnSocketReadable %p trans->ws rv=%" PRIx32
2068
0
             " n=%d socketin=%" PRIx32 "\n",
2069
0
             this, static_cast<uint32_t>(rv), n, static_cast<uint32_t>(mSocketInCondition)));
2070
0
        if (NS_FAILED(rv)) {
2071
0
            // if the transaction didn't want to take any more data, then
2072
0
            // wait for the transaction to call ResumeRecv.
2073
0
            if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
2074
0
                rv = NS_OK;
2075
0
            }
2076
0
            again = false;
2077
0
        } else {
2078
0
            mCurrentBytesRead += n;
2079
0
            mTotalBytesRead += n;
2080
0
            if (NS_FAILED(mSocketInCondition)) {
2081
0
                // continue waiting for the socket if necessary...
2082
0
                if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK) {
2083
0
                    rv = ResumeRecv();
2084
0
                } else {
2085
0
                    rv = mSocketInCondition;
2086
0
                }
2087
0
                again = false;
2088
0
            }
2089
0
        }
2090
0
        // read more from the socket until error...
2091
0
    } while (again && gHttpHandler->Active());
2092
0
2093
0
    return rv;
2094
0
}
2095
2096
void
2097
nsHttpConnection::SetupSecondaryTLS()
2098
0
{
2099
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2100
0
    MOZ_ASSERT(!mTLSFilter);
2101
0
    LOG(("nsHttpConnection %p SetupSecondaryTLS %s %d\n",
2102
0
         this, mConnInfo->Origin(), mConnInfo->OriginPort()));
2103
0
2104
0
    nsHttpConnectionInfo *ci = nullptr;
2105
0
    if (mTransaction) {
2106
0
        ci = mTransaction->ConnectionInfo();
2107
0
    }
2108
0
    if (!ci) {
2109
0
        ci = mConnInfo;
2110
0
    }
2111
0
    MOZ_ASSERT(ci);
2112
0
2113
0
    mTLSFilter = new TLSFilterTransaction(mTransaction,
2114
0
                                          ci->Origin(), ci->OriginPort(), this, this);
2115
0
2116
0
    if (mTransaction) {
2117
0
        mTransaction = mTLSFilter;
2118
0
    }
2119
0
}
2120
2121
void
2122
nsHttpConnection::SetInSpdyTunnel(bool arg)
2123
0
{
2124
0
    MOZ_ASSERT(mTLSFilter);
2125
0
    mInSpdyTunnel = arg;
2126
0
2127
0
    // don't setup another tunnel :)
2128
0
    mProxyConnectStream = nullptr;
2129
0
    mCompletedProxyConnect = true;
2130
0
    mProxyConnectInProgress = false;
2131
0
}
2132
2133
nsresult
2134
nsHttpConnection::MakeConnectString(nsAHttpTransaction *trans,
2135
                                    nsHttpRequestHead *request,
2136
                                    nsACString &result)
2137
0
{
2138
0
    result.Truncate();
2139
0
    if (!trans->ConnectionInfo()) {
2140
0
        return NS_ERROR_NOT_INITIALIZED;
2141
0
    }
2142
0
2143
0
    DebugOnly<nsresult> rv;
2144
0
2145
0
    rv = nsHttpHandler::GenerateHostPort(
2146
0
            nsDependentCString(trans->ConnectionInfo()->Origin()),
2147
0
                               trans->ConnectionInfo()->OriginPort(), result);
2148
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
2149
0
2150
0
    // CONNECT host:port HTTP/1.1
2151
0
    request->SetMethod(NS_LITERAL_CSTRING("CONNECT"));
2152
0
    request->SetVersion(gHttpHandler->HttpVersion());
2153
0
    request->SetRequestURI(result);
2154
0
    rv = request->SetHeader(nsHttp::User_Agent, gHttpHandler->UserAgent());
2155
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
2156
0
2157
0
    // a CONNECT is always persistent
2158
0
    rv = request->SetHeader(nsHttp::Proxy_Connection, NS_LITERAL_CSTRING("keep-alive"));
2159
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
2160
0
    rv = request->SetHeader(nsHttp::Connection, NS_LITERAL_CSTRING("keep-alive"));
2161
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
2162
0
2163
0
    // all HTTP/1.1 requests must include a Host header (even though it
2164
0
    // may seem redundant in this case; see bug 82388).
2165
0
    rv = request->SetHeader(nsHttp::Host, result);
2166
0
    MOZ_ASSERT(NS_SUCCEEDED(rv));
2167
0
2168
0
    nsAutoCString val;
2169
0
    if (NS_SUCCEEDED(trans->RequestHead()->GetHeader(
2170
0
                         nsHttp::Proxy_Authorization,
2171
0
                         val))) {
2172
0
        // we don't know for sure if this authorization is intended for the
2173
0
        // SSL proxy, so we add it just in case.
2174
0
        rv = request->SetHeader(nsHttp::Proxy_Authorization, val);
2175
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
2176
0
    }
2177
0
2178
0
    result.Truncate();
2179
0
    request->Flatten(result, false);
2180
0
    result.AppendLiteral("\r\n");
2181
0
    return NS_OK;
2182
0
}
2183
2184
nsresult
2185
nsHttpConnection::SetupProxyConnect()
2186
0
{
2187
0
    LOG(("nsHttpConnection::SetupProxyConnect [this=%p]\n", this));
2188
0
    NS_ENSURE_TRUE(!mProxyConnectStream, NS_ERROR_ALREADY_INITIALIZED);
2189
0
    MOZ_ASSERT(mUsingSpdyVersion == SpdyVersion::NONE,
2190
0
               "SPDY NPN Complete while using proxy connect stream");
2191
0
2192
0
    nsAutoCString buf;
2193
0
    nsHttpRequestHead request;
2194
0
    nsresult rv = MakeConnectString(mTransaction, &request, buf);
2195
0
    if (NS_FAILED(rv)) {
2196
0
        return rv;
2197
0
    }
2198
0
    return NS_NewCStringInputStream(getter_AddRefs(mProxyConnectStream), std::move(buf));
2199
0
}
2200
2201
nsresult
2202
nsHttpConnection::StartShortLivedTCPKeepalives()
2203
0
{
2204
0
    if (mUsingSpdyVersion != SpdyVersion::NONE) {
2205
0
        return NS_OK;
2206
0
    }
2207
0
    MOZ_ASSERT(mSocketTransport);
2208
0
    if (!mSocketTransport) {
2209
0
        return NS_ERROR_NOT_INITIALIZED;
2210
0
    }
2211
0
2212
0
    nsresult rv = NS_OK;
2213
0
    int32_t idleTimeS = -1;
2214
0
    int32_t retryIntervalS = -1;
2215
0
    if (gHttpHandler->TCPKeepaliveEnabledForShortLivedConns()) {
2216
0
        // Set the idle time.
2217
0
        idleTimeS = gHttpHandler->GetTCPKeepaliveShortLivedIdleTime();
2218
0
        LOG(("nsHttpConnection::StartShortLivedTCPKeepalives[%p] "
2219
0
             "idle time[%ds].", this, idleTimeS));
2220
0
2221
0
        retryIntervalS =
2222
0
            std::max<int32_t>((int32_t)PR_IntervalToSeconds(mRtt), 1);
2223
0
        rv = mSocketTransport->SetKeepaliveVals(idleTimeS, retryIntervalS);
2224
0
        if (NS_FAILED(rv)) {
2225
0
            return rv;
2226
0
        }
2227
0
        rv = mSocketTransport->SetKeepaliveEnabled(true);
2228
0
        mTCPKeepaliveConfig = kTCPKeepaliveShortLivedConfig;
2229
0
    } else {
2230
0
        rv = mSocketTransport->SetKeepaliveEnabled(false);
2231
0
        mTCPKeepaliveConfig = kTCPKeepaliveDisabled;
2232
0
    }
2233
0
    if (NS_FAILED(rv)) {
2234
0
        return rv;
2235
0
    }
2236
0
2237
0
    // Start a timer to move to long-lived keepalive config.
2238
0
    if(!mTCPKeepaliveTransitionTimer) {
2239
0
        mTCPKeepaliveTransitionTimer =
2240
0
            NS_NewTimer();
2241
0
    }
2242
0
2243
0
    if (mTCPKeepaliveTransitionTimer) {
2244
0
        int32_t time = gHttpHandler->GetTCPKeepaliveShortLivedTime();
2245
0
2246
0
        // Adjust |time| to ensure a full set of keepalive probes can be sent
2247
0
        // at the end of the short-lived phase.
2248
0
        if (gHttpHandler->TCPKeepaliveEnabledForShortLivedConns()) {
2249
0
            if (NS_WARN_IF(!gSocketTransportService)) {
2250
0
                return NS_ERROR_NOT_INITIALIZED;
2251
0
            }
2252
0
            int32_t probeCount = -1;
2253
0
            rv = gSocketTransportService->GetKeepaliveProbeCount(&probeCount);
2254
0
            if (NS_WARN_IF(NS_FAILED(rv))) {
2255
0
                return rv;
2256
0
            }
2257
0
            if (NS_WARN_IF(probeCount <= 0)) {
2258
0
                return NS_ERROR_UNEXPECTED;
2259
0
            }
2260
0
            // Add time for final keepalive probes, and 2 seconds for a buffer.
2261
0
            time += ((probeCount) * retryIntervalS) - (time % idleTimeS) + 2;
2262
0
        }
2263
0
        mTCPKeepaliveTransitionTimer->InitWithNamedFuncCallback(
2264
0
          nsHttpConnection::UpdateTCPKeepalive,
2265
0
          this,
2266
0
          (uint32_t)time * 1000,
2267
0
          nsITimer::TYPE_ONE_SHOT,
2268
0
          "net::nsHttpConnection::StartShortLivedTCPKeepalives");
2269
0
    } else {
2270
0
        NS_WARNING("nsHttpConnection::StartShortLivedTCPKeepalives failed to "
2271
0
                   "create timer.");
2272
0
    }
2273
0
2274
0
    return NS_OK;
2275
0
}
2276
2277
nsresult
2278
nsHttpConnection::StartLongLivedTCPKeepalives()
2279
0
{
2280
0
    MOZ_ASSERT(mUsingSpdyVersion == SpdyVersion::NONE, "Don't use TCP Keepalive with SPDY!");
2281
0
    if (NS_WARN_IF(mUsingSpdyVersion != SpdyVersion::NONE)) {
2282
0
        return NS_OK;
2283
0
    }
2284
0
    MOZ_ASSERT(mSocketTransport);
2285
0
    if (!mSocketTransport) {
2286
0
        return NS_ERROR_NOT_INITIALIZED;
2287
0
    }
2288
0
2289
0
    nsresult rv = NS_OK;
2290
0
    if (gHttpHandler->TCPKeepaliveEnabledForLongLivedConns()) {
2291
0
        // Increase the idle time.
2292
0
        int32_t idleTimeS = gHttpHandler->GetTCPKeepaliveLongLivedIdleTime();
2293
0
        LOG(("nsHttpConnection::StartLongLivedTCPKeepalives[%p] idle time[%ds]",
2294
0
             this, idleTimeS));
2295
0
2296
0
        int32_t retryIntervalS =
2297
0
            std::max<int32_t>((int32_t)PR_IntervalToSeconds(mRtt), 1);
2298
0
        rv = mSocketTransport->SetKeepaliveVals(idleTimeS, retryIntervalS);
2299
0
        if (NS_FAILED(rv)) {
2300
0
            return rv;
2301
0
        }
2302
0
2303
0
        // Ensure keepalive is enabled, if current status is disabled.
2304
0
        if (mTCPKeepaliveConfig == kTCPKeepaliveDisabled) {
2305
0
            rv = mSocketTransport->SetKeepaliveEnabled(true);
2306
0
            if (NS_FAILED(rv)) {
2307
0
                return rv;
2308
0
            }
2309
0
        }
2310
0
        mTCPKeepaliveConfig = kTCPKeepaliveLongLivedConfig;
2311
0
    } else {
2312
0
        rv = mSocketTransport->SetKeepaliveEnabled(false);
2313
0
        mTCPKeepaliveConfig = kTCPKeepaliveDisabled;
2314
0
    }
2315
0
2316
0
    if (NS_FAILED(rv)) {
2317
0
        return rv;
2318
0
    }
2319
0
    return NS_OK;
2320
0
}
2321
2322
nsresult
2323
nsHttpConnection::DisableTCPKeepalives()
2324
0
{
2325
0
    MOZ_ASSERT(mSocketTransport);
2326
0
    if (!mSocketTransport) {
2327
0
        return NS_ERROR_NOT_INITIALIZED;
2328
0
    }
2329
0
2330
0
    LOG(("nsHttpConnection::DisableTCPKeepalives [%p]", this));
2331
0
    if (mTCPKeepaliveConfig != kTCPKeepaliveDisabled) {
2332
0
        nsresult rv = mSocketTransport->SetKeepaliveEnabled(false);
2333
0
        if (NS_FAILED(rv)) {
2334
0
            return rv;
2335
0
        }
2336
0
        mTCPKeepaliveConfig = kTCPKeepaliveDisabled;
2337
0
    }
2338
0
    if (mTCPKeepaliveTransitionTimer) {
2339
0
        mTCPKeepaliveTransitionTimer->Cancel();
2340
0
        mTCPKeepaliveTransitionTimer = nullptr;
2341
0
    }
2342
0
    return NS_OK;
2343
0
}
2344
2345
//-----------------------------------------------------------------------------
2346
// nsHttpConnection::nsISupports
2347
//-----------------------------------------------------------------------------
2348
2349
NS_IMPL_ADDREF(nsHttpConnection)
2350
NS_IMPL_RELEASE(nsHttpConnection)
2351
2352
0
NS_INTERFACE_MAP_BEGIN(nsHttpConnection)
2353
0
    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
2354
0
    NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
2355
0
    NS_INTERFACE_MAP_ENTRY(nsIOutputStreamCallback)
2356
0
    NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
2357
0
    NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
2358
0
    NS_INTERFACE_MAP_ENTRY_CONCRETE(nsHttpConnection)
2359
0
NS_INTERFACE_MAP_END
2360
2361
//-----------------------------------------------------------------------------
2362
// nsHttpConnection::nsIInputStreamCallback
2363
//-----------------------------------------------------------------------------
2364
2365
// called on the socket transport thread
2366
NS_IMETHODIMP
2367
nsHttpConnection::OnInputStreamReady(nsIAsyncInputStream *in)
2368
0
{
2369
0
    MOZ_ASSERT(in == mSocketIn, "unexpected stream");
2370
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2371
0
2372
0
    if (mIdleMonitoring) {
2373
0
        MOZ_ASSERT(!mTransaction, "Idle Input Event While Active");
2374
0
2375
0
        // The only read event that is protocol compliant for an idle connection
2376
0
        // is an EOF, which we check for with CanReuse(). If the data is
2377
0
        // something else then just ignore it and suspend checking for EOF -
2378
0
        // our normal timers or protocol stack are the place to deal with
2379
0
        // any exception logic.
2380
0
2381
0
        if (!CanReuse()) {
2382
0
            LOG(("Server initiated close of idle conn %p\n", this));
2383
0
            Unused << gHttpHandler->ConnMgr()->CloseIdleConnection(this);
2384
0
            return NS_OK;
2385
0
        }
2386
0
2387
0
        LOG(("Input data on idle conn %p, but not closing yet\n", this));
2388
0
        return NS_OK;
2389
0
    }
2390
0
2391
0
    // if the transaction was dropped...
2392
0
    if (!mTransaction) {
2393
0
        LOG(("  no transaction; ignoring event\n"));
2394
0
        return NS_OK;
2395
0
    }
2396
0
2397
0
    nsresult rv = OnSocketReadable();
2398
0
    if (NS_FAILED(rv))
2399
0
        CloseTransaction(mTransaction, rv);
2400
0
2401
0
    return NS_OK;
2402
0
}
2403
2404
//-----------------------------------------------------------------------------
2405
// nsHttpConnection::nsIOutputStreamCallback
2406
//-----------------------------------------------------------------------------
2407
2408
NS_IMETHODIMP
2409
nsHttpConnection::OnOutputStreamReady(nsIAsyncOutputStream *out)
2410
0
{
2411
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2412
0
    MOZ_ASSERT(out == mSocketOut, "unexpected socket");
2413
0
    // if the transaction was dropped...
2414
0
    if (!mTransaction) {
2415
0
        LOG(("  no transaction; ignoring event\n"));
2416
0
        return NS_OK;
2417
0
    }
2418
0
2419
0
    nsresult rv = OnSocketWritable();
2420
0
    if (NS_FAILED(rv))
2421
0
        CloseTransaction(mTransaction, rv);
2422
0
2423
0
    return NS_OK;
2424
0
}
2425
2426
//-----------------------------------------------------------------------------
2427
// nsHttpConnection::nsITransportEventSink
2428
//-----------------------------------------------------------------------------
2429
2430
NS_IMETHODIMP
2431
nsHttpConnection::OnTransportStatus(nsITransport *trans,
2432
                                    nsresult status,
2433
                                    int64_t progress,
2434
                                    int64_t progressMax)
2435
0
{
2436
0
    if (mTransaction)
2437
0
        mTransaction->OnTransportStatus(trans, status, progress);
2438
0
    return NS_OK;
2439
0
}
2440
2441
//-----------------------------------------------------------------------------
2442
// nsHttpConnection::nsIInterfaceRequestor
2443
//-----------------------------------------------------------------------------
2444
2445
// not called on the socket transport thread
2446
NS_IMETHODIMP
2447
nsHttpConnection::GetInterface(const nsIID &iid, void **result)
2448
0
{
2449
0
    // NOTE: This function is only called on the UI thread via sync proxy from
2450
0
    //       the socket transport thread.  If that weren't the case, then we'd
2451
0
    //       have to worry about the possibility of mTransaction going away
2452
0
    //       part-way through this function call.  See CloseTransaction.
2453
0
2454
0
    // NOTE - there is a bug here, the call to getinterface is proxied off the
2455
0
    // nss thread, not the ui thread as the above comment says. So there is
2456
0
    // indeed a chance of mTransaction going away. bug 615342
2457
0
2458
0
    MOZ_ASSERT(!OnSocketThread(), "on socket thread");
2459
0
2460
0
    nsCOMPtr<nsIInterfaceRequestor> callbacks;
2461
0
    {
2462
0
        MutexAutoLock lock(mCallbacksLock);
2463
0
        callbacks = mCallbacks;
2464
0
    }
2465
0
    if (callbacks)
2466
0
        return callbacks->GetInterface(iid, result);
2467
0
    return NS_ERROR_NO_INTERFACE;
2468
0
}
2469
2470
void
2471
nsHttpConnection::CheckForTraffic(bool check)
2472
0
{
2473
0
    if (check) {
2474
0
        LOG((" CheckForTraffic conn %p\n", this));
2475
0
        if (mSpdySession) {
2476
0
            if (PR_IntervalToMilliseconds(IdleTime()) >= 500) {
2477
0
                // Send a ping to verify it is still alive if it has been idle
2478
0
                // more than half a second, the network changed events are
2479
0
                // rate-limited to one per 1000 ms.
2480
0
                LOG((" SendPing\n"));
2481
0
                mSpdySession->SendPing();
2482
0
            } else {
2483
0
                LOG((" SendPing skipped due to network activity\n"));
2484
0
            }
2485
0
        } else {
2486
0
            // If not SPDY, Store snapshot amount of data right now
2487
0
            mTrafficCount = mTotalBytesWritten + mTotalBytesRead;
2488
0
            mTrafficStamp = true;
2489
0
        }
2490
0
    } else {
2491
0
        // mark it as not checked
2492
0
        mTrafficStamp = false;
2493
0
    }
2494
0
}
2495
2496
nsAHttpTransaction *
2497
nsHttpConnection::CloseConnectionFastOpenTakesTooLongOrError(bool aCloseSocketTransport)
2498
0
{
2499
0
    MOZ_ASSERT(!mCurrentBytesRead);
2500
0
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");
2501
0
2502
0
    mFastOpenStatus = TFO_FAILED;
2503
0
    RefPtr<nsAHttpTransaction> trans;
2504
0
2505
0
    DontReuse();
2506
0
2507
0
    if (mUsingSpdyVersion != SpdyVersion::NONE) {
2508
0
        // If we have a http2 connection just restart it as if 0rtt failed.
2509
0
        // For http2 we do not need to do similar thing as for http1 because
2510
0
        // backup connection will pick immediately all this transaction anyway.
2511
0
        mUsingSpdyVersion = SpdyVersion::NONE;
2512
0
        if (mSpdySession) {
2513
0
            mTransaction->SetFastOpenStatus(TFO_FAILED);
2514
0
            Unused << mSpdySession->Finish0RTT(true, true);
2515
0
        }
2516
0
        mSpdySession = nullptr;
2517
0
    } else {
2518
0
        // For http1 we want to make this transaction an absolute priority to
2519
0
        // get the backup connection so we will return it from here.
2520
0
        if (NS_SUCCEEDED(mTransaction->RestartOnFastOpenError())) {
2521
0
            trans = mTransaction;
2522
0
        }
2523
0
        mTransaction->SetConnection(nullptr);
2524
0
    }
2525
0
2526
0
    {
2527
0
        MutexAutoLock lock(mCallbacksLock);
2528
0
        mCallbacks = nullptr;
2529
0
    }
2530
0
2531
0
    if (mSocketIn) {
2532
0
        mSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
2533
0
    }
2534
0
2535
0
    mTransaction = nullptr;
2536
0
    if (!aCloseSocketTransport) {
2537
0
        if (mSocketOut) {
2538
0
            mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
2539
0
        }
2540
0
        mSocketTransport->SetEventSink(nullptr, nullptr);
2541
0
        mSocketTransport->SetSecurityCallbacks(nullptr);
2542
0
        mSocketTransport = nullptr;
2543
0
    }
2544
0
    Close(NS_ERROR_NET_RESET);
2545
0
    return trans;
2546
0
}
2547
2548
void
2549
nsHttpConnection::SetFastOpen(bool aFastOpen)
2550
0
{
2551
0
    mFastOpen = aFastOpen;
2552
0
    if (!mFastOpen &&
2553
0
        mTransaction &&
2554
0
        !mTransaction->IsNullTransaction()) {
2555
0
2556
0
        mExperienced = true;
2557
0
2558
0
        nsHttpTransaction *hTrans = mTransaction->QueryHttpTransaction();
2559
0
        if (hTrans) {
2560
0
            SetUrgentStartPreferred(hTrans->ClassOfService() & nsIClassOfService::UrgentStart);
2561
0
        }
2562
0
    }
2563
0
}
2564
2565
void
2566
0
nsHttpConnection::SetFastOpenStatus(uint8_t tfoStatus) {
2567
0
    mFastOpenStatus = tfoStatus;
2568
0
    if ((mFastOpenStatus >= TFO_FAILED_CONNECTION_REFUSED) &&
2569
0
        (mFastOpenStatus <= TFO_FAILED_BACKUP_CONNECTION_TFO_DATA_COOKIE_NOT_ACCEPTED) &&
2570
0
        mSocketTransport) {
2571
0
        nsresult firstRetryError;
2572
0
        if (NS_SUCCEEDED(mSocketTransport->GetFirstRetryError(&firstRetryError)) &&
2573
0
            (NS_FAILED(firstRetryError))) {
2574
0
            if ((mFastOpenStatus >= TFO_FAILED_BACKUP_CONNECTION_TFO_NOT_TRIED) &&
2575
0
                (mFastOpenStatus <= TFO_FAILED_BACKUP_CONNECTION_TFO_DATA_COOKIE_NOT_ACCEPTED)) {
2576
0
                mFastOpenStatus = TFO_FAILED_BACKUP_CONNECTION_NO_TFO_FAILED_TOO;
2577
0
            } else {
2578
0
                // We add +7 to tranform TFO_FAILED_CONNECTION_REFUSED into
2579
0
                // TFO_FAILED_CONNECTION_REFUSED_NO_TFO_FAILED_TOO, etc.
2580
0
                // If the list in TCPFastOpenLayer.h changes please addapt +7.
2581
0
                mFastOpenStatus = tfoStatus + 7;
2582
0
            }
2583
0
        }
2584
0
    }
2585
0
}
2586
2587
void
2588
nsHttpConnection::BootstrapTimings(TimingStruct times)
2589
0
{
2590
0
    mBootstrappedTimingsSet = true;
2591
0
    mBootstrappedTimings = times;
2592
0
}
2593
2594
void
2595
nsHttpConnection::SetEvent(nsresult aStatus)
2596
0
{
2597
0
  switch (aStatus) {
2598
0
  case NS_NET_STATUS_RESOLVING_HOST:
2599
0
    mBootstrappedTimings.domainLookupStart = TimeStamp::Now();
2600
0
    break;
2601
0
  case NS_NET_STATUS_RESOLVED_HOST:
2602
0
    mBootstrappedTimings.domainLookupEnd = TimeStamp::Now();
2603
0
    break;
2604
0
  case NS_NET_STATUS_CONNECTING_TO:
2605
0
    mBootstrappedTimings.connectStart = TimeStamp::Now();
2606
0
    break;
2607
0
  case NS_NET_STATUS_CONNECTED_TO:
2608
0
  {
2609
0
    TimeStamp tnow = TimeStamp::Now();
2610
0
    mBootstrappedTimings.tcpConnectEnd = tnow;
2611
0
    mBootstrappedTimings.connectEnd = tnow;
2612
0
    if ((mFastOpenStatus != TFO_DATA_SENT) &&
2613
0
        !mBootstrappedTimings.secureConnectionStart.IsNull()) {
2614
0
      mBootstrappedTimings.secureConnectionStart = tnow;
2615
0
    }
2616
0
    break;
2617
0
  }
2618
0
  case NS_NET_STATUS_TLS_HANDSHAKE_STARTING:
2619
0
    mBootstrappedTimings.secureConnectionStart = TimeStamp::Now();
2620
0
    break;
2621
0
  case NS_NET_STATUS_TLS_HANDSHAKE_ENDED:
2622
0
    mBootstrappedTimings.connectEnd = TimeStamp::Now();
2623
0
    break;
2624
0
  default:
2625
0
    break;
2626
0
  }
2627
0
}
2628
2629
bool
2630
nsHttpConnection::NoClientCertAuth() const
2631
0
{
2632
0
  if (!mSocketTransport) {
2633
0
    return false;
2634
0
  }
2635
0
2636
0
  nsCOMPtr<nsISupports> secInfo;
2637
0
  mSocketTransport->GetSecurityInfo(getter_AddRefs(secInfo));
2638
0
  if (!secInfo) {
2639
0
    return false;
2640
0
  }
2641
0
2642
0
  nsCOMPtr<nsISSLSocketControl> ssc(do_QueryInterface(secInfo));
2643
0
  if (!ssc) {
2644
0
    return false;
2645
0
  }
2646
0
2647
0
  return !ssc->GetClientCertSent();
2648
0
}
2649
2650
} // namespace net
2651
} // namespace mozilla