LCOV - code coverage report
Current view: directory - netwerk/protocol/http - nsHttpConnection.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 567 342 60.3 %
Date: 2012-06-02 Functions: 44 33 75.0 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* vim:set ts=4 sw=4 sts=4 et cin: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Mozilla.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Netscape Communications.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2001
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Darin Fisher <darin@netscape.com> (original author)
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "nsHttpConnection.h"
      41                 : #include "nsHttpTransaction.h"
      42                 : #include "nsHttpRequestHead.h"
      43                 : #include "nsHttpResponseHead.h"
      44                 : #include "nsHttpHandler.h"
      45                 : #include "nsIOService.h"
      46                 : #include "nsISocketTransportService.h"
      47                 : #include "nsISocketTransport.h"
      48                 : #include "nsIServiceManager.h"
      49                 : #include "nsISSLSocketControl.h"
      50                 : #include "nsStringStream.h"
      51                 : #include "netCore.h"
      52                 : #include "nsNetCID.h"
      53                 : #include "nsProxyRelease.h"
      54                 : #include "prmem.h"
      55                 : #include "nsPreloadedStream.h"
      56                 : #include "SpdySession.h"
      57                 : #include "mozilla/Telemetry.h"
      58                 : #include "nsISupportsPriority.h"
      59                 : 
      60                 : #ifdef DEBUG
      61                 : // defined by the socket transport service while active
      62                 : extern PRThread *gSocketThread;
      63                 : #endif
      64                 : 
      65                 : static NS_DEFINE_CID(kSocketTransportServiceCID, NS_SOCKETTRANSPORTSERVICE_CID);
      66                 : 
      67                 : using namespace mozilla::net;
      68                 : 
      69                 : //-----------------------------------------------------------------------------
      70                 : // nsHttpConnection <public>
      71                 : //-----------------------------------------------------------------------------
      72                 : 
      73            2976 : nsHttpConnection::nsHttpConnection()
      74                 :     : mTransaction(nsnull)
      75                 :     , mLastReadTime(0)
      76                 :     , mIdleTimeout(0)
      77                 :     , mConsiderReusedAfterInterval(0)
      78                 :     , mConsiderReusedAfterEpoch(0)
      79                 :     , mCurrentBytesRead(0)
      80                 :     , mMaxBytesRead(0)
      81                 :     , mTotalBytesRead(0)
      82                 :     , mKeepAlive(true) // assume to keep-alive by default
      83                 :     , mKeepAliveMask(true)
      84                 :     , mSupportsPipelining(false) // assume low-grade server
      85                 :     , mIsReused(false)
      86                 :     , mCompletedProxyConnect(false)
      87                 :     , mLastTransactionExpectedNoContent(false)
      88                 :     , mIdleMonitoring(false)
      89                 :     , mHttp1xTransactionCount(0)
      90                 :     , mNPNComplete(false)
      91                 :     , mSetupNPNCalled(false)
      92                 :     , mUsingSpdy(false)
      93                 :     , mPriority(nsISupportsPriority::PRIORITY_NORMAL)
      94                 :     , mReportedSpdy(false)
      95            2976 :     , mEverUsedSpdy(false)
      96                 : {
      97            2976 :     LOG(("Creating nsHttpConnection @%x\n", this));
      98                 : 
      99                 :     // grab a reference to the handler to ensure that it doesn't go away.
     100            2976 :     nsHttpHandler *handler = gHttpHandler;
     101            2976 :     NS_ADDREF(handler);
     102            2976 : }
     103                 : 
     104            8928 : nsHttpConnection::~nsHttpConnection()
     105                 : {
     106            2976 :     LOG(("Destroying nsHttpConnection @%x\n", this));
     107                 : 
     108            2976 :     if (mCallbacks) {
     109               1 :         nsIInterfaceRequestor *cbs = nsnull;
     110               1 :         mCallbacks.swap(cbs);
     111               1 :         NS_ProxyRelease(mCallbackTarget, cbs);
     112                 :     }
     113                 : 
     114                 :     // release our reference to the handler
     115            2976 :     nsHttpHandler *handler = gHttpHandler;
     116            2976 :     NS_RELEASE(handler);
     117                 : 
     118            2976 :     if (!mEverUsedSpdy) {
     119            2976 :         LOG(("nsHttpConnection %p performed %d HTTP/1.x transactions\n",
     120                 :              this, mHttp1xTransactionCount));
     121                 :         mozilla::Telemetry::Accumulate(
     122            2976 :             mozilla::Telemetry::HTTP_REQUEST_PER_CONN, mHttp1xTransactionCount);
     123                 :     }
     124                 : 
     125            2976 :     if (mTotalBytesRead) {
     126            2499 :         PRUint32 totalKBRead = static_cast<PRUint32>(mTotalBytesRead >> 10);
     127            2499 :         LOG(("nsHttpConnection %p read %dkb on connection spdy=%d\n",
     128                 :              this, totalKBRead, mEverUsedSpdy));
     129                 :         mozilla::Telemetry::Accumulate(
     130                 :             mEverUsedSpdy ?
     131                 :               mozilla::Telemetry::SPDY_KBREAD_PER_CONN :
     132                 :               mozilla::Telemetry::HTTP_KBREAD_PER_CONN,
     133            2499 :             totalKBRead);
     134                 :     }
     135           11904 : }
     136                 : 
     137                 : nsresult
     138            2976 : nsHttpConnection::Init(nsHttpConnectionInfo *info,
     139                 :                        PRUint16 maxHangTime,
     140                 :                        nsISocketTransport *transport,
     141                 :                        nsIAsyncInputStream *instream,
     142                 :                        nsIAsyncOutputStream *outstream,
     143                 :                        nsIInterfaceRequestor *callbacks,
     144                 :                        nsIEventTarget *callbackTarget)
     145                 : {
     146            2976 :     NS_ABORT_IF_FALSE(transport && instream && outstream,
     147                 :                       "invalid socket information");
     148            2976 :     LOG(("nsHttpConnection::Init [this=%p "
     149                 :          "transport=%p instream=%p outstream=%p]\n",
     150                 :          this, transport, instream, outstream));
     151                 : 
     152            2976 :     NS_ENSURE_ARG_POINTER(info);
     153            2976 :     NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
     154                 : 
     155            2976 :     mConnInfo = info;
     156            2976 :     mMaxHangTime = PR_SecondsToInterval(maxHangTime);
     157            2976 :     mLastReadTime = PR_IntervalNow();
     158                 : 
     159            2976 :     mSocketTransport = transport;
     160            2976 :     mSocketIn = instream;
     161            2976 :     mSocketOut = outstream;
     162            2976 :     nsresult rv = mSocketTransport->SetEventSink(this, nsnull);
     163            2976 :     NS_ENSURE_SUCCESS(rv, rv);
     164                 : 
     165            2976 :     mCallbacks = callbacks;
     166            2976 :     mCallbackTarget = callbackTarget;
     167            2976 :     rv = mSocketTransport->SetSecurityCallbacks(this);
     168            2976 :     NS_ENSURE_SUCCESS(rv, rv);
     169                 : 
     170            2976 :     return NS_OK;
     171                 : }
     172                 : 
     173                 : void
     174               0 : nsHttpConnection::StartSpdy()
     175                 : {
     176               0 :     LOG(("nsHttpConnection::StartSpdy [this=%p]\n", this));
     177                 : 
     178               0 :     NS_ABORT_IF_FALSE(!mSpdySession, "mSpdySession should be null");
     179                 : 
     180               0 :     mUsingSpdy = true;
     181               0 :     mEverUsedSpdy = true;
     182                 : 
     183                 :     // Setting the connection as reused allows some transactions that fail
     184                 :     // with NS_ERROR_NET_RESET to be restarted and SPDY uses that code
     185                 :     // to handle clean rejections (such as those that arrived after
     186                 :     // a server goaway was generated).
     187               0 :     mIsReused = true;
     188                 : 
     189                 :     // If mTransaction is a pipeline object it might represent
     190                 :     // several requests. If so, we need to unpack that and
     191                 :     // pack them all into a new spdy session.
     192                 : 
     193               0 :     nsTArray<nsRefPtr<nsAHttpTransaction> > list;
     194               0 :     nsresult rv = mTransaction->TakeSubTransactions(list);
     195                 : 
     196               0 :     if (rv == NS_ERROR_ALREADY_OPENED) {
     197                 :         // Has the interface for TakeSubTransactions() changed?
     198               0 :         LOG(("TakeSubTranscations somehow called after "
     199                 :              "nsAHttpTransaction began processing\n"));
     200               0 :         NS_ABORT_IF_FALSE(false,
     201                 :                           "TakeSubTranscations somehow called after "
     202                 :                           "nsAHttpTransaction began processing");
     203               0 :         mTransaction->Close(NS_ERROR_ABORT);
     204                 :         return;
     205                 :     }
     206                 : 
     207               0 :     if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
     208                 :         // Has the interface for TakeSubTransactions() changed?
     209               0 :         LOG(("unexpected rv from nnsAHttpTransaction::TakeSubTransactions()"));
     210               0 :         NS_ABORT_IF_FALSE(false,
     211                 :                           "unexpected result from "
     212                 :                           "nsAHttpTransaction::TakeSubTransactions()");
     213               0 :         mTransaction->Close(NS_ERROR_ABORT);
     214                 :         return;
     215                 :     }
     216                 : 
     217               0 :     if (NS_FAILED(rv)) { // includes NS_ERROR_NOT_IMPLEMENTED
     218               0 :         NS_ABORT_IF_FALSE(list.IsEmpty(), "sub transaction list not empty");
     219                 : 
     220                 :         // This is ok - treat mTransaction as a single real request.
     221                 :         // Wrap the old http transaction into the new spdy session
     222                 :         // as the first stream.
     223                 :         mSpdySession = new SpdySession(mTransaction,
     224                 :                                        mSocketTransport,
     225               0 :                                        mPriority);
     226               0 :         LOG(("nsHttpConnection::StartSpdy moves single transaction %p "
     227                 :              "into SpdySession %p\n", mTransaction.get(), mSpdySession.get()));
     228                 :     }
     229                 :     else {
     230               0 :         NS_ABORT_IF_FALSE(!list.IsEmpty(), "sub transaction list empty");
     231                 :         
     232               0 :         PRInt32 count = list.Length();
     233                 : 
     234               0 :         LOG(("nsHttpConnection::StartSpdy moving transaction list len=%d "
     235                 :              "into SpdySession %p\n", count, mSpdySession.get()));
     236                 : 
     237               0 :         for (PRInt32 index = 0; index < count; ++index) {
     238               0 :             if (!mSpdySession) {
     239               0 :                 mSpdySession = new SpdySession(list[index],
     240                 :                                                mSocketTransport,
     241               0 :                                                mPriority);
     242                 :             }
     243                 :             else {
     244                 :                 // AddStream() cannot fail
     245               0 :                 if (!mSpdySession->AddStream(list[index], mPriority)) {
     246               0 :                     NS_ABORT_IF_FALSE(false, "SpdySession::AddStream failed");
     247               0 :                     LOG(("SpdySession::AddStream failed\n"));
     248               0 :                     mTransaction->Close(NS_ERROR_ABORT);
     249                 :                     return;
     250                 :                 }
     251                 :             }
     252                 :         }
     253                 :     }
     254                 : 
     255               0 :     mSupportsPipelining = false; // dont use http/1 pipelines with spdy
     256               0 :     mTransaction = mSpdySession;
     257               0 :     mIdleTimeout = gHttpHandler->SpdyTimeout();
     258                 : }
     259                 : 
     260                 : bool
     261            5824 : nsHttpConnection::EnsureNPNComplete()
     262                 : {
     263                 :     // NPN is only used by SPDY right now.
     264                 :     //
     265                 :     // If for some reason the components to check on NPN aren't available,
     266                 :     // this function will just return true to continue on and disable SPDY
     267                 : 
     268            5824 :     if (!mSocketTransport) {
     269                 :         // this cannot happen
     270               0 :         NS_ABORT_IF_FALSE(false,
     271                 :                           "EnsureNPNComplete socket transport precondition");
     272               0 :         mNPNComplete = true;
     273               0 :         return true;
     274                 :     }
     275                 : 
     276            5824 :     if (mNPNComplete)
     277            5808 :         return true;
     278                 :     
     279                 :     nsresult rv;
     280                 : 
     281              32 :     nsCOMPtr<nsISupports> securityInfo;
     282              32 :     nsCOMPtr<nsISSLSocketControl> ssl;
     283              32 :     nsCAutoString negotiatedNPN;
     284                 :     
     285              16 :     rv = mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
     286              16 :     if (NS_FAILED(rv))
     287               0 :         goto npnComplete;
     288                 : 
     289              16 :     ssl = do_QueryInterface(securityInfo, &rv);
     290              16 :     if (NS_FAILED(rv))
     291               0 :         goto npnComplete;
     292                 : 
     293              16 :     rv = ssl->GetNegotiatedNPN(negotiatedNPN);
     294              16 :     if (rv == NS_ERROR_NOT_CONNECTED) {
     295                 :     
     296                 :         // By writing 0 bytes to the socket the SSL handshake machine is
     297                 :         // pushed forward.
     298              12 :         PRUint32 count = 0;
     299              12 :         rv = mSocketOut->Write("", 0, &count);
     300                 : 
     301              12 :         if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK)
     302               0 :             goto npnComplete;
     303              12 :         return false;
     304                 :     }
     305                 : 
     306               4 :     if (NS_FAILED(rv))
     307               0 :         goto npnComplete;
     308                 : 
     309               4 :     LOG(("nsHttpConnection::EnsureNPNComplete %p negotiated to '%s'",
     310                 :          this, negotiatedNPN.get()));
     311                 : 
     312               4 :     if (negotiatedNPN.Equals(NS_LITERAL_CSTRING("spdy/2")))
     313               0 :         StartSpdy();
     314                 : 
     315                 :     mozilla::Telemetry::Accumulate(mozilla::Telemetry::SPDY_NPN_CONNECT,
     316               4 :                                    mUsingSpdy);
     317                 : 
     318                 : npnComplete:
     319               4 :     LOG(("nsHttpConnection::EnsureNPNComplete setting complete to true"));
     320               4 :     mNPNComplete = true;
     321               4 :     return true;
     322                 : }
     323                 : 
     324                 : // called on the socket thread
     325                 : nsresult
     326            2975 : nsHttpConnection::Activate(nsAHttpTransaction *trans, PRUint8 caps, PRInt32 pri)
     327                 : {
     328                 :     nsresult rv;
     329                 : 
     330            2975 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     331            2975 :     LOG(("nsHttpConnection::Activate [this=%x trans=%x caps=%x]\n",
     332                 :          this, trans, caps));
     333                 : 
     334            2975 :     mPriority = pri;
     335            2975 :     if (mTransaction && mUsingSpdy)
     336               0 :         return AddTransaction(trans, pri);
     337                 : 
     338            2975 :     NS_ENSURE_ARG_POINTER(trans);
     339            2975 :     NS_ENSURE_TRUE(!mTransaction, NS_ERROR_IN_PROGRESS);
     340                 : 
     341                 :     // Update security callbacks
     342            5950 :     nsCOMPtr<nsIInterfaceRequestor> callbacks;
     343            5950 :     nsCOMPtr<nsIEventTarget>        callbackTarget;
     344            2975 :     trans->GetSecurityCallbacks(getter_AddRefs(callbacks),
     345            5950 :                                 getter_AddRefs(callbackTarget));
     346            2975 :     if (callbacks != mCallbacks) {
     347               0 :         mCallbacks.swap(callbacks);
     348               0 :         if (callbacks)
     349               0 :             NS_ProxyRelease(mCallbackTarget, callbacks);
     350               0 :         mCallbackTarget = callbackTarget;
     351                 :     }
     352                 : 
     353            2975 :     SetupNPN(caps); // only for spdy
     354                 : 
     355                 :     // take ownership of the transaction
     356            2975 :     mTransaction = trans;
     357                 : 
     358            2975 :     NS_ABORT_IF_FALSE(!mIdleMonitoring,
     359                 :                       "Activating a connection with an Idle Monitor");
     360            2975 :     mIdleMonitoring = false;
     361                 : 
     362                 :     // set mKeepAlive according to what will be requested
     363            2975 :     mKeepAliveMask = mKeepAlive = (caps & NS_HTTP_ALLOW_KEEPALIVE);
     364                 : 
     365                 :     // need to handle HTTP CONNECT tunnels if this is the first time if
     366                 :     // we are tunneling through a proxy
     367            5950 :     if (((mConnInfo->UsingSSL() && mConnInfo->UsingHttpProxy()) ||
     368            2975 :          mConnInfo->ShouldForceConnectMethod()) && !mCompletedProxyConnect) {
     369               0 :         rv = SetupProxyConnect();
     370               0 :         if (NS_FAILED(rv))
     371               0 :             goto failed_activation;
     372                 :     }
     373                 : 
     374                 :     // Clear the per activation counter
     375            2975 :     mCurrentBytesRead = 0;
     376                 : 
     377                 :     // The overflow state is not needed between activations
     378            2975 :     mInputOverflow = nsnull;
     379                 : 
     380            2975 :     rv = OnOutputStreamReady(mSocketOut);
     381                 :     
     382                 : failed_activation:
     383            2975 :     if (NS_FAILED(rv)) {
     384               0 :         mTransaction = nsnull;
     385                 :     }
     386                 : 
     387            2975 :     return rv;
     388                 : }
     389                 : 
     390                 : void
     391            2981 : nsHttpConnection::SetupNPN(PRUint8 caps)
     392                 : {
     393            2981 :     if (mSetupNPNCalled)                                /* do only once */
     394               5 :         return;
     395            2976 :     mSetupNPNCalled = true;
     396                 : 
     397                 :     // Setup NPN Negotiation if necessary (only for SPDY)
     398            2976 :     if (!mNPNComplete) {
     399                 : 
     400            2976 :         mNPNComplete = true;
     401                 : 
     402            2988 :         if (mConnInfo->UsingSSL() &&
     403               4 :             !(caps & NS_HTTP_DISALLOW_SPDY) &&
     404               4 :             !mConnInfo->UsingHttpProxy() &&
     405               4 :             gHttpHandler->IsSpdyEnabled()) {
     406               4 :             LOG(("nsHttpConnection::Init Setting up SPDY Negotiation"));
     407               8 :             nsCOMPtr<nsISupports> securityInfo;
     408                 :             nsresult rv =
     409               4 :                 mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
     410               4 :             if (NS_FAILED(rv))
     411                 :                 return;
     412                 : 
     413                 :             nsCOMPtr<nsISSLSocketControl> ssl =
     414               8 :                 do_QueryInterface(securityInfo, &rv);
     415               4 :             if (NS_FAILED(rv))
     416                 :                 return;
     417                 : 
     418              12 :             nsTArray<nsCString> protocolArray;
     419               4 :             protocolArray.AppendElement(NS_LITERAL_CSTRING("spdy/2"));
     420               4 :             protocolArray.AppendElement(NS_LITERAL_CSTRING("http/1.1"));
     421               4 :             if (NS_SUCCEEDED(ssl->SetNPNList(protocolArray))) {
     422               4 :                 LOG(("nsHttpConnection::Init Setting up SPDY Negotiation OK"));
     423               4 :                 mNPNComplete = false;
     424                 :             }
     425                 :         }
     426                 :     }
     427                 : }
     428                 : 
     429                 : void
     430            2819 : nsHttpConnection::HandleAlternateProtocol(nsHttpResponseHead *responseHead)
     431                 : {
     432                 :     // Look for the Alternate-Protocol header. Alternate-Protocol is
     433                 :     // essentially a way to rediect future transactions from http to
     434                 :     // spdy.
     435                 :     //
     436                 : 
     437            2819 :     if (!gHttpHandler->IsSpdyEnabled() || mUsingSpdy)
     438               0 :         return;
     439                 : 
     440            2819 :     const char *val = responseHead->PeekHeader(nsHttp::Alternate_Protocol);
     441            2819 :     if (!val)
     442            2819 :         return;
     443                 : 
     444                 :     // The spec allows redirections to any port, but due to concerns over
     445                 :     // silently redirecting to stealth ports we only allow port 443
     446                 :     //
     447                 :     // Alternate-Protocol: 5678:somethingelse, 443:npn-spdy/2
     448                 : 
     449               0 :     if (nsHttp::FindToken(val, "443:npn-spdy/2", HTTP_HEADER_VALUE_SEPS)) {
     450               0 :         LOG(("Connection %p Transaction %p found Alternate-Protocol "
     451                 :              "header %s", this, mTransaction.get(), val));
     452               0 :         gHttpHandler->ConnMgr()->ReportSpdyAlternateProtocol(this);
     453                 :     }
     454                 : }
     455                 : 
     456                 : nsresult
     457               0 : nsHttpConnection::AddTransaction(nsAHttpTransaction *httpTransaction,
     458                 :                                  PRInt32 priority)
     459                 : {
     460               0 :     LOG(("nsHttpConnection::AddTransaction for SPDY"));
     461                 : 
     462               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     463               0 :     NS_ABORT_IF_FALSE(mSpdySession && mUsingSpdy,
     464                 :                       "AddTransaction to live http connection without spdy");
     465               0 :     NS_ABORT_IF_FALSE(mTransaction,
     466                 :                       "AddTransaction to idle http connection");
     467                 :     
     468               0 :     if (!mSpdySession->AddStream(httpTransaction, priority)) {
     469               0 :         NS_ABORT_IF_FALSE(0, "AddStream should never fail due to"
     470                 :                           "RoomForMore() admission check");
     471               0 :         return NS_ERROR_FAILURE;
     472                 :     }
     473                 : 
     474               0 :     ResumeSend();
     475                 : 
     476               0 :     return NS_OK;
     477                 : }
     478                 : 
     479                 : void
     480            3137 : nsHttpConnection::Close(nsresult reason)
     481                 : {
     482            3137 :     LOG(("nsHttpConnection::Close [this=%x reason=%x]\n", this, reason));
     483                 : 
     484            3137 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     485                 : 
     486            3137 :     if (NS_FAILED(reason)) {
     487            3137 :         if (mIdleMonitoring)
     488               5 :             EndIdleMonitoring();
     489                 : 
     490            3137 :         if (mSocketTransport) {
     491            3137 :             mSocketTransport->SetSecurityCallbacks(nsnull);
     492            3137 :             mSocketTransport->SetEventSink(nsnull, nsnull);
     493            3137 :             mSocketTransport->Close(reason);
     494                 :         }
     495            3137 :         mKeepAlive = false;
     496                 :     }
     497            3137 : }
     498                 : 
     499                 : // called on the socket thread
     500                 : nsresult
     501               0 : nsHttpConnection::ProxyStartSSL()
     502                 : {
     503               0 :     LOG(("nsHttpConnection::ProxyStartSSL [this=%x]\n", this));
     504                 : #ifdef DEBUG
     505               0 :     NS_PRECONDITION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     506                 : #endif
     507                 : 
     508               0 :     nsCOMPtr<nsISupports> securityInfo;
     509               0 :     nsresult rv = mSocketTransport->GetSecurityInfo(getter_AddRefs(securityInfo));
     510               0 :     if (NS_FAILED(rv)) return rv;
     511                 : 
     512               0 :     nsCOMPtr<nsISSLSocketControl> ssl = do_QueryInterface(securityInfo, &rv);
     513               0 :     if (NS_FAILED(rv)) return rv;
     514                 : 
     515               0 :     return ssl->ProxyStartSSL();
     516                 : }
     517                 : 
     518                 : void
     519               1 : nsHttpConnection::DontReuse()
     520                 : {
     521               1 :     mKeepAliveMask = false;
     522               1 :     mKeepAlive = false;
     523               1 :     mIdleTimeout = 0;
     524               1 :     if (mSpdySession)
     525               0 :         mSpdySession->DontReuse();
     526               1 : }
     527                 : 
     528                 : bool
     529            2978 : nsHttpConnection::CanReuse()
     530                 : {
     531                 :     bool canReuse;
     532                 :     
     533            2978 :     if (mUsingSpdy)
     534               0 :         canReuse = mSpdySession->CanReuse();
     535                 :     else
     536            2978 :         canReuse = IsKeepAlive();
     537                 :     
     538            2978 :     canReuse = canReuse && (IdleTime() < mIdleTimeout) && IsAlive();
     539                 : 
     540                 :     // An idle persistent connection should not have data waiting to be read
     541                 :     // before a request is sent. Data here is likely a 408 timeout response
     542                 :     // which we would deal with later on through the restart logic, but that
     543                 :     // path is more expensive than just closing the socket now.
     544                 : 
     545                 :     PRUint32 dataSize;
     546            2984 :     if (canReuse && mSocketIn && !mUsingSpdy &&
     547               6 :         NS_SUCCEEDED(mSocketIn->Available(&dataSize)) && dataSize) {
     548               0 :         LOG(("nsHttpConnection::CanReuse %p %s"
     549                 :              "Socket not reusable because read data pending (%d) on it.\n",
     550                 :              this, mConnInfo->Host(), dataSize));
     551               0 :         canReuse = false;
     552                 :     }
     553            2978 :     return canReuse;
     554                 : }
     555                 : 
     556                 : bool
     557               0 : nsHttpConnection::CanDirectlyActivate()
     558                 : {
     559                 :     // return true if a new transaction can be addded to ths connection at any
     560                 :     // time through Activate(). In practice this means this is a healthy SPDY
     561                 :     // connection with room for more concurrent streams.
     562                 :     
     563               0 :     return UsingSpdy() && CanReuse() && mSpdySession->RoomForMoreStreams();
     564                 : }
     565                 : 
     566                 : PRIntervalTime
     567              19 : nsHttpConnection::IdleTime()
     568                 : {
     569                 :     return mSpdySession ?
     570              19 :         mSpdySession->IdleTime() : (PR_IntervalNow() - mLastReadTime);
     571                 : }
     572                 : 
     573                 : // returns the number of seconds left before the allowable idle period
     574                 : // expires, or 0 if the period has already expied.
     575                 : PRUint32
     576               6 : nsHttpConnection::TimeToLive()
     577                 : {
     578               6 :     if (IdleTime() >= mIdleTimeout)
     579               0 :         return 0;
     580               6 :     PRUint32 timeToLive = PR_IntervalToSeconds(mIdleTimeout - IdleTime());
     581                 : 
     582                 :     // a positive amount of time can be rounded to 0. Because 0 is used
     583                 :     // as the expiration signal, round all values from 0 to 1 up to 1.
     584               6 :     if (!timeToLive)
     585               1 :         timeToLive = 1;
     586               6 :     return timeToLive;
     587                 : }
     588                 : 
     589                 : bool
     590               6 : nsHttpConnection::IsAlive()
     591                 : {
     592               6 :     if (!mSocketTransport)
     593               0 :         return false;
     594                 : 
     595                 :     // SocketTransport::IsAlive can run the SSL state machine, so make sure
     596                 :     // the NPN options are set before that happens.
     597               6 :     SetupNPN(0);
     598                 : 
     599                 :     bool alive;
     600               6 :     nsresult rv = mSocketTransport->IsAlive(&alive);
     601               6 :     if (NS_FAILED(rv))
     602               0 :         alive = false;
     603                 : 
     604                 : //#define TEST_RESTART_LOGIC
     605                 : #ifdef TEST_RESTART_LOGIC
     606                 :     if (!alive) {
     607                 :         LOG(("pretending socket is still alive to test restart logic\n"));
     608                 :         alive = true;
     609                 :     }
     610                 : #endif
     611                 : 
     612               6 :     return alive;
     613                 : }
     614                 : 
     615                 : bool
     616               4 : nsHttpConnection::SupportsPipelining(nsHttpResponseHead *responseHead)
     617                 : {
     618                 :     // SPDY supports infinite parallelism, so no need to pipeline.
     619               4 :     if (mUsingSpdy)
     620               0 :         return false;
     621                 : 
     622                 :     // XXX there should be a strict mode available that disables this
     623                 :     // blacklisting.
     624                 : 
     625                 :     // assuming connection is HTTP/1.1 with keep-alive enabled
     626               4 :     if (mConnInfo->UsingHttpProxy() && !mConnInfo->UsingSSL()) {
     627                 :         // XXX check for bad proxy servers...
     628               0 :         return true;
     629                 :     }
     630                 : 
     631                 :     // XXX what about checking for a Via header? (transparent proxies)
     632                 : 
     633                 :     // check for bad origin servers
     634               4 :     const char *val = responseHead->PeekHeader(nsHttp::Server);
     635               4 :     if (!val)
     636               0 :         return false; // no header, no love
     637                 : 
     638                 :     // The blacklist is indexed by the first character. All of these servers are
     639                 :     // known to return their identifier as the first thing in the server string,
     640                 :     // so we can do a leading match. 
     641                 : 
     642                 :     static const char *bad_servers[26][6] = {
     643                 :         { nsnull }, { nsnull }, { nsnull }, { nsnull },                 // a - d
     644                 :         { "EFAServer/", nsnull },                                       // e
     645                 :         { nsnull }, { nsnull }, { nsnull }, { nsnull },                 // f - i
     646                 :         { nsnull }, { nsnull }, { nsnull },                             // j - l 
     647                 :         { "Microsoft-IIS/4.", "Microsoft-IIS/5.", nsnull },             // m
     648                 :         { "Netscape-Enterprise/3.", "Netscape-Enterprise/4.", 
     649                 :           "Netscape-Enterprise/5.", "Netscape-Enterprise/6.", nsnull }, // n
     650                 :         { nsnull }, { nsnull }, { nsnull }, { nsnull },                 // o - r
     651                 :         { nsnull }, { nsnull }, { nsnull }, { nsnull },                 // s - v
     652                 :         { "WebLogic 3.", "WebLogic 4.","WebLogic 5.", "WebLogic 6.",
     653                 :           "Winstone Servlet Engine v0.", nsnull },                      // w 
     654                 :         { nsnull }, { nsnull }, { nsnull }                              // x - z
     655                 :     };  
     656                 : 
     657               4 :     int index = val[0] - 'A'; // the whole table begins with capital letters
     658               4 :     if ((index >= 0) && (index <= 25))
     659                 :     {
     660               0 :         for (int i = 0; bad_servers[index][i] != nsnull; i++) {
     661               0 :             if (!PL_strncmp (val, bad_servers[index][i], strlen (bad_servers[index][i]))) {
     662               0 :                 LOG(("looks like this server does not support pipelining"));
     663               0 :                 return false;
     664                 :             }
     665                 :         }
     666                 :     }
     667                 : 
     668                 :     // ok, let's allow pipelining to this server
     669               4 :     return true;
     670                 : }
     671                 : 
     672                 : //----------------------------------------------------------------------------
     673                 : // nsHttpConnection::nsAHttpConnection compatible methods
     674                 : //----------------------------------------------------------------------------
     675                 : 
     676                 : nsresult
     677            2819 : nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
     678                 :                                      nsHttpRequestHead *requestHead,
     679                 :                                      nsHttpResponseHead *responseHead,
     680                 :                                      bool *reset)
     681                 : {
     682            2819 :     LOG(("nsHttpConnection::OnHeadersAvailable [this=%p trans=%p response-head=%p]\n",
     683                 :         this, trans, responseHead));
     684                 : 
     685            2819 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     686            2819 :     NS_ENSURE_ARG_POINTER(trans);
     687            2819 :     NS_ASSERTION(responseHead, "No response head?");
     688                 : 
     689                 :     // If the server issued an explicit timeout, then we need to close down the
     690                 :     // socket transport.  We pass an error code of NS_ERROR_NET_RESET to
     691                 :     // trigger the transactions 'restart' mechanism.  We tell it to reset its
     692                 :     // response headers so that it will be ready to receive the new response.
     693            2819 :     if (responseHead->Status() == 408) {
     694               0 :         Close(NS_ERROR_NET_RESET);
     695               0 :         *reset = true;
     696               0 :         return NS_OK;
     697                 :     }
     698                 : 
     699                 :     // we won't change our keep-alive policy unless the server has explicitly
     700                 :     // told us to do so.
     701                 : 
     702                 :     // inspect the connection headers for keep-alive info provided the
     703                 :     // transaction completed successfully.
     704            2819 :     const char *val = responseHead->PeekHeader(nsHttp::Connection);
     705            2819 :     if (!val)
     706              25 :         val = responseHead->PeekHeader(nsHttp::Proxy_Connection);
     707                 : 
     708                 :     // reset to default (the server may have changed since we last checked)
     709            2819 :     mSupportsPipelining = false;
     710                 : 
     711            5606 :     if ((responseHead->Version() < NS_HTTP_VERSION_1_1) ||
     712            2787 :         (requestHead->Version() < NS_HTTP_VERSION_1_1)) {
     713                 :         // HTTP/1.0 connections are by default NOT persistent
     714              32 :         if (val && !PL_strcasecmp(val, "keep-alive"))
     715               0 :             mKeepAlive = true;
     716                 :         else
     717              32 :             mKeepAlive = false;
     718                 :     }
     719                 :     else {
     720                 :         // HTTP/1.1 connections are by default persistent
     721            2787 :         if (val && !PL_strcasecmp(val, "close")) 
     722            2783 :             mKeepAlive = false;
     723                 :         else {
     724               4 :             mKeepAlive = true;
     725                 : 
     726                 :             // Do not support pipelining when we are establishing
     727                 :             // an SSL tunnel though an HTTP proxy. Pipelining support
     728                 :             // determination must be based on comunication with the
     729                 :             // target server in this case. See bug 422016 for futher
     730                 :             // details.
     731               4 :             if (!mProxyConnectStream)
     732               4 :               mSupportsPipelining = SupportsPipelining(responseHead);
     733                 :         }
     734                 :     }
     735            2819 :     mKeepAliveMask = mKeepAlive;
     736                 : 
     737                 :     // if this connection is persistent, then the server may send a "Keep-Alive"
     738                 :     // header specifying the maximum number of times the connection can be
     739                 :     // reused as well as the maximum amount of time the connection can be idle
     740                 :     // before the server will close it.  we ignore the max reuse count, because
     741                 :     // a "keep-alive" connection is by definition capable of being reused, and
     742                 :     // we only care about being able to reuse it once.  if a timeout is not 
     743                 :     // specified then we use our advertized timeout value.
     744            2819 :     if (mKeepAlive) {
     745               4 :         val = responseHead->PeekHeader(nsHttp::Keep_Alive);
     746                 : 
     747               4 :         if (!mUsingSpdy) {
     748               4 :             const char *cp = PL_strcasestr(val, "timeout=");
     749               4 :             if (cp)
     750               0 :                 mIdleTimeout = PR_SecondsToInterval((PRUint32) atoi(cp + 8));
     751                 :             else
     752               4 :                 mIdleTimeout = gHttpHandler->IdleTimeout();
     753                 :         }
     754                 :         else {
     755               0 :             mIdleTimeout = gHttpHandler->SpdyTimeout();
     756                 :         }
     757                 :         
     758               4 :         LOG(("Connection can be reused [this=%x idle-timeout=%usec]\n",
     759                 :              this, PR_IntervalToSeconds(mIdleTimeout)));
     760                 :     }
     761                 : 
     762            2819 :     if (!mProxyConnectStream)
     763            2819 :         HandleAlternateProtocol(responseHead);
     764                 : 
     765                 :     // if we're doing an SSL proxy connect, then we need to check whether or not
     766                 :     // the connect was successful.  if so, then we have to reset the transaction
     767                 :     // and step-up the socket connection to SSL. finally, we have to wake up the
     768                 :     // socket write request.
     769            2819 :     if (mProxyConnectStream) {
     770               0 :         NS_ABORT_IF_FALSE(!mUsingSpdy,
     771                 :                           "SPDY NPN Complete while using proxy connect stream");
     772               0 :         mProxyConnectStream = 0;
     773               0 :         if (responseHead->Status() == 200) {
     774               0 :             LOG(("proxy CONNECT succeeded! ssl=%s\n",
     775                 :                  mConnInfo->UsingSSL() ? "true" :"false"));
     776               0 :             *reset = true;
     777                 :             nsresult rv;
     778               0 :             if (mConnInfo->UsingSSL()) {
     779               0 :                 rv = ProxyStartSSL();
     780               0 :                 if (NS_FAILED(rv)) // XXX need to handle this for real
     781               0 :                     LOG(("ProxyStartSSL failed [rv=%x]\n", rv));
     782                 :             }
     783               0 :             mCompletedProxyConnect = true;
     784               0 :             rv = mSocketOut->AsyncWait(this, 0, 0, nsnull);
     785                 :             // XXX what if this fails -- need to handle this error
     786               0 :             NS_ASSERTION(NS_SUCCEEDED(rv), "mSocketOut->AsyncWait failed");
     787                 :         }
     788                 :         else {
     789               0 :             LOG(("proxy CONNECT failed! ssl=%s\n",
     790                 :                  mConnInfo->UsingSSL() ? "true" :"false"));
     791               0 :             mTransaction->SetSSLConnectFailed();
     792                 :         }
     793                 :     }
     794                 :     
     795            2819 :     const char *upgradeReq = requestHead->PeekHeader(nsHttp::Upgrade);
     796            2819 :     if (upgradeReq) {
     797               0 :         LOG(("HTTP Upgrade in play - disable keepalive\n"));
     798               0 :         DontReuse();
     799                 :     }
     800                 :     
     801            2819 :     if (responseHead->Status() == 101) {
     802               0 :         const char *upgradeResp = responseHead->PeekHeader(nsHttp::Upgrade);
     803               0 :         if (!upgradeReq || !upgradeResp ||
     804                 :             !nsHttp::FindToken(upgradeResp, upgradeReq,
     805               0 :                                HTTP_HEADER_VALUE_SEPS)) {
     806               0 :             LOG(("HTTP 101 Upgrade header mismatch req = %s, resp = %s\n",
     807                 :                  upgradeReq, upgradeResp));
     808               0 :             Close(NS_ERROR_ABORT);
     809                 :         }
     810                 :         else {
     811               0 :             LOG(("HTTP Upgrade Response to %s\n", upgradeResp));
     812                 :         }
     813                 :     }
     814                 : 
     815            2819 :     return NS_OK;
     816                 : }
     817                 : 
     818                 : bool
     819            2975 : nsHttpConnection::IsReused()
     820                 : {
     821            2975 :     if (mIsReused)
     822               0 :         return true;
     823            2975 :     if (!mConsiderReusedAfterInterval)
     824            2975 :         return false;
     825                 :     
     826                 :     // ReusedAfter allows a socket to be consider reused only after a certain
     827                 :     // interval of time has passed
     828               0 :     return (PR_IntervalNow() - mConsiderReusedAfterEpoch) >=
     829               0 :         mConsiderReusedAfterInterval;
     830                 : }
     831                 : 
     832                 : void
     833               1 : nsHttpConnection::SetIsReusedAfter(PRUint32 afterMilliseconds)
     834                 : {
     835               1 :     mConsiderReusedAfterEpoch = PR_IntervalNow();
     836               1 :     mConsiderReusedAfterInterval = PR_MillisecondsToInterval(afterMilliseconds);
     837               1 : }
     838                 : 
     839                 : nsresult
     840               0 : nsHttpConnection::TakeTransport(nsISocketTransport  **aTransport,
     841                 :                                 nsIAsyncInputStream **aInputStream,
     842                 :                                 nsIAsyncOutputStream **aOutputStream)
     843                 : {
     844               0 :     if (mUsingSpdy)
     845               0 :         return NS_ERROR_FAILURE;
     846               0 :     if (mTransaction && !mTransaction->IsDone())
     847               0 :         return NS_ERROR_IN_PROGRESS;
     848               0 :     if (!(mSocketTransport && mSocketIn && mSocketOut))
     849               0 :         return NS_ERROR_NOT_INITIALIZED;
     850                 : 
     851               0 :     if (mInputOverflow)
     852               0 :         mSocketIn = mInputOverflow.forget();
     853                 : 
     854               0 :     NS_IF_ADDREF(*aTransport = mSocketTransport);
     855               0 :     NS_IF_ADDREF(*aInputStream = mSocketIn);
     856               0 :     NS_IF_ADDREF(*aOutputStream = mSocketOut);
     857                 : 
     858               0 :     mSocketTransport->SetSecurityCallbacks(nsnull);
     859               0 :     mSocketTransport->SetEventSink(nsnull, nsnull);
     860               0 :     mSocketTransport = nsnull;
     861               0 :     mSocketIn = nsnull;
     862               0 :     mSocketOut = nsnull;
     863                 :     
     864               0 :     return NS_OK;
     865                 : }
     866                 : 
     867                 : void
     868               0 : nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
     869                 : {
     870               0 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     871                 : 
     872                 :     // make sure timer didn't tick before Activate()
     873               0 :     if (!mTransaction)
     874               0 :         return;
     875                 : 
     876                 :     // Spdy in the future actually should implement some timeout handling
     877                 :     // using the SPDY ping frame.
     878               0 :     if (mSpdySession) {
     879               0 :         mSpdySession->ReadTimeoutTick(now);
     880               0 :         return;
     881                 :     }
     882                 :     
     883                 :     // Pending patches places pipeline rescheduling code will go here
     884                 : 
     885                 : }
     886                 : 
     887                 : void
     888            2975 : nsHttpConnection::GetSecurityInfo(nsISupports **secinfo)
     889                 : {
     890            2975 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     891                 : 
     892            2975 :     if (mSocketTransport) {
     893            2975 :         if (NS_FAILED(mSocketTransport->GetSecurityInfo(secinfo)))
     894               0 :             *secinfo = nsnull;
     895                 :     }
     896            2975 : }
     897                 : 
     898                 : nsresult
     899               0 : nsHttpConnection::PushBack(const char *data, PRUint32 length)
     900                 : {
     901               0 :     LOG(("nsHttpConnection::PushBack [this=%p, length=%d]\n", this, length));
     902                 : 
     903               0 :     if (mInputOverflow) {
     904               0 :         NS_ERROR("nsHttpConnection::PushBack only one buffer supported");
     905               0 :         return NS_ERROR_UNEXPECTED;
     906                 :     }
     907                 :     
     908               0 :     mInputOverflow = new nsPreloadedStream(mSocketIn, data, length);
     909               0 :     return NS_OK;
     910                 : }
     911                 : 
     912                 : nsresult
     913               0 : nsHttpConnection::ResumeSend()
     914                 : {
     915               0 :     LOG(("nsHttpConnection::ResumeSend [this=%p]\n", this));
     916                 : 
     917               0 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     918                 : 
     919               0 :     if (mSocketOut)
     920               0 :         return mSocketOut->AsyncWait(this, 0, 0, nsnull);
     921                 : 
     922               0 :     NS_NOTREACHED("no socket output stream");
     923               0 :     return NS_ERROR_UNEXPECTED;
     924                 : }
     925                 : 
     926                 : nsresult
     927               0 : nsHttpConnection::ResumeRecv()
     928                 : {
     929               0 :     LOG(("nsHttpConnection::ResumeRecv [this=%p]\n", this));
     930                 : 
     931               0 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     932                 : 
     933               0 :     if (mSocketIn)
     934               0 :         return mSocketIn->AsyncWait(this, 0, 0, nsnull);
     935                 : 
     936               0 :     NS_NOTREACHED("no socket input stream");
     937               0 :     return NS_ERROR_UNEXPECTED;
     938                 : }
     939                 : 
     940                 : void
     941               5 : nsHttpConnection::BeginIdleMonitoring()
     942                 : {
     943               5 :     LOG(("nsHttpConnection::BeginIdleMonitoring [this=%p]\n", this));
     944               5 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     945               5 :     NS_ABORT_IF_FALSE(!mTransaction, "BeginIdleMonitoring() while active");
     946               5 :     NS_ABORT_IF_FALSE(!mUsingSpdy, "Idle monitoring of spdy not allowed");
     947                 : 
     948               5 :     LOG(("Entering Idle Monitoring Mode [this=%p]", this));
     949               5 :     mIdleMonitoring = true;
     950               5 :     if (mSocketIn)
     951               5 :         mSocketIn->AsyncWait(this, 0, 0, nsnull);
     952               5 : }
     953                 : 
     954                 : void
     955               5 : nsHttpConnection::EndIdleMonitoring()
     956                 : {
     957               5 :     LOG(("nsHttpConnection::EndIdleMonitoring [this=%p]\n", this));
     958               5 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     959               5 :     NS_ABORT_IF_FALSE(!mTransaction, "EndIdleMonitoring() while active");
     960                 : 
     961               5 :     if (mIdleMonitoring) {
     962               5 :         LOG(("Leaving Idle Monitoring Mode [this=%p]", this));
     963               5 :         mIdleMonitoring = false;
     964               5 :         if (mSocketIn)
     965               5 :             mSocketIn->AsyncWait(nsnull, 0, 0, nsnull);
     966                 :     }
     967               5 : }
     968                 : 
     969                 : //-----------------------------------------------------------------------------
     970                 : // nsHttpConnection <private>
     971                 : //-----------------------------------------------------------------------------
     972                 : 
     973                 : void
     974            2975 : nsHttpConnection::CloseTransaction(nsAHttpTransaction *trans, nsresult reason)
     975                 : {
     976            2975 :     LOG(("nsHttpConnection::CloseTransaction[this=%x trans=%x reason=%x]\n",
     977                 :         this, trans, reason));
     978                 : 
     979            2975 :     NS_ASSERTION(trans == mTransaction, "wrong transaction");
     980            2975 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     981                 : 
     982            2975 :     if (mCurrentBytesRead > mMaxBytesRead)
     983            2499 :         mMaxBytesRead = mCurrentBytesRead;
     984                 : 
     985                 :     // mask this error code because its not a real error.
     986            2975 :     if (reason == NS_BASE_STREAM_CLOSED)
     987            2814 :         reason = NS_OK;
     988                 : 
     989            2975 :     if (mUsingSpdy) {
     990               0 :         DontReuse();
     991                 :         // if !mSpdySession then mUsingSpdy must be false for canreuse()
     992               0 :         mUsingSpdy = false;
     993               0 :         mSpdySession = nsnull;
     994                 :     }
     995                 : 
     996            2975 :     mHttp1xTransactionCount += mTransaction->Http1xTransactionCount();
     997                 : 
     998            2975 :     mTransaction->Close(reason);
     999            2975 :     mTransaction = nsnull;
    1000                 : 
    1001            2975 :     if (mCallbacks) {
    1002            2975 :         nsIInterfaceRequestor *cbs = nsnull;
    1003            2975 :         mCallbacks.swap(cbs);
    1004            2975 :         NS_ProxyRelease(mCallbackTarget, cbs);
    1005                 :     }
    1006                 : 
    1007            2975 :     if (NS_FAILED(reason))
    1008             161 :         Close(reason);
    1009                 : 
    1010                 :     // flag the connection as reused here for convenience sake.  certainly
    1011                 :     // it might be going away instead ;-)
    1012            2975 :     mIsReused = true;
    1013            2975 : }
    1014                 : 
    1015                 : NS_METHOD
    1016               0 : nsHttpConnection::ReadFromStream(nsIInputStream *input,
    1017                 :                                  void *closure,
    1018                 :                                  const char *buf,
    1019                 :                                  PRUint32 offset,
    1020                 :                                  PRUint32 count,
    1021                 :                                  PRUint32 *countRead)
    1022                 : {
    1023                 :     // thunk for nsIInputStream instance
    1024               0 :     nsHttpConnection *conn = (nsHttpConnection *) closure;
    1025               0 :     return conn->OnReadSegment(buf, count, countRead);
    1026                 : }
    1027                 : 
    1028                 : nsresult
    1029            2978 : nsHttpConnection::OnReadSegment(const char *buf,
    1030                 :                                 PRUint32 count,
    1031                 :                                 PRUint32 *countRead)
    1032                 : {
    1033            2978 :     if (count == 0) {
    1034                 :         // some ReadSegments implementations will erroneously call the writer
    1035                 :         // to consume 0 bytes worth of data.  we must protect against this case
    1036                 :         // or else we'd end up closing the socket prematurely.
    1037               0 :         NS_ERROR("bad ReadSegments implementation");
    1038               0 :         return NS_ERROR_FAILURE; // stop iterating
    1039                 :     }
    1040                 : 
    1041            2978 :     nsresult rv = mSocketOut->Write(buf, count, countRead);
    1042            2978 :     if (NS_FAILED(rv))
    1043             142 :         mSocketOutCondition = rv;
    1044            2836 :     else if (*countRead == 0)
    1045               0 :         mSocketOutCondition = NS_BASE_STREAM_CLOSED;
    1046                 :     else
    1047            2836 :         mSocketOutCondition = NS_OK; // reset condition
    1048                 : 
    1049            2978 :     return mSocketOutCondition;
    1050                 : }
    1051                 : 
    1052                 : nsresult
    1053            2988 : nsHttpConnection::OnSocketWritable()
    1054                 : {
    1055            2988 :     LOG(("nsHttpConnection::OnSocketWritable [this=%x]\n", this));
    1056                 : 
    1057                 :     nsresult rv;
    1058                 :     PRUint32 n;
    1059            2988 :     bool again = true;
    1060                 : 
    1061            5824 :     do {
    1062            5824 :         mSocketOutCondition = NS_OK;
    1063                 : 
    1064                 :         // if we're doing an SSL proxy connect, then we need to bypass calling
    1065                 :         // into the transaction.
    1066                 :         //
    1067                 :         // NOTE: this code path can't be shared since the transaction doesn't
    1068                 :         // implement nsIInputStream.  doing so is not worth the added cost of
    1069                 :         // extra indirections during normal reading.
    1070                 :         //
    1071            5824 :         if (mProxyConnectStream) {
    1072               0 :             LOG(("  writing CONNECT request stream\n"));
    1073               0 :             rv = mProxyConnectStream->ReadSegments(ReadFromStream, this,
    1074                 :                                                       nsIOService::gDefaultSegmentSize,
    1075               0 :                                                       &n);
    1076                 :         }
    1077            5824 :         else if (!EnsureNPNComplete()) {
    1078                 :             // When SPDY is disabled this branch is not executed because Activate()
    1079                 :             // sets mNPNComplete to true in that case.
    1080                 : 
    1081                 :             // We are ready to proceed with SSL but the handshake is not done.
    1082                 :             // When using NPN to negotiate between HTTPS and SPDY, we need to
    1083                 :             // see the results of the handshake to know what bytes to send, so
    1084                 :             // we cannot proceed with the request headers.
    1085                 : 
    1086              12 :             rv = NS_OK;
    1087              12 :             mSocketOutCondition = NS_BASE_STREAM_WOULD_BLOCK;
    1088              12 :             n = 0;
    1089                 :         }
    1090                 :         else {
    1091            5812 :             if (!mReportedSpdy) {
    1092            2975 :                 mReportedSpdy = true;
    1093            2975 :                 gHttpHandler->ConnMgr()->ReportSpdyConnection(this, mUsingSpdy);
    1094                 :             }
    1095                 : 
    1096            5812 :             LOG(("  writing transaction request stream\n"));
    1097            5812 :             rv = mTransaction->ReadSegments(this, nsIOService::gDefaultSegmentSize, &n);
    1098                 :         }
    1099                 : 
    1100            5824 :         LOG(("  ReadSegments returned [rv=%x read=%u sock-cond=%x]\n",
    1101                 :             rv, n, mSocketOutCondition));
    1102                 : 
    1103                 :         // XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
    1104            5824 :         if (rv == NS_BASE_STREAM_CLOSED) {
    1105               0 :             rv = NS_OK;
    1106               0 :             n = 0;
    1107                 :         }
    1108                 : 
    1109            5824 :         if (NS_FAILED(rv)) {
    1110                 :             // if the transaction didn't want to write any more data, then
    1111                 :             // wait for the transaction to call ResumeSend.
    1112               0 :             if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    1113               0 :                 rv = NS_OK;
    1114               0 :             again = false;
    1115                 :         }
    1116            5824 :         else if (NS_FAILED(mSocketOutCondition)) {
    1117             154 :             if (mSocketOutCondition == NS_BASE_STREAM_WOULD_BLOCK)
    1118              13 :                 rv = mSocketOut->AsyncWait(this, 0, 0, nsnull); // continue writing
    1119                 :             else
    1120             141 :                 rv = mSocketOutCondition;
    1121             154 :             again = false;
    1122                 :         }
    1123            5670 :         else if (n == 0) {
    1124                 :             // 
    1125                 :             // at this point we've written out the entire transaction, and now we
    1126                 :             // must wait for the server's response.  we manufacture a status message
    1127                 :             // here to reflect the fact that we are waiting.  this message will be
    1128                 :             // trumped (overwritten) if the server responds quickly.
    1129                 :             //
    1130            2834 :             mTransaction->OnTransportStatus(mSocketTransport,
    1131                 :                                             nsISocketTransport::STATUS_WAITING_FOR,
    1132            2834 :                                             LL_ZERO);
    1133                 : 
    1134            2834 :             rv = mSocketIn->AsyncWait(this, 0, 0, nsnull); // start reading
    1135            2834 :             again = false;
    1136                 :         }
    1137                 :         // write more to the socket until error or end-of-request...
    1138                 :     } while (again);
    1139                 : 
    1140            2988 :     return rv;
    1141                 : }
    1142                 : 
    1143                 : nsresult
    1144            8640 : nsHttpConnection::OnWriteSegment(char *buf,
    1145                 :                                  PRUint32 count,
    1146                 :                                  PRUint32 *countWritten)
    1147                 : {
    1148            8640 :     if (count == 0) {
    1149                 :         // some WriteSegments implementations will erroneously call the reader
    1150                 :         // to provide 0 bytes worth of data.  we must protect against this case
    1151                 :         // or else we'd end up closing the socket prematurely.
    1152               0 :         NS_ERROR("bad WriteSegments implementation");
    1153               0 :         return NS_ERROR_FAILURE; // stop iterating
    1154                 :     }
    1155                 : 
    1156            8640 :     nsresult rv = mSocketIn->Read(buf, count, countWritten);
    1157            8640 :     if (NS_FAILED(rv))
    1158            2905 :         mSocketInCondition = rv;
    1159            5735 :     else if (*countWritten == 0)
    1160              14 :         mSocketInCondition = NS_BASE_STREAM_CLOSED;
    1161                 :     else
    1162            5721 :         mSocketInCondition = NS_OK; // reset condition
    1163                 : 
    1164            8640 :     return mSocketInCondition;
    1165                 : }
    1166                 : 
    1167                 : nsresult
    1168            5730 : nsHttpConnection::OnSocketReadable()
    1169                 : {
    1170            5730 :     LOG(("nsHttpConnection::OnSocketReadable [this=%x]\n", this));
    1171                 : 
    1172            5730 :     PRIntervalTime now = PR_IntervalNow();
    1173                 : 
    1174            5730 :     if (mKeepAliveMask && ((now - mLastReadTime) >= mMaxHangTime)) {
    1175               0 :         LOG(("max hang time exceeded!\n"));
    1176                 :         // give the handler a chance to create a new persistent connection to
    1177                 :         // this host if we've been busy for too long.
    1178               0 :         mKeepAliveMask = false;
    1179               0 :         gHttpHandler->ProcessPendingQ(mConnInfo);
    1180                 :     }
    1181            5730 :     mLastReadTime = now;
    1182                 : 
    1183                 :     nsresult rv;
    1184                 :     PRUint32 n;
    1185            5730 :     bool again = true;
    1186                 : 
    1187           11025 :     do {
    1188           11025 :         rv = mTransaction->WriteSegments(this, nsIOService::gDefaultSegmentSize, &n);
    1189           11025 :         if (NS_FAILED(rv)) {
    1190                 :             // if the transaction didn't want to take any more data, then
    1191                 :             // wait for the transaction to call ResumeRecv.
    1192            2811 :             if (rv == NS_BASE_STREAM_WOULD_BLOCK)
    1193               0 :                 rv = NS_OK;
    1194            2811 :             again = false;
    1195                 :         }
    1196                 :         else {
    1197            8214 :             mCurrentBytesRead += n;
    1198            8214 :             mTotalBytesRead += n;
    1199            8214 :             if (NS_FAILED(mSocketInCondition)) {
    1200                 :                 // continue waiting for the socket if necessary...
    1201            2919 :                 if (mSocketInCondition == NS_BASE_STREAM_WOULD_BLOCK)
    1202            2905 :                     rv = mSocketIn->AsyncWait(this, 0, 0, nsnull);
    1203                 :                 else
    1204              14 :                     rv = mSocketInCondition;
    1205            2919 :                 again = false;
    1206                 :             }
    1207                 :         }
    1208                 :         // read more from the socket until error...
    1209                 :     } while (again);
    1210                 : 
    1211            5730 :     return rv;
    1212                 : }
    1213                 : 
    1214                 : nsresult
    1215               0 : nsHttpConnection::SetupProxyConnect()
    1216                 : {
    1217                 :     const char *val;
    1218                 : 
    1219               0 :     LOG(("nsHttpConnection::SetupProxyConnect [this=%x]\n", this));
    1220                 : 
    1221               0 :     NS_ENSURE_TRUE(!mProxyConnectStream, NS_ERROR_ALREADY_INITIALIZED);
    1222               0 :     NS_ABORT_IF_FALSE(!mUsingSpdy,
    1223                 :                       "SPDY NPN Complete while using proxy connect stream");
    1224                 : 
    1225               0 :     nsCAutoString buf;
    1226                 :     nsresult rv = nsHttpHandler::GenerateHostPort(
    1227               0 :             nsDependentCString(mConnInfo->Host()), mConnInfo->Port(), buf);
    1228               0 :     if (NS_FAILED(rv))
    1229               0 :         return rv;
    1230                 : 
    1231                 :     // CONNECT host:port HTTP/1.1
    1232               0 :     nsHttpRequestHead request;
    1233               0 :     request.SetMethod(nsHttp::Connect);
    1234               0 :     request.SetVersion(gHttpHandler->HttpVersion());
    1235               0 :     request.SetRequestURI(buf);
    1236               0 :     request.SetHeader(nsHttp::User_Agent, gHttpHandler->UserAgent());
    1237                 : 
    1238                 :     // send this header for backwards compatibility.
    1239               0 :     request.SetHeader(nsHttp::Proxy_Connection, NS_LITERAL_CSTRING("keep-alive"));
    1240                 : 
    1241               0 :     val = mTransaction->RequestHead()->PeekHeader(nsHttp::Host);
    1242               0 :     if (val) {
    1243                 :         // all HTTP/1.1 requests must include a Host header (even though it
    1244                 :         // may seem redundant in this case; see bug 82388).
    1245               0 :         request.SetHeader(nsHttp::Host, nsDependentCString(val));
    1246                 :     }
    1247                 : 
    1248               0 :     val = mTransaction->RequestHead()->PeekHeader(nsHttp::Proxy_Authorization);
    1249               0 :     if (val) {
    1250                 :         // we don't know for sure if this authorization is intended for the
    1251                 :         // SSL proxy, so we add it just in case.
    1252               0 :         request.SetHeader(nsHttp::Proxy_Authorization, nsDependentCString(val));
    1253                 :     }
    1254                 : 
    1255               0 :     buf.Truncate();
    1256               0 :     request.Flatten(buf, false);
    1257               0 :     buf.AppendLiteral("\r\n");
    1258                 : 
    1259               0 :     return NS_NewCStringInputStream(getter_AddRefs(mProxyConnectStream), buf);
    1260                 : }
    1261                 : 
    1262                 : //-----------------------------------------------------------------------------
    1263                 : // nsHttpConnection::nsISupports
    1264                 : //-----------------------------------------------------------------------------
    1265                 : 
    1266          111041 : NS_IMPL_THREADSAFE_ISUPPORTS4(nsHttpConnection,
    1267                 :                               nsIInputStreamCallback,
    1268                 :                               nsIOutputStreamCallback,
    1269                 :                               nsITransportEventSink,
    1270                 :                               nsIInterfaceRequestor)
    1271                 : 
    1272                 : //-----------------------------------------------------------------------------
    1273                 : // nsHttpConnection::nsIInputStreamCallback
    1274                 : //-----------------------------------------------------------------------------
    1275                 : 
    1276                 : // called on the socket transport thread
    1277                 : NS_IMETHODIMP
    1278            5739 : nsHttpConnection::OnInputStreamReady(nsIAsyncInputStream *in)
    1279                 : {
    1280            5739 :     NS_ASSERTION(in == mSocketIn, "unexpected stream");
    1281            5739 :     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1282                 : 
    1283            5739 :     if (mIdleMonitoring) {
    1284               0 :         NS_ABORT_IF_FALSE(!mTransaction, "Idle Input Event While Active");
    1285                 : 
    1286                 :         // The only read event that is protocol compliant for an idle connection
    1287                 :         // is an EOF, which we check for with CanReuse(). If the data is
    1288                 :         // something else then just ignore it and suspend checking for EOF -
    1289                 :         // our normal timers or protocol stack are the place to deal with
    1290                 :         // any exception logic.
    1291                 : 
    1292               0 :         if (!CanReuse()) {
    1293               0 :             LOG(("Server initiated close of idle conn %p\n", this));
    1294               0 :             gHttpHandler->ConnMgr()->CloseIdleConnection(this);
    1295               0 :             return NS_OK;
    1296                 :         }
    1297                 : 
    1298               0 :         LOG(("Input data on idle conn %p, but not closing yet\n", this));
    1299               0 :         return NS_OK;
    1300                 :     }
    1301                 : 
    1302                 :     // if the transaction was dropped...
    1303            5739 :     if (!mTransaction) {
    1304               9 :         LOG(("  no transaction; ignoring event\n"));
    1305               9 :         return NS_OK;
    1306                 :     }
    1307                 : 
    1308            5730 :     nsresult rv = OnSocketReadable();
    1309            5730 :     if (NS_FAILED(rv))
    1310            2825 :         CloseTransaction(mTransaction, rv);
    1311                 : 
    1312            5730 :     return NS_OK;
    1313                 : }
    1314                 : 
    1315                 : //-----------------------------------------------------------------------------
    1316                 : // nsHttpConnection::nsIOutputStreamCallback
    1317                 : //-----------------------------------------------------------------------------
    1318                 : 
    1319                 : NS_IMETHODIMP
    1320            2988 : nsHttpConnection::OnOutputStreamReady(nsIAsyncOutputStream *out)
    1321                 : {
    1322            2988 :     NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
    1323            2988 :     NS_ABORT_IF_FALSE(out == mSocketOut, "unexpected socket");
    1324                 : 
    1325                 :     // if the transaction was dropped...
    1326            2988 :     if (!mTransaction) {
    1327               0 :         LOG(("  no transaction; ignoring event\n"));
    1328               0 :         return NS_OK;
    1329                 :     }
    1330                 : 
    1331            2988 :     nsresult rv = OnSocketWritable();
    1332            2988 :     if (NS_FAILED(rv))
    1333             141 :         CloseTransaction(mTransaction, rv);
    1334                 : 
    1335            2988 :     return NS_OK;
    1336                 : }
    1337                 : 
    1338                 : //-----------------------------------------------------------------------------
    1339                 : // nsHttpConnection::nsITransportEventSink
    1340                 : //-----------------------------------------------------------------------------
    1341                 : 
    1342                 : NS_IMETHODIMP
    1343            8557 : nsHttpConnection::OnTransportStatus(nsITransport *trans,
    1344                 :                                     nsresult status,
    1345                 :                                     PRUint64 progress,
    1346                 :                                     PRUint64 progressMax)
    1347                 : {
    1348            8557 :     if (mTransaction)
    1349            8557 :         mTransaction->OnTransportStatus(trans, status, progress);
    1350            8557 :     return NS_OK;
    1351                 : }
    1352                 : 
    1353                 : //-----------------------------------------------------------------------------
    1354                 : // nsHttpConnection::nsIInterfaceRequestor
    1355                 : //-----------------------------------------------------------------------------
    1356                 : 
    1357                 : // not called on the socket transport thread
    1358                 : NS_IMETHODIMP
    1359               8 : nsHttpConnection::GetInterface(const nsIID &iid, void **result)
    1360                 : {
    1361                 :     // NOTE: This function is only called on the UI thread via sync proxy from
    1362                 :     //       the socket transport thread.  If that weren't the case, then we'd
    1363                 :     //       have to worry about the possibility of mTransaction going away
    1364                 :     //       part-way through this function call.  See CloseTransaction.
    1365                 : 
    1366                 :     // NOTE - there is a bug here, the call to getinterface is proxied off the
    1367                 :     // nss thread, not the ui thread as the above comment says. So there is
    1368                 :     // indeed a chance of mTransaction going away. bug 615342
    1369                 : 
    1370               8 :     NS_ASSERTION(PR_GetCurrentThread() != gSocketThread, "wrong thread");
    1371                 : 
    1372               8 :     if (mCallbacks)
    1373               8 :         return mCallbacks->GetInterface(iid, result);
    1374               0 :     return NS_ERROR_NO_INTERFACE;
    1375                 : }
    1376                 : 

Generated by: LCOV version 1.7