tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HttpTransactionChild.cpp (22597B)


      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 "HttpTransactionChild.h"
     11 
     12 #include "mozilla/ipc/IPCStreamUtils.h"
     13 #include "mozilla/net/BackgroundDataBridgeParent.h"
     14 #include "mozilla/net/ChannelEventQueue.h"
     15 #include "mozilla/net/InputChannelThrottleQueueChild.h"
     16 #include "mozilla/net/SocketProcessChild.h"
     17 #include "mozilla/ScopeExit.h"
     18 #include "mozilla/StaticPrefs_network.h"
     19 #include "nsInputStreamPump.h"
     20 #include "nsITransportSecurityInfo.h"
     21 #include "nsHttpHandler.h"
     22 #include "nsNetUtil.h"
     23 #include "nsProxyInfo.h"
     24 #include "nsProxyRelease.h"
     25 #include "nsQueryObject.h"
     26 #include "nsSerializationHelper.h"
     27 #include "OpaqueResponseUtils.h"
     28 #include "nsIRequestContext.h"
     29 
     30 namespace mozilla::net {
     31 
     32 NS_IMPL_ISUPPORTS(HttpTransactionChild, nsIRequestObserver, nsIStreamListener,
     33                  nsITransportEventSink, nsIThrottledInputChannel,
     34                  nsIThreadRetargetableStreamListener, nsIEarlyHintObserver);
     35 
     36 //-----------------------------------------------------------------------------
     37 // HttpTransactionChild <public>
     38 //-----------------------------------------------------------------------------
     39 
     40 HttpTransactionChild::HttpTransactionChild() {
     41  LOG(("Creating HttpTransactionChild @%p\n", this));
     42 }
     43 
     44 HttpTransactionChild::~HttpTransactionChild() {
     45  LOG(("Destroying HttpTransactionChild @%p\n", this));
     46 }
     47 
     48 static already_AddRefed<nsIRequestContext> CreateRequestContext(
     49    uint64_t aRequestContextID) {
     50  if (!aRequestContextID) {
     51    return nullptr;
     52  }
     53 
     54  nsIRequestContextService* rcsvc = gHttpHandler->GetRequestContextService();
     55  if (!rcsvc) {
     56    return nullptr;
     57  }
     58 
     59  nsCOMPtr<nsIRequestContext> requestContext;
     60  rcsvc->GetRequestContext(aRequestContextID, getter_AddRefs(requestContext));
     61 
     62  return requestContext.forget();
     63 }
     64 
     65 nsresult HttpTransactionChild::InitInternal(
     66    uint32_t caps, const HttpConnectionInfoCloneArgs& infoArgs,
     67    nsHttpRequestHead* requestHead, nsIInputStream* requestBody,
     68    uint64_t requestContentLength, bool requestBodyHasHeaders,
     69    uint64_t browserId, uint8_t httpTrafficCategory, uint64_t requestContextID,
     70    ClassOfService classOfService, uint32_t initialRwin,
     71    bool responseTimeoutEnabled, uint64_t channelId,
     72    bool aHasTransactionObserver,
     73    const nsILoadInfo::IPAddressSpace& aParentIPAddressSpace,
     74    const LNAPerms& aLnaPermissionStatus) {
     75  LOG(("HttpTransactionChild::InitInternal [this=%p caps=%x]\n", this, caps));
     76 
     77  RefPtr<nsHttpConnectionInfo> cinfo =
     78      nsHttpConnectionInfo::DeserializeHttpConnectionInfoCloneArgs(infoArgs);
     79  nsCOMPtr<nsIRequestContext> rc = CreateRequestContext(requestContextID);
     80 
     81  std::function<void(TransactionObserverResult&&)> observer;
     82  if (aHasTransactionObserver) {
     83    nsMainThreadPtrHandle<HttpTransactionChild> handle(
     84        new nsMainThreadPtrHolder<HttpTransactionChild>(
     85            "HttpTransactionChildProxy", this, false));
     86    observer = [handle](TransactionObserverResult&& aResult) {
     87      handle->mTransactionObserverResult.emplace(std::move(aResult));
     88    };
     89  }
     90 
     91  nsresult rv = mTransaction->Init(
     92      caps, cinfo, requestHead, requestBody, requestContentLength,
     93      requestBodyHasHeaders, GetCurrentSerialEventTarget(),
     94      nullptr,  // TODO: security callback, fix in bug 1512479.
     95      this, browserId, static_cast<HttpTrafficCategory>(httpTrafficCategory),
     96      rc, classOfService, initialRwin, responseTimeoutEnabled, channelId,
     97      std::move(observer), aParentIPAddressSpace, aLnaPermissionStatus);
     98  if (NS_WARN_IF(NS_FAILED(rv))) {
     99    mTransaction = nullptr;
    100    return rv;
    101  }
    102 
    103  (void)mTransaction->AsyncRead(this, getter_AddRefs(mTransactionPump));
    104  return rv;
    105 }
    106 
    107 mozilla::ipc::IPCResult HttpTransactionChild::RecvCancelPump(
    108    const nsresult& aStatus) {
    109  LOG(("HttpTransactionChild::RecvCancelPump start [this=%p]\n", this));
    110  CancelInternal(aStatus);
    111  return IPC_OK();
    112 }
    113 
    114 void HttpTransactionChild::CancelInternal(nsresult aStatus) {
    115  MOZ_ASSERT(NS_FAILED(aStatus));
    116 
    117  mCanceled = true;
    118  mStatus = aStatus;
    119  if (mTransactionPump) {
    120    mTransactionPump->Cancel(mStatus);
    121  }
    122 }
    123 
    124 mozilla::ipc::IPCResult HttpTransactionChild::RecvSuspendPump() {
    125  LOG(("HttpTransactionChild::RecvSuspendPump start [this=%p]\n", this));
    126 
    127  if (mTransactionPump) {
    128    mTransactionPump->Suspend();
    129  }
    130  return IPC_OK();
    131 }
    132 
    133 mozilla::ipc::IPCResult HttpTransactionChild::RecvResumePump() {
    134  LOG(("HttpTransactionChild::RecvResumePump start [this=%p]\n", this));
    135 
    136  if (mTransactionPump) {
    137    mTransactionPump->Resume();
    138  }
    139  return IPC_OK();
    140 }
    141 
    142 mozilla::ipc::IPCResult HttpTransactionChild::RecvInit(
    143    const uint32_t& aCaps, const HttpConnectionInfoCloneArgs& aArgs,
    144    const nsHttpRequestHead& aReqHeaders, const Maybe<IPCStream>& aRequestBody,
    145    const uint64_t& aReqContentLength, const bool& aReqBodyIncludesHeaders,
    146    const uint64_t& aTopLevelOuterContentWindowId,
    147    const uint8_t& aHttpTrafficCategory, const uint64_t& aRequestContextID,
    148    const ClassOfService& aClassOfService, const uint32_t& aInitialRwin,
    149    const bool& aResponseTimeoutEnabled, const uint64_t& aChannelId,
    150    const bool& aHasTransactionObserver,
    151    const mozilla::Maybe<PInputChannelThrottleQueueChild*>& aThrottleQueue,
    152    const bool& aIsDocumentLoad,
    153    const nsILoadInfo::IPAddressSpace& aParentIPAddressSpace,
    154    const LNAPerms& aLnaPermissionStatus, const TimeStamp& aRedirectStart,
    155    const TimeStamp& aRedirectEnd) {
    156  mRequestHead = aReqHeaders;
    157  if (aRequestBody) {
    158    mUploadStream = mozilla::ipc::DeserializeIPCStream(aRequestBody);
    159  }
    160 
    161  mTransaction = new nsHttpTransaction();
    162  mChannelId = aChannelId;
    163  mIsDocumentLoad = aIsDocumentLoad;
    164  mRedirectStart = aRedirectStart;
    165  mRedirectEnd = aRedirectEnd;
    166 
    167  if (aThrottleQueue.isSome()) {
    168    mThrottleQueue =
    169        static_cast<InputChannelThrottleQueueChild*>(aThrottleQueue.ref());
    170  }
    171 
    172  nsresult rv = InitInternal(
    173      aCaps, aArgs, &mRequestHead, mUploadStream, aReqContentLength,
    174      aReqBodyIncludesHeaders, aTopLevelOuterContentWindowId,
    175      aHttpTrafficCategory, aRequestContextID, aClassOfService, aInitialRwin,
    176      aResponseTimeoutEnabled, aChannelId, aHasTransactionObserver,
    177      aParentIPAddressSpace, aLnaPermissionStatus);
    178  if (NS_FAILED(rv)) {
    179    LOG(("HttpTransactionChild::RecvInit: [this=%p] InitInternal failed!\n",
    180         this));
    181    mTransaction = nullptr;
    182    SendOnInitFailed(rv);
    183  }
    184  return IPC_OK();
    185 }
    186 
    187 mozilla::ipc::IPCResult HttpTransactionChild::RecvSetDNSWasRefreshed() {
    188  LOG(("HttpTransactionChild::SetDNSWasRefreshed [this=%p]\n", this));
    189  if (mTransaction) {
    190    mTransaction->SetDNSWasRefreshed();
    191  }
    192  return IPC_OK();
    193 }
    194 
    195 mozilla::ipc::IPCResult HttpTransactionChild::RecvDontReuseConnection() {
    196  LOG(("HttpTransactionChild::RecvDontReuseConnection [this=%p]\n", this));
    197  if (mTransaction) {
    198    mTransaction->DontReuseConnection();
    199  }
    200  return IPC_OK();
    201 }
    202 
    203 mozilla::ipc::IPCResult HttpTransactionChild::RecvSetH2WSConnRefTaken() {
    204  LOG(("HttpTransactionChild::RecvSetH2WSConnRefTaken [this=%p]\n", this));
    205  if (mTransaction) {
    206    mTransaction->SetH2WSConnRefTaken();
    207  }
    208  return IPC_OK();
    209 }
    210 
    211 void HttpTransactionChild::ActorDestroy(ActorDestroyReason aWhy) {
    212  LOG(("HttpTransactionChild::ActorDestroy [this=%p]\n", this));
    213  mTransaction = nullptr;
    214  mTransactionPump = nullptr;
    215 }
    216 
    217 nsHttpTransaction* HttpTransactionChild::GetHttpTransaction() {
    218  return mTransaction.get();
    219 }
    220 
    221 //-----------------------------------------------------------------------------
    222 // HttpTransactionChild <nsIStreamListener>
    223 //-----------------------------------------------------------------------------
    224 
    225 NS_IMETHODIMP
    226 HttpTransactionChild::OnDataAvailable(nsIRequest* aRequest,
    227                                      nsIInputStream* aInputStream,
    228                                      uint64_t aOffset, uint32_t aCount) {
    229  LOG(("HttpTransactionChild::OnDataAvailable [this=%p, aOffset= %" PRIu64
    230       " aCount=%" PRIu32 "]\n",
    231       this, aOffset, aCount));
    232 
    233  // Don't bother sending IPC if already canceled.
    234  if (mCanceled) {
    235    return mStatus;
    236  }
    237 
    238  // TODO: send string data in chunks and handle errors. Bug 1600129.
    239  nsCString data;
    240  nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
    241  if (NS_FAILED(rv)) {
    242    return rv;
    243  }
    244 
    245  mLogicalOffset += aCount;
    246 
    247  if (NS_IsMainThread()) {
    248    if (!CanSend()) {
    249      return NS_ERROR_FAILURE;
    250    }
    251 
    252    nsHttp::SendFunc<nsCString> sendFunc =
    253        [self = UnsafePtr<HttpTransactionChild>(this)](
    254            const nsCString& aData, uint64_t aOffset, uint32_t aCount) {
    255          return self->SendOnDataAvailable(aData, aOffset, aCount,
    256                                           TimeStamp::Now());
    257        };
    258 
    259    LOG(("  ODA to parent process"));
    260    if (!nsHttp::SendDataInChunks(data, aOffset, aCount, sendFunc)) {
    261      return NS_ERROR_FAILURE;
    262    }
    263    return NS_OK;
    264  }
    265 
    266  MOZ_ASSERT(mDataBridgeParent);
    267 
    268  if (!mDataBridgeParent->CanSend()) {
    269    return NS_ERROR_FAILURE;
    270  }
    271 
    272  nsHttp::SendFunc<nsDependentCSubstring> sendFunc =
    273      [self = UnsafePtr<HttpTransactionChild>(this)](
    274          const nsDependentCSubstring& aData, uint64_t aOffset,
    275          uint32_t aCount) {
    276        return self->mDataBridgeParent->SendOnTransportAndData(
    277            aOffset, aCount, aData, TimeStamp::Now());
    278      };
    279 
    280  LOG(("  ODA to content process"));
    281  if (!nsHttp::SendDataInChunks(data, aOffset, aCount, sendFunc)) {
    282    MOZ_ASSERT(false, "Send ODA to content process failed");
    283    return NS_ERROR_FAILURE;
    284  }
    285 
    286  // We still need to send ODA to parent process, because the data needs to be
    287  // saved in cache. Note that we set dataSentToChildProcess to true, so this
    288  // ODA will not be sent to child process.
    289  RefPtr<HttpTransactionChild> self = this;
    290  rv = NS_DispatchToMainThread(
    291      NS_NewRunnableFunction(
    292          "HttpTransactionChild::OnDataAvailable",
    293          [self, offset(aOffset), count(aCount), data(data)]() {
    294            nsHttp::SendFunc<nsCString> sendFunc =
    295                [self](const nsCString& aData, uint64_t aOffset,
    296                       uint32_t aCount) {
    297                  return self->SendOnDataAvailable(aData, aOffset, aCount,
    298                                                   TimeStamp::Now());
    299                };
    300 
    301            if (!nsHttp::SendDataInChunks(data, offset, count, sendFunc)) {
    302              self->CancelInternal(NS_ERROR_FAILURE);
    303            }
    304          }),
    305      NS_DISPATCH_NORMAL);
    306  MOZ_ASSERT(NS_SUCCEEDED(rv));
    307 
    308  return NS_OK;
    309 }
    310 
    311 static TimingStructArgs ToTimingStructArgs(TimingStruct aTiming) {
    312  TimingStructArgs args;
    313  args.domainLookupStart() = aTiming.domainLookupStart;
    314  args.domainLookupEnd() = aTiming.domainLookupEnd;
    315  args.connectStart() = aTiming.connectStart;
    316  args.tcpConnectEnd() = aTiming.tcpConnectEnd;
    317  args.secureConnectionStart() = aTiming.secureConnectionStart;
    318  args.connectEnd() = aTiming.connectEnd;
    319  args.requestStart() = aTiming.requestStart;
    320  args.responseStart() = aTiming.responseStart;
    321  args.responseEnd() = aTiming.responseEnd;
    322  args.transactionPending() = aTiming.transactionPending;
    323  return args;
    324 }
    325 
    326 // The maximum number of bytes to consider when attempting to sniff.
    327 // See https://mimesniff.spec.whatwg.org/#reading-the-resource-header.
    328 static const uint32_t MAX_BYTES_SNIFFED = 1445;
    329 
    330 static void GetDataForSniffer(void* aClosure, const uint8_t* aData,
    331                              uint32_t aCount) {
    332  nsTArray<uint8_t>* outData = static_cast<nsTArray<uint8_t>*>(aClosure);
    333  outData->AppendElements(aData, std::min(aCount, MAX_BYTES_SNIFFED));
    334 }
    335 
    336 bool HttpTransactionChild::CanSendODAToContentProcessDirectly(
    337    const Maybe<nsHttpResponseHead>& aHead) {
    338  if (!StaticPrefs::network_send_ODA_to_content_directly()) {
    339    return false;
    340  }
    341 
    342  // If this is a document load, the content process that receives ODA is not
    343  // decided yet, so don't bother to do the rest check.
    344  if (mIsDocumentLoad) {
    345    return false;
    346  }
    347 
    348  if (!aHead) {
    349    return false;
    350  }
    351 
    352  // We only need to deliver ODA when the response is succeed.
    353  if (aHead->Status() != 200) {
    354    return false;
    355  }
    356 
    357  // UnknownDecoder could be used in parent process, so we can't send ODA to
    358  // content process.
    359  if (!aHead->HasContentType()) {
    360    return false;
    361  }
    362 
    363  return true;
    364 }
    365 
    366 NS_IMETHODIMP
    367 HttpTransactionChild::OnStartRequest(nsIRequest* aRequest) {
    368  LOG(("HttpTransactionChild::OnStartRequest start [this=%p] mTransaction=%p\n",
    369       this, mTransaction.get()));
    370 
    371  // Don't bother sending IPC to parent process if already canceled.
    372  if (mCanceled) {
    373    return mStatus;
    374  }
    375 
    376  if (!CanSend()) {
    377    return NS_ERROR_FAILURE;
    378  }
    379 
    380  MOZ_ASSERT(mTransaction);
    381 
    382  nsresult status;
    383  aRequest->GetStatus(&status);
    384 
    385  mProtocolVersion.Truncate();
    386 
    387  nsCOMPtr<nsITransportSecurityInfo> securityInfo(mTransaction->SecurityInfo());
    388  if (securityInfo) {
    389    nsAutoCString protocol;
    390    if (NS_SUCCEEDED(securityInfo->GetNegotiatedNPN(protocol)) &&
    391        !protocol.IsEmpty()) {
    392      mProtocolVersion.Assign(protocol);
    393    }
    394  }
    395 
    396  RefPtr<nsHttpConnectionInfo> connInfo;
    397  UniquePtr<nsHttpResponseHead> head(
    398      mTransaction->TakeResponseHeadAndConnInfo(getter_AddRefs(connInfo)));
    399  Maybe<nsHttpResponseHead> optionalHead;
    400  nsTArray<uint8_t> dataForSniffer;
    401  if (head) {
    402    if (mProtocolVersion.IsEmpty()) {
    403      HttpVersion version = head->Version();
    404      mProtocolVersion.Assign(nsHttp::GetProtocolVersion(version));
    405    }
    406    optionalHead = Some(*head);
    407 
    408    if (GetOpaqueResponseBlockedReason(*head) ==
    409        OpaqueResponseBlockedReason::BLOCKED_SHOULD_SNIFF) {
    410      RefPtr<nsInputStreamPump> pump = do_QueryObject(mTransactionPump);
    411      pump->PeekStream(GetDataForSniffer, &dataForSniffer);
    412    }
    413  }
    414 
    415  Maybe<nsCString> optionalAltSvcUsed;
    416  nsCString altSvcUsed;
    417  if (NS_SUCCEEDED(mTransaction->RequestHead()->GetHeader(
    418          nsHttp::Alternate_Service_Used, altSvcUsed)) &&
    419      !altSvcUsed.IsEmpty()) {
    420    optionalAltSvcUsed.emplace(altSvcUsed);
    421  }
    422 
    423  if (CanSendODAToContentProcessDirectly(optionalHead)) {
    424    Maybe<RefPtr<BackgroundDataBridgeParent>> dataBridgeParent =
    425        SocketProcessChild::GetSingleton()->GetAndRemoveDataBridge(mChannelId);
    426    // Check if there is a registered BackgroundDataBridgeParent.
    427    if (dataBridgeParent) {
    428      mDataBridgeParent = std::move(dataBridgeParent.ref());
    429 
    430      nsCOMPtr<nsISerialEventTarget> backgroundThread =
    431          mDataBridgeParent->GetBackgroundThread();
    432      nsCOMPtr<nsIThreadRetargetableRequest> retargetableTransactionPump;
    433      retargetableTransactionPump = do_QueryObject(mTransactionPump);
    434      // nsInputStreamPump should implement this interface.
    435      MOZ_ASSERT(retargetableTransactionPump);
    436 
    437      nsresult rv =
    438          retargetableTransactionPump->RetargetDeliveryTo(backgroundThread);
    439      LOG((" Retarget to background thread [this=%p rv=%08x]\n", this,
    440           static_cast<uint32_t>(rv)));
    441      if (NS_FAILED(rv)) {
    442        mDataBridgeParent->Destroy();
    443        mDataBridgeParent = nullptr;
    444      }
    445    }
    446  }
    447 
    448  int32_t proxyConnectResponseCode =
    449      mTransaction->GetProxyConnectResponseCode();
    450 
    451  nsIRequest::TRRMode mode = nsIRequest::TRR_DEFAULT_MODE;
    452  TRRSkippedReason reason = nsITRRSkipReason::TRR_UNSET;
    453  {
    454    NetAddr selfAddr;
    455    NetAddr peerAddr;
    456    bool isTrr = false;
    457    bool echConfigUsed = false;
    458    if (mTransaction) {
    459      mTransaction->GetNetworkAddresses(selfAddr, peerAddr, isTrr, mode, reason,
    460                                        echConfigUsed);
    461    }
    462  }
    463 
    464  HttpConnectionInfoCloneArgs infoArgs;
    465  nsHttpConnectionInfo::SerializeHttpConnectionInfo(connInfo, infoArgs);
    466 
    467  (void)SendOnStartRequest(
    468      status, std::move(optionalHead), securityInfo,
    469      mTransaction->ProxyConnectFailed(),
    470      ToTimingStructArgs(mTransaction->Timings()), proxyConnectResponseCode,
    471      dataForSniffer, optionalAltSvcUsed, !!mDataBridgeParent,
    472      mTransaction->TakeRestartedState(), mTransaction->HTTPSSVCReceivedStage(),
    473      mTransaction->GetSupportsHTTP3(), mode, reason, mTransaction->Caps(),
    474      TimeStamp::Now(), infoArgs, mTransaction->GetTargetIPAddressSpace());
    475  return NS_OK;
    476 }
    477 
    478 ResourceTimingStructArgs HttpTransactionChild::GetTimingAttributes() {
    479  // Note that not all fields in ResourceTimingStructArgs are filled, since
    480  // we only need some in HttpChannelChild::OnStopRequest.
    481  ResourceTimingStructArgs args;
    482  args.domainLookupStart() = mTransaction->GetDomainLookupStart();
    483  args.domainLookupEnd() = mTransaction->GetDomainLookupEnd();
    484  args.connectStart() = mTransaction->GetConnectStart();
    485  args.tcpConnectEnd() = mTransaction->GetTcpConnectEnd();
    486  args.secureConnectionStart() = mTransaction->GetSecureConnectionStart();
    487  args.connectEnd() = mTransaction->GetConnectEnd();
    488  args.requestStart() = mTransaction->GetRequestStart();
    489  args.responseStart() = mTransaction->GetResponseStart();
    490  args.responseEnd() = mTransaction->GetResponseEnd();
    491  args.transferSize() = mTransaction->GetTransferSize();
    492  args.encodedBodySize() = mLogicalOffset;
    493  args.decodedBodySize() = 0;
    494  args.redirectStart() = mRedirectStart;
    495  args.redirectEnd() = mRedirectEnd;
    496  args.transferSize() = mTransaction->GetTransferSize();
    497  args.transactionPending() = mTransaction->GetPendingTime();
    498  return args;
    499 }
    500 
    501 NS_IMETHODIMP
    502 HttpTransactionChild::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
    503  LOG(("HttpTransactionChild::OnStopRequest [this=%p]\n", this));
    504 
    505  mTransactionPump = nullptr;
    506 
    507  auto onStopGuard = MakeScopeExit([&] {
    508    LOG(("  calling mDataBridgeParent->OnStopRequest by ScopeExit [this=%p]\n",
    509         this));
    510    MOZ_ASSERT(NS_FAILED(mStatus), "This shoule be only called when failure");
    511    if (mDataBridgeParent) {
    512      mDataBridgeParent->OnStopRequest(mStatus, ResourceTimingStructArgs(),
    513                                       TimeStamp(), nsHttpHeaderArray(),
    514                                       TimeStamp::Now());
    515      mDataBridgeParent = nullptr;
    516    }
    517  });
    518 
    519  // Don't bother sending IPC to parent process if already canceled.
    520  if (mCanceled) {
    521    return mStatus;
    522  }
    523 
    524  if (!CanSend()) {
    525    mStatus = NS_ERROR_UNEXPECTED;
    526    return mStatus;
    527  }
    528 
    529  MOZ_ASSERT(mTransaction);
    530 
    531  UniquePtr<nsHttpHeaderArray> headerArray(
    532      mTransaction->TakeResponseTrailers());
    533  Maybe<nsHttpHeaderArray> responseTrailers;
    534  if (headerArray) {
    535    responseTrailers.emplace(*headerArray);
    536  }
    537 
    538  onStopGuard.release();
    539 
    540  TimeStamp lastActTabOpt = nsHttp::GetLastActiveTabLoadOptimizationHit();
    541 
    542  if (mDataBridgeParent) {
    543    mDataBridgeParent->OnStopRequest(
    544        aStatus, GetTimingAttributes(), lastActTabOpt,
    545        responseTrailers ? *responseTrailers : nsHttpHeaderArray(),
    546        TimeStamp::Now());
    547    mDataBridgeParent = nullptr;
    548  }
    549 
    550  (void)SendOnStopRequest(aStatus, mTransaction->ResponseIsComplete(),
    551                          mTransaction->GetTransferSize(),
    552                          ToTimingStructArgs(mTransaction->Timings()),
    553                          responseTrailers, mTransactionObserverResult,
    554                          lastActTabOpt, TimeStamp::Now());
    555 
    556  return NS_OK;
    557 }
    558 
    559 //-----------------------------------------------------------------------------
    560 // HttpTransactionChild <nsITransportEventSink>
    561 //-----------------------------------------------------------------------------
    562 
    563 NS_IMETHODIMP
    564 HttpTransactionChild::OnTransportStatus(nsITransport* aTransport,
    565                                        nsresult aStatus, int64_t aProgress,
    566                                        int64_t aProgressMax) {
    567  LOG(("HttpTransactionChild::OnTransportStatus [this=%p status=%" PRIx32
    568       " progress=%" PRId64 "]\n",
    569       this, static_cast<uint32_t>(aStatus), aProgress));
    570 
    571  if (!CanSend()) {
    572    return NS_OK;
    573  }
    574 
    575  Maybe<NetworkAddressArg> arg;
    576  if (aStatus == NS_NET_STATUS_CONNECTED_TO ||
    577      aStatus == NS_NET_STATUS_WAITING_FOR) {
    578    NetAddr selfAddr;
    579    NetAddr peerAddr;
    580    bool isTrr = false;
    581    bool echConfigUsed = false;
    582    nsIRequest::TRRMode mode = nsIRequest::TRR_DEFAULT_MODE;
    583    TRRSkippedReason reason = nsITRRSkipReason::TRR_UNSET;
    584    if (mTransaction) {
    585      mTransaction->GetNetworkAddresses(selfAddr, peerAddr, isTrr, mode, reason,
    586                                        echConfigUsed);
    587    } else {
    588      nsCOMPtr<nsISocketTransport> socketTransport =
    589          do_QueryInterface(aTransport);
    590      if (socketTransport) {
    591        socketTransport->GetSelfAddr(&selfAddr);
    592        socketTransport->GetPeerAddr(&peerAddr);
    593        socketTransport->ResolvedByTRR(&isTrr);
    594        socketTransport->GetEffectiveTRRMode(&mode);
    595        socketTransport->GetTrrSkipReason(&reason);
    596        socketTransport->GetEchConfigUsed(&echConfigUsed);
    597      }
    598    }
    599    arg.emplace(selfAddr, peerAddr, isTrr, mode, reason, echConfigUsed);
    600  }
    601 
    602  (void)SendOnTransportStatus(aStatus, aProgress, aProgressMax, arg);
    603  return NS_OK;
    604 }
    605 
    606 //-----------------------------------------------------------------------------
    607 // HttpBaseChannel::nsIThrottledInputChannel
    608 //-----------------------------------------------------------------------------
    609 
    610 NS_IMETHODIMP
    611 HttpTransactionChild::SetThrottleQueue(nsIInputChannelThrottleQueue* aQueue) {
    612  return NS_ERROR_NOT_IMPLEMENTED;
    613 }
    614 
    615 NS_IMETHODIMP
    616 HttpTransactionChild::GetThrottleQueue(nsIInputChannelThrottleQueue** aQueue) {
    617  nsCOMPtr<nsIInputChannelThrottleQueue> queue =
    618      static_cast<nsIInputChannelThrottleQueue*>(mThrottleQueue.get());
    619  queue.forget(aQueue);
    620  return NS_OK;
    621 }
    622 
    623 //-----------------------------------------------------------------------------
    624 // EventSourceImpl::nsIThreadRetargetableStreamListener
    625 //-----------------------------------------------------------------------------
    626 NS_IMETHODIMP
    627 HttpTransactionChild::CheckListenerChain() {
    628  MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
    629  return NS_OK;
    630 }
    631 
    632 NS_IMETHODIMP
    633 HttpTransactionChild::OnDataFinished(nsresult aStatus) { return NS_OK; }
    634 
    635 NS_IMETHODIMP
    636 HttpTransactionChild::EarlyHint(const nsACString& aValue,
    637                                const nsACString& aReferrerPolicy,
    638                                const nsACString& aCSPHeader) {
    639  LOG(("HttpTransactionChild::EarlyHint"));
    640  if (CanSend()) {
    641    (void)SendEarlyHint(aValue, aReferrerPolicy, aCSPHeader);
    642  }
    643  return NS_OK;
    644 }
    645 
    646 }  // namespace mozilla::net