Bug 1344171 - Improve connection management. r=mcmanus
☠☠ backed out by 552267771708 ☠ ☠
authorDragana Damjanovic dd.mozilla@gmail.com
Tue, 28 Mar 2017 10:19:00 -0400
changeset 350157 d11fb28f9b9c2d9e043dd5eb8c1d34b499ca2fdc
parent 350156 b5ffe95ba00558a905598859dd3b779283ec8bdd
child 350158 f129d307ce4bbfafc2241288033c4f13b7951f59
push id88567
push userryanvm@gmail.com
push dateTue, 28 Mar 2017 21:11:33 +0000
treeherdermozilla-inbound@1138b6fe86e1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1344171
milestone55.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 1344171 - Improve connection management. r=mcmanus
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpConnectionMgr.h
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -672,16 +672,39 @@ nsHttpConnectionMgr::CloseIdleConnection
         return NS_ERROR_UNEXPECTED;
 
     conn->Close(NS_ERROR_ABORT);
     mNumIdleConns--;
     ConditionallyStopPruneDeadConnectionsTimer();
     return NS_OK;
 }
 
+nsresult
+nsHttpConnectionMgr::RemoveIdleConnection(nsHttpConnection *conn)
+{
+    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+    LOG(("nsHttpConnectionMgr::RemoveIdleConnection %p conn=%p",
+         this, conn));
+
+    if (!conn->ConnectionInfo()) {
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
+                                                   conn, nullptr);
+
+    if (!ent || !ent->mIdleConns.RemoveElement(conn)) {
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    mNumIdleConns--;
+    return NS_OK;
+}
+
 // This function lets a connection, after completing the NPN phase,
 // report whether or not it is using spdy through the usingSpdy
 // argument. It would not be necessary if NPN were driven out of
 // the connection manager. The connection entry associated with the
 // connection is then updated to indicate whether or not we want to use
 // spdy with that host and update the preliminary preferred host
 // entries used for de-sharding hostsnames.
 void
@@ -915,33 +938,32 @@ nsHttpConnectionMgr::DispatchPendingQ(ns
         // connections from being established and bound to this
         // transaction. Allow only use of an idle persistent connection
         // (if found) for transactions referred by a half-open connection.
         bool alreadyHalfOpenOrWaitingForTLS = false;
         if (pendingTransInfo->mHalfOpen) {
             MOZ_ASSERT(!pendingTransInfo->mActiveConn);
             RefPtr<nsHalfOpenSocket> halfOpen =
                 do_QueryReferent(pendingTransInfo->mHalfOpen);
+            LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry "
+                 "[trans=%p, halfOpen=%p]\n",
+                 pendingTransInfo->mTransaction.get(), halfOpen.get()));
             if (halfOpen) {
-                // The half open socket was made for this transaction, in
-                // that case ent->mHalfOpens[j]->Transaction() == trans or
-                // the half open socket was opened speculatively and this
-                // transaction took it (in this case it must be:
-                // ent->mHalfOpens[j]->Transaction().IsNullTransaction())
-                MOZ_ASSERT(halfOpen->Transaction()->IsNullTransaction() ||
-                           halfOpen->Transaction() == pendingTransInfo->mTransaction);
                 alreadyHalfOpenOrWaitingForTLS = true;
             } else {
                 // If we have not found the halfOpen socket, remove the pointer.
                 pendingTransInfo->mHalfOpen = nullptr;
             }
         }  else if (pendingTransInfo->mActiveConn) {
             MOZ_ASSERT(!pendingTransInfo->mHalfOpen);
             RefPtr<nsHttpConnection> activeConn =
                 do_QueryReferent(pendingTransInfo->mActiveConn);
+            LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry "
+                 "[trans=%p, activeConn=%p]\n",
+                 pendingTransInfo->mTransaction.get(), activeConn.get()));
             // Check if this transaction claimed a connection that is still
             // performing tls handshake with a NullHttpTransaction or it is between
             // finishing tls and reclaiming (When nullTrans finishes tls handshake,
             // httpConnection does not have a transaction any more and a
             // ReclaimConnection is dispatched). But if an error occurred the
             // connection will be closed, it will exist but CanReused will be
             // false. 
             if (activeConn &&
@@ -1261,44 +1283,27 @@ nsHttpConnectionMgr::MakeNewConnection(n
     nsHttpTransaction *trans = pendingTransInfo->mTransaction;
 
     LOG(("nsHttpConnectionMgr::MakeNewConnection %p ent=%p trans=%p",
          this, ent, trans));
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     uint32_t halfOpenLength = ent->mHalfOpens.Length();
     for (uint32_t i = 0; i < halfOpenLength; i++) {
-        if (ent->mHalfOpens[i]->IsSpeculative()) {
-            // We've found a speculative connection in the half
-            // open list. Remove the speculative bit from it and that
-            // connection can later be used for this transaction
-            // (or another one in the pending queue) - we don't
-            // need to open a new connection here.
+        if (ent->mHalfOpens[i]->Claim()) {
+            // We've found a speculative connection or a connection that
+            // is free to be used in the half open list.
+            // A free to be used connection is a connection that was
+            // open for a concrete transaction, but that trunsaction
+            // ended up using another connection.
             LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s]\n"
-                 "Found a speculative half open connection\n",
+                 "Found a speculative or a free-to-use half open connection\n",
                  ent->mConnInfo->HashKey().get()));
-
-            uint32_t flags;
-            ent->mHalfOpens[i]->SetSpeculative(false);
             pendingTransInfo->mHalfOpen =
                 do_GetWeakReference(static_cast<nsISupportsWeakReference*>(ent->mHalfOpens[i]));
-            nsISocketTransport *transport = ent->mHalfOpens[i]->SocketTransport();
-            if (transport && NS_SUCCEEDED(transport->GetConnectionFlags(&flags))) {
-                flags &= ~nsISocketTransport::DISABLE_RFC1918;
-                transport->SetConnectionFlags(flags);
-            }
-
-            Telemetry::AutoCounter<Telemetry::HTTPCONNMGR_USED_SPECULATIVE_CONN> usedSpeculativeConn;
-            ++usedSpeculativeConn;
-
-            if (ent->mHalfOpens[i]->IsFromPredictor()) {
-              Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRECONNECTS_USED> totalPreconnectsUsed;
-              ++totalPreconnectsUsed;
-            }
-
             // return OK because we have essentially opened a new connection
             // by converting a speculative half-open to general use
             return NS_OK;
         }
     }
 
     // consider null transactions that are being used to drive the ssl handshake if
     // the transaction creating this connection can re-use persistent connections
@@ -1882,23 +1887,22 @@ nsHttpConnectionMgr::RecvdConnect()
 
 void
 nsHttpConnectionMgr::ReleaseClaimedSockets(nsConnectionEntry *ent,
                                            PendingTransactionInfo * pendingTransInfo)
 {
     if (pendingTransInfo->mHalfOpen) {
         RefPtr<nsHalfOpenSocket> halfOpen =
             do_QueryReferent(pendingTransInfo->mHalfOpen);
+        LOG(("nsHttpConnectionMgr::ReleaseClaimedSockets "
+             "[trans=%p halfOpen=%p]",
+             pendingTransInfo->mTransaction.get(),
+             halfOpen.get()));
         if (halfOpen) {
-            if (halfOpen->Transaction() &&
-                halfOpen->Transaction()->IsNullTransaction()) {
-                LOG(("nsHttpConnectionMgr::ReleaseClaimedSockets - mark halfOpne %p "
-                    "speculative again.", halfOpen.get()));
-                halfOpen->SetSpeculative(true);
-            }
+            halfOpen->Unclaim();
         }
         pendingTransInfo->mHalfOpen = nullptr;
     } else if (pendingTransInfo->mActiveConn) {
         RefPtr<nsHttpConnection> activeConn =
             do_QueryReferent(pendingTransInfo->mActiveConn);
         if (activeConn && activeConn->Transaction() &&
             activeConn->Transaction()->IsNullTransaction()) {
             NullHttpTransaction *nullTrans = activeConn->Transaction()->QueryNullTransaction();
@@ -1914,40 +1918,37 @@ nsHttpConnectionMgr::CreateTransport(nsC
                                      nsAHttpTransaction *trans,
                                      uint32_t caps,
                                      bool speculative,
                                      bool isFromPredictor,
                                      bool allow1918,
                                      PendingTransactionInfo *pendingTransInfo)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-
-    RefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans, caps);
+    MOZ_ASSERT((speculative && !pendingTransInfo) ||
+               (!speculative && pendingTransInfo));
+
+    RefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans, caps,
+                                                         speculative,
+                                                         isFromPredictor);
+
     if (speculative) {
-        sock->SetSpeculative(true);
         sock->SetAllow1918(allow1918);
-        Telemetry::AutoCounter<Telemetry::HTTPCONNMGR_TOTAL_SPECULATIVE_CONN> totalSpeculativeConn;
-        ++totalSpeculativeConn;
-
-        if (isFromPredictor) {
-          sock->SetIsFromPredictor(true);
-          Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRECONNECTS_CREATED> totalPreconnectsCreated;
-          ++totalPreconnectsCreated;
-        }
     }
-
     // The socket stream holds the reference to the half open
     // socket - so if the stream fails to init the half open
     // will go away.
     nsresult rv = sock->SetupPrimaryStreams();
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (pendingTransInfo) {
         pendingTransInfo->mHalfOpen =
             do_GetWeakReference(static_cast<nsISupportsWeakReference*>(sock));
+        DebugOnly<bool> claimed = sock->Claim();
+        MOZ_ASSERT(claimed);
     }
 
     ent->mHalfOpens.AppendElement(sock);
     mNumHalfOpenConns++;
     return NS_OK;
 }
 
 void
@@ -2286,18 +2287,16 @@ nsHttpConnectionMgr::OnMsgCancelTransact
                 infoArray->RemoveElementAt(transIndex);
             }
 
             // Abandon all half-open sockets belonging to the given transaction.
             if (pendingTransInfo) {
                 RefPtr<nsHalfOpenSocket> half =
                     do_QueryReferent(pendingTransInfo->mHalfOpen);
                 if (half) {
-                    MOZ_ASSERT(trans == half->Transaction() ||
-                               half->Transaction()->IsNullTransaction());
                     half->Abandon();
                 }
                 pendingTransInfo->mHalfOpen = nullptr;
             }
         }
 
         trans->Close(closeCode);
 
@@ -3046,31 +3045,45 @@ NS_INTERFACE_MAP_BEGIN(nsHttpConnectionM
         *aInstancePtr = this;
         return NS_OK;
     } else
 NS_INTERFACE_MAP_END
 
 nsHttpConnectionMgr::
 nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
                                    nsAHttpTransaction *trans,
-                                   uint32_t caps)
+                                   uint32_t caps,
+                                   bool speculative,
+                                   bool isFromPredictor)
     : mEnt(ent)
     , mTransaction(trans)
     , mDispatchedMTransaction(false)
     , mCaps(caps)
-    , mSpeculative(false)
-    , mIsFromPredictor(false)
+    , mSpeculative(speculative)
+    , mIsFromPredictor(isFromPredictor)
     , mAllow1918(true)
     , mHasConnected(false)
     , mPrimaryConnectedOK(false)
     , mBackupConnectedOK(false)
+    , mFreeToUse(true)
+    , mPrimaryStreamStatus(NS_OK)
 {
     MOZ_ASSERT(ent && trans, "constructor with null arguments");
     LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s key=%s]\n",
          this, trans, ent->mConnInfo->Origin(), ent->mConnInfo->HashKey().get()));
+
+    if (speculative) {
+        Telemetry::AutoCounter<Telemetry::HTTPCONNMGR_TOTAL_SPECULATIVE_CONN> totalSpeculativeConn;
+        ++totalSpeculativeConn;
+
+        if (isFromPredictor) {
+          Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRECONNECTS_CREATED> totalPreconnectsCreated;
+          ++totalPreconnectsCreated;
+        }
+    }
 }
 
 nsHttpConnectionMgr::nsHalfOpenSocket::~nsHalfOpenSocket()
 {
     MOZ_ASSERT(!mStreamOut);
     MOZ_ASSERT(!mBackupStreamOut);
     MOZ_ASSERT(!mSynTimer);
     LOG(("Destroying nsHalfOpenSocket [this=%p]\n", this));
@@ -3237,18 +3250,16 @@ nsHttpConnectionMgr::nsHalfOpenSocket::S
     }
     return rv;
 }
 
 nsresult
 nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams()
 {
     MOZ_ASSERT(mTransaction);
-    MOZ_ASSERT(!mTransaction->IsNullTransaction(),
-               "null transactions dont have backup streams");
 
     mBackupSynStarted = TimeStamp::Now();
     nsresult rv = SetupStreams(getter_AddRefs(mBackupTransport),
                                getter_AddRefs(mBackupStreamIn),
                                getter_AddRefs(mBackupStreamOut),
                                true);
     LOG(("nsHalfOpenSocket::SetupBackupStream [this=%p ent=%s rv=%" PRIx32 "]",
          this, mEnt->mConnInfo->Origin(), static_cast<uint32_t>(rv)));
@@ -3262,17 +3273,17 @@ nsHttpConnectionMgr::nsHalfOpenSocket::S
     return rv;
 }
 
 void
 nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
 {
     uint16_t timeout = gHttpHandler->GetIdleSynTimeout();
     MOZ_ASSERT(!mSynTimer, "timer already initd");
-    if (timeout && !mTransaction->IsDone() && !mTransaction->IsNullTransaction()) {
+    if (timeout && !mSpeculative) {
         // Setup the timer that will establish a backup socket
         // if we do not get a writable event on the main one.
         // We do this because a lost SYN takes a very long time
         // to repair at the TCP level.
         //
         // Failure to setup the timer is something we can live with,
         // so don't return an error in that case.
         nsresult rv;
@@ -3357,18 +3368,16 @@ nsHttpConnectionMgr::nsHalfOpenSocket::D
 }
 
 
 NS_IMETHODIMP // method for nsITimerCallback
 nsHttpConnectionMgr::nsHalfOpenSocket::Notify(nsITimer *timer)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
     MOZ_ASSERT(timer == mSynTimer, "wrong timer");
-    MOZ_ASSERT(mTransaction && !mTransaction->IsNullTransaction(),
-               "null transactions dont have backup streams");
 
     DebugOnly<nsresult> rv = SetupBackupStreams();
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
     mSynTimer = nullptr;
     return NS_OK;
 }
 
@@ -3441,18 +3450,16 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
         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 if (out == mBackupStreamOut) {
-        MOZ_ASSERT(!mTransaction->IsNullTransaction(),
-                   "null transactions dont have backup streams");
         TimeDuration rtt = TimeStamp::Now() - mBackupSynStarted;
         rv = conn->Init(mEnt->mConnInfo,
                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
                         mBackupTransport, mBackupStreamIn, mBackupStreamOut,
                         mBackupConnectedOK, callbacks,
                         PR_MillisecondsToInterval(
                           static_cast<uint32_t>(rtt.ToMilliseconds())));
 
@@ -3523,16 +3530,45 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
             gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
             rv = gHttpHandler->ConnMgr()->
                 DispatchAbstractTransaction(mEnt, trans, mCaps, conn, 0);
         } else {
             // otherwise just put this in the persistent connection pool
             LOG(("nsHalfOpenSocket::OnOutputStreamReady no transaction match "
                  "returning conn %p to pool\n", conn.get()));
             gHttpHandler->ConnMgr()->OnMsgReclaimConnection(0, conn);
+
+            // We expect that there is at least one tranasction in the pending
+            // queue that can take this connection, but it can happened that
+            // all transactions are blocked or they have took other idle
+            // connections. In that case the connection has been added to the
+            // idle queue.
+            // If the connection is in the idle queue but it is using ssl, make
+            // a nulltransaction for it to finish ssl handshake!
+            int32_t idx = mEnt->mIdleConns.IndexOf(conn);
+            if (idx != -1) {
+                if (mEnt->mConnInfo->FirstHopSSL() &&
+                    !mEnt->mConnInfo->UsingConnect()) {
+                    DebugOnly<nsresult> rv = gHttpHandler->ConnMgr()->RemoveIdleConnection(conn);
+                    MOZ_ASSERT(NS_SUCCEEDED(rv));
+                    conn->EndIdleMonitoring();
+                    RefPtr<nsAHttpTransaction> trans;
+                    if (mTransaction->IsNullTransaction() &&
+                        !mDispatchedMTransaction) {
+                        mDispatchedMTransaction = true;
+                        trans = mTransaction;
+                    } else {
+                        trans = new NullHttpTransaction(mEnt->mConnInfo,
+                                                        callbacks, mCaps);
+                    }
+                    gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
+                    rv = gHttpHandler->ConnMgr()->
+                        DispatchAbstractTransaction(mEnt, trans, mCaps, conn, 0);
+                }
+            }
         }
     }
 
     return rv;
 }
 
 // method for nsITransportEventSink
 NS_IMETHODIMP
@@ -3569,16 +3605,18 @@ nsHttpConnectionMgr::nsHalfOpenSocket::O
         }
     }
 
     // The rest of this method only applies to the primary transport
     if (trans != mSocketTransport) {
         return NS_OK;
     }
 
+    mPrimaryStreamStatus = status;
+
     // 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_CONNECTING_TO &&
         gHttpHandler->IsSpdyEnabled() &&
         gHttpHandler->CoalesceSpdy() &&
@@ -3654,16 +3692,57 @@ nsHttpConnectionMgr::nsHalfOpenSocket::G
         nsCOMPtr<nsIInterfaceRequestor> callbacks;
         mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
         if (callbacks)
             return callbacks->GetInterface(iid, result);
     }
     return NS_ERROR_NO_INTERFACE;
 }
 
+bool
+nsHttpConnectionMgr::nsHalfOpenSocket::Claim()
+{
+    if (mSpeculative) {
+        mSpeculative = false;
+        uint32_t flags;
+        if (mSocketTransport && NS_SUCCEEDED(mSocketTransport->GetConnectionFlags(&flags))) {
+            flags &= ~nsISocketTransport::DISABLE_RFC1918;
+            mSocketTransport->SetConnectionFlags(flags);
+        }
+
+        Telemetry::AutoCounter<Telemetry::HTTPCONNMGR_USED_SPECULATIVE_CONN> usedSpeculativeConn;
+        ++usedSpeculativeConn;
+
+        if (mIsFromPredictor) {
+            Telemetry::AutoCounter<Telemetry::PREDICTOR_TOTAL_PRECONNECTS_USED> totalPreconnectsUsed;
+            ++totalPreconnectsUsed;
+        }
+
+        if ((mPrimaryStreamStatus == NS_NET_STATUS_CONNECTING_TO) &&
+            mEnt && !mBackupTransport && !mSynTimer) {
+            SetupBackupTimer();
+        }
+    }
+
+    if (mFreeToUse) {
+        mFreeToUse = false;
+        return true;
+    }
+    return false;
+}
+
+void
+nsHttpConnectionMgr::nsHalfOpenSocket::Unclaim()
+{
+    MOZ_ASSERT(!mSpeculative && !mFreeToUse);
+    // We will keep the backup-timer running. Most probably this halfOpen will
+    // be used by a transaction from which this transaction took the halfOpen.
+    // (this is happening because of the transaction priority.)
+    mFreeToUse = true;
+}
 
 already_AddRefed<nsHttpConnection>
 ConnectionHandle::TakeHttpConnection()
 {
     // return our connection object to the caller and clear it internally
     // do not drop our reference - the caller now owns it.
     MOZ_ASSERT(mConn);
     return mConn.forget();
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -179,16 +179,17 @@ public:
 
     // Try and process all pending transactions
     MOZ_MUST_USE nsresult ProcessPendingQ();
 
     // This is used to force an idle connection to be closed and removed from
     // the idle connection list. It is called when the idle connection detects
     // that the network peer has closed the transport.
     MOZ_MUST_USE nsresult CloseIdleConnection(nsHttpConnection *);
+    MOZ_MUST_USE nsresult RemoveIdleConnection(nsHttpConnection *);
 
     // The connection manager needs to know when a normal HTTP connection has been
     // upgraded to SPDY because the dispatch and idle semantics are a little
     // bit different.
     void ReportSpdyConnection(nsHttpConnection *, bool usingSpdy);
 
     bool GetConnectionData(nsTArray<HttpRetParams> *);
 
@@ -325,17 +326,19 @@ private:
         NS_DECL_THREADSAFE_ISUPPORTS
         NS_DECL_NSIOUTPUTSTREAMCALLBACK
         NS_DECL_NSITRANSPORTEVENTSINK
         NS_DECL_NSIINTERFACEREQUESTOR
         NS_DECL_NSITIMERCALLBACK
 
         nsHalfOpenSocket(nsConnectionEntry *ent,
                          nsAHttpTransaction *trans,
-                         uint32_t caps);
+                         uint32_t caps,
+                         bool speculative,
+                         bool isFromPredictor);
 
         MOZ_MUST_USE nsresult SetupStreams(nsISocketTransport **,
                                            nsIAsyncInputStream **,
                                            nsIAsyncOutputStream **,
                                            bool isBackup);
         MOZ_MUST_USE nsresult SetupPrimaryStreams();
         MOZ_MUST_USE nsresult SetupBackupStreams();
         void     SetupBackupTimer();
@@ -343,27 +346,28 @@ private:
         void     Abandon();
         double   Duration(TimeStamp epoch);
         nsISocketTransport *SocketTransport() { return mSocketTransport; }
         nsISocketTransport *BackupTransport() { return mBackupTransport; }
 
         nsAHttpTransaction *Transaction() { return mTransaction; }
 
         bool IsSpeculative() { return mSpeculative; }
-        void SetSpeculative(bool val) { mSpeculative = val; }
 
         bool IsFromPredictor() { return mIsFromPredictor; }
-        void SetIsFromPredictor(bool val) { mIsFromPredictor = val; }
 
         bool Allow1918() { return mAllow1918; }
         void SetAllow1918(bool val) { mAllow1918 = val; }
 
         bool HasConnected() { return mHasConnected; }
 
         void PrintDiagnostics(nsCString &log);
+
+        bool Claim();
+        void Unclaim();
     private:
         // To find out whether |mTransaction| is still in the connection entry's
         // pending queue. If the transaction is found and |removeWhenFound| is
         // true, the transaction will be removed from the pending queue.
         already_AddRefed<PendingTransactionInfo>
         FindTransactionHelper(bool removeWhenFound);
 
         nsConnectionEntry              *mEnt;
@@ -400,16 +404,23 @@ private:
         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;
+
+        // A nsHalfOpenSocket can be made for a concrete non-null transaction,
+        // but the transaction can be dispatch to another connection. In that
+        // case we can free this transaction to be claimed by other
+        // transactions.
+        bool                           mFreeToUse;
+        nsresult                       mPrimaryStreamStatus;
     };
     friend class nsHalfOpenSocket;
 
     class PendingTransactionInfo : public ARefBase
     {
     public:
         explicit PendingTransactionInfo(nsHttpTransaction * trans)
             : mTransaction(trans)