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

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

Generated by: LCOV version 1.7