зеркало из https://github.com/mozilla/gecko-dev.git
bug 623948 - Accelerate TCP connection retries in HTTP r=honzab
This commit is contained in:
Родитель
60a2b4eaa8
Коммит
82e6a8e87c
|
@ -753,6 +753,11 @@ pref("network.http.prompt-temp-redirect", true);
|
|||
// Section 4.8 "High-Throughput Data Service Class"
|
||||
pref("network.http.qos", 0);
|
||||
|
||||
// The number of milliseconds after sending a SYN for an HTTP connection,
|
||||
// to wait before trying a different connection. 0 means do not use a second
|
||||
// connection.
|
||||
pref("network.http.connection-retry-timeout", 250);
|
||||
|
||||
// default values for FTP
|
||||
// in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
|
||||
// Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)
|
||||
|
|
|
@ -1797,9 +1797,21 @@ nsSocketTransport::GetSecurityCallbacks(nsIInterfaceRequestor **callbacks)
|
|||
NS_IMETHODIMP
|
||||
nsSocketTransport::SetSecurityCallbacks(nsIInterfaceRequestor *callbacks)
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
mCallbacks = callbacks;
|
||||
// XXX should we tell PSM about this?
|
||||
nsCOMPtr<nsISupports> secinfo;
|
||||
{
|
||||
nsAutoLock lock(mLock);
|
||||
mCallbacks = callbacks;
|
||||
SOCKET_LOG(("Reset callbacks for secinfo=%p callbacks=%p\n",
|
||||
mSecInfo.get(), mCallbacks.get()));
|
||||
|
||||
secinfo = mSecInfo;
|
||||
}
|
||||
|
||||
// don't call into PSM while holding mLock!!
|
||||
nsCOMPtr<nsISSLSocketControl> secCtrl(do_QueryInterface(secinfo));
|
||||
if (secCtrl)
|
||||
secCtrl->SetNotificationCallbacks(callbacks);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ class nsAHttpConnection;
|
|||
class nsAHttpSegmentReader;
|
||||
class nsAHttpSegmentWriter;
|
||||
class nsIInterfaceRequestor;
|
||||
class nsIEventTarget;
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Abstract base class for a HTTP transaction:
|
||||
|
@ -62,7 +63,8 @@ public:
|
|||
|
||||
// called by the connection to get security callbacks to set on the
|
||||
// socket transport.
|
||||
virtual void GetSecurityCallbacks(nsIInterfaceRequestor **) = 0;
|
||||
virtual void GetSecurityCallbacks(nsIInterfaceRequestor **,
|
||||
nsIEventTarget **) = 0;
|
||||
|
||||
// called to report socket status (see nsITransportEventSink)
|
||||
virtual void OnTransportStatus(nsresult status, PRUint64 progress) = 0;
|
||||
|
@ -88,7 +90,8 @@ public:
|
|||
|
||||
#define NS_DECL_NSAHTTPTRANSACTION \
|
||||
void SetConnection(nsAHttpConnection *); \
|
||||
void GetSecurityCallbacks(nsIInterfaceRequestor **); \
|
||||
void GetSecurityCallbacks(nsIInterfaceRequestor **, \
|
||||
nsIEventTarget **); \
|
||||
void OnTransportStatus(nsresult status, PRUint64 progress); \
|
||||
PRBool IsDone(); \
|
||||
nsresult Status(); \
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "netCore.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "nsProxyRelease.h"
|
||||
#include "prmem.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -66,9 +67,10 @@ static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
|
|||
|
||||
nsHttpConnection::nsHttpConnection()
|
||||
: mTransaction(nsnull)
|
||||
, mConnInfo(nsnull)
|
||||
, mLastReadTime(0)
|
||||
, mIdleTimeout(0)
|
||||
, mConsiderReusedAfterInterval(0)
|
||||
, mConsiderReusedAfterEpoch(0)
|
||||
, mKeepAlive(PR_TRUE) // assume to keep-alive by default
|
||||
, mKeepAliveMask(PR_TRUE)
|
||||
, mSupportsPipelining(PR_FALSE) // assume low-grade server
|
||||
|
@ -87,8 +89,11 @@ nsHttpConnection::~nsHttpConnection()
|
|||
{
|
||||
LOG(("Destroying nsHttpConnection @%x\n", this));
|
||||
|
||||
NS_IF_RELEASE(mConnInfo);
|
||||
NS_IF_RELEASE(mTransaction);
|
||||
if (mCallbacks) {
|
||||
nsIInterfaceRequestor *cbs = nsnull;
|
||||
mCallbacks.swap(cbs);
|
||||
NS_ProxyRelease(mCallbackTarget, cbs);
|
||||
}
|
||||
|
||||
// release our reference to the handler
|
||||
nsHttpHandler *handler = gHttpHandler;
|
||||
|
@ -96,18 +101,38 @@ nsHttpConnection::~nsHttpConnection()
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnection::Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime)
|
||||
nsHttpConnection::Init(nsHttpConnectionInfo *info,
|
||||
PRUint16 maxHangTime,
|
||||
nsISocketTransport *transport,
|
||||
nsIAsyncInputStream *instream,
|
||||
nsIAsyncOutputStream *outstream,
|
||||
nsIInterfaceRequestor *callbacks,
|
||||
nsIEventTarget *callbackTarget)
|
||||
{
|
||||
LOG(("nsHttpConnection::Init [this=%x]\n", this));
|
||||
NS_ABORT_IF_FALSE(transport && instream && outstream,
|
||||
"invalid socket information");
|
||||
LOG(("nsHttpConnection::Init [this=%p "
|
||||
"transport=%p instream=%p outstream=%p]\n",
|
||||
this, transport, instream, outstream));
|
||||
|
||||
NS_ENSURE_ARG_POINTER(info);
|
||||
NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
|
||||
|
||||
mConnInfo = info;
|
||||
NS_ADDREF(mConnInfo);
|
||||
|
||||
mMaxHangTime = maxHangTime;
|
||||
mLastReadTime = NowInSeconds();
|
||||
|
||||
mSocketTransport = transport;
|
||||
mSocketIn = instream;
|
||||
mSocketOut = outstream;
|
||||
nsresult rv = mSocketTransport->SetEventSink(this, nsnull);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mCallbacks = callbacks;
|
||||
mCallbackTarget = callbackTarget;
|
||||
rv = mSocketTransport->SetSecurityCallbacks(this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -117,6 +142,7 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps)
|
|||
{
|
||||
nsresult rv;
|
||||
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
LOG(("nsHttpConnection::Activate [this=%x trans=%x caps=%x]\n",
|
||||
this, trans, caps));
|
||||
|
||||
|
@ -125,32 +151,24 @@ nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps)
|
|||
|
||||
// take ownership of the transaction
|
||||
mTransaction = trans;
|
||||
NS_ADDREF(mTransaction);
|
||||
|
||||
// set mKeepAlive according to what will be requested
|
||||
mKeepAliveMask = mKeepAlive = (caps & NS_HTTP_ALLOW_KEEPALIVE);
|
||||
|
||||
// if we don't have a socket transport then create a new one
|
||||
if (!mSocketTransport) {
|
||||
rv = CreateTransport(caps);
|
||||
if (NS_FAILED(rv))
|
||||
goto loser;
|
||||
}
|
||||
|
||||
// need to handle SSL proxy CONNECT if this is the first time.
|
||||
if (mConnInfo->UsingSSL() && mConnInfo->UsingHttpProxy() && !mCompletedSSLConnect) {
|
||||
rv = SetupSSLProxyConnect();
|
||||
if (NS_FAILED(rv))
|
||||
goto loser;
|
||||
goto failed_activation;
|
||||
}
|
||||
|
||||
// wait for the output stream to be readable
|
||||
rv = mSocketOut->AsyncWait(this, 0, 0, nsnull);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
return rv;
|
||||
rv = OnOutputStreamReady(mSocketOut);
|
||||
|
||||
failed_activation:
|
||||
if (NS_FAILED(rv)) {
|
||||
mTransaction = nsnull;
|
||||
}
|
||||
|
||||
loser:
|
||||
NS_RELEASE(mTransaction);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -159,7 +177,7 @@ nsHttpConnection::Close(nsresult reason)
|
|||
{
|
||||
LOG(("nsHttpConnection::Close [this=%x reason=%x]\n", this, reason));
|
||||
|
||||
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
if (NS_FAILED(reason)) {
|
||||
if (mSocketTransport) {
|
||||
|
@ -193,8 +211,25 @@ nsHttpConnection::ProxyStartSSL()
|
|||
PRBool
|
||||
nsHttpConnection::CanReuse()
|
||||
{
|
||||
return IsKeepAlive() && (NowInSeconds() - mLastReadTime < mIdleTimeout)
|
||||
&& IsAlive();
|
||||
PRBool canReuse = IsKeepAlive() &&
|
||||
(NowInSeconds() - mLastReadTime < mIdleTimeout) &&
|
||||
IsAlive();
|
||||
|
||||
// An idle persistent connection should not have data waiting to be read
|
||||
// before a request is sent. Data here is likely a 408 timeout response
|
||||
// which we would deal with later on through the restart logic, but that
|
||||
// path is more expensive than just closing the socket now. SSL check can
|
||||
// be removed with fixing of 631801
|
||||
|
||||
PRUint32 dataSize;
|
||||
if (canReuse && mSocketIn && !mConnInfo->UsingSSL() &&
|
||||
NS_SUCCEEDED(mSocketIn->Available(&dataSize)) && dataSize) {
|
||||
LOG(("nsHttpConnection::CanReuse %p %s"
|
||||
"Socket not reusable because read data pending (%d) on it.\n",
|
||||
this, mConnInfo->Host(), dataSize));
|
||||
canReuse = PR_FALSE;
|
||||
}
|
||||
return canReuse;
|
||||
}
|
||||
|
||||
PRUint32 nsHttpConnection::TimeToLive()
|
||||
|
@ -387,7 +422,7 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
|
|||
// processing a transaction pipeline until after the first HTTP/1.1
|
||||
// response.
|
||||
nsHttpTransaction *trans =
|
||||
static_cast<nsHttpTransaction *>(mTransaction);
|
||||
static_cast<nsHttpTransaction *>(mTransaction.get());
|
||||
trans->SetSSLConnectFailed();
|
||||
}
|
||||
}
|
||||
|
@ -395,6 +430,27 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHttpConnection::IsReused()
|
||||
{
|
||||
if (mIsReused)
|
||||
return PR_TRUE;
|
||||
if (!mConsiderReusedAfterInterval)
|
||||
return PR_FALSE;
|
||||
|
||||
// ReusedAfter allows a socket to be consider reused only after a certain
|
||||
// interval of time has passed
|
||||
return (PR_IntervalNow() - mConsiderReusedAfterEpoch) >=
|
||||
mConsiderReusedAfterInterval;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnection::SetIsReusedAfter(PRUint32 afterMilliseconds)
|
||||
{
|
||||
mConsiderReusedAfterEpoch = PR_IntervalNow();
|
||||
mConsiderReusedAfterInterval = PR_MillisecondsToInterval(afterMilliseconds);
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnection::GetSecurityInfo(nsISupports **secinfo)
|
||||
{
|
||||
|
@ -438,69 +494,6 @@ nsHttpConnection::ResumeRecv()
|
|||
// nsHttpConnection <private>
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
nsHttpConnection::CreateTransport(PRUint8 caps)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
NS_PRECONDITION(!mSocketTransport, "unexpected");
|
||||
|
||||
nsCOMPtr<nsISocketTransportService> sts =
|
||||
do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// configure the socket type based on the connection type requested.
|
||||
const char* types[1];
|
||||
|
||||
if (mConnInfo->UsingSSL())
|
||||
types[0] = "ssl";
|
||||
else
|
||||
types[0] = gHttpHandler->DefaultSocketType();
|
||||
|
||||
nsCOMPtr<nsISocketTransport> strans;
|
||||
PRUint32 typeCount = (types[0] != nsnull);
|
||||
|
||||
rv = sts->CreateTransport(types, typeCount,
|
||||
nsDependentCString(mConnInfo->Host()),
|
||||
mConnInfo->Port(),
|
||||
mConnInfo->ProxyInfo(),
|
||||
getter_AddRefs(strans));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
PRUint32 tmpFlags = 0;
|
||||
if (caps & NS_HTTP_REFRESH_DNS)
|
||||
tmpFlags = nsISocketTransport::BYPASS_CACHE;
|
||||
|
||||
if (caps & NS_HTTP_LOAD_ANONYMOUS)
|
||||
tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
|
||||
|
||||
strans->SetConnectionFlags(tmpFlags);
|
||||
|
||||
strans->SetQoSBits(gHttpHandler->GetQoSBits());
|
||||
|
||||
// NOTE: these create cyclical references, which we break inside
|
||||
// nsHttpConnection::Close
|
||||
rv = strans->SetEventSink(this, nsnull);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
rv = strans->SetSecurityCallbacks(this);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// next open the socket streams
|
||||
nsCOMPtr<nsIOutputStream> sout;
|
||||
rv = strans->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
|
||||
getter_AddRefs(sout));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
nsCOMPtr<nsIInputStream> sin;
|
||||
rv = strans->OpenInputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
|
||||
getter_AddRefs(sin));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
mSocketTransport = strans;
|
||||
mSocketIn = do_QueryInterface(sin);
|
||||
mSocketOut = do_QueryInterface(sout);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
|
||||
{
|
||||
|
@ -515,9 +508,7 @@ nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
|
|||
reason = NS_OK;
|
||||
|
||||
mTransaction->Close(reason);
|
||||
|
||||
NS_RELEASE(mTransaction);
|
||||
mTransaction = 0;
|
||||
mTransaction = nsnull;
|
||||
|
||||
if (NS_FAILED(reason))
|
||||
Close(reason);
|
||||
|
@ -728,7 +719,8 @@ nsHttpConnection::SetupSSLProxyConnect()
|
|||
|
||||
// NOTE: this cast is valid since this connection cannot be processing a
|
||||
// transaction pipeline until after the first HTTP/1.1 response.
|
||||
nsHttpTransaction *trans = static_cast<nsHttpTransaction *>(mTransaction);
|
||||
nsHttpTransaction *trans =
|
||||
static_cast<nsHttpTransaction *>(mTransaction.get());
|
||||
|
||||
val = trans->RequestHead()->PeekHeader(nsHttp::Host);
|
||||
if (val) {
|
||||
|
@ -792,8 +784,8 @@ nsHttpConnection::OnInputStreamReady(nsIAsyncInputStream *in)
|
|||
NS_IMETHODIMP
|
||||
nsHttpConnection::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
{
|
||||
NS_ASSERTION(out == mSocketOut, "unexpected stream");
|
||||
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
NS_ABORT_IF_FALSE(out == mSocketOut, "unexpected socket");
|
||||
|
||||
// if the transaction was dropped...
|
||||
if (!mTransaction) {
|
||||
|
@ -835,14 +827,14 @@ nsHttpConnection::GetInterface(const nsIID &iid, void **result)
|
|||
// the socket transport thread. If that weren't the case, then we'd
|
||||
// have to worry about the possibility of mTransaction going away
|
||||
// part-way through this function call. See CloseTransaction.
|
||||
|
||||
// NOTE - there is a bug here, the call to getinterface is proxied off the
|
||||
// nss thread, not the ui thread as the above comment says. So there is
|
||||
// indeed a chance of mTransaction going away. bug 615342
|
||||
|
||||
NS_ASSERTION(PR_GetCurrentThread() != gSocketThread, "wrong thread");
|
||||
|
||||
if (mTransaction) {
|
||||
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
||||
mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks));
|
||||
if (callbacks)
|
||||
return callbacks->GetInterface(iid, result);
|
||||
}
|
||||
|
||||
if (mCallbacks)
|
||||
return mCallbacks->GetInterface(iid, result);
|
||||
return NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
|
|
@ -47,12 +47,14 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "prlock.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "prinrval.h"
|
||||
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIAsyncOutputStream.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIEventTarget.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpConnection - represents a connection to a HTTP server (or proxy)
|
||||
|
@ -85,7 +87,10 @@ public:
|
|||
// 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);
|
||||
nsresult Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime,
|
||||
nsISocketTransport *, nsIAsyncInputStream *,
|
||||
nsIAsyncOutputStream *, nsIInterfaceRequestor *,
|
||||
nsIEventTarget *);
|
||||
|
||||
// Activate causes the given transaction to be processed on this
|
||||
// connection. It fails if there is already an existing transaction.
|
||||
|
@ -128,7 +133,9 @@ public:
|
|||
void GetConnectionInfo(nsHttpConnectionInfo **ci) { NS_IF_ADDREF(*ci = mConnInfo); }
|
||||
void GetSecurityInfo(nsISupports **);
|
||||
PRBool IsPersistent() { return IsKeepAlive(); }
|
||||
PRBool IsReused() { return mIsReused; }
|
||||
PRBool IsReused();
|
||||
void SetIsReusedAfter(PRUint32 afterMilliseconds);
|
||||
void SetIdleTimeout(PRUint16 val) {mIdleTimeout = val;}
|
||||
nsresult PushBack(const char *data, PRUint32 length) { NS_NOTREACHED("PushBack"); return NS_ERROR_UNEXPECTED; }
|
||||
nsresult ResumeSend();
|
||||
nsresult ResumeRecv();
|
||||
|
@ -140,7 +147,6 @@ private:
|
|||
// called to cause the underlying socket to start speaking SSL
|
||||
nsresult ProxyStartSSL();
|
||||
|
||||
nsresult CreateTransport(PRUint8 caps);
|
||||
nsresult OnTransactionDone(nsresult reason);
|
||||
nsresult OnSocketWritable();
|
||||
nsresult OnSocketReadable();
|
||||
|
@ -161,12 +167,20 @@ private:
|
|||
nsCOMPtr<nsIInputStream> mSSLProxyConnectStream;
|
||||
nsCOMPtr<nsIInputStream> mRequestStream;
|
||||
|
||||
nsAHttpTransaction *mTransaction; // hard ref
|
||||
nsHttpConnectionInfo *mConnInfo; // hard ref
|
||||
// mTransaction only points to the HTTP Transaction callbacks if the
|
||||
// transaction is open, otherwise it is null.
|
||||
nsRefPtr<nsAHttpTransaction> mTransaction;
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
|
||||
nsCOMPtr<nsIEventTarget> mCallbackTarget;
|
||||
|
||||
nsRefPtr<nsHttpConnectionInfo> mConnInfo;
|
||||
|
||||
PRUint32 mLastReadTime;
|
||||
PRUint16 mMaxHangTime; // max download time before dropping keep-alive status
|
||||
PRUint16 mIdleTimeout; // value of keep-alive: timeout=
|
||||
PRIntervalTime mConsiderReusedAfterInterval;
|
||||
PRIntervalTime mConsiderReusedAfterEpoch;
|
||||
|
||||
PRPackedBool mKeepAlive;
|
||||
PRPackedBool mKeepAliveMask;
|
||||
|
|
|
@ -400,13 +400,21 @@ nsHttpConnectionMgr::ProcessOneTransactionCB(nsHashKey *key, void *data, void *c
|
|||
return kHashEnumerateNext;
|
||||
}
|
||||
|
||||
// If the global number of idle connections is preventing the opening of
|
||||
// new connections to a host without idle connections, then
|
||||
// close them regardless of their TTL
|
||||
PRIntn
|
||||
nsHttpConnectionMgr::PurgeOneIdleConnectionCB(nsHashKey *key, void *data, void *closure)
|
||||
nsHttpConnectionMgr::PurgeExcessIdleConnectionsCB(nsHashKey *key,
|
||||
void *data, void *closure)
|
||||
{
|
||||
nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
|
||||
nsConnectionEntry *ent = (nsConnectionEntry *) data;
|
||||
|
||||
if (ent->mIdleConns.Length() > 0) {
|
||||
while (self->mNumIdleConns + self->mNumActiveConns + 1 >= self->mMaxConns) {
|
||||
if (!ent->mIdleConns.Length()) {
|
||||
// There are no idle conns left in this connection entry
|
||||
return kHashEnumerateNext;
|
||||
}
|
||||
nsHttpConnection *conn = ent->mIdleConns[0];
|
||||
ent->mIdleConns.RemoveElementAt(0);
|
||||
conn->Close(NS_ERROR_ABORT);
|
||||
|
@ -414,10 +422,8 @@ nsHttpConnectionMgr::PurgeOneIdleConnectionCB(nsHashKey *key, void *data, void *
|
|||
self->mNumIdleConns--;
|
||||
if (0 == self->mNumIdleConns)
|
||||
self->StopPruneDeadConnectionsTimer();
|
||||
return kHashEnumerateStop;
|
||||
}
|
||||
|
||||
return kHashEnumerateNext;
|
||||
return kHashEnumerateStop;
|
||||
}
|
||||
|
||||
PRIntn
|
||||
|
@ -474,6 +480,7 @@ nsHttpConnectionMgr::PruneDeadConnectionsCB(nsHashKey *key, void *data, void *cl
|
|||
// if this entry is empty, then we can remove it.
|
||||
if (ent->mIdleConns.Length() == 0 &&
|
||||
ent->mActiveConns.Length() == 0 &&
|
||||
ent->mHalfOpens.Length() == 0 &&
|
||||
ent->mPendingQ.Length() == 0) {
|
||||
LOG((" removing empty connection entry\n"));
|
||||
delete ent;
|
||||
|
@ -533,6 +540,10 @@ nsHttpConnectionMgr::ShutdownPassCB(nsHashKey *key, void *data, void *closure)
|
|||
NS_RELEASE(trans);
|
||||
}
|
||||
|
||||
// close all half open tcp connections
|
||||
for (PRInt32 i = ((PRInt32) ent->mHalfOpens.Length()) - 1; i >= 0; i--)
|
||||
ent->mHalfOpens[i]->Abandon();
|
||||
|
||||
delete ent;
|
||||
return kHashEnumerateRemove;
|
||||
}
|
||||
|
@ -552,7 +563,7 @@ nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
|
|||
nsHttpConnection *conn = nsnull;
|
||||
for (i=0; i<count; ++i) {
|
||||
trans = ent->mPendingQ[i];
|
||||
GetConnection(ent, trans->Caps(), &conn);
|
||||
GetConnection(ent, trans, &conn);
|
||||
if (conn)
|
||||
break;
|
||||
}
|
||||
|
@ -592,8 +603,8 @@ nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 cap
|
|||
LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
|
||||
ci->HashKey().get(), caps));
|
||||
|
||||
// If we have more active connections than the limit, then we're done --
|
||||
// purging idle connections won't get us below it.
|
||||
// If there are more active connections than the global limit, then we're
|
||||
// done. Purging idle connections won't get us below it.
|
||||
if (mNumActiveConns >= mMaxConns) {
|
||||
LOG((" num active conns == max conns\n"));
|
||||
return PR_TRUE;
|
||||
|
@ -611,6 +622,11 @@ nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 cap
|
|||
persistCount++;
|
||||
}
|
||||
|
||||
// Add in the in-progress tcp connections, we will assume they are
|
||||
// keepalive enabled.
|
||||
totalCount += ent->mHalfOpens.Length();
|
||||
persistCount += ent->mHalfOpens.Length();
|
||||
|
||||
LOG((" total=%d, persist=%d\n", totalCount, persistCount));
|
||||
|
||||
PRUint16 maxConns;
|
||||
|
@ -631,18 +647,26 @@ nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 cap
|
|||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent, PRUint8 caps,
|
||||
nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
|
||||
nsHttpTransaction *trans,
|
||||
nsHttpConnection **result)
|
||||
{
|
||||
LOG(("nsHttpConnectionMgr::GetConnection [ci=%s caps=%x]\n",
|
||||
ent->mConnInfo->HashKey().get(), PRUint32(caps)));
|
||||
ent->mConnInfo->HashKey().get(), PRUint32(trans->Caps())));
|
||||
|
||||
// First, see if an idle persistent connection may be reused instead of
|
||||
// establishing a new socket. We do not need to check the connection limits
|
||||
// yet as they govern the maximum number of open connections and reusing
|
||||
// an old connection never increases that.
|
||||
|
||||
*result = nsnull;
|
||||
|
||||
nsHttpConnection *conn = nsnull;
|
||||
|
||||
if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
|
||||
// search the idle connection list
|
||||
if (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) {
|
||||
// search the idle connection list. Each element in the list
|
||||
// has a reference, so if we remove it from the list into a local
|
||||
// ptr, that ptr now owns the reference
|
||||
while (!conn && (ent->mIdleConns.Length() > 0)) {
|
||||
conn = ent->mIdleConns[0];
|
||||
// we check if the connection can be reused before even checking if
|
||||
|
@ -672,31 +696,68 @@ nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent, PRUint8 caps,
|
|||
// XXX this just purges a random idle connection. we should instead
|
||||
// enumerate the entire hash table to find the eldest idle connection.
|
||||
if (mNumIdleConns && mNumIdleConns + mNumActiveConns + 1 >= mMaxConns)
|
||||
mCT.Enumerate(PurgeOneIdleConnectionCB, this);
|
||||
mCT.Enumerate(PurgeExcessIdleConnectionsCB, this);
|
||||
|
||||
// Need to make a new TCP connection. First, we check if we've hit
|
||||
// either the maximum connection limit globally or for this particular
|
||||
// host or proxy. If we have, we're done.
|
||||
if (AtActiveConnectionLimit(ent, caps)) {
|
||||
LOG((" at active connection limit!\n"));
|
||||
if (AtActiveConnectionLimit(ent, trans->Caps())) {
|
||||
LOG(("nsHttpConnectionMgr::GetConnection [ci = %s]"
|
||||
"at active connection limit - will queue\n",
|
||||
ent->mConnInfo->HashKey().get()));
|
||||
return;
|
||||
}
|
||||
|
||||
conn = new nsHttpConnection();
|
||||
if (!conn)
|
||||
return;
|
||||
NS_ADDREF(conn);
|
||||
|
||||
nsresult rv = conn->Init(ent->mConnInfo, mMaxRequestDelay);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_RELEASE(conn);
|
||||
return;
|
||||
}
|
||||
nsresult rv = CreateTransport(ent, trans);
|
||||
if (NS_FAILED(rv))
|
||||
trans->Close(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
// hold an owning ref to this connection
|
||||
ent->mActiveConns.AppendElement(conn);
|
||||
mNumActiveConns++;
|
||||
NS_ADDREF(conn);
|
||||
|
||||
*result = conn;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
|
||||
nsConnectionEntry *ent)
|
||||
{
|
||||
NS_ADDREF(conn);
|
||||
ent->mActiveConns.AppendElement(conn);
|
||||
mNumActiveConns++;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::StartedConnect()
|
||||
{
|
||||
mNumActiveConns++;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::RecvdConnect()
|
||||
{
|
||||
mNumActiveConns--;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
|
||||
nsHttpTransaction *trans)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans);
|
||||
nsresult rv = sock->SetupPrimaryStreams();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
sock->SetupBackupTimer();
|
||||
ent->mHalfOpens.AppendElement(sock);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
|
||||
nsAHttpTransaction *trans,
|
||||
|
@ -718,11 +779,6 @@ nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
|
|||
trans = pipeline;
|
||||
}
|
||||
|
||||
// hold an owning ref to this connection
|
||||
ent->mActiveConns.AppendElement(conn);
|
||||
mNumActiveConns++;
|
||||
NS_ADDREF(conn);
|
||||
|
||||
// give the transaction the indirect reference to the connection.
|
||||
trans->SetConnection(handle);
|
||||
|
||||
|
@ -795,6 +851,8 @@ nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
|
|||
nsresult
|
||||
nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
// since "adds" and "cancels" are processed asynchronously and because
|
||||
// various events might trigger an "add" directly on the socket thread,
|
||||
// we must take care to avoid dispatching a transaction that has already
|
||||
|
@ -837,18 +895,9 @@ nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
|
|||
|
||||
// destroy connection handle.
|
||||
trans->SetConnection(nsnull);
|
||||
|
||||
// remove sticky connection from active connection list; we'll add it
|
||||
// right back in DispatchTransaction.
|
||||
if (ent->mActiveConns.RemoveElement(conn))
|
||||
mNumActiveConns--;
|
||||
else {
|
||||
NS_ERROR("sticky connection not found in active list");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
else
|
||||
GetConnection(ent, caps, &conn);
|
||||
GetConnection(ent, trans, &conn);
|
||||
|
||||
nsresult rv;
|
||||
if (!conn) {
|
||||
|
@ -998,13 +1047,22 @@ nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
|
|||
|
||||
NS_ASSERTION(ent, "no connection entry");
|
||||
if (ent) {
|
||||
ent->mActiveConns.RemoveElement(conn);
|
||||
mNumActiveConns--;
|
||||
// If the connection is in the active list, remove that entry
|
||||
// and the reference held by the mActiveConns list.
|
||||
// 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->mActiveConns.RemoveElement(conn)) {
|
||||
nsHttpConnection *temp = conn;
|
||||
NS_RELEASE(temp);
|
||||
mNumActiveConns--;
|
||||
}
|
||||
|
||||
if (conn->CanReuse()) {
|
||||
LOG((" adding connection to idle list\n"));
|
||||
// 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.
|
||||
NS_ADDREF(conn);
|
||||
ent->mIdleConns.AppendElement(conn);
|
||||
mNumIdleConns++;
|
||||
// If the added connection was first idle connection or has shortest
|
||||
|
@ -1018,8 +1076,6 @@ nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
|
|||
LOG((" connection cannot be reused; closing connection\n"));
|
||||
// make sure the connection is closed and release our reference.
|
||||
conn->Close(NS_ERROR_ABORT);
|
||||
nsHttpConnection *temp = conn;
|
||||
NS_RELEASE(temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1130,6 +1186,323 @@ nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufL
|
|||
return mConn->PushBack(buf, bufLen);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////// nsHalfOpenSocket
|
||||
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpConnectionMgr::nsHalfOpenSocket,
|
||||
nsIOutputStreamCallback,
|
||||
nsITransportEventSink,
|
||||
nsIInterfaceRequestor,
|
||||
nsITimerCallback)
|
||||
|
||||
nsHttpConnectionMgr::
|
||||
nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
|
||||
nsHttpTransaction *trans)
|
||||
: mEnt(ent),
|
||||
mTransaction(trans)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(ent && trans, "constructor with null arguments");
|
||||
LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s]\n",
|
||||
this, trans, ent->mConnInfo->Host()));
|
||||
}
|
||||
|
||||
nsHttpConnectionMgr::nsHalfOpenSocket::~nsHalfOpenSocket()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!mStreamOut, "streamout not null");
|
||||
NS_ABORT_IF_FALSE(!mBackupStreamOut, "backupstreamout not null");
|
||||
NS_ABORT_IF_FALSE(!mSynTimer, "syntimer not null");
|
||||
LOG(("Destroying nsHalfOpenSocket [this=%p]\n", this));
|
||||
|
||||
if (mEnt) {
|
||||
PRInt32 index = mEnt->mHalfOpens.IndexOf(this);
|
||||
NS_ABORT_IF_FALSE(index != -1, "half open complete but no item");
|
||||
mEnt->mHalfOpens.RemoveElementAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::
|
||||
nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
|
||||
nsIAsyncInputStream **instream,
|
||||
nsIAsyncOutputStream **outstream)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
const char* types[1];
|
||||
types[0] = (mEnt->mConnInfo->UsingSSL()) ?
|
||||
"ssl" : gHttpHandler->DefaultSocketType();
|
||||
PRUint32 typeCount = (types[0] != nsnull);
|
||||
|
||||
nsCOMPtr<nsISocketTransport> socketTransport;
|
||||
nsCOMPtr<nsISocketTransportService> sts;
|
||||
|
||||
sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = sts->CreateTransport(types, typeCount,
|
||||
nsDependentCString(mEnt->mConnInfo->Host()),
|
||||
mEnt->mConnInfo->Port(),
|
||||
mEnt->mConnInfo->ProxyInfo(),
|
||||
getter_AddRefs(socketTransport));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUint32 tmpFlags = 0;
|
||||
if (mTransaction->Caps() & NS_HTTP_REFRESH_DNS)
|
||||
tmpFlags = nsISocketTransport::BYPASS_CACHE;
|
||||
|
||||
if (mTransaction->Caps() & NS_HTTP_LOAD_ANONYMOUS)
|
||||
tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
|
||||
|
||||
socketTransport->SetConnectionFlags(tmpFlags);
|
||||
|
||||
socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
|
||||
|
||||
rv = socketTransport->SetEventSink(this, nsnull);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = socketTransport->SetSecurityCallbacks(this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIOutputStream> sout;
|
||||
rv = socketTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,
|
||||
0, 0,
|
||||
getter_AddRefs(sout));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIInputStream> sin;
|
||||
rv = socketTransport->OpenInputStream(nsITransport::OPEN_UNBUFFERED,
|
||||
0, 0,
|
||||
getter_AddRefs(sin));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
socketTransport.forget(transport);
|
||||
CallQueryInterface(sin, instream);
|
||||
CallQueryInterface(sout, outstream);
|
||||
|
||||
rv = (*outstream)->AsyncWait(this, 0, 0, nsnull);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
gHttpHandler->ConnMgr()->StartedConnect();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams()
|
||||
{
|
||||
nsresult rv = SetupStreams(getter_AddRefs(mSocketTransport),
|
||||
getter_AddRefs(mStreamIn),
|
||||
getter_AddRefs(mStreamOut));
|
||||
LOG(("nsHalfOpenSocket::SetupPrimaryStream [this=%p ent=%s rv=%x]",
|
||||
this, mEnt->mConnInfo->Host(), rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
if (mStreamOut)
|
||||
mStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
|
||||
mStreamOut = nsnull;
|
||||
mStreamIn = nsnull;
|
||||
mSocketTransport = nsnull;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams()
|
||||
{
|
||||
nsresult rv = SetupStreams(getter_AddRefs(mBackupTransport),
|
||||
getter_AddRefs(mBackupStreamIn),
|
||||
getter_AddRefs(mBackupStreamOut));
|
||||
LOG(("nsHalfOpenSocket::SetupBackupStream [this=%p ent=%s rv=%x]",
|
||||
this, mEnt->mConnInfo->Host(), rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
if (mBackupStreamOut)
|
||||
mBackupStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
|
||||
mBackupStreamOut = nsnull;
|
||||
mBackupStreamIn = nsnull;
|
||||
mBackupTransport = nsnull;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
|
||||
{
|
||||
PRUint16 timeout = gHttpHandler->GetIdleSynTimeout();
|
||||
NS_ABORT_IF_FALSE(!mSynTimer, "timer already initd");
|
||||
if (timeout) {
|
||||
// Setup the timer that will establish a backup socket
|
||||
// if we do not get a writable event on the main one.
|
||||
// We do this because a lost SYN takes a very long time
|
||||
// to repair at the TCP level.
|
||||
//
|
||||
// Failure to setup the timer is something we can live with,
|
||||
// so don't return an error in that case.
|
||||
nsresult rv;
|
||||
mSynTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mSynTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpConnectionMgr::nsHalfOpenSocket::Abandon()
|
||||
{
|
||||
LOG(("nsHalfOpenSocket::Abandon [this=%p ent=%s]",
|
||||
this, mEnt->mConnInfo->Host()));
|
||||
nsRefPtr<nsHalfOpenSocket> deleteProtector(this);
|
||||
|
||||
if (mStreamOut) {
|
||||
gHttpHandler->ConnMgr()->RecvdConnect();
|
||||
mStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
|
||||
mStreamOut = nsnull;
|
||||
}
|
||||
if (mBackupStreamOut) {
|
||||
gHttpHandler->ConnMgr()->RecvdConnect();
|
||||
mBackupStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
|
||||
mBackupStreamOut = nsnull;
|
||||
}
|
||||
if (mSynTimer) {
|
||||
mSynTimer->Cancel();
|
||||
mSynTimer = nsnull;
|
||||
}
|
||||
|
||||
mEnt = nsnull;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP // method for nsITimerCallback
|
||||
nsHttpConnectionMgr::nsHalfOpenSocket::Notify(nsITimer *timer)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
NS_ABORT_IF_FALSE(timer == mSynTimer, "wrong timer");
|
||||
|
||||
mSynTimer = nsnull;
|
||||
if (!gHttpHandler->ConnMgr()->
|
||||
AtActiveConnectionLimit(mEnt, mTransaction->Caps())) {
|
||||
SetupBackupStreams();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// method for nsIAsyncOutputStreamCallback
|
||||
NS_IMETHODIMP
|
||||
nsHttpConnectionMgr::
|
||||
nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
NS_ABORT_IF_FALSE(out == mStreamOut ||
|
||||
out == mBackupStreamOut, "stream mismatch");
|
||||
LOG(("nsHalfOpenSocket::OnOutputStreamReady [this=%p ent=%s %s]\n",
|
||||
this, mEnt->mConnInfo->Host(),
|
||||
out == mStreamOut ? "primary" : "backup"));
|
||||
PRInt32 index;
|
||||
nsresult rv;
|
||||
|
||||
gHttpHandler->ConnMgr()->RecvdConnect();
|
||||
|
||||
// If the syntimer is still armed, we can cancel it because no backup
|
||||
// socket should be formed at this point
|
||||
if (mSynTimer) {
|
||||
NS_ABORT_IF_FALSE (out == mStreamOut, "timer for non existant stream");
|
||||
LOG(("nsHalfOpenSocket::OnOutputStreamReady "
|
||||
"Backup connection timer canceled\n"));
|
||||
mSynTimer->Cancel();
|
||||
mSynTimer = nsnull;
|
||||
}
|
||||
|
||||
// assign the new socket to the http connection
|
||||
nsRefPtr<nsHttpConnection> conn = new nsHttpConnection();
|
||||
LOG(("nsHalfOpenSocket::OnOutputStreamReady "
|
||||
"Created new nshttpconnection %p\n", conn));
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
||||
nsCOMPtr<nsIEventTarget> callbackTarget;
|
||||
mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks),
|
||||
getter_AddRefs(callbackTarget));
|
||||
if (out == mStreamOut) {
|
||||
rv = conn->Init(mEnt->mConnInfo,
|
||||
gHttpHandler->ConnMgr()->mMaxRequestDelay,
|
||||
mSocketTransport, mStreamIn, mStreamOut,
|
||||
callbacks, callbackTarget);
|
||||
|
||||
// The nsHttpConnection object now owns these streams and sockets
|
||||
mStreamOut = nsnull;
|
||||
mStreamIn = nsnull;
|
||||
mSocketTransport = nsnull;
|
||||
}
|
||||
else {
|
||||
rv = conn->Init(mEnt->mConnInfo,
|
||||
gHttpHandler->ConnMgr()->mMaxRequestDelay,
|
||||
mBackupTransport, mBackupStreamIn, mBackupStreamOut,
|
||||
callbacks, callbackTarget);
|
||||
|
||||
// The nsHttpConnection object now owns these streams and sockets
|
||||
mBackupStreamOut = nsnull;
|
||||
mBackupStreamIn = nsnull;
|
||||
mBackupTransport = nsnull;
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG(("nsHalfOpenSocket::OnOutputStreamReady "
|
||||
"conn->init (%p) failed %x\n", conn, rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
// if this is still in the pending list, remove it and dispatch it
|
||||
index = mEnt->mPendingQ.IndexOf(mTransaction);
|
||||
if (index != -1) {
|
||||
mEnt->mPendingQ.RemoveElementAt(index);
|
||||
nsHttpTransaction *temp = mTransaction;
|
||||
NS_RELEASE(temp);
|
||||
gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
|
||||
rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, mTransaction,
|
||||
mTransaction->Caps(),
|
||||
conn);
|
||||
}
|
||||
else {
|
||||
// this transaction was dispatched off the pending q before all the
|
||||
// sockets established themselves.
|
||||
|
||||
// We need to establish a small non-zero idle timeout so the connection
|
||||
// mgr perceives this socket as suitable for persistent connection reuse
|
||||
conn->SetIdleTimeout(NS_MIN((PRUint16) 5, gHttpHandler->IdleTimeout()));
|
||||
|
||||
// After about 1 second allow for the possibility of restarting a
|
||||
// transaction due to server close. Keep at sub 1 second as that is the
|
||||
// minimum granularity we can expect a server to be timing out with.
|
||||
conn->SetIsReusedAfter(950);
|
||||
|
||||
NS_ADDREF(conn); // because onmsg*() expects to drop a reference
|
||||
gHttpHandler->ConnMgr()->OnMsgReclaimConnection(NS_OK, conn);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// method for nsITransportEventSink
|
||||
NS_IMETHODIMP
|
||||
nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
|
||||
nsresult status,
|
||||
PRUint64 progress,
|
||||
PRUint64 progressMax)
|
||||
{
|
||||
if (mTransaction)
|
||||
mTransaction->OnTransportStatus(status, progress);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// method for nsIInterfaceRequestor
|
||||
NS_IMETHODIMP
|
||||
nsHttpConnectionMgr::nsHalfOpenSocket::GetInterface(const nsIID &iid,
|
||||
void **result)
|
||||
{
|
||||
if (mTransaction) {
|
||||
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
||||
mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks), nsnull);
|
||||
if (callbacks)
|
||||
return callbacks->GetInterface(iid, result);
|
||||
}
|
||||
return NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHttpConnectionMgr::nsConnectionHandle::LastTransactionExpectedNoContent()
|
||||
{
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "nsHashtable.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "prmon.h"
|
||||
#include "nsISocketTransportService.h"
|
||||
|
||||
#include "nsIObserver.h"
|
||||
#include "nsITimer.h"
|
||||
|
@ -139,6 +140,7 @@ public:
|
|||
|
||||
private:
|
||||
virtual ~nsHttpConnectionMgr();
|
||||
class nsHalfOpenSocket;
|
||||
|
||||
// nsConnectionEntry
|
||||
//
|
||||
|
@ -159,6 +161,7 @@ private:
|
|||
nsTArray<nsHttpTransaction*> mPendingQ; // pending transaction queue
|
||||
nsTArray<nsHttpConnection*> mActiveConns; // active connections
|
||||
nsTArray<nsHttpConnection*> mIdleConns; // idle persistent connections
|
||||
nsTArray<nsHalfOpenSocket*> mHalfOpens;
|
||||
};
|
||||
|
||||
// nsConnectionHandle
|
||||
|
@ -182,6 +185,48 @@ private:
|
|||
nsHttpConnection *mConn;
|
||||
};
|
||||
|
||||
// nsHalfOpenSocket is used to hold the state of an opening TCP socket
|
||||
// while we wait for it to establish and bind it to a connection
|
||||
|
||||
class nsHalfOpenSocket : public nsIOutputStreamCallback,
|
||||
public nsITransportEventSink,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsITimerCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOUTPUTSTREAMCALLBACK
|
||||
NS_DECL_NSITRANSPORTEVENTSINK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
|
||||
nsHalfOpenSocket(nsConnectionEntry *ent,
|
||||
nsHttpTransaction *trans);
|
||||
~nsHalfOpenSocket();
|
||||
|
||||
nsresult SetupStreams(nsISocketTransport **,
|
||||
nsIAsyncInputStream **,
|
||||
nsIAsyncOutputStream **);
|
||||
nsresult SetupPrimaryStreams();
|
||||
nsresult SetupBackupStreams();
|
||||
void SetupBackupTimer();
|
||||
void Abandon();
|
||||
|
||||
private:
|
||||
nsConnectionEntry *mEnt;
|
||||
nsRefPtr<nsHttpTransaction> mTransaction;
|
||||
nsCOMPtr<nsISocketTransport> mSocketTransport;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mStreamOut;
|
||||
nsCOMPtr<nsIAsyncInputStream> mStreamIn;
|
||||
|
||||
// for syn retry
|
||||
nsCOMPtr<nsITimer> mSynTimer;
|
||||
nsCOMPtr<nsISocketTransport> mBackupTransport;
|
||||
nsCOMPtr<nsIAsyncOutputStream> mBackupStreamOut;
|
||||
nsCOMPtr<nsIAsyncInputStream> mBackupStreamIn;
|
||||
};
|
||||
friend class nsHalfOpenSocket;
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// NOTE: these members may be accessed from any thread (use mMonitor)
|
||||
//-------------------------------------------------------------------------
|
||||
|
@ -206,18 +251,23 @@ private:
|
|||
//-------------------------------------------------------------------------
|
||||
|
||||
static PRIntn ProcessOneTransactionCB(nsHashKey *, void *, void *);
|
||||
static PRIntn PurgeOneIdleConnectionCB(nsHashKey *, void *, void *);
|
||||
|
||||
static PRIntn PruneDeadConnectionsCB(nsHashKey *, void *, void *);
|
||||
static PRIntn ShutdownPassCB(nsHashKey *, void *, void *);
|
||||
|
||||
static PRIntn PurgeExcessIdleConnectionsCB(nsHashKey *, void *, void *);
|
||||
PRBool ProcessPendingQForEntry(nsConnectionEntry *);
|
||||
PRBool AtActiveConnectionLimit(nsConnectionEntry *, PRUint8 caps);
|
||||
void GetConnection(nsConnectionEntry *, PRUint8 caps, nsHttpConnection **);
|
||||
void GetConnection(nsConnectionEntry *, nsHttpTransaction *,
|
||||
nsHttpConnection **);
|
||||
nsresult DispatchTransaction(nsConnectionEntry *, nsAHttpTransaction *,
|
||||
PRUint8 caps, nsHttpConnection *);
|
||||
PRBool BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **);
|
||||
nsresult ProcessNewTransaction(nsHttpTransaction *);
|
||||
nsresult EnsureSocketThreadTargetIfOnline();
|
||||
nsresult CreateTransport(nsConnectionEntry *, nsHttpTransaction *);
|
||||
void AddActiveConn(nsHttpConnection *, nsConnectionEntry *);
|
||||
void StartedConnect();
|
||||
void RecvdConnect();
|
||||
|
||||
// message handlers have this signature
|
||||
typedef void (nsHttpConnectionMgr:: *nsConnEventHandler)(PRInt32, void *);
|
||||
|
|
|
@ -179,6 +179,7 @@ nsHttpHandler::nsHttpHandler()
|
|||
, mIdleTimeout(10)
|
||||
, mMaxRequestAttempts(10)
|
||||
, mMaxRequestDelay(10)
|
||||
, mIdleSynTimeout(250)
|
||||
, mMaxConnections(24)
|
||||
, mMaxConnectionsPerServer(8)
|
||||
, mMaxPersistentConnectionsPerServer(2)
|
||||
|
@ -926,6 +927,12 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
|
|||
mRedirectionLimit = (PRUint8) NS_CLAMP(val, 0, 0xff);
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("connection-retry-timeout"))) {
|
||||
rv = prefs->GetIntPref(HTTP_PREF("connection-retry-timeout"), &val);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
mIdleSynTimeout = (PRUint16) NS_CLAMP(val, 0, 3000);
|
||||
}
|
||||
|
||||
if (PREF_CHANGED(HTTP_PREF("version"))) {
|
||||
nsXPIDLCString httpVersion;
|
||||
prefs->GetCharPref(HTTP_PREF("version"), getter_Copies(httpVersion));
|
||||
|
|
|
@ -107,6 +107,7 @@ public:
|
|||
nsIIDNService *IDNConverter() { return mIDNConverter; }
|
||||
PRUint32 PhishyUserPassLength() { return mPhishyUserPassLength; }
|
||||
PRUint8 GetQoSBits() { return mQoSBits; }
|
||||
PRUint16 GetIdleSynTimeout() { return mIdleSynTimeout; }
|
||||
|
||||
PRBool IsPersistentHttpsCachingEnabled() { return mEnablePersistentHttpsCaching; }
|
||||
|
||||
|
@ -263,6 +264,7 @@ private:
|
|||
PRUint16 mIdleTimeout;
|
||||
PRUint16 mMaxRequestAttempts;
|
||||
PRUint16 mMaxRequestDelay;
|
||||
PRUint16 mIdleSynTimeout;
|
||||
|
||||
PRUint16 mMaxConnections;
|
||||
PRUint8 mMaxConnectionsPerServer;
|
||||
|
|
|
@ -324,16 +324,20 @@ nsHttpPipeline::SetConnection(nsAHttpConnection *conn)
|
|||
}
|
||||
|
||||
void
|
||||
nsHttpPipeline::GetSecurityCallbacks(nsIInterfaceRequestor **result)
|
||||
nsHttpPipeline::GetSecurityCallbacks(nsIInterfaceRequestor **result,
|
||||
nsIEventTarget **target)
|
||||
{
|
||||
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
||||
|
||||
// return security callbacks from first request
|
||||
nsAHttpTransaction *trans = Request(0);
|
||||
if (trans)
|
||||
trans->GetSecurityCallbacks(result);
|
||||
else
|
||||
trans->GetSecurityCallbacks(result, target);
|
||||
else {
|
||||
*result = nsnull;
|
||||
if (target)
|
||||
*target = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -333,9 +333,12 @@ nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
|
|||
}
|
||||
|
||||
void
|
||||
nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
|
||||
nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb,
|
||||
nsIEventTarget **target)
|
||||
{
|
||||
NS_IF_ADDREF(*cb = mCallbacks);
|
||||
if (target)
|
||||
NS_IF_ADDREF(*target = mConsumerTarget);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Загрузка…
Ссылка в новой задаче