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