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