604796 - close our idle HTTP connection when server closes its end r=honzab

--HG--
extra : rebase_source : b4c89df4d3eab7b7c8c60d625467847557bc7291
This commit is contained in:
Patrick McManus 2011-05-16 09:32:37 -04:00
parent 5be8433fbb
commit f5a58bd8b8
4 changed files with 98 additions and 1 deletions

View File

@ -82,6 +82,7 @@ nsHttpConnection::nsHttpConnection()
, mIsActivated(PR_FALSE)
, mCompletedProxyConnect(PR_FALSE)
, mLastTransactionExpectedNoContent(PR_FALSE)
, mIdleMonitoring(PR_FALSE)
{
LOG(("Creating nsHttpConnection @%x\n", this));
@ -170,6 +171,10 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps)
mTransaction = trans;
mIsActivated = PR_TRUE;
NS_ABORT_IF_FALSE(!mIdleMonitoring,
"Activating a connection with an Idle Monitor");
mIdleMonitoring = PR_FALSE;
// set mKeepAlive according to what will be requested
mKeepAliveMask = mKeepAlive = (caps & NS_HTTP_ALLOW_KEEPALIVE);
@ -203,6 +208,9 @@ nsHttpConnection::Close(nsresult reason)
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
if (NS_FAILED(reason)) {
if (mIdleMonitoring)
EndIdleMonitoring();
if (mSocketTransport) {
mSocketTransport->SetSecurityCallbacks(nsnull);
mSocketTransport->SetEventSink(nsnull, nsnull);
@ -580,6 +588,34 @@ nsHttpConnection::ResumeRecv()
return NS_ERROR_UNEXPECTED;
}
void
nsHttpConnection::BeginIdleMonitoring()
{
LOG(("nsHttpConnection::BeginIdleMonitoring [this=%p]\n", this));
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(!mTransaction, "BeginIdleMonitoring() while active");
LOG(("Entering Idle Monitoring Mode [this=%p]", this));
mIdleMonitoring = PR_TRUE;
if (mSocketIn)
mSocketIn->AsyncWait(this, 0, 0, nsnull);
}
void
nsHttpConnection::EndIdleMonitoring()
{
LOG(("nsHttpConnection::EndIdleMonitoring [this=%p]\n", this));
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(!mTransaction, "EndIdleMonitoring() while active");
if (mIdleMonitoring) {
LOG(("Leaving Idle Monitoring Mode [this=%p]", this));
mIdleMonitoring = PR_FALSE;
if (mSocketIn)
mSocketIn->AsyncWait(nsnull, 0, 0, nsnull);
}
}
//-----------------------------------------------------------------------------
// nsHttpConnection <private>
//-----------------------------------------------------------------------------
@ -856,6 +892,25 @@ nsHttpConnection::OnInputStreamReady(nsIAsyncInputStream *in)
NS_ASSERTION(in == mSocketIn, "unexpected stream");
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
if (mIdleMonitoring) {
NS_ABORT_IF_FALSE(!mTransaction, "Idle Input Event While Active");
// The only read event that is protocol compliant for an idle connection
// is an EOF, which we check for with CanReuse(). If the data is
// something else then just ignore it and suspend checking for EOF -
// our normal timers or protocol stack are the place to deal with
// any exception logic.
if (!CanReuse()) {
LOG(("Server initiated close of idle conn %p\n", this));
gHttpHandler->ConnMgr()->CloseIdleConnection(this);
return NS_OK;
}
LOG(("Input data on idle conn %p, but not closing yet\n", this));
return NS_OK;
}
// if the transaction was dropped...
if (!mTransaction) {
LOG((" no transaction; ignoring event\n"));

View File

@ -146,6 +146,13 @@ public:
static NS_METHOD ReadFromStream(nsIInputStream *, void *, const char *,
PRUint32, PRUint32, PRUint32 *);
// When a persistent connection is in the connection manager idle
// connection pool, the nsHttpConnection still reads errors and hangups
// on the socket so that it can be proactively released if the server
// initiates a termination. Only call on socket thread.
void BeginIdleMonitoring();
void EndIdleMonitoring();
private:
// called to cause the underlying socket to start speaking SSL
nsresult ProxyStartSSL();
@ -196,6 +203,7 @@ private:
PRPackedBool mIsActivated;
PRPackedBool mCompletedProxyConnect;
PRPackedBool mLastTransactionExpectedNoContent;
PRPackedBool mIdleMonitoring;
};
#endif // nsHttpConnection_h__

View File

@ -383,6 +383,31 @@ nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
return rv;
}
nsresult
nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
{
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p",
this, conn));
nsHttpConnectionInfo *ci = conn->ConnectionInfo();
if (!ci)
return NS_ERROR_UNEXPECTED;
nsCStringKey key(ci->HashKey());
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
if (!ent || !ent->mIdleConns.RemoveElement(conn))
return NS_ERROR_UNEXPECTED;
conn->Close(NS_ERROR_ABORT);
NS_RELEASE(conn);
mNumIdleConns--;
if (0 == mNumIdleConns)
StopPruneDeadConnectionsTimer();
return NS_OK;
}
//-----------------------------------------------------------------------------
// enumeration callbacks
@ -689,8 +714,11 @@ nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
conn->Close(NS_ERROR_ABORT);
NS_RELEASE(conn);
}
else
else {
LOG((" reusing connection [conn=%x]\n", conn));
conn->EndIdleMonitoring();
}
ent->mIdleConns.RemoveElementAt(0);
mNumIdleConns--;
@ -1091,6 +1119,7 @@ nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
NS_ADDREF(conn);
ent->mIdleConns.InsertElementAt(idx, conn);
mNumIdleConns++;
conn->BeginIdleMonitoring();
// If the added connection was first idle connection or has shortest
// time to live among the idle connections, pruning dead

View File

@ -138,6 +138,11 @@ public:
// preference to the specified connection.
nsresult ProcessPendingQ(nsHttpConnectionInfo *);
// 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.
nsresult CloseIdleConnection(nsHttpConnection *);
private:
virtual ~nsHttpConnectionMgr();
class nsHalfOpenSocket;