Bug 1341572 - Fix multiple HalfOpen socket for a single transaction. r=mcmanus
authorDragana Damjanovic <dd.mozilla@gmail.com>
Fri, 17 Mar 2017 09:00:50 +0100
changeset 348220 e1f93ab32013f2e8653e54d16b20a1f1cc7c2f6b
parent 348219 71c32198e7c07ea2f4e3719cd0b6530c1029d9d0
child 348221 29e740f90f2c680df09a49387c962907b1fc65b1
push id31515
push userkwierso@gmail.com
push dateFri, 17 Mar 2017 18:04:10 +0000
treeherdermozilla-central@23a4b7430dd7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1341572
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 1341572 - Fix multiple HalfOpen socket for a single transaction. r=mcmanus
netwerk/protocol/http/ConnectionDiagnostics.cpp
netwerk/protocol/http/NullHttpTransaction.cpp
netwerk/protocol/http/NullHttpTransaction.h
netwerk/protocol/http/nsHttpConnection.cpp
netwerk/protocol/http/nsHttpConnection.h
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpConnectionMgr.h
--- a/netwerk/protocol/http/ConnectionDiagnostics.cpp
+++ b/netwerk/protocol/http/ConnectionDiagnostics.cpp
@@ -199,10 +199,20 @@ nsHttpTransaction::PrintDiagnostics(nsCS
   nsAutoCString requestURI;
   mRequestHead->RequestURI(requestURI);
   log.AppendPrintf("     ::: uri = %s\n", requestURI.get());
   log.AppendPrintf("     caps = 0x%x\n", mCaps);
   log.AppendPrintf("     priority = %d\n", mPriority);
   log.AppendPrintf("     restart count = %u\n", mRestartCount);
 }
 
+void
+nsHttpConnectionMgr::PendingTransactionInfo::PrintDiagnostics(nsCString &log)
+{
+  log.AppendPrintf("     ::: Pending transaction\n");
+  mTransaction->PrintDiagnostics(log);
+  RefPtr<nsHalfOpenSocket> halfOpen = do_QueryReferent(mHalfOpen);
+  log.AppendPrintf("     Waiting for half open sock: %p or connection: %p\n",
+                   halfOpen.get(), mActiveConn.get());
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/NullHttpTransaction.cpp
+++ b/netwerk/protocol/http/NullHttpTransaction.cpp
@@ -137,16 +137,22 @@ NullHttpTransaction::Claim()
   if (mClaimed) {
     return false;
   }
   mClaimed = true;
   return true;
 }
 
 void
+NullHttpTransaction::Unclaim()
+{
+  mClaimed = false;
+}
+
+void
 NullHttpTransaction::SetConnection(nsAHttpConnection *conn)
 {
   mConnection = conn;
 }
 
 nsAHttpConnection *
 NullHttpTransaction::Connection()
 {
--- a/netwerk/protocol/http/NullHttpTransaction.h
+++ b/netwerk/protocol/http/NullHttpTransaction.h
@@ -34,16 +34,17 @@ public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSAHTTPTRANSACTION
 
   NullHttpTransaction(nsHttpConnectionInfo *ci,
                       nsIInterfaceRequestor *callbacks,
                       uint32_t caps);
 
   MOZ_MUST_USE bool Claim();
+  void Unclaim();
 
   // Overload of nsAHttpTransaction methods
   bool IsNullTransaction() override final { return true; }
   NullHttpTransaction *QueryNullTransaction() override final { return this; }
   bool ResponseTimeoutEnabled() const override final {return true; }
   PRIntervalTime ResponseTimeout() override final
   {
     return PR_SecondsToInterval(15);
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -2069,21 +2069,32 @@ nsHttpConnection::DisableTCPKeepalives()
     }
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpConnection::nsISupports
 //-----------------------------------------------------------------------------
 
-NS_IMPL_ISUPPORTS(nsHttpConnection,
-                  nsIInputStreamCallback,
-                  nsIOutputStreamCallback,
-                  nsITransportEventSink,
-                  nsIInterfaceRequestor)
+NS_IMPL_ADDREF(nsHttpConnection)
+NS_IMPL_RELEASE(nsHttpConnection)
+
+NS_INTERFACE_MAP_BEGIN(nsHttpConnection)
+    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+    NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
+    NS_INTERFACE_MAP_ENTRY(nsIOutputStreamCallback)
+    NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
+    NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+    // we have no macro that covers this case.
+    if (aIID.Equals(NS_GET_IID(nsHttpConnection)) ) {
+        AddRef();
+        *aInstancePtr = this;
+        return NS_OK;
+    } else
+NS_INTERFACE_MAP_END
 
 //-----------------------------------------------------------------------------
 // nsHttpConnection::nsIInputStreamCallback
 //-----------------------------------------------------------------------------
 
 // called on the socket transport thread
 NS_IMETHODIMP
 nsHttpConnection::OnInputStreamReady(nsIAsyncInputStream *in)
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -26,35 +26,41 @@ class nsISocketTransport;
 class nsISSLSocketControl;
 
 namespace mozilla {
 namespace net {
 
 class nsHttpHandler;
 class ASpdySession;
 
+// 1dcc863e-db90-4652-a1fe-13fea0b54e46
+#define NS_HTTPCONNECTION_IID \
+{ 0x1dcc863e, 0xdb90, 0x4652, {0xa1, 0xfe, 0x13, 0xfe, 0xa0, 0xb5, 0x4e, 0x46 }}
+
 //-----------------------------------------------------------------------------
 // nsHttpConnection - represents a connection to a HTTP server (or proxy)
 //
 // NOTE: this objects lives on the socket thread only.  it should not be
 // accessed from any other thread.
 //-----------------------------------------------------------------------------
 
 class nsHttpConnection final : public nsAHttpSegmentReader
                              , public nsAHttpSegmentWriter
                              , public nsIInputStreamCallback
                              , public nsIOutputStreamCallback
                              , public nsITransportEventSink
                              , public nsIInterfaceRequestor
                              , public NudgeTunnelCallback
                              , public ARefBase
+                             , public nsSupportsWeakReference
 {
     virtual ~nsHttpConnection();
 
 public:
+    NS_DECLARE_STATIC_IID_ACCESSOR(NS_HTTPCONNECTION_IID)
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSAHTTPSEGMENTREADER
     NS_DECL_NSAHTTPSEGMENTWRITER
     NS_DECL_NSIINPUTSTREAMCALLBACK
     NS_DECL_NSIOUTPUTSTREAMCALLBACK
     NS_DECL_NSITRANSPORTEVENTSINK
     NS_DECL_NSIINTERFACEREQUESTOR
     NS_DECL_NUDGETUNNELCALLBACK
@@ -371,12 +377,14 @@ private:
                                                              // for the end of
                                                              // the handsake.
     int64_t                        mContentBytesWritten0RTT;
     bool                           mEarlyDataNegotiated; //Only used for telemetry
     nsCString                      mEarlyNegotiatedALPN;
     bool                           mDid0RTTSpdy;
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(nsHttpConnection, NS_HTTPCONNECTION_IID)
+
 } // namespace net
 } // namespace mozilla
 
 #endif // nsHttpConnection_h__
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -39,41 +39,44 @@
 
 namespace mozilla {
 namespace net {
 
 //-----------------------------------------------------------------------------
 
 NS_IMPL_ISUPPORTS(nsHttpConnectionMgr, nsIObserver)
 
-static void
-InsertTransactionSorted(nsTArray<RefPtr<nsHttpTransaction>> &pendingQ, nsHttpTransaction *trans)
+void
+nsHttpConnectionMgr::InsertTransactionSorted(nsTArray<RefPtr<nsHttpConnectionMgr::PendingTransactionInfo> > &pendingQ,
+                                             nsHttpConnectionMgr::PendingTransactionInfo *pendingTransInfo)
 {
     // insert into queue with smallest valued number first.  search in reverse
     // order under the assumption that many of the existing transactions will
     // have the same priority (usually 0).
 
+    nsHttpTransaction *trans = pendingTransInfo->mTransaction;
+
     for (int32_t i = pendingQ.Length() - 1; i >= 0; --i) {
-        nsHttpTransaction *t = pendingQ[i];
+        nsHttpTransaction *t = pendingQ[i]->mTransaction;
         if (trans->Priority() >= t->Priority()) {
             if (ChaosMode::isActive(ChaosFeature::NetworkScheduling)) {
                 int32_t samePriorityCount;
                 for (samePriorityCount = 0; i - samePriorityCount >= 0; ++samePriorityCount) {
-                    if (pendingQ[i - samePriorityCount]->Priority() != trans->Priority()) {
+                    if (pendingQ[i - samePriorityCount]->mTransaction->Priority() != trans->Priority()) {
                         break;
                     }
                 }
                 // skip over 0...all of the elements with the same priority.
                 i -= ChaosMode::randomUint32LessThan(samePriorityCount + 1);
             }
-            pendingQ.InsertElementAt(i+1, trans);
+            pendingQ.InsertElementAt(i+1, pendingTransInfo);
             return;
         }
     }
-    pendingQ.InsertElementAt(0, trans);
+    pendingQ.InsertElementAt(0, pendingTransInfo);
 }
 
 //-----------------------------------------------------------------------------
 
 nsHttpConnectionMgr::nsHttpConnectionMgr()
     : mReentrantMonitor("nsHttpConnectionMgr.mReentrantMonitor")
     , mMaxUrgentStartQ(0)
     , mMaxConns(0)
@@ -628,18 +631,18 @@ nsHttpConnectionMgr::LookupConnectionEnt
         // likely the only active connection in preferred - so start with that.
         if (preferred->mActiveConns.Contains(conn))
             return preferred;
         if (preferred->mIdleConns.Contains(conn))
             return preferred;
     }
 
     if (trans &&
-        (preferred->mPendingQ.Contains(trans) ||
-         preferred->mUrgentStartQ.Contains(trans))) {
+        (preferred->mPendingQ.Contains(trans, PendingComparator()) ||
+         preferred->mUrgentStartQ.Contains(trans, PendingComparator()))) {
         return preferred;
     }
 
     // Neither conn nor trans found in preferred, use the default entry
     return ent;
 }
 
 nsresult
@@ -872,56 +875,94 @@ nsHttpConnectionMgr::GetSpdyPreferredEnt
          preferred->mConnInfo->Origin(), aOriginalEntry->mConnInfo->Origin(),
          aOriginalEntry->mConnInfo->Origin(), preferred->mConnInfo->Origin()));
     Telemetry::Accumulate(Telemetry::SPDY_NPN_JOIN, true);
     return preferred;
 }
 
 //-----------------------------------------------------------------------------
 void
-nsHttpConnectionMgr::DispatchPendingQ(nsTArray<RefPtr<nsHttpTransaction>> &pendingQ,
+nsHttpConnectionMgr::DispatchPendingQ(nsTArray<RefPtr<nsHttpConnectionMgr::PendingTransactionInfo> > &pendingQ,
                                       nsConnectionEntry *ent,
                                       bool &dispatchedSuccessfully,
                                       bool considerAll)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-    nsHttpTransaction *trans = nullptr;
+    PendingTransactionInfo *pendingTransInfo = nullptr;
     nsresult rv;
     // if !considerAll iterate the pending list until one is dispatched successfully.
     // Keep iterating afterwards only until a transaction fails to dispatch.
     // if considerAll == true then try and dispatch all items.
     for (uint32_t i = 0; i < pendingQ.Length(); ) {
-        trans = pendingQ[i];
+        pendingTransInfo = pendingQ[i];
+        LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry "
+             "[trans=%p, halfOpen=%p, activeConn=%p]\n",
+             pendingTransInfo->mTransaction.get(),
+             pendingTransInfo->mHalfOpen.get(),
+             pendingTransInfo->mActiveConn.get()));
 
         // When this transaction has already established a half-open
         // connection, we want to prevent any duplicate half-open
         // 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 alreadyHalfOpen = false;
-        for (int32_t j = 0; j < ((int32_t) ent->mHalfOpens.Length()); ++j) {
-            if (ent->mHalfOpens[j]->Transaction() == trans) {
-                alreadyHalfOpen = true;
-                break;
+        bool alreadyHalfOpenOrWaitingForTLS = false;
+        if (pendingTransInfo->mHalfOpen) {
+            MOZ_ASSERT(!pendingTransInfo->mActiveConn);
+            RefPtr<nsHalfOpenSocket> halfOpen =
+                do_QueryReferent(pendingTransInfo->mHalfOpen);
+            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);
+            // 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 &&
+                ((activeConn->Transaction() &&
+                  activeConn->Transaction()->IsNullTransaction()) ||
+                 (!activeConn->Transaction() && activeConn->CanReuse()))) {
+                alreadyHalfOpenOrWaitingForTLS = true;
+            } else {
+                // If we have not found the connection, remove the pointer.
+                pendingTransInfo->mActiveConn = nullptr;
             }
         }
 
         rv = TryDispatchTransaction(ent,
-                                    alreadyHalfOpen || !!trans->TunnelProvider(),
-                                    trans);
+                                    alreadyHalfOpenOrWaitingForTLS || !!pendingTransInfo->mTransaction->TunnelProvider(),
+                                    pendingTransInfo);
         if (NS_SUCCEEDED(rv) || (rv != NS_ERROR_NOT_AVAILABLE)) {
             if (NS_SUCCEEDED(rv)) {
                 LOG(("  dispatching pending transaction...\n"));
             } else {
                 LOG(("  removing pending transaction based on "
                      "TryDispatchTransaction returning hard error %" PRIx32 "\n",
                      static_cast<uint32_t>(rv)));
             }
-            if (pendingQ.RemoveElement(trans)) {
-                // trans is now potentially destroyed
+            ReleaseClaimedSockets(ent, pendingTransInfo);
+            if (pendingQ.RemoveElement(pendingTransInfo)) {
+                // pendingTransInfo is now potentially destroyed
                 dispatchedSuccessfully = true;
                 continue; // dont ++i as we just made the array shorter
             }
 
             LOG(("  transaction not found in pending queue\n"));
         }
 
         if (dispatchedSuccessfully && !considerAll)
@@ -1088,18 +1129,20 @@ nsHttpConnectionMgr::RestrictConnections
 }
 
 // returns NS_OK if a connection was started
 // return NS_ERROR_NOT_AVAILABLE if a new connection cannot be made due to
 //        ephemeral limits
 // returns other NS_ERROR on hard failure conditions
 nsresult
 nsHttpConnectionMgr::MakeNewConnection(nsConnectionEntry *ent,
-                                       nsHttpTransaction *trans)
+                                       PendingTransactionInfo *pendingTransInfo)
 {
+    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
@@ -1108,16 +1151,18 @@ nsHttpConnectionMgr::MakeNewConnection(n
             // (or another one in the pending queue) - we don't
             // need to open a new connection here.
             LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s]\n"
                  "Found a speculative 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;
@@ -1139,16 +1184,18 @@ nsHttpConnectionMgr::MakeNewConnection(n
         uint32_t activeLength = ent->mActiveConns.Length();
         for (uint32_t i = 0; i < activeLength; i++) {
             nsAHttpTransaction *activeTrans = ent->mActiveConns[i]->Transaction();
             NullHttpTransaction *nullTrans = activeTrans ? activeTrans->QueryNullTransaction() : nullptr;
             if (nullTrans && nullTrans->Claim()) {
                 LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s] "
                      "Claiming a null transaction for later use\n",
                      ent->mConnInfo->HashKey().get()));
+                pendingTransInfo->mActiveConn =
+                    do_GetWeakReference(static_cast<nsISupportsWeakReference*>(ent->mActiveConns[i]));
                 return NS_OK;
             }
         }
     }
 
     // If this host is trying to negotiate a SPDY session right now,
     // don't create any new connections until the result of the
     // negotiation is known.
@@ -1215,17 +1262,18 @@ nsHttpConnectionMgr::MakeNewConnection(n
         }
       outerLoopEnd:
         ;
     }
 
     if (AtActiveConnectionLimit(ent, trans->Caps()))
         return NS_ERROR_NOT_AVAILABLE;
 
-    nsresult rv = CreateTransport(ent, trans, trans->Caps(), false, false, true);
+    nsresult rv = CreateTransport(ent, trans, trans->Caps(), false, false,
+                                  true, pendingTransInfo);
     if (NS_FAILED(rv)) {
         /* hard failure */
         LOG(("nsHttpConnectionMgr::MakeNewConnection [ci = %s trans = %p] "
              "CreateTransport() hard failure.\n",
              ent->mConnInfo->HashKey().get(), trans));
         trans->Close(rv);
         if (rv == NS_ERROR_NOT_AVAILABLE)
             rv = NS_ERROR_FAILURE;
@@ -1239,23 +1287,28 @@ nsHttpConnectionMgr::MakeNewConnection(n
 //   and the transaction is started.
 // returns ERROR_NOT_AVAILABLE if no connection can be found and it
 //   should be queued until circumstances change
 // returns other ERROR when transaction has a hard failure and should
 //   not remain in the pending queue
 nsresult
 nsHttpConnectionMgr::TryDispatchTransaction(nsConnectionEntry *ent,
                                             bool onlyReusedConnection,
-                                            nsHttpTransaction *trans)
+                                            PendingTransactionInfo *pendingTransInfo)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+    nsHttpTransaction *trans = pendingTransInfo->mTransaction;
+
     LOG(("nsHttpConnectionMgr::TryDispatchTransaction without conn "
-         "[trans=%p ci=%p ci=%s caps=%x tunnelprovider=%p onlyreused=%d "
-         "active=%" PRIuSIZE " idle=%" PRIuSIZE "]\n", trans,
-         ent->mConnInfo.get(), ent->mConnInfo->HashKey().get(),
+         "[trans=%p halfOpen=%p conn=%p ci=%p ci=%s caps=%x tunnelprovider=%p "
+         "onlyreused=%d active=%" PRIuSIZE " idle=%" PRIuSIZE "]\n", trans,
+         pendingTransInfo->mHalfOpen.get(),
+         pendingTransInfo->mActiveConn.get(), ent->mConnInfo.get(),
+         ent->mConnInfo->HashKey().get(),
          uint32_t(trans->Caps()), trans->TunnelProvider(),
          onlyReusedConnection, ent->mActiveConns.Length(),
          ent->mIdleConns.Length()));
 
     uint32_t caps = trans->Caps();
 
     // 0 - If this should use spdy then dispatch it post haste.
     // 1 - If there is connection pressure then see if we can pipeline this on
@@ -1381,17 +1434,17 @@ nsHttpConnectionMgr::TryDispatchTransact
     }
 
     // step 3
     // consider pipelining scripts and revalidations
     // h1 pipelining has been removed
 
     // step 4
     if (!onlyReusedConnection) {
-        nsresult rv = MakeNewConnection(ent, trans);
+        nsresult rv = MakeNewConnection(ent, pendingTransInfo);
         if (NS_SUCCEEDED(rv)) {
             // this function returns NOT_AVAILABLE for asynchronous connects
             LOG(("   dispatched step 4 (async new conn) trans=%p\n", trans));
             return NS_ERROR_NOT_AVAILABLE;
         }
 
         if (rv != NS_ERROR_NOT_AVAILABLE) {
             // not available return codes should try next step as they are
@@ -1610,16 +1663,17 @@ nsHttpConnectionMgr::ProcessNewTransacti
     ReportProxyTelemetry(ent);
 
     // Check if the transaction already has a sticky reference to a connection.
     // If so, then we can just use it directly by transferring its reference
     // to the new connection variable instead of searching for a new one
 
     nsAHttpConnection *wrappedConnection = trans->Connection();
     RefPtr<nsHttpConnection> conn;
+    RefPtr<PendingTransactionInfo> pendingTransInfo;
     if (wrappedConnection)
         conn = wrappedConnection->TakeHttpConnection();
 
     if (conn) {
         MOZ_ASSERT(trans->Caps() & NS_HTTP_STICKY_CONNECTION);
         LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
              "sticky connection=%p\n", trans, conn.get()));
 
@@ -1633,37 +1687,41 @@ nsHttpConnectionMgr::ProcessNewTransacti
             MOZ_ASSERT(!conn->IsExperienced());
 
             AddActiveConn(conn, ent); // make it active
         }
 
         trans->SetConnection(nullptr);
         rv = DispatchTransaction(ent, trans, conn);
     } else {
-        rv = TryDispatchTransaction(ent, !!trans->TunnelProvider(), trans);
+        pendingTransInfo = new PendingTransactionInfo(trans);
+        rv = TryDispatchTransaction(ent, !!trans->TunnelProvider(), pendingTransInfo);
     }
 
     if (NS_SUCCEEDED(rv)) {
         LOG(("  ProcessNewTransaction Dispatch Immediately trans=%p\n", trans));
         return rv;
     }
 
     if (rv == NS_ERROR_NOT_AVAILABLE) {
+        if (!pendingTransInfo) {
+            pendingTransInfo = new PendingTransactionInfo(trans);
+        }
         if (trans->Caps() & NS_HTTP_URGENT_START) {
             LOG(("  adding transaction to pending queue "
                  "[trans=%p urgent-start-count=%" PRIuSIZE "]\n",
                  trans, ent->mUrgentStartQ.Length()+1));
             // put this transaction on the urgent-start queue...
-            InsertTransactionSorted(ent->mUrgentStartQ, trans);
+            InsertTransactionSorted(ent->mUrgentStartQ, pendingTransInfo);
         } else {
             LOG(("  adding transaction to pending queue "
                  "[trans=%p pending-count=%" PRIuSIZE "]\n",
                  trans, ent->mPendingQ.Length()+1));
             // put this transaction on the pending queue...
-            InsertTransactionSorted(ent->mPendingQ, trans);
+            InsertTransactionSorted(ent->mPendingQ, pendingTransInfo);
         }
         return NS_OK;
     }
 
     LOG(("  ProcessNewTransaction Hard Error trans=%p rv=%" PRIx32 "\n",
          trans, static_cast<uint32_t>(rv)));
     return rv;
 }
@@ -1695,23 +1753,53 @@ nsHttpConnectionMgr::StartedConnect()
 
 void
 nsHttpConnectionMgr::RecvdConnect()
 {
     mNumActiveConns--;
     ConditionallyStopTimeoutTick();
 }
 
+void
+nsHttpConnectionMgr::ReleaseClaimedSockets(nsConnectionEntry *ent,
+                                           PendingTransactionInfo * pendingTransInfo)
+{
+    if (pendingTransInfo->mHalfOpen) {
+        RefPtr<nsHalfOpenSocket> halfOpen =
+            do_QueryReferent(pendingTransInfo->mHalfOpen);
+        if (halfOpen) {
+            if (halfOpen->Transaction() &&
+                halfOpen->Transaction()->IsNullTransaction()) {
+                LOG(("nsHttpConnectionMgr::ReleaseClaimedSockets - mark halfOpne %p "
+                    "speculative again.", halfOpen.get()));
+                halfOpen->SetSpeculative(true);
+            }
+        }
+        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();
+            nullTrans->Unclaim();
+            LOG(("nsHttpConnectionMgr::ReleaseClaimedSockets - mark %p unclaimed.",
+                 activeConn.get()));
+        }
+    }
+}
+
 nsresult
 nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
                                      nsAHttpTransaction *trans,
                                      uint32_t caps,
                                      bool speculative,
                                      bool isFromPredictor,
-                                     bool allow1918)
+                                     bool allow1918,
+                                     PendingTransactionInfo *pendingTransInfo)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     RefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans, caps);
     if (speculative) {
         sock->SetSpeculative(true);
         sock->SetAllow1918(allow1918);
         Telemetry::AutoCounter<Telemetry::HTTPCONNMGR_TOTAL_SPECULATIVE_CONN> totalSpeculativeConn;
@@ -1725,60 +1813,67 @@ nsHttpConnectionMgr::CreateTransport(nsC
     }
 
     // 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));
+    }
+
     ent->mHalfOpens.AppendElement(sock);
     mNumHalfOpenConns++;
     return NS_OK;
 }
 
 void
-nsHttpConnectionMgr::DispatchSpdyPendingQ(nsTArray<RefPtr<nsHttpTransaction>> &pendingQ,
+nsHttpConnectionMgr::DispatchSpdyPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
                                           nsConnectionEntry *ent,
                                           nsHttpConnection *conn)
 {
     if (pendingQ.Length() == 0) {
         return;
     }
 
-    nsTArray<RefPtr<nsHttpTransaction>> leftovers;
+    nsTArray<RefPtr<PendingTransactionInfo>> leftovers;
     uint32_t index;
     // Dispatch all the transactions we can
     for (index = 0;
          index < pendingQ.Length() && conn->CanDirectlyActivate();
          ++index) {
-        nsHttpTransaction *trans = pendingQ[index];
-
-        if (!(trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
-            trans->Caps() & NS_HTTP_DISALLOW_SPDY) {
-            leftovers.AppendElement(trans);
+        PendingTransactionInfo *pendingTransInfo = pendingQ[index];
+
+        if (!(pendingTransInfo->mTransaction->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
+            pendingTransInfo->mTransaction->Caps() & NS_HTTP_DISALLOW_SPDY) {
+            leftovers.AppendElement(pendingTransInfo);
             continue;
         }
 
-        nsresult rv = DispatchTransaction(ent, trans, conn);
+        nsresult rv = DispatchTransaction(ent, pendingTransInfo->mTransaction,
+                                          conn);
         if (NS_FAILED(rv)) {
             // this cannot happen, but if due to some bug it does then
             // close the transaction
             MOZ_ASSERT(false, "Dispatch SPDY Transaction");
             LOG(("ProcessSpdyPendingQ Dispatch Transaction failed trans=%p\n",
-                    trans));
-            trans->Close(rv);
+                 pendingTransInfo->mTransaction.get()));
+            pendingTransInfo->mTransaction->Close(rv);
         }
+        ReleaseClaimedSockets(ent, pendingTransInfo);
     }
 
     // Slurp up the rest of the pending queue into our leftovers bucket (we
     // might have some left if conn->CanDirectlyActivate returned false)
     for (; index < pendingQ.Length(); ++index) {
-        nsHttpTransaction *trans = pendingQ[index];
-        leftovers.AppendElement(trans);
+        PendingTransactionInfo *pendingTransInfo = pendingQ[index];
+        leftovers.AppendElement(pendingTransInfo);
     }
 
     // Put the leftovers back in the pending queue and get rid of the
     // transactions we dispatched
     leftovers.SwapElements(pendingQ);
     leftovers.Clear();
 }
 
@@ -1903,25 +1998,25 @@ nsHttpConnectionMgr::OnMsgShutdown(int32
         }
 
         // If all idle connections are removed we can stop pruning dead
         // connections.
         ConditionallyStopPruneDeadConnectionsTimer();
 
         // Close all urgentStart transactions.
         while (ent->mUrgentStartQ.Length()) {
-            nsHttpTransaction *trans = ent->mUrgentStartQ[0];
-            trans->Close(NS_ERROR_ABORT);
+            PendingTransactionInfo *pendingTransInfo = ent->mUrgentStartQ[0];
+            pendingTransInfo->mTransaction->Close(NS_ERROR_ABORT);
             ent->mUrgentStartQ.RemoveElementAt(0);
         }
 
         // Close all pending transactions.
         while (ent->mPendingQ.Length()) {
-            nsHttpTransaction *trans = ent->mPendingQ[0];
-            trans->Close(NS_ERROR_ABORT);
+            PendingTransactionInfo *pendingTransInfo = ent->mPendingQ[0];
+            pendingTransInfo->mTransaction->Close(NS_ERROR_ABORT);
             ent->mPendingQ.RemoveElementAt(0);
         }
 
         // Close all half open tcp connections.
         for (int32_t i = int32_t(ent->mHalfOpens.Length()) - 1; i >= 0; i--) {
             ent->mHalfOpens[i]->Abandon();
         }
 
@@ -1980,27 +2075,28 @@ nsHttpConnectionMgr::OnMsgReschedTransac
     RefPtr<nsHttpTransaction> trans = static_cast<nsHttpTransaction *>(param);
     trans->SetPriority(priority);
 
     nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
                                                    nullptr, trans);
 
     if (ent) {
         int32_t caps = trans->Caps();
-        nsTArray<RefPtr<nsHttpTransaction>> *pendingQ;
+        nsTArray<RefPtr<PendingTransactionInfo>> *pendingQ;
         if (caps & NS_HTTP_URGENT_START) {
             pendingQ = &(ent->mUrgentStartQ);
         } else {
             pendingQ = &(ent->mPendingQ);
         }
 
-        int32_t index = pendingQ->IndexOf(trans);
+        int32_t index = pendingQ->IndexOf(trans, 0, PendingComparator());
         if (index >= 0) {
+            RefPtr<PendingTransactionInfo> pendingTransInfo = (*pendingQ)[index];
             pendingQ->RemoveElementAt(index);
-            InsertTransactionSorted(*pendingQ, trans);
+            InsertTransactionSorted(*pendingQ, pendingTransInfo);
         }
     }
 }
 
 void
 nsHttpConnectionMgr::OnMsgCancelTransaction(int32_t reason, ARefBase *param)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -2021,42 +2117,53 @@ nsHttpConnectionMgr::OnMsgCancelTransact
         conn->CloseTransaction(trans, closeCode);
     } else {
         nsConnectionEntry *ent =
             LookupConnectionEntry(trans->ConnectionInfo(), nullptr, trans);
 
         if (ent) {
             uint32_t caps = trans->Caps();
             int32_t transIndex;
+            // We will abandon all half-open sockets belonging to the given
+            // transaction.
+            RefPtr<PendingTransactionInfo> pendingTransInfo;
             if (caps & NS_HTTP_URGENT_START) {
-                transIndex = ent->mUrgentStartQ.IndexOf(trans);
+                transIndex = ent->mUrgentStartQ.IndexOf(trans, 0,
+                                                        PendingComparator());
                 if (transIndex >=0) {
                     LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]"
                          " found in urgentStart queue\n", trans));
+                    pendingTransInfo = ent->mUrgentStartQ[transIndex];
+                    // We do not need to ReleaseClaimedSockets while we are
+                    // going to close them all any way!
                     ent->mUrgentStartQ.RemoveElementAt(transIndex);
                 }
             } else {
-                transIndex = ent->mPendingQ.IndexOf(trans);
+                transIndex = ent->mPendingQ.IndexOf(trans, 0,
+                                                    PendingComparator());
                 if (transIndex >=0) {
                     LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]"
                          " found in pending queue\n", trans));
+                    pendingTransInfo = ent->mPendingQ[transIndex];
+                    // We do not need to ReleaseClaimedSockets while we are
+                    // going to close them all any way!
                     ent->mPendingQ.RemoveElementAt(transIndex);
                 }
             }
 
             // Abandon all half-open sockets belonging to the given transaction.
-            for (uint32_t index = 0;
-                 index < ent->mHalfOpens.Length();
-                 ++index) {
-                nsHalfOpenSocket *half = ent->mHalfOpens[index];
-                if (trans == half->Transaction()) {
+            if (pendingTransInfo) {
+                RefPtr<nsHalfOpenSocket> half =
+                    do_QueryReferent(pendingTransInfo->mHalfOpen);
+                if (half) {
+                    MOZ_ASSERT(trans == half->Transaction() ||
+                               half->Transaction()->IsNullTransaction());
                     half->Abandon();
-                    // there is only one, and now mHalfOpens[] has been changed.
-                    break;
                 }
+                pendingTransInfo->mHalfOpen = nullptr;
             }
         }
 
         trans->Close(closeCode);
 
         // Cancel is a pretty strong signal that things might be hanging
         // so we want to cancel any null transactions related to this connection
         // entry. They are just optimizations, but they aren't hooked up to
@@ -2127,29 +2234,29 @@ nsHttpConnectionMgr::OnMsgCancelTransact
     LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p\n",
          ci->HashKey().get(), ent));
     if (!ent) {
         return;
     }
 
     for (uint32_t i = ent->mUrgentStartQ.Length(); i > 0;) {
         --i;
-        nsHttpTransaction *trans = ent->mUrgentStartQ[i];
+        PendingTransactionInfo *pendingTransInfo = ent->mUrgentStartQ[i];
         LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p %p\n",
-             ci->HashKey().get(), ent, trans));
-        trans->Close(reason);
+             ci->HashKey().get(), ent, pendingTransInfo->mTransaction.get()));
+        pendingTransInfo->mTransaction->Close(reason);
         ent->mUrgentStartQ.RemoveElementAt(i);
     }
 
     for (uint32_t i = ent->mPendingQ.Length(); i > 0;) {
         --i;
-        nsHttpTransaction *trans = ent->mPendingQ[i];
+        PendingTransactionInfo *pendingTransInfo = ent->mPendingQ[i];
         LOG(("nsHttpConnectionMgr::OnMsgCancelTransactions %s %p %p\n",
-             ci->HashKey().get(), ent, trans));
-        trans->Close(reason);
+             ci->HashKey().get(), ent, pendingTransInfo->mTransaction.get()));
+        pendingTransInfo->mTransaction->Close(reason);
         ent->mPendingQ.RemoveElementAt(i);
     }
 }
 
 void
 nsHttpConnectionMgr::OnMsgPruneDeadConnections(int32_t, ARefBase *)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
@@ -2734,17 +2841,18 @@ nsHttpConnectionMgr::OnMsgSpeculativeCon
     bool keepAlive = args->mTrans->Caps() & NS_HTTP_ALLOW_KEEPALIVE;
     if (mNumHalfOpenConns < parallelSpeculativeConnectLimit &&
         ((ignoreIdle && (ent->mIdleConns.Length() < parallelSpeculativeConnectLimit)) ||
          !ent->mIdleConns.Length()) &&
         !(keepAlive && RestrictConnections(ent)) &&
         !AtActiveConnectionLimit(ent, args->mTrans->Caps())) {
         DebugOnly<nsresult> rv = CreateTransport(ent, args->mTrans,
                                                  args->mTrans->Caps(), true,
-                                                 isFromPredictor, allow1918);
+                                                 isFromPredictor, allow1918,
+                                                 nullptr);
         MOZ_ASSERT(NS_SUCCEEDED(rv));
     } else {
         LOG(("OnMsgSpeculativeConnect Transport "
              "not created due to existing connection count\n"));
     }
 }
 
 bool
@@ -2768,22 +2876,32 @@ ConnectionHandle::DontReuse()
 nsresult
 ConnectionHandle::PushBack(const char *buf, uint32_t bufLen)
 {
     return mConn->PushBack(buf, bufLen);
 }
 
 
 //////////////////////// nsHalfOpenSocket
-
-NS_IMPL_ISUPPORTS(nsHttpConnectionMgr::nsHalfOpenSocket,
-                  nsIOutputStreamCallback,
-                  nsITransportEventSink,
-                  nsIInterfaceRequestor,
-                  nsITimerCallback)
+NS_IMPL_ADDREF(nsHttpConnectionMgr::nsHalfOpenSocket)
+NS_IMPL_RELEASE(nsHttpConnectionMgr::nsHalfOpenSocket)
+
+NS_INTERFACE_MAP_BEGIN(nsHttpConnectionMgr::nsHalfOpenSocket)
+    NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+    NS_INTERFACE_MAP_ENTRY(nsIOutputStreamCallback)
+    NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
+    NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+    NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+    // we have no macro that covers this case.
+    if (aIID.Equals(NS_GET_IID(nsHttpConnectionMgr::nsHalfOpenSocket)) ) {
+        AddRef();
+        *aInstancePtr = this;
+        return NS_OK;
+    } else
+NS_INTERFACE_MAP_END
 
 nsHttpConnectionMgr::
 nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
                                    nsAHttpTransaction *trans,
                                    uint32_t caps)
     : mEnt(ent)
     , mTransaction(trans)
     , mDispatchedMTransaction(false)
@@ -3182,33 +3300,36 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
     // This half-open socket has created a connection.  This flag excludes it
     // from counter of actual connections used for checking limits.
     mHasConnected = true;
 
     // if this is still in the pending list, remove it and dispatch it
     uint32_t caps = mTransaction->Caps();
     int32_t index;
     if (caps & NS_HTTP_URGENT_START) {
-        index = mEnt->mUrgentStartQ.IndexOf(mTransaction);
+        index = mEnt->mUrgentStartQ.IndexOf(mTransaction, 0,
+                                            PendingComparator());
     } else {
-        index = mEnt->mPendingQ.IndexOf(mTransaction);
+        index = mEnt->mPendingQ.IndexOf(mTransaction, 0, PendingComparator());
     }
     if (index > -1) {
         MOZ_ASSERT(!mSpeculative,
                    "Speculative Half Open found mTransaction");
-        RefPtr<nsHttpTransaction> temp;
+        RefPtr<PendingTransactionInfo> temp;
         if (caps & NS_HTTP_URGENT_START) {
             temp = mEnt->mUrgentStartQ[index];
             mEnt->mUrgentStartQ.RemoveElementAt(index);
          } else {
             temp = mEnt->mPendingQ[index];
             mEnt->mPendingQ.RemoveElementAt(index);
         }
         gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
-        rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, temp, conn);
+        rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt,
+                                                          temp->mTransaction,
+                                                          conn);
     } else {
         // this transaction was dispatched off the pending q before all the
         // sockets established themselves.
 
         // After about 1 second allow for the possibility of restarting a
         // transaction due to server close. Keep at sub 1 second as that is the
         // minimum granularity we can expect a server to be timing out with.
         conn->SetIsReusedAfter(950);
@@ -3253,18 +3374,31 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
 NS_IMETHODIMP
 nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
                                                          nsresult status,
                                                          int64_t progress,
                                                          int64_t progressMax)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
-    if (mTransaction)
-        mTransaction->OnTransportStatus(trans, status, progress);
+    if (mTransaction) {
+        if ((trans == mSocketTransport) ||
+            ((trans == mBackupTransport) && (status == NS_NET_STATUS_CONNECTED_TO) &&
+            mEnt->mPendingQ.Contains(mTransaction, PendingComparator()))) {
+            // Send this status event only if the transaction is still panding,
+            // i.e. it has not found a free already connected socket.
+            // Sockets in halfOpen state can only get following events:
+            // NS_NET_STATUS_RESOLVING_HOST, NS_NET_STATUS_RESOLVED_HOST,
+            // NS_NET_STATUS_CONNECTING_TO and NS_NET_STATUS_CONNECTED_TO.
+            // mBackupTransport is only started after
+            // NS_NET_STATUS_CONNECTING_TO of mSocketTransport, so ignore all
+            // mBackupTransport events until NS_NET_STATUS_CONNECTED_TO.
+            mTransaction->OnTransportStatus(trans, status, progress);
+        }
+    }
 
     MOZ_ASSERT(trans == mSocketTransport || trans == mBackupTransport);
     if (status == NS_NET_STATUS_CONNECTED_TO) {
         if (trans == mSocketTransport) {
             mPrimaryConnectedOK = true;
         } else {
             mBackupConnectedOK = true;
         }
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -13,28 +13,33 @@
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsAutoPtr.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Attributes.h"
 #include "AlternateServices.h"
 #include "ARefBase.h"
+#include "nsWeakReference.h"
 
 #include "nsIObserver.h"
 #include "nsITimer.h"
 
 class nsIHttpUpgradeListener;
 
 namespace mozilla {
 namespace net {
 class EventTokenBucket;
 class NullHttpTransaction;
 struct HttpRetParams;
 
+// 8d411b53-54bc-4a99-8b78-ff125eab1564
+#define NS_HALFOPENSOCKET_IID \
+{ 0x8d411b53, 0x54bc, 0x4a99, {0x8b, 0x78, 0xff, 0x12, 0x5e, 0xab, 0x15, 0x64 }}
+
 //-----------------------------------------------------------------------------
 
 // message handlers have this signature
 class nsHttpConnectionMgr;
 typedef void (nsHttpConnectionMgr:: *nsConnEventHandler)(int32_t, ARefBase *);
 
 class nsHttpConnectionMgr final : public nsIObserver
                                 , public AltSvcCache
@@ -195,32 +200,33 @@ public:
     void ActivateTimeoutTick();
 
     nsresult UpdateCurrentTopLevelOuterContentWindowId(uint64_t aWindowId);
 
 private:
     virtual ~nsHttpConnectionMgr();
 
     class nsHalfOpenSocket;
+    class PendingTransactionInfo;
 
     // nsConnectionEntry
     //
     // mCT maps connection info hash key to nsConnectionEntry object, which
     // contains list of active and idle connections as well as the list of
     // pending transactions.
     //
     class nsConnectionEntry
     {
     public:
         explicit nsConnectionEntry(nsHttpConnectionInfo *ci);
         ~nsConnectionEntry();
 
         RefPtr<nsHttpConnectionInfo> mConnInfo;
-        nsTArray<RefPtr<nsHttpTransaction> > mUrgentStartQ;// the urgent start transaction queue
-        nsTArray<RefPtr<nsHttpTransaction> > mPendingQ;    // pending transaction queue
+        nsTArray<RefPtr<PendingTransactionInfo> > mUrgentStartQ;// the urgent start transaction queue
+        nsTArray<RefPtr<PendingTransactionInfo> > mPendingQ;    // pending transaction queue
         nsTArray<RefPtr<nsHttpConnection> >  mActiveConns; // active connections
         nsTArray<RefPtr<nsHttpConnection> >  mIdleConns;   // idle persistent connections
         nsTArray<nsHalfOpenSocket*>  mHalfOpens;   // half open connections
 
         bool AvailableForDispatchNow();
 
         // calculate the number of half open sockets that have not had at least 1
         // connection complete
@@ -271,21 +277,23 @@ public:
 private:
 
     // nsHalfOpenSocket is used to hold the state of an opening TCP socket
     // while we wait for it to establish and bind it to a connection
 
     class nsHalfOpenSocket final : public nsIOutputStreamCallback,
                                    public nsITransportEventSink,
                                    public nsIInterfaceRequestor,
-                                   public nsITimerCallback
+                                   public nsITimerCallback,
+                                   public nsSupportsWeakReference
     {
         ~nsHalfOpenSocket();
 
     public:
+        NS_DECLARE_STATIC_IID_ACCESSOR(NS_HALFOPENSOCKET_IID)
         NS_DECL_THREADSAFE_ISUPPORTS
         NS_DECL_NSIOUTPUTSTREAMCALLBACK
         NS_DECL_NSITRANSPORTEVENTSINK
         NS_DECL_NSIINTERFACEREQUESTOR
         NS_DECL_NSITIMERCALLBACK
 
         nsHalfOpenSocket(nsConnectionEntry *ent,
                          nsAHttpTransaction *trans,
@@ -356,16 +364,45 @@ private:
         // connection process. It may have completed unsuccessfully.
         bool                           mHasConnected;
 
         bool                           mPrimaryConnectedOK;
         bool                           mBackupConnectedOK;
     };
     friend class nsHalfOpenSocket;
 
+    class PendingTransactionInfo : public ARefBase
+    {
+    public:
+        explicit PendingTransactionInfo(nsHttpTransaction * trans)
+            : mTransaction(trans)
+        {}
+
+        NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PendingTransactionInfo)
+
+        void PrintDiagnostics(nsCString &log);
+    public: // meant to be public.
+        RefPtr<nsHttpTransaction> mTransaction;
+        nsWeakPtr mHalfOpen;
+        nsWeakPtr mActiveConn;
+
+    private:
+        virtual ~PendingTransactionInfo() {}
+    };
+    friend class PendingTransactionInfo;
+
+    class PendingComparator
+    {
+    public:
+        bool Equals(const PendingTransactionInfo *aPendingTrans,
+                    const nsAHttpTransaction *aTrans) const {
+            return aPendingTrans->mTransaction.get() == aTrans;
+        }
+    };
+
     //-------------------------------------------------------------------------
     // NOTE: these members may be accessed from any thread (use mReentrantMonitor)
     //-------------------------------------------------------------------------
 
     ReentrantMonitor    mReentrantMonitor;
     nsCOMPtr<nsIEventTarget>     mSocketThreadTarget;
 
     // connection limits
@@ -377,64 +414,74 @@ private:
     Atomic<bool, mozilla::Relaxed> mIsShuttingDown;
 
     //-------------------------------------------------------------------------
     // NOTE: these members are only accessed on the socket transport thread
     //-------------------------------------------------------------------------
 
     MOZ_MUST_USE bool ProcessPendingQForEntry(nsConnectionEntry *,
                                               bool considerAll);
-    void DispatchPendingQ(nsTArray<RefPtr<nsHttpTransaction>> &pendingQ,
+    void DispatchPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
                                    nsConnectionEntry *ent,
                                    bool &dispatchedSuccessfully,
                                    bool considerAll);
     bool     AtActiveConnectionLimit(nsConnectionEntry *, uint32_t caps);
     MOZ_MUST_USE nsresult TryDispatchTransaction(nsConnectionEntry *ent,
                                                  bool onlyReusedConnection,
-                                                 nsHttpTransaction *trans);
+                                                 PendingTransactionInfo *pendingTransInfo);
     MOZ_MUST_USE nsresult DispatchTransaction(nsConnectionEntry *,
                                               nsHttpTransaction *,
                                               nsHttpConnection *);
     MOZ_MUST_USE nsresult DispatchAbstractTransaction(nsConnectionEntry *,
                                                       nsAHttpTransaction *,
                                                       uint32_t,
                                                       nsHttpConnection *,
                                                       int32_t);
     bool     RestrictConnections(nsConnectionEntry *);
     MOZ_MUST_USE nsresult ProcessNewTransaction(nsHttpTransaction *);
     MOZ_MUST_USE nsresult EnsureSocketThreadTarget();
     void     ClosePersistentConnections(nsConnectionEntry *ent);
     void     ReportProxyTelemetry(nsConnectionEntry *ent);
     MOZ_MUST_USE nsresult CreateTransport(nsConnectionEntry *,
                                           nsAHttpTransaction *, uint32_t, bool,
-                                          bool, bool);
+                                          bool, bool,
+                                          PendingTransactionInfo *pendingTransInfo);
     void     AddActiveConn(nsHttpConnection *, nsConnectionEntry *);
     void     DecrementActiveConnCount(nsHttpConnection *);
     void     StartedConnect();
     void     RecvdConnect();
 
+    // This function will unclaim the claimed connection or set a halfOpen
+    // socket to the speculative state if the transaction claiming them ends up
+    // using another connection.
+    void ReleaseClaimedSockets(nsConnectionEntry *ent,
+                               PendingTransactionInfo * pendingTransInfo);
+
+    void InsertTransactionSorted(nsTArray<RefPtr<PendingTransactionInfo> > &pendingQ,
+                                 PendingTransactionInfo *pendingTransInfo);
+
     nsConnectionEntry *GetOrCreateConnectionEntry(nsHttpConnectionInfo *,
                                                   bool allowWildCard);
 
     MOZ_MUST_USE nsresult MakeNewConnection(nsConnectionEntry *ent,
-                                            nsHttpTransaction *trans);
+                                            PendingTransactionInfo *pendingTransInfo);
 
     // Manage the preferred spdy connection entry for this address
     nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry);
     nsConnectionEntry *LookupPreferredHash(nsConnectionEntry *ent);
     void               StorePreferredHash(nsConnectionEntry *ent);
     void               RemovePreferredHash(nsConnectionEntry *ent);
     nsHttpConnection  *GetSpdyPreferredConn(nsConnectionEntry *ent);
     nsDataHashtable<nsCStringHashKey, nsConnectionEntry *>   mSpdyPreferredHash;
     nsConnectionEntry *LookupConnectionEntry(nsHttpConnectionInfo *ci,
                                              nsHttpConnection *conn,
                                              nsHttpTransaction *trans);
 
     void               ProcessSpdyPendingQ(nsConnectionEntry *ent);
-    void               DispatchSpdyPendingQ(nsTArray<RefPtr<nsHttpTransaction>> &pendingQ,
+    void               DispatchSpdyPendingQ(nsTArray<RefPtr<PendingTransactionInfo>> &pendingQ,
                                             nsConnectionEntry *ent,
                                             nsHttpConnection *conn);
     // used to marshall events to the socket transport thread.
     MOZ_MUST_USE nsresult PostEvent(nsConnEventHandler  handler,
                                     int32_t             iparam = 0,
                                     ARefBase            *vparam = nullptr);
 
     // message handlers
@@ -499,12 +546,14 @@ private:
 
     // For diagnostics
     void OnMsgPrintDiagnostics(int32_t, ARefBase *);
 
     nsCString mLogData;
     uint64_t mCurrentTopLevelOuterContentWindowId;
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(nsHttpConnectionMgr::nsHalfOpenSocket, NS_HALFOPENSOCKET_IID)
+
 } // namespace net
 } // namespace mozilla
 
 #endif // !nsHttpConnectionMgr_h__