LCOV - app.info - netwerk/protocol/http/nsHttpConnectionMgr.cpp

LCOV - code coverage report
| Current view: | directory - netwerk/protocol/http - nsHttpConnectionMgr.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 1064 | 621 | 58.4 %
| Date: | 2012-07-07 | Functions: | 101 | 71 | 70.3 %

           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
           4                 :  *
           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/
           9                 :  *
          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.
          14                 :  *
          15                 :  * The Original Code is Mozilla.
          16                 :  *
          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.
          21                 :  *
          22                 :  * Contributor(s):
          23                 :  *   Darin Fisher <darin@netscape.com>
          24                 :  *
          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.
          36                 :  *
          37                 :  * ***** END LICENSE BLOCK ***** */
          38                 :
          39                 : #include "nsHttpConnectionMgr.h"
          40                 : #include "nsHttpConnection.h"
          41                 : #include "nsHttpPipeline.h"
          42                 : #include "nsHttpHandler.h"
          43                 : #include "nsNetCID.h"
          44                 : #include "nsCOMPtr.h"
          45                 : #include "nsNetUtil.h"
          46                 :
          47                 : #include "nsIServiceManager.h"
          48                 :
          49                 : #include "nsIObserverService.h"
          50                 :
          51                 : #include "nsISSLSocketControl.h"
          52                 : #include "prnetdb.h"
          53                 : #include "mozilla/Telemetry.h"
          54                 :
          55                 : using namespace mozilla;
          56                 :
          57                 : // defined by the socket transport service while active
          58                 : extern PRThread *gSocketThread;
          59                 :
          60                 : static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
          61                 :
          62                 : //-----------------------------------------------------------------------------
          63                 :
          64                 :
          65           20888 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsHttpConnectionMgr, nsIObserver)
          66                 :
          67                 : static void
          68            2976 : InsertTransactionSorted(nsTArray<nsHttpTransaction*> &pendingQ, nsHttpTransaction *trans)
          69                 : {
          70                 :     // insert into queue with smallest valued number first.  search in reverse
          71                 :     // order under the assumption that many of the existing transactions will
          72                 :     // have the same priority (usually 0).
          73                 :
          74            2976 :     for (PRInt32 i=pendingQ.Length()-1; i>=0; --i) {
          75             120 :         nsHttpTransaction *t = pendingQ[i];
          76             120 :         if (trans->Priority() >= t->Priority()) {
          77             120 :             pendingQ.InsertElementAt(i+1, trans);
          78             120 :             return;
          79                 :         }
          80                 :     }
          81            2856 :     pendingQ.InsertElementAt(0, trans);
          82                 : }
          83                 :
          84                 : //-----------------------------------------------------------------------------
          85                 :
          86             679 : nsHttpConnectionMgr::nsHttpConnectionMgr()
          87                 :     : mRef(0)
          88                 :     , mReentrantMonitor("nsHttpConnectionMgr.mReentrantMonitor")
          89                 :     , mMaxConns(0)
          90                 :     , mMaxConnsPerHost(0)
          91                 :     , mMaxConnsPerProxy(0)
          92                 :     , mMaxPersistConnsPerHost(0)
          93                 :     , mMaxPersistConnsPerProxy(0)
          94                 :     , mIsShuttingDown(false)
          95                 :     , mNumActiveConns(0)
          96                 :     , mNumIdleConns(0)
          97                 :     , mTimeOfNextWakeUp(LL_MAXUINT)
          98             679 :     , mReadTimeoutTickArmed(false)
          99                 : {
         100             679 :     LOG(("Creating nsHttpConnectionMgr @%x\n", this));
         101             679 :     mCT.Init();
         102             679 :     mAlternateProtocolHash.Init(16);
         103             679 :     mSpdyPreferredHash.Init();
         104             679 : }
         105                 :
         106            2031 : nsHttpConnectionMgr::~nsHttpConnectionMgr()
         107                 : {
         108             677 :     LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
         109             677 :     if (mReadTimeoutTick)
         110               0 :         mReadTimeoutTick->Cancel();
         111            2708 : }
         112                 :
         113                 : nsresult
         114            7729 : nsHttpConnectionMgr::EnsureSocketThreadTargetIfOnline()
         115                 : {
         116                 :     nsresult rv;
         117           15458 :     nsCOMPtr<nsIEventTarget> sts;
         118           15458 :     nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
         119            7729 :     if (NS_SUCCEEDED(rv)) {
         120            7729 :         bool offline = true;
         121            7729 :         ioService->GetOffline(&offline);
         122                 :
         123            7729 :         if (!offline) {
         124            7715 :             sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
         125                 :         }
         126                 :     }
         127                 :
         128           15458 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
         129                 :
         130                 :     // do nothing if already initialized or if we've shut down
         131            7729 :     if (mSocketThreadTarget || mIsShuttingDown)
         132            7049 :         return NS_OK;
         133                 :
         134             680 :     mSocketThreadTarget = sts;
         135                 :
         136             680 :     return rv;
         137                 : }
         138                 :
         139                 : nsresult
         140             679 : nsHttpConnectionMgr::Init(PRUint16 maxConns,
         141                 :                           PRUint16 maxConnsPerHost,
         142                 :                           PRUint16 maxConnsPerProxy,
         143                 :                           PRUint16 maxPersistConnsPerHost,
         144                 :                           PRUint16 maxPersistConnsPerProxy,
         145                 :                           PRUint16 maxRequestDelay,
         146                 :                           PRUint16 maxPipelinedRequests)
         147                 : {
         148             679 :     LOG(("nsHttpConnectionMgr::Init\n"));
         149                 :
         150                 :     {
         151            1358 :         ReentrantMonitorAutoEnter mon(mReentrantMonitor);
         152                 :
         153             679 :         mMaxConns = maxConns;
         154             679 :         mMaxConnsPerHost = maxConnsPerHost;
         155             679 :         mMaxConnsPerProxy = maxConnsPerProxy;
         156             679 :         mMaxPersistConnsPerHost = maxPersistConnsPerHost;
         157             679 :         mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
         158             679 :         mMaxRequestDelay = maxRequestDelay;
         159             679 :         mMaxPipelinedRequests = maxPipelinedRequests;
         160                 :
         161             679 :         mIsShuttingDown = false;
         162                 :     }
         163                 :
         164             679 :     return EnsureSocketThreadTargetIfOnline();
         165                 : }
         166                 :
         167                 : nsresult
         168            1888 : nsHttpConnectionMgr::Shutdown()
         169                 : {
         170            1888 :     LOG(("nsHttpConnectionMgr::Shutdown\n"));
         171                 :
         172            3776 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
         173                 :
         174                 :     // do nothing if already shutdown
         175            1888 :     if (!mSocketThreadTarget)
         176            1210 :         return NS_OK;
         177                 :
         178             678 :     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgShutdown);
         179                 :
         180                 :     // release our reference to the STS to prevent further events
         181                 :     // from being posted.  this is how we indicate that we are
         182                 :     // shutting down.
         183             678 :     mIsShuttingDown = true;
         184             678 :     mSocketThreadTarget = 0;
         185                 :
         186             678 :     if (NS_FAILED(rv)) {
         187               0 :         NS_WARNING("unable to post SHUTDOWN message");
         188               0 :         return rv;
         189                 :     }
         190                 :
         191                 :     // wait for shutdown event to complete
         192             678 :     mon.Wait();
         193             678 :     return NS_OK;
         194                 : }
         195                 :
         196                 : nsresult
         197            7050 : nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, PRInt32 iparam, void *vparam)
         198                 : {
         199                 :     // This object doesn't get reinitialized if the offline state changes, so our
         200                 :     // socket thread target might be uninitialized if we were offline when this
         201                 :     // object was being initialized, and we go online later on.  This call takes
         202                 :     // care of initializing the socket thread target if that's the case.
         203            7050 :     EnsureSocketThreadTargetIfOnline();
         204                 :
         205           14100 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
         206                 :
         207                 :     nsresult rv;
         208            7050 :     if (!mSocketThreadTarget) {
         209              12 :         NS_WARNING("cannot post event if not initialized");
         210              12 :         rv = NS_ERROR_NOT_INITIALIZED;
         211                 :     }
         212                 :     else {
         213           14076 :         nsRefPtr<nsIRunnable> event = new nsConnEvent(this, handler, iparam, vparam);
         214            7038 :         if (!event)
         215               0 :             rv = NS_ERROR_OUT_OF_MEMORY;
         216                 :         else
         217            7038 :             rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
         218                 :     }
         219            7050 :     return rv;
         220                 : }
         221                 :
         222                 : void
         223               5 : nsHttpConnectionMgr::PruneDeadConnectionsAfter(PRUint32 timeInSeconds)
         224                 : {
         225               5 :     LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));
         226                 :
         227               5 :     if(!mTimer)
         228               5 :         mTimer = do_CreateInstance("@mozilla.org/timer;1");
         229                 :
         230                 :     // failure to create a timer is not a fatal error, but idle connections
         231                 :     // will not be cleaned up until we try to use them.
         232               5 :     if (mTimer) {
         233               5 :         mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
         234               5 :         mTimer->Init(this, timeInSeconds*1000, nsITimer::TYPE_ONE_SHOT);
         235                 :     } else {
         236               0 :         NS_WARNING("failed to create: timer for pruning the dead connections!");
         237                 :     }
         238               5 : }
         239                 :
         240                 : void
         241             293 : nsHttpConnectionMgr::ConditionallyStopPruneDeadConnectionsTimer()
         242                 : {
         243                 :     // Leave the timer in place if there are connections that potentially
         244                 :     // need management
         245             293 :     if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
         246               7 :         return;
         247                 :
         248             286 :     LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));
         249                 :
         250                 :     // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
         251             286 :     mTimeOfNextWakeUp = LL_MAXUINT;
         252             286 :     if (mTimer) {
         253               5 :         mTimer->Cancel();
         254               5 :         mTimer = NULL;
         255                 :     }
         256                 : }
         257                 :
         258                 : void
         259            5949 : nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick()
         260                 : {
         261            5949 :     LOG(("nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick "
         262                 :          "armed=%d active=%d\n", mReadTimeoutTickArmed, mNumActiveConns));
         263                 :
         264            5949 :     if (!mReadTimeoutTickArmed)
         265            2706 :         return;
         266                 :
         267            3243 :     if (mNumActiveConns)
         268             521 :         return;
         269                 :
         270            2722 :     LOG(("nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick stop==true\n"));
         271                 :
         272            2722 :     mReadTimeoutTick->Cancel();
         273            2722 :     mReadTimeoutTickArmed = false;
         274                 : }
         275                 :
         276                 : //-----------------------------------------------------------------------------
         277                 : // nsHttpConnectionMgr::nsIObserver
         278                 : //-----------------------------------------------------------------------------
         279                 :
         280                 : NS_IMETHODIMP
         281               1 : nsHttpConnectionMgr::Observe(nsISupports *subject,
         282                 :                              const char *topic,
         283                 :                              const PRUnichar *data)
         284                 : {
         285               1 :     LOG(("nsHttpConnectionMgr::Observe [topic=\"%s\"]\n", topic));
         286                 :
         287               1 :     if (0 == strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
         288               2 :         nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
         289               1 :         if (timer == mTimer) {
         290               1 :             PruneDeadConnections();
         291                 :         }
         292               0 :         else if (timer == mReadTimeoutTick) {
         293               0 :             ReadTimeoutTick();
         294                 :         }
         295                 :         else {
         296               0 :             NS_ABORT_IF_FALSE(false, "unexpected timer-callback");
         297               0 :             LOG(("Unexpected timer object\n"));
         298               0 :             return NS_ERROR_UNEXPECTED;
         299                 :         }
         300                 :     }
         301                 :
         302               1 :     return NS_OK;
         303                 : }
         304                 :
         305                 :
         306                 : //-----------------------------------------------------------------------------
         307                 :
         308                 : nsresult
         309            2988 : nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, PRInt32 priority)
         310                 : {
         311            2988 :     LOG(("nsHttpConnectionMgr::AddTransaction [trans=%x %d]\n", trans, priority));
         312                 :
         313            2988 :     NS_ADDREF(trans);
         314            2988 :     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
         315            2988 :     if (NS_FAILED(rv))
         316              12 :         NS_RELEASE(trans);
         317            2988 :     return rv;
         318                 : }
         319                 :
         320                 : nsresult
         321               0 : nsHttpConnectionMgr::RescheduleTransaction(nsHttpTransaction *trans, PRInt32 priority)
         322                 : {
         323               0 :     LOG(("nsHttpConnectionMgr::RescheduleTransaction [trans=%x %d]\n", trans, priority));
         324                 :
         325               0 :     NS_ADDREF(trans);
         326               0 :     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReschedTransaction, priority, trans);
         327               0 :     if (NS_FAILED(rv))
         328               0 :         NS_RELEASE(trans);
         329               0 :     return rv;
         330                 : }
         331                 :
         332                 : nsresult
         333             162 : nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
         334                 : {
         335             162 :     LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%x reason=%x]\n", trans, reason));
         336                 :
         337             162 :     NS_ADDREF(trans);
         338             162 :     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction, reason, trans);
         339             162 :     if (NS_FAILED(rv))
         340               0 :         NS_RELEASE(trans);
         341             162 :     return rv;
         342                 : }
         343                 :
         344                 : nsresult
         345             125 : nsHttpConnectionMgr::PruneDeadConnections()
         346                 : {
         347             125 :     return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
         348                 : }
         349                 :
         350                 : nsresult
         351             123 : nsHttpConnectionMgr::ClosePersistentConnections()
         352                 : {
         353             123 :     return PostEvent(&nsHttpConnectionMgr::OnMsgClosePersistentConnections);
         354                 : }
         355                 :
         356                 : nsresult
         357               0 : nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget **target)
         358                 : {
         359                 :     // This object doesn't get reinitialized if the offline state changes, so our
         360                 :     // socket thread target might be uninitialized if we were offline when this
         361                 :     // object was being initialized, and we go online later on.  This call takes
         362                 :     // care of initializing the socket thread target if that's the case.
         363               0 :     EnsureSocketThreadTargetIfOnline();
         364                 :
         365               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
         366               0 :     NS_IF_ADDREF(*target = mSocketThreadTarget);
         367               0 :     return NS_OK;
         368                 : }
         369                 :
         370                 : void
         371               0 : nsHttpConnectionMgr::AddTransactionToPipeline(nsHttpPipeline *pipeline)
         372                 : {
         373               0 :     LOG(("nsHttpConnectionMgr::AddTransactionToPipeline [pipeline=%x]\n", pipeline));
         374                 :
         375               0 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
         376                 :
         377               0 :     nsRefPtr<nsHttpConnectionInfo> ci;
         378               0 :     pipeline->GetConnectionInfo(getter_AddRefs(ci));
         379               0 :     if (ci) {
         380               0 :         nsConnectionEntry *ent = mCT.Get(ci->HashKey());
         381               0 :         if (ent) {
         382                 :             // search for another request to pipeline...
         383               0 :             PRInt32 i, count = ent->mPendingQ.Length();
         384               0 :             for (i=0; i<count; ++i) {
         385               0 :                 nsHttpTransaction *trans = ent->mPendingQ[i];
         386               0 :                 if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
         387               0 :                     pipeline->AddTransaction(trans);
         388                 :
         389                 :                     // remove transaction from pending queue
         390               0 :                     ent->mPendingQ.RemoveElementAt(i);
         391               0 :                     NS_RELEASE(trans);
         392               0 :                     break;
         393                 :                 }
         394                 :             }
         395                 :         }
         396                 :     }
         397               0 : }
         398                 :
         399                 : nsresult
         400            2974 : nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
         401                 : {
         402            2974 :     LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
         403                 :
         404            2974 :     NS_ADDREF(conn);
         405            2974 :     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
         406            2974 :     if (NS_FAILED(rv))
         407               0 :         NS_RELEASE(conn);
         408            2974 :     return rv;
         409                 : }
         410                 :
         411                 : nsresult
         412               0 : nsHttpConnectionMgr::UpdateParam(nsParamName name, PRUint16 value)
         413                 : {
         414               0 :     PRUint32 param = (PRUint32(name) << 16) | PRUint32(value);
         415               0 :     return PostEvent(&nsHttpConnectionMgr::OnMsgUpdateParam, 0, (void *) param);
         416                 : }
         417                 :
         418                 : nsresult
         419               0 : nsHttpConnectionMgr::ProcessPendingQ(nsHttpConnectionInfo *ci)
         420                 : {
         421               0 :     LOG(("nsHttpConnectionMgr::ProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
         422                 :
         423               0 :     NS_ADDREF(ci);
         424               0 :     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgProcessPendingQ, 0, ci);
         425               0 :     if (NS_FAILED(rv))
         426               0 :         NS_RELEASE(ci);
         427               0 :     return rv;
         428                 : }
         429                 :
         430                 : // Given a nsHttpConnectionInfo find the connection entry object that
         431                 : // contains either the nshttpconnection or nshttptransaction parameter.
         432                 : // Normally this is done by the hashkey lookup of connectioninfo,
         433                 : // but if spdy coalescing is in play it might be found in a redirected
         434                 : // entry
         435                 : nsHttpConnectionMgr::nsConnectionEntry *
         436            6102 : nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
         437                 :                                            nsHttpConnection *conn,
         438                 :                                            nsHttpTransaction *trans)
         439                 : {
         440            6102 :     if (!ci)
         441               0 :         return nsnull;
         442                 :
         443            6102 :     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
         444                 :
         445                 :     // If there is no sign of coalescing (or it is disabled) then just
         446                 :     // return the primary hash lookup
         447            6102 :     if (!ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty())
         448            6102 :         return ent;
         449                 :
         450                 :     // If there is no preferred coalescing entry for this host (or the
         451                 :     // preferred entry is the one that matched the mCT hash lookup) then
         452                 :     // there is only option
         453               0 :     nsConnectionEntry *preferred = mSpdyPreferredHash.Get(ent->mCoalescingKey);
         454               0 :     if (!preferred || (preferred == ent))
         455               0 :         return ent;
         456                 :
         457               0 :     if (conn) {
         458                 :         // The connection could be either in preferred or ent. It is most
         459                 :         // likely the only active connection in preferred - so start with that.
         460               0 :         if (preferred->mActiveConns.Contains(conn))
         461               0 :             return preferred;
         462               0 :         if (preferred->mIdleConns.Contains(conn))
         463               0 :             return preferred;
         464                 :     }
         465                 :
         466               0 :     if (trans && preferred->mPendingQ.Contains(trans))
         467               0 :         return preferred;
         468                 :
         469                 :     // Neither conn nor trans found in preferred, use the default entry
         470               0 :     return ent;
         471                 : }
         472                 :
         473                 : nsresult
         474               0 : nsHttpConnectionMgr::CloseIdleConnection(nsHttpConnection *conn)
         475                 : {
         476               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
         477               0 :     LOG(("nsHttpConnectionMgr::CloseIdleConnection %p conn=%p",
         478                 :          this, conn));
         479                 :
         480               0 :     if (!conn->ConnectionInfo())
         481               0 :         return NS_ERROR_UNEXPECTED;
         482                 :
         483                 :     nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
         484               0 :                                                    conn, nsnull);
         485                 :
         486               0 :     if (!ent || !ent->mIdleConns.RemoveElement(conn))
         487               0 :         return NS_ERROR_UNEXPECTED;
         488                 :
         489               0 :     conn->Close(NS_ERROR_ABORT);
         490               0 :     NS_RELEASE(conn);
         491               0 :     mNumIdleConns--;
         492               0 :     ConditionallyStopPruneDeadConnectionsTimer();
         493               0 :     return NS_OK;
         494                 : }
         495                 :
         496                 : // This function lets a connection, after completing the NPN phase,
         497                 : // report whether or not it is using spdy through the usingSpdy
         498                 : // argument. It would not be necessary if NPN were driven out of
         499                 : // the connection manager. The connection entry associated with the
         500                 : // connection is then updated to indicate whether or not we want to use
         501                 : // spdy with that host and update the preliminary preferred host
         502                 : // entries used for de-sharding hostsnames.
         503                 : void
         504            2974 : nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
         505                 :                                           bool usingSpdy)
         506                 : {
         507            2974 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
         508                 :
         509                 :     nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
         510            2974 :                                                    conn, nsnull);
         511                 :
         512            2974 :     NS_ABORT_IF_FALSE(ent, "no connection entry");
         513            2974 :     if (!ent)
         514               0 :         return;
         515                 :
         516            2974 :     ent->mTestedSpdy = true;
         517                 :
         518            2974 :     if (!usingSpdy)
         519            2974 :         return;
         520                 :
         521               0 :     ent->mUsingSpdy = true;
         522                 :
         523               0 :     PRUint32 ttl = conn->TimeToLive();
         524               0 :     PRUint64 timeOfExpire = NowInSeconds() + ttl;
         525               0 :     if (!mTimer || timeOfExpire < mTimeOfNextWakeUp)
         526               0 :         PruneDeadConnectionsAfter(ttl);
         527                 :
         528                 :     // Lookup preferred directly from the hash instead of using
         529                 :     // GetSpdyPreferredEnt() because we want to avoid the cert compatibility
         530                 :     // check at this point because the cert is never part of the hash
         531                 :     // lookup. Filtering on that has to be done at the time of use
         532                 :     // rather than the time of registration (i.e. now).
         533                 :     nsConnectionEntry *preferred =
         534               0 :         mSpdyPreferredHash.Get(ent->mCoalescingKey);
         535                 :
         536               0 :     LOG(("ReportSpdyConnection %s %s ent=%p preferred=%p\n",
         537                 :          ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
         538                 :          ent, preferred));
         539                 :
         540               0 :     if (!preferred) {
         541               0 :         if (!ent->mCoalescingKey.IsEmpty()) {
         542               0 :             mSpdyPreferredHash.Put(ent->mCoalescingKey, ent);
         543               0 :             ent->mSpdyPreferred = true;
         544               0 :             preferred = ent;
         545                 :         }
         546                 :     }
         547               0 :     else if (preferred != ent) {
         548                 :         // A different hostname is the preferred spdy host for this
         549                 :         // IP address. That preferred mapping must have been setup while
         550                 :         // this connection was negotiating NPN.
         551                 :
         552                 :         // Call don't reuse on the current connection to shut it down as soon
         553                 :         // as possible without causing any errors.
         554                 :         // i.e. the current transaction(s) on this connection will be processed
         555                 :         // normally, but then it will go away and future connections will be
         556                 :         // coalesced through the preferred entry.
         557                 :
         558               0 :         conn->DontReuse();
         559                 :     }
         560                 :
         561               0 :     ProcessAllSpdyPendingQ();
         562                 : }
         563                 :
         564                 : bool
         565            5046 : nsHttpConnectionMgr::GetSpdyAlternateProtocol(nsACString &hostPortKey)
         566                 : {
         567            5046 :     if (!gHttpHandler->UseAlternateProtocol())
         568               0 :         return false;
         569                 :
         570                 :     // The Alternate Protocol hash is protected under the monitor because
         571                 :     // it is read from both the main and the network thread.
         572           10092 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
         573                 :
         574            5046 :     return mAlternateProtocolHash.Contains(hostPortKey);
         575                 : }
         576                 :
         577                 : void
         578               0 : nsHttpConnectionMgr::ReportSpdyAlternateProtocol(nsHttpConnection *conn)
         579                 : {
         580                 :     // Check network.http.spdy.use-alternate-protocol pref
         581               0 :     if (!gHttpHandler->UseAlternateProtocol())
         582               0 :         return;
         583                 :
         584                 :     // For now lets not bypass proxies due to the alternate-protocol header
         585               0 :     if (conn->ConnectionInfo()->UsingHttpProxy())
         586               0 :         return;
         587                 :
         588               0 :     nsCString hostPortKey(conn->ConnectionInfo()->Host());
         589               0 :     if (conn->ConnectionInfo()->Port() != 80) {
         590               0 :         hostPortKey.Append(NS_LITERAL_CSTRING(":"));
         591               0 :         hostPortKey.AppendInt(conn->ConnectionInfo()->Port());
         592                 :     }
         593                 :
         594                 :     // The Alternate Protocol hash is protected under the monitor because
         595                 :     // it is read from both the main and the network thread.
         596               0 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
         597                 :
         598                 :     // Check to see if this is already present
         599               0 :     if (mAlternateProtocolHash.Contains(hostPortKey))
         600                 :         return;
         601                 :
         602               0 :     if (mAlternateProtocolHash.Count() > 2000)
         603                 :         mAlternateProtocolHash.EnumerateEntries(&TrimAlternateProtocolHash,
         604               0 :                                                 this);
         605                 :
         606               0 :     mAlternateProtocolHash.PutEntry(hostPortKey);
         607                 : }
         608                 :
         609                 : void
         610               2 : nsHttpConnectionMgr::RemoveSpdyAlternateProtocol(nsACString &hostPortKey)
         611                 : {
         612                 :     // The Alternate Protocol hash is protected under the monitor because
         613                 :     // it is read from both the main and the network thread.
         614               4 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
         615                 :
         616               2 :     return mAlternateProtocolHash.RemoveEntry(hostPortKey);
         617                 : }
         618                 :
         619                 : PLDHashOperator
         620               0 : nsHttpConnectionMgr::TrimAlternateProtocolHash(nsCStringHashKey *entry,
         621                 :                                                void *closure)
         622                 : {
         623               0 :     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
         624                 :
         625               0 :     if (self->mAlternateProtocolHash.Count() > 2000)
         626               0 :         return PL_DHASH_REMOVE;
         627               0 :     return PL_DHASH_STOP;
         628                 : }
         629                 :
         630                 : nsHttpConnectionMgr::nsConnectionEntry *
         631           12254 : nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
         632                 : {
         633           36762 :     if (!gHttpHandler->IsSpdyEnabled() ||
         634           12254 :         !gHttpHandler->CoalesceSpdy() ||
         635           12254 :         aOriginalEntry->mCoalescingKey.IsEmpty())
         636           12218 :         return nsnull;
         637                 :
         638                 :     nsConnectionEntry *preferred =
         639              36 :         mSpdyPreferredHash.Get(aOriginalEntry->mCoalescingKey);
         640                 :
         641                 :     // if there is no redirection no cert validation is required
         642              36 :     if (preferred == aOriginalEntry)
         643               0 :         return aOriginalEntry;
         644                 :
         645                 :     // if there is no preferred host or it is no longer using spdy
         646                 :     // then skip pooling
         647              36 :     if (!preferred || !preferred->mUsingSpdy)
         648              36 :         return nsnull;
         649                 :
         650                 :     // if there is not an active spdy session in this entry then
         651                 :     // we cannot pool because the cert upon activation may not
         652                 :     // be the same as the old one. Active sessions are prohibited
         653                 :     // from changing certs.
         654                 :
         655               0 :     nsHttpConnection *activeSpdy = nsnull;
         656                 :
         657               0 :     for (PRUint32 index = 0; index < preferred->mActiveConns.Length(); ++index) {
         658               0 :         if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
         659               0 :             activeSpdy = preferred->mActiveConns[index];
         660               0 :             break;
         661                 :         }
         662                 :     }
         663                 :
         664               0 :     if (!activeSpdy) {
         665                 :         // remove the preferred status of this entry if it cannot be
         666                 :         // used for pooling.
         667               0 :         preferred->mSpdyPreferred = false;
         668               0 :         RemoveSpdyPreferredEnt(preferred->mCoalescingKey);
         669               0 :         LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
         670                 :              "preferred host mapping %s to %s removed due to inactivity.\n",
         671                 :              aOriginalEntry->mConnInfo->Host(),
         672                 :              preferred->mConnInfo->Host()));
         673                 :
         674               0 :         return nsnull;
         675                 :     }
         676                 :
         677                 :     // Check that the server cert supports redirection
         678                 :     nsresult rv;
         679               0 :     bool isJoined = false;
         680                 :
         681               0 :     nsCOMPtr<nsISupports> securityInfo;
         682               0 :     nsCOMPtr<nsISSLSocketControl> sslSocketControl;
         683               0 :     nsCAutoString negotiatedNPN;
         684                 :
         685               0 :     activeSpdy->GetSecurityInfo(getter_AddRefs(securityInfo));
         686               0 :     if (!securityInfo) {
         687               0 :         NS_WARNING("cannot obtain spdy security info");
         688               0 :         return nsnull;
         689                 :     }
         690                 :
         691               0 :     sslSocketControl = do_QueryInterface(securityInfo, &rv);
         692               0 :     if (NS_FAILED(rv)) {
         693               0 :         NS_WARNING("sslSocketControl QI Failed");
         694               0 :         return nsnull;
         695                 :     }
         696                 :
         697               0 :     rv = sslSocketControl->JoinConnection(NS_LITERAL_CSTRING("spdy/2"),
         698               0 :                                           aOriginalEntry->mConnInfo->GetHost(),
         699                 :                                           aOriginalEntry->mConnInfo->Port(),
         700               0 :                                           &isJoined);
         701                 :
         702               0 :     if (NS_FAILED(rv) || !isJoined) {
         703               0 :         LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
         704                 :              "Host %s cannot be confirmed to be joined "
         705                 :              "with %s connections. rv=%x isJoined=%d",
         706                 :              preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
         707                 :              rv, isJoined));
         708                 :         mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_JOIN,
         709               0 :                                        false);
         710               0 :         return nsnull;
         711                 :     }
         712                 :
         713                 :     // IP pooling confirmed
         714               0 :     LOG(("nsHttpConnectionMgr::GetSpdyPreferredConnection "
         715                 :          "Host %s has cert valid for %s connections, "
         716                 :          "so %s will be coalesced with %s",
         717                 :          preferred->mConnInfo->Host(), aOriginalEntry->mConnInfo->Host(),
         718                 :          aOriginalEntry->mConnInfo->Host(), preferred->mConnInfo->Host()));
         719               0 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_JOIN, true);
         720               0 :     return preferred;
         721                 : }
         722                 :
         723                 : void
         724               0 : nsHttpConnectionMgr::RemoveSpdyPreferredEnt(nsACString &aHashKey)
         725                 : {
         726               0 :     if (aHashKey.IsEmpty())
         727               0 :         return;
         728                 :
         729               0 :     mSpdyPreferredHash.Remove(aHashKey);
         730                 : }
         731                 :
         732                 : //-----------------------------------------------------------------------------
         733                 : // enumeration callbacks
         734                 :
         735                 : PLDHashOperator
         736            3232 : nsHttpConnectionMgr::ProcessOneTransactionCB(const nsACString &key,
         737                 :                                              nsAutoPtr<nsConnectionEntry> &ent,
         738                 :                                              void *closure)
         739                 : {
         740            3232 :     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
         741                 :
         742            3232 :     if (self->ProcessPendingQForEntry(ent))
         743               0 :         return PL_DHASH_STOP;
         744                 :
         745            3232 :     return PL_DHASH_NEXT;
         746                 : }
         747                 :
         748                 : // If the global number of idle connections is preventing the opening of
         749                 : // new connections to a host without idle connections, then
         750                 : // close them regardless of their TTL
         751                 : PLDHashOperator
         752               0 : nsHttpConnectionMgr::PurgeExcessIdleConnectionsCB(const nsACString &key,
         753                 :                                                   nsAutoPtr<nsConnectionEntry> &ent,
         754                 :                                                   void *closure)
         755                 : {
         756               0 :     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
         757                 :
         758               0 :     while (self->mNumIdleConns + self->mNumActiveConns + 1 >= self->mMaxConns) {
         759               0 :         if (!ent->mIdleConns.Length()) {
         760                 :             // There are no idle conns left in this connection entry
         761               0 :             return PL_DHASH_NEXT;
         762                 :         }
         763               0 :         nsHttpConnection *conn = ent->mIdleConns[0];
         764               0 :         ent->mIdleConns.RemoveElementAt(0);
         765               0 :         conn->Close(NS_ERROR_ABORT);
         766               0 :         NS_RELEASE(conn);
         767               0 :         self->mNumIdleConns--;
         768               0 :         self->ConditionallyStopPruneDeadConnectionsTimer();
         769                 :     }
         770               0 :     return PL_DHASH_STOP;
         771                 : }
         772                 :
         773                 : PLDHashOperator
         774               2 : nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key,
         775                 :                                             nsAutoPtr<nsConnectionEntry> &ent,
         776                 :                                             void *closure)
         777                 : {
         778               2 :     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
         779                 :
         780               2 :     LOG(("  pruning [ci=%s]\n", ent->mConnInfo->HashKey().get()));
         781                 :
         782                 :     // Find out how long it will take for next idle connection to not be reusable
         783                 :     // anymore.
         784               2 :     PRUint32 timeToNextExpire = PR_UINT32_MAX;
         785               2 :     PRInt32 count = ent->mIdleConns.Length();
         786               2 :     if (count > 0) {
         787               2 :         for (PRInt32 i=count-1; i>=0; --i) {
         788               1 :             nsHttpConnection *conn = ent->mIdleConns[i];
         789               1 :             if (!conn->CanReuse()) {
         790               1 :                 ent->mIdleConns.RemoveElementAt(i);
         791               1 :                 conn->Close(NS_ERROR_ABORT);
         792               1 :                 NS_RELEASE(conn);
         793               1 :                 self->mNumIdleConns--;
         794                 :             } else {
         795               0 :                 timeToNextExpire = NS_MIN(timeToNextExpire, conn->TimeToLive());
         796                 :             }
         797                 :         }
         798                 :     }
         799                 :
         800               2 :     if (ent->mUsingSpdy) {
         801               0 :         for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index) {
         802               0 :             nsHttpConnection *conn = ent->mActiveConns[index];
         803               0 :             if (conn->UsingSpdy()) {
         804               0 :                 if (!conn->CanReuse()) {
         805                 :                     // marking it dont reuse will create an active tear down if
         806                 :                     // the spdy session is idle.
         807               0 :                     conn->DontReuse();
         808                 :                 }
         809                 :                 else {
         810                 :                     timeToNextExpire = NS_MIN(timeToNextExpire,
         811               0 :                                               conn->TimeToLive());
         812                 :                 }
         813                 :             }
         814                 :         }
         815                 :     }
         816                 :
         817                 :     // If time to next expire found is shorter than time to next wake-up, we need to
         818                 :     // change the time for next wake-up.
         819               2 :     if (timeToNextExpire != PR_UINT32_MAX) {
         820               0 :         PRUint32 now = NowInSeconds();
         821               0 :         PRUint64 timeOfNextExpire = now + timeToNextExpire;
         822                 :         // If pruning of dead connections is not already scheduled to happen
         823                 :         // or time found for next connection to expire is is before
         824                 :         // mTimeOfNextWakeUp, we need to schedule the pruning to happen
         825                 :         // after timeToNextExpire.
         826               0 :         if (!self->mTimer || timeOfNextExpire < self->mTimeOfNextWakeUp) {
         827               0 :             self->PruneDeadConnectionsAfter(timeToNextExpire);
         828                 :         }
         829                 :     } else {
         830               2 :         self->ConditionallyStopPruneDeadConnectionsTimer();
         831                 :     }
         832                 : #ifdef DEBUG
         833               2 :     count = ent->mActiveConns.Length();
         834               2 :     if (count > 0) {
         835               2 :         for (PRInt32 i=count-1; i>=0; --i) {
         836               1 :             nsHttpConnection *conn = ent->mActiveConns[i];
         837               1 :             LOG(("    active conn [%x] with trans [%x]\n", conn, conn->Transaction()));
         838                 :         }
         839                 :     }
         840                 : #endif
         841                 :
         842                 :     // if this entry is empty, then we can remove it.
         843               9 :     if (ent->mIdleConns.Length()   == 0 &&
         844               2 :         ent->mActiveConns.Length() == 0 &&
         845               1 :         ent->mHalfOpens.Length()   == 0 &&
         846               1 :         ent->mPendingQ.Length()    == 0 &&
         847               1 :         ((!ent->mTestedSpdy && !ent->mUsingSpdy) ||
         848               1 :          !gHttpHandler->IsSpdyEnabled() ||
         849               1 :          self->mCT.Count() > 300)) {
         850               0 :         LOG(("    removing empty connection entry\n"));
         851               0 :         return PL_DHASH_REMOVE;
         852                 :     }
         853                 :
         854                 :     // else, use this opportunity to compact our arrays...
         855               2 :     ent->mIdleConns.Compact();
         856               2 :     ent->mActiveConns.Compact();
         857               2 :     ent->mPendingQ.Compact();
         858                 :
         859               2 :     return PL_DHASH_NEXT;
         860                 : }
         861                 :
         862                 : PLDHashOperator
         863             291 : nsHttpConnectionMgr::ShutdownPassCB(const nsACString &key,
         864                 :                                     nsAutoPtr<nsConnectionEntry> &ent,
         865                 :                                     void *closure)
         866                 : {
         867             291 :     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
         868                 :
         869                 :     nsHttpTransaction *trans;
         870                 :     nsHttpConnection *conn;
         871                 :
         872                 :     // close all active connections
         873             582 :     while (ent->mActiveConns.Length()) {
         874               0 :         conn = ent->mActiveConns[0];
         875                 :
         876               0 :         ent->mActiveConns.RemoveElementAt(0);
         877               0 :         self->mNumActiveConns--;
         878                 :
         879               0 :         conn->Close(NS_ERROR_ABORT);
         880               0 :         NS_RELEASE(conn);
         881                 :     }
         882                 :
         883                 :     // close all idle connections
         884             586 :     while (ent->mIdleConns.Length()) {
         885               4 :         conn = ent->mIdleConns[0];
         886                 :
         887               4 :         ent->mIdleConns.RemoveElementAt(0);
         888               4 :         self->mNumIdleConns--;
         889                 :
         890               4 :         conn->Close(NS_ERROR_ABORT);
         891               4 :         NS_RELEASE(conn);
         892                 :     }
         893                 :     // If all idle connections are removed,
         894                 :     // we can stop pruning dead connections.
         895             291 :     self->ConditionallyStopPruneDeadConnectionsTimer();
         896                 :
         897                 :     // close all pending transactions
         898             583 :     while (ent->mPendingQ.Length()) {
         899               1 :         trans = ent->mPendingQ[0];
         900                 :
         901               1 :         ent->mPendingQ.RemoveElementAt(0);
         902                 :
         903               1 :         trans->Close(NS_ERROR_ABORT);
         904               1 :         NS_RELEASE(trans);
         905                 :     }
         906                 :
         907                 :     // close all half open tcp connections
         908             291 :     for (PRInt32 i = ((PRInt32) ent->mHalfOpens.Length()) - 1; i >= 0; i--)
         909               0 :         ent->mHalfOpens[i]->Abandon();
         910                 :
         911             291 :     return PL_DHASH_REMOVE;
         912                 : }
         913                 :
         914                 : //-----------------------------------------------------------------------------
         915                 :
         916                 : bool
         917            6207 : nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
         918                 : {
         919            6207 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
         920            6207 :     LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
         921                 :         ent->mConnInfo->HashKey().get()));
         922                 :
         923            6207 :     ProcessSpdyPendingQ(ent);
         924                 :
         925            6207 :     PRUint32 i, count = ent->mPendingQ.Length();
         926            6207 :     if (count > 0) {
         927              75 :         LOG(("  pending-count=%u\n", count));
         928              75 :         nsHttpTransaction *trans = nsnull;
         929              75 :         nsHttpConnection *conn = nsnull;
         930             166 :         for (i = 0; i < count; ++i) {
         931              91 :             trans = ent->mPendingQ[i];
         932                 :
         933                 :             // When this transaction has already established a half-open
         934                 :             // connection, we want to prevent any duplicate half-open
         935                 :             // connections from being established and bound to this
         936                 :             // transaction. Allow only use of an idle persistent connection
         937                 :             // (if found) for transactions referred by a half-open connection.
         938              91 :             bool alreadyHalfOpen = false;
         939             109 :             for (PRInt32 j = 0; j < ((PRInt32) ent->mHalfOpens.Length()); j++) {
         940             109 :                 if (ent->mHalfOpens[j]->Transaction() == trans) {
         941              91 :                     alreadyHalfOpen = true;
         942              91 :                     break;
         943                 :                 }
         944                 :             }
         945                 :
         946              91 :             GetConnection(ent, trans, alreadyHalfOpen, &conn);
         947              91 :             if (conn)
         948               0 :                 break;
         949                 :
         950              91 :             NS_ABORT_IF_FALSE(count == ent->mPendingQ.Length(),
         951                 :                               "something mutated pending queue from "
         952                 :                               "GetConnection()");
         953                 :         }
         954              75 :         if (conn) {
         955               0 :             LOG(("  dispatching pending transaction...\n"));
         956                 :
         957                 :             // remove pending transaction
         958               0 :             ent->mPendingQ.RemoveElementAt(i);
         959                 :
         960               0 :             nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
         961               0 :             if (NS_SUCCEEDED(rv))
         962               0 :                 NS_RELEASE(trans);
         963                 :             else {
         964               0 :                 LOG(("  DispatchTransaction failed [rv=%x]\n", rv));
         965                 :                 // on failure, just put the transaction back
         966               0 :                 ent->mPendingQ.InsertElementAt(i, trans);
         967                 :                 // might be something wrong with the connection... close it.
         968               0 :                 conn->Close(rv);
         969                 :             }
         970                 :
         971               0 :             NS_RELEASE(conn);
         972               0 :             return true;
         973                 :         }
         974                 :     }
         975            6207 :     return false;
         976                 : }
         977                 :
         978                 : // we're at the active connection limit if any one of the following conditions is true:
         979                 : //  (1) at max-connections
         980                 : //  (2) keep-alive enabled and at max-persistent-connections-per-server/proxy
         981                 : //  (3) keep-alive disabled and at max-connections-per-server
         982                 : bool
         983            2976 : nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 caps)
         984                 : {
         985            2976 :     nsHttpConnectionInfo *ci = ent->mConnInfo;
         986                 :
         987            2976 :     LOG(("nsHttpConnectionMgr::AtActiveConnectionLimit [ci=%s caps=%x]\n",
         988                 :         ci->HashKey().get(), caps));
         989                 :
         990                 :     // update maxconns if potentially limited by the max socket count
         991                 :     // this requires a dynamic reduction in the max socket count to a point
         992                 :     // lower than the max-connections pref.
         993            2976 :     PRUint32 maxSocketCount = gHttpHandler->MaxSocketCount();
         994            2976 :     if (mMaxConns > maxSocketCount) {
         995               0 :         mMaxConns = maxSocketCount;
         996               0 :         LOG(("nsHttpConnectionMgr %p mMaxConns dynamically reduced to %u",
         997                 :              this, mMaxConns));
         998                 :     }
         999                 :
        1000                 :     // If there are more active connections than the global limit, then we're
        1001                 :     // done. Purging idle connections won't get us below it.
        1002            2976 :     if (mNumActiveConns >= mMaxConns) {
        1003               0 :         LOG(("  num active conns == max conns\n"));
        1004               0 :         return true;
        1005                 :     }
        1006                 :
        1007                 :     nsHttpConnection *conn;
        1008            2976 :     PRInt32 i, totalCount, persistCount = 0;
        1009                 :
        1010            2976 :     totalCount = ent->mActiveConns.Length();
        1011                 :
        1012                 :     // count the number of persistent connections
        1013            3272 :     for (i=0; i<totalCount; ++i) {
        1014             296 :         conn = ent->mActiveConns[i];
        1015             296 :         if (conn->IsKeepAlive()) // XXX make sure this is thread-safe
        1016             256 :             persistCount++;
        1017                 :     }
        1018                 :
        1019                 :     // Add in the in-progress tcp connections, we will assume they are
        1020                 :     // keepalive enabled.
        1021            2976 :     totalCount += ent->mHalfOpens.Length();
        1022            2976 :     persistCount += ent->mHalfOpens.Length();
        1023                 :
        1024            2976 :     LOG(("   total=%d, persist=%d\n", totalCount, persistCount));
        1025                 :
        1026                 :     PRUint16 maxConns;
        1027                 :     PRUint16 maxPersistConns;
        1028                 :
        1029            2976 :     if (ci->UsingHttpProxy() && !ci->UsingSSL()) {
        1030              27 :         maxConns = mMaxConnsPerProxy;
        1031              27 :         maxPersistConns = mMaxPersistConnsPerProxy;
        1032                 :     }
        1033                 :     else {
        1034            2949 :         maxConns = mMaxConnsPerHost;
        1035            2949 :         maxPersistConns = mMaxPersistConnsPerHost;
        1036                 :     }
        1037                 :
        1038                 :     // use >= just to be safe
        1039                 :     return (totalCount >= maxConns) || ( (caps & NS_HTTP_ALLOW_KEEPALIVE) &&
        1040            2976 :                                          (persistCount >= maxPersistConns) );
        1041                 : }
        1042                 :
        1043                 : void
        1044               4 : nsHttpConnectionMgr::ClosePersistentConnections(nsConnectionEntry *ent)
        1045                 : {
        1046               4 :     LOG(("nsHttpConnectionMgr::ClosePersistentConnections [ci=%s]\n",
        1047                 :          ent->mConnInfo->HashKey().get()));
        1048               8 :     while (ent->mIdleConns.Length()) {
        1049               0 :         nsHttpConnection *conn = ent->mIdleConns[0];
        1050               0 :         ent->mIdleConns.RemoveElementAt(0);
        1051               0 :         mNumIdleConns--;
        1052               0 :         conn->Close(NS_ERROR_ABORT);
        1053               0 :         NS_RELEASE(conn);
        1054                 :     }
        1055                 :
        1056               4 :     PRInt32 activeCount = ent->mActiveConns.Length();
        1057               5 :     for (PRInt32 i=0; i < activeCount; i++)
        1058               1 :         ent->mActiveConns[i]->DontReuse();
        1059               4 : }
        1060                 :
        1061                 : PLDHashOperator
        1062               4 : nsHttpConnectionMgr::ClosePersistentConnectionsCB(const nsACString &key,
        1063                 :                                                   nsAutoPtr<nsConnectionEntry> &ent,
        1064                 :                                                   void *closure)
        1065                 : {
        1066               4 :     nsHttpConnectionMgr *self = static_cast<nsHttpConnectionMgr *>(closure);
        1067               4 :     self->ClosePersistentConnections(ent);
        1068               4 :     return PL_DHASH_NEXT;
        1069                 : }
        1070                 :
        1071                 : void
        1072            3067 : nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
        1073                 :                                    nsHttpTransaction *trans,
        1074                 :                                    bool onlyReusedConnection,
        1075                 :                                    nsHttpConnection **result)
        1076                 : {
        1077            3067 :     LOG(("nsHttpConnectionMgr::GetConnection [ci=%s caps=%x]\n",
        1078                 :         ent->mConnInfo->HashKey().get(), PRUint32(trans->Caps())));
        1079                 :
        1080                 :     // First, see if an existing connection can be used - either an idle
        1081                 :     // persistent connection or an active spdy session may be reused instead of
        1082                 :     // establishing a new socket. We do not need to check the connection limits
        1083                 :     // yet as they govern the maximum number of open connections and reusing
        1084                 :     // an old connection never increases that.
        1085                 :
        1086            3067 :     *result = nsnull;
        1087                 :
        1088            3067 :     nsHttpConnection *conn = nsnull;
        1089            3067 :     bool addConnToActiveList = true;
        1090                 :
        1091            3067 :     if (trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) {
        1092                 :
        1093                 :         // if willing to use spdy look for an active spdy connections
        1094                 :         // before considering idle http ones.
        1095            3067 :         if (gHttpHandler->IsSpdyEnabled()) {
        1096            3067 :             conn = GetSpdyPreferredConn(ent);
        1097            3067 :             if (conn)
        1098               0 :                 addConnToActiveList = false;
        1099                 :         }
        1100                 :
        1101                 :         // search the idle connection list. Each element in the list
        1102                 :         // has a reference, so if we remove it from the list into a local
        1103                 :         // ptr, that ptr now owns the reference
        1104            6134 :         while (!conn && (ent->mIdleConns.Length() > 0)) {
        1105               0 :             conn = ent->mIdleConns[0];
        1106                 :             // we check if the connection can be reused before even checking if
        1107                 :             // it is a "matching" connection.
        1108               0 :             if (!conn->CanReuse()) {
        1109               0 :                 LOG(("   dropping stale connection: [conn=%x]\n", conn));
        1110               0 :                 conn->Close(NS_ERROR_ABORT);
        1111               0 :                 NS_RELEASE(conn);
        1112                 :             }
        1113                 :             else {
        1114               0 :                 LOG(("   reusing connection [conn=%x]\n", conn));
        1115               0 :                 conn->EndIdleMonitoring();
        1116                 :             }
        1117                 :
        1118               0 :             ent->mIdleConns.RemoveElementAt(0);
        1119               0 :             mNumIdleConns--;
        1120                 :
        1121                 :             // If there are no idle connections left at all, we need to make
        1122                 :             // sure that we are not pruning dead connections anymore.
        1123               0 :             ConditionallyStopPruneDeadConnectionsTimer();
        1124                 :         }
        1125                 :     }
        1126                 :
        1127            3067 :     if (!conn) {
        1128                 :
        1129                 :         // If the onlyReusedConnection parameter is TRUE, then GetConnection()
        1130                 :         // does not create new transports under any circumstances.
        1131            3067 :         if (onlyReusedConnection)
        1132              91 :             return;
        1133                 :
        1134            5972 :         if (gHttpHandler->IsSpdyEnabled() &&
        1135            2976 :             ent->mConnInfo->UsingSSL() &&
        1136               4 :             !ent->mConnInfo->UsingHttpProxy() &&
        1137               4 :             !(trans->Caps() & NS_HTTP_DISALLOW_SPDY) &&
        1138               4 :             (!ent->mTestedSpdy || ent->mUsingSpdy) &&
        1139               8 :             (ent->mHalfOpens.Length() || ent->mActiveConns.Length())) {
        1140               0 :             bool restrictConnection = true;
        1141                 :
        1142                 :             // There is a concern that a host is using a mix of HTTP/1 and SPDY.
        1143                 :             // In that case we don't want to restrict connections just because
        1144                 :             // there is a single active HTTP/1 session in use. Confirm that the
        1145                 :             // restriction is due to a handshake in progress or a live spdy
        1146                 :             // session.
        1147               0 :             if (!ent->mHalfOpens.Length() &&
        1148               0 :                 ent->mUsingSpdy && ent->mActiveConns.Length()) {
        1149               0 :                 bool confirmedRestrict = false;
        1150                 :
        1151               0 :                 for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index) {
        1152               0 :                     nsHttpConnection *conn = ent->mActiveConns[index];
        1153               0 :                     if (!conn->ReportedNPN() || conn->CanDirectlyActivate()) {
        1154               0 :                         confirmedRestrict = true;
        1155               0 :                         break; // confirmed;
        1156                 :                     }
        1157                 :                 }
        1158               0 :                 if (!confirmedRestrict) {
        1159               0 :                     LOG(("nsHttpConnectionMgr spdy connection restriction to "
        1160                 :                          "%s bypassed.\n", ent->mConnInfo->Host()));
        1161               0 :                     restrictConnection = false;
        1162                 :                 }
        1163                 :             }
        1164               0 :             if (restrictConnection)
        1165               0 :                 return;
        1166                 :         }
        1167                 :
        1168                 :         // Check if we need to purge an idle connection. Note that we may have
        1169                 :         // removed one above; if so, this will be a no-op. We do this before
        1170                 :         // checking the active connection limit to catch the case where we do
        1171                 :         // have an idle connection, but the purge timer hasn't fired yet.
        1172                 :         // XXX this just purges a random idle connection.  we should instead
        1173                 :         // enumerate the entire hash table to find the eldest idle connection.
        1174            2976 :         if (mNumIdleConns && mNumIdleConns + mNumActiveConns + 1 >= mMaxConns)
        1175               0 :             mCT.Enumerate(PurgeExcessIdleConnectionsCB, this);
        1176                 :
        1177                 :         // Need to make a new TCP connection. First, we check if we've hit
        1178                 :         // either the maximum connection limit globally or for this particular
        1179                 :         // host or proxy. If we have, we're done.
        1180            2976 :         if (AtActiveConnectionLimit(ent, trans->Caps())) {
        1181               0 :             LOG(("nsHttpConnectionMgr::GetConnection [ci = %s]"
        1182                 :                  "at active connection limit - will queue\n",
        1183                 :                  ent->mConnInfo->HashKey().get()));
        1184               0 :             return;
        1185                 :         }
        1186                 :
        1187            2976 :         LOG(("nsHttpConnectionMgr::GetConnection Open Connection "
        1188                 :              "%s %s ent=%p spdy=%d",
        1189                 :              ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
        1190                 :              ent, ent->mUsingSpdy));
        1191                 :
        1192            2976 :         nsresult rv = CreateTransport(ent, trans);
        1193            2976 :         if (NS_FAILED(rv))
        1194               1 :             trans->Close(rv);
        1195            2976 :         return;
        1196                 :     }
        1197                 :
        1198               0 :     if (addConnToActiveList) {
        1199                 :         // hold an owning ref to this connection
        1200               0 :         ent->mActiveConns.AppendElement(conn);
        1201               0 :         mNumActiveConns++;
        1202                 :     }
        1203                 :
        1204               0 :     NS_ADDREF(conn);
        1205               0 :     *result = conn;
        1206                 : }
        1207                 :
        1208                 : void
        1209            2974 : nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
        1210                 :                                    nsConnectionEntry *ent)
        1211                 : {
        1212            2974 :     NS_ADDREF(conn);
        1213            2974 :     ent->mActiveConns.AppendElement(conn);
        1214            2974 :     mNumActiveConns++;
        1215            2974 :     ActivateTimeoutTick();
        1216            2974 : }
        1217                 :
        1218                 : void
        1219            2975 : nsHttpConnectionMgr::StartedConnect()
        1220                 : {
        1221            2975 :     mNumActiveConns++;
        1222            2975 : }
        1223                 :
        1224                 : void
        1225            2975 : nsHttpConnectionMgr::RecvdConnect()
        1226                 : {
        1227            2975 :     mNumActiveConns--;
        1228            2975 :     ConditionallyStopReadTimeoutTick();
        1229            2975 : }
        1230                 :
        1231                 : nsresult
        1232            2976 : nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
        1233                 :                                      nsHttpTransaction *trans)
        1234                 : {
        1235            2976 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
        1236                 :
        1237            5952 :     nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans);
        1238            2976 :     nsresult rv = sock->SetupPrimaryStreams();
        1239            2976 :     NS_ENSURE_SUCCESS(rv, rv);
        1240                 :
        1241            2975 :     ent->mHalfOpens.AppendElement(sock);
        1242            2975 :     return NS_OK;
        1243                 : }
        1244                 :
        1245                 : nsresult
        1246            2974 : nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
        1247                 :                                          nsHttpTransaction *aTrans,
        1248                 :                                          PRUint8 caps,
        1249                 :                                          nsHttpConnection *conn)
        1250                 : {
        1251            2974 :     LOG(("nsHttpConnectionMgr::DispatchTransaction [ci=%s trans=%x caps=%x conn=%x]\n",
        1252                 :         ent->mConnInfo->HashKey().get(), aTrans, caps, conn));
        1253                 :     nsresult rv;
        1254                 :
        1255            2974 :     PRInt32 priority = aTrans->Priority();
        1256                 :
        1257            2974 :     if (conn->UsingSpdy()) {
        1258               0 :         LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s,"
        1259                 :              "Connection host = %s\n",
        1260                 :              aTrans->ConnectionInfo()->Host(),
        1261                 :              conn->ConnectionInfo()->Host()));
        1262               0 :         rv = conn->Activate(aTrans, caps, priority);
        1263               0 :         NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "SPDY Cannot Fail Dispatch");
        1264               0 :         return rv;
        1265                 :     }
        1266                 :
        1267            2974 :     nsConnectionHandle *handle = new nsConnectionHandle(conn);
        1268            2974 :     if (!handle)
        1269               0 :         return NS_ERROR_OUT_OF_MEMORY;
        1270            2974 :     NS_ADDREF(handle);
        1271                 :
        1272            2974 :     nsHttpPipeline *pipeline = nsnull;
        1273            2974 :     nsAHttpTransaction *trans = aTrans;
        1274                 :
        1275            2974 :     if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
        1276               0 :         LOG(("  looking to build pipeline...\n"));
        1277               0 :         if (BuildPipeline(ent, trans, &pipeline))
        1278               0 :             trans = pipeline;
        1279                 :     }
        1280                 :
        1281                 :     // give the transaction the indirect reference to the connection.
        1282            2974 :     trans->SetConnection(handle);
        1283                 :
        1284            2974 :     rv = conn->Activate(trans, caps, priority);
        1285                 :
        1286            2974 :     if (NS_FAILED(rv)) {
        1287               0 :         LOG(("  conn->Activate failed [rv=%x]\n", rv));
        1288               0 :         ent->mActiveConns.RemoveElement(conn);
        1289               0 :         mNumActiveConns--;
        1290               0 :         ConditionallyStopReadTimeoutTick();
        1291                 :
        1292                 :         // sever back references to connection, and do so without triggering
        1293                 :         // a call to ReclaimConnection ;-)
        1294               0 :         trans->SetConnection(nsnull);
        1295               0 :         NS_RELEASE(handle->mConn);
        1296                 :         // destroy the connection
        1297               0 :         NS_RELEASE(conn);
        1298                 :     }
        1299                 :
        1300                 :     // if we were unable to activate the pipeline, then this will destroy
        1301                 :     // the pipeline, which will cause each the transactions owned by the
        1302                 :     // pipeline to be restarted.
        1303            2974 :     NS_IF_RELEASE(pipeline);
        1304                 :
        1305            2974 :     NS_RELEASE(handle);
        1306            2974 :     return rv;
        1307                 : }
        1308                 :
        1309                 : bool
        1310               0 : nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
        1311                 :                                    nsAHttpTransaction *firstTrans,
        1312                 :                                    nsHttpPipeline **result)
        1313                 : {
        1314               0 :     if (mMaxPipelinedRequests < 2)
        1315               0 :         return false;
        1316                 :
        1317               0 :     nsHttpPipeline *pipeline = nsnull;
        1318                 :     nsHttpTransaction *trans;
        1319                 :
        1320               0 :     PRUint32 i = 0, numAdded = 0;
        1321               0 :     while (i < ent->mPendingQ.Length()) {
        1322               0 :         trans = ent->mPendingQ[i];
        1323               0 :         if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
        1324               0 :             if (numAdded == 0) {
        1325               0 :                 pipeline = new nsHttpPipeline;
        1326               0 :                 if (!pipeline)
        1327               0 :                     return false;
        1328               0 :                 pipeline->AddTransaction(firstTrans);
        1329               0 :                 numAdded = 1;
        1330                 :             }
        1331               0 :             pipeline->AddTransaction(trans);
        1332                 :
        1333                 :             // remove transaction from pending queue
        1334               0 :             ent->mPendingQ.RemoveElementAt(i);
        1335               0 :             NS_RELEASE(trans);
        1336                 :
        1337               0 :             if (++numAdded == mMaxPipelinedRequests)
        1338               0 :                 break;
        1339                 :         }
        1340                 :         else
        1341               0 :             ++i; // skip to next pending transaction
        1342                 :     }
        1343                 :
        1344               0 :     if (numAdded == 0)
        1345               0 :         return false;
        1346                 :
        1347               0 :     LOG(("  pipelined %u transactions\n", numAdded));
        1348               0 :     NS_ADDREF(*result = pipeline);
        1349               0 :     return true;
        1350                 : }
        1351                 :
        1352                 : nsresult
        1353            2976 : nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
        1354                 : {
        1355            2976 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
        1356                 :
        1357                 :     // since "adds" and "cancels" are processed asynchronously and because
        1358                 :     // various events might trigger an "add" directly on the socket thread,
        1359                 :     // we must take care to avoid dispatching a transaction that has already
        1360                 :     // been canceled (see bug 190001).
        1361            2976 :     if (NS_FAILED(trans->Status())) {
        1362               0 :         LOG(("  transaction was canceled... dropping event!\n"));
        1363               0 :         return NS_OK;
        1364                 :     }
        1365                 :
        1366            2976 :     PRUint8 caps = trans->Caps();
        1367            2976 :     nsHttpConnectionInfo *ci = trans->ConnectionInfo();
        1368            2976 :     NS_ASSERTION(ci, "no connection info");
        1369                 :
        1370            2976 :     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
        1371            2976 :     if (!ent) {
        1372             291 :         nsHttpConnectionInfo *clone = ci->Clone();
        1373             291 :         if (!clone)
        1374               0 :             return NS_ERROR_OUT_OF_MEMORY;
        1375             291 :         ent = new nsConnectionEntry(clone);
        1376             291 :         if (!ent)
        1377               0 :             return NS_ERROR_OUT_OF_MEMORY;
        1378             291 :         mCT.Put(ci->HashKey(), ent);
        1379                 :     }
        1380                 :
        1381                 :     // SPDY coalescing of hostnames means we might redirect from this
        1382                 :     // connection entry onto the preferred one.
        1383            2976 :     nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
        1384            2976 :     if (preferredEntry && (preferredEntry != ent)) {
        1385               0 :         LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
        1386                 :              "redirected via coalescing from %s to %s\n", trans,
        1387                 :              ent->mConnInfo->Host(), preferredEntry->mConnInfo->Host()));
        1388                 :
        1389               0 :         ent = preferredEntry;
        1390                 :     }
        1391                 :
        1392                 :     // If we are doing a force reload then close out any existing conns
        1393                 :     // to this host so that changes in DNS, LBs, etc.. are reflected
        1394            2976 :     if (caps & NS_HTTP_CLEAR_KEEPALIVES)
        1395               0 :         ClosePersistentConnections(ent);
        1396                 :
        1397                 :     // Check if the transaction already has a sticky reference to a connection.
        1398                 :     // If so, then we can just use it directly by transferring its reference
        1399                 :     // to the new connection var instead of calling GetConnection() to search
        1400                 :     // for an available one.
        1401                 :
        1402            2976 :     nsAHttpConnection *wrappedConnection = trans->Connection();
        1403                 :     nsHttpConnection  *conn;
        1404            2976 :     conn = wrappedConnection ? wrappedConnection->TakeHttpConnection() : nsnull;
        1405                 :
        1406            2976 :     if (conn) {
        1407               0 :         NS_ASSERTION(caps & NS_HTTP_STICKY_CONNECTION, "unexpected caps");
        1408                 :
        1409               0 :         trans->SetConnection(nsnull);
        1410                 :     }
        1411                 :     else
        1412            2976 :         GetConnection(ent, trans, false, &conn);
        1413                 :
        1414                 :     nsresult rv;
        1415            2976 :     if (!conn) {
        1416            2976 :         LOG(("  adding transaction to pending queue [trans=%x pending-count=%u]\n",
        1417                 :             trans, ent->mPendingQ.Length()+1));
        1418                 :         // put this transaction on the pending queue...
        1419            2976 :         InsertTransactionSorted(ent->mPendingQ, trans);
        1420            2976 :         NS_ADDREF(trans);
        1421            2976 :         rv = NS_OK;
        1422                 :     }
        1423                 :     else {
        1424               0 :         rv = DispatchTransaction(ent, trans, caps, conn);
        1425               0 :         NS_RELEASE(conn);
        1426                 :     }
        1427                 :
        1428            2976 :     return rv;
        1429                 : }
        1430                 :
        1431                 : // This function tries to dispatch the pending spdy transactions on
        1432                 : // the connection entry sent in as an argument. It will do so on the
        1433                 : // active spdy connection either in that same entry or in the
        1434                 : // redirected 'preferred' entry for the same coalescing hash key if
        1435                 : // coalescing is enabled.
        1436                 :
        1437                 : void
        1438            6211 : nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
        1439                 : {
        1440            6211 :     nsHttpConnection *conn = GetSpdyPreferredConn(ent);
        1441            6211 :     if (!conn)
        1442            6211 :         return;
        1443                 :
        1444               0 :     for (PRInt32 index = ent->mPendingQ.Length() - 1;
        1445               0 :          index >= 0 && conn->CanDirectlyActivate();
        1446                 :          --index) {
        1447               0 :         nsHttpTransaction *trans = ent->mPendingQ[index];
        1448                 :
        1449               0 :         if (!(trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
        1450               0 :             trans->Caps() & NS_HTTP_DISALLOW_SPDY)
        1451               0 :             continue;
        1452                 :
        1453               0 :         ent->mPendingQ.RemoveElementAt(index);
        1454                 :
        1455               0 :         nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
        1456               0 :         if (NS_FAILED(rv)) {
        1457                 :             // this cannot happen, but if due to some bug it does then
        1458                 :             // close the transaction
        1459               0 :             NS_ABORT_IF_FALSE(false, "Dispatch SPDY Transaction");
        1460               0 :             LOG(("ProcessSpdyPendingQ Dispatch Transaction failed trans=%p\n",
        1461                 :                     trans));
        1462               0 :             trans->Close(rv);
        1463                 :         }
        1464               0 :         NS_RELEASE(trans);
        1465                 :     }
        1466                 : }
        1467                 :
        1468                 : PLDHashOperator
        1469               0 : nsHttpConnectionMgr::ProcessSpdyPendingQCB(const nsACString &key,
        1470                 :                                            nsAutoPtr<nsConnectionEntry> &ent,
        1471                 :                                            void *closure)
        1472                 : {
        1473               0 :     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
        1474               0 :     self->ProcessSpdyPendingQ(ent);
        1475               0 :     return PL_DHASH_NEXT;
        1476                 : }
        1477                 :
        1478                 : void
        1479               0 : nsHttpConnectionMgr::ProcessAllSpdyPendingQ()
        1480                 : {
        1481               0 :     mCT.Enumerate(ProcessSpdyPendingQCB, this);
        1482               0 : }
        1483                 :
        1484                 : nsHttpConnection *
        1485            9278 : nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
        1486                 : {
        1487            9278 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
        1488            9278 :     NS_ABORT_IF_FALSE(ent, "no connection entry");
        1489                 :
        1490            9278 :     nsConnectionEntry *preferred = GetSpdyPreferredEnt(ent);
        1491                 :
        1492                 :     // this entry is spdy-enabled if it is involved in a redirect
        1493            9278 :     if (preferred)
        1494                 :         // all new connections for this entry will use spdy too
        1495               0 :         ent->mUsingSpdy = true;
        1496                 :     else
        1497            9278 :         preferred = ent;
        1498                 :
        1499            9278 :     nsHttpConnection *conn = nsnull;
        1500                 :
        1501            9278 :     if (preferred->mUsingSpdy) {
        1502               0 :         for (PRUint32 index = 0;
        1503               0 :              index < preferred->mActiveConns.Length();
        1504                 :              ++index) {
        1505               0 :             if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
        1506               0 :                 conn = preferred->mActiveConns[index];
        1507               0 :                 break;
        1508                 :             }
        1509                 :         }
        1510                 :     }
        1511                 :
        1512            9278 :     return conn;
        1513                 : }
        1514                 :
        1515                 : //-----------------------------------------------------------------------------
        1516                 :
        1517                 : void
        1518             678 : nsHttpConnectionMgr::OnMsgShutdown(PRInt32, void *)
        1519                 : {
        1520             678 :     LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
        1521                 :
        1522             678 :     mCT.Enumerate(ShutdownPassCB, this);
        1523                 :
        1524             678 :     if (mReadTimeoutTick) {
        1525             260 :         mReadTimeoutTick->Cancel();
        1526             260 :         mReadTimeoutTick = nsnull;
        1527             260 :         mReadTimeoutTickArmed = false;
        1528                 :     }
        1529                 :
        1530                 :     // signal shutdown complete
        1531            1356 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
        1532             678 :     mon.Notify();
        1533             678 : }
        1534                 :
        1535                 : void
        1536            2976 : nsHttpConnectionMgr::OnMsgNewTransaction(PRInt32 priority, void *param)
        1537                 : {
        1538            2976 :     LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
        1539                 :
        1540            2976 :     nsHttpTransaction *trans = (nsHttpTransaction *) param;
        1541            2976 :     trans->SetPriority(priority);
        1542            2976 :     nsresult rv = ProcessNewTransaction(trans);
        1543            2976 :     if (NS_FAILED(rv))
        1544               0 :         trans->Close(rv); // for whatever its worth
        1545            2976 :     NS_RELEASE(trans);
        1546            2976 : }
        1547                 :
        1548                 : void
        1549               0 : nsHttpConnectionMgr::OnMsgReschedTransaction(PRInt32 priority, void *param)
        1550                 : {
        1551               0 :     LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
        1552                 :
        1553               0 :     nsHttpTransaction *trans = (nsHttpTransaction *) param;
        1554               0 :     trans->SetPriority(priority);
        1555                 :
        1556                 :     nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
        1557               0 :                                                    nsnull, trans);
        1558                 :
        1559               0 :     if (ent) {
        1560               0 :         PRInt32 index = ent->mPendingQ.IndexOf(trans);
        1561               0 :         if (index >= 0) {
        1562               0 :             ent->mPendingQ.RemoveElementAt(index);
        1563               0 :             InsertTransactionSorted(ent->mPendingQ, trans);
        1564                 :         }
        1565                 :     }
        1566                 :
        1567               0 :     NS_RELEASE(trans);
        1568               0 : }
        1569                 :
        1570                 : void
        1571             162 : nsHttpConnectionMgr::OnMsgCancelTransaction(PRInt32 reason, void *param)
        1572                 : {
        1573             162 :     LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
        1574                 :
        1575             162 :     nsHttpTransaction *trans = (nsHttpTransaction *) param;
        1576                 :     //
        1577                 :     // if the transaction owns a connection and the transaction is not done,
        1578                 :     // then ask the connection to close the transaction.  otherwise, close the
        1579                 :     // transaction directly (removing it from the pending queue first).
        1580                 :     //
        1581             162 :     nsAHttpConnection *conn = trans->Connection();
        1582             162 :     if (conn && !trans->IsDone())
        1583               9 :         conn->CloseTransaction(trans, reason);
        1584                 :     else {
        1585                 :         nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
        1586             153 :                                                        nsnull, trans);
        1587                 :
        1588             153 :         if (ent) {
        1589             153 :             PRInt32 index = ent->mPendingQ.IndexOf(trans);
        1590             153 :             if (index >= 0) {
        1591               1 :                 ent->mPendingQ.RemoveElementAt(index);
        1592               1 :                 nsHttpTransaction *temp = trans;
        1593               1 :                 NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
        1594                 :             }
        1595                 :         }
        1596             153 :         trans->Close(reason);
        1597                 :     }
        1598             162 :     NS_RELEASE(trans);
        1599             162 : }
        1600                 :
        1601                 : void
        1602            2975 : nsHttpConnectionMgr::OnMsgProcessPendingQ(PRInt32, void *param)
        1603                 : {
        1604            2975 :     nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param;
        1605                 :
        1606            2975 :     LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
        1607                 :
        1608                 :     // start by processing the queue identified by the given connection info.
        1609            2975 :     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
        1610            2975 :     if (!(ent && ProcessPendingQForEntry(ent))) {
        1611                 :         // if we reach here, it means that we couldn't dispatch a transaction
        1612                 :         // for the specified connection info.  walk the connection table...
        1613            2975 :         mCT.Enumerate(ProcessOneTransactionCB, this);
        1614                 :     }
        1615                 :
        1616            2975 :     NS_RELEASE(ci);
        1617            2975 : }
        1618                 :
        1619                 : void
        1620             125 : nsHttpConnectionMgr::OnMsgPruneDeadConnections(PRInt32, void *)
        1621                 : {
        1622             125 :     LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
        1623                 :
        1624                 :     // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
        1625             125 :     mTimeOfNextWakeUp = LL_MAXUINT;
        1626                 :
        1627                 :     // check canreuse() for all idle connections plus any active connections on
        1628                 :     // connection entries that are using spdy.
        1629             125 :     if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
        1630               2 :         mCT.Enumerate(PruneDeadConnectionsCB, this);
        1631             125 : }
        1632                 :
        1633                 : void
        1634             123 : nsHttpConnectionMgr::OnMsgClosePersistentConnections(PRInt32, void *)
        1635                 : {
        1636             123 :     LOG(("nsHttpConnectionMgr::OnMsgClosePersistentConnections\n"));
        1637                 :
        1638             123 :     mCT.Enumerate(ClosePersistentConnectionsCB, this);
        1639             123 : }
        1640                 :
        1641                 : void
        1642            2975 : nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
        1643                 : {
        1644            2975 :     LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
        1645                 :
        1646            2975 :     nsHttpConnection *conn = (nsHttpConnection *) param;
        1647                 :
        1648                 :     //
        1649                 :     // 1) remove the connection from the active list
        1650                 :     // 2) if keep-alive, add connection to idle list
        1651                 :     // 3) post event to process the pending transaction queue
        1652                 :     //
        1653                 :
        1654                 :     nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
        1655            2975 :                                                    conn, nsnull);
        1656            2975 :     nsHttpConnectionInfo *ci = nsnull;
        1657                 :
        1658            2975 :     if (!ent) {
        1659                 :         // this should never happen
        1660               0 :         LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection ent == null\n"));
        1661               0 :         NS_ABORT_IF_FALSE(false, "no connection entry");
        1662               0 :         NS_ADDREF(ci = conn->ConnectionInfo());
        1663                 :     }
        1664                 :     else {
        1665            2975 :         NS_ADDREF(ci = ent->mConnInfo);
        1666                 :
        1667                 :         // If the connection is in the active list, remove that entry
        1668                 :         // and the reference held by the mActiveConns list.
        1669                 :         // This is never the final reference on conn as the event context
        1670                 :         // is also holding one that is released at the end of this function.
        1671                 :
        1672            2975 :         if (ent->mUsingSpdy) {
        1673                 :             // Spdy connections aren't reused in the traditional HTTP way in
        1674                 :             // the idleconns list, they are actively multplexed as active
        1675                 :             // conns. Even when they have 0 transactions on them they are
        1676                 :             // considered active connections. So when one is reclaimed it
        1677                 :             // is really complete and is meant to be shut down and not
        1678                 :             // reused.
        1679               0 :             conn->DontReuse();
        1680                 :         }
        1681                 :
        1682            2975 :         if (ent->mActiveConns.RemoveElement(conn)) {
        1683            2974 :             nsHttpConnection *temp = conn;
        1684            2974 :             NS_RELEASE(temp);
        1685            2974 :             mNumActiveConns--;
        1686            2974 :             ConditionallyStopReadTimeoutTick();
        1687                 :         }
        1688                 :
        1689            2975 :         if (conn->CanReuse()) {
        1690               5 :             LOG(("  adding connection to idle list\n"));
        1691                 :             // Keep The idle connection list sorted with the connections that
        1692                 :             // have moved the largest data pipelines at the front because these
        1693                 :             // connections have the largest cwnds on the server.
        1694                 :
        1695                 :             // The linear search is ok here because the number of idleconns
        1696                 :             // in a single entry is generally limited to a small number (i.e. 6)
        1697                 :
        1698                 :             PRUint32 idx;
        1699               5 :             for (idx = 0; idx < ent->mIdleConns.Length(); idx++) {
        1700               0 :                 nsHttpConnection *idleConn = ent->mIdleConns[idx];
        1701               0 :                 if (idleConn->MaxBytesRead() < conn->MaxBytesRead())
        1702               0 :                     break;
        1703                 :             }
        1704                 :
        1705               5 :             NS_ADDREF(conn);
        1706               5 :             ent->mIdleConns.InsertElementAt(idx, conn);
        1707               5 :             mNumIdleConns++;
        1708               5 :             conn->BeginIdleMonitoring();
        1709                 :
        1710                 :             // If the added connection was first idle connection or has shortest
        1711                 :             // time to live among the watched connections, pruning dead
        1712                 :             // connections needs to be done when it can't be reused anymore.
        1713               5 :             PRUint32 timeToLive = conn->TimeToLive();
        1714               5 :             if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
        1715               5 :                 PruneDeadConnectionsAfter(timeToLive);
        1716                 :         }
        1717                 :         else {
        1718            2970 :             LOG(("  connection cannot be reused; closing connection\n"));
        1719                 :             // make sure the connection is closed and release our reference.
        1720            2970 :             conn->Close(NS_ERROR_ABORT);
        1721                 :         }
        1722                 :     }
        1723                 :
        1724            2975 :     OnMsgProcessPendingQ(NS_OK, ci); // releases |ci|
        1725            2975 :     NS_RELEASE(conn);
        1726            2975 : }
        1727                 :
        1728                 : void
        1729               0 : nsHttpConnectionMgr::OnMsgUpdateParam(PRInt32, void *param)
        1730                 : {
        1731               0 :     PRUint16 name  = (NS_PTR_TO_INT32(param) & 0xFFFF0000) >> 16;
        1732               0 :     PRUint16 value =  NS_PTR_TO_INT32(param) & 0x0000FFFF;
        1733                 :
        1734               0 :     switch (name) {
        1735                 :     case MAX_CONNECTIONS:
        1736               0 :         mMaxConns = value;
        1737               0 :         break;
        1738                 :     case MAX_CONNECTIONS_PER_HOST:
        1739               0 :         mMaxConnsPerHost = value;
        1740               0 :         break;
        1741                 :     case MAX_CONNECTIONS_PER_PROXY:
        1742               0 :         mMaxConnsPerProxy = value;
        1743               0 :         break;
        1744                 :     case MAX_PERSISTENT_CONNECTIONS_PER_HOST:
        1745               0 :         mMaxPersistConnsPerHost = value;
        1746               0 :         break;
        1747                 :     case MAX_PERSISTENT_CONNECTIONS_PER_PROXY:
        1748               0 :         mMaxPersistConnsPerProxy = value;
        1749               0 :         break;
        1750                 :     case MAX_REQUEST_DELAY:
        1751               0 :         mMaxRequestDelay = value;
        1752               0 :         break;
        1753                 :     case MAX_PIPELINED_REQUESTS:
        1754               0 :         mMaxPipelinedRequests = value;
        1755               0 :         break;
        1756                 :     default:
        1757               0 :         NS_NOTREACHED("unexpected parameter name");
        1758                 :     }
        1759               0 : }
        1760                 :
        1761                 : // nsHttpConnectionMgr::nsConnectionEntry
        1762             582 : nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
        1763                 : {
        1764             291 :     if (mSpdyPreferred)
        1765               0 :         gHttpHandler->ConnMgr()->RemoveSpdyPreferredEnt(mCoalescingKey);
        1766                 :
        1767             291 :     NS_RELEASE(mConnInfo);
        1768             291 : }
        1769                 :
        1770                 : // Read Timeout Tick handlers
        1771                 :
        1772                 : void
        1773            2974 : nsHttpConnectionMgr::ActivateTimeoutTick()
        1774                 : {
        1775            2974 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
        1776            2974 :     LOG(("nsHttpConnectionMgr::ActivateTimeoutTick() "
        1777                 :          "this=%p mReadTimeoutTick=%p\n"));
        1778                 :
        1779                 :     // right now the spdy timeout code is the only thing hooked to the timeout
        1780                 :     // tick, so disable it if spdy is not being used. However pipelining code
        1781                 :     // will also want this functionality soon.
        1782            2974 :     if (!gHttpHandler->IsSpdyEnabled())
        1783               0 :         return;
        1784                 :
        1785                 :     // The timer tick should be enabled if it is not already pending.
        1786                 :     // Upon running the tick will rearm itself if there are active
        1787                 :     // connections available.
        1788                 :
        1789            2974 :     if (mReadTimeoutTick && mReadTimeoutTickArmed)
        1790             252 :         return;
        1791                 :
        1792            2722 :     if (!mReadTimeoutTick) {
        1793             260 :         mReadTimeoutTick = do_CreateInstance(NS_TIMER_CONTRACTID);
        1794             260 :         if (!mReadTimeoutTick) {
        1795               0 :             NS_WARNING("failed to create timer for http timeout management");
        1796               0 :             return;
        1797                 :         }
        1798             260 :         mReadTimeoutTick->SetTarget(mSocketThreadTarget);
        1799                 :     }
        1800                 :
        1801            2722 :     NS_ABORT_IF_FALSE(!mReadTimeoutTickArmed, "timer tick armed");
        1802            2722 :     mReadTimeoutTickArmed = true;
        1803                 :     // pipeline will expect a 1000ms granuality
        1804            2722 :     mReadTimeoutTick->Init(this, 15000, nsITimer::TYPE_REPEATING_SLACK);
        1805                 : }
        1806                 :
        1807                 : void
        1808               0 : nsHttpConnectionMgr::ReadTimeoutTick()
        1809                 : {
        1810               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
        1811               0 :     NS_ABORT_IF_FALSE(mReadTimeoutTick, "no readtimeout tick");
        1812                 :
        1813               0 :     LOG(("nsHttpConnectionMgr::ReadTimeoutTick active=%d\n",
        1814                 :          mNumActiveConns));
        1815                 :
        1816               0 :     mCT.Enumerate(ReadTimeoutTickCB, this);
        1817               0 : }
        1818                 :
        1819                 : PLDHashOperator
        1820               0 : nsHttpConnectionMgr::ReadTimeoutTickCB(const nsACString &key,
        1821                 :                                        nsAutoPtr<nsConnectionEntry> &ent,
        1822                 :                                        void *closure)
        1823                 : {
        1824               0 :     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
        1825                 :
        1826               0 :     LOG(("nsHttpConnectionMgr::ReadTimeoutTickCB() this=%p host=%s\n",
        1827                 :          self, ent->mConnInfo->Host()));
        1828                 :
        1829               0 :     PRIntervalTime now = PR_IntervalNow();
        1830               0 :     for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index)
        1831               0 :         ent->mActiveConns[index]->ReadTimeoutTick(now);
        1832                 :
        1833               0 :     return PL_DHASH_NEXT;
        1834                 : }
        1835                 :
        1836                 : //-----------------------------------------------------------------------------
        1837                 : // nsHttpConnectionMgr::nsConnectionHandle
        1838                 :
        1839            5948 : nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
        1840                 : {
        1841            2974 :     if (mConn) {
        1842            2974 :         gHttpHandler->ReclaimConnection(mConn);
        1843            2974 :         NS_RELEASE(mConn);
        1844                 :     }
        1845           11896 : }
        1846                 :
        1847           11964 : NS_IMPL_THREADSAFE_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
        1848                 :
        1849                 : nsresult
        1850            2818 : nsHttpConnectionMgr::nsConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
        1851                 :                                                             nsHttpRequestHead *req,
        1852                 :                                                             nsHttpResponseHead *resp,
        1853                 :                                                             bool *reset)
        1854                 : {
        1855            2818 :     return mConn->OnHeadersAvailable(trans, req, resp, reset);
        1856                 : }
        1857                 :
        1858                 : nsresult
        1859               0 : nsHttpConnectionMgr::nsConnectionHandle::ResumeSend()
        1860                 : {
        1861               0 :     return mConn->ResumeSend();
        1862                 : }
        1863                 :
        1864                 : nsresult
        1865               0 : nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv()
        1866                 : {
        1867               0 :     return mConn->ResumeRecv();
        1868                 : }
        1869                 :
        1870                 : void
        1871               9 : nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
        1872                 : {
        1873               9 :     mConn->CloseTransaction(trans, reason);
        1874               9 : }
        1875                 :
        1876                 : void
        1877               0 : nsHttpConnectionMgr::nsConnectionHandle::GetConnectionInfo(nsHttpConnectionInfo **result)
        1878                 : {
        1879               0 :     mConn->GetConnectionInfo(result);
        1880               0 : }
        1881                 :
        1882                 : nsresult
        1883               0 : nsHttpConnectionMgr::
        1884                 : nsConnectionHandle::TakeTransport(nsISocketTransport  **aTransport,
        1885                 :                                   nsIAsyncInputStream **aInputStream,
        1886                 :                                   nsIAsyncOutputStream **aOutputStream)
        1887                 : {
        1888               0 :     return mConn->TakeTransport(aTransport, aInputStream, aOutputStream);
        1889                 : }
        1890                 :
        1891                 : void
        1892            2974 : nsHttpConnectionMgr::nsConnectionHandle::GetSecurityInfo(nsISupports **result)
        1893                 : {
        1894            2974 :     mConn->GetSecurityInfo(result);
        1895            2974 : }
        1896                 :
        1897                 : bool
        1898            5540 : nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
        1899                 : {
        1900            5540 :     return mConn->IsPersistent();
        1901                 : }
        1902                 :
        1903                 : bool
        1904            2974 : nsHttpConnectionMgr::nsConnectionHandle::IsReused()
        1905                 : {
        1906            2974 :     return mConn->IsReused();
        1907                 : }
        1908                 :
        1909                 : nsresult
        1910               0 : nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufLen)
        1911                 : {
        1912               0 :     return mConn->PushBack(buf, bufLen);
        1913                 : }
        1914                 :
        1915                 :
        1916                 : //////////////////////// nsHalfOpenSocket
        1917                 :
        1918                 :
        1919           88971 : NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpConnectionMgr::nsHalfOpenSocket,
        1920                 :                               nsIOutputStreamCallback,
        1921                 :                               nsITransportEventSink,
        1922                 :                               nsIInterfaceRequestor,
        1923                 :                               nsITimerCallback)
        1924                 :
        1925            2976 : nsHttpConnectionMgr::
        1926                 : nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
        1927                 :                                    nsHttpTransaction *trans)
        1928                 :     : mEnt(ent),
        1929            2976 :       mTransaction(trans)
        1930                 : {
        1931            2976 :     NS_ABORT_IF_FALSE(ent && trans, "constructor with null arguments");
        1932            2976 :     LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s]\n",
        1933                 :          this, trans, ent->mConnInfo->Host()));
        1934            2976 : }
        1935                 :
        1936            5952 : nsHttpConnectionMgr::nsHalfOpenSocket::~nsHalfOpenSocket()
        1937                 : {
        1938            2976 :     NS_ABORT_IF_FALSE(!mStreamOut, "streamout not null");
        1939            2976 :     NS_ABORT_IF_FALSE(!mBackupStreamOut, "backupstreamout not null");
        1940            2976 :     NS_ABORT_IF_FALSE(!mSynTimer, "syntimer not null");
        1941            2976 :     LOG(("Destroying nsHalfOpenSocket [this=%p]\n", this));
        1942                 :
        1943            2976 :     if (mEnt) {
        1944                 :         // A failure to create the transport object at all
        1945                 :         // will result in this not being present in the halfopen table
        1946                 :         // so ignore failures of RemoveElement()
        1947            2976 :         mEnt->mHalfOpens.RemoveElement(this);
        1948                 :     }
        1949            2976 : }
        1950                 :
        1951                 : nsresult
        1952            2976 : nsHttpConnectionMgr::
        1953                 : nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
        1954                 :                                nsIAsyncInputStream **instream,
        1955                 :                                nsIAsyncOutputStream **outstream,
        1956                 :                                bool isBackup)
        1957                 : {
        1958                 :     nsresult rv;
        1959                 :
        1960                 :     const char* types[1];
        1961            2976 :     types[0] = (mEnt->mConnInfo->UsingSSL()) ?
        1962            2976 :         "ssl" : gHttpHandler->DefaultSocketType();
        1963            2976 :     PRUint32 typeCount = (types[0] != nsnull);
        1964                 :
        1965            5952 :     nsCOMPtr<nsISocketTransport> socketTransport;
        1966            5952 :     nsCOMPtr<nsISocketTransportService> sts;
        1967                 :
        1968            2976 :     sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
        1969            2976 :     NS_ENSURE_SUCCESS(rv, rv);
        1970                 :
        1971            2976 :     rv = sts->CreateTransport(types, typeCount,
        1972            2976 :                               nsDependentCString(mEnt->mConnInfo->Host()),
        1973                 :                               mEnt->mConnInfo->Port(),
        1974            2976 :                               mEnt->mConnInfo->ProxyInfo(),
        1975            8928 :                               getter_AddRefs(socketTransport));
        1976            2976 :     NS_ENSURE_SUCCESS(rv, rv);
        1977                 :
        1978            2975 :     PRUint32 tmpFlags = 0;
        1979            2975 :     if (mTransaction->Caps() & NS_HTTP_REFRESH_DNS)
        1980            1922 :         tmpFlags = nsISocketTransport::BYPASS_CACHE;
        1981                 :
        1982            2975 :     if (mTransaction->Caps() & NS_HTTP_LOAD_ANONYMOUS)
        1983               1 :         tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
        1984                 :
        1985                 :     // For backup connections, we disable IPv6. That's because some users have
        1986                 :     // broken IPv6 connectivity (leading to very long timeouts), and disabling
        1987                 :     // IPv6 on the backup connection gives them a much better user experience
        1988                 :     // with dual-stack hosts, though they still pay the 250ms delay for each new
        1989                 :     // connection. This strategy is also known as "happy eyeballs".
        1990            2975 :     if (isBackup && gHttpHandler->FastFallbackToIPv4())
        1991               0 :         tmpFlags |= nsISocketTransport::DISABLE_IPV6;
        1992                 :
        1993            2975 :     socketTransport->SetConnectionFlags(tmpFlags);
        1994                 :
        1995            2975 :     socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
        1996                 :
        1997            2975 :     rv = socketTransport->SetEventSink(this, nsnull);
        1998            2975 :     NS_ENSURE_SUCCESS(rv, rv);
        1999                 :
        2000            2975 :     rv = socketTransport->SetSecurityCallbacks(this);
        2001            2975 :     NS_ENSURE_SUCCESS(rv, rv);
        2002                 :
        2003            5950 :     nsCOMPtr<nsIOutputStream> sout;
        2004            2975 :     rv = socketTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,
        2005                 :                                             0, 0,
        2006            2975 :                                             getter_AddRefs(sout));
        2007            2975 :     NS_ENSURE_SUCCESS(rv, rv);
        2008                 :
        2009            5950 :     nsCOMPtr<nsIInputStream> sin;
        2010            2975 :     rv = socketTransport->OpenInputStream(nsITransport::OPEN_UNBUFFERED,
        2011                 :                                            0, 0,
        2012            2975 :                                            getter_AddRefs(sin));
        2013            2975 :     NS_ENSURE_SUCCESS(rv, rv);
        2014                 :
        2015            2975 :     socketTransport.forget(transport);
        2016            2975 :     CallQueryInterface(sin, instream);
        2017            2975 :     CallQueryInterface(sout, outstream);
        2018                 :
        2019            2975 :     rv = (*outstream)->AsyncWait(this, 0, 0, nsnull);
        2020            2975 :     if (NS_SUCCEEDED(rv))
        2021            2975 :         gHttpHandler->ConnMgr()->StartedConnect();
        2022                 :
        2023            2975 :     return rv;
        2024                 : }
        2025                 :
        2026                 : nsresult
        2027            2976 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams()
        2028                 : {
        2029            2976 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
        2030                 :
        2031                 :     nsresult rv;
        2032                 :
        2033            2976 :     rv = SetupStreams(getter_AddRefs(mSocketTransport),
        2034            2976 :                       getter_AddRefs(mStreamIn),
        2035            2976 :                       getter_AddRefs(mStreamOut),
        2036            2976 :                       false);
        2037            2976 :     LOG(("nsHalfOpenSocket::SetupPrimaryStream [this=%p ent=%s rv=%x]",
        2038                 :          this, mEnt->mConnInfo->Host(), rv));
        2039            2976 :     if (NS_FAILED(rv)) {
        2040               1 :         if (mStreamOut)
        2041               0 :             mStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
        2042               1 :         mStreamOut = nsnull;
        2043               1 :         mStreamIn = nsnull;
        2044               1 :         mSocketTransport = nsnull;
        2045                 :     }
        2046            2976 :     return rv;
        2047                 : }
        2048                 :
        2049                 : nsresult
        2050               0 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams()
        2051                 : {
        2052               0 :     nsresult rv = SetupStreams(getter_AddRefs(mBackupTransport),
        2053               0 :                                getter_AddRefs(mBackupStreamIn),
        2054               0 :                                getter_AddRefs(mBackupStreamOut),
        2055               0 :                                true);
        2056               0 :     LOG(("nsHalfOpenSocket::SetupBackupStream [this=%p ent=%s rv=%x]",
        2057                 :          this, mEnt->mConnInfo->Host(), rv));
        2058               0 :     if (NS_FAILED(rv)) {
        2059               0 :         if (mBackupStreamOut)
        2060               0 :             mBackupStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
        2061               0 :         mBackupStreamOut = nsnull;
        2062               0 :         mBackupStreamIn = nsnull;
        2063               0 :         mBackupTransport = nsnull;
        2064                 :     }
        2065               0 :     return rv;
        2066                 : }
        2067                 :
        2068                 : void
        2069            2968 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
        2070                 : {
        2071            2968 :     PRUint16 timeout = gHttpHandler->GetIdleSynTimeout();
        2072            2968 :     NS_ABORT_IF_FALSE(!mSynTimer, "timer already initd");
        2073                 :
        2074            2968 :     if (timeout) {
        2075                 :         // Setup the timer that will establish a backup socket
        2076                 :         // if we do not get a writable event on the main one.
        2077                 :         // We do this because a lost SYN takes a very long time
        2078                 :         // to repair at the TCP level.
        2079                 :         //
        2080                 :         // Failure to setup the timer is something we can live with,
        2081                 :         // so don't return an error in that case.
        2082                 :         nsresult rv;
        2083            2968 :         mSynTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
        2084            2968 :         if (NS_SUCCEEDED(rv)) {
        2085            2968 :             mSynTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
        2086            2968 :             LOG(("nsHalfOpenSocket::SetupBackupTimer()"));
        2087                 :         }
        2088                 :     }
        2089            2968 : }
        2090                 :
        2091                 : void
        2092            5809 : nsHttpConnectionMgr::nsHalfOpenSocket::CancelBackupTimer()
        2093                 : {
        2094                 :     // If the syntimer is still armed, we can cancel it because no backup
        2095                 :     // socket should be formed at this point
        2096            5809 :     if (!mSynTimer)
        2097            2841 :         return;
        2098                 :
        2099            2968 :     LOG(("nsHalfOpenSocket::CancelBackupTimer()"));
        2100            2968 :     mSynTimer->Cancel();
        2101            2968 :     mSynTimer = nsnull;
        2102                 : }
        2103                 :
        2104                 : void
        2105               0 : nsHttpConnectionMgr::nsHalfOpenSocket::Abandon()
        2106                 : {
        2107               0 :     LOG(("nsHalfOpenSocket::Abandon [this=%p ent=%s]",
        2108                 :          this, mEnt->mConnInfo->Host()));
        2109                 :
        2110               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
        2111                 :
        2112               0 :     nsRefPtr<nsHalfOpenSocket> deleteProtector(this);
        2113                 :
        2114               0 :     if (mStreamOut) {
        2115               0 :         gHttpHandler->ConnMgr()->RecvdConnect();
        2116               0 :         mStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
        2117               0 :         mStreamOut = nsnull;
        2118                 :     }
        2119               0 :     if (mBackupStreamOut) {
        2120               0 :         gHttpHandler->ConnMgr()->RecvdConnect();
        2121               0 :         mBackupStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
        2122               0 :         mBackupStreamOut = nsnull;
        2123                 :     }
        2124                 :
        2125               0 :     CancelBackupTimer();
        2126                 :
        2127               0 :     mEnt = nsnull;
        2128               0 : }
        2129                 :
        2130                 : NS_IMETHODIMP // method for nsITimerCallback
        2131               0 : nsHttpConnectionMgr::nsHalfOpenSocket::Notify(nsITimer *timer)
        2132                 : {
        2133               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
        2134               0 :     NS_ABORT_IF_FALSE(timer == mSynTimer, "wrong timer");
        2135                 :
        2136               0 :     if (!gHttpHandler->ConnMgr()->
        2137               0 :         AtActiveConnectionLimit(mEnt, mTransaction->Caps())) {
        2138               0 :         SetupBackupStreams();
        2139                 :     }
        2140                 :
        2141               0 :     mSynTimer = nsnull;
        2142               0 :     return NS_OK;
        2143                 : }
        2144                 :
        2145                 : // method for nsIAsyncOutputStreamCallback
        2146                 : NS_IMETHODIMP
        2147            2975 : nsHttpConnectionMgr::
        2148                 : nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
        2149                 : {
        2150            2975 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
        2151            2975 :     NS_ABORT_IF_FALSE(out == mStreamOut ||
        2152                 :                       out == mBackupStreamOut, "stream mismatch");
        2153            2975 :     LOG(("nsHalfOpenSocket::OnOutputStreamReady [this=%p ent=%s %s]\n",
        2154                 :          this, mEnt->mConnInfo->Host(),
        2155                 :          out == mStreamOut ? "primary" : "backup"));
        2156                 :     PRInt32 index;
        2157                 :     nsresult rv;
        2158                 :
        2159            2975 :     gHttpHandler->ConnMgr()->RecvdConnect();
        2160                 :
        2161            2975 :     CancelBackupTimer();
        2162                 :
        2163                 :     // assign the new socket to the http connection
        2164            5950 :     nsRefPtr<nsHttpConnection> conn = new nsHttpConnection();
        2165            2975 :     LOG(("nsHalfOpenSocket::OnOutputStreamReady "
        2166                 :          "Created new nshttpconnection %p\n", conn.get()));
        2167                 :
        2168            5950 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
        2169            5950 :     nsCOMPtr<nsIEventTarget>        callbackTarget;
        2170            5950 :     mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks),
        2171            5950 :                                        getter_AddRefs(callbackTarget));
        2172            2975 :     if (out == mStreamOut) {
        2173                 :         rv = conn->Init(mEnt->mConnInfo,
        2174            2975 :                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
        2175                 :                         mSocketTransport, mStreamIn, mStreamOut,
        2176            5950 :                         callbacks, callbackTarget);
        2177                 :
        2178                 :         // The nsHttpConnection object now owns these streams and sockets
        2179            2975 :         mStreamOut = nsnull;
        2180            2975 :         mStreamIn = nsnull;
        2181            2975 :         mSocketTransport = nsnull;
        2182                 :     }
        2183                 :     else {
        2184                 :         rv = conn->Init(mEnt->mConnInfo,
        2185               0 :                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
        2186                 :                         mBackupTransport, mBackupStreamIn, mBackupStreamOut,
        2187               0 :                         callbacks, callbackTarget);
        2188                 :
        2189                 :         // The nsHttpConnection object now owns these streams and sockets
        2190               0 :         mBackupStreamOut = nsnull;
        2191               0 :         mBackupStreamIn = nsnull;
        2192               0 :         mBackupTransport = nsnull;
        2193                 :     }
        2194                 :
        2195            2975 :     if (NS_FAILED(rv)) {
        2196               0 :         LOG(("nsHalfOpenSocket::OnOutputStreamReady "
        2197                 :              "conn->init (%p) failed %x\n", conn.get(), rv));
        2198               0 :         return rv;
        2199                 :     }
        2200                 :
        2201                 :     // if this is still in the pending list, remove it and dispatch it
        2202            2975 :     index = mEnt->mPendingQ.IndexOf(mTransaction);
        2203            2975 :     if (index != -1) {
        2204            2974 :         mEnt->mPendingQ.RemoveElementAt(index);
        2205            2974 :         nsHttpTransaction *temp = mTransaction;
        2206            2974 :         NS_RELEASE(temp);
        2207            2974 :         gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
        2208                 :         rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, mTransaction,
        2209            2974 :                                                           mTransaction->Caps(),
        2210            5948 :                                                           conn);
        2211                 :     }
        2212                 :     else {
        2213                 :         // this transaction was dispatched off the pending q before all the
        2214                 :         // sockets established themselves.
        2215                 :
        2216                 :         // We need to establish a small non-zero idle timeout so the connection
        2217                 :         // mgr perceives this socket as suitable for persistent connection reuse
        2218               1 :         const PRIntervalTime k5Sec = PR_SecondsToInterval(5);
        2219               1 :         if (k5Sec < gHttpHandler->IdleTimeout())
        2220               1 :             conn->SetIdleTimeout(k5Sec);
        2221                 :         else
        2222               0 :             conn->SetIdleTimeout(gHttpHandler->IdleTimeout());
        2223                 :
        2224                 :         // After about 1 second allow for the possibility of restarting a
        2225                 :         // transaction due to server close. Keep at sub 1 second as that is the
        2226                 :         // minimum granularity we can expect a server to be timing out with.
        2227               1 :         conn->SetIsReusedAfter(950);
        2228                 :
        2229               2 :         nsRefPtr<nsHttpConnection> copy(conn);  // because onmsg*() expects to drop a reference
        2230               1 :         gHttpHandler->ConnMgr()->OnMsgReclaimConnection(NS_OK, conn.forget().get());
        2231                 :     }
        2232                 :
        2233            2975 :     return rv;
        2234                 : }
        2235                 :
        2236                 : // method for nsITransportEventSink
        2237                 : NS_IMETHODIMP
        2238           11752 : nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
        2239                 :                                                          nsresult status,
        2240                 :                                                          PRUint64 progress,
        2241                 :                                                          PRUint64 progressMax)
        2242                 : {
        2243           11752 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
        2244                 :
        2245           11752 :     if (mTransaction)
        2246           11752 :         mTransaction->OnTransportStatus(trans, status, progress);
        2247                 :
        2248           11752 :     if (trans != mSocketTransport)
        2249               0 :         return NS_OK;
        2250                 :
        2251                 :     // if we are doing spdy coalescing and haven't recorded the ip address
        2252                 :     // for this entry before then make the hash key if our dns lookup
        2253                 :     // just completed
        2254                 :
        2255           20262 :     if (status == nsISocketTransport::STATUS_CONNECTED_TO &&
        2256            2834 :         gHttpHandler->IsSpdyEnabled() &&
        2257            2834 :         gHttpHandler->CoalesceSpdy() &&
        2258            2834 :         mEnt && mEnt->mConnInfo && mEnt->mConnInfo->UsingSSL() &&
        2259               4 :         !mEnt->mConnInfo->UsingHttpProxy() &&
        2260               4 :         mEnt->mCoalescingKey.IsEmpty()) {
        2261                 :
        2262                 :         PRNetAddr addr;
        2263               4 :         nsresult rv = mSocketTransport->GetPeerAddr(&addr);
        2264               4 :         if (NS_SUCCEEDED(rv)) {
        2265               4 :             mEnt->mCoalescingKey.SetCapacity(72);
        2266               4 :             PR_NetAddrToString(&addr, mEnt->mCoalescingKey.BeginWriting(), 64);
        2267                 :             mEnt->mCoalescingKey.SetLength(
        2268               4 :                 strlen(mEnt->mCoalescingKey.BeginReading()));
        2269                 :
        2270               4 :             if (mEnt->mConnInfo->GetAnonymous())
        2271               0 :                 mEnt->mCoalescingKey.AppendLiteral("~A:");
        2272                 :             else
        2273               4 :                 mEnt->mCoalescingKey.AppendLiteral("~.:");
        2274               4 :             mEnt->mCoalescingKey.AppendInt(mEnt->mConnInfo->Port());
        2275                 :
        2276               4 :             LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus "
        2277                 :                  "STATUS_CONNECTED_TO Established New Coalescing Key for host "
        2278                 :                  "%s [%s]", mEnt->mConnInfo->Host(),
        2279                 :                  mEnt->mCoalescingKey.get()));
        2280                 :
        2281               4 :             gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(mEnt);
        2282                 :         }
        2283                 :     }
        2284                 :
        2285           11752 :     switch (status) {
        2286                 :     case nsISocketTransport::STATUS_CONNECTING_TO:
        2287                 :         // Passed DNS resolution, now trying to connect, start the backup timer
        2288                 :         // only prevent creating another backup transport.
        2289                 :         // We also check for mEnt presence to not instantiate the timer after
        2290                 :         // this half open socket has already been abandoned.  It may happen
        2291                 :         // when we get this notification right between main-thread calls to
        2292                 :         // nsHttpConnectionMgr::Shutdown and nsSocketTransportService::Shutdown
        2293                 :         // where the first abandones all half open socket instances and only
        2294                 :         // after that the second stops the socket thread.
        2295            2968 :         if (mEnt && !mBackupTransport && !mSynTimer)
        2296            2968 :             SetupBackupTimer();
        2297            2968 :         break;
        2298                 :
        2299                 :     case nsISocketTransport::STATUS_CONNECTED_TO:
        2300                 :         // TCP connection's up, now transfer or SSL negotiantion starts,
        2301                 :         // no need for backup socket
        2302            2834 :         CancelBackupTimer();
        2303            2834 :         break;
        2304                 :
        2305                 :     default:
        2306            5950 :         break;
        2307                 :     }
        2308                 :
        2309           11752 :     return NS_OK;
        2310                 : }
        2311                 :
        2312                 : // method for nsIInterfaceRequestor
        2313                 : NS_IMETHODIMP
        2314               0 : nsHttpConnectionMgr::nsHalfOpenSocket::GetInterface(const nsIID &iid,
        2315                 :                                                     void **result)
        2316                 : {
        2317               0 :     if (mTransaction) {
        2318               0 :         nsCOMPtr<nsIInterfaceRequestor> callbacks;
        2319               0 :         mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks), nsnull);
        2320               0 :         if (callbacks)
        2321               0 :             return callbacks->GetInterface(iid, result);
        2322                 :     }
        2323               0 :     return NS_ERROR_NO_INTERFACE;
        2324                 : }
        2325                 :
        2326                 :
        2327                 : nsHttpConnection *
        2328               0 : nsHttpConnectionMgr::nsConnectionHandle::TakeHttpConnection()
        2329                 : {
        2330                 :     // return our connection object to the caller and clear it internally
        2331                 :     // do not drop our reference - the caller now owns it.
        2332                 :
        2333               0 :     NS_ASSERTION(mConn, "no connection");
        2334               0 :     nsHttpConnection *conn = mConn;
        2335               0 :     mConn = nsnull;
        2336               0 :     return conn;
        2337                 : }
        2338                 :
        2339                 : bool
        2340            2829 : nsHttpConnectionMgr::nsConnectionHandle::LastTransactionExpectedNoContent()
        2341                 : {
        2342            2829 :     return mConn->LastTransactionExpectedNoContent();
        2343                 : }
        2344                 :
        2345                 : void
        2346            2818 : nsHttpConnectionMgr::
        2347                 : nsConnectionHandle::SetLastTransactionExpectedNoContent(bool val)
        2348                 : {
        2349            2818 :      mConn->SetLastTransactionExpectedNoContent(val);
        2350            2818 : }
        2351                 :
        2352                 : nsISocketTransport *
        2353               0 : nsHttpConnectionMgr::nsConnectionHandle::Transport()
        2354                 : {
        2355               0 :     if (!mConn)
        2356               0 :         return nsnull;
        2357               0 :     return mConn->Transport();
        2358                 : }

Generated by: LCOV version 1.7