mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 15:15:23 +00:00
6993d42063
Bug 614677 - Connection is reset message appears intermittently Bug 614950 - Connections stall occasionally after 592284 landed A couple of follow-on changes to 592284 rolled together to prevent diff conflicts 1] Set the securitycallback information for unused speculative connections in the connection manager to be the new cloned connection rather than the one they originated on. (613977) 2] When adding unused speculative connections to the connection manager, due so with a short timeout (<= 5 seconds) as some servers get grumpy if they haven't seen a request in that time. Most will close the connection, but some will just sit there quietly and RST things when the connection is used - so if you don't use the connection quickly don't use it at all. This is probably a L4 load balancer issue, actually. Mozillazine illustrates the problem. Connections are made in bursts anyhow, so the reuse optimization is likely still quite useful. (614677 and 614950) 3] mark every connection in the connection manager persistent conneciton pool as "reused". This allows the transaction to be restarted if a RST is recvd upon sending the requests (see #2) - with the conservative timeout this is now a rare event, but still possible so recovery is the right thing to do. (614677 and 614950) 4] obtain an nshttpconnection object from the connection manager, subject to the max connection constraints, at the same time as starting the backup conneciton. If we defer that until recycling time the exceeded limits of the SocketService can cause problems for other connections. also re-enables the syn retry feature by default. r+ honzab
1153 lines
36 KiB
C++
1153 lines
36 KiB
C++
/* vim:set ts=4 sw=4 sts=4 et cin: */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is Mozilla.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 2002
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Darin Fisher <darin@netscape.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "nsHttpConnectionMgr.h"
|
|
#include "nsHttpConnection.h"
|
|
#include "nsHttpPipeline.h"
|
|
#include "nsHttpHandler.h"
|
|
#include "nsAutoLock.h"
|
|
#include "nsNetCID.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsIObserverService.h"
|
|
|
|
// defined by the socket transport service while active
|
|
extern PRThread *gSocketThread;
|
|
|
|
static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS1(nsHttpConnectionMgr, nsIObserver)
|
|
|
|
static void
|
|
InsertTransactionSorted(nsTArray<nsHttpTransaction*> &pendingQ, nsHttpTransaction *trans)
|
|
{
|
|
// insert into queue with smallest valued number first. search in reverse
|
|
// order under the assumption that many of the existing transactions will
|
|
// have the same priority (usually 0).
|
|
|
|
for (PRInt32 i=pendingQ.Length()-1; i>=0; --i) {
|
|
nsHttpTransaction *t = pendingQ[i];
|
|
if (trans->Priority() >= t->Priority()) {
|
|
pendingQ.InsertElementAt(i+1, trans);
|
|
return;
|
|
}
|
|
}
|
|
pendingQ.InsertElementAt(0, trans);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
nsHttpConnectionMgr::nsHttpConnectionMgr()
|
|
: mRef(0)
|
|
, mMonitor(nsAutoMonitor::NewMonitor("nsHttpConnectionMgr"))
|
|
, mMaxConns(0)
|
|
, mMaxConnsPerHost(0)
|
|
, mMaxConnsPerProxy(0)
|
|
, mMaxPersistConnsPerHost(0)
|
|
, mMaxPersistConnsPerProxy(0)
|
|
, mIsShuttingDown(PR_FALSE)
|
|
, mNumActiveConns(0)
|
|
, mNumIdleConns(0)
|
|
, mTimeOfNextWakeUp(LL_MAXUINT)
|
|
{
|
|
LOG(("Creating nsHttpConnectionMgr @%x\n", this));
|
|
}
|
|
|
|
nsHttpConnectionMgr::~nsHttpConnectionMgr()
|
|
{
|
|
LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
|
|
|
|
if (mMonitor)
|
|
nsAutoMonitor::DestroyMonitor(mMonitor);
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::EnsureSocketThreadTargetIfOnline()
|
|
{
|
|
nsresult rv;
|
|
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);
|
|
}
|
|
}
|
|
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
// do nothing if already initialized or if we've shut down
|
|
if (mSocketThreadTarget || mIsShuttingDown)
|
|
return NS_OK;
|
|
|
|
mSocketThreadTarget = sts;
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::Init(PRUint16 maxConns,
|
|
PRUint16 maxConnsPerHost,
|
|
PRUint16 maxConnsPerProxy,
|
|
PRUint16 maxPersistConnsPerHost,
|
|
PRUint16 maxPersistConnsPerProxy,
|
|
PRUint16 maxRequestDelay,
|
|
PRUint16 maxPipelinedRequests)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::Init\n"));
|
|
|
|
{
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
mMaxConns = maxConns;
|
|
mMaxConnsPerHost = maxConnsPerHost;
|
|
mMaxConnsPerProxy = maxConnsPerProxy;
|
|
mMaxPersistConnsPerHost = maxPersistConnsPerHost;
|
|
mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
|
|
mMaxRequestDelay = maxRequestDelay;
|
|
mMaxPipelinedRequests = maxPipelinedRequests;
|
|
|
|
mIsShuttingDown = PR_FALSE;
|
|
}
|
|
|
|
return EnsureSocketThreadTargetIfOnline();
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::Shutdown()
|
|
{
|
|
LOG(("nsHttpConnectionMgr::Shutdown\n"));
|
|
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
// do nothing if already shutdown
|
|
if (!mSocketThreadTarget)
|
|
return NS_OK;
|
|
|
|
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgShutdown);
|
|
|
|
// release our reference to the STS to prevent further events
|
|
// from being posted. this is how we indicate that we are
|
|
// shutting down.
|
|
mIsShuttingDown = PR_TRUE;
|
|
mSocketThreadTarget = 0;
|
|
|
|
if (NS_FAILED(rv)) {
|
|
NS_WARNING("unable to post SHUTDOWN message");
|
|
return rv;
|
|
}
|
|
|
|
// wait for shutdown event to complete
|
|
mon.Wait();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, PRInt32 iparam, void *vparam)
|
|
{
|
|
// This object doesn't get reinitialized if the offline state changes, so our
|
|
// socket thread target might be uninitialized if we were offline when this
|
|
// object was being initialized, and we go online later on. This call takes
|
|
// care of initializing the socket thread target if that's the case.
|
|
EnsureSocketThreadTargetIfOnline();
|
|
|
|
nsAutoMonitor mon(mMonitor);
|
|
|
|
nsresult rv;
|
|
if (!mSocketThreadTarget) {
|
|
NS_WARNING("cannot post event if not initialized");
|
|
rv = NS_ERROR_NOT_INITIALIZED;
|
|
}
|
|
else {
|
|
nsRefPtr<nsIRunnable> event = new nsConnEvent(this, handler, iparam, vparam);
|
|
if (!event)
|
|
rv = NS_ERROR_OUT_OF_MEMORY;
|
|
else
|
|
rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::PruneDeadConnectionsAfter(PRUint32 timeInSeconds)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));
|
|
|
|
if(!mTimer)
|
|
mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
|
|
// failure to create a timer is not a fatal error, but idle connections
|
|
// will not be cleaned up until we try to use them.
|
|
if (mTimer) {
|
|
mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
|
|
mTimer->Init(this, timeInSeconds*1000, nsITimer::TYPE_ONE_SHOT);
|
|
} else {
|
|
NS_WARNING("failed to create: timer for pruning the dead connections!");
|
|
}
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::StopPruneDeadConnectionsTimer()
|
|
{
|
|
LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));
|
|
|
|
// Reset mTimeOfNextWakeUp so that we can find a new shortest value.
|
|
mTimeOfNextWakeUp = LL_MAXUINT;
|
|
if (mTimer) {
|
|
mTimer->Cancel();
|
|
mTimer = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsHttpConnectionMgr::nsIObserver
|
|
//-----------------------------------------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
nsHttpConnectionMgr::Observe(nsISupports *subject,
|
|
const char *topic,
|
|
const PRUnichar *data)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::Observe [topic=\"%s\"]\n", topic));
|
|
|
|
if (0 == strcmp(topic, "timer-callback")) {
|
|
// prune dead connections
|
|
PruneDeadConnections();
|
|
#ifdef DEBUG
|
|
nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
|
|
NS_ASSERTION(timer == mTimer, "unexpected timer-callback");
|
|
#endif
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, PRInt32 priority)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::AddTransaction [trans=%x %d]\n", trans, priority));
|
|
|
|
NS_ADDREF(trans);
|
|
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
|
|
if (NS_FAILED(rv))
|
|
NS_RELEASE(trans);
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, PRInt32 priority)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%x %d]\n", trans, priority));
|
|
|
|
NS_ADDREF(trans);
|
|
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority, trans);
|
|
if (NS_FAILED(rv))
|
|
NS_RELEASE(trans);
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%x reason=%x]\n", trans, reason));
|
|
|
|
NS_ADDREF(trans);
|
|
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction, reason, trans);
|
|
if (NS_FAILED(rv))
|
|
NS_RELEASE(trans);
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::PruneDeadConnections()
|
|
{
|
|
return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget **target)
|
|
{
|
|
// This object doesn't get reinitialized if the offline state changes, so our
|
|
// socket thread target might be uninitialized if we were offline when this
|
|
// object was being initialized, and we go online later on. This call takes
|
|
// care of initializing the socket thread target if that's the case.
|
|
EnsureSocketThreadTargetIfOnline();
|
|
|
|
nsAutoMonitor mon(mMonitor);
|
|
NS_IF_ADDREF(*target = mSocketThreadTarget);
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::AddTransactionToPipeline(nsHttpPipeline *pipeline)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::AddTransactionToPipeline [pipeline=%x]\n", pipeline));
|
|
|
|
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
|
|
nsRefPtr<nsHttpConnectionInfo> ci;
|
|
pipeline->GetConnectionInfo(getter_AddRefs(ci));
|
|
if (ci) {
|
|
nsCStringKey key(ci->HashKey());
|
|
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
|
|
if (ent) {
|
|
// search for another request to pipeline...
|
|
PRInt32 i, count = ent->mPendingQ.Length();
|
|
for (i=0; i<count; ++i) {
|
|
nsHttpTransaction *trans = ent->mPendingQ[i];
|
|
if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
|
|
pipeline->AddTransaction(trans);
|
|
|
|
// remove transaction from pending queue
|
|
ent->mPendingQ.RemoveElementAt(i);
|
|
NS_RELEASE(trans);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
|
|
|
|
NS_ADDREF(conn);
|
|
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
|
|
if (NS_FAILED(rv))
|
|
NS_RELEASE(conn);
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::UpdateParam(nsParamName name, PRUint16 value)
|
|
{
|
|
PRUint32 param = (PRUint32(name) << 16) | PRUint32(value);
|
|
return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam, 0, (void *) param);
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
|
|
|
|
NS_ADDREF(ci);
|
|
nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
|
|
if (NS_FAILED(rv))
|
|
NS_RELEASE(ci);
|
|
return rv;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// enumeration callbacks
|
|
|
|
PRIntn
|
|
nsHttpConnectionMgr::ProcessOneTransactionCB(nsHashKey *key, void *data, void *closure)
|
|
{
|
|
nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
|
|
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::PurgeExcessIdleConnectionsCB(nsHashKey *key,
|
|
void *data, void *closure)
|
|
{
|
|
nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
|
|
nsConnectionEntry *ent = (nsConnectionEntry *) data;
|
|
|
|
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;
|
|
}
|
|
|
|
PRIntn
|
|
nsHttpConnectionMgr::PruneDeadConnectionsCB(nsHashKey *key, void *data, void *closure)
|
|
{
|
|
nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
|
|
nsConnectionEntry *ent = (nsConnectionEntry *) data;
|
|
|
|
LOG((" pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
|
|
|
|
// Find out how long it will take for next idle connection to not be reusable
|
|
// anymore.
|
|
PRUint32 timeToNextExpire = PR_UINT32_MAX;
|
|
PRInt32 count = ent->mIdleConns.Length();
|
|
if (count > 0) {
|
|
for (PRInt32 i=count-1; i>=0; --i) {
|
|
nsHttpConnection *conn = ent->mIdleConns[i];
|
|
if (!conn->CanReuse()) {
|
|
ent->mIdleConns.RemoveElementAt(i);
|
|
conn->Close(NS_ERROR_ABORT);
|
|
NS_RELEASE(conn);
|
|
self->mNumIdleConns--;
|
|
} else {
|
|
timeToNextExpire = PR_MIN(timeToNextExpire, conn->TimeToLive());
|
|
}
|
|
}
|
|
}
|
|
|
|
// If time to next expire found is shorter than time to next wake-up, we need to
|
|
// change the time for next wake-up.
|
|
PRUint32 now = NowInSeconds();
|
|
if (0 < ent->mIdleConns.Length()) {
|
|
PRUint64 timeOfNextExpire = now + timeToNextExpire;
|
|
// If pruning of dead connections is not already scheduled to happen
|
|
// or time found for next connection to expire is is before
|
|
// mTimeOfNextWakeUp, we need to schedule the pruning to happen
|
|
// after timeToNextExpire.
|
|
if (!self->mTimer || timeOfNextExpire < self->mTimeOfNextWakeUp) {
|
|
self->PruneDeadConnectionsAfter(timeToNextExpire);
|
|
}
|
|
} else if (0 == self->mNumIdleConns) {
|
|
self->StopPruneDeadConnectionsTimer();
|
|
}
|
|
#ifdef DEBUG
|
|
count = ent->mActiveConns.Length();
|
|
if (count > 0) {
|
|
for (PRInt32 i=count-1; i>=0; --i) {
|
|
nsHttpConnection *conn = ent->mActiveConns[i];
|
|
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->mPendingQ.Length() == 0) {
|
|
LOG((" removing empty connection entry\n"));
|
|
delete ent;
|
|
return kHashEnumerateRemove;
|
|
}
|
|
|
|
// else, use this opportunity to compact our arrays...
|
|
ent->mIdleConns.Compact();
|
|
ent->mActiveConns.Compact();
|
|
ent->mPendingQ.Compact();
|
|
|
|
return kHashEnumerateNext;
|
|
}
|
|
|
|
PRIntn
|
|
nsHttpConnectionMgr::ShutdownPassCB(nsHashKey *key, void *data, void *closure)
|
|
{
|
|
nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
|
|
nsConnectionEntry *ent = (nsConnectionEntry *) data;
|
|
|
|
nsHttpTransaction *trans;
|
|
nsHttpConnection *conn;
|
|
|
|
// close all active connections
|
|
while (ent->mActiveConns.Length()) {
|
|
conn = ent->mActiveConns[0];
|
|
|
|
ent->mActiveConns.RemoveElementAt(0);
|
|
self->mNumActiveConns--;
|
|
|
|
conn->Close(NS_ERROR_ABORT);
|
|
NS_RELEASE(conn);
|
|
}
|
|
|
|
// close all idle connections
|
|
while (ent->mIdleConns.Length()) {
|
|
conn = ent->mIdleConns[0];
|
|
|
|
ent->mIdleConns.RemoveElementAt(0);
|
|
self->mNumIdleConns--;
|
|
|
|
conn->Close(NS_ERROR_ABORT);
|
|
NS_RELEASE(conn);
|
|
}
|
|
// If all idle connections are removed,
|
|
// we can stop pruning dead connections.
|
|
if (0 == self->mNumIdleConns)
|
|
self->StopPruneDeadConnectionsTimer();
|
|
|
|
// close all pending transactions
|
|
while (ent->mPendingQ.Length()) {
|
|
trans = ent->mPendingQ[0];
|
|
|
|
ent->mPendingQ.RemoveElementAt(0);
|
|
|
|
trans->Close(NS_ERROR_ABORT);
|
|
NS_RELEASE(trans);
|
|
}
|
|
|
|
delete ent;
|
|
return kHashEnumerateRemove;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
PRBool
|
|
nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
|
|
ent->mConnInfo->HashKey().get()));
|
|
|
|
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);
|
|
if (conn)
|
|
break;
|
|
}
|
|
if (conn) {
|
|
LOG((" dispatching pending transaction...\n"));
|
|
|
|
// remove pending transaction
|
|
ent->mPendingQ.RemoveElementAt(i);
|
|
|
|
nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
|
|
if (NS_SUCCEEDED(rv))
|
|
NS_RELEASE(trans);
|
|
else {
|
|
LOG((" DispatchTransaction failed [rv=%x]\n", rv));
|
|
// on failure, just put the transaction back
|
|
ent->mPendingQ.InsertElementAt(i, trans);
|
|
// might be something wrong with the connection... close it.
|
|
conn->Close(rv);
|
|
}
|
|
|
|
NS_RELEASE(conn);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// we're at the active connection limit if any one of the following conditions is true:
|
|
// (1) at max-connections
|
|
// (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
|
|
// (3) keep-alive disabled and at max-connections-per-server
|
|
PRBool
|
|
nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 caps)
|
|
{
|
|
nsHttpConnectionInfo *ci = ent->mConnInfo;
|
|
|
|
LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
|
|
ci->HashKey().get(), caps));
|
|
|
|
// 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;
|
|
|
|
totalCount = ent->mActiveConns.Length();
|
|
|
|
// 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++;
|
|
}
|
|
|
|
LOG((" total=%d, persist=%d\n", totalCount, persistCount));
|
|
|
|
PRUint16 maxConns;
|
|
PRUint16 maxPersistConns;
|
|
|
|
if (ci->UsingHttpProxy() && !ci->UsingSSL()) {
|
|
maxConns = mMaxConnsPerProxy;
|
|
maxPersistConns = mMaxPersistConnsPerProxy;
|
|
}
|
|
else {
|
|
maxConns = mMaxConnsPerHost;
|
|
maxPersistConns = mMaxPersistConnsPerHost;
|
|
}
|
|
|
|
// use >= just to be safe
|
|
return (totalCount >= maxConns) || ( (caps & NS_HTTP_ALLOW_KEEPALIVE) &&
|
|
(persistCount >= maxPersistConns) );
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::GetConnection(nsHttpConnectionInfo *ci,
|
|
PRUint8 caps,
|
|
nsHttpConnection **result)
|
|
{
|
|
NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
|
|
nsCStringKey key(ci->HashKey());
|
|
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
|
|
if (!ent) return;
|
|
GetConnection(ent, caps, result);
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent, PRUint8 caps,
|
|
nsHttpConnection **result)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::GetConnection [ci=%s caps=%x]\n",
|
|
ent->mConnInfo->HashKey().get(), PRUint32(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. 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);
|
|
}
|
|
else
|
|
LOG((" reusing connection [conn=%x]\n", conn));
|
|
ent->mIdleConns.RemoveElementAt(0);
|
|
mNumIdleConns--;
|
|
|
|
// If there are no idle connections left at all, we need to make
|
|
// sure that we are not pruning dead connections anymore.
|
|
if (0 == mNumIdleConns)
|
|
StopPruneDeadConnectionsTimer();
|
|
}
|
|
}
|
|
|
|
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(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"));
|
|
return;
|
|
}
|
|
|
|
conn = new nsHttpConnection();
|
|
NS_ADDREF(conn);
|
|
nsresult rv = conn->Init(ent->mConnInfo, mMaxRequestDelay);
|
|
if (NS_FAILED(rv)) {
|
|
NS_RELEASE(conn);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// hold an owning ref to this connection
|
|
ent->mActiveConns.AppendElement(conn);
|
|
mNumActiveConns++;
|
|
NS_ADDREF(conn);
|
|
|
|
*result = conn;
|
|
}
|
|
|
|
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));
|
|
|
|
nsConnectionHandle *handle = new nsConnectionHandle(conn);
|
|
if (!handle)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
NS_ADDREF(handle);
|
|
|
|
nsHttpPipeline *pipeline = nsnull;
|
|
if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
|
|
LOG((" looking to build pipeline...\n"));
|
|
if (BuildPipeline(ent, trans, &pipeline))
|
|
trans = pipeline;
|
|
}
|
|
|
|
// 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);
|
|
mNumActiveConns--;
|
|
// sever back references to connection, and do so without triggering
|
|
// a call to ReclaimConnection ;-)
|
|
trans->SetConnection(nsnull);
|
|
NS_RELEASE(handle->mConn);
|
|
// destroy the connection
|
|
NS_RELEASE(conn);
|
|
}
|
|
|
|
// if we were unable to activate the pipeline, then this will destroy
|
|
// the pipeline, which will cause each the transactions owned by the
|
|
// pipeline to be restarted.
|
|
NS_IF_RELEASE(pipeline);
|
|
|
|
NS_RELEASE(handle);
|
|
return rv;
|
|
}
|
|
|
|
PRBool
|
|
nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
|
|
nsAHttpTransaction *firstTrans,
|
|
nsHttpPipeline **result)
|
|
{
|
|
if (mMaxPipelinedRequests < 2)
|
|
return PR_FALSE;
|
|
|
|
nsHttpPipeline *pipeline = nsnull;
|
|
nsHttpTransaction *trans;
|
|
|
|
PRUint32 i = 0, numAdded = 0;
|
|
while (i < ent->mPendingQ.Length()) {
|
|
trans = ent->mPendingQ[i];
|
|
if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
|
|
if (numAdded == 0) {
|
|
pipeline = new nsHttpPipeline;
|
|
if (!pipeline)
|
|
return PR_FALSE;
|
|
pipeline->AddTransaction(firstTrans);
|
|
numAdded = 1;
|
|
}
|
|
pipeline->AddTransaction(trans);
|
|
|
|
// remove transaction from pending queue
|
|
ent->mPendingQ.RemoveElementAt(i);
|
|
NS_RELEASE(trans);
|
|
|
|
if (++numAdded == mMaxPipelinedRequests)
|
|
break;
|
|
}
|
|
else
|
|
++i; // skip to next pending transaction
|
|
}
|
|
|
|
if (numAdded == 0)
|
|
return PR_FALSE;
|
|
|
|
LOG((" pipelined %u transactions\n", numAdded));
|
|
NS_ADDREF(*result = pipeline);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
PRUint8 caps = trans->Caps();
|
|
nsHttpConnectionInfo *ci = trans->ConnectionInfo();
|
|
NS_ASSERTION(ci, "no connection info");
|
|
|
|
nsCStringKey key(ci->HashKey());
|
|
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
|
|
if (!ent) {
|
|
nsHttpConnectionInfo *clone = ci->Clone();
|
|
if (!clone)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
ent = new nsConnectionEntry(clone);
|
|
if (!ent)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
mCT.Put(&key, ent);
|
|
}
|
|
|
|
nsHttpConnection *conn;
|
|
|
|
// check if the transaction already has a sticky reference to a connection.
|
|
// if so, then we can just use it directly. XXX check if alive??
|
|
// XXX add a TakeConnection method or something to make this clearer!
|
|
nsConnectionHandle *handle = (nsConnectionHandle *) trans->Connection();
|
|
if (handle) {
|
|
NS_ASSERTION(caps & NS_HTTP_STICKY_CONNECTION, "unexpected caps");
|
|
NS_ASSERTION(handle->mConn, "no connection");
|
|
|
|
// steal reference from connection handle.
|
|
// XXX prevent SetConnection(nsnull) from calling ReclaimConnection
|
|
conn = handle->mConn;
|
|
handle->mConn = nsnull;
|
|
|
|
// destroy connection handle.
|
|
trans->SetConnection(nsnull);
|
|
}
|
|
else
|
|
GetConnection(ent, caps, &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);
|
|
rv = NS_OK;
|
|
}
|
|
else {
|
|
rv = DispatchTransaction(ent, trans, caps, conn);
|
|
NS_RELEASE(conn);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void
|
|
nsHttpConnectionMgr::OnMsgShutdown(PRInt32, void *)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
|
|
|
|
mCT.Reset(ShutdownPassCB, this);
|
|
|
|
// signal shutdown complete
|
|
nsAutoMonitor mon(mMonitor);
|
|
mon.Notify();
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::OnMsgNewTransaction(PRInt32 priority, void *param)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
|
|
|
|
nsHttpTransaction *trans = (nsHttpTransaction *) param;
|
|
trans->SetPriority(priority);
|
|
nsresult rv = ProcessNewTransaction(trans);
|
|
if (NS_FAILED(rv))
|
|
trans->Close(rv); // for whatever its worth
|
|
NS_RELEASE(trans);
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::OnMsgReschedTransaction(PRInt32 priority, void *param)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
|
|
|
|
nsHttpTransaction *trans = (nsHttpTransaction *) param;
|
|
trans->SetPriority(priority);
|
|
|
|
nsHttpConnectionInfo *ci = trans->ConnectionInfo();
|
|
nsCStringKey key(ci->HashKey());
|
|
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
|
|
if (ent) {
|
|
PRInt32 index = ent->mPendingQ.IndexOf(trans);
|
|
if (index >= 0) {
|
|
ent->mPendingQ.RemoveElementAt(index);
|
|
InsertTransactionSorted(ent->mPendingQ, trans);
|
|
}
|
|
}
|
|
|
|
NS_RELEASE(trans);
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::OnMsgCancelTransaction(PRInt32 reason, void *param)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
|
|
|
|
nsHttpTransaction *trans = (nsHttpTransaction *) param;
|
|
//
|
|
// if the transaction owns a connection and the transaction is not done,
|
|
// then ask the connection to close the transaction. otherwise, close the
|
|
// transaction directly (removing it from the pending queue first).
|
|
//
|
|
nsAHttpConnection *conn = trans->Connection();
|
|
if (conn && !trans->IsDone())
|
|
conn->CloseTransaction(trans, reason);
|
|
else {
|
|
nsHttpConnectionInfo *ci = trans->ConnectionInfo();
|
|
nsCStringKey key(ci->HashKey());
|
|
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
|
|
if (ent) {
|
|
PRInt32 index = ent->mPendingQ.IndexOf(trans);
|
|
if (index >= 0) {
|
|
ent->mPendingQ.RemoveElementAt(index);
|
|
nsHttpTransaction *temp = trans;
|
|
NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
|
|
}
|
|
}
|
|
trans->Close(reason);
|
|
}
|
|
NS_RELEASE(trans);
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::OnMsgProcessPendingQ(PRInt32, void *param)
|
|
{
|
|
nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param;
|
|
|
|
LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
|
|
|
|
// start by processing the queue identified by the given connection info.
|
|
nsCStringKey key(ci->HashKey());
|
|
nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
|
|
if (!(ent && ProcessPendingQForEntry(ent))) {
|
|
// if we reach here, it means that we couldn't dispatch a transaction
|
|
// for the specified connection info. walk the connection table...
|
|
mCT.Enumerate(ProcessOneTransactionCB, this);
|
|
}
|
|
|
|
NS_RELEASE(ci);
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::OnMsgPruneDeadConnections(PRInt32, void *)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
|
|
|
|
// Reset mTimeOfNextWakeUp so that we can find a new shortest value.
|
|
mTimeOfNextWakeUp = LL_MAXUINT;
|
|
if (mNumIdleConns > 0)
|
|
mCT.Enumerate(PruneDeadConnectionsCB, this);
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
|
|
{
|
|
LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
|
|
|
|
nsHttpConnection *conn = (nsHttpConnection *) param;
|
|
|
|
//
|
|
// 1) remove the connection from the active list
|
|
// 2) if keep-alive, add connection to idle list
|
|
// 3) post event to process the pending transaction queue
|
|
//
|
|
|
|
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) {
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
OnMsgProcessPendingQ(NS_OK, ci); // releases |ci|
|
|
NS_RELEASE(conn);
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::OnMsgUpdateParam(PRInt32, void *param)
|
|
{
|
|
PRUint16 name = (NS_PTR_TO_INT32(param) & 0xFFFF0000) >> 16;
|
|
PRUint16 value = NS_PTR_TO_INT32(param) & 0x0000FFFF;
|
|
|
|
switch (name) {
|
|
case MAX_CONNECTIONS:
|
|
mMaxConns = value;
|
|
break;
|
|
case MAX_CONNECTIONS_PER_HOST:
|
|
mMaxConnsPerHost = value;
|
|
break;
|
|
case MAX_CONNECTIONS_PER_PROXY:
|
|
mMaxConnsPerProxy = value;
|
|
break;
|
|
case MAX_PERSISTENT_CONNECTIONS_PER_HOST:
|
|
mMaxPersistConnsPerHost = value;
|
|
break;
|
|
case MAX_PERSISTENT_CONNECTIONS_PER_PROXY:
|
|
mMaxPersistConnsPerProxy = value;
|
|
break;
|
|
case MAX_REQUEST_DELAY:
|
|
mMaxRequestDelay = value;
|
|
break;
|
|
case MAX_PIPELINED_REQUESTS:
|
|
mMaxPipelinedRequests = value;
|
|
break;
|
|
default:
|
|
NS_NOTREACHED("unexpected parameter name");
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// nsHttpConnectionMgr::nsConnectionHandle
|
|
|
|
nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
|
|
{
|
|
if (mConn) {
|
|
gHttpHandler->ReclaimConnection(mConn);
|
|
NS_RELEASE(mConn);
|
|
}
|
|
}
|
|
|
|
NS_IMPL_THREADSAFE_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::nsConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
|
|
nsHttpRequestHead *req,
|
|
nsHttpResponseHead *resp,
|
|
PRBool *reset)
|
|
{
|
|
return mConn->OnHeadersAvailable(trans, req, resp, reset);
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::nsConnectionHandle::ResumeSend()
|
|
{
|
|
return mConn->ResumeSend();
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv()
|
|
{
|
|
return mConn->ResumeRecv();
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
|
|
{
|
|
mConn->CloseTransaction(trans, reason);
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::nsConnectionHandle::GetConnectionInfo(nsHttpConnectionInfo **result)
|
|
{
|
|
mConn->GetConnectionInfo(result);
|
|
}
|
|
|
|
void
|
|
nsHttpConnectionMgr::nsConnectionHandle::GetSecurityInfo(nsISupports **result)
|
|
{
|
|
mConn->GetSecurityInfo(result);
|
|
}
|
|
|
|
PRBool
|
|
nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
|
|
{
|
|
return mConn->IsPersistent();
|
|
}
|
|
|
|
PRBool
|
|
nsHttpConnectionMgr::nsConnectionHandle::IsReused()
|
|
{
|
|
return mConn->IsReused();
|
|
}
|
|
|
|
nsresult
|
|
nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufLen)
|
|
{
|
|
return mConn->PushBack(buf, bufLen);
|
|
}
|
|
|