diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index de5ec631826c..083ff71f890d 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -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) diff --git a/netwerk/base/src/nsSocketTransport2.cpp b/netwerk/base/src/nsSocketTransport2.cpp index 6d0812e584c3..54d34341e7bc 100644 --- a/netwerk/base/src/nsSocketTransport2.cpp +++ b/netwerk/base/src/nsSocketTransport2.cpp @@ -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 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 secCtrl(do_QueryInterface(secinfo)); + if (secCtrl) + secCtrl->SetNotificationCallbacks(callbacks); + return NS_OK; } diff --git a/netwerk/protocol/http/nsAHttpTransaction.h b/netwerk/protocol/http/nsAHttpTransaction.h index 9ec6078a921e..8c11c62c88b5 100644 --- a/netwerk/protocol/http/nsAHttpTransaction.h +++ b/netwerk/protocol/http/nsAHttpTransaction.h @@ -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(); \ diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp index ae87851f0801..70a1f90595b1 100644 --- a/netwerk/protocol/http/nsHttpConnection.cpp +++ b/netwerk/protocol/http/nsHttpConnection.cpp @@ -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 @@ -86,9 +88,12 @@ nsHttpConnection::nsHttpConnection() 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(mTransaction); + static_cast(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 //----------------------------------------------------------------------------- -nsresult -nsHttpConnection::CreateTransport(PRUint8 caps) -{ - nsresult rv; - - NS_PRECONDITION(!mSocketTransport, "unexpected"); - - nsCOMPtr 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 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 sout; - rv = strans->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, - getter_AddRefs(sout)); - if (NS_FAILED(rv)) return rv; - nsCOMPtr 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(mTransaction); + nsHttpTransaction *trans = + static_cast(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. - NS_ASSERTION(PR_GetCurrentThread() != gSocketThread, "wrong thread"); - - if (mTransaction) { - nsCOMPtr callbacks; - mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks)); - if (callbacks) - return callbacks->GetInterface(iid, result); - } + // 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 (mCallbacks) + return mCallbacks->GetInterface(iid, result); return NS_ERROR_NO_INTERFACE; } diff --git a/netwerk/protocol/http/nsHttpConnection.h b/netwerk/protocol/http/nsHttpConnection.h index 4008d0076f16..0a53909105b7 100644 --- a/netwerk/protocol/http/nsHttpConnection.h +++ b/netwerk/protocol/http/nsHttpConnection.h @@ -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 mSSLProxyConnectStream; nsCOMPtr 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 mTransaction; + + nsCOMPtr mCallbacks; + nsCOMPtr mCallbackTarget; + + nsRefPtr 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; diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp index c74f21629877..8c9c8e74486b 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp +++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp @@ -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; imPendingQ[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 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 socketTransport; + nsCOMPtr 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 sout; + rv = socketTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, + 0, 0, + getter_AddRefs(sout)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr 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 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 conn = new nsHttpConnection(); + LOG(("nsHalfOpenSocket::OnOutputStreamReady " + "Created new nshttpconnection %p\n", conn)); + + nsCOMPtr callbacks; + nsCOMPtr 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 callbacks; + mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks), nsnull); + if (callbacks) + return callbacks->GetInterface(iid, result); + } + return NS_ERROR_NO_INTERFACE; +} + PRBool nsHttpConnectionMgr::nsConnectionHandle::LastTransactionExpectedNoContent() { diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h index 569ed8d7efe8..c6247d9728ba 100644 --- a/netwerk/protocol/http/nsHttpConnectionMgr.h +++ b/netwerk/protocol/http/nsHttpConnectionMgr.h @@ -47,6 +47,7 @@ #include "nsHashtable.h" #include "nsAutoPtr.h" #include "prmon.h" +#include "nsISocketTransportService.h" #include "nsIObserver.h" #include "nsITimer.h" @@ -139,7 +140,8 @@ public: private: virtual ~nsHttpConnectionMgr(); - + class nsHalfOpenSocket; + // nsConnectionEntry // // mCT maps connection info hash key to nsConnectionEntry object, which @@ -159,6 +161,7 @@ private: nsTArray mPendingQ; // pending transaction queue nsTArray mActiveConns; // active connections nsTArray mIdleConns; // idle persistent connections + nsTArray 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 mTransaction; + nsCOMPtr mSocketTransport; + nsCOMPtr mStreamOut; + nsCOMPtr mStreamIn; + + // for syn retry + nsCOMPtr mSynTimer; + nsCOMPtr mBackupTransport; + nsCOMPtr mBackupStreamOut; + nsCOMPtr mBackupStreamIn; + }; + friend class nsHalfOpenSocket; + //------------------------------------------------------------------------- // NOTE: these members may be accessed from any thread (use mMonitor) //------------------------------------------------------------------------- @@ -206,19 +251,24 @@ 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 *); diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index fe91f80ca012..f741349b6a96 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -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)); diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index 1882852f847b..927732a8e295 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -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; diff --git a/netwerk/protocol/http/nsHttpPipeline.cpp b/netwerk/protocol/http/nsHttpPipeline.cpp index bc0fccc6a6ae..ea605c8900f1 100644 --- a/netwerk/protocol/http/nsHttpPipeline.cpp +++ b/netwerk/protocol/http/nsHttpPipeline.cpp @@ -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 diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp index f991138a2dc5..6dcc604f8bf2 100644 --- a/netwerk/protocol/http/nsHttpTransaction.cpp +++ b/netwerk/protocol/http/nsHttpTransaction.cpp @@ -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