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
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;