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

LCOV - code coverage report
| Current view: | directory - netwerk/protocol/http - nsHttpConnectionMgr.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 1050 | 607 | 57.8 %
| Date: | 2012-04-21 | Functions: | 101 | 73 | 72.3 %

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

Generated by: LCOV version 1.7