bug 378637 part 4 - more proactive failed to connect detection in nsHttpConnection r=hurley
authorPatrick McManus <mcmanus@ducksong.com>
Wed, 02 Apr 2014 17:26:58 -0400
changeset 183599 ec87f45db49e
parent 183598 25c7d263eb6e
child 183600 1455ff7a4ecf
push id26799
push userphilringnalda@gmail.com
push date2014-05-18 00:55 +0000
treeherdermozilla-central@00ef3a7d7aa7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershurley
bugs378637
milestone32.0a1
bug 378637 part 4 - more proactive failed to connect detection in nsHttpConnection r=hurley
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnection.h
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpConnectionMgr.h
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -19,16 +19,17 @@
 #include "nsHttpHandler.h"
 #include "nsIOService.h"
 #include "nsISocketTransport.h"
 #include "nsSocketTransportService2.h"
 #include "nsISSLSocketControl.h"
 #include "sslt.h"
 #include "nsStringStream.h"
 #include "nsProxyRelease.h"
+#include "nsSocketTransport2.h"
 #include "nsPreloadedStream.h"
 #include "ASpdySession.h"
 #include "mozilla/Telemetry.h"
 #include "nsISupportsPriority.h"
 #include "nsHttpPipeline.h"
 #include <algorithm>
 #include "mozilla/ChaosMode.h"
 
@@ -49,16 +50,17 @@ nsHttpConnection::nsHttpConnection()
     , mHttpHandler(gHttpHandler)
     , mCallbacksLock("nsHttpConnection::mCallbacksLock")
     , mConsiderReusedAfterInterval(0)
     , mConsiderReusedAfterEpoch(0)
     , mCurrentBytesRead(0)
     , mMaxBytesRead(0)
     , mTotalBytesRead(0)
     , mTotalBytesWritten(0)
+    , mConnectedTransport(false)
     , mKeepAlive(true) // assume to keep-alive by default
     , mKeepAliveMask(true)
     , mDontReuse(false)
     , mSupportsPipelining(false) // assume low-grade server
     , mIsReused(false)
     , mCompletedProxyConnect(false)
     , mLastTransactionExpectedNoContent(false)
     , mIdleMonitoring(false)
@@ -110,29 +112,31 @@ nsHttpConnection::~nsHttpConnection()
 }
 
 nsresult
 nsHttpConnection::Init(nsHttpConnectionInfo *info,
                        uint16_t maxHangTime,
                        nsISocketTransport *transport,
                        nsIAsyncInputStream *instream,
                        nsIAsyncOutputStream *outstream,
+                       bool connectedTransport,
                        nsIInterfaceRequestor *callbacks,
                        PRIntervalTime rtt)
 {
     MOZ_ASSERT(transport && instream && outstream,
                "invalid socket information");
     LOG(("nsHttpConnection::Init [this=%p "
          "transport=%p instream=%p outstream=%p rtt=%d]\n",
          this, transport, instream, outstream,
          PR_IntervalToMilliseconds(rtt)));
 
     NS_ENSURE_ARG_POINTER(info);
     NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
 
+    mConnectedTransport = connectedTransport;
     mConnInfo = info;
     mLastWriteTime = mLastReadTime = PR_IntervalNow();
     mSupportsPipelining =
         gHttpHandler->ConnMgr()->SupportsPipelining(mConnInfo);
     mRtt = rtt;
     mMaxHangTime = PR_SecondsToInterval(maxHangTime);
 
     mSocketTransport = transport;
@@ -326,16 +330,36 @@ nsHttpConnection::Activate(nsAHttpTransa
         return AddTransaction(trans, pri);
 
     NS_ENSURE_ARG_POINTER(trans);
     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
 
     // reset the read timers to wash away any idle time
     mLastWriteTime = mLastReadTime = PR_IntervalNow();
 
+    // Connection failures are Activated() just like regular transacions.
+    // If we don't have a confirmation of a connected socket then test it
+    // with a write() to get relevant error code.
+    if (!mConnectedTransport) {
+        uint32_t count;
+        mSocketOutCondition = NS_ERROR_FAILURE;
+        if (mSocketOut) {
+            mSocketOutCondition = mSocketOut->Write("", 0, &count);
+        }
+        if (NS_FAILED(mSocketOutCondition) &&
+            mSocketOutCondition != NS_BASE_STREAM_WOULD_BLOCK) {
+            LOG(("nsHttpConnection::Activate [this=%p] Bad Socket %x\n",
+                 this, mSocketOutCondition));
+            mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
+            mTransaction = trans;
+            CloseTransaction(mTransaction, mSocketOutCondition);
+            return mSocketOutCondition;
+        }
+    }
+
     // Update security callbacks
     nsCOMPtr<nsIInterfaceRequestor> callbacks;
     trans->GetSecurityCallbacks(getter_AddRefs(callbacks));
     SetSecurityCallbacks(callbacks);
 
     SetupSSL(caps);
 
     // take ownership of the transaction
@@ -624,17 +648,17 @@ nsHttpConnection::TimeToLive()
     if (!timeToLive)
         timeToLive = 1;
     return timeToLive;
 }
 
 bool
 nsHttpConnection::IsAlive()
 {
-    if (!mSocketTransport)
+    if (!mSocketTransport || !mConnectedTransport)
         return false;
 
     // SocketTransport::IsAlive can run the SSL state machine, so make sure
     // the NPN options are set before that happens.
     SetupSSL(mTransactionCaps);
 
     bool alive;
     nsresult rv = mSocketTransport->IsAlive(&alive);
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -54,18 +54,18 @@ public:
 
     // Initialize the connection:
     //  info        - specifies the connection parameters.
     //  maxHangTime - limits the amount of time this connection can spend on a
     //                single transaction before it should no longer be kept
     //                alive.  a value of 0xffff indicates no limit.
     nsresult Init(nsHttpConnectionInfo *info, uint16_t maxHangTime,
                   nsISocketTransport *, nsIAsyncInputStream *,
-                  nsIAsyncOutputStream *, nsIInterfaceRequestor *,
-                  PRIntervalTime);
+                  nsIAsyncOutputStream *, bool connectedTransport,
+                  nsIInterfaceRequestor *, PRIntervalTime);
 
     // Activate causes the given transaction to be processed on this
     // connection.  It fails if there is already an existing transaction unless
     // a multiplexing protocol such as SPDY is being used
     nsresult Activate(nsAHttpTransaction *, uint32_t caps, int32_t pri);
 
     // Close the underlying socket transport.
     void Close(nsresult reason);
@@ -245,16 +245,17 @@ private:
     int64_t                         mMaxBytesRead;       // max read in 1 activation
     int64_t                         mTotalBytesRead;     // total data read
     int64_t                         mTotalBytesWritten;  // does not include CONNECT tunnel
 
     nsRefPtr<nsIAsyncInputStream>   mInputOverflow;
 
     PRIntervalTime                  mRtt;
 
+    bool                            mConnectedTransport;
     bool                            mKeepAlive;
     bool                            mKeepAliveMask;
     bool                            mDontReuse;
     bool                            mSupportsPipelining;
     bool                            mIsReused;
     bool                            mCompletedProxyConnect;
     bool                            mLastTransactionExpectedNoContent;
     bool                            mIdleMonitoring;
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -2720,21 +2720,23 @@ NS_IMPL_ISUPPORTS(nsHttpConnectionMgr::n
                   nsITransportEventSink,
                   nsIInterfaceRequestor,
                   nsITimerCallback)
 
 nsHttpConnectionMgr::
 nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
                                    nsAHttpTransaction *trans,
                                    uint32_t caps)
-    : mEnt(ent),
-      mTransaction(trans),
-      mCaps(caps),
-      mSpeculative(false),
-      mHasConnected(false)
+    : mEnt(ent)
+    , mTransaction(trans)
+    , mCaps(caps)
+    , mSpeculative(false)
+    , mHasConnected(false)
+    , mPrimaryConnectedOK(false)
+    , mBackupConnectedOK(false)
 {
     MOZ_ASSERT(ent && trans, "constructor with null arguments");
     LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s]\n",
          this, trans, ent->mConnInfo->Host()));
 }
 
 nsHttpConnectionMgr::nsHalfOpenSocket::~nsHalfOpenSocket()
 {
@@ -3016,34 +3018,34 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
     NetAddr peeraddr;
     nsCOMPtr<nsIInterfaceRequestor> callbacks;
     mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
     if (out == mStreamOut) {
         TimeDuration rtt = TimeStamp::Now() - mPrimarySynStarted;
         rv = conn->Init(mEnt->mConnInfo,
                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
                         mSocketTransport, mStreamIn, mStreamOut,
-                        callbacks,
+                        mPrimaryConnectedOK, callbacks,
                         PR_MillisecondsToInterval(
                           static_cast<uint32_t>(rtt.ToMilliseconds())));
 
         if (NS_SUCCEEDED(mSocketTransport->GetPeerAddr(&peeraddr)))
             mEnt->RecordIPFamilyPreference(peeraddr.raw.family);
 
         // The nsHttpConnection object now owns these streams and sockets
         mStreamOut = nullptr;
         mStreamIn = nullptr;
         mSocketTransport = nullptr;
     }
     else {
         TimeDuration rtt = TimeStamp::Now() - mBackupSynStarted;
         rv = conn->Init(mEnt->mConnInfo,
                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
                         mBackupTransport, mBackupStreamIn, mBackupStreamOut,
-                        callbacks,
+                        mBackupConnectedOK, callbacks,
                         PR_MillisecondsToInterval(
                           static_cast<uint32_t>(rtt.ToMilliseconds())));
 
         if (NS_SUCCEEDED(mBackupTransport->GetPeerAddr(&peeraddr)))
             mEnt->RecordIPFamilyPreference(peeraddr.raw.family);
 
         // The nsHttpConnection object now owns these streams and sockets
         mBackupStreamOut = nullptr;
@@ -3120,18 +3122,29 @@ nsHttpConnectionMgr::nsHalfOpenSocket::O
                                                          uint64_t progress,
                                                          uint64_t progressMax)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     if (mTransaction)
         mTransaction->OnTransportStatus(trans, status, progress);
 
-    if (trans != mSocketTransport)
+    MOZ_ASSERT(trans == mSocketTransport || trans == mBackupTransport);
+    if (status == NS_NET_STATUS_CONNECTED_TO) {
+        if (trans == mSocketTransport) {
+            mPrimaryConnectedOK = true;
+        } else {
+            mBackupConnectedOK = true;
+        }
+    }
+
+    // The rest of this method only applies to the primary transport
+    if (trans != mSocketTransport) {
         return NS_OK;
+    }
 
     // if we are doing spdy coalescing and haven't recorded the ip address
     // for this entry before then make the hash key if our dns lookup
     // just completed. We can't do coalescing if using a proxy because the
     // ip addresses are not available to the client.
 
     if (status == NS_NET_STATUS_CONNECTED_TO &&
         gHttpHandler->IsSpdyEnabled() &&
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -453,17 +453,22 @@ private:
         TimeStamp             mBackupSynStarted;
 
         // for syn retry
         nsCOMPtr<nsITimer>             mSynTimer;
         nsCOMPtr<nsISocketTransport>   mBackupTransport;
         nsCOMPtr<nsIAsyncOutputStream> mBackupStreamOut;
         nsCOMPtr<nsIAsyncInputStream>  mBackupStreamIn;
 
+        // mHasConnected tracks whether one of the sockets has completed the
+        // connection process. It may have completed unsuccessfully.
         bool                           mHasConnected;
+
+        bool                           mPrimaryConnectedOK;
+        bool                           mBackupConnectedOK;
     };
     friend class nsHalfOpenSocket;
 
     //-------------------------------------------------------------------------
     // NOTE: these members may be accessed from any thread (use mReentrantMonitor)
     //-------------------------------------------------------------------------
 
     ReentrantMonitor    mReentrantMonitor;