Mozilla Home
Privacy
Cookies
Legal
Bugzilla
Browse
Advanced Search
New Bug
Reports
Documentation
Log In
Log In with GitHub
or
Remember me
Browse
Advanced Search
New Bug
Reports
Documentation
Attachment 509492 Details for
Bug 623948
[patch]
syn retry v1
623948-syn-retry-redux.1 (text/plain), 59.10 KB, created by
Patrick McManus [:mcmanus]
(
hide
)
Description:
syn retry v1
Filename:
MIME Type:
Creator:
Patrick McManus [:mcmanus]
Size:
59.10 KB
patch
obsolete
># HG changeset patch ># Parent 5212ab4ae8082122ad57b5e8bc8015fb25814f68 >Accelerate TCP connection retries in HTTP > >Losing a TCP SYN requires a long painful (typically 3 second) delay >before being retried. This patch creates a second parallel connection >attempt for any nsHttpConnection which has not become writable before >a timeout occurs. > >If you assume .5% packet loss, this converts a full 3 second delay >from a 1 in 200 event into a 1 in 40,000 event. > >Whichever connection establishes itself first is used. If another one >has been started and it does connect before the one being used is >closed then the extra one is handed to the connection manager for use >by a different transaction - essentially a persistent connection with >0 previous transactions on it. (Another way to think about is >pre-fetching a 3WHS on a high latency connection). > >The pref network.http.connection-retry-timeout controls the amount of >time in ms to wait for success on the initial connection before beginning >the second one. Setting it to 0 disables the parallel connection, the >default is 250. > >This patch also reorganizes tcp connections to be driven from the >connection manager instead of nshttpconnection. This allows a >transaction that opens a new tcp connection to actually reschedule >itself onto a persistent connection that becomes available only after >the open has happened. > >diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js >--- a/modules/libpref/src/init/all.js >+++ b/modules/libpref/src/init/all.js >@@ -743,16 +743,21 @@ pref("network.http.prompt-temp-redirect" > // for certain services (i.e. EF for VoIP, AF4x for interactive video, > // AF3x for broadcast/streaming video, etc) > > // default value for HTTP > // in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594, > // 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) > // per Section 4.7 "Low-Latency Data Service Class". > pref("network.ftp.data.qos", 0); > pref("network.ftp.control.qos", 0); > > // </http> >diff --git a/netwerk/base/src/nsSocketTransport2.cpp b/netwerk/base/src/nsSocketTransport2.cpp >--- a/netwerk/base/src/nsSocketTransport2.cpp >+++ b/netwerk/base/src/nsSocketTransport2.cpp >@@ -1773,19 +1773,30 @@ nsSocketTransport::GetSecurityCallbacks( > nsAutoLock lock(mLock); > NS_IF_ADDREF(*callbacks = mCallbacks); > return NS_OK; > } > > 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; > } > > NS_IMETHODIMP > nsSocketTransport::SetEventSink(nsITransportEventSink *sink, > nsIEventTarget *target) > { > nsCOMPtr<nsITransportEventSink> temp; >diff --git a/netwerk/protocol/http/nsAHttpTransaction.h b/netwerk/protocol/http/nsAHttpTransaction.h >--- a/netwerk/protocol/http/nsAHttpTransaction.h >+++ b/netwerk/protocol/http/nsAHttpTransaction.h >@@ -39,16 +39,17 @@ > #define nsAHttpTransaction_h__ > > #include "nsISupports.h" > > class nsAHttpConnection; > class nsAHttpSegmentReader; > class nsAHttpSegmentWriter; > class nsIInterfaceRequestor; >+class nsIEventTarget; > > //---------------------------------------------------------------------------- > // Abstract base class for a HTTP transaction: > // > // A transaction is a "sink" for the response data. The connection pushes > // data to the transaction by writing to it. The transaction supports > // WriteSegments and may refuse to accept data if its buffers are full (its > // write function returns NS_BASE_STREAM_WOULD_BLOCK in this case). >@@ -57,17 +58,18 @@ class nsIInterfaceRequestor; > class nsAHttpTransaction : public nsISupports > { > public: > // called by the connection when it takes ownership of the transaction. > virtual void SetConnection(nsAHttpConnection *) = 0; > > // 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; > > // called to check the transaction status. > virtual PRBool IsDone() = 0; > virtual nsresult Status() = 0; > >@@ -83,17 +85,18 @@ public: > PRUint32 count, PRUint32 *countWritten) = 0; > > // called to close the transaction > virtual void Close(nsresult reason) = 0; > }; > > #define NS_DECL_NSAHTTPTRANSACTION \ > void SetConnection(nsAHttpConnection *); \ >- void GetSecurityCallbacks(nsIInterfaceRequestor **); \ >+ void GetSecurityCallbacks(nsIInterfaceRequestor **, \ >+ nsIEventTarget **); \ > void OnTransportStatus(nsresult status, PRUint64 progress); \ > PRBool IsDone(); \ > nsresult Status(); \ > PRUint32 Available(); \ > nsresult ReadSegments(nsAHttpSegmentReader *, PRUint32, PRUint32 *); \ > nsresult WriteSegments(nsAHttpSegmentWriter *, PRUint32, PRUint32 *); \ > void Close(nsresult reason); > >diff --git a/netwerk/protocol/http/nsHttpConnection.cpp b/netwerk/protocol/http/nsHttpConnection.cpp >--- a/netwerk/protocol/http/nsHttpConnection.cpp >+++ b/netwerk/protocol/http/nsHttpConnection.cpp >@@ -46,32 +46,32 @@ > #include "nsISocketTransportService.h" > #include "nsISocketTransport.h" > #include "nsIServiceManager.h" > #include "nsISSLSocketControl.h" > #include "nsStringStream.h" > #include "netCore.h" > #include "nsNetCID.h" > #include "nsAutoLock.h" >+#include "nsProxyRelease.h" > #include "prmem.h" > > #ifdef DEBUG > // defined by the socket transport service while active > extern PRThread *gSocketThread; > #endif > > static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID); > > //----------------------------------------------------------------------------- > // nsHttpConnection <public> > //----------------------------------------------------------------------------- > > nsHttpConnection::nsHttpConnection() > : mTransaction(nsnull) >- , mConnInfo(nsnull) > , mLastReadTime(0) > , mIdleTimeout(0) > , mKeepAlive(PR_TRUE) // assume to keep-alive by default > , mKeepAliveMask(PR_TRUE) > , mSupportsPipelining(PR_FALSE) // assume low-grade server > , mIsReused(PR_FALSE) > , mCompletedSSLConnect(PR_FALSE) > , mLastTransactionExpectedNoContent(PR_FALSE) >@@ -81,90 +81,102 @@ nsHttpConnection::nsHttpConnection() > // grab a reference to the handler to ensure that it doesn't go away. > nsHttpHandler *handler = gHttpHandler; > NS_ADDREF(handler); > } > > 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; > NS_RELEASE(handler); > } > > nsresult >-nsHttpConnection::Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime) >+nsHttpConnection::Init(nsHttpConnectionInfo *info, PRUint16 maxHangTime, >+ nsISocketTransport *transport, nsIAsyncInputStream *instream, >+ nsIAsyncOutputStream *outstream, >+ nsIInterfaceRequestor *callbacks, >+ nsIEventTarget *callbackTarget) >+ >+ > { >+ NS_ABORT_IF_FALSE (transport && instream && outstream, "init precond"); > LOG(("nsHttpConnection::Init [this=%x]\n", this)); > > 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; > } > > // called on the socket thread > nsresult > 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)); > > NS_ENSURE_ARG_POINTER(trans); > NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS); > > // 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; > } > > void > 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) { > mSocketTransport->SetSecurityCallbacks(nsnull); > mSocketTransport->SetEventSink(nsnull, nsnull); > mSocketTransport->Close(reason); > } > mKeepAlive = PR_FALSE; >@@ -382,17 +394,17 @@ nsHttpConnection::OnHeadersAvailable(nsA > NS_ASSERTION(NS_SUCCEEDED(rv), "mSocketOut->AsyncWait failed"); > } > else { > LOG(("SSL proxy CONNECT failed!\n")); > // 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); >+ static_cast<nsHttpTransaction *>(mTransaction.get()); > trans->SetSSLConnectFailed(); > } > } > > return NS_OK; > } > > void >@@ -433,96 +445,31 @@ nsHttpConnection::ResumeRecv() > NS_NOTREACHED("no socket input stream"); > return NS_ERROR_UNEXPECTED; > } > > //----------------------------------------------------------------------------- > // 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) > { > LOG(("nsHttpConnection::CloseTransaction[this=%x trans=%x reason=%x]\n", > this, trans, reason)); > > NS_ASSERTION(trans == mTransaction, "wrong transaction"); > NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread"); > > // mask this error code because its not a real error. > if (reason == NS_BASE_STREAM_CLOSED) > reason = NS_OK; > > mTransaction->Close(reason); >- >- NS_RELEASE(mTransaction); >- mTransaction = 0; >+ mTransaction = nsnull; > > if (NS_FAILED(reason)) > Close(reason); > > // flag the connection as reused here for convenience sake. certainly > // it might be going away instead ;-) > mIsReused = PR_TRUE; > } >@@ -723,17 +670,18 @@ nsHttpConnection::SetupSSLProxyConnect() > request.SetRequestURI(buf); > request.SetHeader(nsHttp::User_Agent, gHttpHandler->UserAgent()); > > // send this header for backwards compatibility. > request.SetHeader(nsHttp::Proxy_Connection, NS_LITERAL_CSTRING("keep-alive")); > > // 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) { > // all HTTP/1.1 requests must include a Host header (even though it > // may seem redundant in this case; see bug 82388). > request.SetHeader(nsHttp::Host, nsDependentCString(val)); > } > >@@ -787,18 +735,18 @@ nsHttpConnection::OnInputStreamReady(nsI > > //----------------------------------------------------------------------------- > // nsHttpConnection::nsIOutputStreamCallback > //----------------------------------------------------------------------------- > > 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) { > LOG((" no transaction; ignoring event\n")); > return NS_OK; > } > > nsresult rv = OnSocketWritable(); >@@ -830,19 +778,19 @@ nsHttpConnection::OnTransportStatus(nsIT > // not called on the socket transport thread > NS_IMETHODIMP > nsHttpConnection::GetInterface(const nsIID &iid, void **result) > { > // NOTE: This function is only called on the UI thread via sync proxy from > // 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; > } >diff --git a/netwerk/protocol/http/nsHttpConnection.h b/netwerk/protocol/http/nsHttpConnection.h >--- a/netwerk/protocol/http/nsHttpConnection.h >+++ b/netwerk/protocol/http/nsHttpConnection.h >@@ -48,16 +48,17 @@ > #include "prlock.h" > #include "nsAutoPtr.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) > // > // NOTE: this objects lives on the socket thread only. it should not be > // accessed from any other thread. > //----------------------------------------------------------------------------- > >@@ -80,17 +81,20 @@ public: > nsHttpConnection(); > virtual ~nsHttpConnection(); > > // 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); >+ 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. > nsresult Activate(nsAHttpTransaction *, PRUint8 caps); > > // Close the underlying socket transport. > void Close(nsresult reason); > >@@ -124,28 +128,29 @@ public: > > // nsAHttpConnection compatible methods (non-virtual): > nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, PRBool *reset); > void CloseTransaction(nsAHttpTransaction *, nsresult reason); > void GetConnectionInfo(nsHttpConnectionInfo **ci) { NS_IF_ADDREF(*ci = mConnInfo); } > void GetSecurityInfo(nsISupports **); > PRBool IsPersistent() { return IsKeepAlive(); } > PRBool IsReused() { return mIsReused; } >+ void SetIsReused(PRBool val) {mIsReused = val;} >+ void SetIdleTimeout(PRUint16 val) {mIdleTimeout = val;} > nsresult PushBack(const char *data, PRUint32 length) { NS_NOTREACHED("PushBack"); return NS_ERROR_UNEXPECTED; } > nsresult ResumeSend(); > nsresult ResumeRecv(); > > static NS_METHOD ReadFromStream(nsIInputStream *, void *, const char *, > PRUint32, PRUint32, PRUint32 *); > > 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(); > > nsresult SetupSSLProxyConnect(); > > PRBool IsAlive(); > PRBool SupportsPipelining(nsHttpResponseHead *); >@@ -156,18 +161,24 @@ private: > nsCOMPtr<nsIAsyncOutputStream> mSocketOut; > > nsresult mSocketInCondition; > nsresult mSocketOutCondition; > > 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= > > PRPackedBool mKeepAlive; > PRPackedBool mKeepAliveMask; > PRPackedBool mSupportsPipelining; >diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.cpp b/netwerk/protocol/http/nsHttpConnectionMgr.cpp >--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp >+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp >@@ -109,16 +109,20 @@ nsHttpConnectionMgr::EnsureSocketThreadT > nsCOMPtr<nsIEventTarget> sts; > nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); > if (NS_SUCCEEDED(rv)) { > PRBool offline = PR_TRUE; > ioService->GetOffline(&offline); > > if (!offline) { > sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); >+ if (!mSTS && sts) { >+ mSTS = do_QueryInterface(sts); >+ NS_ABORT_IF_FALSE(mSTS.get(), "nonsensical failed qi"); >+ } > } > } > > nsAutoMonitor mon(mMonitor); > > // do nothing if already initialized or if we've shut down > if (mSocketThreadTarget || mIsShuttingDown) > return NS_OK; >@@ -395,34 +399,40 @@ nsHttpConnectionMgr::ProcessOneTransacti > nsConnectionEntry *ent = (nsConnectionEntry *) data; > > if (self->ProcessPendingQForEntry(ent)) > return kHashEnumerateStop; > > 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); > NS_RELEASE(conn); > self->mNumIdleConns--; > if (0 == self->mNumIdleConns) > self->StopPruneDeadConnectionsTimer(); >- return kHashEnumerateStop; > } >- >- return kHashEnumerateNext; >+ return kHashEnumerateStop; > } > > PRIntn > nsHttpConnectionMgr::PruneDeadConnectionsCB(nsHashKey *key, void *data, void *closure) > { > nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure; > nsConnectionEntry *ent = (nsConnectionEntry *) data; > >@@ -469,16 +479,17 @@ nsHttpConnectionMgr::PruneDeadConnection > LOG((" active conn [%x] with trans [%x]\n", conn, conn->Transaction())); > } > } > #endif > > // 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; > return kHashEnumerateRemove; > } > > // else, use this opportunity to compact our arrays... > ent->mIdleConns.Compact(); >@@ -528,16 +539,20 @@ nsHttpConnectionMgr::ShutdownPassCB(nsHa > trans = ent->mPendingQ[0]; > > ent->mPendingQ.RemoveElementAt(0); > > trans->Close(NS_ERROR_ABORT); > 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; > } > > //----------------------------------------------------------------------------- > > PRBool > nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent) >@@ -547,17 +562,17 @@ nsHttpConnectionMgr::ProcessPendingQForE > > PRInt32 i, count = ent->mPendingQ.Length(); > if (count > 0) { > LOG((" pending-count=%u\n", count)); > nsHttpTransaction *trans = nsnull; > 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; > } > if (conn) { > LOG((" dispatching pending transaction...\n")); > > // remove pending transaction > ent->mPendingQ.RemoveElementAt(i); >@@ -587,18 +602,18 @@ nsHttpConnectionMgr::ProcessPendingQForE > PRBool > nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 caps) > { > nsHttpConnectionInfo *ci = ent->mConnInfo; > > 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; > } > > nsHttpConnection *conn; > PRInt32 i, totalCount, persistCount = 0; > >@@ -606,16 +621,21 @@ nsHttpConnectionMgr::AtActiveConnectionL > > // count the number of persistent connections > for (i=0; i<totalCount; ++i) { > conn = ent->mActiveConns[i]; > if (conn->IsKeepAlive()) // XXX make sure this is thread-safe > 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; > PRUint16 maxPersistConns; > > if (ci->UsingHttpProxy() && !ci->UsingSSL()) { > maxConns = mMaxConnsPerProxy; > maxPersistConns = mMaxPersistConnsPerProxy; >@@ -626,28 +646,36 @@ nsHttpConnectionMgr::AtActiveConnectionL > } > > // use >= just to be safe > return (totalCount >= maxConns) || ( (caps & NS_HTTP_ALLOW_KEEPALIVE) && > (persistCount >= maxPersistConns) ); > } > > 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 > // it is a "matching" connection. > if (!conn->CanReuse()) { > LOG((" dropping stale connection: [conn=%x]\n", conn)); > conn->Close(NS_ERROR_ABORT); > NS_RELEASE(conn); >@@ -667,41 +695,77 @@ nsHttpConnectionMgr::GetConnection(nsCon > if (!conn) { > // Check if we need to purge an idle connection. Note that we may have > // removed one above; if so, this will be a no-op. We do this before > // checking the active connection limit to catch the case where we do > // have an idle connection, but the purge timer hasn't fired yet. > // 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((" at active connection limit - will queue\n")); > 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"); >+ NS_ABORT_IF_FALSE (mSTS, "no socket transport?"); >+ >+ nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, this, trans); >+ nsresult rv = sock->SetupPrimaryStreams(); >+ NS_ENSURE_SUCCESS(rv, rv); >+ StartedConnect(); >+ sock->SetupBackupTimer(); >+ ent->mHalfOpens.AppendElement(sock); >+ return NS_OK; >+} >+ > nsresult > nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent, > nsAHttpTransaction *trans, > PRUint8 caps, > nsHttpConnection *conn) > { > LOG(("nsHttpConnectionMgr::DispatchTransaction [ci=%s trans=%x caps=%x conn=%x]\n", > ent->mConnInfo->HashKey().get(), trans, caps, conn)); >@@ -713,21 +777,16 @@ nsHttpConnectionMgr::DispatchTransaction > > nsHttpPipeline *pipeline = nsnull; > if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) { > LOG((" looking to build pipeline...\n")); > if (BuildPipeline(ent, trans, &pipeline)) > 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); > > nsresult rv = conn->Activate(trans, caps); > > if (NS_FAILED(rv)) { > LOG((" conn->Activate failed [rv=%x]\n", rv)); > ent->mActiveConns.RemoveElement(conn); >@@ -790,16 +849,18 @@ nsHttpConnectionMgr::BuildPipeline(nsCon > LOG((" pipelined %u transactions\n", numAdded)); > NS_ADDREF(*result = pipeline); > return PR_TRUE; > } > > 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 > // been canceled (see bug 190001). > if (NS_FAILED(trans->Status())) { > LOG((" transaction was canceled... dropping event!\n")); > return NS_OK; > } >@@ -832,28 +893,19 @@ nsHttpConnectionMgr::ProcessNewTransacti > > // steal reference from connection handle. > // XXX prevent SetConnection(nsnull) from calling ReclaimConnection > conn = handle->mConn; > handle->mConn = nsnull; > > // 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) { > LOG((" adding transaction to pending queue [trans=%x pending-count=%u]\n", > trans, ent->mPendingQ.Length()+1)); > // put this transaction on the pending queue... > InsertTransactionSorted(ent->mPendingQ, trans); > NS_ADDREF(trans); >@@ -993,38 +1045,45 @@ nsHttpConnectionMgr::OnMsgReclaimConnect > nsHttpConnectionInfo *ci = conn->ConnectionInfo(); > NS_ADDREF(ci); > > nsCStringKey key(ci->HashKey()); > nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key); > > 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 > // time to live among the idle connections, pruning dead > // connections needs to be done when it can't be reused anymore. > PRUint32 timeToLive = conn->TimeToLive(); > if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp) > PruneDeadConnectionsAfter(timeToLive); > } > else { > 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); > } > } > > OnMsgProcessPendingQ(NS_OK, ci); // releases |ci| > NS_RELEASE(conn); > } > > void >@@ -1125,16 +1184,274 @@ nsHttpConnectionMgr::nsConnectionHandle: > } > > nsresult > nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufLen) > { > return mConn->PushBack(buf, bufLen); > } > >+ >+//////////////////////// nsHalfOpenSocket >+ >+ >+NS_IMPL_THREADSAFE_ISUPPORTS3(nsHttpConnectionMgr::nsHalfOpenSocket, >+ nsIOutputStreamCallback, >+ nsITransportEventSink, >+ nsIInterfaceRequestor) >+ >+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"); >+ >+ 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; >+ rv = mMgr-> >+ mSTS->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)) >+ NS_ADDREF(this); >+ return rv; >+} >+ >+nsresult >+nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams() >+{ >+ return SetupStreams(getter_AddRefs(mSocketTransport), >+ getter_AddRefs(mStreamIn), >+ getter_AddRefs(mStreamOut)); >+} >+ >+nsresult >+nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams() >+{ >+ return SetupStreams(getter_AddRefs(mBackupTransport), >+ getter_AddRefs(mBackupStreamIn), >+ getter_AddRefs(mBackupStreamOut)); >+} >+ >+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->InitWithFuncCallback(SynTimeout, this, >+ timeout, nsITimer::TYPE_ONE_SHOT); >+ NS_ADDREF(this); >+ } >+ } >+} >+ >+void >+nsHttpConnectionMgr::nsHalfOpenSocket::Abandon() >+{ >+ PRInt32 refs = 0; >+ >+ if (mStreamOut) { >+ mStreamOut->AsyncWait(nsnull, 0, 0, nsnull); >+ mMgr->RecvdConnect(); >+ mStreamOut = nsnull; >+ refs++; >+ } >+ if (mBackupStreamOut) { >+ mBackupStreamOut->AsyncWait(nsnull, 0, 0, nsnull); >+ mMgr->RecvdConnect(); >+ mBackupStreamOut = nsnull; >+ refs++; >+ } >+ if (mSynTimer) { >+ mSynTimer->Cancel(); >+ mSynTimer = nsnull; >+ refs++; >+ } >+ >+ mEnt = nsnull; >+ for (PRInt32 i = 0; i < refs; i++) { >+ nsHalfOpenSocket *temp = this; >+ NS_RELEASE(temp); >+ } >+} >+ >+void >+nsHttpConnectionMgr:: >+nsHalfOpenSocket::SynTimeout(nsITimer *timer, void *closure) >+{ >+ NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread"); >+ nsHalfOpenSocket *self = (nsHalfOpenSocket *)closure; >+ NS_ABORT_IF_FALSE(timer == self->mSynTimer, "wrong timer"); >+ if (!self->mMgr->AtActiveConnectionLimit(self->mEnt, >+ self->mTransaction->Caps())) { >+ nsresult rv = self->SetupBackupStreams(); >+ if (NS_SUCCEEDED(rv)) >+ self->mMgr->StartedConnect(); >+ } >+ >+ // this is the ref established when timer was armed >+ self->mSynTimer = nsnull; >+ NS_RELEASE(self); >+} >+ >+// 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"); >+ PRInt32 index; >+ nsresult rv; >+ >+ mMgr->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"); >+ mSynTimer->Cancel(); >+ mSynTimer = nsnull; >+ nsHalfOpenSocket *temp = this; >+ NS_RELEASE(temp); >+ } >+ >+ // assign the new socket to the http connection >+ nsRefPtr<nsHttpConnection> conn = new nsHttpConnection(); >+ nsCOMPtr<nsIInterfaceRequestor> callbacks; >+ nsCOMPtr<nsIEventTarget> callbackTarget; >+ mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks), >+ getter_AddRefs(callbackTarget)); >+ if (out == mStreamOut) { >+ rv = conn->Init(mEnt->mConnInfo, mMgr->mMaxRequestDelay, >+ mSocketTransport, mStreamIn, mStreamOut, >+ callbacks, callbackTarget); >+ mStreamOut = nsnull; >+ } >+ else { >+ rv = conn->Init(mEnt->mConnInfo, mMgr->mMaxRequestDelay, >+ mBackupTransport, mBackupStreamIn, mBackupStreamOut, >+ callbacks, callbackTarget); >+ mBackupStreamOut = nsnull; >+ } >+ >+ if (NS_FAILED(rv)) >+ goto cleanup_osr; >+ >+ // 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); >+ mMgr->AddActiveConn(conn, mEnt); >+ rv = mMgr->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->SetIsReused(PR_TRUE); >+ conn->SetIdleTimeout(NS_MIN((PRUint16) 5, gHttpHandler->IdleTimeout())); >+ NS_ADDREF(conn); // because onmsg*() expects to drop a reference >+ mMgr->OnMsgReclaimConnection(NS_OK, conn); >+ } >+ >+cleanup_osr: >+ // cleanup reference originated in successful setupstreams() >+ nsHalfOpenSocket *tmp = this; >+ NS_RELEASE(tmp); >+ 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() > { > return mConn->LastTransactionExpectedNoContent(); > } > > void > nsHttpConnectionMgr:: >diff --git a/netwerk/protocol/http/nsHttpConnectionMgr.h b/netwerk/protocol/http/nsHttpConnectionMgr.h >--- a/netwerk/protocol/http/nsHttpConnectionMgr.h >+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h >@@ -42,16 +42,17 @@ > #include "nsHttpConnectionInfo.h" > #include "nsHttpConnection.h" > #include "nsHttpTransaction.h" > #include "nsTArray.h" > #include "nsThreadUtils.h" > #include "nsHashtable.h" > #include "nsAutoPtr.h" > #include "prmon.h" >+#include "nsISocketTransportService.h" > > #include "nsIObserver.h" > #include "nsITimer.h" > > class nsHttpPipeline; > > //----------------------------------------------------------------------------- > >@@ -134,17 +135,18 @@ public: > void AddTransactionToPipeline(nsHttpPipeline *); > > // called to force the transaction queue to be processed once more, giving > // preference to the specified connection. > nsresult ProcessPendingQ(nsHttpConnectionInfo *); > > private: > virtual ~nsHttpConnectionMgr(); >- >+ class nsHalfOpenSocket; >+ > // nsConnectionEntry > // > // mCT maps connection info hash key to nsConnectionEntry object, which > // contains list of active and idle connections as well as the list of > // pending transactions. > // > struct nsConnectionEntry > { >@@ -154,16 +156,17 @@ private: > NS_ADDREF(mConnInfo); > } > ~nsConnectionEntry() { NS_RELEASE(mConnInfo); } > > nsHttpConnectionInfo *mConnInfo; > nsTArray<nsHttpTransaction*> mPendingQ; // pending transaction queue > nsTArray<nsHttpConnection*> mActiveConns; // active connections > nsTArray<nsHttpConnection*> mIdleConns; // idle persistent connections >+ nsTArray<nsHalfOpenSocket*> mHalfOpens; > }; > > // nsConnectionHandle > // > // thin wrapper around a real connection, used to keep track of references > // to the connection to determine when the connection may be reused. the > // transaction (or pipeline) owns a reference to this handle. this extra > // layer of indirection greatly simplifies consumer code, avoiding the >@@ -177,23 +180,71 @@ private: > NS_DECL_NSAHTTPCONNECTION > > nsConnectionHandle(nsHttpConnection *conn) { NS_ADDREF(mConn = conn); } > virtual ~nsConnectionHandle(); > > 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: >+ NS_DECL_ISUPPORTS >+ NS_DECL_NSIOUTPUTSTREAMCALLBACK >+ NS_DECL_NSITRANSPORTEVENTSINK >+ NS_DECL_NSIINTERFACEREQUESTOR >+ >+ nsHalfOpenSocket(nsConnectionEntry *ent, >+ nsHttpConnectionMgr *mgr, >+ nsHttpTransaction *trans) >+ : mEnt(ent), >+ mMgr(mgr), >+ mTransaction(trans) {} >+ ~nsHalfOpenSocket(); >+ >+ nsresult SetupStreams(nsISocketTransport **, >+ nsIAsyncInputStream **, >+ nsIAsyncOutputStream **); >+ nsresult SetupPrimaryStreams(); >+ nsresult SetupBackupStreams(); >+ void SetupBackupTimer(); >+ void Abandon(); >+ >+ private: >+ static void SynTimeout(nsITimer *, void *); >+ >+ nsConnectionEntry *mEnt; >+ nsHttpConnectionMgr *mMgr; >+ 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) > //------------------------------------------------------------------------- > > PRInt32 mRef; > PRMonitor *mMonitor; > nsCOMPtr<nsIEventTarget> mSocketThreadTarget; >+ nsCOMPtr<nsISocketTransportService> mSTS; > > // connection limits > PRUint16 mMaxConns; > PRUint16 mMaxConnsPerHost; > PRUint16 mMaxConnsPerProxy; > PRUint16 mMaxPersistConnsPerHost; > PRUint16 mMaxPersistConnsPerProxy; > PRUint16 mMaxRequestDelay; // in seconds >@@ -201,29 +252,34 @@ private: > > PRPackedBool mIsShuttingDown; > > //------------------------------------------------------------------------- > // NOTE: these members are only accessed on the socket transport thread > //------------------------------------------------------------------------- > > 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 *); > > // nsConnEvent > // > // subclass of nsRunnable used to marshall events to the socket transport > // thread. this class is used to implement PostEvent. > // >diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp >--- a/netwerk/protocol/http/nsHttpHandler.cpp >+++ b/netwerk/protocol/http/nsHttpHandler.cpp >@@ -174,16 +174,17 @@ nsHttpHandler::nsHttpHandler() > , mHttpVersion(NS_HTTP_VERSION_1_1) > , mProxyHttpVersion(NS_HTTP_VERSION_1_1) > , mCapabilities(NS_HTTP_ALLOW_KEEPALIVE) > , mProxyCapabilities(NS_HTTP_ALLOW_KEEPALIVE) > , mReferrerLevel(0xff) // by default we always send a referrer > , mIdleTimeout(10) > , mMaxRequestAttempts(10) > , mMaxRequestDelay(10) >+ , mIdleSynTimeout(250) > , mMaxConnections(24) > , mMaxConnectionsPerServer(8) > , mMaxPersistentConnectionsPerServer(2) > , mMaxPersistentConnectionsPerProxy(4) > , mMaxPipelinedRequests(2) > , mRedirectionLimit(10) > , mPhishyUserPassLength(1) > , mQoSBits(0x00) >@@ -923,16 +924,22 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc > } > > if (PREF_CHANGED(HTTP_PREF("redirection-limit"))) { > rv = prefs->GetIntPref(HTTP_PREF("redirection-limit"), &val); > if (NS_SUCCEEDED(rv)) > 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)); > if (httpVersion) { > if (!PL_strcmp(httpVersion, "1.1")) > mHttpVersion = NS_HTTP_VERSION_1_1; > else if (!PL_strcmp(httpVersion, "0.9")) > mHttpVersion = NS_HTTP_VERSION_0_9; >diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h >--- a/netwerk/protocol/http/nsHttpHandler.h >+++ b/netwerk/protocol/http/nsHttpHandler.h >@@ -102,16 +102,17 @@ public: > PRBool SendSecureXSiteReferrer() { return mSendSecureXSiteReferrer; } > PRUint8 RedirectionLimit() { return mRedirectionLimit; } > PRUint16 IdleTimeout() { return mIdleTimeout; } > PRUint16 MaxRequestAttempts() { return mMaxRequestAttempts; } > const char *DefaultSocketType() { return mDefaultSocketType.get(); /* ok to return null */ } > nsIIDNService *IDNConverter() { return mIDNConverter; } > PRUint32 PhishyUserPassLength() { return mPhishyUserPassLength; } > PRUint8 GetQoSBits() { return mQoSBits; } >+ PRUint16 GetIdleSynTimeout() { return mIdleSynTimeout; } > > PRBool IsPersistentHttpsCachingEnabled() { return mEnablePersistentHttpsCaching; } > > PRBool PromptTempRedirect() { return mPromptTempRedirect; } > > nsHttpAuthCache *AuthCache() { return &mAuthCache; } > nsHttpConnectionMgr *ConnMgr() { return mConnMgr; } > >@@ -258,16 +259,17 @@ private: > PRUint8 mProxyHttpVersion; > PRUint8 mCapabilities; > PRUint8 mProxyCapabilities; > PRUint8 mReferrerLevel; > > PRUint16 mIdleTimeout; > PRUint16 mMaxRequestAttempts; > PRUint16 mMaxRequestDelay; >+ PRUint16 mIdleSynTimeout; > > PRUint16 mMaxConnections; > PRUint8 mMaxConnectionsPerServer; > PRUint8 mMaxPersistentConnectionsPerServer; > PRUint8 mMaxPersistentConnectionsPerProxy; > PRUint8 mMaxPipelinedRequests; > > PRUint8 mRedirectionLimit; >diff --git a/netwerk/protocol/http/nsHttpPipeline.cpp b/netwerk/protocol/http/nsHttpPipeline.cpp >--- a/netwerk/protocol/http/nsHttpPipeline.cpp >+++ b/netwerk/protocol/http/nsHttpPipeline.cpp >@@ -319,26 +319,30 @@ nsHttpPipeline::SetConnection(nsAHttpCon > NS_IF_ADDREF(mConnection = conn); > > PRInt32 i, count = mRequestQ.Length(); > for (i=0; i<count; ++i) > Request(i)->SetConnection(this); > } > > 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 > nsHttpPipeline::OnTransportStatus(nsresult status, PRUint64 progress) > { > LOG(("nsHttpPipeline::OnStatus [this=%x status=%x progress=%llu]\n", > this, status, progress)); > >diff --git a/netwerk/protocol/http/nsHttpTransaction.cpp b/netwerk/protocol/http/nsHttpTransaction.cpp >--- a/netwerk/protocol/http/nsHttpTransaction.cpp >+++ b/netwerk/protocol/http/nsHttpTransaction.cpp >@@ -328,19 +328,22 @@ nsHttpTransaction::TakeResponseHead() > void > nsHttpTransaction::SetConnection(nsAHttpConnection *conn) > { > NS_IF_RELEASE(mConnection); > NS_IF_ADDREF(mConnection = 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 > nsHttpTransaction::OnTransportStatus(nsresult status, PRUint64 progress) > { > LOG(("nsHttpTransaction::OnSocketStatus [this=%x status=%x progress=%llu]\n", > this, status, progress)); >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
Actions:
View
|
Diff
|
Review
Attachments on
bug 623948
:
509492
|
510735
|
513192
|
518362
|
520642
|
520980
|
523673