bug 708415 spdy code review of nshttp* r=honzab

This commit is contained in:
Patrick McManus 2012-01-26 00:15:26 -05:00
parent f9f63d758e
commit b67c1b14d9
4 changed files with 109 additions and 80 deletions

View File

@ -4131,7 +4131,7 @@ nsHttpChannel::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
mSecurityInfo = mTransaction->SecurityInfo();
}
if (gHttpHandler->IsSpdyEnabled() && !mCachePump && NS_FAILED(mStatus) &&
if (!mCachePump && NS_FAILED(mStatus) &&
(mLoadFlags & LOAD_REPLACE) && mOriginalURI && mAllowSpdy) {
// For sanity's sake we may want to cancel an alternate protocol
// redirection involving the original host name

View File

@ -178,8 +178,13 @@ nsHttpConnection::EnsureNPNComplete()
// If for some reason the components to check on NPN aren't available,
// this function will just return true to continue on and disable SPDY
NS_ABORT_IF_FALSE(mSocketTransport, "EnsureNPNComplete "
"socket transport precondition");
if (!mSocketTransport) {
// this cannot happen
NS_ABORT_IF_FALSE(false,
"EnsureNPNComplete socket transport precondition");
mNPNComplete = true;
return true;
}
if (mNPNComplete)
return true;
@ -220,7 +225,12 @@ nsHttpConnection::EnsureNPNComplete()
if (negotiatedNPN.Equals(NS_LITERAL_CSTRING("spdy/2"))) {
mUsingSpdy = true;
mEverUsedSpdy = true;
mIsReused = true; /* all spdy streams are reused */
// Setting the connection as reused allows some transactions that fail
// with NS_ERROR_NET_RESET to be restarted and SPDY uses that code
// to handle clean rejections (such as those that arrived after
// a server goaway was generated).
mIsReused = true;
// Wrap the old http transaction into the new spdy session
// as the first stream
@ -440,7 +450,7 @@ nsHttpConnection::DontReuse()
mKeepAliveMask = false;
mKeepAlive = false;
mIdleTimeout = 0;
if (mUsingSpdy)
if (mSpdySession)
mSpdySession->DontReuse();
}
@ -974,10 +984,9 @@ nsHttpConnection::OnSocketWritable()
n = 0;
}
else {
if (gHttpHandler->IsSpdyEnabled() && !mReportedSpdy) {
if (!mReportedSpdy) {
mReportedSpdy = true;
gHttpHandler->ConnMgr()->
ReportSpdyConnection(this, mUsingSpdy);
gHttpHandler->ConnMgr()->ReportSpdyConnection(this, mUsingSpdy);
}
LOG((" writing transaction request stream\n"));

View File

@ -99,6 +99,7 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
LOG(("Creating nsHttpConnectionMgr @%x\n", this));
mCT.Init();
mAlternateProtocolHash.Init(16);
mSpdyPreferredHash.Init();
}
nsHttpConnectionMgr::~nsHttpConnectionMgr()
@ -145,7 +146,6 @@ nsHttpConnectionMgr::Init(PRUint16 maxConns,
{
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mSpdyPreferredHash.Init();
mMaxConns = maxConns;
mMaxConnsPerHost = maxConnsPerHost;
@ -417,8 +417,7 @@ nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
// If there is no sign of coalescing (or it is disabled) then just
// return the primary hash lookup
if (!gHttpHandler->IsSpdyEnabled() || !gHttpHandler->CoalesceSpdy() ||
!ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty())
if (!ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty())
return ent;
// If there is no preferred coalescing entry for this host (or the
@ -467,6 +466,13 @@ nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
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
nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
bool usingSpdy)
@ -482,11 +488,8 @@ nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
ent->mTestedSpdy = true;
if (!usingSpdy) {
if (ent->mUsingSpdy)
conn->DontReuse();
if (!usingSpdy)
return;
}
ent->mUsingSpdy = true;
@ -496,35 +499,47 @@ nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
PruneDeadConnectionsAfter(ttl);
// Lookup preferred directly from the hash instead of using
// GetSpdyPreferred() because we want to avoid the cert compatibility
// GetSpdyPreferredEnt() because we want to avoid the cert compatibility
// check at this point because the cert is never part of the hash
// lookup. Filtering on that has to be done at the time of use
// rather than the time of registration (i.e. now).
nsConnectionEntry *preferred =
mSpdyPreferredHash.Get(ent->mCoalescingKey);
LOG(("ReportSpdyConnection %s %s ent=%p ispreferred=%d\n",
LOG(("ReportSpdyConnection %s %s ent=%p preferred=%p\n",
ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
ent, preferred));
if (!preferred) {
ent->mSpdyPreferred = true;
SetSpdyPreferred(ent);
preferred = ent;
if (!ent->mCoalescingKey.IsEmpty()) {
mSpdyPreferredHash.Put(ent->mCoalescingKey, ent);
ent->mSpdyPreferred = true;
preferred = ent;
}
}
else if (preferred != ent) {
// A different hostname is the preferred spdy host for this
// IP address.
ent->mUsingSpdy = true;
// IP address. That preferred mapping must have been setup while
// this connection was negotiating NPN.
// Call don't reuse on the current connection to shut it down as soon
// as possible without causing any errors.
// i.e. the current transaction(s) on this connection will be processed
// normally, but then it will go away and future connections will be
// coalesced through the preferred entry.
conn->DontReuse();
}
ProcessSpdyPendingQ();
ProcessAllSpdyPendingQ();
}
bool
nsHttpConnectionMgr::GetSpdyAlternateProtocol(nsACString &hostPortKey)
{
if (!gHttpHandler->UseAlternateProtocol())
return false;
// The Alternate Protocol hash is protected under the monitor because
// it is read from both the main and the network thread.
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
@ -589,7 +604,7 @@ nsHttpConnectionMgr::TrimAlternateProtocolHash(PLDHashTable *table,
}
nsHttpConnectionMgr::nsConnectionEntry *
nsHttpConnectionMgr::GetSpdyPreferred(nsConnectionEntry *aOriginalEntry)
nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
{
if (!gHttpHandler->IsSpdyEnabled() ||
!gHttpHandler->CoalesceSpdy() ||
@ -626,7 +641,7 @@ nsHttpConnectionMgr::GetSpdyPreferred(nsConnectionEntry *aOriginalEntry)
// remove the preferred status of this entry if it cannot be
// used for pooling.
preferred->mSpdyPreferred = false;
RemoveSpdyPreferred(preferred->mCoalescingKey);
RemoveSpdyPreferredEnt(preferred->mCoalescingKey);
LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
"preferred host mapping %s to %s removed due to inactivity.\n",
aOriginalEntry->mConnInfo->Host(),
@ -644,12 +659,16 @@ nsHttpConnectionMgr::GetSpdyPreferred(nsConnectionEntry *aOriginalEntry)
nsCAutoString negotiatedNPN;
activeSpdy->GetSecurityInfo(getter_AddRefs(securityInfo));
if (!securityInfo)
if (!securityInfo) {
NS_WARNING("cannot obtain spdy security info");
return nsnull;
}
sslSocketControl = do_QueryInterface(securityInfo, &rv);
if (NS_FAILED(rv))
if (NS_FAILED(rv)) {
NS_WARNING("sslSocketControl QI Failed");
return nsnull;
}
rv = sslSocketControl->JoinConnection(NS_LITERAL_CSTRING("spdy/2"),
aOriginalEntry->mConnInfo->GetHost(),
@ -659,8 +678,9 @@ nsHttpConnectionMgr::GetSpdyPreferred(nsConnectionEntry *aOriginalEntry)
if (NS_FAILED(rv) || !isJoined) {
LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
"Host %s cannot be confirmed to be joined "
"with %s connections",
preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host()));
"with %s connections. rv=%x isJoined=%d",
preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
rv, isJoined));
mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_JOIN,
false);
return nsnull;
@ -668,30 +688,17 @@ nsHttpConnectionMgr::GetSpdyPreferred(nsConnectionEntry *aOriginalEntry)
// IP pooling confirmed
LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
"Host %s has cert valid for %s connections",
preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host()));
"Host %s has cert valid for %s connections, "
"so %s will be coalesced with %s",
preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
aOriginalEntry->mConnInfo->Host(), preferred->mConnInfo->Host()));
mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_JOIN, true);
return preferred;
}
void
nsHttpConnectionMgr::SetSpdyPreferred(nsConnectionEntry *ent)
nsHttpConnectionMgr::RemoveSpdyPreferredEnt(nsACString &aHashKey)
{
if (!gHttpHandler->CoalesceSpdy())
return;
if (ent->mCoalescingKey.IsEmpty())
return;
mSpdyPreferredHash.Put(ent->mCoalescingKey, ent);
}
void
nsHttpConnectionMgr::RemoveSpdyPreferred(nsACString &aHashKey)
{
if (!gHttpHandler->CoalesceSpdy())
return;
if (aHashKey.IsEmpty())
return;
@ -750,7 +757,6 @@ nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key,
// Find out how long it will take for next idle connection to not be reusable
// anymore.
bool liveConnections = false;
PRUint32 timeToNextExpire = PR_UINT32_MAX;
PRInt32 count = ent->mIdleConns.Length();
if (count > 0) {
@ -763,7 +769,6 @@ nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key,
self->mNumIdleConns--;
} else {
timeToNextExpire = NS_MIN(timeToNextExpire, conn->TimeToLive());
liveConnections = true;
}
}
}
@ -780,7 +785,6 @@ nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key,
else {
timeToNextExpire = NS_MIN(timeToNextExpire,
conn->TimeToLive());
liveConnections = true;
}
}
}
@ -788,7 +792,7 @@ nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key,
// If time to next expire found is shorter than time to next wake-up, we need to
// change the time for next wake-up.
if (liveConnections) {
if (timeToNextExpire != PR_UINT32_MAX) {
PRUint32 now = NowInSeconds();
PRUint64 timeOfNextExpire = now + timeToNextExpire;
// If pruning of dead connections is not already scheduled to happen
@ -892,8 +896,7 @@ nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
ent->mConnInfo->HashKey().get()));
if (gHttpHandler->IsSpdyEnabled())
ProcessSpdyPendingQ(ent);
ProcessSpdyPendingQ(ent);
PRUint32 i, count = ent->mPendingQ.Length();
if (count > 0) {
@ -920,12 +923,9 @@ nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
if (conn)
break;
// Check to see if a pending transaction was dispatched with the
// coalesce logic
if (count != ent->mPendingQ.Length()) {
count = ent->mPendingQ.Length();
i = 0;
}
NS_ABORT_IF_FALSE(count == ent->mPendingQ.Length(),
"something mutated pending queue from "
"GetConnection()");
}
if (conn) {
LOG((" dispatching pending transaction...\n"));
@ -1111,9 +1111,9 @@ nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
ent->mConnInfo->UsingSSL() &&
!ent->mConnInfo->UsingHttpProxy())
{
// If this is a possible Spdy connection we need to limit the number
// of connections outstanding to 1 while we wait for the spdy/https
// ReportSpdyConnection()
// 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.
if ((!ent->mTestedSpdy || ent->mUsingSpdy) &&
(ent->mHalfOpens.Length() || ent->mActiveConns.Length()))
@ -1331,7 +1331,7 @@ nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
// SPDY coalescing of hostnames means we might redirect from this
// connection entry onto the preferred one.
nsConnectionEntry *preferredEntry = GetSpdyPreferred(ent);
nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
if (preferredEntry && (preferredEntry != ent)) {
LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
"redirected via coalescing from %s to %s\n", trans,
@ -1403,8 +1403,15 @@ nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
ent->mPendingQ.RemoveElementAt(index);
nsresult rv2 = DispatchTransaction(ent, trans, trans->Caps(), conn);
NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv2), "Dispatch SPDY Transaction");
nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
if (NS_FAILED(rv)) {
// this cannot happen, but if due to some bug it does then
// close the transaction
NS_ABORT_IF_FALSE(false, "Dispatch SPDY Transaction");
LOG(("ProcessSpdyPendingQ Dispatch Transaction failed trans=%p\n",
trans));
trans->Close(rv);
}
NS_RELEASE(trans);
}
}
@ -1420,7 +1427,7 @@ nsHttpConnectionMgr::ProcessSpdyPendingQCB(const nsACString &key,
}
void
nsHttpConnectionMgr::ProcessSpdyPendingQ()
nsHttpConnectionMgr::ProcessAllSpdyPendingQ()
{
mCT.Enumerate(ProcessSpdyPendingQCB, this);
}
@ -1431,10 +1438,11 @@ nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(ent, "no connection entry");
nsConnectionEntry *preferred = GetSpdyPreferred(ent);
nsConnectionEntry *preferred = GetSpdyPreferredEnt(ent);
// this entry is spdy-enabled if it is involved in a redirect
if (preferred)
// all new connections for this entry will use spdy too
ent->mUsingSpdy = true;
else
preferred = ent;
@ -1594,7 +1602,8 @@ nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
if (!ent) {
// this should never happen
NS_ASSERTION(ent, "no connection entry");
LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection ent == null\n"));
NS_ABORT_IF_FALSE(false, "no connection entry");
NS_ADDREF(ci = conn->ConnectionInfo());
}
else {
@ -1605,9 +1614,16 @@ nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
// This is never the final reference on conn as the event context
// is also holding one that is released at the end of this function.
if (ent->mUsingSpdy)
if (ent->mUsingSpdy) {
// Spdy connections aren't reused in the traditional HTTP way in
// the idleconns list, they are actively multplexed as active
// conns. Even when they have 0 transactions on them they are
// considered active connections. So when one is reclaimed it
// is really complete and is meant to be shut down and not
// reused.
conn->DontReuse();
}
if (ent->mActiveConns.RemoveElement(conn)) {
nsHttpConnection *temp = conn;
NS_RELEASE(temp);
@ -1690,7 +1706,7 @@ nsHttpConnectionMgr::OnMsgUpdateParam(PRInt32, void *param)
nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
{
if (mSpdyPreferred)
gHttpHandler->ConnMgr()->RemoveSpdyPreferred(mCoalescingKey);
gHttpHandler->ConnMgr()->RemoveSpdyPreferredEnt(mCoalescingKey);
NS_RELEASE(mConnInfo);
}

View File

@ -200,12 +200,17 @@ private:
//
nsCString mCoalescingKey;
// To have the UsingSpdy flag means some host with the same hash information
// has done NPN=spdy/2 at some point. It does not mean every connection
// is currently using spdy.
// To have the UsingSpdy flag means some host with the same connection
// entry has done NPN=spdy/2 at some point. It does not mean every
// 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;
};
@ -321,9 +326,8 @@ private:
void RecvdConnect();
// Manage the preferred spdy connection entry for this address
nsConnectionEntry *GetSpdyPreferred(nsConnectionEntry *aOriginalEntry);
void SetSpdyPreferred(nsConnectionEntry *ent);
void RemoveSpdyPreferred(nsACString &aDottedDecimal);
nsConnectionEntry *GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry);
void RemoveSpdyPreferredEnt(nsACString &aDottedDecimal);
nsHttpConnection *GetSpdyPreferredConn(nsConnectionEntry *ent);
nsDataHashtable<nsCStringHashKey, nsConnectionEntry *> mSpdyPreferredHash;
nsConnectionEntry *LookupConnectionEntry(nsHttpConnectionInfo *ci,
@ -331,7 +335,7 @@ private:
nsHttpTransaction *trans);
void ProcessSpdyPendingQ(nsConnectionEntry *ent);
void ProcessSpdyPendingQ();
void ProcessAllSpdyPendingQ();
static PLDHashOperator ProcessSpdyPendingQCB(
const nsACString &key, nsAutoPtr<nsConnectionEntry> &ent,
void *closure);
@ -414,8 +418,8 @@ private:
//
nsClassHashtable<nsCStringHashKey, nsConnectionEntry> mCT;
// this table is protected by the monitor
nsCStringHashSet mAlternateProtocolHash;
// mAlternateProtocolHash is used only for spdy/2 upgrades for now
nsCStringHashSet mAlternateProtocolHash; // protected by the monitor
static PLDHashOperator TrimAlternateProtocolHash(PLDHashTable *table,
PLDHashEntryHdr *hdr,
PRUint32 number,