fixes bug 83526 "http should use fewer connections per server per page"
r=bbaetz sr=dougt,blizzard git-svn-id: svn://10.0.0.236/trunk@102220 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
@@ -288,11 +288,28 @@ pref("network.http.keep-alive", true); // set it to false in case of problems
|
||||
pref("network.http.proxy.keep-alive", true );
|
||||
pref("network.http.keep-alive.timeout", 300);
|
||||
|
||||
pref("network.http.max-connections", 16);
|
||||
pref("network.http.max-connections-per-server", 8);
|
||||
pref("network.http.keep-alive.max-connections", 20); // max connections to be kept alive
|
||||
pref("network.http.keep-alive.max-connections-per-server", 8);
|
||||
// limit the absolute number of http connections.
|
||||
pref("network.http.max-connections", 24);
|
||||
|
||||
// limit the absolute number of http connections that can be established per
|
||||
// host. if a http proxy server is enabled, then the "server" is the proxy
|
||||
// server. Otherwise, "server" is the http origin server.
|
||||
pref("network.http.max-connections-per-server", 8);
|
||||
|
||||
// if network.http.keep-alive is true, then a new connection will only be
|
||||
// attempted if the number of active connections to a host is less then
|
||||
// network.http.max-persistent-connections-per-server. if a http proxy server
|
||||
// is enabled, then the "server" is the proxy server. Otherwise, "server" is
|
||||
// the http origin server.
|
||||
pref("network.http.max-persistent-connections-per-server", 2);
|
||||
|
||||
// amount of time (in seconds) to suspend pending requests, before spawning a
|
||||
// new connection, once the limit on the number of persistent connections per
|
||||
// host has been reached. however, a new connection will not be created if the
|
||||
// limit on the number of connections per host has also been reached.
|
||||
pref("network.http.request.max-start-delay", 10);
|
||||
|
||||
// http specific network timeouts (XXX currently unused)
|
||||
pref("network.http.connect.timeout", 30); // in seconds
|
||||
pref("network.http.request.timeout", 120); // in seconds
|
||||
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
#include "nsString.h"
|
||||
#include "nsNetCID.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
// in debug builds this will be valid while the socket transport service is active.
|
||||
PRThread *NS_SOCKET_THREAD = 0;
|
||||
#endif
|
||||
|
||||
static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID);
|
||||
static NS_DEFINE_CID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID);
|
||||
|
||||
@@ -356,6 +361,10 @@ nsSocketTransportService::Run(void)
|
||||
PRBool hadThreadEvent = mThreadEvent ? PR_TRUE : PR_FALSE;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
NS_SOCKET_THREAD = PR_GetCurrentThread();
|
||||
#endif
|
||||
|
||||
if (mThreadEvent)
|
||||
{
|
||||
//
|
||||
@@ -517,7 +526,10 @@ nsSocketTransportService::Run(void)
|
||||
/* Process any pending operations on the mWorkQ... */
|
||||
rv = ProcessWorkQ();
|
||||
}
|
||||
return NS_OK;
|
||||
#ifdef DEBUG
|
||||
NS_SOCKET_THREAD = 0;
|
||||
#endif
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -291,7 +291,7 @@ nsHttpChannel::SetupTransaction()
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// create the transaction object
|
||||
mTransaction = new nsHttpTransaction(listenerProxy, this);
|
||||
mTransaction = new nsHttpTransaction(listenerProxy, this, mCapabilities);
|
||||
if (!mTransaction)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(mTransaction);
|
||||
|
||||
@@ -47,9 +47,11 @@ nsHttpConnection::nsHttpConnection()
|
||||
: mTransaction(0)
|
||||
, mConnectionInfo(0)
|
||||
, mLock(nsnull)
|
||||
, mReadStartTime(0)
|
||||
, mLastActiveTime(0)
|
||||
, mIdleTimeout(0)
|
||||
, mKeepAlive(0)
|
||||
, mKeepAlive(1) // assume to keep-alive by default
|
||||
, mKeepAliveMask(1)
|
||||
, mWriteDone(0)
|
||||
, mReadDone(0)
|
||||
{
|
||||
@@ -72,7 +74,7 @@ nsHttpConnection::~nsHttpConnection()
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnection::Init(nsHttpConnectionInfo *info)
|
||||
nsHttpConnection::Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime)
|
||||
{
|
||||
LOG(("nsHttpConnection::Init [this=%x]\n", this));
|
||||
|
||||
@@ -86,6 +88,7 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info)
|
||||
mConnectionInfo = info;
|
||||
NS_ADDREF(mConnectionInfo);
|
||||
|
||||
mMaxHangTime = maxHangTime;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -103,6 +106,10 @@ nsHttpConnection::SetTransaction(nsHttpTransaction *transaction)
|
||||
mTransaction = transaction;
|
||||
NS_ADDREF(mTransaction);
|
||||
|
||||
// default mKeepAlive according to what will be requested
|
||||
mKeepAliveMask = mKeepAlive =
|
||||
mTransaction->Capabilities() & NS_HTTP_ALLOW_KEEPALIVE;
|
||||
|
||||
// build a proxy for the progress event sink
|
||||
mProgressSink = 0;
|
||||
if (mTransaction->Callbacks() && mTransaction->ConsumerEventQ()) {
|
||||
@@ -160,6 +167,7 @@ nsHttpConnection::OnHeadersAvailable(nsHttpTransaction *trans, PRBool *reset)
|
||||
else
|
||||
mKeepAlive = PR_TRUE;
|
||||
}
|
||||
mKeepAliveMask = mKeepAlive;
|
||||
|
||||
// if this connection is persistent, then the server may send a "Keep-Alive"
|
||||
// header specifying the maximum number of times the connection can be
|
||||
@@ -290,8 +298,8 @@ nsHttpConnection::ProxyStepUp()
|
||||
PRBool
|
||||
nsHttpConnection::CanReuse()
|
||||
{
|
||||
return mKeepAlive && (NowInSeconds() - mLastActiveTime < mIdleTimeout)
|
||||
&& IsAlive();
|
||||
return IsKeepAlive() && (NowInSeconds() - mLastActiveTime < mIdleTimeout)
|
||||
&& IsAlive();
|
||||
}
|
||||
|
||||
PRBool
|
||||
@@ -318,7 +326,7 @@ nsHttpConnection::DropTransaction()
|
||||
mProgressSink = 0;
|
||||
|
||||
// if the transaction was dropped, then we cannot reuse this connection.
|
||||
mKeepAlive = PR_FALSE;
|
||||
mKeepAliveMask = mKeepAlive = PR_FALSE;
|
||||
}
|
||||
|
||||
// called on the socket thread
|
||||
@@ -574,8 +582,10 @@ nsHttpConnection::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
|
||||
mTransaction->SetSecurityInfo(info);
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
mReadRequest = request;
|
||||
mReadStartTime = NowInSeconds();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -630,6 +640,9 @@ nsHttpConnection::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
|
||||
NS_RELEASE(trans);
|
||||
}
|
||||
|
||||
// reset the keep-alive mask
|
||||
mKeepAliveMask = mKeepAlive;
|
||||
|
||||
nsHttpHandler::get()->ReclaimConnection(this);
|
||||
}
|
||||
// no point in returning anything else but NS_OK
|
||||
@@ -690,14 +703,23 @@ nsHttpConnection::OnDataAvailable(nsIRequest *request, nsISupports *context,
|
||||
nsIInputStream *inputStream,
|
||||
PRUint32 offset, PRUint32 count)
|
||||
{
|
||||
LOG(("nsHttpConnection::OnDataAvailable [this=%x]\n", this));
|
||||
|
||||
if (!mTransaction) {
|
||||
LOG(("nsHttpConnection: no transaction! closing stream\n"));
|
||||
LOG(("no transaction! closing stream\n"));
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
mLastActiveTime = NowInSeconds();
|
||||
|
||||
LOG(("nsHttpConnection::OnDataAvailable [this=%x]\n", this));
|
||||
if (mKeepAliveMask &&
|
||||
(mLastActiveTime - mReadStartTime >= PRUint32(mMaxHangTime))) {
|
||||
LOG(("max hang time exceeded!\n"));
|
||||
// give the handler a chance to create a new persistent connection to
|
||||
// this host if we've been busy for too long.
|
||||
mKeepAliveMask = PR_FALSE;
|
||||
nsHttpHandler::get()->ProcessTransactionQ();
|
||||
}
|
||||
|
||||
nsresult rv = mTransaction->OnDataReadable(inputStream);
|
||||
|
||||
|
||||
@@ -63,7 +63,12 @@ public:
|
||||
nsHttpConnection();
|
||||
virtual ~nsHttpConnection();
|
||||
|
||||
nsresult Init(nsHttpConnectionInfo *);
|
||||
// Initialize the connection:
|
||||
// info - specifies the connection parameters.
|
||||
// maxHangTime - limits the amount of time this connection can spend on a
|
||||
// single transaction before it should no longer be kept
|
||||
// alive. a value of 0xffff indicates no limit.
|
||||
nsresult Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime);
|
||||
|
||||
// SetTransaction causes the given transaction to be processed on this
|
||||
// connection. It fails if there is already an existing transaction.
|
||||
@@ -83,13 +88,11 @@ public:
|
||||
// called to cause the underlying socket to start speaking SSL
|
||||
nsresult ProxyStepUp();
|
||||
|
||||
PRBool CanReuse(); // can this connection be reused?
|
||||
PRBool IsAlive();
|
||||
PRBool IsKeepAlive() { return mKeepAlive; }
|
||||
PRUint16 IdleTimeout() { return mIdleTimeout; }
|
||||
|
||||
void DontReuse() { mKeepAlive = PR_FALSE;
|
||||
mIdleTimeout = 0; }
|
||||
PRBool IsKeepAlive() { return mKeepAliveMask && mKeepAlive; }
|
||||
PRBool CanReuse(); // can this connection be reused?
|
||||
void DontReuse() { mKeepAliveMask = PR_FALSE;
|
||||
mKeepAlive = PR_FALSE;
|
||||
mIdleTimeout = 0; }
|
||||
|
||||
void DropTransaction();
|
||||
void ReportProgress(PRUint32 progress, PRInt32 progressMax);
|
||||
@@ -107,6 +110,8 @@ private:
|
||||
|
||||
nsresult SetupSSLProxyConnect();
|
||||
|
||||
PRBool IsAlive();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISocketTransport> mSocketTransport;
|
||||
nsCOMPtr<nsIRequest> mWriteRequest;
|
||||
@@ -122,10 +127,13 @@ private:
|
||||
|
||||
PRLock *mLock;
|
||||
|
||||
PRUint32 mReadStartTime; // time of OnStartRequest
|
||||
PRUint32 mLastActiveTime;
|
||||
PRUint16 mIdleTimeout; // value of keep-alive: timeout=
|
||||
PRUint16 mMaxHangTime; // max download time before dropping keep-alive status
|
||||
PRUint16 mIdleTimeout; // value of keep-alive: timeout=
|
||||
|
||||
PRPackedBool mKeepAlive;
|
||||
PRPackedBool mKeepAliveMask;
|
||||
PRPackedBool mWriteDone;
|
||||
PRPackedBool mReadDone;
|
||||
};
|
||||
|
||||
@@ -62,6 +62,11 @@
|
||||
#include <Gestalt.h>
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
// defined by the socket transport service while active
|
||||
extern PRThread *NS_SOCKET_THREAD;
|
||||
#endif
|
||||
|
||||
static const char NETWORK_PREFS[] = "network.";
|
||||
static const char INTL_ACCEPT_LANGUAGES[] = "intl.accept_languages";
|
||||
static const char INTL_ACCEPT_CHARSET[] = "intl.charset.default";
|
||||
@@ -95,9 +100,10 @@ nsHttpHandler::nsHttpHandler()
|
||||
, mProxyCapabilities(NS_HTTP_ALLOW_KEEPALIVE)
|
||||
, mIdleTimeout(10)
|
||||
, mMaxRequestAttempts(10)
|
||||
, mMaxConnections(16)
|
||||
, mMaxRequestDelay(10)
|
||||
, mMaxConnections(24)
|
||||
, mMaxConnectionsPerServer(8)
|
||||
, mMaxIdleConnectionsPerServer(4)
|
||||
, mMaxPersistentConnectionsPerServer(2)
|
||||
, mActiveConnections(0)
|
||||
, mIdleConnections(0)
|
||||
, mTransactionQ(0)
|
||||
@@ -378,8 +384,7 @@ nsHttpHandler::GetCacheSession(nsCacheStoragePolicy storagePolicy,
|
||||
// may be called from any thread
|
||||
nsresult
|
||||
nsHttpHandler::InitiateTransaction(nsHttpTransaction *trans,
|
||||
nsHttpConnectionInfo *ci,
|
||||
PRBool failIfBusy)
|
||||
nsHttpConnectionInfo *ci)
|
||||
{
|
||||
LOG(("nsHttpHandler::InitiateTransaction\n"));
|
||||
|
||||
@@ -388,19 +393,22 @@ nsHttpHandler::InitiateTransaction(nsHttpTransaction *trans,
|
||||
|
||||
nsAutoLock lock(mConnectionLock);
|
||||
|
||||
return InitiateTransaction_Locked(trans, ci, failIfBusy);
|
||||
return InitiateTransaction_Locked(trans, ci);
|
||||
}
|
||||
|
||||
// may be called from any thread
|
||||
// called from the socket thread
|
||||
nsresult
|
||||
nsHttpHandler::ReclaimConnection(nsHttpConnection *conn)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(conn);
|
||||
#ifdef DEBUG
|
||||
NS_PRECONDITION(PR_GetCurrentThread() == NS_SOCKET_THREAD, "wrong thread");
|
||||
#endif
|
||||
|
||||
PRBool reusable = conn->CanReuse();
|
||||
|
||||
LOG(("nsHttpHandler::ReclaimConnection [conn=%x keep-alive=%d]\n",
|
||||
conn, reusable));
|
||||
LOG(("nsHttpHandler::ReclaimConnection [conn=%x(%s:%d) keep-alive=%d]\n",
|
||||
conn, conn->ConnectionInfo()->Host(), conn->ConnectionInfo()->Port(), reusable));
|
||||
|
||||
nsAutoLock lock(mConnectionLock);
|
||||
|
||||
@@ -408,21 +416,11 @@ nsHttpHandler::ReclaimConnection(nsHttpConnection *conn)
|
||||
mActiveConnections.RemoveElement(conn);
|
||||
|
||||
if (reusable) {
|
||||
// verify that we aren't already maxed out on the number of
|
||||
// keep-alives we can have for this server.
|
||||
PRUint32 count = CountIdleConnections(conn->ConnectionInfo());
|
||||
if (count == PRUint32(mMaxIdleConnectionsPerServer)) {
|
||||
LOG(("not caching keep-alive connection: "
|
||||
"would exceed max allowed per server\n"));
|
||||
NS_RELEASE(conn);
|
||||
}
|
||||
else {
|
||||
LOG(("adding connection to idle list [conn=%x]\n", conn));
|
||||
// hold onto this connection in the idle list. we push it
|
||||
// to the end of the list so as to ensure that we'll visit
|
||||
// older connections first before getting to this one.
|
||||
mIdleConnections.AppendElement(conn);
|
||||
}
|
||||
LOG(("adding connection to idle list [conn=%x]\n", conn));
|
||||
// hold onto this connection in the idle list. we push it
|
||||
// to the end of the list so as to ensure that we'll visit
|
||||
// older connections first before getting to this one.
|
||||
mIdleConnections.AppendElement(conn);
|
||||
}
|
||||
else {
|
||||
LOG(("closing connection: connection can't be reused\n"));
|
||||
@@ -433,7 +431,26 @@ nsHttpHandler::ReclaimConnection(nsHttpConnection *conn)
|
||||
|
||||
// process the pending transaction queue...
|
||||
if (mTransactionQ.Count() > 0)
|
||||
ProcessTransactionQ();
|
||||
ProcessTransactionQ_Locked();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// called from the socket thread (see nsHttpConnection::OnDataAvailable)
|
||||
nsresult
|
||||
nsHttpHandler::ProcessTransactionQ()
|
||||
{
|
||||
LOG(("nsHttpHandler::ProcessTransactionQ\n"));
|
||||
#ifdef DEBUG
|
||||
NS_PRECONDITION(PR_GetCurrentThread() == NS_SOCKET_THREAD, "wrong thread");
|
||||
#endif
|
||||
|
||||
nsAutoLock lock(mConnectionLock);
|
||||
|
||||
// conn is no longer keep-alive, so we may be able to initiate
|
||||
// a pending transaction to the same host.
|
||||
if (mTransactionQ.Count() > 0)
|
||||
ProcessTransactionQ_Locked();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -632,9 +649,9 @@ nsHttpHandler::UserAgent()
|
||||
|
||||
// called with the connection lock held
|
||||
void
|
||||
nsHttpHandler::ProcessTransactionQ()
|
||||
nsHttpHandler::ProcessTransactionQ_Locked()
|
||||
{
|
||||
LOG(("nsHttpHandler::ProcessTransactionQ\n"));
|
||||
LOG(("nsHttpHandler::ProcessTransactionQ_Locked\n"));
|
||||
|
||||
nsPendingTransaction *pt = nsnull;
|
||||
|
||||
@@ -644,17 +661,30 @@ nsHttpHandler::ProcessTransactionQ()
|
||||
|
||||
pt = (nsPendingTransaction *) mTransactionQ[i];
|
||||
|
||||
// try to initiate this transaction... if it fails
|
||||
// then we'll just skip over this pending transaction
|
||||
// and try the next.
|
||||
// skip over a busy pending transaction
|
||||
if (pt->IsBusy())
|
||||
continue;
|
||||
|
||||
// the connection lock is released during InitiateTransaction_Locked, so
|
||||
// the transaction queue could get modified. mark this pending transaction
|
||||
// as busy to cause it to be skipped if this function happens to recurse.
|
||||
pt->SetBusy(PR_TRUE);
|
||||
|
||||
// try to initiate this transaction... if it fails then we'll just skip
|
||||
// over this pending transaction and try the next.
|
||||
nsresult rv = InitiateTransaction_Locked(pt->Transaction(),
|
||||
pt->ConnectionInfo(),
|
||||
PR_TRUE);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mTransactionQ.RemoveElementAt(i);
|
||||
delete pt;
|
||||
i--;
|
||||
}
|
||||
else {
|
||||
LOG(("InitiateTransaction_Locked failed [rv=%x]\n", rv));
|
||||
pt->SetBusy(PR_FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -671,7 +701,7 @@ nsHttpHandler::EnqueueTransaction(nsHttpTransaction *trans,
|
||||
|
||||
mTransactionQ.AppendElement(pt);
|
||||
|
||||
LOG(("transaction queue contains %u elements\n", mTransactionQ.Count()));
|
||||
LOG((">> transaction queue contains %u elements\n", mTransactionQ.Count()));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -682,57 +712,59 @@ nsHttpHandler::InitiateTransaction_Locked(nsHttpTransaction *trans,
|
||||
PRBool failIfBusy)
|
||||
{
|
||||
nsresult rv;
|
||||
PRUint8 caps = trans->Capabilities();
|
||||
|
||||
LOG(("nsHttpHandler::InitiateTransaction_Locked [failIfBusy=%d]\n", failIfBusy));
|
||||
|
||||
if ((mActiveConnections.Count() == PRInt32(mMaxConnections)) ||
|
||||
(CountActiveConnections(ci) == mMaxConnectionsPerServer)) {
|
||||
LOG(("unable to perform the transaction at this time [trans=%x]\n", trans));
|
||||
if (failIfBusy) return NS_ERROR_FAILURE;
|
||||
return EnqueueTransaction(trans, ci);
|
||||
if (AtActiveConnectionLimit(ci, caps)) {
|
||||
LOG((">> unable to perform the transaction at this time [trans=%x]\n", trans));
|
||||
return failIfBusy ? NS_ERROR_FAILURE : EnqueueTransaction(trans, ci);
|
||||
}
|
||||
|
||||
nsHttpConnection *conn = nsnull;
|
||||
|
||||
// search the idle connection list
|
||||
PRInt32 i;
|
||||
for (i=0; i<mIdleConnections.Count(); ++i) {
|
||||
conn = (nsHttpConnection *) mIdleConnections[i];
|
||||
if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
|
||||
// search the idle connection list
|
||||
PRInt32 i;
|
||||
for (i=0; i<mIdleConnections.Count(); ++i) {
|
||||
conn = (nsHttpConnection *) mIdleConnections[i];
|
||||
|
||||
LOG(("comparing against idle connection [host=%s:%d]\n",
|
||||
conn->ConnectionInfo()->Host(), conn->ConnectionInfo()->Port()));
|
||||
LOG((">> comparing against idle connection [conn=%x host=%s:%d]\n",
|
||||
conn, conn->ConnectionInfo()->Host(), conn->ConnectionInfo()->Port()));
|
||||
|
||||
// we check if the connection can be reused before even checking if it
|
||||
// is a "matching" connection. this is how we keep the idle connection
|
||||
// list fresh. we could alternatively use some sort of timer for this.
|
||||
if (!conn->CanReuse()) {
|
||||
LOG(("dropping stale connection: [conn=%x]\n", conn));
|
||||
mIdleConnections.RemoveElementAt(i);
|
||||
i--;
|
||||
NS_RELEASE(conn);
|
||||
// we check if the connection can be reused before even checking if it
|
||||
// is a "matching" connection. this is how we keep the idle connection
|
||||
// list fresh. we could alternatively use some sort of timer for this.
|
||||
if (!conn->CanReuse()) {
|
||||
LOG((" dropping stale connection: [conn=%x]\n", conn));
|
||||
mIdleConnections.RemoveElementAt(i);
|
||||
i--;
|
||||
NS_RELEASE(conn);
|
||||
}
|
||||
else if (conn->ConnectionInfo()->Equals(ci)) {
|
||||
LOG((" reusing connection [conn=%x]\n", conn));
|
||||
mIdleConnections.RemoveElementAt(i);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
conn = nsnull;
|
||||
}
|
||||
else if (conn->ConnectionInfo()->Equals(ci)) {
|
||||
LOG(("reusing connection [conn=%x]\n", conn));
|
||||
mIdleConnections.RemoveElementAt(i);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
conn = nsnull;
|
||||
}
|
||||
|
||||
if (!conn) {
|
||||
LOG(("creating new connection...\n"));
|
||||
LOG((">> creating new connection...\n"));
|
||||
NS_NEWXPCOM(conn, nsHttpConnection);
|
||||
if (!conn)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(conn);
|
||||
|
||||
rv = conn->Init(ci);
|
||||
rv = conn->Init(ci, mMaxRequestDelay);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(conn);
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// Update the connectionInfo (bug 94038)
|
||||
conn->ConnectionInfo()->SetOriginServer(ci->Host(), ci->Port());
|
||||
}
|
||||
@@ -750,6 +782,7 @@ nsHttpHandler::InitiateTransaction_Locked(nsHttpTransaction *trans,
|
||||
PR_Lock(mConnectionLock);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("nsHttpConnection::SetTransaction failed [rv=%x]\n", rv));
|
||||
// the connection may already have been removed from the
|
||||
// active connection list.
|
||||
if (mActiveConnections.RemoveElement(conn))
|
||||
@@ -783,57 +816,41 @@ nsHttpHandler::RemovePendingTransaction(nsHttpTransaction *trans)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
PRUint8
|
||||
nsHttpHandler::CountActiveConnections(nsHttpConnectionInfo *ci)
|
||||
// we're at the active connection limit if any one of the following conditions is true:
|
||||
// (1) at max-connections
|
||||
// (2) keep-alive enabled and at max-persistent-connections-per-host
|
||||
// (3) keep-alive disabled and at max-connections-per-host
|
||||
PRBool
|
||||
nsHttpHandler::AtActiveConnectionLimit(nsHttpConnectionInfo *ci, PRUint8 caps)
|
||||
{
|
||||
PRUint8 count = 0;
|
||||
nsHttpConnection *conn = 0;
|
||||
LOG(("nsHttpHandler::AtActiveConnectionLimit [host=%s:%d caps=%x]\n",
|
||||
ci->Host(), ci->Port(), caps));
|
||||
|
||||
LOG(("nsHttpHandler::CountActiveConnections [host=%s:%d]\n",
|
||||
ci->Host(), ci->Port()));
|
||||
// use >= just to be safe
|
||||
if (mActiveConnections.Count() >= mMaxConnections)
|
||||
return PR_TRUE;
|
||||
|
||||
nsHttpConnection *conn;
|
||||
PRUint8 totalCount = 0, persistentCount = 0;
|
||||
PRInt32 i;
|
||||
for (i=0; i<mActiveConnections.Count(); ++i) {
|
||||
conn = NS_STATIC_CAST(nsHttpConnection *, mActiveConnections[i]);
|
||||
// only include a matching connection in the count...
|
||||
if (conn->ConnectionInfo()->Equals(ci))
|
||||
count++;
|
||||
}
|
||||
|
||||
LOG(("found count=%u\n", (PRUintn) count));
|
||||
return count;
|
||||
}
|
||||
|
||||
PRUint8
|
||||
nsHttpHandler::CountIdleConnections(nsHttpConnectionInfo *ci)
|
||||
{
|
||||
PRUint8 count = 0;
|
||||
nsHttpConnection *conn = 0;
|
||||
|
||||
if (!ci)
|
||||
return 0;
|
||||
|
||||
LOG(("nsHttpHandler::CountIdleConnections [host=%s:%d]\n",
|
||||
ci->Host(), ci->Port()));
|
||||
|
||||
PRInt32 i;
|
||||
for (i=0; i<mIdleConnections.Count(); ++i) {
|
||||
conn = NS_STATIC_CAST(nsHttpConnection *, mIdleConnections[i]);
|
||||
// only include a matching connection in the count if it
|
||||
// can still be reused.
|
||||
LOG((">> comparing against active connection [conn=%x host=%s:%d]\n",
|
||||
conn, conn->ConnectionInfo()->Host(), conn->ConnectionInfo()->Port()));
|
||||
if (conn->ConnectionInfo()->Equals(ci)) {
|
||||
if (conn->CanReuse())
|
||||
count++;
|
||||
else {
|
||||
mIdleConnections.RemoveElementAt(i);
|
||||
NS_RELEASE(conn);
|
||||
i--;
|
||||
}
|
||||
totalCount++;
|
||||
if (conn->IsKeepAlive())
|
||||
persistentCount++;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(("found count=%u\n", (PRUintn) count));
|
||||
return count;
|
||||
LOG((" total-count=%u, persistent-count=%u\n",
|
||||
PRUint32(totalCount), PRUint32(persistentCount)));
|
||||
|
||||
// use >= just to be safe
|
||||
return (totalCount >= mMaxConnectionsPerServer) ||
|
||||
((caps & NS_HTTP_ALLOW_KEEPALIVE) &&
|
||||
(persistentCount >= mMaxPersistentConnectionsPerServer));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1075,6 +1092,12 @@ nsHttpHandler::PrefsChanged(const char *pref)
|
||||
mMaxRequestAttempts = (PRUint16) CLAMP(val, 1, 0xffff);
|
||||
}
|
||||
|
||||
if (bChangedAll || PL_strcmp(pref, "network.http.request.max-start-delay") == 0) {
|
||||
rv = mPrefs->GetIntPref("network.http.request.max-start-delay", &val);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mMaxRequestDelay = (PRUint16) CLAMP(val, 0, 0xffff);
|
||||
}
|
||||
|
||||
if (bChangedAll || PL_strcmp(pref, "network.http.max-connections") == 0) {
|
||||
rv = mPrefs->GetIntPref("network.http.max-connections", &val);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
@@ -1087,18 +1110,10 @@ nsHttpHandler::PrefsChanged(const char *pref)
|
||||
mMaxConnectionsPerServer = (PRUint8) CLAMP(val, 1, 0xff);
|
||||
}
|
||||
|
||||
/*
|
||||
if (bChangedAll || PL_strcmp(pref, "network.http.keep-alive.max-connections") == 0) {
|
||||
rv = mPrefs->GetIntPref("network.http.keep-alive.max-connections", &val);
|
||||
if (bChangedAll || PL_strcmp(pref, "network.http.max-persistent-connections-per-server") == 0) {
|
||||
rv = mPrefs->GetIntPref("network.http.max-persistent-connections-per-server", &val);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mMaxIdleConnections = (PRUint16) CLAMP(val, 1, 0xffff);
|
||||
}
|
||||
*/
|
||||
|
||||
if (bChangedAll || PL_strcmp(pref, "network.http.keep-alive.max-connections-per-server") == 0) {
|
||||
rv = mPrefs->GetIntPref("network.http.keep-alive.max-connections-per-server", &val);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mMaxIdleConnectionsPerServer = (PRUint8) CLAMP(val, 1, 0xff);
|
||||
mMaxPersistentConnectionsPerServer = (PRUint8) CLAMP(val, 1, 0xff);
|
||||
}
|
||||
|
||||
if (bChangedAll || PL_strcmp(pref, "network.http.sendRefererHeader") == 0) {
|
||||
@@ -1787,6 +1802,7 @@ nsPendingTransaction::nsPendingTransaction(nsHttpTransaction *trans,
|
||||
nsHttpConnectionInfo *ci)
|
||||
: mTransaction(trans)
|
||||
, mConnectionInfo(ci)
|
||||
, mBusy(0)
|
||||
{
|
||||
LOG(("Creating nsPendingTransaction @%x\n", this));
|
||||
|
||||
|
||||
@@ -106,9 +106,7 @@ public:
|
||||
// Called to kick-off a new transaction, by default the transaction
|
||||
// will be put on the pending transaction queue if it cannot be
|
||||
// initiated at this time. Callable from any thread.
|
||||
nsresult InitiateTransaction(nsHttpTransaction *,
|
||||
nsHttpConnectionInfo *,
|
||||
PRBool failIfBusy = PR_FALSE);
|
||||
nsresult InitiateTransaction(nsHttpTransaction *, nsHttpConnectionInfo *);
|
||||
|
||||
// Called to cancel a transaction, which may or may not be assigned to
|
||||
// a connection. Callable from any thread.
|
||||
@@ -118,6 +116,10 @@ public:
|
||||
// from any thread.
|
||||
nsresult ReclaimConnection(nsHttpConnection *);
|
||||
|
||||
// Called when a connection has been busy with a single transaction for
|
||||
// longer than mMaxRequestDelay.
|
||||
nsresult ProcessTransactionQ();
|
||||
|
||||
//
|
||||
// The HTTP handler caches pointers to specific XPCOM services, and
|
||||
// provides the following helper routines for accessing those services:
|
||||
@@ -150,15 +152,19 @@ private:
|
||||
nsHttpTransaction *Transaction() { return mTransaction; }
|
||||
nsHttpConnectionInfo *ConnectionInfo() { return mConnectionInfo; }
|
||||
|
||||
PRBool IsBusy() { return mBusy; }
|
||||
void SetBusy(PRBool value) { mBusy = value; }
|
||||
|
||||
private:
|
||||
nsHttpTransaction *mTransaction;
|
||||
nsHttpConnectionInfo *mConnectionInfo;
|
||||
PRPackedBool mBusy;
|
||||
};
|
||||
|
||||
//
|
||||
// Transaction queue helper methods
|
||||
//
|
||||
void ProcessTransactionQ();
|
||||
void ProcessTransactionQ_Locked();
|
||||
nsresult EnqueueTransaction(nsHttpTransaction *, nsHttpConnectionInfo *);
|
||||
|
||||
// Called with mConnectionLock held
|
||||
@@ -167,10 +173,7 @@ private:
|
||||
PRBool failIfBusy = PR_FALSE);
|
||||
|
||||
nsresult RemovePendingTransaction(nsHttpTransaction *);
|
||||
|
||||
PRUint8 CountActiveConnections(nsHttpConnectionInfo *);
|
||||
PRUint8 CountIdleConnections(nsHttpConnectionInfo *);
|
||||
|
||||
PRBool AtActiveConnectionLimit(nsHttpConnectionInfo *, PRUint8 caps);
|
||||
void DropConnections(nsVoidArray &);
|
||||
|
||||
//
|
||||
@@ -213,10 +216,11 @@ private:
|
||||
|
||||
PRUint16 mIdleTimeout;
|
||||
PRUint16 mMaxRequestAttempts;
|
||||
PRUint16 mMaxRequestDelay;
|
||||
|
||||
PRUint16 mMaxConnections;
|
||||
PRUint8 mMaxConnectionsPerServer;
|
||||
PRUint8 mMaxIdleConnectionsPerServer;
|
||||
PRUint8 mMaxPersistentConnectionsPerServer;
|
||||
|
||||
nsCString mAccept;
|
||||
nsCString mAcceptLanguages;
|
||||
@@ -230,10 +234,10 @@ private:
|
||||
PRUint32 mSessionStartTime;
|
||||
|
||||
// connection management
|
||||
nsVoidArray mActiveConnections; // list of nsHttpConnection objects
|
||||
nsVoidArray mIdleConnections; // list of nsHttpConnection objects
|
||||
nsVoidArray mTransactionQ; // list of nsPendingTransaction objects
|
||||
PRLock *mConnectionLock; // protect connection lists
|
||||
nsVoidArray mActiveConnections; // list of nsHttpConnection objects
|
||||
nsVoidArray mIdleConnections; // list of nsHttpConnection objects
|
||||
nsVoidArray mTransactionQ; // list of nsPendingTransaction objects
|
||||
PRLock *mConnectionLock; // protect connection lists
|
||||
|
||||
// useragent components
|
||||
nsXPIDLCString mAppName;
|
||||
|
||||
@@ -38,7 +38,8 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsHttpTransaction::nsHttpTransaction(nsIStreamListener *listener,
|
||||
nsIInterfaceRequestor *callbacks)
|
||||
nsIInterfaceRequestor *callbacks,
|
||||
PRUint8 caps)
|
||||
: mListener(listener)
|
||||
, mCallbacks(callbacks)
|
||||
, mConnection(nsnull)
|
||||
@@ -49,6 +50,7 @@ nsHttpTransaction::nsHttpTransaction(nsIStreamListener *listener,
|
||||
, mTransactionDone(0)
|
||||
, mStatus(NS_OK)
|
||||
, mRestartCount(0)
|
||||
, mCapabilities(caps)
|
||||
, mHaveStatusLine(PR_FALSE)
|
||||
, mHaveAllHeaders(PR_FALSE)
|
||||
, mFiredOnStart(PR_FALSE)
|
||||
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
NS_DECL_NSIINPUTSTREAM
|
||||
|
||||
// A transaction is constructed from request headers.
|
||||
nsHttpTransaction(nsIStreamListener *, nsIInterfaceRequestor *);
|
||||
nsHttpTransaction(nsIStreamListener *, nsIInterfaceRequestor *, PRUint8 caps);
|
||||
virtual ~nsHttpTransaction();
|
||||
|
||||
nsrefcnt RefCnt() { return mRefCnt; }
|
||||
@@ -75,6 +75,7 @@ public:
|
||||
nsISupports *SecurityInfo() { return mSecurityInfo; }
|
||||
PRBool IsDone() { return mTransactionDone; }
|
||||
nsresult Status() { return mStatus; }
|
||||
PRUint8 Capabilities() { return mCapabilities; }
|
||||
|
||||
// Called to take ownership of the response headers; the transaction
|
||||
// will drop any reference to the response headers after this call.
|
||||
@@ -127,6 +128,7 @@ private:
|
||||
nsresult mStatus;
|
||||
|
||||
PRUint16 mRestartCount; // the number of times this transaction has been restarted
|
||||
PRUint8 mCapabilities;
|
||||
|
||||
PRPackedBool mHaveStatusLine;
|
||||
PRPackedBool mHaveAllHeaders;
|
||||
|
||||
Reference in New Issue
Block a user