diff --git a/netwerk/base/public/nsISpeculativeConnect.idl b/netwerk/base/public/nsISpeculativeConnect.idl index 7bf6ceb5d5d..4ef559d6337 100644 --- a/netwerk/base/public/nsISpeculativeConnect.idl +++ b/netwerk/base/public/nsISpeculativeConnect.idl @@ -35,7 +35,7 @@ interface nsISpeculativeConnect : nsISupports * inline) to determine whether or not to actually make a speculative * connection. */ -[builtinclass, uuid(1040ebe3-6ed1-45a6-8587-995e082518d7)] +[builtinclass, uuid(f6a0d1e5-369f-4abc-81ae-d370d36e4006)] interface nsISpeculativeConnectionOverrider : nsISupports { /** @@ -44,6 +44,13 @@ interface nsISpeculativeConnectionOverrider : nsISupports */ [infallible] readonly attribute unsigned long parallelSpeculativeConnectLimit; + /** + * Used to loosen the restrictions nsHttpConnectionMgr::RestrictConnections + * to allow more speculative connections when we're unsure if a host will + * connect via SPDY or not. + */ + [infallible] readonly attribute boolean ignorePossibleSpdyConnections; + /** * Used to determine if we will ignore the existence of any currently idle * connections when we decide whether or not to make a speculative diff --git a/netwerk/base/src/Predictor.cpp b/netwerk/base/src/Predictor.cpp index 4a4d277a390..e827bd5e875 100644 --- a/netwerk/base/src/Predictor.cpp +++ b/netwerk/base/src/Predictor.cpp @@ -373,6 +373,13 @@ Predictor::GetIgnoreIdle(bool *ignoreIdle) return NS_OK; } +NS_IMETHODIMP +Predictor::GetIgnorePossibleSpdyConnections(bool *ignorePossibleSpdyConnections) +{ + *ignorePossibleSpdyConnections = true; + return NS_OK; +} + NS_IMETHODIMP Predictor::GetParallelSpeculativeConnectLimit( uint32_t *parallelSpeculativeConnectLimit) diff --git a/netwerk/protocol/http/AlternateServices.cpp b/netwerk/protocol/http/AlternateServices.cpp index 1dfb4063236..8d82cd5c517 100644 --- a/netwerk/protocol/http/AlternateServices.cpp +++ b/netwerk/protocol/http/AlternateServices.cpp @@ -413,6 +413,13 @@ AltSvcOverride::GetIgnoreIdle(bool *ignoreIdle) return NS_OK; } +NS_IMETHODIMP +AltSvcOverride::GetIgnorePossibleSpdyConnections(bool *ignorePossibleSpdyConnections) +{ + *ignorePossibleSpdyConnections = true; + return NS_OK; +} + NS_IMETHODIMP AltSvcOverride::GetParallelSpeculativeConnectLimit( uint32_t *parallelSpeculativeConnectLimit) diff --git a/netwerk/protocol/http/ConnectionDiagnostics.cpp b/netwerk/protocol/http/ConnectionDiagnostics.cpp index 48c917f29d3..df490de10de 100644 --- a/netwerk/protocol/http/ConnectionDiagnostics.cpp +++ b/netwerk/protocol/http/ConnectionDiagnostics.cpp @@ -72,8 +72,8 @@ nsHttpConnectionMgr::PrintDiagnosticsCB(const nsACString &key, ent->mHalfOpens.Length()); self->mLogData.AppendPrintf(" Coalescing Key = %s\n", ent->mCoalescingKey.get()); - self->mLogData.AppendPrintf(" Spdy using = %d, preferred = %d\n", - ent->mUsingSpdy, ent->mSpdyPreferred); + self->mLogData.AppendPrintf(" Spdy using = %d, tested = %d, preferred = %d\n", + ent->mUsingSpdy, ent->mTestedSpdy, ent->mSpdyPreferred); self->mLogData.AppendPrintf(" pipelinestate = %d penalty = %d\n", ent->mPipelineState, ent->mPipeliningPenalty); for (i = 0; i < nsAHttpTransaction::CLASS_MAX; ++i) { diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp index 5858646f556..cc9ffdb02e0 100644 --- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -159,7 +159,6 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion) mUsingSpdyVersion = spdyVersion; mEverUsedSpdy = true; - mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport); if (!mReportedSpdy) { mReportedSpdy = true; @@ -210,6 +209,7 @@ nsHttpConnection::StartSpdy(uint8_t spdyVersion) mProxyConnectInProgress = false; } + mSpdySession = ASpdySession::NewSpdySession(spdyVersion, mSocketTransport); bool spdyProxy = mConnInfo->UsingHttpsProxy() && !mTLSFilter; if (spdyProxy) { nsRefPtr wildCardProxyCi; diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index 9386f504986..6f3b2ce0299 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -380,6 +380,7 @@ public: // intentional! bool mOverridesOK; uint32_t mParallelSpeculativeConnectLimit; bool mIgnoreIdle; + bool mIgnorePossibleSpdyConnections; bool mIsFromPredictor; bool mAllow1918; @@ -436,6 +437,8 @@ nsHttpConnectionMgr::SpeculativeConnect(nsHttpConnectionInfo *ci, overrider->GetParallelSpeculativeConnectLimit( &args->mParallelSpeculativeConnectLimit); overrider->GetIgnoreIdle(&args->mIgnoreIdle); + overrider->GetIgnorePossibleSpdyConnections( + &args->mIgnorePossibleSpdyConnections); overrider->GetIsFromPredictor(&args->mIsFromPredictor); overrider->GetAllow1918(&args->mAllow1918); } @@ -652,6 +655,8 @@ nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn, if (!ent) return; + ent->mTestedSpdy = true; + if (!usingSpdy) return; @@ -672,11 +677,11 @@ nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn, nsConnectionEntry *preferred = mSpdyPreferredHash.Get(ent->mCoalescingKey); - LOG(("ReportSpdyConnection conn=%p %s %s ent=%p preferred=%p\n",conn, - ent->mConnInfo->Host(), ent->mCoalescingKey.get(), ent, preferred)); + LOG(("ReportSpdyConnection %s %s ent=%p preferred=%p\n", + ent->mConnInfo->Host(), ent->mCoalescingKey.get(), + ent, preferred)); if (!preferred) { - preferred = ent; if (!ent->mCoalescingKey.IsEmpty()) { mSpdyPreferredHash.Put(ent->mCoalescingKey, ent); ent->mSpdyPreferred = true; @@ -691,7 +696,8 @@ nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn, // new transactions migrate over. LOG(("ReportSpdyConnection graceful close of conn=%p ent=%p to " - "migrate to preferred (desharding)\n", conn, ent)); + "migrate to preferred\n", conn, ent)); + conn->DontReuse(); } else if (preferred != ent) { LOG (("ReportSpdyConnection preferred host may be in false start or " @@ -699,35 +705,6 @@ nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn, "abandon this connection yet.")); } - if ((preferred == ent) && conn->CanDirectlyActivate()) { - // this is a new spdy connection to the preferred entry - - // Cancel any other pending connections - their associated transactions - // are in the pending queue and will be dispatched onto this connection - for (int32_t index = ent->mHalfOpens.Length() - 1; - index >= 0; --index) { - // this will shorten mHalfOpens by one at the tail - ent->mHalfOpens[index]->Abandon(); - } - - if (ent->mActiveConns.Length() > 1) { - // this is a new connection to an established preferred spdy host. - // if there is more than 1 live and established spdy connection (e.g. - // some could still be handshaking, shutting down, etc..) then close - // this one down after any transactions that are on it are complete. - // This probably happened due to the parallel connection algorithm - // that is used only before the host is known to speak spdy. - for (uint32_t index = 0; index < ent->mActiveConns.Length(); ++index) { - nsHttpConnection *otherConn = ent->mActiveConns[index]; - if (otherConn != conn) { - LOG(("ReportSpdyConnection shutting down connection (%p) because new " - "spdy connection (%p) takes precedence\n", otherConn, conn)); - otherConn->DontReuse(); - } - } - } - } - ProcessPendingQ(ent->mConnInfo); PostEvent(&nsHttpConnectionMgr::OnMsgProcessAllSpdyPendingQ); } @@ -1050,7 +1027,9 @@ nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key, ent->mActiveConns.Length() == 0 && ent->mHalfOpens.Length() == 0 && ent->mPendingQ.Length() == 0 && - (!ent->mUsingSpdy || self->mCT.Count() > 300)) { + ((!ent->mTestedSpdy && !ent->mUsingSpdy) || + !gHttpHandler->IsSpdyEnabled() || + self->mCT.Count() > 300)) { LOG((" removing empty connection entry\n")); return PL_DHASH_REMOVE; } @@ -1430,7 +1409,8 @@ nsHttpConnectionMgr::ClosePersistentConnectionsCB(const nsACString &key, } bool -nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent) +nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent, + bool ignorePossibleSpdyConnections) { MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread); @@ -1440,7 +1420,8 @@ nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent) bool doRestrict = ent->mConnInfo->FirstHopSSL() && gHttpHandler->IsSpdyEnabled() && - ent->mUsingSpdy && + ((!ent->mTestedSpdy && !ignorePossibleSpdyConnections) || + ent->mUsingSpdy) && (ent->mHalfOpens.Length() || ent->mActiveConns.Length()); // If there are no restrictions, we are done @@ -1449,9 +1430,8 @@ nsHttpConnectionMgr::RestrictConnections(nsConnectionEntry *ent) // If the restriction is based on a tcp handshake in progress // let that connect and then see if it was SPDY or not - if (ent->UnconnectedHalfOpens()) { + if (ent->UnconnectedHalfOpens() && !ignorePossibleSpdyConnections) return true; - } // There is a concern that a host is using a mix of HTTP/1 and SPDY. // In that case we don't want to restrict connections just because @@ -2976,12 +2956,14 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param) uint32_t parallelSpeculativeConnectLimit = gHttpHandler->ParallelSpeculativeConnectLimit(); + bool ignorePossibleSpdyConnections = false; bool ignoreIdle = false; bool isFromPredictor = false; bool allow1918 = false; if (args->mOverridesOK) { parallelSpeculativeConnectLimit = args->mParallelSpeculativeConnectLimit; + ignorePossibleSpdyConnections = args->mIgnorePossibleSpdyConnections; ignoreIdle = args->mIgnoreIdle; isFromPredictor = args->mIsFromPredictor; allow1918 = args->mAllow1918; @@ -2991,7 +2973,7 @@ nsHttpConnectionMgr::OnMsgSpeculativeConnect(int32_t, void *param) if (mNumHalfOpenConns < parallelSpeculativeConnectLimit && ((ignoreIdle && (ent->mIdleConns.Length() < parallelSpeculativeConnectLimit)) || !ent->mIdleConns.Length()) && - !(keepAlive && RestrictConnections(ent)) && + !(keepAlive && RestrictConnections(ent, ignorePossibleSpdyConnections)) && !AtActiveConnectionLimit(ent, args->mTrans->Caps())) { CreateTransport(ent, args->mTrans, args->mTrans->Caps(), true, isFromPredictor, allow1918); } @@ -3355,9 +3337,8 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out) mStreamOut = nullptr; mStreamIn = nullptr; mSocketTransport = nullptr; - } else { - MOZ_ASSERT(!mTransaction->IsNullTransaction(), - "null transactions dont have backup timers"); + } + else { TimeDuration rtt = TimeStamp::Now() - mBackupSynStarted; rv = conn->Init(mEnt->mConnInfo, gHttpHandler->ConnMgr()->mMaxRequestDelay, @@ -3587,6 +3568,7 @@ nsConnectionEntry::nsConnectionEntry(nsHttpConnectionInfo *ci) , mPipeliningPenalty(0) , mSpdyCWND(0) , mUsingSpdy(false) + , mTestedSpdy(false) , mSpdyPreferred(false) , mPreferIPv4(false) , mPreferIPv6(false) @@ -3988,6 +3970,7 @@ nsHttpConnectionMgr::MoveToWildCardConnEntry(nsHttpConnectionInfo *specificCI, return; } wcEnt->mUsingSpdy = true; + wcEnt->mTestedSpdy = true; LOG(("nsHttpConnectionMgr::MakeConnEntryWildCard ent %p " "idle=%d active=%d half=%d pending=%d\n", ent, diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h index bc5ad0aab65..1fd8bff14e2 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.h +++ b/netwerk/protocol/http/nsHttpConnectionMgr.h @@ -376,6 +376,12 @@ private: // connection is currently using spdy. bool mUsingSpdy; + // mTestedSpdy is set after NPN negotiation has occurred and we know + // with confidence whether a host speaks spdy or not (which is reflected + // in mUsingSpdy). Before mTestedSpdy is set, handshake parallelism is + // minimized so that we can multiplex on a single spdy connection. + bool mTestedSpdy; + bool mSpdyPreferred; // Flags to remember our happy-eyeballs decision. @@ -559,7 +565,7 @@ private: nsresult BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **); - bool RestrictConnections(nsConnectionEntry *); + bool RestrictConnections(nsConnectionEntry *, bool = false); nsresult ProcessNewTransaction(nsHttpTransaction *); nsresult EnsureSocketThreadTarget(); void ClosePersistentConnections(nsConnectionEntry *ent);