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

Generated by: LCOV version 1.13