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

Generated by: LCOV version 1.13