Back to index

lightning-sunbird  0.9+nobinonly
nsHttpConnectionMgr.cpp
Go to the documentation of this file.
00001 /* vim:set ts=4 sw=4 sts=4 et cin: */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Darin Fisher <darin@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsHttpConnectionMgr.h"
00040 #include "nsHttpConnection.h"
00041 #include "nsHttpPipeline.h"
00042 #include "nsHttpHandler.h"
00043 #include "nsAutoLock.h"
00044 #include "nsNetCID.h"
00045 #include "nsCOMPtr.h"
00046 
00047 #include "nsIServiceManager.h"
00048 
00049 // defined by the socket transport service while active
00050 extern PRThread *gSocketThread;
00051 
00052 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
00053 
00054 //-----------------------------------------------------------------------------
00055 
00056 static void
00057 InsertTransactionSorted(nsVoidArray &pendingQ, nsHttpTransaction *trans)
00058 {
00059     // insert into queue with smallest valued number first.  search in reverse
00060     // order under the assumption that many of the existing transactions will
00061     // have the same priority (usually 0).
00062 
00063     for (PRInt32 i=pendingQ.Count()-1; i>=0; --i) {
00064         nsHttpTransaction *t = (nsHttpTransaction *) pendingQ[i];
00065         if (trans->Priority() >= t->Priority()) {
00066             pendingQ.InsertElementAt(trans, i+1);
00067             return;
00068         }
00069     }
00070     pendingQ.InsertElementAt(trans, 0);
00071 }
00072 
00073 //-----------------------------------------------------------------------------
00074 
00075 nsHttpConnectionMgr::nsHttpConnectionMgr()
00076     : mRef(0)
00077     , mMonitor(nsAutoMonitor::NewMonitor("nsHttpConnectionMgr"))
00078     , mMaxConns(0)
00079     , mMaxConnsPerHost(0)
00080     , mMaxConnsPerProxy(0)
00081     , mMaxPersistConnsPerHost(0)
00082     , mMaxPersistConnsPerProxy(0)
00083     , mNumActiveConns(0)
00084     , mNumIdleConns(0)
00085 {
00086     LOG(("Creating nsHttpConnectionMgr @%x\n", this));
00087 }
00088 
00089 nsHttpConnectionMgr::~nsHttpConnectionMgr()
00090 {
00091     LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
00092  
00093     if (mMonitor)
00094         nsAutoMonitor::DestroyMonitor(mMonitor);
00095 }
00096 
00097 nsresult
00098 nsHttpConnectionMgr::Init(PRUint16 maxConns,
00099                           PRUint16 maxConnsPerHost,
00100                           PRUint16 maxConnsPerProxy,
00101                           PRUint16 maxPersistConnsPerHost,
00102                           PRUint16 maxPersistConnsPerProxy,
00103                           PRUint16 maxRequestDelay,
00104                           PRUint16 maxPipelinedRequests)
00105 {
00106     LOG(("nsHttpConnectionMgr::Init\n"));
00107 
00108     nsresult rv;
00109     nsCOMPtr<nsIEventTarget> sts = do_GetService(kSocketTransportServiceCID, &rv);
00110     if NS_FAILED(rv) return rv;
00111 
00112     nsAutoMonitor mon(mMonitor);
00113 
00114     // do nothing if already initialized
00115     if (mSTEventTarget)
00116         return NS_OK;
00117 
00118     // no need to do any special synchronization here since there cannot be
00119     // any activity on the socket thread (because Shutdown is synchronous).
00120     mMaxConns = maxConns;
00121     mMaxConnsPerHost = maxConnsPerHost;
00122     mMaxConnsPerProxy = maxConnsPerProxy;
00123     mMaxPersistConnsPerHost = maxPersistConnsPerHost;
00124     mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
00125     mMaxRequestDelay = maxRequestDelay;
00126     mMaxPipelinedRequests = maxPipelinedRequests;
00127 
00128     mSTEventTarget = sts;
00129     return rv;
00130 }
00131 
00132 nsresult
00133 nsHttpConnectionMgr::Shutdown()
00134 {
00135     LOG(("nsHttpConnectionMgr::Shutdown\n"));
00136 
00137     nsAutoMonitor mon(mMonitor);
00138 
00139     // do nothing if already shutdown
00140     if (!mSTEventTarget)
00141         return NS_OK;
00142 
00143     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgShutdown);
00144 
00145     // release our reference to the STS to prevent further events
00146     // from being posted.  this is how we indicate that we are
00147     // shutting down.
00148     mSTEventTarget = 0;
00149 
00150     if (NS_FAILED(rv)) {
00151         NS_WARNING("unable to post SHUTDOWN message\n");
00152         return rv;
00153     }
00154 
00155     // wait for shutdown event to complete
00156     mon.Wait();
00157     return NS_OK;
00158 }
00159 
00160 nsresult
00161 nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, PRInt32 iparam, void *vparam)
00162 {
00163     nsAutoMonitor mon(mMonitor);
00164 
00165     nsresult rv;
00166     if (!mSTEventTarget) {
00167         NS_WARNING("cannot post event if not initialized");
00168         rv = NS_ERROR_NOT_INITIALIZED;
00169     }
00170     else {
00171         PLEvent *event = new nsConnEvent(this, handler, iparam, vparam);
00172         if (!event)
00173             rv = NS_ERROR_OUT_OF_MEMORY;
00174         else {
00175             rv = mSTEventTarget->PostEvent(event);
00176             if (NS_FAILED(rv))
00177                 PL_DestroyEvent(event);
00178         }
00179     }
00180     return rv;
00181 }
00182 
00183 //-----------------------------------------------------------------------------
00184 
00185 nsresult
00186 nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, PRInt32 priority)
00187 {
00188     LOG(("nsHttpConnectionMgr::AddTransaction [trans=%x %d]\n", trans, priority));
00189 
00190     NS_ADDREF(trans);
00191     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
00192     if (NS_FAILED(rv))
00193         NS_RELEASE(trans);
00194     return rv;
00195 }
00196 
00197 nsresult
00198 nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, PRInt32 priority)
00199 {
00200     LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%x %d]\n", trans, priority));
00201 
00202     NS_ADDREF(trans);
00203     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority, trans);
00204     if (NS_FAILED(rv))
00205         NS_RELEASE(trans);
00206     return rv;
00207 }
00208 
00209 nsresult
00210 nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
00211 {
00212     LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%x reason=%x]\n", trans, reason));
00213 
00214     NS_ADDREF(trans);
00215     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction, reason, trans);
00216     if (NS_FAILED(rv))
00217         NS_RELEASE(trans);
00218     return rv;
00219 }
00220 
00221 nsresult
00222 nsHttpConnectionMgr::PruneDeadConnections()
00223 {
00224     return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
00225 }
00226 
00227 nsresult
00228 nsHttpConnectionMgr::GetSocketThreadEventTarget(nsIEventTarget **target)
00229 {
00230     nsAutoMonitor mon(mMonitor);
00231     NS_IF_ADDREF(*target = mSTEventTarget);
00232     return NS_OK;
00233 }
00234 
00235 void
00236 nsHttpConnectionMgr::AddTransactionToPipeline(nsHttpPipeline *pipeline)
00237 {
00238     LOG(("nsHttpConnectionMgr::AddTransactionToPipeline [pipeline=%x]\n", pipeline));
00239 
00240     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
00241 
00242     nsHttpConnectionInfo *ci = nsnull;
00243     pipeline->GetConnectionInfo(&ci);
00244     if (ci) {
00245         nsCStringKey key(ci->HashKey());
00246         nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
00247         if (ent) {
00248             // search for another request to pipeline...
00249             PRInt32 i, count = ent->mPendingQ.Count();
00250             for (i=0; i<count; ++i) {
00251                 nsHttpTransaction *trans = (nsHttpTransaction *) ent->mPendingQ[i];
00252                 if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
00253                     pipeline->AddTransaction(trans);
00254 
00255                     // remove transaction from pending queue
00256                     ent->mPendingQ.RemoveElementAt(i);
00257                     NS_RELEASE(trans);
00258                     break;
00259                 }
00260             }
00261         }
00262     }
00263 }
00264 
00265 nsresult
00266 nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
00267 {
00268     LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
00269 
00270     NS_ADDREF(conn);
00271     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
00272     if (NS_FAILED(rv))
00273         NS_RELEASE(conn);
00274     return rv;
00275 }
00276 
00277 nsresult
00278 nsHttpConnectionMgr::UpdateParam(nsParamName name, PRUint16 value)
00279 {
00280     PRUint32 param = (PRUint32(name) << 16) | PRUint32(value);
00281     return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam, 0, (void *) param);
00282 }
00283 
00284 nsresult
00285 nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
00286 {
00287     LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
00288 
00289     NS_ADDREF(ci);
00290     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
00291     if (NS_FAILED(rv))
00292         NS_RELEASE(ci);
00293     return rv;
00294 }
00295 
00296 //-----------------------------------------------------------------------------
00297 // enumeration callbacks
00298 
00299 PRIntn PR_CALLBACK
00300 nsHttpConnectionMgr::ProcessOneTransactionCB(nsHashKey *key, void *data, void *closure)
00301 {
00302     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
00303     nsConnectionEntry *ent = (nsConnectionEntry *) data;
00304 
00305     if (self->ProcessPendingQForEntry(ent))
00306         return kHashEnumerateStop;
00307 
00308     return kHashEnumerateNext;
00309 }
00310 
00311 PRIntn PR_CALLBACK
00312 nsHttpConnectionMgr::PurgeOneIdleConnectionCB(nsHashKey *key, void *data, void *closure)
00313 {
00314     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
00315     nsConnectionEntry *ent = (nsConnectionEntry *) data;
00316 
00317     if (ent->mIdleConns.Count() > 0) {
00318         nsHttpConnection *conn = (nsHttpConnection *) ent->mIdleConns[0];
00319         ent->mIdleConns.RemoveElementAt(0);
00320         conn->Close(NS_ERROR_ABORT);
00321         NS_RELEASE(conn);
00322         self->mNumIdleConns--;
00323         return kHashEnumerateStop;
00324     }
00325 
00326     return kHashEnumerateNext;
00327 }
00328 
00329 PRIntn PR_CALLBACK
00330 nsHttpConnectionMgr::PruneDeadConnectionsCB(nsHashKey *key, void *data, void *closure)
00331 {
00332     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
00333     nsConnectionEntry *ent = (nsConnectionEntry *) data;
00334 
00335     LOG(("  pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
00336 
00337     PRInt32 count = ent->mIdleConns.Count();
00338     if (count > 0) {
00339         for (PRInt32 i=count-1; i>=0; --i) {
00340             nsHttpConnection *conn = (nsHttpConnection *) ent->mIdleConns[i];
00341             if (!conn->CanReuse()) {
00342                 ent->mIdleConns.RemoveElementAt(i);
00343                 conn->Close(NS_ERROR_ABORT);
00344                 NS_RELEASE(conn);
00345                 self->mNumIdleConns--;
00346             }
00347         }
00348     }
00349 
00350 #ifdef DEBUG
00351     count = ent->mActiveConns.Count();
00352     if (count > 0) {
00353         for (PRInt32 i=count-1; i>=0; --i) {
00354             nsHttpConnection *conn = (nsHttpConnection *) ent->mActiveConns[i];
00355             LOG(("    active conn [%x] with trans [%x]\n", conn, conn->Transaction()));
00356         }
00357     }
00358 #endif
00359 
00360     // if this entry is empty, then we can remove it.
00361     if (ent->mIdleConns.Count()   == 0 &&
00362         ent->mActiveConns.Count() == 0 &&
00363         ent->mPendingQ.Count()    == 0) {
00364         LOG(("    removing empty connection entry\n"));
00365         delete ent;
00366         return kHashEnumerateRemove;
00367     }
00368 
00369     // else, use this opportunity to compact our arrays...
00370     ent->mIdleConns.Compact();
00371     ent->mActiveConns.Compact();
00372     ent->mPendingQ.Compact();
00373 
00374     return kHashEnumerateNext;
00375 }
00376 
00377 PRIntn PR_CALLBACK
00378 nsHttpConnectionMgr::ShutdownPassCB(nsHashKey *key, void *data, void *closure)
00379 {
00380     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
00381     nsConnectionEntry *ent = (nsConnectionEntry *) data;
00382 
00383     nsHttpTransaction *trans;
00384     nsHttpConnection *conn;
00385 
00386     // close all active connections
00387     while (ent->mActiveConns.Count()) {
00388         conn = (nsHttpConnection *) ent->mActiveConns[0];
00389 
00390         ent->mActiveConns.RemoveElementAt(0);
00391         self->mNumActiveConns--;
00392 
00393         conn->Close(NS_ERROR_ABORT);
00394         NS_RELEASE(conn);
00395     }
00396 
00397     // close all idle connections
00398     while (ent->mIdleConns.Count()) {
00399         conn = (nsHttpConnection *) ent->mIdleConns[0];
00400 
00401         ent->mIdleConns.RemoveElementAt(0);
00402         self->mNumIdleConns--;
00403 
00404         conn->Close(NS_ERROR_ABORT);
00405         NS_RELEASE(conn);
00406     }
00407 
00408     // close all pending transactions
00409     while (ent->mPendingQ.Count()) {
00410         trans = (nsHttpTransaction *) ent->mPendingQ[0];
00411 
00412         ent->mPendingQ.RemoveElementAt(0);
00413 
00414         trans->Close(NS_ERROR_ABORT);
00415         NS_RELEASE(trans);
00416     }
00417 
00418     delete ent;
00419     return kHashEnumerateRemove;
00420 }
00421 
00422 //-----------------------------------------------------------------------------
00423 
00424 PRBool
00425 nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
00426 {
00427     LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
00428         ent->mConnInfo->HashKey().get()));
00429 
00430     PRInt32 i, count = ent->mPendingQ.Count();
00431     if (count > 0) {
00432         LOG(("  pending-count=%u\n", count));
00433         nsHttpTransaction *trans = nsnull;
00434         nsHttpConnection *conn = nsnull;
00435         for (i=0; i<count; ++i) {
00436             trans = (nsHttpTransaction *) ent->mPendingQ[i];
00437             GetConnection(ent, trans->Caps(), &conn);
00438             if (conn)
00439                 break;
00440         }
00441         if (conn) {
00442             LOG(("  dispatching pending transaction...\n"));
00443 
00444             // remove pending transaction
00445             ent->mPendingQ.RemoveElementAt(i);
00446 
00447             nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
00448             if (NS_SUCCEEDED(rv))
00449                 NS_RELEASE(trans);
00450             else {
00451                 LOG(("  DispatchTransaction failed [rv=%x]\n", rv));
00452                 // on failure, just put the transaction back
00453                 ent->mPendingQ.InsertElementAt(trans, i);
00454                 // might be something wrong with the connection... close it.
00455                 conn->Close(rv);
00456             }
00457 
00458             NS_RELEASE(conn);
00459             return PR_TRUE;
00460         }
00461     }
00462     return PR_FALSE;
00463 }
00464 
00465 // we're at the active connection limit if any one of the following conditions is true:
00466 //  (1) at max-connections
00467 //  (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
00468 //  (3) keep-alive disabled and at max-connections-per-server
00469 PRBool
00470 nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 caps)
00471 {
00472     nsHttpConnectionInfo *ci = ent->mConnInfo;
00473 
00474     LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
00475         ci->HashKey().get(), caps));
00476 
00477     // use >= just to be safe
00478     if (mNumActiveConns >= mMaxConns) {
00479         LOG(("  num active conns == max conns\n"));
00480         return PR_TRUE;
00481     }
00482 
00483     nsHttpConnection *conn;
00484     PRInt32 i, totalCount, persistCount = 0;
00485     
00486     totalCount = ent->mActiveConns.Count();
00487 
00488     // count the number of persistent connections
00489     for (i=0; i<totalCount; ++i) {
00490         conn = (nsHttpConnection *) ent->mActiveConns[i];
00491         if (conn->IsKeepAlive()) // XXX make sure this is thread-safe
00492             persistCount++;
00493     }
00494 
00495     LOG(("   total=%d, persist=%d\n", totalCount, persistCount));
00496 
00497     PRUint16 maxConns;
00498     PRUint16 maxPersistConns;
00499 
00500     if (ci->UsingHttpProxy() && !ci->UsingSSL()) {
00501         maxConns = mMaxConnsPerProxy;
00502         maxPersistConns = mMaxPersistConnsPerProxy;
00503     }
00504     else {
00505         maxConns = mMaxConnsPerHost;
00506         maxPersistConns = mMaxPersistConnsPerHost;
00507     }
00508 
00509     // use >= just to be safe
00510     return (totalCount >= maxConns) || ( (caps & NS_HTTP_ALLOW_KEEPALIVE) &&
00511                                          (persistCount >= maxPersistConns) );
00512 }
00513 
00514 void
00515 nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent, PRUint8 caps,
00516                                    nsHttpConnection **result)
00517 {
00518     LOG(("nsHttpConnectionMgr::GetConnection [ci=%s caps=%x]\n",
00519         ent->mConnInfo->HashKey().get(), PRUint32(caps)));
00520 
00521     *result = nsnull;
00522 
00523     if (AtActiveConnectionLimit(ent, caps)) {
00524         LOG(("  at active connection limit!\n"));
00525         return;
00526     }
00527 
00528     nsHttpConnection *conn = nsnull;
00529 
00530     if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
00531         // search the idle connection list
00532         while (!conn && (ent->mIdleConns.Count() > 0)) {
00533             conn = (nsHttpConnection *) ent->mIdleConns[0];
00534             // we check if the connection can be reused before even checking if
00535             // it is a "matching" connection.
00536             if (!conn->CanReuse()) {
00537                 LOG(("   dropping stale connection: [conn=%x]\n", conn));
00538                 conn->Close(NS_ERROR_ABORT);
00539                 NS_RELEASE(conn);
00540             }
00541             else
00542                 LOG(("   reusing connection [conn=%x]\n", conn));
00543             ent->mIdleConns.RemoveElementAt(0);
00544             mNumIdleConns--;
00545         }
00546     }
00547 
00548     if (!conn) {
00549         conn = new nsHttpConnection();
00550         if (!conn)
00551             return;
00552         NS_ADDREF(conn);
00553 
00554         nsresult rv = conn->Init(ent->mConnInfo, mMaxRequestDelay);
00555         if (NS_FAILED(rv)) {
00556             NS_RELEASE(conn);
00557             return;
00558         }
00559         
00560         // We created a new connection that will become active, purge the
00561         // oldest idle connection if we've reached the upper limit.
00562         if (mNumIdleConns + mNumActiveConns + 1 > mMaxConns)
00563             mCT.Enumerate(PurgeOneIdleConnectionCB, this);
00564 
00565         // XXX this just purges a random idle connection.  we should instead
00566         // enumerate the entire hash table to find the eldest idle connection.
00567     }
00568 
00569     *result = conn;
00570 }
00571 
00572 nsresult
00573 nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
00574                                          nsAHttpTransaction *trans,
00575                                          PRUint8 caps,
00576                                          nsHttpConnection *conn)
00577 {
00578     LOG(("nsHttpConnectionMgr::DispatchTransaction [ci=%s trans=%x caps=%x conn=%x]\n",
00579         ent->mConnInfo->HashKey().get(), trans, caps, conn));
00580 
00581     nsConnectionHandle *handle = new nsConnectionHandle(conn);
00582     if (!handle)
00583         return NS_ERROR_OUT_OF_MEMORY;
00584     NS_ADDREF(handle);
00585 
00586     nsHttpPipeline *pipeline = nsnull;
00587     if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
00588         LOG(("  looking to build pipeline...\n"));
00589         if (BuildPipeline(ent, trans, &pipeline))
00590             trans = pipeline;
00591     }
00592 
00593     // hold an owning ref to this connection
00594     ent->mActiveConns.AppendElement(conn);
00595     mNumActiveConns++;
00596     NS_ADDREF(conn);
00597 
00598     // give the transaction the indirect reference to the connection.
00599     trans->SetConnection(handle);
00600 
00601     nsresult rv = conn->Activate(trans, caps);
00602 
00603     if (NS_FAILED(rv)) {
00604         LOG(("  conn->Activate failed [rv=%x]\n", rv));
00605         ent->mActiveConns.RemoveElement(conn);
00606         mNumActiveConns--;
00607         // sever back references to connection, and do so without triggering
00608         // a call to ReclaimConnection ;-)
00609         trans->SetConnection(nsnull);
00610         NS_RELEASE(handle->mConn);
00611         // destroy the connection
00612         NS_RELEASE(conn);
00613     }
00614 
00615     // if we were unable to activate the pipeline, then this will destroy
00616     // the pipeline, which will cause each the transactions owned by the 
00617     // pipeline to be restarted.
00618     NS_IF_RELEASE(pipeline);
00619 
00620     NS_RELEASE(handle);
00621     return rv;
00622 }
00623 
00624 PRBool
00625 nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
00626                                    nsAHttpTransaction *firstTrans,
00627                                    nsHttpPipeline **result)
00628 {
00629     if (mMaxPipelinedRequests < 2)
00630         return PR_FALSE;
00631 
00632     nsHttpPipeline *pipeline = nsnull;
00633     nsHttpTransaction *trans;
00634 
00635     PRInt32 i = 0, numAdded = 0;
00636     while (i < ent->mPendingQ.Count()) {
00637         trans = (nsHttpTransaction *) ent->mPendingQ[i];
00638         if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
00639             if (numAdded == 0) {
00640                 pipeline = new nsHttpPipeline;
00641                 if (!pipeline)
00642                     return PR_FALSE;
00643                 pipeline->AddTransaction(firstTrans);
00644                 numAdded = 1;
00645             }
00646             pipeline->AddTransaction(trans);
00647 
00648             // remove transaction from pending queue
00649             ent->mPendingQ.RemoveElementAt(i);
00650             NS_RELEASE(trans);
00651 
00652             if (++numAdded == mMaxPipelinedRequests)
00653                 break;
00654         }
00655         else
00656             ++i; // skip to next pending transaction
00657     }
00658 
00659     if (numAdded == 0)
00660         return PR_FALSE;
00661 
00662     LOG(("  pipelined %u transactions\n", numAdded));
00663     NS_ADDREF(*result = pipeline);
00664     return PR_TRUE;
00665 }
00666 
00667 nsresult
00668 nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
00669 {
00670     // since "adds" and "cancels" are processed asynchronously and because
00671     // various events might trigger an "add" directly on the socket thread,
00672     // we must take care to avoid dispatching a transaction that has already
00673     // been canceled (see bug 190001).
00674     if (NS_FAILED(trans->Status())) {
00675         LOG(("  transaction was canceled... dropping event!\n"));
00676         return NS_OK;
00677     }
00678 
00679     PRUint8 caps = trans->Caps();
00680     nsHttpConnectionInfo *ci = trans->ConnectionInfo();
00681     NS_ASSERTION(ci, "no connection info");
00682 
00683     nsCStringKey key(ci->HashKey());
00684     nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
00685     if (!ent) {
00686         ent = new nsConnectionEntry(ci);
00687         if (!ent)
00688             return NS_ERROR_OUT_OF_MEMORY;
00689         mCT.Put(&key, ent);
00690     }
00691 
00692     nsHttpConnection *conn;
00693 
00694     // check if the transaction already has a sticky reference to a connection.
00695     // if so, then we can just use it directly.  XXX check if alive??
00696     // XXX add a TakeConnection method or something to make this clearer!
00697     nsConnectionHandle *handle = (nsConnectionHandle *) trans->Connection();
00698     if (handle) {
00699         NS_ASSERTION(caps & NS_HTTP_STICKY_CONNECTION, "unexpected caps");
00700         NS_ASSERTION(handle->mConn, "no connection");
00701 
00702         // steal reference from connection handle.
00703         // XXX prevent SetConnection(nsnull) from calling ReclaimConnection
00704         conn = handle->mConn;
00705         handle->mConn = nsnull;
00706 
00707         // destroy connection handle.
00708         trans->SetConnection(nsnull);
00709 
00710         // remove sticky connection from active connection list; we'll add it
00711         // right back in DispatchTransaction.
00712         if (ent->mActiveConns.RemoveElement(conn))
00713             mNumActiveConns--;
00714         else {
00715             NS_ERROR("sticky connection not found in active list");
00716             return NS_ERROR_UNEXPECTED;
00717         }
00718     }
00719     else
00720         GetConnection(ent, caps, &conn);
00721 
00722     nsresult rv;
00723     if (!conn) {
00724         LOG(("  adding transaction to pending queue [trans=%x pending-count=%u]\n",
00725             trans, ent->mPendingQ.Count()+1));
00726         // put this transaction on the pending queue...
00727         InsertTransactionSorted(ent->mPendingQ, trans);
00728         NS_ADDREF(trans);
00729         rv = NS_OK;
00730     }
00731     else {
00732         rv = DispatchTransaction(ent, trans, caps, conn);
00733         NS_RELEASE(conn);
00734     }
00735 
00736     return rv;
00737 }
00738 
00739 //-----------------------------------------------------------------------------
00740 
00741 void
00742 nsHttpConnectionMgr::OnMsgShutdown(PRInt32, void *)
00743 {
00744     LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
00745 
00746     mCT.Reset(ShutdownPassCB, this);
00747 
00748     // signal shutdown complete
00749     nsAutoMonitor mon(mMonitor);
00750     mon.Notify();
00751 }
00752 
00753 void
00754 nsHttpConnectionMgr::OnMsgNewTransaction(PRInt32 priority, void *param)
00755 {
00756     LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
00757 
00758     nsHttpTransaction *trans = (nsHttpTransaction *) param;
00759     trans->SetPriority(priority);
00760     nsresult rv = ProcessNewTransaction(trans);
00761     if (NS_FAILED(rv))
00762         trans->Close(rv); // for whatever its worth
00763     NS_RELEASE(trans);
00764 }
00765 
00766 void
00767 nsHttpConnectionMgr::OnMsgReschedTransaction(PRInt32 priority, void *param)
00768 {
00769     LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
00770 
00771     nsHttpTransaction *trans = (nsHttpTransaction *) param;
00772     trans->SetPriority(priority);
00773 
00774     nsHttpConnectionInfo *ci = trans->ConnectionInfo();
00775     nsCStringKey key(ci->HashKey());
00776     nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
00777     if (ent) {
00778         PRInt32 index = ent->mPendingQ.IndexOf(trans);
00779         if (index >= 0) {
00780             ent->mPendingQ.RemoveElementAt(index);
00781             InsertTransactionSorted(ent->mPendingQ, trans);
00782         }
00783     }
00784 
00785     NS_RELEASE(trans);
00786 }
00787 
00788 void
00789 nsHttpConnectionMgr::OnMsgCancelTransaction(PRInt32 reason, void *param)
00790 {
00791     LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
00792 
00793     nsHttpTransaction *trans = (nsHttpTransaction *) param;
00794     //
00795     // if the transaction owns a connection and the transaction is not done,
00796     // then ask the connection to close the transaction.  otherwise, close the
00797     // transaction directly (removing it from the pending queue first).
00798     //
00799     nsAHttpConnection *conn = trans->Connection();
00800     if (conn && !trans->IsDone())
00801         conn->CloseTransaction(trans, reason);
00802     else {
00803         nsHttpConnectionInfo *ci = trans->ConnectionInfo();
00804         nsCStringKey key(ci->HashKey());
00805         nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
00806         if (ent) {
00807             PRInt32 index = ent->mPendingQ.IndexOf(trans);
00808             if (index >= 0) {
00809                 ent->mPendingQ.RemoveElementAt(index);
00810                 nsHttpTransaction *temp = trans;
00811                 NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
00812             }
00813         }
00814         trans->Close(reason);
00815     }
00816     NS_RELEASE(trans);
00817 }
00818 
00819 void
00820 nsHttpConnectionMgr::OnMsgProcessPendingQ(PRInt32, void *param)
00821 {
00822     nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param;
00823 
00824     LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
00825 
00826     // start by processing the queue identified by the given connection info.
00827     nsCStringKey key(ci->HashKey());
00828     nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
00829     if (!(ent && ProcessPendingQForEntry(ent))) {
00830         // if we reach here, it means that we couldn't dispatch a transaction
00831         // for the specified connection info.  walk the connection table...
00832         mCT.Enumerate(ProcessOneTransactionCB, this);
00833     }
00834 
00835     NS_RELEASE(ci);
00836 }
00837 
00838 void
00839 nsHttpConnectionMgr::OnMsgPruneDeadConnections(PRInt32, void *)
00840 {
00841     LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
00842 
00843     if (mNumIdleConns > 0) 
00844         mCT.Enumerate(PruneDeadConnectionsCB, this);
00845 }
00846 
00847 void
00848 nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
00849 {
00850     LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
00851 
00852     nsHttpConnection *conn = (nsHttpConnection *) param;
00853 
00854     // 
00855     // 1) remove the connection from the active list
00856     // 2) if keep-alive, add connection to idle list
00857     // 3) post event to process the pending transaction queue
00858     //
00859 
00860     nsHttpConnectionInfo *ci = conn->ConnectionInfo();
00861     NS_ADDREF(ci);
00862 
00863     nsCStringKey key(ci->HashKey());
00864     nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
00865 
00866     NS_ASSERTION(ent, "no connection entry");
00867     if (ent) {
00868         ent->mActiveConns.RemoveElement(conn);
00869         mNumActiveConns--;
00870         if (conn->CanReuse()) {
00871             LOG(("  adding connection to idle list\n"));
00872             // hold onto this connection in the idle list.  we push it to
00873             // the end of the list so as to ensure that we'll visit older
00874             // connections first before getting to this one.
00875             ent->mIdleConns.AppendElement(conn);
00876             mNumIdleConns++;
00877         }
00878         else {
00879             LOG(("  connection cannot be reused; closing connection\n"));
00880             // make sure the connection is closed and release our reference.
00881             conn->Close(NS_ERROR_ABORT);
00882             nsHttpConnection *temp = conn;
00883             NS_RELEASE(temp);
00884         }
00885     }
00886  
00887     OnMsgProcessPendingQ(NS_OK, ci); // releases |ci|
00888     NS_RELEASE(conn);
00889 }
00890 
00891 void
00892 nsHttpConnectionMgr::OnMsgUpdateParam(PRInt32, void *param)
00893 {
00894     PRUint16 name  = (NS_PTR_TO_INT32(param) & 0xFFFF0000) >> 16;
00895     PRUint16 value =  NS_PTR_TO_INT32(param) & 0x0000FFFF;
00896 
00897     switch (name) {
00898     case MAX_CONNECTIONS:
00899         mMaxConns = value;
00900         break;
00901     case MAX_CONNECTIONS_PER_HOST:
00902         mMaxConnsPerHost = value;
00903         break;
00904     case MAX_CONNECTIONS_PER_PROXY:
00905         mMaxConnsPerProxy = value;
00906         break;
00907     case MAX_PERSISTENT_CONNECTIONS_PER_HOST:
00908         mMaxPersistConnsPerHost = value;
00909         break;
00910     case MAX_PERSISTENT_CONNECTIONS_PER_PROXY:
00911         mMaxPersistConnsPerProxy = value;
00912         break;
00913     case MAX_REQUEST_DELAY:
00914         mMaxRequestDelay = value;
00915         break;
00916     case MAX_PIPELINED_REQUESTS:
00917         mMaxPipelinedRequests = value;
00918         break;
00919     default:
00920         NS_NOTREACHED("unexpected parameter name");
00921     }
00922 }
00923 
00924 //-----------------------------------------------------------------------------
00925 // nsHttpConnectionMgr::nsConnectionHandle
00926 
00927 nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
00928 {
00929     if (mConn) {
00930         gHttpHandler->ReclaimConnection(mConn);
00931         NS_RELEASE(mConn);
00932     }
00933 }
00934 
00935 NS_IMPL_THREADSAFE_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
00936 
00937 nsresult
00938 nsHttpConnectionMgr::nsConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
00939                                                             nsHttpRequestHead *req,
00940                                                             nsHttpResponseHead *resp,
00941                                                             PRBool *reset)
00942 {
00943     return mConn->OnHeadersAvailable(trans, req, resp, reset);
00944 }
00945 
00946 nsresult
00947 nsHttpConnectionMgr::nsConnectionHandle::ResumeSend()
00948 {
00949     return mConn->ResumeSend();
00950 }
00951 
00952 nsresult
00953 nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv()
00954 {
00955     return mConn->ResumeRecv();
00956 }
00957 
00958 void
00959 nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
00960 {
00961     mConn->CloseTransaction(trans, reason);
00962 }
00963 
00964 void
00965 nsHttpConnectionMgr::nsConnectionHandle::GetConnectionInfo(nsHttpConnectionInfo **result)
00966 {
00967     mConn->GetConnectionInfo(result);
00968 }
00969 
00970 void
00971 nsHttpConnectionMgr::nsConnectionHandle::GetSecurityInfo(nsISupports **result)
00972 {
00973     mConn->GetSecurityInfo(result);
00974 }
00975 
00976 PRBool
00977 nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
00978 {
00979     return mConn->IsPersistent();
00980 }
00981 
00982 PRBool
00983 nsHttpConnectionMgr::nsConnectionHandle::IsReused()
00984 {
00985     return mConn->IsReused();
00986 }
00987 
00988 nsresult
00989 nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufLen)
00990 {
00991     return mConn->PushBack(buf, bufLen);
00992 }