Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / protocol / http / src / nsHttpConnectionMgr.cpp
blob417379b160c962aa6f04647603989d5469cc2062
1 /* vim:set ts=4 sw=4 sts=4 et cin: */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 2002
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Darin Fisher <darin@netscape.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsHttpConnectionMgr.h"
40 #include "nsHttpConnection.h"
41 #include "nsHttpPipeline.h"
42 #include "nsHttpHandler.h"
43 #include "nsAutoLock.h"
44 #include "nsNetCID.h"
45 #include "nsCOMPtr.h"
47 #include "nsIServiceManager.h"
49 // defined by the socket transport service while active
50 extern PRThread *gSocketThread;
52 static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
54 //-----------------------------------------------------------------------------
56 static void
57 InsertTransactionSorted(nsVoidArray &pendingQ, nsHttpTransaction *trans)
59 // insert into queue with smallest valued number first. search in reverse
60 // order under the assumption that many of the existing transactions will
61 // have the same priority (usually 0).
63 for (PRInt32 i=pendingQ.Count()-1; i>=0; --i) {
64 nsHttpTransaction *t = (nsHttpTransaction *) pendingQ[i];
65 if (trans->Priority() >= t->Priority()) {
66 pendingQ.InsertElementAt(trans, i+1);
67 return;
70 pendingQ.InsertElementAt(trans, 0);
73 //-----------------------------------------------------------------------------
75 nsHttpConnectionMgr::nsHttpConnectionMgr()
76 : mRef(0)
77 , mMonitor(nsAutoMonitor::NewMonitor("nsHttpConnectionMgr"))
78 , mMaxConns(0)
79 , mMaxConnsPerHost(0)
80 , mMaxConnsPerProxy(0)
81 , mMaxPersistConnsPerHost(0)
82 , mMaxPersistConnsPerProxy(0)
83 , mNumActiveConns(0)
84 , mNumIdleConns(0)
86 LOG(("Creating nsHttpConnectionMgr @%x\n", this));
89 nsHttpConnectionMgr::~nsHttpConnectionMgr()
91 LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
93 if (mMonitor)
94 nsAutoMonitor::DestroyMonitor(mMonitor);
97 nsresult
98 nsHttpConnectionMgr::Init(PRUint16 maxConns,
99 PRUint16 maxConnsPerHost,
100 PRUint16 maxConnsPerProxy,
101 PRUint16 maxPersistConnsPerHost,
102 PRUint16 maxPersistConnsPerProxy,
103 PRUint16 maxRequestDelay,
104 PRUint16 maxPipelinedRequests)
106 LOG(("nsHttpConnectionMgr::Init\n"));
108 nsresult rv;
109 nsCOMPtr<nsIEventTarget> sts = do_GetService(kSocketTransportServiceCID, &rv);
110 if (NS_FAILED(rv)) return rv;
112 nsAutoMonitor mon(mMonitor);
114 // do nothing if already initialized
115 if (mSocketThreadTarget)
116 return NS_OK;
118 // no need to do any special synchronization here since there cannot be
119 // any activity on the socket thread (because Shutdown is synchronous).
120 mMaxConns = maxConns;
121 mMaxConnsPerHost = maxConnsPerHost;
122 mMaxConnsPerProxy = maxConnsPerProxy;
123 mMaxPersistConnsPerHost = maxPersistConnsPerHost;
124 mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
125 mMaxRequestDelay = maxRequestDelay;
126 mMaxPipelinedRequests = maxPipelinedRequests;
128 mSocketThreadTarget = sts;
129 return rv;
132 nsresult
133 nsHttpConnectionMgr::Shutdown()
135 LOG(("nsHttpConnectionMgr::Shutdown\n"));
137 nsAutoMonitor mon(mMonitor);
139 // do nothing if already shutdown
140 if (!mSocketThreadTarget)
141 return NS_OK;
143 nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgShutdown);
145 // release our reference to the STS to prevent further events
146 // from being posted. this is how we indicate that we are
147 // shutting down.
148 mSocketThreadTarget = 0;
150 if (NS_FAILED(rv)) {
151 NS_WARNING("unable to post SHUTDOWN message\n");
152 return rv;
155 // wait for shutdown event to complete
156 mon.Wait();
157 return NS_OK;
160 nsresult
161 nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, PRInt32 iparam, void *vparam)
163 nsAutoMonitor mon(mMonitor);
165 nsresult rv;
166 if (!mSocketThreadTarget) {
167 NS_WARNING("cannot post event if not initialized");
168 rv = NS_ERROR_NOT_INITIALIZED;
170 else {
171 nsRefPtr<nsIRunnable> event = new nsConnEvent(this, handler, iparam, vparam);
172 if (!event)
173 rv = NS_ERROR_OUT_OF_MEMORY;
174 else
175 rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
177 return rv;
180 //-----------------------------------------------------------------------------
182 nsresult
183 nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, PRInt32 priority)
185 LOG(("nsHttpConnectionMgr::AddTransaction [trans=%x %d]\n", trans, priority));
187 NS_ADDREF(trans);
188 nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
189 if (NS_FAILED(rv))
190 NS_RELEASE(trans);
191 return rv;
194 nsresult
195 nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, PRInt32 priority)
197 LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%x %d]\n", trans, priority));
199 NS_ADDREF(trans);
200 nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority, trans);
201 if (NS_FAILED(rv))
202 NS_RELEASE(trans);
203 return rv;
206 nsresult
207 nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
209 LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%x reason=%x]\n", trans, reason));
211 NS_ADDREF(trans);
212 nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction, reason, trans);
213 if (NS_FAILED(rv))
214 NS_RELEASE(trans);
215 return rv;
218 nsresult
219 nsHttpConnectionMgr::PruneDeadConnections()
221 return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
224 nsresult
225 nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget **target)
227 nsAutoMonitor mon(mMonitor);
228 NS_IF_ADDREF(*target = mSocketThreadTarget);
229 return NS_OK;
232 void
233 nsHttpConnectionMgr::AddTransactionToPipeline(nsHttpPipeline *pipeline)
235 LOG(("nsHttpConnectionMgr::AddTransactionToPipeline [pipeline=%x]\n", pipeline));
237 NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
239 nsRefPtr<nsHttpConnectionInfo> ci;
240 pipeline->GetConnectionInfo(getter_AddRefs(ci));
241 if (ci) {
242 nsCStringKey key(ci->HashKey());
243 nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
244 if (ent) {
245 // search for another request to pipeline...
246 PRInt32 i, count = ent->mPendingQ.Count();
247 for (i=0; i<count; ++i) {
248 nsHttpTransaction *trans = (nsHttpTransaction *) ent->mPendingQ[i];
249 if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
250 pipeline->AddTransaction(trans);
252 // remove transaction from pending queue
253 ent->mPendingQ.RemoveElementAt(i);
254 NS_RELEASE(trans);
255 break;
262 nsresult
263 nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
265 LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
267 NS_ADDREF(conn);
268 nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
269 if (NS_FAILED(rv))
270 NS_RELEASE(conn);
271 return rv;
274 nsresult
275 nsHttpConnectionMgr::UpdateParam(nsParamName name, PRUint16 value)
277 PRUint32 param = (PRUint32(name) << 16) | PRUint32(value);
278 return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam, 0, (void *) param);
281 nsresult
282 nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
284 LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
286 NS_ADDREF(ci);
287 nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
288 if (NS_FAILED(rv))
289 NS_RELEASE(ci);
290 return rv;
293 //-----------------------------------------------------------------------------
294 // enumeration callbacks
296 PRIntn
297 nsHttpConnectionMgr::ProcessOneTransactionCB(nsHashKey *key, void *data, void *closure)
299 nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
300 nsConnectionEntry *ent = (nsConnectionEntry *) data;
302 if (self->ProcessPendingQForEntry(ent))
303 return kHashEnumerateStop;
305 return kHashEnumerateNext;
308 PRIntn
309 nsHttpConnectionMgr::PurgeOneIdleConnectionCB(nsHashKey *key, void *data, void *closure)
311 nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
312 nsConnectionEntry *ent = (nsConnectionEntry *) data;
314 if (ent->mIdleConns.Count() > 0) {
315 nsHttpConnection *conn = (nsHttpConnection *) ent->mIdleConns[0];
316 ent->mIdleConns.RemoveElementAt(0);
317 conn->Close(NS_ERROR_ABORT);
318 NS_RELEASE(conn);
319 self->mNumIdleConns--;
320 return kHashEnumerateStop;
323 return kHashEnumerateNext;
326 PRIntn
327 nsHttpConnectionMgr::PruneDeadConnectionsCB(nsHashKey *key, void *data, void *closure)
329 nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
330 nsConnectionEntry *ent = (nsConnectionEntry *) data;
332 LOG((" pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
334 PRInt32 count = ent->mIdleConns.Count();
335 if (count > 0) {
336 for (PRInt32 i=count-1; i>=0; --i) {
337 nsHttpConnection *conn = (nsHttpConnection *) ent->mIdleConns[i];
338 if (!conn->CanReuse()) {
339 ent->mIdleConns.RemoveElementAt(i);
340 conn->Close(NS_ERROR_ABORT);
341 NS_RELEASE(conn);
342 self->mNumIdleConns--;
347 #ifdef DEBUG
348 count = ent->mActiveConns.Count();
349 if (count > 0) {
350 for (PRInt32 i=count-1; i>=0; --i) {
351 nsHttpConnection *conn = (nsHttpConnection *) ent->mActiveConns[i];
352 LOG((" active conn [%x] with trans [%x]\n", conn, conn->Transaction()));
355 #endif
357 // if this entry is empty, then we can remove it.
358 if (ent->mIdleConns.Count() == 0 &&
359 ent->mActiveConns.Count() == 0 &&
360 ent->mPendingQ.Count() == 0) {
361 LOG((" removing empty connection entry\n"));
362 delete ent;
363 return kHashEnumerateRemove;
366 // else, use this opportunity to compact our arrays...
367 ent->mIdleConns.Compact();
368 ent->mActiveConns.Compact();
369 ent->mPendingQ.Compact();
371 return kHashEnumerateNext;
374 PRIntn
375 nsHttpConnectionMgr::ShutdownPassCB(nsHashKey *key, void *data, void *closure)
377 nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
378 nsConnectionEntry *ent = (nsConnectionEntry *) data;
380 nsHttpTransaction *trans;
381 nsHttpConnection *conn;
383 // close all active connections
384 while (ent->mActiveConns.Count()) {
385 conn = (nsHttpConnection *) ent->mActiveConns[0];
387 ent->mActiveConns.RemoveElementAt(0);
388 self->mNumActiveConns--;
390 conn->Close(NS_ERROR_ABORT);
391 NS_RELEASE(conn);
394 // close all idle connections
395 while (ent->mIdleConns.Count()) {
396 conn = (nsHttpConnection *) ent->mIdleConns[0];
398 ent->mIdleConns.RemoveElementAt(0);
399 self->mNumIdleConns--;
401 conn->Close(NS_ERROR_ABORT);
402 NS_RELEASE(conn);
405 // close all pending transactions
406 while (ent->mPendingQ.Count()) {
407 trans = (nsHttpTransaction *) ent->mPendingQ[0];
409 ent->mPendingQ.RemoveElementAt(0);
411 trans->Close(NS_ERROR_ABORT);
412 NS_RELEASE(trans);
415 delete ent;
416 return kHashEnumerateRemove;
419 //-----------------------------------------------------------------------------
421 PRBool
422 nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
424 LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
425 ent->mConnInfo->HashKey().get()));
427 PRInt32 i, count = ent->mPendingQ.Count();
428 if (count > 0) {
429 LOG((" pending-count=%u\n", count));
430 nsHttpTransaction *trans = nsnull;
431 nsHttpConnection *conn = nsnull;
432 for (i=0; i<count; ++i) {
433 trans = (nsHttpTransaction *) ent->mPendingQ[i];
434 GetConnection(ent, trans->Caps(), &conn);
435 if (conn)
436 break;
438 if (conn) {
439 LOG((" dispatching pending transaction...\n"));
441 // remove pending transaction
442 ent->mPendingQ.RemoveElementAt(i);
444 nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
445 if (NS_SUCCEEDED(rv))
446 NS_RELEASE(trans);
447 else {
448 LOG((" DispatchTransaction failed [rv=%x]\n", rv));
449 // on failure, just put the transaction back
450 ent->mPendingQ.InsertElementAt(trans, i);
451 // might be something wrong with the connection... close it.
452 conn->Close(rv);
455 NS_RELEASE(conn);
456 return PR_TRUE;
459 return PR_FALSE;
462 // we're at the active connection limit if any one of the following conditions is true:
463 // (1) at max-connections
464 // (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
465 // (3) keep-alive disabled and at max-connections-per-server
466 PRBool
467 nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 caps)
469 nsHttpConnectionInfo *ci = ent->mConnInfo;
471 LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
472 ci->HashKey().get(), caps));
474 // use >= just to be safe
475 if (mNumActiveConns >= mMaxConns) {
476 LOG((" num active conns == max conns\n"));
477 return PR_TRUE;
480 nsHttpConnection *conn;
481 PRInt32 i, totalCount, persistCount = 0;
483 totalCount = ent->mActiveConns.Count();
485 // count the number of persistent connections
486 for (i=0; i<totalCount; ++i) {
487 conn = (nsHttpConnection *) ent->mActiveConns[i];
488 if (conn->IsKeepAlive()) // XXX make sure this is thread-safe
489 persistCount++;
492 LOG((" total=%d, persist=%d\n", totalCount, persistCount));
494 PRUint16 maxConns;
495 PRUint16 maxPersistConns;
497 if (ci->UsingHttpProxy() && !ci->UsingSSL()) {
498 maxConns = mMaxConnsPerProxy;
499 maxPersistConns = mMaxPersistConnsPerProxy;
501 else {
502 maxConns = mMaxConnsPerHost;
503 maxPersistConns = mMaxPersistConnsPerHost;
506 // use >= just to be safe
507 return (totalCount >= maxConns) || ( (caps & NS_HTTP_ALLOW_KEEPALIVE) &&
508 (persistCount >= maxPersistConns) );
511 void
512 nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent, PRUint8 caps,
513 nsHttpConnection **result)
515 LOG(("nsHttpConnectionMgr::GetConnection [ci=%s caps=%x]\n",
516 ent->mConnInfo->HashKey().get(), PRUint32(caps)));
518 *result = nsnull;
520 if (AtActiveConnectionLimit(ent, caps)) {
521 LOG((" at active connection limit!\n"));
522 return;
525 nsHttpConnection *conn = nsnull;
527 if (caps & NS_HTTP_ALLOW_KEEPALIVE) {
528 // search the idle connection list
529 while (!conn && (ent->mIdleConns.Count() > 0)) {
530 conn = (nsHttpConnection *) ent->mIdleConns[0];
531 // we check if the connection can be reused before even checking if
532 // it is a "matching" connection.
533 if (!conn->CanReuse()) {
534 LOG((" dropping stale connection: [conn=%x]\n", conn));
535 conn->Close(NS_ERROR_ABORT);
536 NS_RELEASE(conn);
538 else
539 LOG((" reusing connection [conn=%x]\n", conn));
540 ent->mIdleConns.RemoveElementAt(0);
541 mNumIdleConns--;
545 if (!conn) {
546 conn = new nsHttpConnection();
547 if (!conn)
548 return;
549 NS_ADDREF(conn);
551 nsresult rv = conn->Init(ent->mConnInfo, mMaxRequestDelay);
552 if (NS_FAILED(rv)) {
553 NS_RELEASE(conn);
554 return;
557 // We created a new connection that will become active, purge the
558 // oldest idle connection if we've reached the upper limit.
559 if (mNumIdleConns + mNumActiveConns + 1 > mMaxConns)
560 mCT.Enumerate(PurgeOneIdleConnectionCB, this);
562 // XXX this just purges a random idle connection. we should instead
563 // enumerate the entire hash table to find the eldest idle connection.
566 *result = conn;
569 nsresult
570 nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
571 nsAHttpTransaction *trans,
572 PRUint8 caps,
573 nsHttpConnection *conn)
575 LOG(("nsHttpConnectionMgr::DispatchTransaction [ci=%s trans=%x caps=%x conn=%x]\n",
576 ent->mConnInfo->HashKey().get(), trans, caps, conn));
578 nsConnectionHandle *handle = new nsConnectionHandle(conn);
579 if (!handle)
580 return NS_ERROR_OUT_OF_MEMORY;
581 NS_ADDREF(handle);
583 nsHttpPipeline *pipeline = nsnull;
584 if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
585 LOG((" looking to build pipeline...\n"));
586 if (BuildPipeline(ent, trans, &pipeline))
587 trans = pipeline;
590 // hold an owning ref to this connection
591 ent->mActiveConns.AppendElement(conn);
592 mNumActiveConns++;
593 NS_ADDREF(conn);
595 // give the transaction the indirect reference to the connection.
596 trans->SetConnection(handle);
598 nsresult rv = conn->Activate(trans, caps);
600 if (NS_FAILED(rv)) {
601 LOG((" conn->Activate failed [rv=%x]\n", rv));
602 ent->mActiveConns.RemoveElement(conn);
603 mNumActiveConns--;
604 // sever back references to connection, and do so without triggering
605 // a call to ReclaimConnection ;-)
606 trans->SetConnection(nsnull);
607 NS_RELEASE(handle->mConn);
608 // destroy the connection
609 NS_RELEASE(conn);
612 // if we were unable to activate the pipeline, then this will destroy
613 // the pipeline, which will cause each the transactions owned by the
614 // pipeline to be restarted.
615 NS_IF_RELEASE(pipeline);
617 NS_RELEASE(handle);
618 return rv;
621 PRBool
622 nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
623 nsAHttpTransaction *firstTrans,
624 nsHttpPipeline **result)
626 if (mMaxPipelinedRequests < 2)
627 return PR_FALSE;
629 nsHttpPipeline *pipeline = nsnull;
630 nsHttpTransaction *trans;
632 PRInt32 i = 0, numAdded = 0;
633 while (i < ent->mPendingQ.Count()) {
634 trans = (nsHttpTransaction *) ent->mPendingQ[i];
635 if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
636 if (numAdded == 0) {
637 pipeline = new nsHttpPipeline;
638 if (!pipeline)
639 return PR_FALSE;
640 pipeline->AddTransaction(firstTrans);
641 numAdded = 1;
643 pipeline->AddTransaction(trans);
645 // remove transaction from pending queue
646 ent->mPendingQ.RemoveElementAt(i);
647 NS_RELEASE(trans);
649 if (++numAdded == mMaxPipelinedRequests)
650 break;
652 else
653 ++i; // skip to next pending transaction
656 if (numAdded == 0)
657 return PR_FALSE;
659 LOG((" pipelined %u transactions\n", numAdded));
660 NS_ADDREF(*result = pipeline);
661 return PR_TRUE;
664 nsresult
665 nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
667 // since "adds" and "cancels" are processed asynchronously and because
668 // various events might trigger an "add" directly on the socket thread,
669 // we must take care to avoid dispatching a transaction that has already
670 // been canceled (see bug 190001).
671 if (NS_FAILED(trans->Status())) {
672 LOG((" transaction was canceled... dropping event!\n"));
673 return NS_OK;
676 PRUint8 caps = trans->Caps();
677 nsHttpConnectionInfo *ci = trans->ConnectionInfo();
678 NS_ASSERTION(ci, "no connection info");
680 nsCStringKey key(ci->HashKey());
681 nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
682 if (!ent) {
683 ent = new nsConnectionEntry(ci);
684 if (!ent)
685 return NS_ERROR_OUT_OF_MEMORY;
686 mCT.Put(&key, ent);
689 nsHttpConnection *conn;
691 // check if the transaction already has a sticky reference to a connection.
692 // if so, then we can just use it directly. XXX check if alive??
693 // XXX add a TakeConnection method or something to make this clearer!
694 nsConnectionHandle *handle = (nsConnectionHandle *) trans->Connection();
695 if (handle) {
696 NS_ASSERTION(caps & NS_HTTP_STICKY_CONNECTION, "unexpected caps");
697 NS_ASSERTION(handle->mConn, "no connection");
699 // steal reference from connection handle.
700 // XXX prevent SetConnection(nsnull) from calling ReclaimConnection
701 conn = handle->mConn;
702 handle->mConn = nsnull;
704 // destroy connection handle.
705 trans->SetConnection(nsnull);
707 // remove sticky connection from active connection list; we'll add it
708 // right back in DispatchTransaction.
709 if (ent->mActiveConns.RemoveElement(conn))
710 mNumActiveConns--;
711 else {
712 NS_ERROR("sticky connection not found in active list");
713 return NS_ERROR_UNEXPECTED;
716 else
717 GetConnection(ent, caps, &conn);
719 nsresult rv;
720 if (!conn) {
721 LOG((" adding transaction to pending queue [trans=%x pending-count=%u]\n",
722 trans, ent->mPendingQ.Count()+1));
723 // put this transaction on the pending queue...
724 InsertTransactionSorted(ent->mPendingQ, trans);
725 NS_ADDREF(trans);
726 rv = NS_OK;
728 else {
729 rv = DispatchTransaction(ent, trans, caps, conn);
730 NS_RELEASE(conn);
733 return rv;
736 //-----------------------------------------------------------------------------
738 void
739 nsHttpConnectionMgr::OnMsgShutdown(PRInt32, void *)
741 LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
743 mCT.Reset(ShutdownPassCB, this);
745 // signal shutdown complete
746 nsAutoMonitor mon(mMonitor);
747 mon.Notify();
750 void
751 nsHttpConnectionMgr::OnMsgNewTransaction(PRInt32 priority, void *param)
753 LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
755 nsHttpTransaction *trans = (nsHttpTransaction *) param;
756 trans->SetPriority(priority);
757 nsresult rv = ProcessNewTransaction(trans);
758 if (NS_FAILED(rv))
759 trans->Close(rv); // for whatever its worth
760 NS_RELEASE(trans);
763 void
764 nsHttpConnectionMgr::OnMsgReschedTransaction(PRInt32 priority, void *param)
766 LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
768 nsHttpTransaction *trans = (nsHttpTransaction *) param;
769 trans->SetPriority(priority);
771 nsHttpConnectionInfo *ci = trans->ConnectionInfo();
772 nsCStringKey key(ci->HashKey());
773 nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
774 if (ent) {
775 PRInt32 index = ent->mPendingQ.IndexOf(trans);
776 if (index >= 0) {
777 ent->mPendingQ.RemoveElementAt(index);
778 InsertTransactionSorted(ent->mPendingQ, trans);
782 NS_RELEASE(trans);
785 void
786 nsHttpConnectionMgr::OnMsgCancelTransaction(PRInt32 reason, void *param)
788 LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
790 nsHttpTransaction *trans = (nsHttpTransaction *) param;
792 // if the transaction owns a connection and the transaction is not done,
793 // then ask the connection to close the transaction. otherwise, close the
794 // transaction directly (removing it from the pending queue first).
796 nsAHttpConnection *conn = trans->Connection();
797 if (conn && !trans->IsDone())
798 conn->CloseTransaction(trans, reason);
799 else {
800 nsHttpConnectionInfo *ci = trans->ConnectionInfo();
801 nsCStringKey key(ci->HashKey());
802 nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
803 if (ent) {
804 PRInt32 index = ent->mPendingQ.IndexOf(trans);
805 if (index >= 0) {
806 ent->mPendingQ.RemoveElementAt(index);
807 nsHttpTransaction *temp = trans;
808 NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
811 trans->Close(reason);
813 NS_RELEASE(trans);
816 void
817 nsHttpConnectionMgr::OnMsgProcessPendingQ(PRInt32, void *param)
819 nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param;
821 LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
823 // start by processing the queue identified by the given connection info.
824 nsCStringKey key(ci->HashKey());
825 nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
826 if (!(ent && ProcessPendingQForEntry(ent))) {
827 // if we reach here, it means that we couldn't dispatch a transaction
828 // for the specified connection info. walk the connection table...
829 mCT.Enumerate(ProcessOneTransactionCB, this);
832 NS_RELEASE(ci);
835 void
836 nsHttpConnectionMgr::OnMsgPruneDeadConnections(PRInt32, void *)
838 LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
840 if (mNumIdleConns > 0)
841 mCT.Enumerate(PruneDeadConnectionsCB, this);
844 void
845 nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
847 LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
849 nsHttpConnection *conn = (nsHttpConnection *) param;
852 // 1) remove the connection from the active list
853 // 2) if keep-alive, add connection to idle list
854 // 3) post event to process the pending transaction queue
857 nsHttpConnectionInfo *ci = conn->ConnectionInfo();
858 NS_ADDREF(ci);
860 nsCStringKey key(ci->HashKey());
861 nsConnectionEntry *ent = (nsConnectionEntry *) mCT.Get(&key);
863 NS_ASSERTION(ent, "no connection entry");
864 if (ent) {
865 ent->mActiveConns.RemoveElement(conn);
866 mNumActiveConns--;
867 if (conn->CanReuse()) {
868 LOG((" adding connection to idle list\n"));
869 // hold onto this connection in the idle list. we push it to
870 // the end of the list so as to ensure that we'll visit older
871 // connections first before getting to this one.
872 ent->mIdleConns.AppendElement(conn);
873 mNumIdleConns++;
875 else {
876 LOG((" connection cannot be reused; closing connection\n"));
877 // make sure the connection is closed and release our reference.
878 conn->Close(NS_ERROR_ABORT);
879 nsHttpConnection *temp = conn;
880 NS_RELEASE(temp);
884 OnMsgProcessPendingQ(NS_OK, ci); // releases |ci|
885 NS_RELEASE(conn);
888 void
889 nsHttpConnectionMgr::OnMsgUpdateParam(PRInt32, void *param)
891 PRUint16 name = (NS_PTR_TO_INT32(param) & 0xFFFF0000) >> 16;
892 PRUint16 value = NS_PTR_TO_INT32(param) & 0x0000FFFF;
894 switch (name) {
895 case MAX_CONNECTIONS:
896 mMaxConns = value;
897 break;
898 case MAX_CONNECTIONS_PER_HOST:
899 mMaxConnsPerHost = value;
900 break;
901 case MAX_CONNECTIONS_PER_PROXY:
902 mMaxConnsPerProxy = value;
903 break;
904 case MAX_PERSISTENT_CONNECTIONS_PER_HOST:
905 mMaxPersistConnsPerHost = value;
906 break;
907 case MAX_PERSISTENT_CONNECTIONS_PER_PROXY:
908 mMaxPersistConnsPerProxy = value;
909 break;
910 case MAX_REQUEST_DELAY:
911 mMaxRequestDelay = value;
912 break;
913 case MAX_PIPELINED_REQUESTS:
914 mMaxPipelinedRequests = value;
915 break;
916 default:
917 NS_NOTREACHED("unexpected parameter name");
921 //-----------------------------------------------------------------------------
922 // nsHttpConnectionMgr::nsConnectionHandle
924 nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
926 if (mConn) {
927 gHttpHandler->ReclaimConnection(mConn);
928 NS_RELEASE(mConn);
932 NS_IMPL_THREADSAFE_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
934 nsresult
935 nsHttpConnectionMgr::nsConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
936 nsHttpRequestHead *req,
937 nsHttpResponseHead *resp,
938 PRBool *reset)
940 return mConn->OnHeadersAvailable(trans, req, resp, reset);
943 nsresult
944 nsHttpConnectionMgr::nsConnectionHandle::ResumeSend()
946 return mConn->ResumeSend();
949 nsresult
950 nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv()
952 return mConn->ResumeRecv();
955 void
956 nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
958 mConn->CloseTransaction(trans, reason);
961 void
962 nsHttpConnectionMgr::nsConnectionHandle::GetConnectionInfo(nsHttpConnectionInfo **result)
964 mConn->GetConnectionInfo(result);
967 void
968 nsHttpConnectionMgr::nsConnectionHandle::GetSecurityInfo(nsISupports **result)
970 mConn->GetSecurityInfo(result);
973 PRBool
974 nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
976 return mConn->IsPersistent();
979 PRBool
980 nsHttpConnectionMgr::nsConnectionHandle::IsReused()
982 return mConn->IsReused();
985 nsresult
986 nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufLen)
988 return mConn->PushBack(buf, bufLen);