LCOV - code coverage report
Current view: top level - netwerk/protocol/http - nsHttpConnection.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 154 1144 13.5 %
Date: 2018-08-07 16:35:00 Functions: 1 1 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13-14-ga5dd952