mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 10:00:54 +00:00
Fixes bug 80419 (part 2) "http should reclaim connections immediately"
r=bbaetz, rs=dougt, a=blizzard
This commit is contained in:
parent
1f7dc204b3
commit
cf3d4afb48
@ -38,29 +38,6 @@
|
||||
|
||||
static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// helpers...
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void *PR_CALLBACK
|
||||
TransactionReleaseEventHandler(PLEvent *ev)
|
||||
{
|
||||
nsHttpTransaction *trans =
|
||||
NS_STATIC_CAST(nsHttpTransaction *, PL_GetEventOwner(ev));
|
||||
|
||||
LOG(("TransactionReleaseEventHandler [trans=%x refcnt=%u] calling release...\n",
|
||||
trans, trans->RefCnt()));
|
||||
|
||||
NS_RELEASE(trans);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PR_CALLBACK
|
||||
TransactionReleaseDestroyHandler(PLEvent *ev)
|
||||
{
|
||||
delete ev;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpConnection <public>
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -86,10 +63,7 @@ nsHttpConnection::~nsHttpConnection()
|
||||
NS_IF_RELEASE(mConnectionInfo);
|
||||
mConnectionInfo = 0;
|
||||
|
||||
if (mTransaction) {
|
||||
ProxyReleaseTransaction(mTransaction);
|
||||
mTransaction = 0;
|
||||
}
|
||||
NS_IF_RELEASE(mTransaction);
|
||||
|
||||
// warning: this call could result in OnStopRequest being called. this
|
||||
// is why we are careful to null out mTransaction and mConnectionInfo ;-)
|
||||
@ -111,7 +85,7 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// never called from the socket thread
|
||||
// called from any thread
|
||||
nsresult
|
||||
nsHttpConnection::SetTransaction(nsHttpTransaction *transaction)
|
||||
{
|
||||
@ -125,22 +99,15 @@ nsHttpConnection::SetTransaction(nsHttpTransaction *transaction)
|
||||
mTransaction = transaction;
|
||||
NS_ADDREF(mTransaction);
|
||||
|
||||
// grab a reference to the calling thread's event queue.
|
||||
mConsumerEventQ = 0;
|
||||
nsCOMPtr<nsIEventQueueService> eqs;
|
||||
nsHttpHandler::get()->GetEventQueueService(getter_AddRefs(eqs));
|
||||
if (eqs)
|
||||
eqs->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(mConsumerEventQ));
|
||||
|
||||
// build a proxy for the progress event sink
|
||||
mProgressSink = 0;
|
||||
if (mTransaction->Callbacks()) {
|
||||
if (mTransaction->Callbacks() && mTransaction->ConsumerEventQ()) {
|
||||
nsCOMPtr<nsIProgressEventSink> temp = do_GetInterface(mTransaction->Callbacks());
|
||||
if (temp) {
|
||||
nsCOMPtr<nsIProxyObjectManager> mgr;
|
||||
nsHttpHandler::get()->GetProxyObjectManager(getter_AddRefs(mgr));
|
||||
if (mgr)
|
||||
mgr->GetProxyForObject(mConsumerEventQ,
|
||||
mgr->GetProxyForObject(mTransaction->ConsumerEventQ(),
|
||||
NS_GET_IID(nsIProgressEventSink),
|
||||
temp,
|
||||
PROXY_ASYNC | PROXY_ALWAYS,
|
||||
@ -429,26 +396,6 @@ nsHttpConnection::CreateTransport()
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnection::ProxyReleaseTransaction(nsHttpTransaction *trans)
|
||||
{
|
||||
LOG(("nsHttpConnection::ProxyReleaseTransaction [this=%x trans=%x refcnt=%u]\n",
|
||||
this, trans, trans->RefCnt()));
|
||||
|
||||
NS_ENSURE_TRUE(mConsumerEventQ, NS_ERROR_NOT_INITIALIZED);
|
||||
NS_ENSURE_ARG_POINTER(trans);
|
||||
|
||||
PLEvent *event = new PLEvent;
|
||||
if (!event)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
PL_InitEvent(event, trans,
|
||||
TransactionReleaseEventHandler,
|
||||
TransactionReleaseDestroyHandler);
|
||||
|
||||
return mConsumerEventQ->PostEvent(event);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpConnection::SetupSSLProxyConnect()
|
||||
{
|
||||
@ -549,17 +496,15 @@ nsHttpConnection::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
|
||||
mProgressSink = 0;
|
||||
|
||||
// make sure mTransaction is clear before calling OnStopTransaction
|
||||
nsHttpTransaction *trans = mTransaction;
|
||||
mTransaction = nsnull;
|
||||
if (mTransaction) {
|
||||
nsHttpTransaction *trans = mTransaction;
|
||||
mTransaction = nsnull;
|
||||
|
||||
trans->OnStopTransaction(status);
|
||||
trans->OnStopTransaction(status);
|
||||
NS_RELEASE(trans);
|
||||
}
|
||||
|
||||
// because this could be the last reference to the transaction and
|
||||
// because we are on the socket transport thread, it is essential that
|
||||
// this final release be proxied to the thread which called
|
||||
// SetTransaction.
|
||||
if (NS_FAILED(ProxyReleaseTransaction(trans)))
|
||||
NS_NOTREACHED("proxy release of transaction failed");
|
||||
nsHttpHandler::get()->ReclaimConnection(this);
|
||||
}
|
||||
// no point in returning anything else but NS_OK
|
||||
return NS_OK;
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsAutoLock.h"
|
||||
#include "prprf.h"
|
||||
|
||||
#if defined(XP_UNIX) || defined(XP_BEOS)
|
||||
@ -143,6 +144,11 @@ nsHttpHandler::~nsHttpHandler()
|
||||
mAuthCache = nsnull;
|
||||
}
|
||||
|
||||
if (mConnectionLock) {
|
||||
PR_DestroyLock(mConnectionLock);
|
||||
mConnectionLock = nsnull;
|
||||
}
|
||||
|
||||
mGlobalInstance = nsnull;
|
||||
}
|
||||
|
||||
@ -186,6 +192,10 @@ nsHttpHandler::Init()
|
||||
return rv;
|
||||
}
|
||||
|
||||
mConnectionLock = PR_NewLock();
|
||||
if (!mConnectionLock)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
mPrefs = do_GetService(kPrefServiceCID, &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("unable to continue without prefs service");
|
||||
@ -326,75 +336,23 @@ nsHttpHandler::GetCacheSession(nsCacheStoragePolicy storagePolicy,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// may be called from any thread
|
||||
nsresult
|
||||
nsHttpHandler::InitiateTransaction(nsHttpTransaction *trans,
|
||||
nsHttpConnectionInfo *ci,
|
||||
PRBool failIfBusy)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
LOG(("nsHttpHandler::InitiateTransaction\n"));
|
||||
|
||||
NS_ENSURE_ARG_POINTER(trans);
|
||||
NS_ENSURE_ARG_POINTER(ci);
|
||||
|
||||
if ((mActiveConnections.Count() == mMaxConnections) ||
|
||||
(CountActiveConnections(ci) == PRUint32(mMaxConnectionsPerServer))) {
|
||||
LOG(("unable to perform the transaction at this time [trans=%x]\n", trans));
|
||||
if (failIfBusy) return NS_ERROR_FAILURE;
|
||||
return EnqueueTransaction(trans, ci);
|
||||
}
|
||||
nsAutoLock lock(mConnectionLock);
|
||||
|
||||
nsHttpConnection *conn = nsnull;
|
||||
|
||||
// search the idle connection list
|
||||
PRInt32 i;
|
||||
for (i=0; i<mIdleConnections.Count(); ++i) {
|
||||
conn = (nsHttpConnection *) mIdleConnections[i];
|
||||
|
||||
LOG(("comparing against idle connection [host=%s:%d]\n",
|
||||
conn->ConnectionInfo()->Host(), conn->ConnectionInfo()->Port()));
|
||||
|
||||
// we check if the connection can be reused before even checking if it
|
||||
// is a "matching" connection. this is how we keep the idle connection
|
||||
// list fresh. we could alternatively use some sort of timer for this.
|
||||
if (!conn->CanReuse()) {
|
||||
LOG(("dropping stale connection: [conn=%x]\n", conn));
|
||||
mIdleConnections.RemoveElementAt(i);
|
||||
i--;
|
||||
NS_RELEASE(conn);
|
||||
}
|
||||
else if (conn->ConnectionInfo()->Equals(ci)) {
|
||||
LOG(("reusing connection [conn=%x]\n", conn));
|
||||
mIdleConnections.RemoveElementAt(i);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
conn = nsnull;
|
||||
}
|
||||
|
||||
if (!conn) {
|
||||
LOG(("creating new connection...\n"));
|
||||
NS_NEWXPCOM(conn, nsHttpConnection);
|
||||
if (!conn)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(conn);
|
||||
|
||||
rv = conn->Init(ci);
|
||||
if (NS_FAILED(rv)) goto failed;
|
||||
}
|
||||
|
||||
rv = conn->SetTransaction(trans);
|
||||
if (NS_FAILED(rv)) goto failed;
|
||||
|
||||
mActiveConnections.AppendElement(conn);
|
||||
return NS_OK;
|
||||
|
||||
failed:
|
||||
NS_RELEASE(conn);
|
||||
return rv;
|
||||
return InitiateTransaction_Locked(trans, ci, failIfBusy);
|
||||
}
|
||||
|
||||
// may be called from any thread
|
||||
nsresult
|
||||
nsHttpHandler::ReclaimConnection(nsHttpConnection *conn)
|
||||
{
|
||||
@ -403,6 +361,8 @@ nsHttpHandler::ReclaimConnection(nsHttpConnection *conn)
|
||||
LOG(("nsHttpHandler::ReclaimConnection [conn=%x keep-alive=%d]\n",
|
||||
conn, conn->CanReuse()));
|
||||
|
||||
nsAutoLock lock(mConnectionLock);
|
||||
|
||||
// remove connection from the active connection list
|
||||
mActiveConnections.RemoveElement(conn);
|
||||
|
||||
@ -446,8 +406,9 @@ nsHttpHandler::CancelPendingTransaction(nsHttpTransaction *trans,
|
||||
|
||||
NS_ENSURE_ARG_POINTER(trans);
|
||||
|
||||
nsPendingTransaction *pt = nsnull;
|
||||
nsAutoLock lock(mConnectionLock);
|
||||
|
||||
nsPendingTransaction *pt = nsnull;
|
||||
PRInt32 i;
|
||||
for (i=0; i<mTransactionQ.Count(); ++i) {
|
||||
pt = (nsPendingTransaction *) mTransactionQ[i];
|
||||
@ -624,6 +585,7 @@ nsHttpHandler::UserAgent()
|
||||
return mUserAgent.get();
|
||||
}
|
||||
|
||||
// called while holding the connection lock
|
||||
void
|
||||
nsHttpHandler::ProcessTransactionQ()
|
||||
{
|
||||
@ -640,9 +602,9 @@ nsHttpHandler::ProcessTransactionQ()
|
||||
// try to initiate this transaction... if it fails
|
||||
// then we'll just skip over this pending transaction
|
||||
// and try the next.
|
||||
nsresult rv = InitiateTransaction(pt->Transaction(),
|
||||
pt->ConnectionInfo(),
|
||||
PR_TRUE);
|
||||
nsresult rv = InitiateTransaction_Locked(pt->Transaction(),
|
||||
pt->ConnectionInfo(),
|
||||
PR_TRUE);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mTransactionQ.RemoveElementAt(i);
|
||||
delete pt;
|
||||
@ -651,6 +613,7 @@ nsHttpHandler::ProcessTransactionQ()
|
||||
}
|
||||
}
|
||||
|
||||
// called while holding the connection lock
|
||||
nsresult
|
||||
nsHttpHandler::EnqueueTransaction(nsHttpTransaction *trans,
|
||||
nsHttpConnectionInfo *ci)
|
||||
@ -667,14 +630,90 @@ nsHttpHandler::EnqueueTransaction(nsHttpTransaction *trans,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpHandler::InitiateTransaction_Locked(nsHttpTransaction *trans,
|
||||
nsHttpConnectionInfo *ci,
|
||||
PRBool failIfBusy)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
LOG(("nsHttpHandler::InitiateTransaction_Locked [failIfBusy=%d]\n", failIfBusy));
|
||||
|
||||
if ((mActiveConnections.Count() == mMaxConnections) ||
|
||||
(CountActiveConnections(ci) == PRUint32(mMaxConnectionsPerServer))) {
|
||||
LOG(("unable to perform the transaction at this time [trans=%x]\n", trans));
|
||||
if (failIfBusy) return NS_ERROR_FAILURE;
|
||||
return EnqueueTransaction(trans, ci);
|
||||
}
|
||||
|
||||
nsHttpConnection *conn = nsnull;
|
||||
|
||||
// search the idle connection list
|
||||
PRInt32 i;
|
||||
for (i=0; i<mIdleConnections.Count(); ++i) {
|
||||
conn = (nsHttpConnection *) mIdleConnections[i];
|
||||
|
||||
LOG(("comparing against idle connection [host=%s:%d]\n",
|
||||
conn->ConnectionInfo()->Host(), conn->ConnectionInfo()->Port()));
|
||||
|
||||
// we check if the connection can be reused before even checking if it
|
||||
// is a "matching" connection. this is how we keep the idle connection
|
||||
// list fresh. we could alternatively use some sort of timer for this.
|
||||
if (!conn->CanReuse()) {
|
||||
LOG(("dropping stale connection: [conn=%x]\n", conn));
|
||||
mIdleConnections.RemoveElementAt(i);
|
||||
i--;
|
||||
NS_RELEASE(conn);
|
||||
}
|
||||
else if (conn->ConnectionInfo()->Equals(ci)) {
|
||||
LOG(("reusing connection [conn=%x]\n", conn));
|
||||
mIdleConnections.RemoveElementAt(i);
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
conn = nsnull;
|
||||
}
|
||||
|
||||
if (!conn) {
|
||||
LOG(("creating new connection...\n"));
|
||||
NS_NEWXPCOM(conn, nsHttpConnection);
|
||||
if (!conn)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
NS_ADDREF(conn);
|
||||
|
||||
rv = conn->Init(ci);
|
||||
if (NS_FAILED(rv)) goto failed;
|
||||
}
|
||||
|
||||
rv = conn->SetTransaction(trans);
|
||||
if (NS_FAILED(rv)) goto failed;
|
||||
|
||||
mActiveConnections.AppendElement(conn);
|
||||
return NS_OK;
|
||||
|
||||
failed:
|
||||
NS_RELEASE(conn);
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRUint32
|
||||
nsHttpHandler::CountActiveConnections(nsHttpConnectionInfo *ci)
|
||||
{
|
||||
PRUint32 count = mActiveConnections.Count();
|
||||
PRUint32 count = 0;
|
||||
nsHttpConnection *conn = 0;
|
||||
|
||||
LOG(("nsHttpHandler::CountActiveConnections [host=%s:%d] found %u\n",
|
||||
ci->Host(), ci->Port(), count));
|
||||
LOG(("nsHttpHandler::CountActiveConnections [host=%s:%d]\n",
|
||||
ci->Host(), ci->Port()));
|
||||
|
||||
PRInt32 i;
|
||||
for (i=0; i<mActiveConnections.Count(); ++i) {
|
||||
conn = NS_STATIC_CAST(nsHttpConnection *, mActiveConnections[i]);
|
||||
// only include a matching connection in the count...
|
||||
if (conn->ConnectionInfo()->Equals(ci))
|
||||
count++;
|
||||
}
|
||||
|
||||
LOG(("found count=%u\n", count));
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -692,7 +731,7 @@ nsHttpHandler::CountIdleConnections(nsHttpConnectionInfo *ci)
|
||||
|
||||
PRInt32 i;
|
||||
for (i=0; i<mIdleConnections.Count(); ++i) {
|
||||
conn = (nsHttpConnection *) mIdleConnections[i];
|
||||
conn = NS_STATIC_CAST(nsHttpConnection *, mIdleConnections[i]);
|
||||
// only include a matching connection in the count if it
|
||||
// can still be reused.
|
||||
if (conn->ConnectionInfo()->Equals(ci)) {
|
||||
|
@ -106,16 +106,17 @@ public:
|
||||
|
||||
// Called to kick-off a new transaction, by default the transaction
|
||||
// will be put on the pending transaction queue if it cannot be
|
||||
// initiated at this time.
|
||||
// initiated at this time. Callable from any thread.
|
||||
nsresult InitiateTransaction(nsHttpTransaction *,
|
||||
nsHttpConnectionInfo *,
|
||||
PRBool failIfBusy = PR_FALSE);
|
||||
|
||||
// Called when a connection is done processing a transaction
|
||||
// Called when a connection is done processing a transaction. Callable
|
||||
// from any thread.
|
||||
nsresult ReclaimConnection(nsHttpConnection *);
|
||||
|
||||
// Called when a transaction, which is not assigned to a connection,
|
||||
// is canceled.
|
||||
// is canceled. Callable from any thread.
|
||||
nsresult CancelPendingTransaction(nsHttpTransaction *, nsresult status);
|
||||
|
||||
//
|
||||
@ -161,6 +162,11 @@ private:
|
||||
void ProcessTransactionQ();
|
||||
nsresult EnqueueTransaction(nsHttpTransaction *, nsHttpConnectionInfo *);
|
||||
|
||||
// Called with mConnectionLock held
|
||||
nsresult InitiateTransaction_Locked(nsHttpTransaction *,
|
||||
nsHttpConnectionInfo *,
|
||||
PRBool failIfBusy = PR_FALSE);
|
||||
|
||||
PRUint32 CountActiveConnections(nsHttpConnectionInfo *);
|
||||
PRUint32 CountIdleConnections(nsHttpConnectionInfo *);
|
||||
|
||||
@ -195,7 +201,7 @@ private:
|
||||
nsCOMPtr<nsIMIMEService> mMimeService;
|
||||
|
||||
// the authentication credentials cache
|
||||
nsHttpAuthCache *mAuthCache;
|
||||
nsHttpAuthCache *mAuthCache;
|
||||
|
||||
//
|
||||
// prefs
|
||||
@ -231,6 +237,7 @@ private:
|
||||
nsVoidArray mActiveConnections; // list of nsHttpConnection objects
|
||||
nsVoidArray mIdleConnections; // list of nsHttpConnection objects
|
||||
nsVoidArray mTransactionQ; // list of nsPendingTransaction objects
|
||||
PRLock *mConnectionLock; // protect connection lists
|
||||
|
||||
// useragent components
|
||||
nsXPIDLCString mAppName;
|
||||
|
@ -32,45 +32,6 @@
|
||||
#include "pratom.h"
|
||||
#include "plevent.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// helpers...
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void *PR_CALLBACK
|
||||
TransactionRestartEventHandler(PLEvent *ev)
|
||||
{
|
||||
nsHttpTransaction *trans =
|
||||
NS_STATIC_CAST(nsHttpTransaction *, PL_GetEventOwner(ev));
|
||||
|
||||
LOG(("TransactionRestartEventHandler [trans=%x]\n", trans));
|
||||
|
||||
NS_PRECONDITION(trans->Connection() &&
|
||||
trans->Connection()->ConnectionInfo(), "oops");
|
||||
|
||||
if (trans->Connection()) {
|
||||
nsHttpConnectionInfo *ci = trans->Connection()->ConnectionInfo();
|
||||
if (ci) {
|
||||
NS_ADDREF(ci);
|
||||
|
||||
// clean up the old connection
|
||||
nsHttpHandler::get()->ReclaimConnection(trans->Connection());
|
||||
trans->SetConnection(nsnull);
|
||||
|
||||
// initiate the transaction again
|
||||
nsHttpHandler::get()->InitiateTransaction(trans, ci);
|
||||
NS_RELEASE(ci);
|
||||
}
|
||||
}
|
||||
NS_RELEASE(trans);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void PR_CALLBACK
|
||||
TransactionRestartDestroyHandler(PLEvent *ev)
|
||||
{
|
||||
delete ev;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpTransaction
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -103,10 +64,7 @@ nsHttpTransaction::~nsHttpTransaction()
|
||||
{
|
||||
LOG(("Destroying nsHttpTransaction @%x\n", this));
|
||||
|
||||
if (mConnection) {
|
||||
nsHttpHandler::get()->ReclaimConnection(mConnection);
|
||||
NS_RELEASE(mConnection);
|
||||
}
|
||||
NS_IF_RELEASE(mConnection);
|
||||
|
||||
if (mChunkedDecoder)
|
||||
delete mChunkedDecoder;
|
||||
@ -122,6 +80,12 @@ nsHttpTransaction::SetupRequest(nsHttpRequestHead *requestHead,
|
||||
|
||||
NS_ENSURE_ARG_POINTER(requestHead);
|
||||
|
||||
// grab a reference to the calling thread's event queue.
|
||||
nsCOMPtr<nsIEventQueueService> eqs;
|
||||
nsHttpHandler::get()->GetEventQueueService(getter_AddRefs(eqs));
|
||||
if (eqs)
|
||||
eqs->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(mConsumerEventQ));
|
||||
|
||||
if (requestHead->Method() == nsHttp::Head)
|
||||
mNoContent = PR_TRUE;
|
||||
|
||||
@ -150,6 +114,9 @@ nsHttpTransaction::SetupRequest(nsHttpRequestHead *requestHead,
|
||||
nsresult
|
||||
nsHttpTransaction::SetConnection(nsHttpConnection *conn)
|
||||
{
|
||||
LOG(("nsHttpTransaction::SetConnection [this=%x mConnection=%x conn=%x]\n",
|
||||
this, mConnection, conn));
|
||||
|
||||
mConnection = conn;
|
||||
NS_IF_ADDREF(mConnection);
|
||||
return NS_OK;
|
||||
@ -229,11 +196,17 @@ nsHttpTransaction::OnDataReadable(nsIInputStream *is)
|
||||
// we don't want the connection to send anymore notifications to us.
|
||||
mConnection->DropTransaction();
|
||||
|
||||
// the transaction needs to be restarted from the thread on which
|
||||
// it was created.
|
||||
rv = ProxyRestartTransaction(mConnection->ConsumerEventQ());
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "ProxyRestartTransaction failed");
|
||||
nsHttpConnectionInfo *ci = mConnection->ConnectionInfo();
|
||||
NS_ADDREF(ci);
|
||||
|
||||
// we must release the connection before calling initiate transaction
|
||||
// since we will be getting a new connection.
|
||||
NS_RELEASE(mConnection);
|
||||
|
||||
rv = nsHttpHandler::get()->InitiateTransaction(this, ci);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "InitiateTransaction failed");
|
||||
|
||||
NS_RELEASE(ci);
|
||||
NS_RELEASE_THIS();
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
@ -502,7 +475,13 @@ nsHttpTransaction::HandleContent(char *buf,
|
||||
if (priorVal == 0 && mConnection) {
|
||||
// let the connection know that we are done with it; this should
|
||||
// result in OnStopTransaction being fired.
|
||||
return mConnection->OnTransactionComplete(NS_OK);
|
||||
rv = mConnection->OnTransactionComplete(NS_OK);
|
||||
|
||||
// at this point, we no longer need the connection
|
||||
//nsHttpHandler::get()->ReclaimConnection(mConnection);
|
||||
NS_RELEASE(mConnection);
|
||||
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -512,34 +491,83 @@ nsHttpTransaction::HandleContent(char *buf,
|
||||
return (!mNoContent && !*countRead) ? NS_BASE_STREAM_WOULD_BLOCK : NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHttpTransaction::ProxyRestartTransaction(nsIEventQueue *eventQ)
|
||||
void
|
||||
nsHttpTransaction::DeleteSelfOnConsumerThread()
|
||||
{
|
||||
LOG(("nsHttpTransaction::ProxyRestartTransaction [this=%x]\n", this));
|
||||
nsCOMPtr<nsIEventQueueService> eqs;
|
||||
nsCOMPtr<nsIEventQueue> currentEventQ;
|
||||
|
||||
NS_ENSURE_ARG_POINTER(eventQ);
|
||||
NS_PRECONDITION(!mResponseHead, "already received a (partial) response!");
|
||||
LOG(("nsHttpTransaction::DeleteSelfOnConsumerThread [this=%x]\n", this));
|
||||
|
||||
PLEvent *event = new PLEvent;
|
||||
if (!event)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
nsHttpHandler::get()->GetEventQueueService(getter_AddRefs(eqs));
|
||||
if (eqs)
|
||||
eqs->ResolveEventQueue(NS_CURRENT_EVENTQ, getter_AddRefs(currentEventQ));
|
||||
|
||||
NS_ADDREF_THIS();
|
||||
if (currentEventQ == mConsumerEventQ)
|
||||
delete this;
|
||||
else {
|
||||
LOG(("proxying delete to consumer thread...\n"));
|
||||
|
||||
PL_InitEvent(event, this,
|
||||
TransactionRestartEventHandler,
|
||||
TransactionRestartDestroyHandler);
|
||||
PLEvent *event = new PLEvent;
|
||||
if (!event) {
|
||||
NS_WARNING("out of memory");
|
||||
// probably better to leak |this| than to delete it on this thread.
|
||||
return;
|
||||
}
|
||||
|
||||
return eventQ->PostEvent(event);
|
||||
PL_InitEvent(event, this,
|
||||
nsHttpTransaction::DeleteThis_EventHandlerFunc,
|
||||
nsHttpTransaction::DeleteThis_EventCleanupFunc);
|
||||
|
||||
PRStatus status = mConsumerEventQ->PostEvent(event);
|
||||
NS_ASSERTION(status == PR_SUCCESS, "PostEvent failed");
|
||||
}
|
||||
}
|
||||
|
||||
void *PR_CALLBACK
|
||||
nsHttpTransaction::DeleteThis_EventHandlerFunc(PLEvent *ev)
|
||||
{
|
||||
nsHttpTransaction *trans =
|
||||
NS_STATIC_CAST(nsHttpTransaction *, PL_GetEventOwner(ev));
|
||||
|
||||
LOG(("nsHttpTransaction::DeleteThis_EventHandlerFunc [trans=%x]\n", trans));
|
||||
|
||||
delete trans;
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
void PR_CALLBACK
|
||||
nsHttpTransaction::DeleteThis_EventCleanupFunc(PLEvent *ev)
|
||||
{
|
||||
delete ev;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpTransaction::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_THREADSAFE_ISUPPORTS2(nsHttpTransaction,
|
||||
nsIRequest,
|
||||
nsIInputStream)
|
||||
NS_IMPL_THREADSAFE_ADDREF(nsHttpTransaction)
|
||||
|
||||
NS_IMETHODIMP_(nsrefcnt)
|
||||
nsHttpTransaction::Release()
|
||||
{
|
||||
nsrefcnt count;
|
||||
NS_PRECONDITION(0 != mRefCnt, "dup release");
|
||||
count = PR_AtomicDecrement((PRInt32 *) &mRefCnt);
|
||||
NS_LOG_RELEASE(this, count, "nsHttpTransaction");
|
||||
if (0 == count) {
|
||||
mRefCnt = 1; /* stablize */
|
||||
// it is essential that the transaction be destroyed on the consumer
|
||||
// thread (we could be holding the last reference to our consumer).
|
||||
DeleteSelfOnConsumerThread();
|
||||
return 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
NS_IMPL_THREADSAFE_QUERY_INTERFACE2(nsHttpTransaction,
|
||||
nsIRequest,
|
||||
nsIInputStream)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpTransaction::nsIRequest
|
||||
@ -592,8 +620,10 @@ nsHttpTransaction::Cancel(nsresult status)
|
||||
// completion, in which case we should ignore this cancelation request.
|
||||
|
||||
PRInt32 priorVal = PR_AtomicSet(&mTransactionDone, 1);
|
||||
if (priorVal == 0)
|
||||
if (priorVal == 0) {
|
||||
mConnection->OnTransactionComplete(status);
|
||||
NS_RELEASE(mConnection);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIEventQueue.h"
|
||||
#include "nsXPIDLString.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
@ -36,7 +37,6 @@ class nsHttpRequestHead;
|
||||
class nsHttpResponseHead;
|
||||
class nsHttpConnection;
|
||||
class nsHttpChunkedDecoder;
|
||||
class nsIEventQueue;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// nsHttpTransaction represents a single HTTP transaction. It is thread-safe,
|
||||
@ -63,11 +63,12 @@ public:
|
||||
// Called to initialize the transaction
|
||||
nsresult SetupRequest(nsHttpRequestHead *, nsIInputStream *);
|
||||
|
||||
nsIStreamListener *Listener() { return mListener; }
|
||||
nsHttpConnection *Connection() { return mConnection; }
|
||||
nsHttpRequestHead *RequestHead() { return mRequestHead; }
|
||||
nsHttpResponseHead *ResponseHead() { return mResponseHead; }
|
||||
nsIInterfaceRequestor *Callbacks() { return mCallbacks; }
|
||||
nsIStreamListener *Listener() { return mListener; }
|
||||
nsHttpConnection *Connection() { return mConnection; }
|
||||
nsHttpRequestHead *RequestHead() { return mRequestHead; }
|
||||
nsHttpResponseHead *ResponseHead() { return mResponseHead; }
|
||||
nsIInterfaceRequestor *Callbacks() { return mCallbacks; }
|
||||
nsIEventQueue *ConsumerEventQ() { return mConsumerEventQ; }
|
||||
|
||||
// Called to take ownership of the response headers; the transaction
|
||||
// will drop any reference to the response headers after this call.
|
||||
@ -90,11 +91,15 @@ private:
|
||||
void ParseLineSegment(char *seg, PRUint32 len);
|
||||
nsresult ParseHead(char *, PRUint32 count, PRUint32 *countRead);
|
||||
nsresult HandleContent(char *, PRUint32 count, PRUint32 *countRead);
|
||||
nsresult ProxyRestartTransaction(nsIEventQueue *);
|
||||
void DeleteSelfOnConsumerThread();
|
||||
|
||||
static void *PR_CALLBACK DeleteThis_EventHandlerFunc(PLEvent *);
|
||||
static void PR_CALLBACK DeleteThis_EventCleanupFunc(PLEvent *);
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIStreamListener> mListener;
|
||||
nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
|
||||
nsCOMPtr<nsIEventQueue> mConsumerEventQ;
|
||||
|
||||
nsHttpConnection *mConnection; // hard ref
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user