LCOV - code coverage report
Current view: directory - netwerk/protocol/http - nsHttpConnectionMgr.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1050 607 57.8 %
Date: 2012-04-21 Functions: 101 73 72.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           20954 : NS_IMPL_THREADSAFE_ISUPPORTS1(nsHttpConnectionMgr, nsIObserver)
      66                 : 
      67                 : static void
      68            2990 : 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            2990 :     for (PRInt32 i=pendingQ.Length()-1; i>=0; --i) {
      75               9 :         nsHttpTransaction *t = pendingQ[i];
      76               9 :         if (trans->Priority() >= t->Priority()) {
      77               9 :             pendingQ.InsertElementAt(i+1, trans);
      78               9 :             return;
      79                 :         }
      80                 :     }
      81            2981 :     pendingQ.InsertElementAt(0, trans);
      82                 : }
      83                 : 
      84                 : //-----------------------------------------------------------------------------
      85                 : 
      86             673 : 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             673 :     , mReadTimeoutTickArmed(false)
      99                 : {
     100             673 :     LOG(("Creating nsHttpConnectionMgr @%x\n", this));
     101             673 :     mCT.Init();
     102             673 :     mAlternateProtocolHash.Init(16);
     103             673 :     mSpdyPreferredHash.Init();
     104             673 : }
     105                 : 
     106            2013 : nsHttpConnectionMgr::~nsHttpConnectionMgr()
     107                 : {
     108             671 :     LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
     109             671 :     if (mReadTimeoutTick)
     110               0 :         mReadTimeoutTick->Cancel();
     111            2684 : }
     112                 : 
     113                 : nsresult
     114            7745 : nsHttpConnectionMgr::EnsureSocketThreadTargetIfOnline()
     115                 : {
     116                 :     nsresult rv;
     117           15490 :     nsCOMPtr<nsIEventTarget> sts;
     118           15490 :     nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
     119            7745 :     if (NS_SUCCEEDED(rv)) {
     120            7745 :         bool offline = true;
     121            7745 :         ioService->GetOffline(&offline);
     122                 : 
     123            7745 :         if (!offline) {
     124            7731 :             sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
     125                 :         }
     126                 :     }
     127                 : 
     128           15490 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     129                 : 
     130                 :     // do nothing if already initialized or if we've shut down
     131            7745 :     if (mSocketThreadTarget || mIsShuttingDown)
     132            7071 :         return NS_OK;
     133                 : 
     134             674 :     mSocketThreadTarget = sts;
     135                 : 
     136             674 :     return rv;
     137                 : }
     138                 : 
     139                 : nsresult
     140             673 : nsHttpConnectionMgr::Init(PRUint16 maxConns,
     141                 :                           PRUint16 maxConnsPerHost,
     142                 :                           PRUint16 maxConnsPerProxy,
     143                 :                           PRUint16 maxPersistConnsPerHost,
     144                 :                           PRUint16 maxPersistConnsPerProxy,
     145                 :                           PRUint16 maxRequestDelay,
     146                 :                           PRUint16 maxPipelinedRequests)
     147                 : {
     148             673 :     LOG(("nsHttpConnectionMgr::Init\n"));
     149                 : 
     150                 :     {
     151            1346 :         ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     152                 : 
     153             673 :         mMaxConns = maxConns;
     154             673 :         mMaxConnsPerHost = maxConnsPerHost;
     155             673 :         mMaxConnsPerProxy = maxConnsPerProxy;
     156             673 :         mMaxPersistConnsPerHost = maxPersistConnsPerHost;
     157             673 :         mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
     158             673 :         mMaxRequestDelay = maxRequestDelay;
     159             673 :         mMaxPipelinedRequests = maxPipelinedRequests;
     160                 : 
     161             673 :         mIsShuttingDown = false;
     162                 :     }
     163                 : 
     164             673 :     return EnsureSocketThreadTargetIfOnline();
     165                 : }
     166                 : 
     167                 : nsresult
     168            1873 : nsHttpConnectionMgr::Shutdown()
     169                 : {
     170            1873 :     LOG(("nsHttpConnectionMgr::Shutdown\n"));
     171                 : 
     172            3746 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     173                 : 
     174                 :     // do nothing if already shutdown
     175            1873 :     if (!mSocketThreadTarget)
     176            1201 :         return NS_OK;
     177                 : 
     178             672 :     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             672 :     mIsShuttingDown = true;
     184             672 :     mSocketThreadTarget = 0;
     185                 : 
     186             672 :     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             672 :     mon.Wait();
     193             672 :     return NS_OK;
     194                 : }
     195                 : 
     196                 : nsresult
     197            7072 : 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            7072 :     EnsureSocketThreadTargetIfOnline();
     204                 : 
     205           14144 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     206                 : 
     207                 :     nsresult rv;
     208            7072 :     if (!mSocketThreadTarget) {
     209              12 :         NS_WARNING("cannot post event if not initialized");
     210              12 :         rv = NS_ERROR_NOT_INITIALIZED;
     211                 :     }
     212                 :     else {
     213           14120 :         nsRefPtr<nsIRunnable> event = new nsConnEvent(this, handler, iparam, vparam);
     214            7060 :         if (!event)
     215               0 :             rv = NS_ERROR_OUT_OF_MEMORY;
     216                 :         else
     217            7060 :             rv = mSocketThreadTarget->Dispatch(event, NS_DISPATCH_NORMAL);
     218                 :     }
     219            7072 :     return rv;
     220                 : }
     221                 : 
     222                 : void
     223               4 : nsHttpConnectionMgr::PruneDeadConnectionsAfter(PRUint32 timeInSeconds)
     224                 : {
     225               4 :     LOG(("nsHttpConnectionMgr::PruneDeadConnectionsAfter\n"));
     226                 : 
     227               4 :     if(!mTimer)
     228               4 :         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               4 :     if (mTimer) {
     233               4 :         mTimeOfNextWakeUp = timeInSeconds + NowInSeconds();
     234               4 :         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               4 : }
     239                 : 
     240                 : void
     241             292 : nsHttpConnectionMgr::ConditionallyStopPruneDeadConnectionsTimer()
     242                 : {
     243                 :     // Leave the timer in place if there are connections that potentially
     244                 :     // need management
     245             292 :     if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
     246               7 :         return;
     247                 : 
     248             285 :     LOG(("nsHttpConnectionMgr::StopPruneDeadConnectionsTimer\n"));
     249                 : 
     250                 :     // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
     251             285 :     mTimeOfNextWakeUp = LL_MAXUINT;
     252             285 :     if (mTimer) {
     253               4 :         mTimer->Cancel();
     254               4 :         mTimer = NULL;
     255                 :     }
     256                 : }
     257                 : 
     258                 : void
     259            5977 : nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick()
     260                 : {
     261            5977 :     LOG(("nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick "
     262                 :          "armed=%d active=%d\n", mReadTimeoutTickArmed, mNumActiveConns));
     263                 : 
     264            5977 :     if (!mReadTimeoutTickArmed)
     265            2741 :         return;
     266                 : 
     267            3236 :     if (mNumActiveConns)
     268             496 :         return;
     269                 : 
     270            2740 :     LOG(("nsHttpConnectionMgr::ConditionallyStopReadTimeoutTick stop==true\n"));
     271                 : 
     272            2740 :     mReadTimeoutTick->Cancel();
     273            2740 :     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               0 :             PruneDeadConnections();
     291                 :         }
     292               1 :         else if (timer == mReadTimeoutTick) {
     293               1 :             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            3002 : nsHttpConnectionMgr::AddTransaction(nsHttpTransaction *trans, PRInt32 priority)
     310                 : {
     311            3002 :     LOG(("nsHttpConnectionMgr::AddTransaction [trans=%x %d]\n", trans, priority));
     312                 : 
     313            3002 :     NS_ADDREF(trans);
     314            3002 :     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgNewTransaction, priority, trans);
     315            3002 :     if (NS_FAILED(rv))
     316              12 :         NS_RELEASE(trans);
     317            3002 :     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             163 : nsHttpConnectionMgr::CancelTransaction(nsHttpTransaction *trans, nsresult reason)
     334                 : {
     335             163 :     LOG(("nsHttpConnectionMgr::CancelTransaction [trans=%x reason=%x]\n", trans, reason));
     336                 : 
     337             163 :     NS_ADDREF(trans);
     338             163 :     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgCancelTransaction, reason, trans);
     339             163 :     if (NS_FAILED(rv))
     340               0 :         NS_RELEASE(trans);
     341             163 :     return rv;
     342                 : }
     343                 : 
     344                 : nsresult
     345             124 : nsHttpConnectionMgr::PruneDeadConnections()
     346                 : {
     347             124 :     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            2988 : nsHttpConnectionMgr::ReclaimConnection(nsHttpConnection *conn)
     401                 : {
     402            2988 :     LOG(("nsHttpConnectionMgr::ReclaimConnection [conn=%x]\n", conn));
     403                 : 
     404            2988 :     NS_ADDREF(conn);
     405            2988 :     nsresult rv = PostEvent(&nsHttpConnectionMgr::OnMsgReclaimConnection, 0, conn);
     406            2988 :     if (NS_FAILED(rv))
     407               0 :         NS_RELEASE(conn);
     408            2988 :     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            6130 : nsHttpConnectionMgr::LookupConnectionEntry(nsHttpConnectionInfo *ci,
     437                 :                                            nsHttpConnection *conn,
     438                 :                                            nsHttpTransaction *trans)
     439                 : {
     440            6130 :     if (!ci)
     441               0 :         return nsnull;
     442                 : 
     443            6130 :     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            6130 :     if (!ent || !ent->mUsingSpdy || ent->mCoalescingKey.IsEmpty())
     448            6130 :         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            2988 : nsHttpConnectionMgr::ReportSpdyConnection(nsHttpConnection *conn,
     505                 :                                           bool usingSpdy)
     506                 : {
     507            2988 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     508                 :     
     509                 :     nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
     510            2988 :                                                    conn, nsnull);
     511                 : 
     512            2988 :     NS_ABORT_IF_FALSE(ent, "no connection entry");
     513            2988 :     if (!ent)
     514               0 :         return;
     515                 : 
     516            2988 :     ent->mTestedSpdy = true;
     517                 : 
     518            2988 :     if (!usingSpdy)
     519            2988 :         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            5060 : nsHttpConnectionMgr::GetSpdyAlternateProtocol(nsACString &hostPortKey)
     566                 : {
     567            5060 :     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           10120 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     573                 : 
     574            5060 :     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           12219 : nsHttpConnectionMgr::GetSpdyPreferredEnt(nsConnectionEntry *aOriginalEntry)
     632                 : {
     633           36657 :     if (!gHttpHandler->IsSpdyEnabled() ||
     634           12219 :         !gHttpHandler->CoalesceSpdy() ||
     635           12219 :         aOriginalEntry->mCoalescingKey.IsEmpty())
     636           12183 :         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            3246 : nsHttpConnectionMgr::ProcessOneTransactionCB(const nsACString &key,
     737                 :                                              nsAutoPtr<nsConnectionEntry> &ent,
     738                 :                                              void *closure)
     739                 : {
     740            3246 :     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
     741                 : 
     742            3246 :     if (self->ProcessPendingQForEntry(ent))
     743               0 :         return PL_DHASH_STOP;
     744                 : 
     745            3246 :     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               1 : nsHttpConnectionMgr::PruneDeadConnectionsCB(const nsACString &key,
     775                 :                                             nsAutoPtr<nsConnectionEntry> &ent,
     776                 :                                             void *closure)
     777                 : {
     778               1 :     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
     779                 : 
     780               1 :     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               1 :     PRUint32 timeToNextExpire = PR_UINT32_MAX;
     785               1 :     PRInt32 count = ent->mIdleConns.Length();
     786               1 :     if (count > 0) {
     787               0 :         for (PRInt32 i=count-1; i>=0; --i) {
     788               0 :             nsHttpConnection *conn = ent->mIdleConns[i];
     789               0 :             if (!conn->CanReuse()) {
     790               0 :                 ent->mIdleConns.RemoveElementAt(i);
     791               0 :                 conn->Close(NS_ERROR_ABORT);
     792               0 :                 NS_RELEASE(conn);
     793               0 :                 self->mNumIdleConns--;
     794                 :             } else {
     795               0 :                 timeToNextExpire = NS_MIN(timeToNextExpire, conn->TimeToLive());
     796                 :             }
     797                 :         }
     798                 :     }
     799                 : 
     800               1 :     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               1 :     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               1 :         self->ConditionallyStopPruneDeadConnectionsTimer();
     831                 :     }
     832                 : #ifdef DEBUG
     833               1 :     count = ent->mActiveConns.Length();
     834               1 :     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               2 :     if (ent->mIdleConns.Length()   == 0 &&
     844               1 :         ent->mActiveConns.Length() == 0 &&
     845               0 :         ent->mHalfOpens.Length()   == 0 &&
     846               0 :         ent->mPendingQ.Length()    == 0 &&
     847               0 :         ((!ent->mTestedSpdy && !ent->mUsingSpdy) ||
     848               0 :          !gHttpHandler->IsSpdyEnabled() ||
     849               0 :          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               1 :     ent->mIdleConns.Compact();
     856               1 :     ent->mActiveConns.Compact();
     857               1 :     ent->mPendingQ.Compact();
     858                 : 
     859               1 :     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            6235 : nsHttpConnectionMgr::ProcessPendingQForEntry(nsConnectionEntry *ent)
     918                 : {
     919            6235 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     920            6235 :     LOG(("nsHttpConnectionMgr::ProcessPendingQForEntry [ci=%s]\n",
     921                 :         ent->mConnInfo->HashKey().get()));
     922                 : 
     923            6235 :     ProcessSpdyPendingQ(ent);
     924                 : 
     925            6235 :     PRUint32 i, count = ent->mPendingQ.Length();
     926            6235 :     if (count > 0) {
     927               0 :         LOG(("  pending-count=%u\n", count));
     928               0 :         nsHttpTransaction *trans = nsnull;
     929               0 :         nsHttpConnection *conn = nsnull;
     930               0 :         for (i = 0; i < count; ++i) {
     931               0 :             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               0 :             bool alreadyHalfOpen = false;
     939               0 :             for (PRInt32 j = 0; j < ((PRInt32) ent->mHalfOpens.Length()); j++) {
     940               0 :                 if (ent->mHalfOpens[j]->Transaction() == trans) {
     941               0 :                     alreadyHalfOpen = true;
     942               0 :                     break;
     943                 :                 }
     944                 :             }
     945                 : 
     946               0 :             GetConnection(ent, trans, alreadyHalfOpen, &conn);
     947               0 :             if (conn)
     948               0 :                 break;
     949                 : 
     950               0 :             NS_ABORT_IF_FALSE(count == ent->mPendingQ.Length(),
     951                 :                               "something mutated pending queue from "
     952                 :                               "GetConnection()");
     953                 :         }
     954               0 :         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            6235 :     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            2990 : nsHttpConnectionMgr::AtActiveConnectionLimit(nsConnectionEntry *ent, PRUint8 caps)
     984                 : {
     985            2990 :     nsHttpConnectionInfo *ci = ent->mConnInfo;
     986                 : 
     987            2990 :     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            2990 :     PRUint32 maxSocketCount = gHttpHandler->MaxSocketCount();
     994            2990 :     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            2990 :     if (mNumActiveConns >= mMaxConns) {
    1003               0 :         LOG(("  num active conns == max conns\n"));
    1004               0 :         return true;
    1005                 :     }
    1006                 : 
    1007                 :     nsHttpConnection *conn;
    1008            2990 :     PRInt32 i, totalCount, persistCount = 0;
    1009                 :     
    1010            2990 :     totalCount = ent->mActiveConns.Length();
    1011                 : 
    1012                 :     // count the number of persistent connections
    1013            3420 :     for (i=0; i<totalCount; ++i) {
    1014             430 :         conn = ent->mActiveConns[i];
    1015             430 :         if (conn->IsKeepAlive()) // XXX make sure this is thread-safe
    1016             407 :             persistCount++;
    1017                 :     }
    1018                 : 
    1019                 :     // Add in the in-progress tcp connections, we will assume they are
    1020                 :     // keepalive enabled.
    1021            2990 :     totalCount += ent->mHalfOpens.Length();
    1022            2990 :     persistCount += ent->mHalfOpens.Length();
    1023                 :     
    1024            2990 :     LOG(("   total=%d, persist=%d\n", totalCount, persistCount));
    1025                 : 
    1026                 :     PRUint16 maxConns;
    1027                 :     PRUint16 maxPersistConns;
    1028                 : 
    1029            2990 :     if (ci->UsingHttpProxy() && !ci->UsingSSL()) {
    1030              27 :         maxConns = mMaxConnsPerProxy;
    1031              27 :         maxPersistConns = mMaxPersistConnsPerProxy;
    1032                 :     }
    1033                 :     else {
    1034            2963 :         maxConns = mMaxConnsPerHost;
    1035            2963 :         maxPersistConns = mMaxPersistConnsPerHost;
    1036                 :     }
    1037                 : 
    1038                 :     // use >= just to be safe
    1039                 :     return (totalCount >= maxConns) || ( (caps & NS_HTTP_ALLOW_KEEPALIVE) &&
    1040            2990 :                                          (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            2990 : nsHttpConnectionMgr::GetConnection(nsConnectionEntry *ent,
    1073                 :                                    nsHttpTransaction *trans,
    1074                 :                                    bool onlyReusedConnection,
    1075                 :                                    nsHttpConnection **result)
    1076                 : {
    1077            2990 :     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            2990 :     *result = nsnull;
    1087                 : 
    1088            2990 :     nsHttpConnection *conn = nsnull;
    1089            2990 :     bool addConnToActiveList = true;
    1090                 : 
    1091            2990 :     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            2990 :         if (gHttpHandler->IsSpdyEnabled()) {
    1096            2990 :             conn = GetSpdyPreferredConn(ent);
    1097            2990 :             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            5980 :         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            2990 :     if (!conn) {
    1128                 : 
    1129                 :         // If the onlyReusedConnection parameter is TRUE, then GetConnection()
    1130                 :         // does not create new transports under any circumstances.
    1131            2990 :         if (onlyReusedConnection)
    1132               0 :             return;
    1133                 :         
    1134            5984 :         if (gHttpHandler->IsSpdyEnabled() &&
    1135            2990 :             ent->mConnInfo->UsingSSL() &&
    1136               4 :             !ent->mConnInfo->UsingHttpProxy())
    1137                 :         {
    1138                 :             // If this host is trying to negotiate a SPDY session right now,
    1139                 :             // don't create any new connections until the result of the
    1140                 :             // negotiation is known.
    1141                 :     
    1142              12 :             if ((!ent->mTestedSpdy || ent->mUsingSpdy) &&
    1143               8 :                 (ent->mHalfOpens.Length() || ent->mActiveConns.Length()))
    1144               0 :                 return;
    1145                 :         }
    1146                 :         
    1147                 :         // Check if we need to purge an idle connection. Note that we may have
    1148                 :         // removed one above; if so, this will be a no-op. We do this before
    1149                 :         // checking the active connection limit to catch the case where we do
    1150                 :         // have an idle connection, but the purge timer hasn't fired yet.
    1151                 :         // XXX this just purges a random idle connection.  we should instead
    1152                 :         // enumerate the entire hash table to find the eldest idle connection.
    1153            2990 :         if (mNumIdleConns && mNumIdleConns + mNumActiveConns + 1 >= mMaxConns)
    1154               0 :             mCT.Enumerate(PurgeExcessIdleConnectionsCB, this);
    1155                 : 
    1156                 :         // Need to make a new TCP connection. First, we check if we've hit
    1157                 :         // either the maximum connection limit globally or for this particular
    1158                 :         // host or proxy. If we have, we're done.
    1159            2990 :         if (AtActiveConnectionLimit(ent, trans->Caps())) {
    1160               0 :             LOG(("nsHttpConnectionMgr::GetConnection [ci = %s]"
    1161                 :                  "at active connection limit - will queue\n",
    1162                 :                  ent->mConnInfo->HashKey().get()));
    1163               0 :             return;
    1164                 :         }
    1165                 : 
    1166            2990 :         LOG(("nsHttpConnectionMgr::GetConnection Open Connection "
    1167                 :              "%s %s ent=%p spdy=%d",
    1168                 :              ent->mConnInfo->Host(), ent->mCoalescingKey.get(),
    1169                 :              ent, ent->mUsingSpdy));
    1170                 :         
    1171            2990 :         nsresult rv = CreateTransport(ent, trans);
    1172            2990 :         if (NS_FAILED(rv))
    1173               1 :             trans->Close(rv);
    1174            2990 :         return;
    1175                 :     }
    1176                 : 
    1177               0 :     if (addConnToActiveList) {
    1178                 :         // hold an owning ref to this connection
    1179               0 :         ent->mActiveConns.AppendElement(conn);
    1180               0 :         mNumActiveConns++;
    1181                 :     }
    1182                 :     
    1183               0 :     NS_ADDREF(conn);
    1184               0 :     *result = conn;
    1185                 : }
    1186                 : 
    1187                 : void
    1188            2988 : nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
    1189                 :                                    nsConnectionEntry *ent)
    1190                 : {
    1191            2988 :     NS_ADDREF(conn);
    1192            2988 :     ent->mActiveConns.AppendElement(conn);
    1193            2988 :     mNumActiveConns++;
    1194            2988 :     ActivateTimeoutTick();
    1195            2988 : }
    1196                 : 
    1197                 : void
    1198            2989 : nsHttpConnectionMgr::StartedConnect()
    1199                 : {
    1200            2989 :     mNumActiveConns++;
    1201            2989 : }
    1202                 : 
    1203                 : void
    1204            2989 : nsHttpConnectionMgr::RecvdConnect()
    1205                 : {
    1206            2989 :     mNumActiveConns--;
    1207            2989 :     ConditionallyStopReadTimeoutTick();
    1208            2989 : }
    1209                 : 
    1210                 : nsresult
    1211            2990 : nsHttpConnectionMgr::CreateTransport(nsConnectionEntry *ent,
    1212                 :                                      nsHttpTransaction *trans)
    1213                 : {
    1214            2990 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1215                 : 
    1216            5980 :     nsRefPtr<nsHalfOpenSocket> sock = new nsHalfOpenSocket(ent, trans);
    1217            2990 :     nsresult rv = sock->SetupPrimaryStreams();
    1218            2990 :     NS_ENSURE_SUCCESS(rv, rv);
    1219                 : 
    1220            2989 :     ent->mHalfOpens.AppendElement(sock);
    1221            2989 :     return NS_OK;
    1222                 : }
    1223                 : 
    1224                 : nsresult
    1225            2988 : nsHttpConnectionMgr::DispatchTransaction(nsConnectionEntry *ent,
    1226                 :                                          nsHttpTransaction *aTrans,
    1227                 :                                          PRUint8 caps,
    1228                 :                                          nsHttpConnection *conn)
    1229                 : {
    1230            2988 :     LOG(("nsHttpConnectionMgr::DispatchTransaction [ci=%s trans=%x caps=%x conn=%x]\n",
    1231                 :         ent->mConnInfo->HashKey().get(), aTrans, caps, conn));
    1232                 :     nsresult rv;
    1233                 :     
    1234            2988 :     PRInt32 priority = aTrans->Priority();
    1235                 : 
    1236            2988 :     if (conn->UsingSpdy()) {
    1237               0 :         LOG(("Spdy Dispatch Transaction via Activate(). Transaction host = %s,"
    1238                 :              "Connection host = %s\n",
    1239                 :              aTrans->ConnectionInfo()->Host(),
    1240                 :              conn->ConnectionInfo()->Host()));
    1241               0 :         rv = conn->Activate(aTrans, caps, priority);
    1242               0 :         NS_ABORT_IF_FALSE(NS_SUCCEEDED(rv), "SPDY Cannot Fail Dispatch");
    1243               0 :         return rv;
    1244                 :     }
    1245                 : 
    1246            2988 :     nsConnectionHandle *handle = new nsConnectionHandle(conn);
    1247            2988 :     if (!handle)
    1248               0 :         return NS_ERROR_OUT_OF_MEMORY;
    1249            2988 :     NS_ADDREF(handle);
    1250                 : 
    1251            2988 :     nsHttpPipeline *pipeline = nsnull;
    1252            2988 :     nsAHttpTransaction *trans = aTrans;
    1253                 : 
    1254            2988 :     if (conn->SupportsPipelining() && (caps & NS_HTTP_ALLOW_PIPELINING)) {
    1255               0 :         LOG(("  looking to build pipeline...\n"));
    1256               0 :         if (BuildPipeline(ent, trans, &pipeline))
    1257               0 :             trans = pipeline;
    1258                 :     }
    1259                 : 
    1260                 :     // give the transaction the indirect reference to the connection.
    1261            2988 :     trans->SetConnection(handle);
    1262                 : 
    1263            2988 :     rv = conn->Activate(trans, caps, priority);
    1264                 : 
    1265            2988 :     if (NS_FAILED(rv)) {
    1266               0 :         LOG(("  conn->Activate failed [rv=%x]\n", rv));
    1267               0 :         ent->mActiveConns.RemoveElement(conn);
    1268               0 :         mNumActiveConns--;
    1269               0 :         ConditionallyStopReadTimeoutTick();
    1270                 : 
    1271                 :         // sever back references to connection, and do so without triggering
    1272                 :         // a call to ReclaimConnection ;-)
    1273               0 :         trans->SetConnection(nsnull);
    1274               0 :         NS_RELEASE(handle->mConn);
    1275                 :         // destroy the connection
    1276               0 :         NS_RELEASE(conn);
    1277                 :     }
    1278                 : 
    1279                 :     // if we were unable to activate the pipeline, then this will destroy
    1280                 :     // the pipeline, which will cause each the transactions owned by the 
    1281                 :     // pipeline to be restarted.
    1282            2988 :     NS_IF_RELEASE(pipeline);
    1283                 : 
    1284            2988 :     NS_RELEASE(handle);
    1285            2988 :     return rv;
    1286                 : }
    1287                 : 
    1288                 : bool
    1289               0 : nsHttpConnectionMgr::BuildPipeline(nsConnectionEntry *ent,
    1290                 :                                    nsAHttpTransaction *firstTrans,
    1291                 :                                    nsHttpPipeline **result)
    1292                 : {
    1293               0 :     if (mMaxPipelinedRequests < 2)
    1294               0 :         return false;
    1295                 : 
    1296               0 :     nsHttpPipeline *pipeline = nsnull;
    1297                 :     nsHttpTransaction *trans;
    1298                 : 
    1299               0 :     PRUint32 i = 0, numAdded = 0;
    1300               0 :     while (i < ent->mPendingQ.Length()) {
    1301               0 :         trans = ent->mPendingQ[i];
    1302               0 :         if (trans->Caps() & NS_HTTP_ALLOW_PIPELINING) {
    1303               0 :             if (numAdded == 0) {
    1304               0 :                 pipeline = new nsHttpPipeline;
    1305               0 :                 if (!pipeline)
    1306               0 :                     return false;
    1307               0 :                 pipeline->AddTransaction(firstTrans);
    1308               0 :                 numAdded = 1;
    1309                 :             }
    1310               0 :             pipeline->AddTransaction(trans);
    1311                 : 
    1312                 :             // remove transaction from pending queue
    1313               0 :             ent->mPendingQ.RemoveElementAt(i);
    1314               0 :             NS_RELEASE(trans);
    1315                 : 
    1316               0 :             if (++numAdded == mMaxPipelinedRequests)
    1317               0 :                 break;
    1318                 :         }
    1319                 :         else
    1320               0 :             ++i; // skip to next pending transaction
    1321                 :     }
    1322                 : 
    1323               0 :     if (numAdded == 0)
    1324               0 :         return false;
    1325                 : 
    1326               0 :     LOG(("  pipelined %u transactions\n", numAdded));
    1327               0 :     NS_ADDREF(*result = pipeline);
    1328               0 :     return true;
    1329                 : }
    1330                 : 
    1331                 : nsresult
    1332            2990 : nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction *trans)
    1333                 : {
    1334            2990 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1335                 : 
    1336                 :     // since "adds" and "cancels" are processed asynchronously and because
    1337                 :     // various events might trigger an "add" directly on the socket thread,
    1338                 :     // we must take care to avoid dispatching a transaction that has already
    1339                 :     // been canceled (see bug 190001).
    1340            2990 :     if (NS_FAILED(trans->Status())) {
    1341               0 :         LOG(("  transaction was canceled... dropping event!\n"));
    1342               0 :         return NS_OK;
    1343                 :     }
    1344                 : 
    1345            2990 :     PRUint8 caps = trans->Caps();
    1346            2990 :     nsHttpConnectionInfo *ci = trans->ConnectionInfo();
    1347            2990 :     NS_ASSERTION(ci, "no connection info");
    1348                 : 
    1349            2990 :     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
    1350            2990 :     if (!ent) {
    1351             291 :         nsHttpConnectionInfo *clone = ci->Clone();
    1352             291 :         if (!clone)
    1353               0 :             return NS_ERROR_OUT_OF_MEMORY;
    1354             291 :         ent = new nsConnectionEntry(clone);
    1355             291 :         if (!ent)
    1356               0 :             return NS_ERROR_OUT_OF_MEMORY;
    1357             291 :         mCT.Put(ci->HashKey(), ent);
    1358                 :     }
    1359                 : 
    1360                 :     // SPDY coalescing of hostnames means we might redirect from this
    1361                 :     // connection entry onto the preferred one.
    1362            2990 :     nsConnectionEntry *preferredEntry = GetSpdyPreferredEnt(ent);
    1363            2990 :     if (preferredEntry && (preferredEntry != ent)) {
    1364               0 :         LOG(("nsHttpConnectionMgr::ProcessNewTransaction trans=%p "
    1365                 :              "redirected via coalescing from %s to %s\n", trans,
    1366                 :              ent->mConnInfo->Host(), preferredEntry->mConnInfo->Host()));
    1367                 : 
    1368               0 :         ent = preferredEntry;
    1369                 :     }
    1370                 : 
    1371                 :     // If we are doing a force reload then close out any existing conns
    1372                 :     // to this host so that changes in DNS, LBs, etc.. are reflected
    1373            2990 :     if (caps & NS_HTTP_CLEAR_KEEPALIVES)
    1374               0 :         ClosePersistentConnections(ent);
    1375                 : 
    1376                 :     // Check if the transaction already has a sticky reference to a connection.
    1377                 :     // If so, then we can just use it directly by transferring its reference
    1378                 :     // to the new connection var instead of calling GetConnection() to search
    1379                 :     // for an available one.
    1380                 : 
    1381            2990 :     nsAHttpConnection *wrappedConnection = trans->Connection();
    1382                 :     nsHttpConnection  *conn;
    1383            2990 :     conn = wrappedConnection ? wrappedConnection->TakeHttpConnection() : nsnull;
    1384                 : 
    1385            2990 :     if (conn) {
    1386               0 :         NS_ASSERTION(caps & NS_HTTP_STICKY_CONNECTION, "unexpected caps");
    1387                 : 
    1388               0 :         trans->SetConnection(nsnull);
    1389                 :     }
    1390                 :     else
    1391            2990 :         GetConnection(ent, trans, false, &conn);
    1392                 : 
    1393                 :     nsresult rv;
    1394            2990 :     if (!conn) {
    1395            2990 :         LOG(("  adding transaction to pending queue [trans=%x pending-count=%u]\n",
    1396                 :             trans, ent->mPendingQ.Length()+1));
    1397                 :         // put this transaction on the pending queue...
    1398            2990 :         InsertTransactionSorted(ent->mPendingQ, trans);
    1399            2990 :         NS_ADDREF(trans);
    1400            2990 :         rv = NS_OK;
    1401                 :     }
    1402                 :     else {
    1403               0 :         rv = DispatchTransaction(ent, trans, caps, conn);
    1404               0 :         NS_RELEASE(conn);
    1405                 :     }
    1406                 : 
    1407            2990 :     return rv;
    1408                 : }
    1409                 : 
    1410                 : // This function tries to dispatch the pending spdy transactions on
    1411                 : // the connection entry sent in as an argument. It will do so on the
    1412                 : // active spdy connection either in that same entry or in the
    1413                 : // redirected 'preferred' entry for the same coalescing hash key if
    1414                 : // coalescing is enabled.
    1415                 : 
    1416                 : void
    1417            6239 : nsHttpConnectionMgr::ProcessSpdyPendingQ(nsConnectionEntry *ent)
    1418                 : {
    1419            6239 :     nsHttpConnection *conn = GetSpdyPreferredConn(ent);
    1420            6239 :     if (!conn)
    1421            6239 :         return;
    1422                 : 
    1423               0 :     for (PRInt32 index = ent->mPendingQ.Length() - 1;
    1424               0 :          index >= 0 && conn->CanDirectlyActivate();
    1425                 :          --index) {
    1426               0 :         nsHttpTransaction *trans = ent->mPendingQ[index];
    1427                 : 
    1428               0 :         if (!(trans->Caps() & NS_HTTP_ALLOW_KEEPALIVE) ||
    1429               0 :             trans->Caps() & NS_HTTP_DISALLOW_SPDY)
    1430               0 :             continue;
    1431                 :  
    1432               0 :         ent->mPendingQ.RemoveElementAt(index);
    1433                 : 
    1434               0 :         nsresult rv = DispatchTransaction(ent, trans, trans->Caps(), conn);
    1435               0 :         if (NS_FAILED(rv)) {
    1436                 :             // this cannot happen, but if due to some bug it does then
    1437                 :             // close the transaction
    1438               0 :             NS_ABORT_IF_FALSE(false, "Dispatch SPDY Transaction");
    1439               0 :             LOG(("ProcessSpdyPendingQ Dispatch Transaction failed trans=%p\n",
    1440                 :                     trans));
    1441               0 :             trans->Close(rv);
    1442                 :         }
    1443               0 :         NS_RELEASE(trans);
    1444                 :     }
    1445                 : }
    1446                 : 
    1447                 : PLDHashOperator
    1448               0 : nsHttpConnectionMgr::ProcessSpdyPendingQCB(const nsACString &key,
    1449                 :                                            nsAutoPtr<nsConnectionEntry> &ent,
    1450                 :                                            void *closure)
    1451                 : {
    1452               0 :     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
    1453               0 :     self->ProcessSpdyPendingQ(ent);
    1454               0 :     return PL_DHASH_NEXT;
    1455                 : }
    1456                 : 
    1457                 : void
    1458               0 : nsHttpConnectionMgr::ProcessAllSpdyPendingQ()
    1459                 : {
    1460               0 :     mCT.Enumerate(ProcessSpdyPendingQCB, this);
    1461               0 : }
    1462                 : 
    1463                 : nsHttpConnection *
    1464            9229 : nsHttpConnectionMgr::GetSpdyPreferredConn(nsConnectionEntry *ent)
    1465                 : {
    1466            9229 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1467            9229 :     NS_ABORT_IF_FALSE(ent, "no connection entry");
    1468                 : 
    1469            9229 :     nsConnectionEntry *preferred = GetSpdyPreferredEnt(ent);
    1470                 : 
    1471                 :     // this entry is spdy-enabled if it is involved in a redirect
    1472            9229 :     if (preferred)
    1473                 :         // all new connections for this entry will use spdy too
    1474               0 :         ent->mUsingSpdy = true;
    1475                 :     else
    1476            9229 :         preferred = ent;
    1477                 :     
    1478            9229 :     nsHttpConnection *conn = nsnull;
    1479                 :     
    1480            9229 :     if (preferred->mUsingSpdy) {
    1481               0 :         for (PRUint32 index = 0;
    1482               0 :              index < preferred->mActiveConns.Length();
    1483                 :              ++index) {
    1484               0 :             if (preferred->mActiveConns[index]->CanDirectlyActivate()) {
    1485               0 :                 conn = preferred->mActiveConns[index];
    1486               0 :                 break;
    1487                 :             }
    1488                 :         }
    1489                 :     }
    1490                 :     
    1491            9229 :     return conn;
    1492                 : }
    1493                 : 
    1494                 : //-----------------------------------------------------------------------------
    1495                 : 
    1496                 : void
    1497             672 : nsHttpConnectionMgr::OnMsgShutdown(PRInt32, void *)
    1498                 : {
    1499             672 :     LOG(("nsHttpConnectionMgr::OnMsgShutdown\n"));
    1500                 : 
    1501             672 :     mCT.Enumerate(ShutdownPassCB, this);
    1502                 : 
    1503             672 :     if (mReadTimeoutTick) {
    1504             259 :         mReadTimeoutTick->Cancel();
    1505             259 :         mReadTimeoutTick = nsnull;
    1506             259 :         mReadTimeoutTickArmed = false;
    1507                 :     }
    1508                 :     
    1509                 :     // signal shutdown complete
    1510            1344 :     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
    1511             672 :     mon.Notify();
    1512             672 : }
    1513                 : 
    1514                 : void
    1515            2990 : nsHttpConnectionMgr::OnMsgNewTransaction(PRInt32 priority, void *param)
    1516                 : {
    1517            2990 :     LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
    1518                 : 
    1519            2990 :     nsHttpTransaction *trans = (nsHttpTransaction *) param;
    1520            2990 :     trans->SetPriority(priority);
    1521            2990 :     nsresult rv = ProcessNewTransaction(trans);
    1522            2990 :     if (NS_FAILED(rv))
    1523               0 :         trans->Close(rv); // for whatever its worth
    1524            2990 :     NS_RELEASE(trans);
    1525            2990 : }
    1526                 : 
    1527                 : void
    1528               0 : nsHttpConnectionMgr::OnMsgReschedTransaction(PRInt32 priority, void *param)
    1529                 : {
    1530               0 :     LOG(("nsHttpConnectionMgr::OnMsgNewTransaction [trans=%p]\n", param));
    1531                 : 
    1532               0 :     nsHttpTransaction *trans = (nsHttpTransaction *) param;
    1533               0 :     trans->SetPriority(priority);
    1534                 : 
    1535                 :     nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
    1536               0 :                                                    nsnull, trans);
    1537                 : 
    1538               0 :     if (ent) {
    1539               0 :         PRInt32 index = ent->mPendingQ.IndexOf(trans);
    1540               0 :         if (index >= 0) {
    1541               0 :             ent->mPendingQ.RemoveElementAt(index);
    1542               0 :             InsertTransactionSorted(ent->mPendingQ, trans);
    1543                 :         }
    1544                 :     }
    1545                 : 
    1546               0 :     NS_RELEASE(trans);
    1547               0 : }
    1548                 : 
    1549                 : void
    1550             163 : nsHttpConnectionMgr::OnMsgCancelTransaction(PRInt32 reason, void *param)
    1551                 : {
    1552             163 :     LOG(("nsHttpConnectionMgr::OnMsgCancelTransaction [trans=%p]\n", param));
    1553                 : 
    1554             163 :     nsHttpTransaction *trans = (nsHttpTransaction *) param;
    1555                 :     //
    1556                 :     // if the transaction owns a connection and the transaction is not done,
    1557                 :     // then ask the connection to close the transaction.  otherwise, close the
    1558                 :     // transaction directly (removing it from the pending queue first).
    1559                 :     //
    1560             163 :     nsAHttpConnection *conn = trans->Connection();
    1561             163 :     if (conn && !trans->IsDone())
    1562              10 :         conn->CloseTransaction(trans, reason);
    1563                 :     else {
    1564                 :         nsConnectionEntry *ent = LookupConnectionEntry(trans->ConnectionInfo(),
    1565             153 :                                                        nsnull, trans);
    1566                 : 
    1567             153 :         if (ent) {
    1568             153 :             PRInt32 index = ent->mPendingQ.IndexOf(trans);
    1569             153 :             if (index >= 0) {
    1570               1 :                 ent->mPendingQ.RemoveElementAt(index);
    1571               1 :                 nsHttpTransaction *temp = trans;
    1572               1 :                 NS_RELEASE(temp); // b/c NS_RELEASE nulls its argument!
    1573                 :             }
    1574                 :         }
    1575             153 :         trans->Close(reason);
    1576                 :     }
    1577             163 :     NS_RELEASE(trans);
    1578             163 : }
    1579                 : 
    1580                 : void
    1581            2989 : nsHttpConnectionMgr::OnMsgProcessPendingQ(PRInt32, void *param)
    1582                 : {
    1583            2989 :     nsHttpConnectionInfo *ci = (nsHttpConnectionInfo *) param;
    1584                 : 
    1585            2989 :     LOG(("nsHttpConnectionMgr::OnMsgProcessPendingQ [ci=%s]\n", ci->HashKey().get()));
    1586                 : 
    1587                 :     // start by processing the queue identified by the given connection info.
    1588            2989 :     nsConnectionEntry *ent = mCT.Get(ci->HashKey());
    1589            2989 :     if (!(ent && ProcessPendingQForEntry(ent))) {
    1590                 :         // if we reach here, it means that we couldn't dispatch a transaction
    1591                 :         // for the specified connection info.  walk the connection table...
    1592            2989 :         mCT.Enumerate(ProcessOneTransactionCB, this);
    1593                 :     }
    1594                 : 
    1595            2989 :     NS_RELEASE(ci);
    1596            2989 : }
    1597                 : 
    1598                 : void
    1599             124 : nsHttpConnectionMgr::OnMsgPruneDeadConnections(PRInt32, void *)
    1600                 : {
    1601             124 :     LOG(("nsHttpConnectionMgr::OnMsgPruneDeadConnections\n"));
    1602                 : 
    1603                 :     // Reset mTimeOfNextWakeUp so that we can find a new shortest value.
    1604             124 :     mTimeOfNextWakeUp = LL_MAXUINT;
    1605                 : 
    1606                 :     // check canreuse() for all idle connections plus any active connections on
    1607                 :     // connection entries that are using spdy.
    1608             124 :     if (mNumIdleConns || (mNumActiveConns && gHttpHandler->IsSpdyEnabled()))
    1609               1 :         mCT.Enumerate(PruneDeadConnectionsCB, this);
    1610             124 : }
    1611                 : 
    1612                 : void
    1613             123 : nsHttpConnectionMgr::OnMsgClosePersistentConnections(PRInt32, void *)
    1614                 : {
    1615             123 :     LOG(("nsHttpConnectionMgr::OnMsgClosePersistentConnections\n"));
    1616                 : 
    1617             123 :     mCT.Enumerate(ClosePersistentConnectionsCB, this);
    1618             123 : }
    1619                 : 
    1620                 : void
    1621            2989 : nsHttpConnectionMgr::OnMsgReclaimConnection(PRInt32, void *param)
    1622                 : {
    1623            2989 :     LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection [conn=%p]\n", param));
    1624                 : 
    1625            2989 :     nsHttpConnection *conn = (nsHttpConnection *) param;
    1626                 : 
    1627                 :     // 
    1628                 :     // 1) remove the connection from the active list
    1629                 :     // 2) if keep-alive, add connection to idle list
    1630                 :     // 3) post event to process the pending transaction queue
    1631                 :     //
    1632                 : 
    1633                 :     nsConnectionEntry *ent = LookupConnectionEntry(conn->ConnectionInfo(),
    1634            2989 :                                                    conn, nsnull);
    1635            2989 :     nsHttpConnectionInfo *ci = nsnull;
    1636                 : 
    1637            2989 :     if (!ent) {
    1638                 :         // this should never happen
    1639               0 :         LOG(("nsHttpConnectionMgr::OnMsgReclaimConnection ent == null\n"));
    1640               0 :         NS_ABORT_IF_FALSE(false, "no connection entry");
    1641               0 :         NS_ADDREF(ci = conn->ConnectionInfo());
    1642                 :     }
    1643                 :     else {
    1644            2989 :         NS_ADDREF(ci = ent->mConnInfo);
    1645                 : 
    1646                 :         // If the connection is in the active list, remove that entry
    1647                 :         // and the reference held by the mActiveConns list.
    1648                 :         // This is never the final reference on conn as the event context
    1649                 :         // is also holding one that is released at the end of this function.
    1650                 : 
    1651            2989 :         if (ent->mUsingSpdy) {
    1652                 :             // Spdy connections aren't reused in the traditional HTTP way in
    1653                 :             // the idleconns list, they are actively multplexed as active
    1654                 :             // conns. Even when they have 0 transactions on them they are
    1655                 :             // considered active connections. So when one is reclaimed it
    1656                 :             // is really complete and is meant to be shut down and not
    1657                 :             // reused.
    1658               0 :             conn->DontReuse();
    1659                 :         }
    1660                 :         
    1661            2989 :         if (ent->mActiveConns.RemoveElement(conn)) {
    1662            2988 :             nsHttpConnection *temp = conn;
    1663            2988 :             NS_RELEASE(temp);
    1664            2988 :             mNumActiveConns--;
    1665            2988 :             ConditionallyStopReadTimeoutTick();
    1666                 :         }
    1667                 : 
    1668            2989 :         if (conn->CanReuse()) {
    1669               4 :             LOG(("  adding connection to idle list\n"));
    1670                 :             // Keep The idle connection list sorted with the connections that
    1671                 :             // have moved the largest data pipelines at the front because these
    1672                 :             // connections have the largest cwnds on the server.
    1673                 : 
    1674                 :             // The linear search is ok here because the number of idleconns
    1675                 :             // in a single entry is generally limited to a small number (i.e. 6)
    1676                 : 
    1677                 :             PRUint32 idx;
    1678               4 :             for (idx = 0; idx < ent->mIdleConns.Length(); idx++) {
    1679               0 :                 nsHttpConnection *idleConn = ent->mIdleConns[idx];
    1680               0 :                 if (idleConn->MaxBytesRead() < conn->MaxBytesRead())
    1681               0 :                     break;
    1682                 :             }
    1683                 : 
    1684               4 :             NS_ADDREF(conn);
    1685               4 :             ent->mIdleConns.InsertElementAt(idx, conn);
    1686               4 :             mNumIdleConns++;
    1687               4 :             conn->BeginIdleMonitoring();
    1688                 : 
    1689                 :             // If the added connection was first idle connection or has shortest
    1690                 :             // time to live among the watched connections, pruning dead
    1691                 :             // connections needs to be done when it can't be reused anymore.
    1692               4 :             PRUint32 timeToLive = conn->TimeToLive();
    1693               4 :             if(!mTimer || NowInSeconds() + timeToLive < mTimeOfNextWakeUp)
    1694               4 :                 PruneDeadConnectionsAfter(timeToLive);
    1695                 :         }
    1696                 :         else {
    1697            2985 :             LOG(("  connection cannot be reused; closing connection\n"));
    1698                 :             // make sure the connection is closed and release our reference.
    1699            2985 :             conn->Close(NS_ERROR_ABORT);
    1700                 :         }
    1701                 :     }
    1702                 :  
    1703            2989 :     OnMsgProcessPendingQ(NS_OK, ci); // releases |ci|
    1704            2989 :     NS_RELEASE(conn);
    1705            2989 : }
    1706                 : 
    1707                 : void
    1708               0 : nsHttpConnectionMgr::OnMsgUpdateParam(PRInt32, void *param)
    1709                 : {
    1710               0 :     PRUint16 name  = (NS_PTR_TO_INT32(param) & 0xFFFF0000) >> 16;
    1711               0 :     PRUint16 value =  NS_PTR_TO_INT32(param) & 0x0000FFFF;
    1712                 : 
    1713               0 :     switch (name) {
    1714                 :     case MAX_CONNECTIONS:
    1715               0 :         mMaxConns = value;
    1716               0 :         break;
    1717                 :     case MAX_CONNECTIONS_PER_HOST:
    1718               0 :         mMaxConnsPerHost = value;
    1719               0 :         break;
    1720                 :     case MAX_CONNECTIONS_PER_PROXY:
    1721               0 :         mMaxConnsPerProxy = value;
    1722               0 :         break;
    1723                 :     case MAX_PERSISTENT_CONNECTIONS_PER_HOST:
    1724               0 :         mMaxPersistConnsPerHost = value;
    1725               0 :         break;
    1726                 :     case MAX_PERSISTENT_CONNECTIONS_PER_PROXY:
    1727               0 :         mMaxPersistConnsPerProxy = value;
    1728               0 :         break;
    1729                 :     case MAX_REQUEST_DELAY:
    1730               0 :         mMaxRequestDelay = value;
    1731               0 :         break;
    1732                 :     case MAX_PIPELINED_REQUESTS:
    1733               0 :         mMaxPipelinedRequests = value;
    1734               0 :         break;
    1735                 :     default:
    1736               0 :         NS_NOTREACHED("unexpected parameter name");
    1737                 :     }
    1738               0 : }
    1739                 : 
    1740                 : // nsHttpConnectionMgr::nsConnectionEntry
    1741             582 : nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
    1742                 : {
    1743             291 :     if (mSpdyPreferred)
    1744               0 :         gHttpHandler->ConnMgr()->RemoveSpdyPreferredEnt(mCoalescingKey);
    1745                 : 
    1746             291 :     NS_RELEASE(mConnInfo);
    1747             291 : }
    1748                 : 
    1749                 : // Read Timeout Tick handlers
    1750                 : 
    1751                 : void
    1752            2988 : nsHttpConnectionMgr::ActivateTimeoutTick()
    1753                 : {
    1754            2988 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1755            2988 :     LOG(("nsHttpConnectionMgr::ActivateTimeoutTick() "
    1756                 :          "this=%p mReadTimeoutTick=%p\n"));
    1757                 : 
    1758                 :     // right now the spdy timeout code is the only thing hooked to the timeout
    1759                 :     // tick, so disable it if spdy is not being used. However pipelining code
    1760                 :     // will also want this functionality soon.
    1761            2988 :     if (!gHttpHandler->IsSpdyEnabled())
    1762               0 :         return;
    1763                 : 
    1764                 :     // The timer tick should be enabled if it is not already pending.
    1765                 :     // Upon running the tick will rearm itself if there are active
    1766                 :     // connections available.
    1767                 : 
    1768            2988 :     if (mReadTimeoutTick && mReadTimeoutTickArmed)
    1769             248 :         return;
    1770                 : 
    1771            2740 :     if (!mReadTimeoutTick) {
    1772             259 :         mReadTimeoutTick = do_CreateInstance(NS_TIMER_CONTRACTID);
    1773             259 :         if (!mReadTimeoutTick) {
    1774               0 :             NS_WARNING("failed to create timer for http timeout management");
    1775               0 :             return;
    1776                 :         }
    1777             259 :         mReadTimeoutTick->SetTarget(mSocketThreadTarget);
    1778                 :     }
    1779                 : 
    1780            2740 :     NS_ABORT_IF_FALSE(!mReadTimeoutTickArmed, "timer tick armed");
    1781            2740 :     mReadTimeoutTickArmed = true;
    1782                 :     // pipeline will expect a 1000ms granuality
    1783            2740 :     mReadTimeoutTick->Init(this, 15000, nsITimer::TYPE_REPEATING_SLACK);
    1784                 : }
    1785                 : 
    1786                 : void
    1787               1 : nsHttpConnectionMgr::ReadTimeoutTick()
    1788                 : {
    1789               1 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1790               1 :     NS_ABORT_IF_FALSE(mReadTimeoutTick, "no readtimeout tick");
    1791                 : 
    1792               1 :     LOG(("nsHttpConnectionMgr::ReadTimeoutTick active=%d\n",
    1793                 :          mNumActiveConns));
    1794                 : 
    1795               1 :     mCT.Enumerate(ReadTimeoutTickCB, this);
    1796               1 : }
    1797                 : 
    1798                 : PLDHashOperator
    1799               1 : nsHttpConnectionMgr::ReadTimeoutTickCB(const nsACString &key,
    1800                 :                                        nsAutoPtr<nsConnectionEntry> &ent,
    1801                 :                                        void *closure)
    1802                 : {
    1803               1 :     nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
    1804                 : 
    1805               1 :     LOG(("nsHttpConnectionMgr::ReadTimeoutTickCB() this=%p host=%s\n",
    1806                 :          self, ent->mConnInfo->Host()));
    1807                 : 
    1808               1 :     PRIntervalTime now = PR_IntervalNow();
    1809               2 :     for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index)
    1810               1 :         ent->mActiveConns[index]->ReadTimeoutTick(now);
    1811                 : 
    1812               1 :     return PL_DHASH_NEXT;
    1813                 : }
    1814                 : 
    1815                 : //-----------------------------------------------------------------------------
    1816                 : // nsHttpConnectionMgr::nsConnectionHandle
    1817                 : 
    1818            5976 : nsHttpConnectionMgr::nsConnectionHandle::~nsConnectionHandle()
    1819                 : {
    1820            2988 :     if (mConn) {
    1821            2988 :         gHttpHandler->ReclaimConnection(mConn);
    1822            2988 :         NS_RELEASE(mConn);
    1823                 :     }
    1824           11952 : }
    1825                 : 
    1826           12020 : NS_IMPL_THREADSAFE_ISUPPORTS0(nsHttpConnectionMgr::nsConnectionHandle)
    1827                 : 
    1828                 : nsresult
    1829            2832 : nsHttpConnectionMgr::nsConnectionHandle::OnHeadersAvailable(nsAHttpTransaction *trans,
    1830                 :                                                             nsHttpRequestHead *req,
    1831                 :                                                             nsHttpResponseHead *resp,
    1832                 :                                                             bool *reset)
    1833                 : {
    1834            2832 :     return mConn->OnHeadersAvailable(trans, req, resp, reset);
    1835                 : }
    1836                 : 
    1837                 : nsresult
    1838               0 : nsHttpConnectionMgr::nsConnectionHandle::ResumeSend()
    1839                 : {
    1840               0 :     return mConn->ResumeSend();
    1841                 : }
    1842                 : 
    1843                 : nsresult
    1844               0 : nsHttpConnectionMgr::nsConnectionHandle::ResumeRecv()
    1845                 : {
    1846               0 :     return mConn->ResumeRecv();
    1847                 : }
    1848                 : 
    1849                 : void
    1850              10 : nsHttpConnectionMgr::nsConnectionHandle::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
    1851                 : {
    1852              10 :     mConn->CloseTransaction(trans, reason);
    1853              10 : }
    1854                 : 
    1855                 : void
    1856               0 : nsHttpConnectionMgr::nsConnectionHandle::GetConnectionInfo(nsHttpConnectionInfo **result)
    1857                 : {
    1858               0 :     mConn->GetConnectionInfo(result);
    1859               0 : }
    1860                 : 
    1861                 : nsresult
    1862               0 : nsHttpConnectionMgr::
    1863                 : nsConnectionHandle::TakeTransport(nsISocketTransport  **aTransport,
    1864                 :                                   nsIAsyncInputStream **aInputStream,
    1865                 :                                   nsIAsyncOutputStream **aOutputStream)
    1866                 : {
    1867               0 :     return mConn->TakeTransport(aTransport, aInputStream, aOutputStream);
    1868                 : }
    1869                 : 
    1870                 : void
    1871            2988 : nsHttpConnectionMgr::nsConnectionHandle::GetSecurityInfo(nsISupports **result)
    1872                 : {
    1873            2988 :     mConn->GetSecurityInfo(result);
    1874            2988 : }
    1875                 : 
    1876                 : bool
    1877            5558 : nsHttpConnectionMgr::nsConnectionHandle::IsPersistent()
    1878                 : {
    1879            5558 :     return mConn->IsPersistent();
    1880                 : }
    1881                 : 
    1882                 : bool
    1883            2988 : nsHttpConnectionMgr::nsConnectionHandle::IsReused()
    1884                 : {
    1885            2988 :     return mConn->IsReused();
    1886                 : }
    1887                 : 
    1888                 : nsresult
    1889               0 : nsHttpConnectionMgr::nsConnectionHandle::PushBack(const char *buf, PRUint32 bufLen)
    1890                 : {
    1891               0 :     return mConn->PushBack(buf, bufLen);
    1892                 : }
    1893                 : 
    1894                 : 
    1895                 : //////////////////////// nsHalfOpenSocket
    1896                 : 
    1897                 : 
    1898           89391 : NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpConnectionMgr::nsHalfOpenSocket,
    1899                 :                               nsIOutputStreamCallback,
    1900                 :                               nsITransportEventSink,
    1901                 :                               nsIInterfaceRequestor,
    1902                 :                               nsITimerCallback)
    1903                 : 
    1904            2990 : nsHttpConnectionMgr::
    1905                 : nsHalfOpenSocket::nsHalfOpenSocket(nsConnectionEntry *ent,
    1906                 :                                    nsHttpTransaction *trans)
    1907                 :     : mEnt(ent),
    1908            2990 :       mTransaction(trans)
    1909                 : {
    1910            2990 :     NS_ABORT_IF_FALSE(ent && trans, "constructor with null arguments");
    1911            2990 :     LOG(("Creating nsHalfOpenSocket [this=%p trans=%p ent=%s]\n",
    1912                 :          this, trans, ent->mConnInfo->Host()));
    1913            2990 : }
    1914                 : 
    1915            5980 : nsHttpConnectionMgr::nsHalfOpenSocket::~nsHalfOpenSocket()
    1916                 : {
    1917            2990 :     NS_ABORT_IF_FALSE(!mStreamOut, "streamout not null");
    1918            2990 :     NS_ABORT_IF_FALSE(!mBackupStreamOut, "backupstreamout not null");
    1919            2990 :     NS_ABORT_IF_FALSE(!mSynTimer, "syntimer not null");
    1920            2990 :     LOG(("Destroying nsHalfOpenSocket [this=%p]\n", this));
    1921                 :     
    1922            2990 :     if (mEnt) {
    1923                 :         // A failure to create the transport object at all
    1924                 :         // will result in this not being present in the halfopen table
    1925                 :         // so ignore failures of RemoveElement()
    1926            2990 :         mEnt->mHalfOpens.RemoveElement(this);
    1927                 :     }
    1928            2990 : }
    1929                 : 
    1930                 : nsresult
    1931            2990 : nsHttpConnectionMgr::
    1932                 : nsHalfOpenSocket::SetupStreams(nsISocketTransport **transport,
    1933                 :                                nsIAsyncInputStream **instream,
    1934                 :                                nsIAsyncOutputStream **outstream,
    1935                 :                                bool isBackup)
    1936                 : {
    1937                 :     nsresult rv;
    1938                 : 
    1939                 :     const char* types[1];
    1940            2990 :     types[0] = (mEnt->mConnInfo->UsingSSL()) ?
    1941            2990 :         "ssl" : gHttpHandler->DefaultSocketType();
    1942            2990 :     PRUint32 typeCount = (types[0] != nsnull);
    1943                 : 
    1944            5980 :     nsCOMPtr<nsISocketTransport> socketTransport;
    1945            5980 :     nsCOMPtr<nsISocketTransportService> sts;
    1946                 : 
    1947            2990 :     sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
    1948            2990 :     NS_ENSURE_SUCCESS(rv, rv);
    1949                 : 
    1950            2990 :     rv = sts->CreateTransport(types, typeCount,
    1951            2990 :                               nsDependentCString(mEnt->mConnInfo->Host()),
    1952                 :                               mEnt->mConnInfo->Port(),
    1953            2990 :                               mEnt->mConnInfo->ProxyInfo(),
    1954            8970 :                               getter_AddRefs(socketTransport));
    1955            2990 :     NS_ENSURE_SUCCESS(rv, rv);
    1956                 :     
    1957            2989 :     PRUint32 tmpFlags = 0;
    1958            2989 :     if (mTransaction->Caps() & NS_HTTP_REFRESH_DNS)
    1959            1936 :         tmpFlags = nsISocketTransport::BYPASS_CACHE;
    1960                 : 
    1961            2989 :     if (mTransaction->Caps() & NS_HTTP_LOAD_ANONYMOUS)
    1962               1 :         tmpFlags |= nsISocketTransport::ANONYMOUS_CONNECT;
    1963                 : 
    1964                 :     // For backup connections, we disable IPv6. That's because some users have
    1965                 :     // broken IPv6 connectivity (leading to very long timeouts), and disabling
    1966                 :     // IPv6 on the backup connection gives them a much better user experience
    1967                 :     // with dual-stack hosts, though they still pay the 250ms delay for each new
    1968                 :     // connection. This strategy is also known as "happy eyeballs".
    1969            2989 :     if (isBackup && gHttpHandler->FastFallbackToIPv4())
    1970               0 :         tmpFlags |= nsISocketTransport::DISABLE_IPV6;
    1971                 : 
    1972            2989 :     socketTransport->SetConnectionFlags(tmpFlags);
    1973                 : 
    1974            2989 :     socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
    1975                 : 
    1976            2989 :     rv = socketTransport->SetEventSink(this, nsnull);
    1977            2989 :     NS_ENSURE_SUCCESS(rv, rv);
    1978                 : 
    1979            2989 :     rv = socketTransport->SetSecurityCallbacks(this);
    1980            2989 :     NS_ENSURE_SUCCESS(rv, rv);
    1981                 : 
    1982            5978 :     nsCOMPtr<nsIOutputStream> sout;
    1983            2989 :     rv = socketTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED,
    1984                 :                                             0, 0,
    1985            2989 :                                             getter_AddRefs(sout));
    1986            2989 :     NS_ENSURE_SUCCESS(rv, rv);
    1987                 : 
    1988            5978 :     nsCOMPtr<nsIInputStream> sin;
    1989            2989 :     rv = socketTransport->OpenInputStream(nsITransport::OPEN_UNBUFFERED,
    1990                 :                                            0, 0,
    1991            2989 :                                            getter_AddRefs(sin));
    1992            2989 :     NS_ENSURE_SUCCESS(rv, rv);
    1993                 : 
    1994            2989 :     socketTransport.forget(transport);
    1995            2989 :     CallQueryInterface(sin, instream);
    1996            2989 :     CallQueryInterface(sout, outstream);
    1997                 : 
    1998            2989 :     rv = (*outstream)->AsyncWait(this, 0, 0, nsnull);
    1999            2989 :     if (NS_SUCCEEDED(rv))
    2000            2989 :         gHttpHandler->ConnMgr()->StartedConnect();
    2001                 : 
    2002            2989 :     return rv;
    2003                 : }
    2004                 : 
    2005                 : nsresult
    2006            2990 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupPrimaryStreams()
    2007                 : {
    2008            2990 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    2009                 : 
    2010                 :     nsresult rv;
    2011                 : 
    2012            2990 :     rv = SetupStreams(getter_AddRefs(mSocketTransport),
    2013            2990 :                       getter_AddRefs(mStreamIn),
    2014            2990 :                       getter_AddRefs(mStreamOut),
    2015            2990 :                       false);
    2016            2990 :     LOG(("nsHalfOpenSocket::SetupPrimaryStream [this=%p ent=%s rv=%x]",
    2017                 :          this, mEnt->mConnInfo->Host(), rv));
    2018            2990 :     if (NS_FAILED(rv)) {
    2019               1 :         if (mStreamOut)
    2020               0 :             mStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
    2021               1 :         mStreamOut = nsnull;
    2022               1 :         mStreamIn = nsnull;
    2023               1 :         mSocketTransport = nsnull;
    2024                 :     }
    2025            2990 :     return rv;
    2026                 : }
    2027                 : 
    2028                 : nsresult
    2029               0 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupStreams()
    2030                 : {
    2031               0 :     nsresult rv = SetupStreams(getter_AddRefs(mBackupTransport),
    2032               0 :                                getter_AddRefs(mBackupStreamIn),
    2033               0 :                                getter_AddRefs(mBackupStreamOut),
    2034               0 :                                true);
    2035               0 :     LOG(("nsHalfOpenSocket::SetupBackupStream [this=%p ent=%s rv=%x]",
    2036                 :          this, mEnt->mConnInfo->Host(), rv));
    2037               0 :     if (NS_FAILED(rv)) {
    2038               0 :         if (mBackupStreamOut)
    2039               0 :             mBackupStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
    2040               0 :         mBackupStreamOut = nsnull;
    2041               0 :         mBackupStreamIn = nsnull;
    2042               0 :         mBackupTransport = nsnull;
    2043                 :     }
    2044               0 :     return rv;
    2045                 : }
    2046                 : 
    2047                 : void
    2048            2982 : nsHttpConnectionMgr::nsHalfOpenSocket::SetupBackupTimer()
    2049                 : {
    2050            2982 :     PRUint16 timeout = gHttpHandler->GetIdleSynTimeout();
    2051            2982 :     NS_ABORT_IF_FALSE(!mSynTimer, "timer already initd");
    2052                 :     
    2053            2982 :     if (timeout) {
    2054                 :         // Setup the timer that will establish a backup socket
    2055                 :         // if we do not get a writable event on the main one.
    2056                 :         // We do this because a lost SYN takes a very long time
    2057                 :         // to repair at the TCP level.
    2058                 :         //
    2059                 :         // Failure to setup the timer is something we can live with,
    2060                 :         // so don't return an error in that case.
    2061                 :         nsresult rv;
    2062            2982 :         mSynTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
    2063            2982 :         if (NS_SUCCEEDED(rv)) {
    2064            2982 :             mSynTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
    2065            2982 :             LOG(("nsHalfOpenSocket::SetupBackupTimer()"));
    2066                 :         }
    2067                 :     }
    2068            2982 : }
    2069                 : 
    2070                 : void
    2071            5837 : nsHttpConnectionMgr::nsHalfOpenSocket::CancelBackupTimer()
    2072                 : {
    2073                 :     // If the syntimer is still armed, we can cancel it because no backup
    2074                 :     // socket should be formed at this point
    2075            5837 :     if (!mSynTimer)
    2076            2855 :         return;
    2077                 : 
    2078            2982 :     LOG(("nsHalfOpenSocket::CancelBackupTimer()"));
    2079            2982 :     mSynTimer->Cancel();
    2080            2982 :     mSynTimer = nsnull;
    2081                 : }
    2082                 : 
    2083                 : void
    2084               0 : nsHttpConnectionMgr::nsHalfOpenSocket::Abandon()
    2085                 : {
    2086               0 :     LOG(("nsHalfOpenSocket::Abandon [this=%p ent=%s]",
    2087                 :          this, mEnt->mConnInfo->Host()));
    2088                 : 
    2089               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    2090                 : 
    2091               0 :     nsRefPtr<nsHalfOpenSocket> deleteProtector(this);
    2092                 : 
    2093               0 :     if (mStreamOut) {
    2094               0 :         gHttpHandler->ConnMgr()->RecvdConnect();
    2095               0 :         mStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
    2096               0 :         mStreamOut = nsnull;
    2097                 :     }
    2098               0 :     if (mBackupStreamOut) {
    2099               0 :         gHttpHandler->ConnMgr()->RecvdConnect();
    2100               0 :         mBackupStreamOut->AsyncWait(nsnull, 0, 0, nsnull);
    2101               0 :         mBackupStreamOut = nsnull;
    2102                 :     }
    2103                 : 
    2104               0 :     CancelBackupTimer();
    2105                 : 
    2106               0 :     mEnt = nsnull;
    2107               0 : }
    2108                 : 
    2109                 : NS_IMETHODIMP // method for nsITimerCallback
    2110               0 : nsHttpConnectionMgr::nsHalfOpenSocket::Notify(nsITimer *timer)
    2111                 : {
    2112               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    2113               0 :     NS_ABORT_IF_FALSE(timer == mSynTimer, "wrong timer");
    2114                 : 
    2115               0 :     if (!gHttpHandler->ConnMgr()->
    2116               0 :         AtActiveConnectionLimit(mEnt, mTransaction->Caps())) {
    2117               0 :         SetupBackupStreams();
    2118                 :     }
    2119                 : 
    2120               0 :     mSynTimer = nsnull;
    2121               0 :     return NS_OK;
    2122                 : }
    2123                 : 
    2124                 : // method for nsIAsyncOutputStreamCallback
    2125                 : NS_IMETHODIMP
    2126            2989 : nsHttpConnectionMgr::
    2127                 : nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
    2128                 : {
    2129            2989 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    2130            2989 :     NS_ABORT_IF_FALSE(out == mStreamOut ||
    2131                 :                       out == mBackupStreamOut, "stream mismatch");
    2132            2989 :     LOG(("nsHalfOpenSocket::OnOutputStreamReady [this=%p ent=%s %s]\n", 
    2133                 :          this, mEnt->mConnInfo->Host(),
    2134                 :          out == mStreamOut ? "primary" : "backup"));
    2135                 :     PRInt32 index;
    2136                 :     nsresult rv;
    2137                 :     
    2138            2989 :     gHttpHandler->ConnMgr()->RecvdConnect();
    2139                 : 
    2140            2989 :     CancelBackupTimer();
    2141                 : 
    2142                 :     // assign the new socket to the http connection
    2143            5978 :     nsRefPtr<nsHttpConnection> conn = new nsHttpConnection();
    2144            2989 :     LOG(("nsHalfOpenSocket::OnOutputStreamReady "
    2145                 :          "Created new nshttpconnection %p\n", conn.get()));
    2146                 : 
    2147            5978 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
    2148            5978 :     nsCOMPtr<nsIEventTarget>        callbackTarget;
    2149            5978 :     mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks),
    2150            5978 :                                        getter_AddRefs(callbackTarget));
    2151            2989 :     if (out == mStreamOut) {
    2152                 :         rv = conn->Init(mEnt->mConnInfo,
    2153            2989 :                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
    2154                 :                         mSocketTransport, mStreamIn, mStreamOut,
    2155            5978 :                         callbacks, callbackTarget);
    2156                 : 
    2157                 :         // The nsHttpConnection object now owns these streams and sockets
    2158            2989 :         mStreamOut = nsnull;
    2159            2989 :         mStreamIn = nsnull;
    2160            2989 :         mSocketTransport = nsnull;
    2161                 :     }
    2162                 :     else {
    2163                 :         rv = conn->Init(mEnt->mConnInfo,
    2164               0 :                         gHttpHandler->ConnMgr()->mMaxRequestDelay,
    2165                 :                         mBackupTransport, mBackupStreamIn, mBackupStreamOut,
    2166               0 :                         callbacks, callbackTarget);
    2167                 : 
    2168                 :         // The nsHttpConnection object now owns these streams and sockets
    2169               0 :         mBackupStreamOut = nsnull;
    2170               0 :         mBackupStreamIn = nsnull;
    2171               0 :         mBackupTransport = nsnull;
    2172                 :     }
    2173                 : 
    2174            2989 :     if (NS_FAILED(rv)) {
    2175               0 :         LOG(("nsHalfOpenSocket::OnOutputStreamReady "
    2176                 :              "conn->init (%p) failed %x\n", conn.get(), rv));
    2177               0 :         return rv;
    2178                 :     }
    2179                 : 
    2180                 :     // if this is still in the pending list, remove it and dispatch it
    2181            2989 :     index = mEnt->mPendingQ.IndexOf(mTransaction);
    2182            2989 :     if (index != -1) {
    2183            2988 :         mEnt->mPendingQ.RemoveElementAt(index);
    2184            2988 :         nsHttpTransaction *temp = mTransaction;
    2185            2988 :         NS_RELEASE(temp);
    2186            2988 :         gHttpHandler->ConnMgr()->AddActiveConn(conn, mEnt);
    2187                 :         rv = gHttpHandler->ConnMgr()->DispatchTransaction(mEnt, mTransaction,
    2188            2988 :                                                           mTransaction->Caps(),
    2189            5976 :                                                           conn);
    2190                 :     }
    2191                 :     else {
    2192                 :         // this transaction was dispatched off the pending q before all the
    2193                 :         // sockets established themselves.
    2194                 : 
    2195                 :         // We need to establish a small non-zero idle timeout so the connection
    2196                 :         // mgr perceives this socket as suitable for persistent connection reuse
    2197               1 :         const PRIntervalTime k5Sec = PR_SecondsToInterval(5);
    2198               1 :         if (k5Sec < gHttpHandler->IdleTimeout())
    2199               1 :             conn->SetIdleTimeout(k5Sec);
    2200                 :         else
    2201               0 :             conn->SetIdleTimeout(gHttpHandler->IdleTimeout());
    2202                 : 
    2203                 :         // After about 1 second allow for the possibility of restarting a
    2204                 :         // transaction due to server close. Keep at sub 1 second as that is the
    2205                 :         // minimum granularity we can expect a server to be timing out with.
    2206               1 :         conn->SetIsReusedAfter(950);
    2207                 : 
    2208               2 :         nsRefPtr<nsHttpConnection> copy(conn);  // because onmsg*() expects to drop a reference
    2209               1 :         gHttpHandler->ConnMgr()->OnMsgReclaimConnection(NS_OK, conn.forget().get());
    2210                 :     }
    2211                 : 
    2212            2989 :     return rv;
    2213                 : }
    2214                 : 
    2215                 : // method for nsITransportEventSink
    2216                 : NS_IMETHODIMP
    2217           11808 : nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
    2218                 :                                                          nsresult status,
    2219                 :                                                          PRUint64 progress,
    2220                 :                                                          PRUint64 progressMax)
    2221                 : {
    2222           11808 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    2223                 : 
    2224           11808 :     if (mTransaction)
    2225           11808 :         mTransaction->OnTransportStatus(trans, status, progress);
    2226                 : 
    2227           11808 :     if (trans != mSocketTransport)
    2228               0 :         return NS_OK;
    2229                 : 
    2230                 :     // if we are doing spdy coalescing and haven't recorded the ip address
    2231                 :     // for this entry before then make the hash key if our dns lookup
    2232                 :     // just completed
    2233                 : 
    2234           20360 :     if (status == nsISocketTransport::STATUS_CONNECTED_TO &&
    2235            2848 :         gHttpHandler->IsSpdyEnabled() &&
    2236            2848 :         gHttpHandler->CoalesceSpdy() &&
    2237            2848 :         mEnt && mEnt->mConnInfo && mEnt->mConnInfo->UsingSSL() &&
    2238               4 :         !mEnt->mConnInfo->UsingHttpProxy() &&
    2239               4 :         mEnt->mCoalescingKey.IsEmpty()) {
    2240                 : 
    2241                 :         PRNetAddr addr;
    2242               4 :         nsresult rv = mSocketTransport->GetPeerAddr(&addr);
    2243               4 :         if (NS_SUCCEEDED(rv)) {
    2244               4 :             mEnt->mCoalescingKey.SetCapacity(72);
    2245               4 :             PR_NetAddrToString(&addr, mEnt->mCoalescingKey.BeginWriting(), 64);
    2246                 :             mEnt->mCoalescingKey.SetLength(
    2247               4 :                 strlen(mEnt->mCoalescingKey.BeginReading()));
    2248                 : 
    2249               4 :             if (mEnt->mConnInfo->GetAnonymous())
    2250               0 :                 mEnt->mCoalescingKey.AppendLiteral("~A:");
    2251                 :             else
    2252               4 :                 mEnt->mCoalescingKey.AppendLiteral("~.:");
    2253               4 :             mEnt->mCoalescingKey.AppendInt(mEnt->mConnInfo->Port());
    2254                 : 
    2255               4 :             LOG(("nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus "
    2256                 :                  "STATUS_CONNECTED_TO Established New Coalescing Key for host "
    2257                 :                  "%s [%s]", mEnt->mConnInfo->Host(),
    2258                 :                  mEnt->mCoalescingKey.get()));
    2259                 : 
    2260               4 :             gHttpHandler->ConnMgr()->ProcessSpdyPendingQ(mEnt);
    2261                 :         }
    2262                 :     }
    2263                 : 
    2264           11808 :     switch (status) {
    2265                 :     case nsISocketTransport::STATUS_CONNECTING_TO:
    2266                 :         // Passed DNS resolution, now trying to connect, start the backup timer
    2267                 :         // only prevent creating another backup transport.
    2268                 :         // We also check for mEnt presence to not instantiate the timer after
    2269                 :         // this half open socket has already been abandoned.  It may happen
    2270                 :         // when we get this notification right between main-thread calls to
    2271                 :         // nsHttpConnectionMgr::Shutdown and nsSocketTransportService::Shutdown
    2272                 :         // where the first abandones all half open socket instances and only
    2273                 :         // after that the second stops the socket thread.
    2274            2982 :         if (mEnt && !mBackupTransport && !mSynTimer)
    2275            2982 :             SetupBackupTimer();
    2276            2982 :         break;
    2277                 : 
    2278                 :     case nsISocketTransport::STATUS_CONNECTED_TO:
    2279                 :         // TCP connection's up, now transfer or SSL negotiantion starts,
    2280                 :         // no need for backup socket
    2281            2848 :         CancelBackupTimer();
    2282            2848 :         break;
    2283                 : 
    2284                 :     default:
    2285            5978 :         break;
    2286                 :     }
    2287                 : 
    2288           11808 :     return NS_OK;
    2289                 : }
    2290                 : 
    2291                 : // method for nsIInterfaceRequestor
    2292                 : NS_IMETHODIMP
    2293               0 : nsHttpConnectionMgr::nsHalfOpenSocket::GetInterface(const nsIID &iid,
    2294                 :                                                     void **result)
    2295                 : {
    2296               0 :     if (mTransaction) {
    2297               0 :         nsCOMPtr<nsIInterfaceRequestor> callbacks;
    2298               0 :         mTransaction->GetSecurityCallbacks(getter_AddRefs(callbacks), nsnull);
    2299               0 :         if (callbacks)
    2300               0 :             return callbacks->GetInterface(iid, result);
    2301                 :     }
    2302               0 :     return NS_ERROR_NO_INTERFACE;
    2303                 : }
    2304                 : 
    2305                 : 
    2306                 : nsHttpConnection *
    2307               0 : nsHttpConnectionMgr::nsConnectionHandle::TakeHttpConnection()
    2308                 : {
    2309                 :     // return our connection object to the caller and clear it internally
    2310                 :     // do not drop our reference - the caller now owns it.
    2311                 : 
    2312               0 :     NS_ASSERTION(mConn, "no connection");
    2313               0 :     nsHttpConnection *conn = mConn;
    2314               0 :     mConn = nsnull;
    2315               0 :     return conn;
    2316                 : }
    2317                 : 
    2318                 : bool
    2319            2843 : nsHttpConnectionMgr::nsConnectionHandle::LastTransactionExpectedNoContent()
    2320                 : {
    2321            2843 :     return mConn->LastTransactionExpectedNoContent();
    2322                 : }
    2323                 : 
    2324                 : void
    2325            2832 : nsHttpConnectionMgr::
    2326                 : nsConnectionHandle::SetLastTransactionExpectedNoContent(bool val)
    2327                 : {
    2328            2832 :      mConn->SetLastTransactionExpectedNoContent(val);
    2329            2832 : }
    2330                 : 
    2331                 : nsISocketTransport *
    2332               0 : nsHttpConnectionMgr::nsConnectionHandle::Transport()
    2333                 : {
    2334               0 :     if (!mConn)
    2335               0 :         return nsnull;
    2336               0 :     return mConn->Transport();
    2337                 : }

Generated by: LCOV version 1.7