LCOV - code coverage report
Current view: top level - netwerk/protocol/http - nsHttpTransaction.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 220 1059 20.8 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       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             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : // HttpLog.h should generally be included first
       8             : #include "HttpLog.h"
       9             : 
      10             : #include "base/basictypes.h"
      11             : 
      12             : #include "nsHttpHandler.h"
      13             : #include "nsHttpTransaction.h"
      14             : #include "nsHttpRequestHead.h"
      15             : #include "nsHttpResponseHead.h"
      16             : #include "nsHttpChunkedDecoder.h"
      17             : #include "nsTransportUtils.h"
      18             : #include "nsNetCID.h"
      19             : #include "nsNetUtil.h"
      20             : #include "nsIChannel.h"
      21             : #include "nsIPipe.h"
      22             : #include "nsCRT.h"
      23             : #include "mozilla/Tokenizer.h"
      24             : #include "TCPFastOpenLayer.h"
      25             : 
      26             : #include "nsISeekableStream.h"
      27             : #include "nsMultiplexInputStream.h"
      28             : #include "nsStringStream.h"
      29             : 
      30             : #include "nsComponentManagerUtils.h" // do_CreateInstance
      31             : #include "nsIHttpActivityObserver.h"
      32             : #include "nsSocketTransportService2.h"
      33             : #include "nsICancelable.h"
      34             : #include "nsIClassOfService.h"
      35             : #include "nsIEventTarget.h"
      36             : #include "nsIHttpChannelInternal.h"
      37             : #include "nsIInputStream.h"
      38             : #include "nsIThrottledInputChannel.h"
      39             : #include "nsITransport.h"
      40             : #include "nsIOService.h"
      41             : #include "nsIRequestContext.h"
      42             : #include "nsIHttpAuthenticator.h"
      43             : #include "NSSErrorsService.h"
      44             : #include "sslerr.h"
      45             : #include <algorithm>
      46             : 
      47             : //-----------------------------------------------------------------------------
      48             : 
      49             : static NS_DEFINE_CID(kMultiplexInputStream, NS_MULTIPLEXINPUTSTREAM_CID);
      50             : 
      51             : // Place a limit on how much non-compliant HTTP can be skipped while
      52             : // looking for a response header
      53             : #define MAX_INVALID_RESPONSE_BODY_SIZE (1024 * 128)
      54             : 
      55             : using namespace mozilla::net;
      56             : 
      57             : namespace mozilla {
      58             : namespace net {
      59             : 
      60             : //-----------------------------------------------------------------------------
      61             : // helpers
      62             : //-----------------------------------------------------------------------------
      63             : 
      64             : static void
      65           0 : LogHeaders(const char *lineStart)
      66             : {
      67           0 :     nsAutoCString buf;
      68             :     char *endOfLine;
      69           0 :     while ((endOfLine = PL_strstr(lineStart, "\r\n"))) {
      70           0 :         buf.Assign(lineStart, endOfLine - lineStart);
      71           0 :         if (PL_strcasestr(buf.get(), "authorization: ") ||
      72           0 :             PL_strcasestr(buf.get(), "proxy-authorization: ")) {
      73           0 :             char *p = PL_strchr(PL_strchr(buf.get(), ' ') + 1, ' ');
      74           0 :             while (p && *++p)
      75           0 :                 *p = '*';
      76             :         }
      77           0 :         LOG3(("  %s\n", buf.get()));
      78           0 :         lineStart = endOfLine + 2;
      79             :     }
      80           0 : }
      81             : 
      82             : //-----------------------------------------------------------------------------
      83             : // nsHttpTransaction <public>
      84             : //-----------------------------------------------------------------------------
      85             : 
      86           0 : nsHttpTransaction::nsHttpTransaction()
      87             :     : mLock("transaction lock")
      88             :     , mRequestSize(0)
      89             :     , mRequestHead(nullptr)
      90             :     , mResponseHead(nullptr)
      91             :     , mReader(nullptr)
      92             :     , mWriter(nullptr)
      93             :     , mContentLength(-1)
      94             :     , mContentRead(0)
      95             :     , mTransferSize(0)
      96             :     , mInvalidResponseBytesRead(0)
      97             :     , mPushedStream(nullptr)
      98             :     , mInitialRwin(0)
      99             :     , mChunkedDecoder(nullptr)
     100             :     , mStatus(NS_OK)
     101             :     , mPriority(0)
     102             :     , mRestartCount(0)
     103             :     , mCaps(0)
     104             :     , mHttpVersion(HttpVersion::UNKNOWN)
     105             :     , mHttpResponseCode(0)
     106             :     , mCurrentHttpResponseHeaderSize(0)
     107             :     , mThrottlingReadAllowance(THROTTLE_NO_LIMIT)
     108             :     , mCapsToClear(0)
     109             :     , mResponseIsComplete(false)
     110             :     , mReadingStopped(false)
     111             :     , mClosed(false)
     112             :     , mConnected(false)
     113             :     , mActivated(false)
     114             :     , mHaveStatusLine(false)
     115             :     , mHaveAllHeaders(false)
     116             :     , mTransactionDone(false)
     117             :     , mDidContentStart(false)
     118             :     , mNoContent(false)
     119             :     , mSentData(false)
     120             :     , mReceivedData(false)
     121             :     , mStatusEventPending(false)
     122             :     , mHasRequestBody(false)
     123             :     , mProxyConnectFailed(false)
     124             :     , mHttpResponseMatched(false)
     125             :     , mPreserveStream(false)
     126             :     , mDispatchedAsBlocking(false)
     127             :     , mResponseTimeoutEnabled(true)
     128             :     , mForceRestart(false)
     129             :     , mReuseOnRestart(false)
     130             :     , mContentDecoding(false)
     131             :     , mContentDecodingCheck(false)
     132             :     , mDeferredSendProgress(false)
     133             :     , mWaitingOnPipeOut(false)
     134             :     , mReportedStart(false)
     135             :     , mReportedResponseHeader(false)
     136             :     , mResponseHeadTaken(false)
     137             :     , mForTakeResponseTrailers(nullptr)
     138             :     , mResponseTrailersTaken(false)
     139             :     , mTopLevelOuterContentWindowId(0)
     140             :     , mSubmittedRatePacing(false)
     141             :     , mPassedRatePacing(false)
     142             :     , mSynchronousRatePaceRequest(false)
     143             :     , mClassOfService(0)
     144             :     , m0RTTInProgress(false)
     145             :     , mDoNotTryEarlyData(false)
     146             :     , mEarlyDataDisposition(EARLY_NONE)
     147           0 :     , mFastOpenStatus(TFO_NOT_TRIED)
     148             : {
     149           0 :     this->mSelfAddr.inet = {};
     150             :     this->mPeerAddr.inet = {};
     151             :     LOG(("Creating nsHttpTransaction @%p\n", this));
     152             : 
     153             : #ifdef MOZ_VALGRIND
     154             :     memset(&mSelfAddr, 0, sizeof(NetAddr));
     155           0 :     memset(&mPeerAddr, 0, sizeof(NetAddr));
     156           0 : #endif
     157           0 :     mSelfAddr.raw.family = PR_AF_UNSPEC;
     158             :     mPeerAddr.raw.family = PR_AF_UNSPEC;
     159           0 : }
     160             : 
     161           0 : void nsHttpTransaction::ResumeReading()
     162             : {
     163           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     164             : 
     165             :     if (!mReadingStopped) {
     166             :         return;
     167           0 :     }
     168             : 
     169           0 :     LOG(("nsHttpTransaction::ResumeReading %p", this));
     170             : 
     171             :     mReadingStopped = false;
     172             : 
     173           0 :     // This with either reengage the limit when still throttled in WriteSegments or
     174             :     // simply reset to allow unlimeted reading again.
     175           0 :     mThrottlingReadAllowance = THROTTLE_NO_LIMIT;
     176           0 : 
     177           0 :     if (mConnection) {
     178           0 :         mConnection->TransactionHasDataToRecv(this);
     179           0 :         nsresult rv = mConnection->ResumeRecv();
     180             :         if (NS_FAILED(rv)) {
     181             :             LOG(("  resume failed with rv=%" PRIx32, static_cast<uint32_t>(rv)));
     182             :         }
     183             :     }
     184           0 : }
     185             : 
     186           0 : bool nsHttpTransaction::EligibleForThrottling() const
     187             : {
     188             :     return (mClassOfService & (nsIClassOfService::Throttleable |
     189             :                                nsIClassOfService::DontThrottle |
     190           0 :                                nsIClassOfService::Leader |
     191             :                                nsIClassOfService::Unblocked)) ==
     192             :         nsIClassOfService::Throttleable;
     193           0 : }
     194             : 
     195           0 : void nsHttpTransaction::SetClassOfService(uint32_t cos)
     196           0 : {
     197           0 :     bool wasThrottling = EligibleForThrottling();
     198             :     mClassOfService = cos;
     199           0 :     bool isThrottling = EligibleForThrottling();
     200             : 
     201             :     if (mConnection && wasThrottling != isThrottling) {
     202             :         // Do nothing until we are actually activated.  For now
     203           0 :         // only remember the throttle flag.  Call to UpdateActiveTransaction
     204             :         // would add this transaction to the list too early.
     205           0 :         gHttpHandler->ConnMgr()->UpdateActiveTransaction(this);
     206           0 : 
     207             :         if (mReadingStopped && !isThrottling) {
     208             :             ResumeReading();
     209           0 :         }
     210             :     }
     211           0 : }
     212             : 
     213           0 : nsHttpTransaction::~nsHttpTransaction()
     214           0 : {
     215           0 :     LOG(("Destroying nsHttpTransaction @%p\n", this));
     216             :     if (mTransactionObserver) {
     217           0 :         mTransactionObserver->Complete(this, NS_OK);
     218           0 :     }
     219           0 :     if (mPushedStream) {
     220             :         mPushedStream->OnPushFailed();
     221             :         mPushedStream = nullptr;
     222           0 :     }
     223           0 : 
     224           0 :     if (mTokenBucketCancel) {
     225             :         mTokenBucketCancel->Cancel(NS_ERROR_ABORT);
     226             :         mTokenBucketCancel = nullptr;
     227             :     }
     228           0 : 
     229           0 :     // Force the callbacks and connection to be released right now
     230             :     mCallbacks = nullptr;
     231           0 :     mConnection = nullptr;
     232           0 : 
     233           0 :     delete mResponseHead;
     234           0 :     delete mChunkedDecoder;
     235             :     ReleaseBlockingTransaction();
     236             : }
     237           0 : 
     238             : nsresult
     239             : nsHttpTransaction::Init(uint32_t caps,
     240             :                         nsHttpConnectionInfo *cinfo,
     241             :                         nsHttpRequestHead *requestHead,
     242             :                         nsIInputStream *requestBody,
     243             :                         uint64_t requestContentLength,
     244             :                         bool requestBodyHasHeaders,
     245             :                         nsIEventTarget *target,
     246             :                         nsIInterfaceRequestor *callbacks,
     247             :                         nsITransportEventSink *eventsink,
     248             :                         uint64_t topLevelOuterContentWindowId,
     249             :                         nsIAsyncInputStream **responseBody)
     250             : {
     251           0 :     nsresult rv;
     252             : 
     253           0 :     LOG(("nsHttpTransaction::Init [this=%p caps=%x]\n", this, caps));
     254           0 : 
     255           0 :     MOZ_ASSERT(cinfo);
     256           0 :     MOZ_ASSERT(requestHead);
     257             :     MOZ_ASSERT(target);
     258           0 :     MOZ_ASSERT(NS_IsMainThread());
     259           0 : 
     260             :     mTopLevelOuterContentWindowId = topLevelOuterContentWindowId;
     261           0 :     LOG(("  window-id = %" PRIx64, mTopLevelOuterContentWindowId));
     262           0 : 
     263             :     mActivityDistributor = services::GetActivityDistributor();
     264             :     if (!mActivityDistributor) {
     265             :         return NS_ERROR_NOT_AVAILABLE;
     266             :     }
     267           0 : 
     268           0 :     bool activityDistributorActive;
     269             :     rv = mActivityDistributor->GetIsActive(&activityDistributorActive);
     270             :     if (NS_SUCCEEDED(rv) && activityDistributorActive) {
     271           0 :         // there are some observers registered at activity distributor, gather
     272             :         // nsISupports for the channel that called Init()
     273             :         LOG(("nsHttpTransaction::Init() " \
     274             :              "mActivityDistributor is active " \
     275             :              "this=%p", this));
     276           0 :     } else {
     277           0 :         // there is no observer, so don't use it
     278             :         activityDistributorActive = false;
     279           0 :         mActivityDistributor = nullptr;
     280             :     }
     281             :     mChannel = do_QueryInterface(eventsink);
     282           0 : 
     283           0 :     nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
     284           0 :         do_QueryInterface(eventsink);
     285           0 :     if (httpChannelInternal) {
     286           0 :         rv = httpChannelInternal->GetResponseTimeoutEnabled(
     287           0 :             &mResponseTimeoutEnabled);
     288             :         if (NS_WARN_IF(NS_FAILED(rv))) {
     289           0 :             return rv;
     290           0 :         }
     291             :         rv = httpChannelInternal->GetInitialRwin(&mInitialRwin);
     292             :         MOZ_ASSERT(NS_SUCCEEDED(rv));
     293             :     }
     294             : 
     295           0 :     // create transport event sink proxy. it coalesces consecutive
     296             :     // events of the same status type.
     297             :     rv = net_NewTransportEventSinkProxy(getter_AddRefs(mTransportSink),
     298           0 :                                         eventsink, target);
     299             : 
     300           0 :     if (NS_FAILED(rv)) return rv;
     301           0 : 
     302           0 :     mConnInfo = cinfo;
     303           0 :     mCallbacks = callbacks;
     304             :     mConsumerTarget = target;
     305           0 :     mCaps = caps;
     306           0 : 
     307             :     if (requestHead->IsHead()) {
     308             :         mNoContent = true;
     309             :     }
     310             : 
     311             :     // Make sure that there is "Content-Length: 0" header in the requestHead
     312             :     // in case of POST and PUT methods when there is no requestBody and
     313             :     // requestHead doesn't contain "Transfer-Encoding" header.
     314             :     //
     315             :     // RFC1945 section 7.2.2:
     316             :     //   HTTP/1.0 requests containing an entity body must include a valid
     317             :     //   Content-Length header field.
     318             :     //
     319             :     // RFC2616 section 4.4:
     320             :     //   For compatibility with HTTP/1.0 applications, HTTP/1.1 requests
     321           0 :     //   containing a message-body MUST include a valid Content-Length header
     322           0 :     //   field unless the server is known to be HTTP/1.1 compliant.
     323           0 :     if ((requestHead->IsPost() || requestHead->IsPut()) &&
     324           0 :         !requestBody && !requestHead->HasHeader(nsHttp::Transfer_Encoding)) {
     325             :         rv = requestHead->SetHeader(nsHttp::Content_Length, NS_LITERAL_CSTRING("0"));
     326             :         MOZ_ASSERT(NS_SUCCEEDED(rv));
     327             :     }
     328           0 : 
     329             :     // grab a weak reference to the request head
     330             :     mRequestHead = requestHead;
     331             : 
     332           0 :     // make sure we eliminate any proxy specific headers from
     333             :     // the request if we are using CONNECT
     334           0 :     bool pruneProxyHeaders = cinfo->UsingConnect();
     335           0 : 
     336             :     mReqHeaderBuf.Truncate();
     337           0 :     requestHead->Flatten(mReqHeaderBuf, pruneProxyHeaders);
     338           0 : 
     339           0 :     if (LOG3_ENABLED()) {
     340           0 :         LOG3(("http request [\n"));
     341             :         LogHeaders(mReqHeaderBuf.get());
     342             :         LOG3(("]\n"));
     343             :     }
     344             : 
     345           0 :     // If the request body does not include headers or if there is no request
     346           0 :     // body, then we must add the header/body separator manually.
     347             :     if (!requestBodyHasHeaders || !requestBody)
     348             :         mReqHeaderBuf.AppendLiteral("\r\n");
     349           0 : 
     350           0 :     // report the request header
     351             :     if (mActivityDistributor) {
     352             :         rv = mActivityDistributor->ObserveActivity(
     353             :             mChannel,
     354             :             NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
     355           0 :             NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_HEADER,
     356           0 :             PR_Now(), 0,
     357           0 :             mReqHeaderBuf);
     358             :         if (NS_FAILED(rv)) {
     359             :             LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
     360             :         }
     361             :     }
     362             : 
     363             :     // Create a string stream for the request header buf (the stream holds
     364           0 :     // a non-owning reference to the request header data, so we MUST keep
     365           0 :     // mReqHeaderBuf around).
     366             :     nsCOMPtr<nsIInputStream> headers;
     367           0 :     rv = NS_NewByteInputStream(getter_AddRefs(headers),
     368           0 :                                mReqHeaderBuf.get(),
     369             :                                mReqHeaderBuf.Length());
     370           0 :     if (NS_FAILED(rv)) return rv;
     371           0 : 
     372           0 :     mHasRequestBody = !!requestBody;
     373             :     if (mHasRequestBody && !requestContentLength) {
     374             :         mHasRequestBody = false;
     375           0 :     }
     376             : 
     377           0 :     requestContentLength += mReqHeaderBuf.Length();
     378             : 
     379             :     if (mHasRequestBody) {
     380           0 :         // wrap the headers and request body in a multiplexed input stream.
     381           0 :         nsCOMPtr<nsIMultiplexInputStream> multi =
     382             :             do_CreateInstance(kMultiplexInputStream, &rv);
     383           0 :         if (NS_FAILED(rv)) return rv;
     384           0 : 
     385             :         rv = multi->AppendStream(headers);
     386           0 :         if (NS_FAILED(rv)) return rv;
     387           0 : 
     388             :         rv = multi->AppendStream(requestBody);
     389             :         if (NS_FAILED(rv)) return rv;
     390             : 
     391             :         // wrap the multiplexed input stream with a buffered input stream, so
     392           0 :         // that we write data in the largest chunks possible.  this is actually
     393           0 :         // necessary to workaround some common server bugs (see bug 137155).
     394           0 :         nsCOMPtr<nsIInputStream> stream(do_QueryInterface(multi));
     395             :         rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream),
     396           0 :                                        stream.forget(),
     397             :                                        nsIOService::gDefaultSegmentSize);
     398           0 :         if (NS_FAILED(rv)) return rv;
     399             :     } else {
     400             :         mRequestStream = headers;
     401           0 :     }
     402             : 
     403           0 :     nsCOMPtr<nsIThrottledInputChannel> throttled = do_QueryInterface(mChannel);
     404           0 :     nsIInputChannelThrottleQueue* queue;
     405             :     if (throttled) {
     406           0 :         rv = throttled->GetThrottleQueue(&queue);
     407           0 :         // In case of failure, just carry on without throttling.
     408           0 :         if (NS_SUCCEEDED(rv) && queue) {
     409             :             nsCOMPtr<nsIAsyncInputStream> wrappedStream;
     410             :             rv = queue->WrapStream(mRequestStream, getter_AddRefs(wrappedStream));
     411           0 :             // Failure to throttle isn't sufficient reason to fail
     412           0 :             // initialization
     413           0 :             if (NS_SUCCEEDED(rv)) {
     414             :                 MOZ_ASSERT(wrappedStream != nullptr);
     415           0 :                 LOG(("nsHttpTransaction::Init %p wrapping input stream using throttle queue %p\n",
     416             :                      this, queue));
     417             :                 mRequestStream = do_QueryInterface(wrappedStream);
     418             :             }
     419             :         }
     420             :     }
     421           0 : 
     422             :     // make sure request content-length fits within js MAX_SAFE_INTEGER
     423             :     mRequestSize = InScriptableRange(requestContentLength) ? static_cast<int64_t>(requestContentLength) : -1;
     424           0 : 
     425           0 :     // create pipe for response stream
     426             :     rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
     427             :                      getter_AddRefs(mPipeOut),
     428             :                      true, true,
     429           0 :                      nsIOService::gDefaultSegmentSize,
     430             :                      nsIOService::gDefaultSegmentCount);
     431             :     if (NS_FAILED(rv)) return rv;
     432             : 
     433             : #ifdef WIN32 // bug 1153929
     434             :     MOZ_DIAGNOSTIC_ASSERT(mPipeOut);
     435             :     uint32_t * vtable = (uint32_t *) mPipeOut.get();
     436             :     MOZ_DIAGNOSTIC_ASSERT(*vtable != 0);
     437           0 : #endif // WIN32
     438           0 : 
     439             :     nsCOMPtr<nsIAsyncInputStream> tmp(mPipeIn);
     440             :     tmp.forget(responseBody);
     441             :     return NS_OK;
     442             : }
     443             : 
     444           0 : // This method should only be used on the socket thread
     445             : nsAHttpConnection *
     446           0 : nsHttpTransaction::Connection()
     447           0 : {
     448             :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     449             :     return mConnection.get();
     450             : }
     451           0 : 
     452             : already_AddRefed<nsAHttpConnection>
     453           0 : nsHttpTransaction::GetConnectionReference()
     454           0 : {
     455           0 :     MutexAutoLock lock(mLock);
     456             :     RefPtr<nsAHttpConnection> connection(mConnection);
     457             :     return connection.forget();
     458             : }
     459           0 : 
     460             : nsHttpResponseHead *
     461           0 : nsHttpTransaction::TakeResponseHead()
     462             : {
     463             :     MOZ_ASSERT(!mResponseHeadTaken, "TakeResponseHead called 2x");
     464           0 : 
     465             :     // Lock TakeResponseHead() against main thread
     466           0 :     MutexAutoLock lock(*nsHttp::GetLock());
     467             : 
     468             :     mResponseHeadTaken = true;
     469             : 
     470           0 :     // Even in OnStartRequest() the headers won't be available if we were
     471           0 :     // canceled
     472           0 :     if (!mHaveAllHeaders) {
     473             :         NS_WARNING("response headers not available or incomplete");
     474             :         return nullptr;
     475           0 :     }
     476           0 : 
     477           0 :     nsHttpResponseHead *head = mResponseHead;
     478             :     mResponseHead = nullptr;
     479             :     return head;
     480             : }
     481           0 : 
     482             : nsHttpHeaderArray *
     483           0 : nsHttpTransaction::TakeResponseTrailers()
     484             : {
     485             :     MOZ_ASSERT(!mResponseTrailersTaken, "TakeResponseTrailers called 2x");
     486           0 : 
     487             :     // Lock TakeResponseTrailers() against main thread
     488           0 :     MutexAutoLock lock(*nsHttp::GetLock());
     489           0 : 
     490             :     mResponseTrailersTaken = true;
     491             :     return mForTakeResponseTrailers.forget();
     492             : }
     493           0 : 
     494             : void
     495           0 : nsHttpTransaction::SetProxyConnectFailed()
     496           0 : {
     497             :     mProxyConnectFailed = true;
     498             : }
     499           0 : 
     500             : nsHttpRequestHead *
     501           0 : nsHttpTransaction::RequestHead()
     502             : {
     503             :     return mRequestHead;
     504             : }
     505           0 : 
     506             : uint32_t
     507           0 : nsHttpTransaction::Http1xTransactionCount()
     508             : {
     509             :   return 1;
     510             : }
     511           0 : 
     512             : nsresult
     513             : nsHttpTransaction::TakeSubTransactions(
     514           0 :     nsTArray<RefPtr<nsAHttpTransaction> > &outTransactions)
     515             : {
     516             :     return NS_ERROR_NOT_IMPLEMENTED;
     517             : }
     518             : 
     519             : //----------------------------------------------------------------------------
     520             : // nsHttpTransaction::nsAHttpTransaction
     521             : //----------------------------------------------------------------------------
     522           0 : 
     523             : void
     524             : nsHttpTransaction::SetConnection(nsAHttpConnection *conn)
     525           0 : {
     526           0 :     {
     527             :         MutexAutoLock lock(mLock);
     528           0 :         mConnection = conn;
     529             :     }
     530             : }
     531           0 : 
     532             : void
     533           0 : nsHttpTransaction::OnActivated()
     534             : {
     535           0 :     MOZ_ASSERT(OnSocketThread());
     536             : 
     537             :     if (mActivated) {
     538             :         return;
     539           0 :     }
     540             : 
     541             :     if (mConnection && mRequestHead && mConnection->Version() >= HttpVersion::v2_0) {
     542             :         // So this is fun. On http/2, we want to send TE: Trailers, to be
     543             :         // spec-compliant. So we add it to the request head here. The fun part
     544             :         // is that adding a header to the request head at this point has no
     545             :         // effect on what we send on the wire, as the headers are already
     546             :         // flattened (in Init()) by the time we get here. So the *real* adding
     547             :         // of the header happens in the h2 compression code. We still have to
     548           0 :         // add the header to the request head here, though, so that devtools can
     549             :         // show that we sent the header. FUN!
     550             :         Unused << mRequestHead->SetHeader(nsHttp::TE, NS_LITERAL_CSTRING("Trailers"));
     551           0 :     }
     552           0 : 
     553             :     mActivated = true;
     554             :     gHttpHandler->ConnMgr()->AddActiveTransaction(this);
     555             : }
     556           0 : 
     557             : void
     558          12 : nsHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **cb)
     559          12 : {
     560           0 :     MutexAutoLock lock(mLock);
     561           0 :     nsCOMPtr<nsIInterfaceRequestor> tmp(mCallbacks);
     562             :     tmp.forget(cb);
     563             : }
     564           0 : 
     565             : void
     566             : nsHttpTransaction::SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks)
     567           0 : {
     568           0 :     {
     569             :         MutexAutoLock lock(mLock);
     570             :         mCallbacks = aCallbacks;
     571           0 :     }
     572           0 : 
     573           0 :     if (gSocketTransportService) {
     574             :         RefPtr<UpdateSecurityCallbacks> event = new UpdateSecurityCallbacks(this, aCallbacks);
     575           0 :         gSocketTransportService->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
     576             :     }
     577             : }
     578          20 : 
     579             : void
     580             : nsHttpTransaction::OnTransportStatus(nsITransport* transport,
     581           0 :                                      nsresult status, int64_t progress)
     582             : {
     583             :     LOG(("nsHttpTransaction::OnSocketStatus [this=%p status=%" PRIx32 " progress=%" PRId64 "]\n",
     584           0 :          this, static_cast<uint32_t>(status), progress));
     585          20 : 
     586             :     if (status == NS_NET_STATUS_CONNECTED_TO ||
     587           0 :         status == NS_NET_STATUS_WAITING_FOR) {
     588           5 :         nsISocketTransport *socketTransport =
     589           4 :             mConnection ? mConnection->Transport() : nullptr;
     590           0 :         if (socketTransport) {
     591           2 :             MutexAutoLock lock(mLock);
     592             :             socketTransport->GetSelfAddr(&mSelfAddr);
     593             :             socketTransport->GetPeerAddr(&mPeerAddr);
     594             :         }
     595             :     }
     596             : 
     597             :     // If the timing is enabled, and we are not using a persistent connection
     598             :     // then the requestStart timestamp will be null, so we mark the timestamps
     599             :     // for domainLookupStart/End and connectStart/End
     600           0 :     // If we are using a persistent connection they will remain null,
     601           0 :     // and the correct value will be returned in Performance.
     602           0 :     if (TimingEnabled() && GetRequestStart().IsNull()) {
     603           0 :         if (status == NS_NET_STATUS_RESOLVING_HOST) {
     604           0 :             SetDomainLookupStart(TimeStamp::Now(), true);
     605           0 :         } else if (status == NS_NET_STATUS_RESOLVED_HOST) {
     606           0 :             SetDomainLookupEnd(TimeStamp::Now());
     607           0 :         } else if (status == NS_NET_STATUS_CONNECTING_TO) {
     608           0 :             SetConnectStart(TimeStamp::Now());
     609           0 :         } else if (status == NS_NET_STATUS_CONNECTED_TO) {
     610             :             TimeStamp tnow = TimeStamp::Now();
     611           0 :             SetConnectEnd(tnow, true);
     612           0 :             {
     613             :                 MutexAutoLock lock(mLock);
     614             :                 mTimings.tcpConnectEnd = tnow;
     615             :                 // After a socket is connected we know for sure whether data
     616           0 :                 // has been sent on SYN packet and if not we should update TLS
     617           0 :                 // start timing.
     618           0 :                 if ((mFastOpenStatus != TFO_DATA_SENT) &&
     619             :                     !mTimings.secureConnectionStart.IsNull()) {
     620             :                     mTimings.secureConnectionStart = tnow;
     621           0 :                 }
     622             :             }
     623           0 :         } else if (status == NS_NET_STATUS_TLS_HANDSHAKE_STARTING) {
     624           0 :             {
     625             :                 MutexAutoLock lock(mLock);
     626           0 :                 mTimings.secureConnectionStart = TimeStamp::Now();
     627           0 :             }
     628           0 :         } else if (status == NS_NET_STATUS_TLS_HANDSHAKE_ENDED) {
     629             :             SetConnectEnd(TimeStamp::Now(), false);
     630           0 :         } else if (status == NS_NET_STATUS_SENDING_TO) {
     631             :             // Set the timestamp to Now(), only if it null
     632             :             SetRequestStart(TimeStamp::Now(), true);
     633             :         }
     634          40 :     }
     635             : 
     636             :     if (!mTransportSink)
     637           0 :         return;
     638             : 
     639             :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     640             : 
     641          40 :     // Need to do this before the STATUS_RECEIVING_FROM check below, to make
     642             :     // sure that the activity distributor gets told about all status events.
     643           0 :     if (mActivityDistributor) {
     644             :         // upon STATUS_WAITING_FOR; report request body sent
     645           0 :         if ((mHasRequestBody) &&
     646             :             (status == NS_NET_STATUS_WAITING_FOR)) {
     647             :             nsresult rv = mActivityDistributor->ObserveActivity(
     648             :                 mChannel,
     649           0 :                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
     650           0 :                 NS_HTTP_ACTIVITY_SUBTYPE_REQUEST_BODY_SENT,
     651           0 :                 PR_Now(), 0, EmptyCString());
     652             :             if (NS_FAILED(rv)) {
     653             :                 LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
     654             :             }
     655             :         }
     656           0 : 
     657             :         // report the status and progress
     658             :         nsresult rv = mActivityDistributor->ObserveActivity(
     659             :             mChannel,
     660             :             NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
     661             :             static_cast<uint32_t>(status),
     662           0 :             PR_Now(),
     663           0 :             progress,
     664           0 :             EmptyCString());
     665             :         if (NS_FAILED(rv)) {
     666             :             LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
     667             :         }
     668             :     }
     669          20 : 
     670             :     // nsHttpChannel synthesizes progress events in OnDataAvailable
     671             :     if (status == NS_NET_STATUS_RECEIVING_FROM)
     672             :         return;
     673             : 
     674          18 :     int64_t progressMax;
     675             : 
     676           3 :     if (status == NS_NET_STATUS_SENDING_TO) {
     677           1 :         // suppress progress when only writing request headers
     678             :         if (!mHasRequestBody) {
     679           2 :             LOG(("nsHttpTransaction::OnTransportStatus %p "
     680             :                  "SENDING_TO without request body\n", this));
     681             :             return;
     682           2 :         }
     683             : 
     684           1 :         if (mReader) {
     685             :             // A mRequestStream method is on the stack - wait.
     686             :             LOG(("nsHttpTransaction::OnSocketStatus [this=%p] "
     687           1 :                  "Skipping Re-Entrant NS_NET_STATUS_SENDING_TO\n", this));
     688           0 :             // its ok to coalesce several of these into one deferred event
     689             :             mDeferredSendProgress = true;
     690             :             return;
     691           0 :         }
     692           1 : 
     693           0 :         nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
     694             :         if (!seekable) {
     695             :             LOG(("nsHttpTransaction::OnTransportStatus %p "
     696             :                  "SENDING_TO without seekable request stream\n", this));
     697           0 :             progress = 0;
     698           1 :         } else {
     699           1 :             int64_t prog = 0;
     700             :             seekable->Tell(&prog);
     701             :             progress = prog;
     702             :         }
     703             : 
     704           1 :         // when uploading, we include the request headers in the progress
     705             :         // notifications.
     706             :         progressMax = mRequestSize;
     707             :     }
     708             :     else {
     709             :         progress = 0;
     710             :         progressMax = 0;
     711          16 :     }
     712             : 
     713             :     mTransportSink->OnTransportStatus(transport, status, progress, progressMax);
     714             : }
     715           0 : 
     716             : bool
     717           0 : nsHttpTransaction::IsDone()
     718             : {
     719             :     return mTransactionDone;
     720             : }
     721           0 : 
     722             : nsresult
     723           3 : nsHttpTransaction::Status()
     724             : {
     725             :     return mStatus;
     726             : }
     727          29 : 
     728             : uint32_t
     729          58 : nsHttpTransaction::Caps()
     730             : {
     731             :     return mCaps & ~mCapsToClear;
     732             : }
     733           2 : 
     734             : void
     735           2 : nsHttpTransaction::SetDNSWasRefreshed()
     736           0 : {
     737           2 :     MOZ_ASSERT(NS_IsMainThread(), "SetDNSWasRefreshed on main thread only!");
     738             :     mCapsToClear |= NS_HTTP_REFRESH_DNS;
     739             : }
     740           2 : 
     741             : nsresult
     742             : nsHttpTransaction::ReadRequestSegment(nsIInputStream *stream,
     743             :                                       void *closure,
     744             :                                       const char *buf,
     745             :                                       uint32_t offset,
     746             :                                       uint32_t count,
     747             :                                       uint32_t *countRead)
     748             : {
     749             :     // For the tracking of sent bytes that we used to do for the networkstats
     750           2 :     // API, please see bug 1318883 where it was removed.
     751           2 : 
     752           2 :     nsHttpTransaction *trans = (nsHttpTransaction *) closure;
     753             :     nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead);
     754           2 :     if (NS_FAILED(rv)) return rv;
     755             : 
     756           2 :     LOG(("nsHttpTransaction::ReadRequestSegment %p read=%u", trans, *countRead));
     757           2 : 
     758             :     trans->mSentData = true;
     759             :     return NS_OK;
     760             : }
     761           0 : 
     762             : nsresult
     763             : nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
     764           4 :                                 uint32_t count, uint32_t *countRead)
     765             : {
     766           0 :     LOG(("nsHttpTransaction::ReadSegments %p", this));
     767             : 
     768           4 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     769           0 : 
     770           0 :     if (mTransactionDone) {
     771             :         *countRead = 0;
     772             :         return mStatus;
     773           0 :     }
     774           2 : 
     775           0 :     if (!mConnected && !m0RTTInProgress) {
     776             :         mConnected = true;
     777             :         mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
     778           1 :     }
     779           1 : 
     780           4 :     mDeferredSendProgress = false;
     781           4 :     mReader = reader;
     782             :     nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
     783           0 :     mReader = nullptr;
     784           0 : 
     785           0 :     if (m0RTTInProgress && (mEarlyDataDisposition == EARLY_NONE) &&
     786             :         NS_SUCCEEDED(rv) && (*countRead > 0)) {
     787             :         mEarlyDataDisposition = EARLY_SENT;
     788           0 :     }
     789             : 
     790             :     if (mDeferredSendProgress && mConnection && mConnection->Transport()) {
     791             :         // to avoid using mRequestStream concurrently, OnTransportStatus()
     792           0 :         // did not report upload status off the ReadSegments() stack from nsSocketTransport
     793             :         // do it now.
     794           1 :         OnTransportStatus(mConnection->Transport(), NS_NET_STATUS_SENDING_TO, 0);
     795             :     }
     796           4 :     mDeferredSendProgress = false;
     797             : 
     798             :     if (mForceRestart) {
     799             :         // The forceRestart condition was dealt with on the stack, but it did not
     800           0 :         // clear the flag because nsPipe in the readsegment stack clears out
     801           0 :         // return codes, so we need to use the flag here as a cue to return ERETARGETED
     802             :         if (NS_SUCCEEDED(rv)) {
     803           0 :             rv = NS_BINDING_RETARGETED;
     804             :         }
     805             :         mForceRestart = false;
     806             :     }
     807             : 
     808           4 :     // if read would block then we need to AsyncWait on the request stream.
     809             :     // have callback occur on socket thread so we stay synchronized.
     810           0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
     811           0 :         nsCOMPtr<nsIAsyncInputStream> asyncIn =
     812           0 :                 do_QueryInterface(mRequestStream);
     813           0 :         if (asyncIn) {
     814           0 :             nsCOMPtr<nsIEventTarget> target;
     815           0 :             Unused << gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
     816             :             if (target)
     817           0 :                 asyncIn->AsyncWait(this, 0, 0, target);
     818           0 :             else {
     819             :                 NS_ERROR("no socket thread event target");
     820             :                 rv = NS_ERROR_UNEXPECTED;
     821             :             }
     822             :         }
     823             :     }
     824             : 
     825             :     return rv;
     826             : }
     827           1 : 
     828             : nsresult
     829             : nsHttpTransaction::WritePipeSegment(nsIOutputStream *stream,
     830             :                                     void *closure,
     831             :                                     char *buf,
     832             :                                     uint32_t offset,
     833             :                                     uint32_t count,
     834           4 :                                     uint32_t *countWritten)
     835             : {
     836           0 :     nsHttpTransaction *trans = (nsHttpTransaction *) closure;
     837             : 
     838             :     if (trans->mTransactionDone)
     839           4 :         return NS_BASE_STREAM_CLOSED; // stop iterating
     840             : 
     841           0 :     if (trans->TimingEnabled()) {
     842             :         // Set the timestamp to Now(), only if it null
     843             :         trans->SetResponseStart(TimeStamp::Now(), true);
     844             :     }
     845           0 : 
     846           2 :     // Bug 1153929 - add checks to fix windows crash
     847             :     MOZ_ASSERT(trans->mWriter);
     848             :     if (!trans->mWriter) {
     849             :         return NS_ERROR_UNEXPECTED;
     850             :     }
     851             : 
     852             :     nsresult rv;
     853             :     //
     854           0 :     // OK, now let the caller fill this segment with data.
     855           0 :     //
     856             :     rv = trans->mWriter->OnWriteSegment(buf, count, countWritten);
     857           2 :     if (NS_FAILED(rv)) return rv; // caller didn't want to write anything
     858             : 
     859           2 :     LOG(("nsHttpTransaction::WritePipeSegment %p written=%u", trans, *countWritten));
     860           2 : 
     861           2 :     MOZ_ASSERT(*countWritten > 0, "bad writer");
     862             :     trans->mReceivedData = true;
     863             :     trans->mTransferSize += *countWritten;
     864             : 
     865             :     // Let the transaction "play" with the buffer.  It is free to modify
     866             :     // the contents of the buffer and/or modify countWritten.
     867             :     // - Bytes in HTTP headers don't count towards countWritten, so the input
     868             :     // side of pipe (aka nsHttpChannel's mTransactionPump) won't hit
     869           0 :     // OnInputStreamReady until all headers have been parsed.
     870           0 :     //
     871           0 :     rv = trans->ProcessData(buf, *countWritten, countWritten);
     872             :     if (NS_FAILED(rv))
     873             :         trans->Close(rv);
     874             : 
     875             :     return rv; // failure code only stops WriteSegments; it is not propagated.
     876           2 : }
     877             : 
     878           0 : bool nsHttpTransaction::ShouldThrottle()
     879             : {
     880             :     if (mClassOfService & nsIClassOfService::DontThrottle) {
     881             :         // We deliberately don't touch the throttling window here since
     882             :         // DontThrottle requests are expected to be long-standing media
     883             :         // streams and would just unnecessarily block running downloads.
     884             :         // If we want to ballance bandwidth for media responses against
     885             :         // running downloads, we need to find something smarter like 
     886             :         // changing the suspend/resume throttling intervals at-runtime.
     887             :         return false;
     888           4 :     }
     889             : 
     890             :     if (!gHttpHandler->ConnMgr()->ShouldThrottle(this)) {
     891             :         // We are not obligated to throttle
     892             :         return false;
     893           0 :     }
     894             : 
     895           0 :     if (mContentRead < 16000) {
     896             :         // Let the first bytes go, it may also well be all the content we get
     897             :         LOG(("nsHttpTransaction::ShouldThrottle too few content (%" PRIi64 ") this=%p", mContentRead, this));
     898             :         return false;
     899           0 :     }
     900           0 : 
     901           0 :     if (!(mClassOfService & nsIClassOfService::Throttleable) &&
     902             :         gHttpHandler->ConnMgr()->IsConnEntryUnderPressure(mConnInfo)) {
     903             :         LOG(("nsHttpTransaction::ShouldThrottle entry pressure this=%p", this));
     904             :         // This is expensive to check (two hashtable lookups) but may help
     905             :         // freeing connections for active tab transactions.
     906             :         // Checking this only for transactions that are not explicitly marked
     907             :         // as throttleable because trackers and (specially) downloads should
     908             :         // keep throttling even under pressure.
     909             :         return false;
     910             :     }
     911             : 
     912             :     return true;
     913             : }
     914           4 : 
     915             : nsresult
     916             : nsHttpTransaction::WriteSegments(nsAHttpSegmentWriter *writer,
     917           4 :                                  uint32_t count, uint32_t *countWritten)
     918             : {
     919           4 :     LOG(("nsHttpTransaction::WriteSegments %p", this));
     920             : 
     921           4 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     922           2 : 
     923             :     if (mTransactionDone) {
     924             :         return NS_SUCCEEDED(mStatus) ? NS_BASE_STREAM_CLOSED : mStatus;
     925           2 :     }
     926           0 : 
     927             :     if (ShouldThrottle()) {
     928           0 :         if (mThrottlingReadAllowance == THROTTLE_NO_LIMIT) { // no limit set
     929             :             // V1: ThrottlingReadLimit() returns 0
     930             :             mThrottlingReadAllowance = gHttpHandler->ThrottlingReadLimit();
     931           0 :         }
     932             :     } else {
     933             :         mThrottlingReadAllowance = THROTTLE_NO_LIMIT; // don't limit
     934           0 :     }
     935           0 : 
     936           0 :     if (mThrottlingReadAllowance == 0) { // depleted
     937           0 :         if (gHttpHandler->ConnMgr()->CurrentTopLevelOuterContentWindowId() !=
     938             :             mTopLevelOuterContentWindowId) {
     939             :             nsHttp::NotifyActiveTabLoadOptimization();
     940             :         }
     941             : 
     942           0 :         // Must remember that we have to call ResumeRecv() on our connection when
     943           0 :         // called back by the conn manager to resume reading.
     944             :         LOG(("nsHttpTransaction::WriteSegments %p response throttled", this));
     945             :         mReadingStopped = true;
     946             :         // This makes the underlaying connection or stream wait for explicit resume.
     947           0 :         // For h1 this means we stop reading from the socket.
     948             :         // For h2 this means we stop updating recv window for the stream.
     949             :         return NS_BASE_STREAM_WOULD_BLOCK;
     950           2 :     }
     951             : 
     952             :     mWriter = writer;
     953             : 
     954             : #ifdef WIN32 // bug 1153929
     955             :     MOZ_DIAGNOSTIC_ASSERT(mPipeOut);
     956             :     uint32_t * vtable = (uint32_t *) mPipeOut.get();
     957             :     MOZ_DIAGNOSTIC_ASSERT(*vtable != 0);
     958           4 : #endif // WIN32
     959             : 
     960             :     if (!mPipeOut) {
     961             :         return NS_ERROR_UNEXPECTED;
     962           2 :     }
     963           0 : 
     964             :     if (mThrottlingReadAllowance > 0) {
     965           0 :         LOG(("nsHttpTransaction::WriteSegments %p limiting read from %u to %d",
     966             :              this, count, mThrottlingReadAllowance));
     967             :         count = std::min(count, static_cast<uint32_t>(mThrottlingReadAllowance));
     968           2 :     }
     969             : 
     970           2 :     nsresult rv = mPipeOut->WriteSegments(WritePipeSegment, this, count, countWritten);
     971             : 
     972           0 :     mWriter = nullptr;
     973             : 
     974             :     if (mForceRestart) {
     975             :         // The forceRestart condition was dealt with on the stack, but it did not
     976           0 :         // clear the flag because nsPipe in the writesegment stack clears out
     977           0 :         // return codes, so we need to use the flag here as a cue to return ERETARGETED
     978             :         if (NS_SUCCEEDED(rv)) {
     979           0 :             rv = NS_BINDING_RETARGETED;
     980             :         }
     981             :         mForceRestart = false;
     982             :     }
     983             : 
     984           2 :     // if pipe would block then we need to AsyncWait on it.  have callback
     985           0 :     // occur on socket thread so we stay synchronized.
     986           0 :     if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
     987           0 :         nsCOMPtr<nsIEventTarget> target;
     988           0 :         Unused << gHttpHandler->GetSocketThreadTarget(getter_AddRefs(target));
     989           0 :         if (target) {
     990             :             mPipeOut->AsyncWait(this, 0, 0, target);
     991           0 :             mWaitingOnPipeOut = true;
     992           0 :         } else {
     993             :             NS_ERROR("no socket thread event target");
     994           0 :             rv = NS_ERROR_UNEXPECTED;
     995           0 :         }
     996           0 :     } else if (mThrottlingReadAllowance > 0 && NS_SUCCEEDED(rv)) {
     997             :         MOZ_ASSERT(count >= *countWritten);
     998             :         mThrottlingReadAllowance -= *countWritten;
     999             :     }
    1000             : 
    1001             :     return rv;
    1002             : }
    1003           0 : 
    1004             : void
    1005           0 : nsHttpTransaction::Close(nsresult reason)
    1006             : {
    1007             :     LOG(("nsHttpTransaction::Close [this=%p reason=%" PRIx32 "]\n",
    1008           2 :          this, static_cast<uint32_t>(reason)));
    1009           6 : 
    1010           2 :     if (!mClosed) {
    1011             :         gHttpHandler->ConnMgr()->RemoveActiveTransaction(this);
    1012             :         mActivated = false;
    1013           2 :     }
    1014           0 : 
    1015           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1016             :     if (reason == NS_BINDING_RETARGETED) {
    1017             :         LOG(("  close %p skipped due to ERETARGETED\n", this));
    1018             :         return;
    1019           0 :     }
    1020           0 : 
    1021             :     if (mClosed) {
    1022             :         LOG(("  already closed\n"));
    1023             :         return;
    1024           0 :     }
    1025           0 : 
    1026           0 :     if (mTransactionObserver) {
    1027             :         mTransactionObserver->Complete(this, reason);
    1028             :         mTransactionObserver = nullptr;
    1029           0 :     }
    1030           0 : 
    1031           0 :     if (mTokenBucketCancel) {
    1032             :         mTokenBucketCancel->Cancel(reason);
    1033             :         mTokenBucketCancel = nullptr;
    1034           0 :     }
    1035             : 
    1036           0 :     if (mActivityDistributor) {
    1037           0 :         // report the reponse is complete if not already reported
    1038             :         if (!mResponseIsComplete) {
    1039             :             nsresult rv = mActivityDistributor->ObserveActivity(
    1040             :                 mChannel,
    1041             :                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
    1042           0 :                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
    1043           0 :                 PR_Now(),
    1044           0 :                 static_cast<uint64_t>(mContentRead),
    1045           0 :                 EmptyCString());
    1046             :             if (NS_FAILED(rv)) {
    1047             :                 LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
    1048             :             }
    1049             :         }
    1050           0 : 
    1051             :         // report that this transaction is closing
    1052             :         nsresult rv = mActivityDistributor->ObserveActivity(
    1053             :             mChannel,
    1054           0 :             NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
    1055           0 :             NS_HTTP_ACTIVITY_SUBTYPE_TRANSACTION_CLOSE,
    1056           0 :             PR_Now(), 0, EmptyCString());
    1057             :         if (NS_FAILED(rv)) {
    1058             :             LOG3(("ObserveActivity failed (%08x)", static_cast<uint32_t>(rv)));
    1059             :         }
    1060             :     }
    1061             : 
    1062           2 :     // we must no longer reference the connection!  find out if the
    1063           0 :     // connection was being reused before letting it go.
    1064           0 :     bool connReused = false;
    1065             :     if (mConnection) {
    1066           2 :         connReused = mConnection->IsReused();
    1067           2 :     }
    1068             :     mConnected = false;
    1069             :     mTunnelProvider = nullptr;
    1070             : 
    1071             :     //
    1072             :     // if the connection was reset or closed before we wrote any part of the
    1073             :     // request or if we wrote the request but didn't receive any part of the
    1074             :     // response and the connection was being reused, then we can (and really
    1075             :     // should) assume that we wrote to a stale connection and we must therefore
    1076             :     // repeat the request over a new connection.
    1077             :     //
    1078             :     // We have decided to retry not only in case of the reused connections, but
    1079             :     // all safe methods(bug 1236277).
    1080             :     //
    1081             :     // NOTE: the conditions under which we will automatically retry the HTTP
    1082             :     // request have to be carefully selected to avoid duplication of the
    1083             :     // request from the point-of-view of the server.  such duplication could
    1084             :     // have dire consequences including repeated purchases, etc.
    1085             :     //
    1086             :     // NOTE: because of the way SSL proxy CONNECT is implemented, it is
    1087             :     // possible that the transaction may have received data without having
    1088             :     // sent any data.  for this reason, mSendData == FALSE does not imply
    1089             :     // mReceivedData == FALSE.  (see bug 203057 for more info.)
    1090             :     //
    1091             :     // Never restart transactions that are marked as sticky to their conenction.
    1092             :     // We use that capability to identify transactions bound to connection based
    1093             :     // authentication.  Reissuing them on a different connections will break
    1094             :     // this bondage.  Major issue may arise when there is an NTLM message auth
    1095             :     // header on the transaction and we send it to a different NTLM authenticated
    1096             :     // connection.  It will break that connection and also confuse the channel's
    1097           6 :     // auth provider, beliving the cached credentials are wrong and asking for
    1098           2 :     // the password mistakenly again from the user.
    1099           4 :     if ((reason == NS_ERROR_NET_RESET ||
    1100           2 :          reason == NS_OK ||
    1101             :          reason == psm::GetXPCOMFromNSSError(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA)) &&
    1102           2 :         (!(mCaps & NS_HTTP_STICKY_CONNECTION) || (mCaps & NS_HTTP_CONNECTION_RESTARTABLE))) {
    1103           0 : 
    1104           0 :         if (mForceRestart && NS_SUCCEEDED(Restart())) {
    1105             :             if (mResponseHead) {
    1106           0 :                 mResponseHead->Reset();
    1107           0 :             }
    1108           0 :             mContentRead = 0;
    1109           0 :             mContentLength = -1;
    1110           0 :             delete mChunkedDecoder;
    1111           0 :             mChunkedDecoder = nullptr;
    1112           0 :             mHaveStatusLine = false;
    1113           0 :             mHaveAllHeaders = false;
    1114           0 :             mHttpResponseMatched = false;
    1115           0 :             mResponseIsComplete = false;
    1116           0 :             mDidContentStart = false;
    1117           0 :             mNoContent = false;
    1118           0 :             mSentData = false;
    1119             :             mReceivedData = false;
    1120             :             LOG(("transaction force restarted\n"));
    1121             :             return;
    1122             :         }
    1123             : 
    1124             :         // reallySentData is meant to separate the instances where data has
    1125             :         // been sent by this transaction but buffered at a higher level while
    1126           0 :         // a TLS session (perhaps via a tunnel) is setup.
    1127             :         bool reallySentData =
    1128           6 :             mSentData && (!mConnection || mConnection->BytesWritten());
    1129           2 : 
    1130           0 :         if (reason == psm::GetXPCOMFromNSSError(SSL_ERROR_DOWNGRADE_WITH_EARLY_DATA) ||
    1131           0 :             (!mReceivedData &&
    1132             :             ((mRequestHead && mRequestHead->IsSafeMethod()) ||
    1133             :              !reallySentData || connReused))) {
    1134             :             // if restarting fails, then we must proceed to close the pipe,
    1135           0 :             // which will notify the channel that the transaction failed.
    1136             : 
    1137             :             if (NS_SUCCEEDED(Restart()))
    1138             :                 return;
    1139             :         }
    1140           1 :     }
    1141           4 : 
    1142             :     if ((mChunkedDecoder || (mContentLength >= int64_t(0))) &&
    1143           0 :         (NS_SUCCEEDED(reason) && !mResponseIsComplete)) {
    1144             : 
    1145           0 :         NS_WARNING("Partial transfer, incomplete HTTP response received");
    1146           0 : 
    1147           0 :         if ((mHttpResponseCode / 100 == 2) &&
    1148           0 :             (mHttpVersion >= HttpVersion::v1_1)) {
    1149           0 :             FrameCheckLevel clevel = gHttpHandler->GetEnforceH1Framing();
    1150           0 :             if (clevel >= FRAMECHECK_BARELY) {
    1151           0 :                 if ((clevel == FRAMECHECK_STRICT) ||
    1152           0 :                     (mChunkedDecoder && mChunkedDecoder->GetChunkRemaining()) ||
    1153           0 :                     (!mChunkedDecoder && !mContentDecoding && mContentDecodingCheck) ) {
    1154             :                     reason = NS_ERROR_NET_PARTIAL_TRANSFER;
    1155             :                     LOG(("Partial transfer, incomplete HTTP response received: %s",
    1156             :                          mChunkedDecoder ? "broken chunk" : "c-l underrun"));
    1157             :                 }
    1158             :             }
    1159           0 :         }
    1160             : 
    1161             :         if (mConnection) {
    1162           0 :             // whether or not we generate an error for the transaction
    1163             :             // bad framing means we don't want a pconn
    1164             :             mConnection->DontReuse();
    1165             :         }
    1166           2 :     }
    1167           2 : 
    1168             :     bool relConn = true;
    1169             :     if (NS_SUCCEEDED(reason)) {
    1170             : 
    1171             :         // the server has not sent the final \r\n terminating the header
    1172             :         // section, and there may still be a header line unparsed.  let's make
    1173           2 :         // sure we parse the remaining header line, and then hopefully, the
    1174           0 :         // response will be usable (see bug 88792).
    1175             :         if (!mHaveAllHeaders) {
    1176           0 :             char data = '\n';
    1177             :             uint32_t unused;
    1178           0 :             Unused << ParseHead(&data, 1, &unused);
    1179             : 
    1180           0 :             if (mResponseHead->Version() == HttpVersion::v0_9) {
    1181             :                 // Reject 0 byte HTTP/0.9 Responses - bug 423506
    1182             :                 LOG(("nsHttpTransaction::Close %p 0 Byte 0.9 Response", this));
    1183             :                 reason = NS_ERROR_NET_RESET;
    1184             :             }
    1185             :         }
    1186           2 : 
    1187           0 :         // honor the sticky connection flag...
    1188             :         if (mCaps & NS_HTTP_STICKY_CONNECTION)
    1189             :             relConn = false;
    1190             :     }
    1191             : 
    1192             :     // mTimings.responseEnd is normally recorded based on the end of a
    1193           4 :     // HTTP delimiter such as chunked-encodings or content-length. However,
    1194           0 :     // EOF or an error still require an end time be recorded.
    1195           0 :     if (TimingEnabled()) {
    1196           0 :         const TimingStruct timings = Timings();
    1197             :         if (timings.responseEnd.IsNull() && !timings.responseStart.IsNull()) {
    1198             :             SetResponseEnd(TimeStamp::Now());
    1199             :         }
    1200           4 :     }
    1201           4 : 
    1202           0 :     if (relConn && mConnection) {
    1203             :         MutexAutoLock lock(mLock);
    1204             :         mConnection = nullptr;
    1205           0 :     }
    1206           2 : 
    1207           2 :     mStatus = reason;
    1208           2 :     mTransactionDone = true; // forcibly flag the transaction as complete
    1209             :     mClosed = true;
    1210             :     ReleaseBlockingTransaction();
    1211           0 : 
    1212           2 :     // release some resources that we no longer need
    1213           2 :     mRequestStream = nullptr;
    1214           0 :     mReqHeaderBuf.Truncate();
    1215           0 :     mLineBuf.Truncate();
    1216           0 :     if (mChunkedDecoder) {
    1217             :         delete mChunkedDecoder;
    1218             :         mChunkedDecoder = nullptr;
    1219             :     }
    1220           0 : 
    1221             :     // closing this pipe triggers the channel's OnStopRequest method.
    1222             :     mPipeOut->CloseWithStatus(reason);
    1223             : 
    1224             : #ifdef WIN32 // bug 1153929
    1225             :     MOZ_DIAGNOSTIC_ASSERT(mPipeOut);
    1226             :     uint32_t * vtable = (uint32_t *) mPipeOut.get();
    1227             :     MOZ_DIAGNOSTIC_ASSERT(*vtable != 0);
    1228             :     mPipeOut = nullptr; // just in case
    1229             : #endif // WIN32
    1230             : }
    1231           3 : 
    1232             : nsHttpConnectionInfo *
    1233          12 : nsHttpTransaction::ConnectionInfo()
    1234             : {
    1235             :     return mConnInfo.get();
    1236             : }
    1237           0 : 
    1238             : bool // NOTE BASE CLASS
    1239           0 : nsAHttpTransaction::ResponseTimeoutEnabled() const
    1240             : {
    1241             :     return false;
    1242             : }
    1243           0 : 
    1244             : PRIntervalTime // NOTE BASE CLASS
    1245           0 : nsAHttpTransaction::ResponseTimeout()
    1246             : {
    1247             :     return gHttpHandler->ResponseTimeout();
    1248             : }
    1249           0 : 
    1250             : bool
    1251           0 : nsHttpTransaction::ResponseTimeoutEnabled() const
    1252             : {
    1253             :     return mResponseTimeoutEnabled;
    1254             : }
    1255             : 
    1256             : //-----------------------------------------------------------------------------
    1257             : // nsHttpTransaction <private>
    1258             : //-----------------------------------------------------------------------------
    1259           0 : 
    1260             : nsresult
    1261           0 : nsHttpTransaction::Restart()
    1262             : {
    1263             :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1264           0 : 
    1265           0 :     // limit the number of restart attempts - bug 92224
    1266             :     if (++mRestartCount >= gHttpHandler->MaxRequestAttempts()) {
    1267             :         LOG(("reached max request attempts, failing transaction @%p\n", this));
    1268             :         return NS_ERROR_NET_RESET;
    1269           0 :     }
    1270           0 : 
    1271             :     LOG(("restarting transaction @%p\n", this));
    1272             :     mTunnelProvider = nullptr;
    1273           0 : 
    1274           0 :     // rewind streams in case we already wrote out the request
    1275           0 :     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
    1276             :     if (seekable)
    1277             :         seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
    1278           0 : 
    1279           0 :     // clear old connection state...
    1280           0 :     mSecurityInfo = nullptr;
    1281           0 :     if (mConnection) {
    1282             :         if (!mReuseOnRestart) {
    1283           0 :             mConnection->DontReuse();
    1284           0 :         }
    1285             :         MutexAutoLock lock(mLock);
    1286             :         mConnection = nullptr;
    1287             :     }
    1288             : 
    1289           0 :     // Reset this to our default state, since this may change from one restart
    1290             :     // to the next
    1291           0 :     mReuseOnRestart = false;
    1292           0 : 
    1293           0 :     if (!mConnInfo->GetRoutedHost().IsEmpty()) {
    1294           0 :         MutexAutoLock lock(*nsHttp::GetLock());
    1295           0 :         RefPtr<nsHttpConnectionInfo> ci;
    1296           0 :          mConnInfo->CloneAsDirectRoute(getter_AddRefs(ci));
    1297             :          mConnInfo = ci;
    1298           0 :         if (mRequestHead) {
    1299           0 :             DebugOnly<nsresult> rv =
    1300           0 :                 mRequestHead->SetHeader(nsHttp::Alternate_Service_Used,
    1301             :                                         NS_LITERAL_CSTRING("0"));
    1302             :             MOZ_ASSERT(NS_SUCCEEDED(rv));
    1303             :         }
    1304           0 :     }
    1305             : 
    1306             :     return gHttpHandler->InitiateTransaction(this, mPriority);
    1307             : }
    1308           1 : 
    1309             : char *
    1310             : nsHttpTransaction::LocateHttpStart(char *buf, uint32_t len,
    1311           2 :                                    bool aAllowPartialMatch)
    1312             : {
    1313             :     MOZ_ASSERT(!aAllowPartialMatch || mLineBuf.IsEmpty());
    1314             : 
    1315             :     static const char HTTPHeader[] = "HTTP/1.";
    1316             :     static const uint32_t HTTPHeaderLen = sizeof(HTTPHeader) - 1;
    1317             :     static const char HTTP2Header[] = "HTTP/2.0";
    1318             :     static const uint32_t HTTP2HeaderLen = sizeof(HTTP2Header) - 1;
    1319             :     // ShoutCast ICY is treated as HTTP/1.0
    1320             :     static const char ICYHeader[] = "ICY ";
    1321           2 :     static const uint32_t ICYHeaderLen = sizeof(ICYHeader) - 1;
    1322           0 : 
    1323             :     if (aAllowPartialMatch && (len < HTTPHeaderLen))
    1324             :         return (PL_strncasecmp(buf, HTTPHeader, len) == 0) ? buf : nullptr;
    1325           2 : 
    1326           0 :     // mLineBuf can contain partial match from previous search
    1327           0 :     if (!mLineBuf.IsEmpty()) {
    1328           0 :         MOZ_ASSERT(mLineBuf.Length() < HTTPHeaderLen);
    1329             :         int32_t checkChars = std::min(len, HTTPHeaderLen - mLineBuf.Length());
    1330           0 :         if (PL_strncasecmp(buf, HTTPHeader + mLineBuf.Length(),
    1331           0 :                            checkChars) == 0) {
    1332             :             mLineBuf.Append(buf, checkChars);
    1333             :             if (mLineBuf.Length() == HTTPHeaderLen) {
    1334           0 :                 // We've found whole HTTPHeader sequence. Return pointer at the
    1335             :                 // end of matched sequence since it is stored in mLineBuf.
    1336             :                 return (buf + checkChars);
    1337             :             }
    1338             :             // Response matches pattern but is still incomplete.
    1339             :             return nullptr;
    1340             :         }
    1341           0 :         // Previous partial match together with new data doesn't match the
    1342             :         // pattern. Start the search again.
    1343             :         mLineBuf.Truncate();
    1344             :     }
    1345           2 : 
    1346           2 :     bool firstByte = true;
    1347           2 :     while (len > 0) {
    1348             :         if (PL_strncasecmp(buf, HTTPHeader, std::min<uint32_t>(len, HTTPHeaderLen)) == 0) {
    1349             :             if (len < HTTPHeaderLen) {
    1350           0 :                 // partial HTTPHeader sequence found
    1351           0 :                 // save partial match to mLineBuf
    1352             :                 mLineBuf.Assign(buf, len);
    1353             :                 return nullptr;
    1354             :             }
    1355             : 
    1356             :             // whole HTTPHeader sequence found
    1357             :             return buf;
    1358             :         }
    1359             : 
    1360             :         // At least "SmarterTools/2.0.3974.16813" generates nonsensical
    1361             :         // HTTP/2.0 responses to our HTTP/1 requests. Treat the minimal case of
    1362             :         // it as HTTP/1.1 to be compatible with old versions of ourselves and
    1363           0 :         // other browsers
    1364           0 : 
    1365           0 :         if (firstByte && !mInvalidResponseBytesRead && len >= HTTP2HeaderLen &&
    1366             :             (PL_strncasecmp(buf, HTTP2Header, HTTP2HeaderLen) == 0)) {
    1367             :             LOG(("nsHttpTransaction:: Identified HTTP/2.0 treating as 1.x\n"));
    1368             :             return buf;
    1369             :         }
    1370             : 
    1371             :         // Treat ICY (AOL/Nullsoft ShoutCast) non-standard header in same fashion
    1372             :         // as HTTP/2.0 is treated above. This will allow "ICY " to be interpretted
    1373           0 :         // as HTTP/1.0 in nsHttpResponseHead::ParseVersion
    1374           0 : 
    1375           0 :         if (firstByte && !mInvalidResponseBytesRead && len >= ICYHeaderLen &&
    1376             :             (PL_strncasecmp(buf, ICYHeader, ICYHeaderLen) == 0)) {
    1377             :             LOG(("nsHttpTransaction:: Identified ICY treating as HTTP/1.0\n"));
    1378             :             return buf;
    1379           0 :         }
    1380           0 : 
    1381           0 :         if (!nsCRT::IsAsciiSpace(*buf))
    1382           0 :             firstByte = false;
    1383             :         buf++;
    1384             :         len--;
    1385             :     }
    1386             :     return nullptr;
    1387             : }
    1388           0 : 
    1389             : nsresult
    1390           0 : nsHttpTransaction::ParseLine(nsACString &line)
    1391           0 : {
    1392             :     LOG(("nsHttpTransaction::ParseLine [%s]\n", PromiseFlatCString(line).get()));
    1393          22 :     nsresult rv = NS_OK;
    1394           2 : 
    1395           2 :     if (!mHaveStatusLine) {
    1396             :         mResponseHead->ParseStatusLine(line);
    1397           0 :         mHaveStatusLine = true;
    1398           0 :         // XXX this should probably never happen
    1399             :         if (mResponseHead->Version() == HttpVersion::v0_9)
    1400             :             mHaveAllHeaders = true;
    1401          20 :     }
    1402             :     else {
    1403           0 :         rv = mResponseHead->ParseHeaderLine(line);
    1404             :     }
    1405             :     return rv;
    1406             : }
    1407           1 : 
    1408             : nsresult
    1409          24 : nsHttpTransaction::ParseLineSegment(char *segment, uint32_t len)
    1410             : {
    1411          24 :     MOZ_ASSERT(!mHaveAllHeaders, "already have all headers");
    1412             : 
    1413             :     if (!mLineBuf.IsEmpty() && mLineBuf.Last() == '\n') {
    1414             :         // trim off the new line char, and if this segment is
    1415             :         // not a continuation of the previous or if we haven't
    1416           0 :         // parsed the status line yet, then parse the contents
    1417          22 :         // of mLineBuf.
    1418           0 :         mLineBuf.Truncate(mLineBuf.Length() - 1);
    1419          22 :         if (!mHaveStatusLine || (*segment != ' ' && *segment != '\t')) {
    1420           0 :             nsresult rv = ParseLine(mLineBuf);
    1421             :             mLineBuf.Truncate();
    1422             :             if (NS_FAILED(rv)) {
    1423             :                 return rv;
    1424             :             }
    1425             :         }
    1426             :     }
    1427           0 : 
    1428             :     // append segment to mLineBuf...
    1429             :     mLineBuf.Append(segment, len);
    1430          24 : 
    1431           2 :     // a line buf with only a new line char signifies the end of headers.
    1432             :     if (mLineBuf.First() == '\n') {
    1433           2 :         mLineBuf.Truncate();
    1434           2 :         // discard this response if it is a 100 continue or other 1xx status.
    1435           0 :         uint16_t status = mResponseHead->Status();
    1436           0 :         if ((status != 101) && (status / 100 == 1)) {
    1437           0 :             LOG(("ignoring 1xx response\n"));
    1438           0 :             mHaveStatusLine = false;
    1439           0 :             mHttpResponseMatched = false;
    1440           0 :             mConnection->SetLastTransactionExpectedNoContent(true);
    1441             :             mResponseHead->Reset();
    1442           0 :             return NS_OK;
    1443             :         }
    1444             :         mHaveAllHeaders = true;
    1445             :     }
    1446             :     return NS_OK;
    1447             : }
    1448           1 : 
    1449             : nsresult
    1450             : nsHttpTransaction::ParseHead(char *buf,
    1451             :                              uint32_t count,
    1452             :                              uint32_t *countRead)
    1453             : {
    1454             :     nsresult rv;
    1455             :     uint32_t len;
    1456           2 :     char *eol;
    1457             : 
    1458           2 :     LOG(("nsHttpTransaction::ParseHead [count=%u]\n", count));
    1459             : 
    1460           2 :     *countRead = 0;
    1461             : 
    1462             :     MOZ_ASSERT(!mHaveAllHeaders, "oops");
    1463           2 : 
    1464           2 :     // allocate the response head object if necessary
    1465           0 :     if (!mResponseHead) {
    1466             :         mResponseHead = new nsHttpResponseHead();
    1467             :         if (!mResponseHead)
    1468             :             return NS_ERROR_OUT_OF_MEMORY;
    1469           0 : 
    1470           0 :         // report that we have a least some of the response
    1471           0 :         if (mActivityDistributor && !mReportedStart) {
    1472             :             mReportedStart = true;
    1473             :             rv = mActivityDistributor->ObserveActivity(
    1474             :                 mChannel,
    1475           0 :                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
    1476           0 :                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_START,
    1477           0 :                 PR_Now(), 0, EmptyCString());
    1478             :             if (NS_FAILED(rv)) {
    1479             :                 LOG3(("ObserveActivity failed (%08x)",
    1480             :                       static_cast<uint32_t>(rv)));
    1481             :             }
    1482             :         }
    1483           2 :     }
    1484             : 
    1485             :     if (!mHttpResponseMatched) {
    1486             :         // Normally we insist on seeing HTTP/1.x in the first few bytes,
    1487             :         // but if we are on a persistent connection and the previous transaction
    1488             :         // was not supposed to have any content then we need to be prepared
    1489           4 :         // to skip over a response body that the server may have sent even
    1490             :         // though it wasn't allowed.
    1491           2 :         if (!mConnection || !mConnection->LastTransactionExpectedNoContent()) {
    1492           0 :             // tolerate only minor junk before the status line
    1493           2 :             mHttpResponseMatched = true;
    1494             :             char *p = LocateHttpStart(buf, std::min<uint32_t>(count, 11), true);
    1495           0 :             if (!p) {
    1496             :                 // Treat any 0.9 style response of a put as a failure.
    1497             :                 if (mRequestHead->IsPut())
    1498           0 :                     return NS_ERROR_ABORT;
    1499           0 : 
    1500           0 :                 mResponseHead->ParseStatusLine(EmptyCString());
    1501           0 :                 mHaveStatusLine = true;
    1502             :                 mHaveAllHeaders = true;
    1503           2 :                 return NS_OK;
    1504             :             }
    1505           0 :             if (p > buf) {
    1506           0 :                 // skip over the junk
    1507           0 :                 mInvalidResponseBytesRead += p - buf;
    1508             :                 *countRead = p - buf;
    1509             :                 buf = p;
    1510             :             }
    1511           0 :         }
    1512           0 :         else {
    1513           0 :             char *p = LocateHttpStart(buf, count, false);
    1514           0 :             if (p) {
    1515           0 :                 mInvalidResponseBytesRead += p - buf;
    1516           0 :                 *countRead = p - buf;
    1517             :                 buf = p;
    1518           0 :                 mHttpResponseMatched = true;
    1519           0 :             } else {
    1520           0 :                 mInvalidResponseBytesRead += count;
    1521           0 :                 *countRead = count;
    1522             :                 if (mInvalidResponseBytesRead > MAX_INVALID_RESPONSE_BODY_SIZE) {
    1523             :                     LOG(("nsHttpTransaction::ParseHead() "
    1524             :                          "Cannot find Response Header\n"));
    1525             :                     // cannot go back and call this 0.9 anymore as we
    1526             :                     // have thrown away a lot of the leading junk
    1527             :                     return NS_ERROR_ABORT;
    1528             :                 }
    1529             :                 return NS_OK;
    1530             :             }
    1531             :         }
    1532             :     }
    1533           2 :     // otherwise we can assume that we don't have a HTTP/0.9 response.
    1534          48 : 
    1535             :     MOZ_ASSERT (mHttpResponseMatched);
    1536          24 :     while ((eol = static_cast<char *>(memchr(buf, '\n', count - *countRead))) != nullptr) {
    1537             :         // found line in range [buf:eol]
    1538          24 :         len = eol - buf + 1;
    1539             : 
    1540             :         *countRead += len;
    1541          24 : 
    1542           0 :         // actually, the line is in the range [buf:eol-1]
    1543             :         if ((eol > buf) && (*(eol-1) == '\r'))
    1544          24 :             len--;
    1545           0 : 
    1546          24 :         buf[len-1] = '\n';
    1547             :         rv = ParseLineSegment(buf, len);
    1548             :         if (NS_FAILED(rv))
    1549          24 :             return rv;
    1550             : 
    1551             :         if (mHaveAllHeaders)
    1552             :             return NS_OK;
    1553           0 : 
    1554             :         // skip over line
    1555           0 :         buf = eol + 1;
    1556             : 
    1557             :         if (!mHttpResponseMatched) {
    1558             :             // a 100 class response has caused us to throw away that set of
    1559             :             // response headers and look for the next response
    1560             :             return NS_ERROR_NET_INTERRUPT;
    1561             :         }
    1562             :     }
    1563           0 : 
    1564           0 :     // do something about a partial header line
    1565             :     if (!mHaveAllHeaders && (len = count - *countRead)) {
    1566             :         *countRead = count;
    1567           0 :         // ignore a trailing carriage return, and don't bother calling
    1568             :         // ParseLineSegment if buf only contains a carriage return.
    1569           0 :         if ((buf[len-1] == '\r') && (--len == 0))
    1570           0 :             return NS_OK;
    1571             :         rv = ParseLineSegment(buf, len);
    1572             :         if (NS_FAILED(rv))
    1573             :             return rv;
    1574             :     }
    1575             :     return NS_OK;
    1576             : }
    1577           2 : 
    1578             : nsresult
    1579           0 : nsHttpTransaction::HandleContentStart()
    1580           2 : {
    1581             :     LOG(("nsHttpTransaction::HandleContentStart [this=%p]\n", this));
    1582           2 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    1583           2 : 
    1584           0 :     if (mResponseHead) {
    1585             :         if (mEarlyDataDisposition == EARLY_ACCEPTED) {
    1586           0 :             if (mResponseHead->Status() == 425) {
    1587             :                 // We will report this state when the final responce arrives.
    1588           0 :                 mEarlyDataDisposition = EARLY_425;
    1589             :             } else {
    1590           2 :                 Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_Early_Data, NS_LITERAL_CSTRING("accepted"));
    1591           0 :             }
    1592           0 :         } else if (mEarlyDataDisposition == EARLY_SENT) {
    1593           0 :             Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_Early_Data, NS_LITERAL_CSTRING("sent"));
    1594           0 :         } else if (mEarlyDataDisposition == EARLY_425) {
    1595             :             Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_Early_Data, NS_LITERAL_CSTRING("received 425"));
    1596             :             mEarlyDataDisposition = EARLY_NONE;
    1597           1 :         } // no header on NONE case
    1598           0 : 
    1599           0 :         if (mFastOpenStatus == TFO_DATA_SENT) {
    1600           0 :             Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_TCP_Fast_Open, NS_LITERAL_CSTRING("data sent"));
    1601           0 :         } else if (mFastOpenStatus == TFO_TRIED) {
    1602           0 :             Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_TCP_Fast_Open, NS_LITERAL_CSTRING("tried negotiating"));
    1603             :         } else if (mFastOpenStatus == TFO_FAILED) {
    1604             :             Unused << mResponseHead->SetHeader(nsHttp::X_Firefox_TCP_Fast_Open, NS_LITERAL_CSTRING("failed"));
    1605           2 :         } // no header on TFO_NOT_TRIED case
    1606           0 : 
    1607           0 :         if (LOG3_ENABLED()) {
    1608           0 :             LOG3(("http response [\n"));
    1609           0 :             nsAutoCString headers;
    1610           0 :             mResponseHead->Flatten(headers, false);
    1611           0 :             headers.AppendLiteral("  OriginalHeaders");
    1612           0 :             headers.AppendLiteral("\r\n");
    1613           0 :             mResponseHead->FlattenNetworkOriginalHeaders(headers);
    1614             :             LogHeaders(headers.get());
    1615             :             LOG3(("]\n"));
    1616           1 :         }
    1617             : 
    1618             :         CheckForStickyAuthScheme();
    1619             : 
    1620           1 :         // Save http version, mResponseHead isn't available anymore after
    1621           1 :         // TakeResponseHead() is called
    1622             :         mHttpVersion = mResponseHead->Version();
    1623             :         mHttpResponseCode = mResponseHead->Status();
    1624           2 : 
    1625           0 :         // notify the connection, give it a chance to cause a reset.
    1626           4 :         bool reset = false;
    1627           2 :         nsresult rv = mConnection->OnHeadersAvailable(this, mRequestHead,
    1628             :                                                       mResponseHead, &reset);
    1629             :         NS_ENSURE_SUCCESS(rv, rv);
    1630           0 : 
    1631           0 :         // looks like we should ignore this response, resetting...
    1632           0 :         if (reset) {
    1633           0 :             LOG(("resetting transaction's response head\n"));
    1634           0 :             mHaveAllHeaders = false;
    1635           0 :             mHaveStatusLine = false;
    1636           0 :             mReceivedData = false;
    1637           0 :             mSentData = false;
    1638             :             mHttpResponseMatched = false;
    1639           0 :             mResponseHead->Reset();
    1640             :             // wait to be called again...
    1641             :             return NS_OK;
    1642             :         }
    1643           0 : 
    1644             :         // check if this is a no-content response
    1645           0 :         switch (mResponseHead->Status()) {
    1646             :         case 101:
    1647             :             mPreserveStream = true;
    1648             :             MOZ_FALLTHROUGH; // to other no content cases:
    1649             :         case 204:
    1650           0 :         case 205:
    1651           0 :         case 304:
    1652             :             mNoContent = true;
    1653             :             LOG(("this response should not contain a body.\n"));
    1654           0 :             break;
    1655           0 :         case 421:
    1656             :             LOG(("Misdirected Request.\n"));
    1657             :             gHttpHandler->ConnMgr()->ClearHostMapping(mConnInfo);
    1658           0 : 
    1659           0 :             // retry on a new connection - just in case
    1660           0 :             if (!mRestartCount) {
    1661           0 :                 mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE;
    1662             :                 mForceRestart = true; // force restart has built in loop protection
    1663             :                 return NS_ERROR_NET_RESET;
    1664             :             }
    1665           0 :             break;
    1666           0 :         case 425:
    1667           0 :             LOG(("Too Early."));
    1668           0 :             if ((mEarlyDataDisposition == EARLY_425) && !mDoNotTryEarlyData) {
    1669           0 :                 mDoNotTryEarlyData = true;
    1670           0 :                 mForceRestart = true; // force restart has built in loop protection
    1671             :                 if (mConnection->Version() == HttpVersion::v2_0) {
    1672             :                     mReuseOnRestart = true;
    1673             :                 }
    1674             :                 return NS_ERROR_NET_RESET;
    1675             :             }
    1676             :             break;
    1677           1 :         }
    1678           1 : 
    1679             :         if (mResponseHead->Status() == 200 &&
    1680           0 :             mConnection->IsProxyConnectInProgress()) {
    1681             :             // successful CONNECTs do not have response bodies
    1682           2 :             mNoContent = true;
    1683             :         }
    1684           2 :         mConnection->SetLastTransactionExpectedNoContent(mNoContent);
    1685           0 : 
    1686             :         if (mNoContent) {
    1687             :             mContentLength = 0;
    1688           2 :         } else {
    1689             :             // grab the content-length from the response headers
    1690             :             mContentLength = mResponseHead->ContentLength();
    1691             : 
    1692             :             // handle chunked encoding here, so we'll know immediately when
    1693             :             // we're done with the socket.  please note that _all_ other
    1694           0 :             // decoding is done when the channel receives the content data
    1695           2 :             // so as not to block the socket transport thread too much.
    1696             :             if (mResponseHead->Version() >= HttpVersion::v1_0 &&
    1697           0 :                 mResponseHead->HasHeaderValue(nsHttp::Transfer_Encoding, "chunked")) {
    1698           0 :                 // we only support the "chunked" transfer encoding right now.
    1699             :                 mChunkedDecoder = new nsHttpChunkedDecoder();
    1700           0 :                 LOG(("nsHttpTransaction %p chunked decoder created\n", this));
    1701           0 :                 // Ignore server specified Content-Length.
    1702           0 :                 if (mContentLength != int64_t(-1)) {
    1703           0 :                     LOG(("nsHttpTransaction %p chunked with C-L ignores C-L\n", this));
    1704           0 :                     mContentLength = -1;
    1705             :                     if (mConnection) {
    1706             :                         mConnection->DontReuse();
    1707             :                     }
    1708           2 :                 }
    1709           0 :             }
    1710             :             else if (mContentLength == int64_t(-1))
    1711             :                 LOG(("waiting for the server to close the connection.\n"));
    1712             :         }
    1713           0 :     }
    1714           2 : 
    1715             :     mDidContentStart = true;
    1716             :     return NS_OK;
    1717             : }
    1718             : 
    1719           2 : // called on the socket thread
    1720             : nsresult
    1721             : nsHttpTransaction::HandleContent(char *buf,
    1722             :                                  uint32_t count,
    1723             :                                  uint32_t *contentRead,
    1724             :                                  uint32_t *contentRemaining)
    1725             : {
    1726           2 :     nsresult rv;
    1727             : 
    1728           0 :     LOG(("nsHttpTransaction::HandleContent [this=%p count=%u]\n", this, count));
    1729           2 : 
    1730             :     *contentRead = 0;
    1731           4 :     *contentRemaining = 0;
    1732             : 
    1733           2 :     MOZ_ASSERT(mConnection);
    1734           2 : 
    1735           0 :     if (!mDidContentStart) {
    1736             :         rv = HandleContentStart();
    1737           0 :         if (NS_FAILED(rv)) return rv;
    1738             :         // Do not write content to the pipe if we haven't started streaming yet
    1739             :         if (!mDidContentStart)
    1740             :             return NS_OK;
    1741           2 :     }
    1742             : 
    1743             :     if (mChunkedDecoder) {
    1744           0 :         // give the buf over to the chunked decoder so it can reformat the
    1745           0 :         // data and tell us how much is really there.
    1746             :         rv = mChunkedDecoder->HandleChunkedContent(buf, count, contentRead, contentRemaining);
    1747           2 :         if (NS_FAILED(rv)) return rv;
    1748             :     }
    1749             :     else if (mContentLength >= int64_t(0)) {
    1750             :         // HTTP/1.0 servers have been known to send erroneous Content-Length
    1751             :         // headers. So, unless the connection is persistent, we must make
    1752           2 :         // allowances for a possibly invalid Content-Length header. Thus, if
    1753           0 :         // NOT persistent, we simply accept everything in |buf|.
    1754           0 :         if (mConnection->IsPersistent() || mPreserveStream ||
    1755           4 :             mHttpVersion >= HttpVersion::v1_1) {
    1756           0 :             int64_t remaining = mContentLength - mContentRead;
    1757             :             *contentRead = uint32_t(std::min<int64_t>(count, remaining));
    1758             :             *contentRemaining = count - *contentRead;
    1759           0 :         }
    1760             :         else {
    1761           0 :             *contentRead = count;
    1762           0 :             // mContentLength might need to be increased...
    1763           0 :             int64_t position = mContentRead + int64_t(count);
    1764             :             if (position > mContentLength) {
    1765             :                 mContentLength = position;
    1766             :                 //mResponseHead->SetContentLength(mContentLength);
    1767             :             }
    1768             :         }
    1769             :     }
    1770             :     else {
    1771           0 :         // when we are just waiting for the server to close the connection...
    1772             :         // (no explicit content-length given)
    1773             :         *contentRead = count;
    1774           2 :     }
    1775             : 
    1776           2 :     if (*contentRead) {
    1777             :         // update count of content bytes read and report progress...
    1778             :         mContentRead += *contentRead;
    1779           2 :     }
    1780             : 
    1781             :     LOG(("nsHttpTransaction::HandleContent [this=%p count=%u read=%u mContentRead=%" PRId64 " mContentLength=%" PRId64 "]\n",
    1782             :         this, count, *contentRead, mContentRead, mContentLength));
    1783           0 : 
    1784           0 :     // check for end-of-file
    1785           0 :     if ((mContentRead == mContentLength) ||
    1786           2 :         (mChunkedDecoder && mChunkedDecoder->ReachedEOF())) {
    1787           0 :         MutexAutoLock lock(*nsHttp::GetLock());
    1788             :         if (mChunkedDecoder) {
    1789             :             mForTakeResponseTrailers = mChunkedDecoder->TakeTrailers();
    1790             :         }
    1791           2 : 
    1792           0 :         // the transaction is done with a complete response.
    1793           0 :         mTransactionDone = true;
    1794             :         mResponseIsComplete = true;
    1795           0 :         ReleaseBlockingTransaction();
    1796           0 : 
    1797             :         if (TimingEnabled()) {
    1798             :             SetResponseEnd(TimeStamp::Now());
    1799             :         }
    1800           0 : 
    1801           0 :         // report the entire response has arrived
    1802             :         if (mActivityDistributor) {
    1803             :             rv = mActivityDistributor->ObserveActivity(
    1804             :                 mChannel,
    1805             :                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
    1806           0 :                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
    1807           0 :                 PR_Now(),
    1808           0 :                 static_cast<uint64_t>(mContentRead),
    1809           0 :                 EmptyCString());
    1810             :             if (NS_FAILED(rv)) {
    1811             :                 LOG3(("ObserveActivity failed (%08x)",
    1812             :                       static_cast<uint32_t>(rv)));
    1813             :             }
    1814             :         }
    1815             :     }
    1816             : 
    1817             :     return NS_OK;
    1818             : }
    1819           2 : 
    1820             : nsresult
    1821             : nsHttpTransaction::ProcessData(char *buf, uint32_t count, uint32_t *countRead)
    1822             : {
    1823           2 :     nsresult rv;
    1824             : 
    1825           2 :     LOG(("nsHttpTransaction::ProcessData [this=%p count=%u]\n", this, count));
    1826             : 
    1827             :     *countRead = 0;
    1828           0 : 
    1829             :     // we may not have read all of the headers yet...
    1830             :     if (!mHaveAllHeaders) {
    1831             :         uint32_t bytesConsumed = 0;
    1832           0 : 
    1833           2 :         do {
    1834           0 :             uint32_t localBytesConsumed = 0;
    1835             :             char *localBuf = buf + bytesConsumed;
    1836           2 :             uint32_t localCount = count - bytesConsumed;
    1837           0 : 
    1838           0 :             rv = ParseHead(localBuf, localCount, &localBytesConsumed);
    1839           2 :             if (NS_FAILED(rv) && rv != NS_ERROR_NET_INTERRUPT)
    1840           2 :                 return rv;
    1841             :             bytesConsumed += localBytesConsumed;
    1842           0 :         } while (rv == NS_ERROR_NET_INTERRUPT);
    1843           0 : 
    1844           2 :         mCurrentHttpResponseHeaderSize += bytesConsumed;
    1845           0 :         if (mCurrentHttpResponseHeaderSize >
    1846             :             gHttpHandler->MaxHttpResponseHeaderSize()) {
    1847             :             LOG(("nsHttpTransaction %p The response header exceeds the limit.\n",
    1848             :                  this));
    1849           0 :             return NS_ERROR_FILE_TOO_BIG;
    1850             :         }
    1851             :         count -= bytesConsumed;
    1852           0 : 
    1853           0 :         // if buf has some content in it, shift bytes to top of buf.
    1854             :         if (count && bytesConsumed)
    1855             :             memmove(buf, buf + bytesConsumed, count);
    1856           4 : 
    1857           0 :         // report the completed response header
    1858           0 :         if (mActivityDistributor && mResponseHead && mHaveAllHeaders &&
    1859           0 :             !mReportedResponseHeader) {
    1860           0 :             mReportedResponseHeader = true;
    1861           0 :             nsAutoCString completeResponseHeaders;
    1862           0 :             mResponseHead->Flatten(completeResponseHeaders, false);
    1863             :             completeResponseHeaders.AppendLiteral("\r\n");
    1864             :             rv = mActivityDistributor->ObserveActivity(
    1865             :                 mChannel,
    1866             :                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
    1867           0 :                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_HEADER,
    1868           0 :                 PR_Now(), 0,
    1869           0 :                 completeResponseHeaders);
    1870             :             if (NS_FAILED(rv)) {
    1871             :                 LOG3(("ObserveActivity failed (%08x)",
    1872             :                       static_cast<uint32_t>(rv)));
    1873             :             }
    1874             :         }
    1875             :     }
    1876             : 
    1877           0 :     // even though count may be 0, we still want to call HandleContent
    1878           1 :     // so it can complete the transaction if this is a "no-content" response.
    1879             :     if (mHaveAllHeaders) {
    1880             :         uint32_t countRemaining = 0;
    1881             :         //
    1882             :         // buf layout:
    1883             :         //
    1884             :         // +--------------------------------------+----------------+-----+
    1885             :         // |              countRead               | countRemaining |     |
    1886             :         // +--------------------------------------+----------------+-----+
    1887             :         //
    1888             :         // count          : bytes read from the socket
    1889             :         // countRead      : bytes corresponding to this transaction
    1890             :         // countRemaining : bytes corresponding to next transaction on conn
    1891             :         //
    1892             :         // NOTE:
    1893           2 :         // count > countRead + countRemaining <==> chunked transfer encoding
    1894           2 :         //
    1895             :         rv = HandleContent(buf, count, countRead, &countRemaining);
    1896             :         if (NS_FAILED(rv)) return rv;
    1897           2 :         // we may have read more than our share, in which case we must give
    1898           0 :         // the excess bytes back to the connection
    1899           0 :         if (mResponseIsComplete && countRemaining) {
    1900           0 :             MOZ_ASSERT(mConnection);
    1901             :             rv = mConnection->PushBack(buf + *countRead, countRemaining);
    1902             :             NS_ENSURE_SUCCESS(rv, rv);
    1903           0 :         }
    1904           2 : 
    1905           2 :         if (!mContentDecodingCheck && mResponseHead) {
    1906           0 :             mContentDecoding =
    1907             :                 mResponseHead->HasHeader(nsHttp::Content_Encoding);
    1908             :             mContentDecodingCheck = true;
    1909             :         }
    1910             :     }
    1911             : 
    1912             :     return NS_OK;
    1913             : }
    1914           0 : 
    1915             : void
    1916           0 : nsHttpTransaction::SetRequestContext(nsIRequestContext *aRequestContext)
    1917           0 : {
    1918           0 :     LOG(("nsHttpTransaction %p SetRequestContext %p\n", this, aRequestContext));
    1919             :     mRequestContext = aRequestContext;
    1920             : }
    1921             : 
    1922             : // Called when the transaction marked for blocking is associated with a connection
    1923             : // (i.e. added to a new h1 conn, an idle http connection, etc..)
    1924             : // It is safe to call this multiple times with it only
    1925           0 : // having an effect once.
    1926             : void
    1927           0 : nsHttpTransaction::DispatchedAsBlocking()
    1928             : {
    1929             :     if (mDispatchedAsBlocking)
    1930           0 :         return;
    1931             : 
    1932           0 :     LOG(("nsHttpTransaction %p dispatched as blocking\n", this));
    1933             : 
    1934             :     if (!mRequestContext)
    1935           0 :         return;
    1936             : 
    1937             :     LOG(("nsHttpTransaction adding blocking transaction %p from "
    1938           0 :          "request context %p\n", this, mRequestContext.get()));
    1939           0 : 
    1940             :     mRequestContext->AddBlockingTransaction();
    1941             :     mDispatchedAsBlocking = true;
    1942             : }
    1943           5 : 
    1944             : void
    1945          10 : nsHttpTransaction::RemoveDispatchedAsBlocking()
    1946           5 : {
    1947             :     if (!mRequestContext || !mDispatchedAsBlocking) {
    1948           0 :         LOG(("nsHttpTransaction::RemoveDispatchedAsBlocking this=%p not blocking",
    1949             :              this));
    1950             :         return;
    1951           0 :     }
    1952           0 : 
    1953             :     uint32_t blockers = 0;
    1954           0 :     nsresult rv = mRequestContext->RemoveBlockingTransaction(&blockers);
    1955             : 
    1956             :     LOG(("nsHttpTransaction removing blocking transaction %p from "
    1957             :          "request context %p. %d blockers remain.\n", this,
    1958           0 :          mRequestContext.get(), blockers));
    1959           0 : 
    1960             :     if (NS_SUCCEEDED(rv) && !blockers) {
    1961           0 :         LOG(("nsHttpTransaction %p triggering release of blocked channels "
    1962           0 :              " with request context=%p\n", this, mRequestContext.get()));
    1963           0 :         rv = gHttpHandler->ConnMgr()->ProcessPendingQ();
    1964             :         if (NS_FAILED(rv)) {
    1965             :             LOG(("nsHttpTransaction::RemoveDispatchedAsBlocking\n"
    1966             :                  "    failed to process pending queue\n"));
    1967             :         }
    1968           0 :     }
    1969             : 
    1970             :     mDispatchedAsBlocking = false;
    1971             : }
    1972           1 : 
    1973             : void
    1974           5 : nsHttpTransaction::ReleaseBlockingTransaction()
    1975           5 : {
    1976             :     RemoveDispatchedAsBlocking();
    1977           0 :     LOG(("nsHttpTransaction %p request context set to null "
    1978           5 :          "in ReleaseBlockingTransaction() - was %p\n", this, mRequestContext.get()));
    1979             :     mRequestContext = nullptr;
    1980             : }
    1981           0 : 
    1982             : void
    1983           0 : nsHttpTransaction::DisableSpdy()
    1984           0 : {
    1985             :     mCaps |= NS_HTTP_DISALLOW_SPDY;
    1986             :     if (mConnInfo) {
    1987           0 :         // This is our clone of the connection info, not the persistent one that
    1988             :         // is owned by the connection manager, so we're safe to change this here
    1989           0 :         mConnInfo->SetNoSpdy(true);
    1990             :     }
    1991             : }
    1992           0 : 
    1993             : void
    1994           2 : nsHttpTransaction::CheckForStickyAuthScheme()
    1995             : {
    1996           0 :   LOG(("nsHttpTransaction::CheckForStickyAuthScheme this=%p", this));
    1997           2 : 
    1998           0 :   MOZ_ASSERT(mHaveAllHeaders);
    1999             :   MOZ_ASSERT(mResponseHead);
    2000           2 :   MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2001           0 : 
    2002           2 :   CheckForStickyAuthSchemeAt(nsHttp::WWW_Authenticate);
    2003             :   CheckForStickyAuthSchemeAt(nsHttp::Proxy_Authenticate);
    2004             : }
    2005           0 : 
    2006             : void
    2007           0 : nsHttpTransaction::CheckForStickyAuthSchemeAt(nsHttpAtom const& header)
    2008           0 : {
    2009           0 :   if (mCaps & NS_HTTP_STICKY_CONNECTION) {
    2010             :       LOG(("  already sticky"));
    2011             :       return;
    2012           4 :   }
    2013           4 : 
    2014             :   nsAutoCString auth;
    2015             :   if (NS_FAILED(mResponseHead->GetHeader(header, auth))) {
    2016             :       return;
    2017           0 :   }
    2018           0 : 
    2019           0 :   Tokenizer p(auth);
    2020           0 :   nsAutoCString schema;
    2021             :   while (p.ReadWord(schema)) {
    2022           0 :       ToLowerCase(schema);
    2023           0 : 
    2024           0 :       nsAutoCString contractid;
    2025             :       contractid.AssignLiteral(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
    2026             :       contractid.Append(schema);
    2027           0 : 
    2028           0 :       // using a new instance because of thread safety of auth modules refcnt
    2029             :       nsCOMPtr<nsIHttpAuthenticator> authenticator(do_CreateInstance(contractid.get()));
    2030           0 :       if (authenticator) {
    2031           0 :           uint32_t flags;
    2032           0 :           nsresult rv = authenticator->GetAuthFlags(&flags);
    2033             :           if (NS_SUCCEEDED(rv) && (flags & nsIHttpAuthenticator::CONNECTION_BASED)) {
    2034             :               LOG(("  connection made sticky, found %s auth shema", schema.get()));
    2035           0 :               // This is enough to make this transaction keep it's current connection,
    2036           0 :               // prevents the connection from being released back to the pool.
    2037             :               mCaps |= NS_HTTP_STICKY_CONNECTION;
    2038             :               break;
    2039             :           }
    2040             :       }
    2041           0 : 
    2042           0 :       // schemes are separated with LFs, nsHttpHeaderArray::MergeHeader
    2043             :       p.SkipUntil(Tokenizer::Token::NewLine());
    2044             :       p.SkipWhites(Tokenizer::INCLUDE_NEW_LINE);
    2045             :   }
    2046             : }
    2047           1 : 
    2048             : const TimingStruct
    2049           2 : nsHttpTransaction::Timings()
    2050           0 : {
    2051           0 :     mozilla::MutexAutoLock lock(mLock);
    2052             :     TimingStruct timings = mTimings;
    2053             :     return timings;
    2054             : }
    2055           0 : 
    2056             : void
    2057           0 : nsHttpTransaction::BootstrapTimings(TimingStruct times)
    2058           0 : {
    2059           0 :     mozilla::MutexAutoLock lock(mLock);
    2060             :     mTimings = times;
    2061             : }
    2062           0 : 
    2063             : void
    2064           0 : nsHttpTransaction::SetDomainLookupStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
    2065           0 : {
    2066           0 :     mozilla::MutexAutoLock lock(mLock);
    2067             :     if (onlyIfNull && !mTimings.domainLookupStart.IsNull()) {
    2068           0 :         return; // We only set the timestamp if it was previously null
    2069             :     }
    2070             :     mTimings.domainLookupStart = timeStamp;
    2071             : }
    2072           0 : 
    2073             : void
    2074           0 : nsHttpTransaction::SetDomainLookupEnd(mozilla::TimeStamp timeStamp, bool onlyIfNull)
    2075           0 : {
    2076           0 :     mozilla::MutexAutoLock lock(mLock);
    2077             :     if (onlyIfNull && !mTimings.domainLookupEnd.IsNull()) {
    2078           0 :         return; // We only set the timestamp if it was previously null
    2079             :     }
    2080             :     mTimings.domainLookupEnd = timeStamp;
    2081             : }
    2082           0 : 
    2083             : void
    2084           0 : nsHttpTransaction::SetConnectStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
    2085           0 : {
    2086           0 :     mozilla::MutexAutoLock lock(mLock);
    2087             :     if (onlyIfNull && !mTimings.connectStart.IsNull()) {
    2088           0 :         return; // We only set the timestamp if it was previously null
    2089             :     }
    2090             :     mTimings.connectStart = timeStamp;
    2091             : }
    2092           0 : 
    2093             : void
    2094           0 : nsHttpTransaction::SetConnectEnd(mozilla::TimeStamp timeStamp, bool onlyIfNull)
    2095           0 : {
    2096           0 :     mozilla::MutexAutoLock lock(mLock);
    2097             :     if (onlyIfNull && !mTimings.connectEnd.IsNull()) {
    2098           0 :         return; // We only set the timestamp if it was previously null
    2099             :     }
    2100             :     mTimings.connectEnd = timeStamp;
    2101             : }
    2102           0 : 
    2103             : void
    2104           0 : nsHttpTransaction::SetRequestStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
    2105           0 : {
    2106           0 :     mozilla::MutexAutoLock lock(mLock);
    2107             :     if (onlyIfNull && !mTimings.requestStart.IsNull()) {
    2108           0 :         return; // We only set the timestamp if it was previously null
    2109             :     }
    2110             :     mTimings.requestStart = timeStamp;
    2111             : }
    2112           0 : 
    2113             : void
    2114           0 : nsHttpTransaction::SetResponseStart(mozilla::TimeStamp timeStamp, bool onlyIfNull)
    2115           0 : {
    2116           0 :     mozilla::MutexAutoLock lock(mLock);
    2117             :     if (onlyIfNull && !mTimings.responseStart.IsNull()) {
    2118           0 :         return; // We only set the timestamp if it was previously null
    2119             :     }
    2120             :     mTimings.responseStart = timeStamp;
    2121             : }
    2122           0 : 
    2123             : void
    2124           0 : nsHttpTransaction::SetResponseEnd(mozilla::TimeStamp timeStamp, bool onlyIfNull)
    2125           0 : {
    2126           0 :     mozilla::MutexAutoLock lock(mLock);
    2127             :     if (onlyIfNull && !mTimings.responseEnd.IsNull()) {
    2128           0 :         return; // We only set the timestamp if it was previously null
    2129             :     }
    2130             :     mTimings.responseEnd = timeStamp;
    2131             : }
    2132           0 : 
    2133             : mozilla::TimeStamp
    2134           0 : nsHttpTransaction::GetDomainLookupStart()
    2135           0 : {
    2136             :     mozilla::MutexAutoLock lock(mLock);
    2137             :     return mTimings.domainLookupStart;
    2138             : }
    2139           0 : 
    2140             : mozilla::TimeStamp
    2141           0 : nsHttpTransaction::GetDomainLookupEnd()
    2142           0 : {
    2143             :     mozilla::MutexAutoLock lock(mLock);
    2144             :     return mTimings.domainLookupEnd;
    2145             : }
    2146           0 : 
    2147             : mozilla::TimeStamp
    2148           0 : nsHttpTransaction::GetConnectStart()
    2149           0 : {
    2150             :     mozilla::MutexAutoLock lock(mLock);
    2151             :     return mTimings.connectStart;
    2152             : }
    2153           0 : 
    2154             : mozilla::TimeStamp
    2155           0 : nsHttpTransaction::GetTcpConnectEnd()
    2156           0 : {
    2157             :     mozilla::MutexAutoLock lock(mLock);
    2158             :     return mTimings.tcpConnectEnd;
    2159             : }
    2160           0 : 
    2161             : mozilla::TimeStamp
    2162           0 : nsHttpTransaction::GetSecureConnectionStart()
    2163           0 : {
    2164             :     mozilla::MutexAutoLock lock(mLock);
    2165             :     return mTimings.secureConnectionStart;
    2166             : }
    2167           0 : 
    2168             : mozilla::TimeStamp
    2169           0 : nsHttpTransaction::GetConnectEnd()
    2170           0 : {
    2171             :     mozilla::MutexAutoLock lock(mLock);
    2172             :     return mTimings.connectEnd;
    2173             : }
    2174           0 : 
    2175             : mozilla::TimeStamp
    2176           0 : nsHttpTransaction::GetRequestStart()
    2177           0 : {
    2178             :     mozilla::MutexAutoLock lock(mLock);
    2179             :     return mTimings.requestStart;
    2180             : }
    2181           0 : 
    2182             : mozilla::TimeStamp
    2183           0 : nsHttpTransaction::GetResponseStart()
    2184           0 : {
    2185             :     mozilla::MutexAutoLock lock(mLock);
    2186             :     return mTimings.responseStart;
    2187             : }
    2188           0 : 
    2189             : mozilla::TimeStamp
    2190           0 : nsHttpTransaction::GetResponseEnd()
    2191           0 : {
    2192             :     mozilla::MutexAutoLock lock(mLock);
    2193             :     return mTimings.responseEnd;
    2194             : }
    2195             : 
    2196             : //-----------------------------------------------------------------------------
    2197             : // nsHttpTransaction deletion event
    2198           0 : //-----------------------------------------------------------------------------
    2199             : 
    2200           0 : class DeleteHttpTransaction : public Runnable {
    2201           0 : public:
    2202           0 :   explicit DeleteHttpTransaction(nsHttpTransaction* trans)
    2203             :     : Runnable("net::DeleteHttpTransaction")
    2204           0 :     , mTrans(trans)
    2205             :   {
    2206           0 :   }
    2207             : 
    2208           0 :   NS_IMETHOD Run() override
    2209           0 :   {
    2210             :     delete mTrans;
    2211             :     return NS_OK;
    2212             :     }
    2213             : private:
    2214             :     nsHttpTransaction *mTrans;
    2215             : };
    2216           1 : 
    2217             : void
    2218           0 : nsHttpTransaction::DeleteSelfOnConsumerThread()
    2219             : {
    2220             :     LOG(("nsHttpTransaction::DeleteSelfOnConsumerThread [this=%p]\n", this));
    2221           4 : 
    2222           2 :     bool val;
    2223           1 :     if (!mConsumerTarget ||
    2224             :         (NS_SUCCEEDED(mConsumerTarget->IsOnCurrentThread(&val)) && val)) {
    2225           0 :         delete this;
    2226           0 :     } else {
    2227           0 :         LOG(("proxying delete to consumer thread...\n"));
    2228           0 :         nsCOMPtr<nsIRunnable> event = new DeleteHttpTransaction(this);
    2229             :         if (NS_FAILED(mConsumerTarget->Dispatch(event, NS_DISPATCH_NORMAL)))
    2230           0 :             NS_WARNING("failed to dispatch nsHttpDeleteTransaction event");
    2231             :     }
    2232             : }
    2233           3 : 
    2234             : bool
    2235           0 : nsHttpTransaction::TryToRunPacedRequest()
    2236           0 : {
    2237             :     if (mSubmittedRatePacing)
    2238           3 :         return mPassedRatePacing;
    2239           0 : 
    2240           6 :     mSubmittedRatePacing = true;
    2241           3 :     mSynchronousRatePaceRequest = true;
    2242           0 :     Unused << gHttpHandler->SubmitPacedRequest(this, getter_AddRefs(mTokenBucketCancel));
    2243             :     mSynchronousRatePaceRequest = false;
    2244             :     return mPassedRatePacing;
    2245             : }
    2246           3 : 
    2247             : void
    2248           0 : nsHttpTransaction::OnTokenBucketAdmitted()
    2249           0 : {
    2250             :     mPassedRatePacing = true;
    2251           0 :     mTokenBucketCancel = nullptr;
    2252           0 : 
    2253           0 :     if (!mSynchronousRatePaceRequest) {
    2254           0 :         nsresult rv = gHttpHandler->ConnMgr()->ProcessPendingQ(mConnInfo);
    2255             :         if (NS_FAILED(rv)) {
    2256             :             LOG(("nsHttpTransaction::OnTokenBucketAdmitted\n"
    2257             :                  "    failed to process pending queue\n"));
    2258           0 :         }
    2259             :     }
    2260             : }
    2261           0 : 
    2262             : void
    2263           1 : nsHttpTransaction::CancelPacing(nsresult reason)
    2264           0 : {
    2265           0 :     if (mTokenBucketCancel) {
    2266             :         mTokenBucketCancel->Cancel(reason);
    2267           0 :         mTokenBucketCancel = nullptr;
    2268             :     }
    2269             : }
    2270             : 
    2271             : //-----------------------------------------------------------------------------
    2272             : // nsHttpTransaction::nsISupports
    2273           0 : //-----------------------------------------------------------------------------
    2274             : 
    2275             : NS_IMPL_ADDREF(nsHttpTransaction)
    2276           0 : 
    2277             : NS_IMETHODIMP_(MozExternalRefCountType)
    2278             : nsHttpTransaction::Release()
    2279          34 : {
    2280          17 :     nsrefcnt count;
    2281          17 :     MOZ_ASSERT(0 != mRefCnt, "dup release");
    2282           0 :     count = --mRefCnt;
    2283           1 :     NS_LOG_RELEASE(this, count, "nsHttpTransaction");
    2284             :     if (0 == count) {
    2285             :         mRefCnt = 1; /* stablize */
    2286           1 :         // it is essential that the transaction be destroyed on the consumer
    2287           1 :         // thread (we could be holding the last reference to our consumer).
    2288             :         DeleteSelfOnConsumerThread();
    2289           0 :         return 0;
    2290             :     }
    2291             :     return count;
    2292           0 : }
    2293             : 
    2294             : NS_IMPL_QUERY_INTERFACE(nsHttpTransaction,
    2295             :                         nsIInputStreamCallback,
    2296             :                         nsIOutputStreamCallback)
    2297             : 
    2298             : //-----------------------------------------------------------------------------
    2299             : // nsHttpTransaction::nsIInputStreamCallback
    2300             : //-----------------------------------------------------------------------------
    2301             : 
    2302           0 : // called on the socket thread
    2303             : NS_IMETHODIMP
    2304           0 : nsHttpTransaction::OnInputStreamReady(nsIAsyncInputStream *out)
    2305           0 : {
    2306           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2307           0 :     if (mConnection) {
    2308           0 :         mConnection->TransactionHasDataToWrite(this);
    2309           0 :         nsresult rv = mConnection->ResumeSend();
    2310             :         if (NS_FAILED(rv))
    2311           0 :             NS_ERROR("ResumeSend failed");
    2312             :     }
    2313             :     return NS_OK;
    2314             : }
    2315             : 
    2316             : //-----------------------------------------------------------------------------
    2317             : // nsHttpTransaction::nsIOutputStreamCallback
    2318             : //-----------------------------------------------------------------------------
    2319             : 
    2320           0 : // called on the socket thread
    2321             : NS_IMETHODIMP
    2322           0 : nsHttpTransaction::OnOutputStreamReady(nsIAsyncOutputStream *out)
    2323           0 : {
    2324           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2325           0 :     mWaitingOnPipeOut = false;
    2326           0 :     if (mConnection) {
    2327           0 :         mConnection->TransactionHasDataToRecv(this);
    2328           0 :         nsresult rv = mConnection->ResumeRecv();
    2329             :         if (NS_FAILED(rv))
    2330           0 :             NS_ERROR("ResumeRecv failed");
    2331             :     }
    2332             :     return NS_OK;
    2333             : }
    2334           0 : 
    2335             : void
    2336           0 : nsHttpTransaction::GetNetworkAddresses(NetAddr &self, NetAddr &peer)
    2337           1 : {
    2338           5 :     MutexAutoLock lock(mLock);
    2339           0 :     self = mSelfAddr;
    2340             :     peer = mPeerAddr;
    2341             : }
    2342           0 : 
    2343             : bool
    2344           0 : nsHttpTransaction::CanDo0RTT()
    2345           0 : {
    2346           0 :     if (mRequestHead->IsSafeMethod() &&
    2347           0 :         !mDoNotTryEarlyData &&
    2348             :         (!mConnection ||
    2349             :          !mConnection->IsProxyConnectInProgress())) {
    2350           0 :         return true;
    2351             :     }
    2352             :     return false;
    2353             : }
    2354           0 : 
    2355             : bool
    2356           0 : nsHttpTransaction::Do0RTT()
    2357           0 : {
    2358           0 :    if (mRequestHead->IsSafeMethod() &&
    2359           0 :        !mDoNotTryEarlyData &&
    2360           0 :        (!mConnection ||
    2361             :        !mConnection->IsProxyConnectInProgress())) {
    2362           0 :      m0RTTInProgress = true;
    2363             :    }
    2364             :    return m0RTTInProgress;
    2365             : }
    2366           0 : 
    2367             : nsresult
    2368           0 : nsHttpTransaction::Finish0RTT(bool aRestart, bool aAlpnChanged /* ignored */)
    2369           0 : {
    2370           0 :     LOG(("nsHttpTransaction::Finish0RTT %p %d %d\n", this, aRestart, aAlpnChanged));
    2371           0 :     MOZ_ASSERT(m0RTTInProgress);
    2372             :     m0RTTInProgress = false;
    2373             :     if (!aRestart && (mEarlyDataDisposition == EARLY_SENT)) {
    2374           0 :         // note that if this is invoked by a 3 param version of finish0rtt this
    2375             :         // disposition might be reverted
    2376           0 :         mEarlyDataDisposition = EARLY_ACCEPTED;
    2377             :     }
    2378             :     if (aRestart) {
    2379           0 :         // Reset request headers to be sent again.
    2380           0 :         nsCOMPtr<nsISeekableStream> seekable =
    2381           0 :             do_QueryInterface(mRequestStream);
    2382             :         if (seekable) {
    2383           0 :             seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
    2384             :         } else {
    2385           0 :             return NS_ERROR_FAILURE;
    2386             :         }
    2387           0 :     } else if (!mConnected) {
    2388           0 :         // this is code that was skipped in ::ReadSegments while in 0RTT
    2389             :         mConnected = true;
    2390             :         mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
    2391             :     }
    2392             :     return NS_OK;
    2393             : }
    2394           0 : 
    2395             : nsresult
    2396             : nsHttpTransaction::RestartOnFastOpenError()
    2397             : {
    2398             :     // This will happen on connection error during Fast Open or if connect
    2399           0 :     // during Fast Open takes too long. So we should not have received any
    2400           0 :     // data!
    2401             :     MOZ_ASSERT(!mReceivedData);
    2402           0 :     MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    2403             : 
    2404             :     LOG(("nsHttpTransaction::RestartOnFastOpenError - restarting transaction "
    2405             :          "%p\n", this));
    2406           0 : 
    2407           0 :     // rewind streams in case we already wrote out the request
    2408           0 :     nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
    2409             :     if (seekable)
    2410           0 :         seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
    2411             :         // clear old connection state...
    2412           0 :     mSecurityInfo = nullptr;
    2413           0 : 
    2414           0 :     if (!mConnInfo->GetRoutedHost().IsEmpty()) {
    2415           0 :         MutexAutoLock lock(*nsHttp::GetLock());
    2416           0 :         RefPtr<nsHttpConnectionInfo> ci;
    2417             :         mConnInfo->CloneAsDirectRoute(getter_AddRefs(ci));
    2418           0 :         mConnInfo = ci;
    2419           0 :     }
    2420           0 :     mEarlyDataDisposition = EARLY_NONE;
    2421           0 :     m0RTTInProgress = false;
    2422           0 :     mFastOpenStatus = TFO_FAILED;
    2423             :     mTimings = TimingStruct();
    2424             :     return NS_OK;
    2425             : }
    2426           0 : 
    2427             : void
    2428           0 : nsHttpTransaction::SetFastOpenStatus(uint8_t aStatus)
    2429             : {
    2430           0 :     LOG(("nsHttpTransaction::SetFastOpenStatus %d [this=%p]\n",
    2431           0 :          aStatus, this));
    2432             :     mFastOpenStatus = aStatus;
    2433             : }
    2434           0 : 
    2435             : void
    2436           0 : nsHttpTransaction::Refused0RTT()
    2437           0 : {
    2438           0 :     LOG(("nsHttpTransaction::Refused0RTT %p\n", this));
    2439             :     if (mEarlyDataDisposition == EARLY_ACCEPTED) {
    2440           0 :         mEarlyDataDisposition = EARLY_SENT; // undo accepted state
    2441             :     }
    2442             : }
    2443           0 : 
    2444             : void
    2445           0 : nsHttpTransaction::SetHttpTrailers(nsCString &aTrailers)
    2446           0 : {
    2447           0 :     LOG(("nsHttpTransaction::SetHttpTrailers %p", this));
    2448           0 :     LOG(("[\n    %s\n]", aTrailers.BeginReading()));
    2449             :     if (!mForTakeResponseTrailers) {
    2450             :         mForTakeResponseTrailers = new nsHttpHeaderArray();
    2451           0 :     }
    2452           0 : 
    2453           0 :     int32_t cur = 0;
    2454           0 :     int32_t len = aTrailers.Length();
    2455           0 :     while (cur < len) {
    2456           0 :         int32_t newline = aTrailers.FindCharInSet("\n", cur);
    2457             :         if (newline == -1) {
    2458             :             newline = len;
    2459           0 :         }
    2460           0 : 
    2461           0 :         int32_t end = aTrailers[newline - 1] == '\r' ? newline - 1 : newline;
    2462           0 :         nsDependentCSubstring line(aTrailers, cur, end);
    2463           0 :         nsHttpAtom hdr = {nullptr};
    2464           0 :         nsAutoCString hdrNameOriginal;
    2465           0 :         nsAutoCString val;
    2466           0 :         if (NS_SUCCEEDED(mForTakeResponseTrailers->ParseHeaderLine(line, &hdr, &hdrNameOriginal, &val))) {
    2467             :             if (hdr == nsHttp::Server_Timing) {
    2468             :                 Unused << mForTakeResponseTrailers->SetHeaderFromNet(hdr, hdrNameOriginal, val, true);
    2469             :             }
    2470           0 :         }
    2471             : 
    2472             :         cur = newline + 1;
    2473           0 :     }
    2474             : 
    2475           0 :     if (mForTakeResponseTrailers->Count() == 0) {
    2476             :         // Didn't find a Server-Timing header, so get rid of this.
    2477           0 :         mForTakeResponseTrailers = nullptr;
    2478             :     }
    2479             : }
    2480             : 
    2481             : } // namespace net
    2482             : } // namespace mozilla

Generated by: LCOV version 1.13-14-ga5dd952