diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp index 1e84ead5a299..92eb39333f3f 100644 --- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -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 //----------------------------------------------------------------------------- @@ -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")); diff --git a/netwerk/protocol/http/nsHttpConnection.h b/netwerk/protocol/http/nsHttpConnection.h index 0461faa786f1..01a08be7c3b6 100644 --- a/netwerk/protocol/http/nsHttpConnection.h +++ b/netwerk/protocol/http/nsHttpConnection.h @@ -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__ diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index 108f2c58750b..45ad333933f5 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -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 diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h index fc3ed1d463c4..b8e7f395fe37 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.h +++ b/netwerk/protocol/http/nsHttpConnectionMgr.h @@ -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;