tor-browser

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

HttpTransactionParent.cpp (30924B)


      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 "HttpTransactionParent.h"
     11 
     12 #include "HttpTrafficAnalyzer.h"
     13 #include "mozilla/ipc/IPCStreamUtils.h"
     14 #include "mozilla/net/ChannelEventQueue.h"
     15 #include "mozilla/net/InputChannelThrottleQueueParent.h"
     16 #include "mozilla/net/SocketProcessParent.h"
     17 #include "nsHttpHandler.h"
     18 #include "nsIThreadRetargetableStreamListener.h"
     19 #include "nsITransportSecurityInfo.h"
     20 #include "nsNetUtil.h"
     21 #include "nsQueryObject.h"
     22 #include "nsSerializationHelper.h"
     23 #include "nsStreamUtils.h"
     24 #include "nsStringStream.h"
     25 #include "nsIRequestContext.h"
     26 
     27 namespace mozilla::net {
     28 
     29 NS_IMPL_ADDREF(HttpTransactionParent)
     30 NS_INTERFACE_MAP_BEGIN(HttpTransactionParent)
     31  NS_INTERFACE_MAP_ENTRY(nsIRequest)
     32  NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
     33  NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpTransactionParent)
     34  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequest)
     35 NS_INTERFACE_MAP_END
     36 
     37 NS_IMETHODIMP_(MozExternalRefCountType) HttpTransactionParent::Release(void) {
     38  MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
     39  nsrefcnt count = --mRefCnt;
     40  NS_LOG_RELEASE(this, count, "HttpTransactionParent");
     41  if (count == 0) {
     42    mRefCnt = 1; /* stabilize */
     43    delete (this);
     44    return 0;
     45  }
     46 
     47  // When ref count goes down to 1 (held internally by IPDL), it means that
     48  // we are done with this transaction. We should send a delete message
     49  // to delete the transaction child in socket process.
     50  if (count == 1 && CanSend()) {
     51    if (!NS_IsMainThread()) {
     52      RefPtr<HttpTransactionParent> self = this;
     53      MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
     54          NS_NewRunnableFunction("HttpTransactionParent::Release", [self]() {
     55            (void)self->Send__delete__(self);
     56            // Make sure we can not send IPC after Send__delete__().
     57            MOZ_ASSERT(!self->CanSend());
     58          })));
     59    } else {
     60      (void)Send__delete__(this);
     61    }
     62    return 1;
     63  }
     64  return count;
     65 }
     66 
     67 //-----------------------------------------------------------------------------
     68 // HttpTransactionParent <public>
     69 //-----------------------------------------------------------------------------
     70 
     71 HttpTransactionParent::HttpTransactionParent(bool aIsDocumentLoad)
     72    : mIsDocumentLoad(aIsDocumentLoad) {
     73  LOG(("Creating HttpTransactionParent @%p\n", this));
     74  mEventQ = new ChannelEventQueue(static_cast<nsIRequest*>(this));
     75 }
     76 
     77 HttpTransactionParent::~HttpTransactionParent() {
     78  LOG(("Destroying HttpTransactionParent @%p\n", this));
     79  mEventQ->NotifyReleasingOwner();
     80 }
     81 
     82 //-----------------------------------------------------------------------------
     83 // HttpTransactionParent <nsAHttpTransactionShell>
     84 //-----------------------------------------------------------------------------
     85 
     86 // Let socket process init the *real* nsHttpTransaction.
     87 nsresult HttpTransactionParent::Init(
     88    uint32_t caps, nsHttpConnectionInfo* cinfo, nsHttpRequestHead* requestHead,
     89    nsIInputStream* requestBody, uint64_t requestContentLength,
     90    bool requestBodyHasHeaders, nsIEventTarget* target,
     91    nsIInterfaceRequestor* callbacks, nsITransportEventSink* eventsink,
     92    uint64_t browserId, HttpTrafficCategory trafficCategory,
     93    nsIRequestContext* requestContext, ClassOfService classOfService,
     94    uint32_t initialRwin, bool responseTimeoutEnabled, uint64_t channelId,
     95    TransactionObserverFunc&& transactionObserver,
     96    nsILoadInfo::IPAddressSpace aParentIpAddressSpace,
     97    const LNAPerms& aLnaPermissionStatus) {
     98  LOG(("HttpTransactionParent::Init [this=%p caps=%x]\n", this, caps));
     99 
    100  if (!CanSend()) {
    101    return NS_ERROR_FAILURE;
    102  }
    103 
    104  mEventsink = eventsink;
    105  mTargetThread = GetCurrentSerialEventTarget();
    106  mChannelId = channelId;
    107  mTransactionObserver = std::move(transactionObserver);
    108  mCaps = caps;
    109  mConnInfo = cinfo->Clone();
    110  mIsHttp3Used = cinfo->IsHttp3();
    111 
    112  HttpConnectionInfoCloneArgs infoArgs;
    113  nsHttpConnectionInfo::SerializeHttpConnectionInfo(cinfo, infoArgs);
    114 
    115  Maybe<mozilla::ipc::IPCStream> ipcStream;
    116  if (!mozilla::ipc::SerializeIPCStream(do_AddRef(requestBody), ipcStream,
    117                                        /* aAllowLazy */ false)) {
    118    return NS_ERROR_FAILURE;
    119  }
    120 
    121  uint64_t requestContextID = requestContext ? requestContext->GetID() : 0;
    122 
    123  nsCOMPtr<nsIThrottledInputChannel> throttled = do_QueryInterface(mEventsink);
    124  Maybe<NotNull<PInputChannelThrottleQueueParent*>> throttleQueue;
    125  if (throttled) {
    126    nsCOMPtr<nsIInputChannelThrottleQueue> queue;
    127    nsresult rv = throttled->GetThrottleQueue(getter_AddRefs(queue));
    128    // In case of failure, just carry on without throttling.
    129    if (NS_SUCCEEDED(rv) && queue) {
    130      LOG1(("HttpTransactionParent::Init %p using throttle queue %p\n", this,
    131            queue.get()));
    132      RefPtr<InputChannelThrottleQueueParent> tqParent = do_QueryObject(queue);
    133      MOZ_ASSERT(tqParent);
    134      throttleQueue.emplace(WrapNotNull(tqParent.get()));
    135    }
    136  }
    137 
    138  // TODO: Figure out if we have to implement nsIThreadRetargetableRequest in
    139  // bug 1544378.
    140  if (!SendInit(caps, infoArgs, *requestHead, ipcStream, requestContentLength,
    141                requestBodyHasHeaders, browserId,
    142                static_cast<uint8_t>(trafficCategory), requestContextID,
    143                classOfService, initialRwin, responseTimeoutEnabled, mChannelId,
    144                !!mTransactionObserver, throttleQueue, mIsDocumentLoad,
    145                aParentIpAddressSpace, aLnaPermissionStatus, mRedirectStart,
    146                mRedirectEnd)) {
    147    return NS_ERROR_FAILURE;
    148  }
    149 
    150  nsCString reqHeaderBuf = nsHttp::ConvertRequestHeadToString(
    151      *requestHead, !!requestBody, requestBodyHasHeaders,
    152      cinfo->UsingConnect());
    153  requestContentLength += reqHeaderBuf.Length();
    154 
    155  mRequestSize = InScriptableRange(requestContentLength)
    156                     ? static_cast<int64_t>(requestContentLength)
    157                     : -1;
    158 
    159  return NS_OK;
    160 }
    161 
    162 nsresult HttpTransactionParent::AsyncRead(nsIStreamListener* listener,
    163                                          nsIRequest** pump) {
    164  MOZ_ASSERT(pump);
    165 
    166  *pump = do_AddRef(this).take();
    167  mChannel = listener;
    168  return NS_OK;
    169 }
    170 
    171 UniquePtr<nsHttpResponseHead>
    172 HttpTransactionParent::TakeResponseHeadAndConnInfo(
    173    nsHttpConnectionInfo** aOut) {
    174  MOZ_ASSERT(NS_IsMainThread());
    175  MOZ_ASSERT(!mResponseHeadTaken, "TakeResponseHead called 2x");
    176 
    177  if (aOut) {
    178    RefPtr<nsHttpConnectionInfo> connInfo = mConnInfo;
    179    connInfo.forget(aOut);
    180  }
    181 
    182  mResponseHeadTaken = true;
    183  return std::move(mResponseHead);
    184 }
    185 
    186 UniquePtr<nsHttpHeaderArray> HttpTransactionParent::TakeResponseTrailers() {
    187  MOZ_ASSERT(NS_IsMainThread());
    188  MOZ_ASSERT(!mResponseTrailersTaken, "TakeResponseTrailers called 2x");
    189 
    190  mResponseTrailersTaken = true;
    191  return std::move(mResponseTrailers);
    192 }
    193 
    194 void HttpTransactionParent::SetSniffedTypeToChannel(
    195    nsInputStreamPump::PeekSegmentFun aCallTypeSniffers, nsIChannel* aChannel) {
    196  if (!mDataForSniffer.IsEmpty()) {
    197    aCallTypeSniffers(aChannel, mDataForSniffer.Elements(),
    198                      mDataForSniffer.Length());
    199  }
    200 }
    201 
    202 NS_IMETHODIMP
    203 HttpTransactionParent::GetDeliveryTarget(nsISerialEventTarget** aEventTarget) {
    204  MutexAutoLock lock(mEventTargetMutex);
    205 
    206  nsCOMPtr<nsISerialEventTarget> target = mODATarget;
    207  if (!mODATarget) {
    208    target = mTargetThread;
    209  }
    210  target.forget(aEventTarget);
    211  return NS_OK;
    212 }
    213 
    214 already_AddRefed<nsISerialEventTarget> HttpTransactionParent::GetODATarget() {
    215  nsCOMPtr<nsISerialEventTarget> target;
    216  {
    217    MutexAutoLock lock(mEventTargetMutex);
    218    target = mODATarget ? mODATarget : mTargetThread;
    219  }
    220 
    221  if (!target) {
    222    target = GetMainThreadSerialEventTarget();
    223  }
    224  return target.forget();
    225 }
    226 
    227 NS_IMETHODIMP HttpTransactionParent::RetargetDeliveryTo(
    228    nsISerialEventTarget* aEventTarget) {
    229  LOG(("HttpTransactionParent::RetargetDeliveryTo [this=%p, aTarget=%p]", this,
    230       aEventTarget));
    231 
    232  MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only");
    233  MOZ_ASSERT(!mODATarget);
    234  NS_ENSURE_ARG(aEventTarget);
    235 
    236  if (aEventTarget->IsOnCurrentThread()) {
    237    NS_WARNING("Retargeting delivery to same thread");
    238    return NS_OK;
    239  }
    240 
    241  nsresult rv = NS_OK;
    242  nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
    243      do_QueryInterface(mChannel, &rv);
    244  if (!retargetableListener || NS_FAILED(rv)) {
    245    NS_WARNING("Listener is not retargetable");
    246    return NS_ERROR_NO_INTERFACE;
    247  }
    248 
    249  rv = retargetableListener->CheckListenerChain();
    250  if (NS_FAILED(rv)) {
    251    NS_WARNING("Subsequent listeners are not retargetable");
    252    return rv;
    253  }
    254 
    255  {
    256    MutexAutoLock lock(mEventTargetMutex);
    257    mODATarget = aEventTarget;
    258  }
    259 
    260  return NS_OK;
    261 }
    262 
    263 void HttpTransactionParent::SetDNSWasRefreshed() {
    264  MOZ_ASSERT(NS_IsMainThread(), "SetDNSWasRefreshed on main thread only!");
    265  (void)SendSetDNSWasRefreshed();
    266 }
    267 
    268 void HttpTransactionParent::GetNetworkAddresses(
    269    NetAddr& self, NetAddr& peer, bool& aResolvedByTRR,
    270    nsIRequest::TRRMode& aEffectiveTRRMode, TRRSkippedReason& aSkipReason,
    271    bool& aEchConfigUsed) {
    272  self = mSelfAddr;
    273  peer = mPeerAddr;
    274  aResolvedByTRR = mResolvedByTRR;
    275  aEffectiveTRRMode = mEffectiveTRRMode;
    276  aSkipReason = mTRRSkipReason;
    277  aEchConfigUsed = mEchConfigUsed;
    278 }
    279 
    280 nsILoadInfo::IPAddressSpace HttpTransactionParent::GetTargetIPAddressSpace() {
    281  return mTargetIPAddressSpace;
    282 }
    283 
    284 bool HttpTransactionParent::HasStickyConnection() const {
    285  return mCaps & NS_HTTP_STICKY_CONNECTION;
    286 }
    287 
    288 mozilla::TimeStamp HttpTransactionParent::GetDomainLookupStart() {
    289  return mTimings.domainLookupStart;
    290 }
    291 
    292 mozilla::TimeStamp HttpTransactionParent::GetDomainLookupEnd() {
    293  return mTimings.domainLookupEnd;
    294 }
    295 
    296 mozilla::TimeStamp HttpTransactionParent::GetConnectStart() {
    297  return mTimings.connectStart;
    298 }
    299 
    300 mozilla::TimeStamp HttpTransactionParent::GetTcpConnectEnd() {
    301  return mTimings.tcpConnectEnd;
    302 }
    303 
    304 mozilla::TimeStamp HttpTransactionParent::GetSecureConnectionStart() {
    305  return mTimings.secureConnectionStart;
    306 }
    307 
    308 mozilla::TimeStamp HttpTransactionParent::GetConnectEnd() {
    309  return mTimings.connectEnd;
    310 }
    311 
    312 mozilla::TimeStamp HttpTransactionParent::GetRequestStart() {
    313  return mTimings.requestStart;
    314 }
    315 
    316 mozilla::TimeStamp HttpTransactionParent::GetResponseStart() {
    317  return mTimings.responseStart;
    318 }
    319 
    320 mozilla::TimeStamp HttpTransactionParent::GetResponseEnd() {
    321  return mTimings.responseEnd;
    322 }
    323 
    324 TimingStruct HttpTransactionParent::Timings() { return mTimings; }
    325 
    326 bool HttpTransactionParent::ResponseIsComplete() { return mResponseIsComplete; }
    327 
    328 int64_t HttpTransactionParent::GetTransferSize() { return mTransferSize; }
    329 
    330 int64_t HttpTransactionParent::GetRequestSize() { return mRequestSize; }
    331 
    332 bool HttpTransactionParent::IsHttp3Used() { return mIsHttp3Used; }
    333 
    334 bool HttpTransactionParent::DataSentToChildProcess() {
    335  return mDataSentToChildProcess;
    336 }
    337 
    338 already_AddRefed<nsITransportSecurityInfo>
    339 HttpTransactionParent::SecurityInfo() {
    340  return do_AddRef(mSecurityInfo);
    341 }
    342 
    343 bool HttpTransactionParent::ProxyConnectFailed() { return mProxyConnectFailed; }
    344 
    345 bool HttpTransactionParent::TakeRestartedState() {
    346  bool result = mRestarted;
    347  mRestarted = false;
    348  return result;
    349 }
    350 
    351 uint32_t HttpTransactionParent::HTTPSSVCReceivedStage() {
    352  return mHTTPSSVCReceivedStage;
    353 }
    354 
    355 void HttpTransactionParent::DontReuseConnection() {
    356  MOZ_ASSERT(NS_IsMainThread());
    357  (void)SendDontReuseConnection();
    358 }
    359 
    360 void HttpTransactionParent::SetH2WSConnRefTaken() {
    361  MOZ_ASSERT(NS_IsMainThread());
    362  (void)SendSetH2WSConnRefTaken();
    363 }
    364 
    365 void HttpTransactionParent::SetSecurityCallbacks(
    366    nsIInterfaceRequestor* aCallbacks) {
    367  // TODO: we might don't need to implement this.
    368  // Will figure out in bug 1512479.
    369 }
    370 
    371 void HttpTransactionParent::SetDomainLookupStart(mozilla::TimeStamp timeStamp,
    372                                                 bool onlyIfNull) {
    373  mDomainLookupStart = timeStamp;
    374  mTimings.domainLookupStart = mDomainLookupStart;
    375 }
    376 void HttpTransactionParent::SetDomainLookupEnd(mozilla::TimeStamp timeStamp,
    377                                               bool onlyIfNull) {
    378  mDomainLookupEnd = timeStamp;
    379  mTimings.domainLookupEnd = mDomainLookupEnd;
    380 }
    381 
    382 nsHttpTransaction* HttpTransactionParent::AsHttpTransaction() {
    383  return nullptr;
    384 }
    385 
    386 HttpTransactionParent* HttpTransactionParent::AsHttpTransactionParent() {
    387  return this;
    388 }
    389 
    390 int32_t HttpTransactionParent::GetProxyConnectResponseCode() {
    391  return mProxyConnectResponseCode;
    392 }
    393 
    394 bool HttpTransactionParent::Http2Disabled() const {
    395  return mCaps & NS_HTTP_DISALLOW_SPDY;
    396 }
    397 
    398 bool HttpTransactionParent::Http3Disabled() const {
    399  return mCaps & NS_HTTP_DISALLOW_HTTP3;
    400 }
    401 
    402 already_AddRefed<nsHttpConnectionInfo> HttpTransactionParent::GetConnInfo()
    403    const {
    404  RefPtr<nsHttpConnectionInfo> connInfo = mConnInfo->Clone();
    405  return connInfo.forget();
    406 }
    407 
    408 already_AddRefed<nsIEventTarget> HttpTransactionParent::GetNeckoTarget() {
    409  nsCOMPtr<nsIEventTarget> target = GetMainThreadSerialEventTarget();
    410  return target.forget();
    411 }
    412 
    413 mozilla::ipc::IPCResult HttpTransactionParent::RecvOnStartRequest(
    414    const nsresult& aStatus, Maybe<nsHttpResponseHead>&& aResponseHead,
    415    nsITransportSecurityInfo* aSecurityInfo, const bool& aProxyConnectFailed,
    416    const TimingStructArgs& aTimings, const int32_t& aProxyConnectResponseCode,
    417    nsTArray<uint8_t>&& aDataForSniffer, const Maybe<nsCString>& aAltSvcUsed,
    418    const bool& aDataToChildProcess, const bool& aRestarted,
    419    const uint32_t& aHTTPSSVCReceivedStage, const bool& aSupportsHttp3,
    420    const nsIRequest::TRRMode& aMode, const TRRSkippedReason& aTrrSkipReason,
    421    const uint32_t& aCaps, const TimeStamp& aOnStartRequestStartTime,
    422    const HttpConnectionInfoCloneArgs& aArgs,
    423    const nsILoadInfo::IPAddressSpace& aTargetIPAddressSpace) {
    424  RefPtr<nsHttpConnectionInfo> cinfo =
    425      nsHttpConnectionInfo::DeserializeHttpConnectionInfoCloneArgs(aArgs);
    426  mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
    427      this,
    428      [self = UnsafePtr<HttpTransactionParent>(this), aStatus,
    429       aResponseHead = std::move(aResponseHead),
    430       securityInfo = nsCOMPtr{aSecurityInfo}, aProxyConnectFailed, aTimings,
    431       aProxyConnectResponseCode,
    432       aDataForSniffer = CopyableTArray{std::move(aDataForSniffer)},
    433       aAltSvcUsed, aDataToChildProcess, aRestarted, aHTTPSSVCReceivedStage,
    434       aSupportsHttp3, aMode, aTrrSkipReason, aCaps, aOnStartRequestStartTime,
    435       aTargetIPAddressSpace, cinfo{std::move(cinfo)}]() mutable {
    436        self->DoOnStartRequest(
    437            aStatus, std::move(aResponseHead), securityInfo,
    438            aProxyConnectFailed, aTimings, aProxyConnectResponseCode,
    439            std::move(aDataForSniffer), aAltSvcUsed, aDataToChildProcess,
    440            aRestarted, aHTTPSSVCReceivedStage, aSupportsHttp3, aMode,
    441            aTrrSkipReason, aCaps, aOnStartRequestStartTime, cinfo,
    442            aTargetIPAddressSpace);
    443      }));
    444  return IPC_OK();
    445 }
    446 
    447 static void TimingStructArgsToTimingsStruct(const TimingStructArgs& aArgs,
    448                                            TimingStruct& aTimings) {
    449  // If domainLookupStart/End was set by the channel before, we use these
    450  // timestamps instead the ones from the transaction.
    451  if (aTimings.domainLookupStart.IsNull() &&
    452      aTimings.domainLookupEnd.IsNull()) {
    453    aTimings.domainLookupStart = aArgs.domainLookupStart();
    454    aTimings.domainLookupEnd = aArgs.domainLookupEnd();
    455  }
    456  aTimings.connectStart = aArgs.connectStart();
    457  aTimings.tcpConnectEnd = aArgs.tcpConnectEnd();
    458  aTimings.secureConnectionStart = aArgs.secureConnectionStart();
    459  aTimings.connectEnd = aArgs.connectEnd();
    460  aTimings.requestStart = aArgs.requestStart();
    461  aTimings.responseStart = aArgs.responseStart();
    462  aTimings.responseEnd = aArgs.responseEnd();
    463  aTimings.transactionPending = aArgs.transactionPending();
    464 }
    465 
    466 void HttpTransactionParent::DoOnStartRequest(
    467    const nsresult& aStatus, Maybe<nsHttpResponseHead>&& aResponseHead,
    468    nsITransportSecurityInfo* aSecurityInfo, const bool& aProxyConnectFailed,
    469    const TimingStructArgs& aTimings, const int32_t& aProxyConnectResponseCode,
    470    nsTArray<uint8_t>&& aDataForSniffer, const Maybe<nsCString>& aAltSvcUsed,
    471    const bool& aDataToChildProcess, const bool& aRestarted,
    472    const uint32_t& aHTTPSSVCReceivedStage, const bool& aSupportsHttp3,
    473    const nsIRequest::TRRMode& aMode, const TRRSkippedReason& aSkipReason,
    474    const uint32_t& aCaps, const TimeStamp& aOnStartRequestStartTime,
    475    nsHttpConnectionInfo* aConnInfo,
    476    const nsILoadInfo::IPAddressSpace& aTargetIPAddressSpace) {
    477  LOG(("HttpTransactionParent::DoOnStartRequest [this=%p aStatus=%" PRIx32
    478       "]\n",
    479       this, static_cast<uint32_t>(aStatus)));
    480 
    481  if (mCanceled) {
    482    return;
    483  }
    484 
    485  MOZ_ASSERT(!mOnStartRequestCalled);
    486 
    487  mStatus = aStatus;
    488  mDataSentToChildProcess = aDataToChildProcess;
    489  mHTTPSSVCReceivedStage = aHTTPSSVCReceivedStage;
    490  mSupportsHTTP3 = aSupportsHttp3;
    491  mEffectiveTRRMode = aMode;
    492  mTRRSkipReason = aSkipReason;
    493  mCaps = aCaps;
    494  mSecurityInfo = aSecurityInfo;
    495  mOnStartRequestStartTime = aOnStartRequestStartTime;
    496  mConnInfo = aConnInfo;
    497  mTargetIPAddressSpace = aTargetIPAddressSpace;
    498 
    499  if (aResponseHead.isSome()) {
    500    mResponseHead =
    501        MakeUnique<nsHttpResponseHead>(std::move(aResponseHead.ref()));
    502  }
    503  mProxyConnectFailed = aProxyConnectFailed;
    504  TimingStructArgsToTimingsStruct(aTimings, mTimings);
    505 
    506  mProxyConnectResponseCode = aProxyConnectResponseCode;
    507  mDataForSniffer = std::move(aDataForSniffer);
    508  mRestarted = aRestarted;
    509 
    510  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
    511  MOZ_ASSERT(httpChannel, "mChannel is expected to implement nsIHttpChannel");
    512  if (httpChannel) {
    513    if (aAltSvcUsed.isSome()) {
    514      (void)httpChannel->SetRequestHeader(nsHttp::Alternate_Service_Used.val(),
    515                                          aAltSvcUsed.ref(), false);
    516    }
    517  }
    518 
    519  AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    520  nsresult rv = mChannel->OnStartRequest(this);
    521  mOnStartRequestCalled = true;
    522  if (NS_FAILED(rv)) {
    523    Cancel(rv);
    524  }
    525 }
    526 
    527 mozilla::ipc::IPCResult HttpTransactionParent::RecvOnTransportStatus(
    528    const nsresult& aStatus, const int64_t& aProgress,
    529    const int64_t& aProgressMax,
    530    Maybe<NetworkAddressArg>&& aNetworkAddressArg) {
    531  if (aNetworkAddressArg) {
    532    mSelfAddr = aNetworkAddressArg->selfAddr();
    533    mPeerAddr = aNetworkAddressArg->peerAddr();
    534    mResolvedByTRR = aNetworkAddressArg->resolvedByTRR();
    535    mEffectiveTRRMode = aNetworkAddressArg->mode();
    536    mTRRSkipReason = aNetworkAddressArg->trrSkipReason();
    537    mEchConfigUsed = aNetworkAddressArg->echConfigUsed();
    538  }
    539  mEventsink->OnTransportStatus(nullptr, aStatus, aProgress, aProgressMax);
    540  return IPC_OK();
    541 }
    542 
    543 mozilla::ipc::IPCResult HttpTransactionParent::RecvOnDataAvailable(
    544    const nsCString& aData, const uint64_t& aOffset, const uint32_t& aCount,
    545    const TimeStamp& aOnDataAvailableStartTime) {
    546  LOG(("HttpTransactionParent::RecvOnDataAvailable [this=%p, aOffset= %" PRIu64
    547       " aCount=%" PRIu32,
    548       this, aOffset, aCount));
    549 
    550  // The final transfer size is updated in OnStopRequest ipc message, but in the
    551  // case that the socket process is crashed or something went wrong, we might
    552  // not get the OnStopRequest. So, let's update the transfer size here.
    553  mTransferSize += aCount;
    554 
    555  if (mCanceled) {
    556    return IPC_OK();
    557  }
    558 
    559  mEventQ->RunOrEnqueue(new ChannelFunctionEvent(
    560      [self = UnsafePtr<HttpTransactionParent>(this)]() {
    561        return self->GetODATarget();
    562      },
    563      [self = UnsafePtr<HttpTransactionParent>(this), aData, aOffset, aCount,
    564       aOnDataAvailableStartTime]() {
    565        self->DoOnDataAvailable(aData, aOffset, aCount,
    566                                aOnDataAvailableStartTime);
    567      }));
    568  return IPC_OK();
    569 }
    570 
    571 void HttpTransactionParent::DoOnDataAvailable(
    572    const nsCString& aData, const uint64_t& aOffset, const uint32_t& aCount,
    573    const TimeStamp& aOnDataAvailableStartTime) {
    574  LOG(("HttpTransactionParent::DoOnDataAvailable [this=%p]\n", this));
    575  if (mCanceled) {
    576    return;
    577  }
    578 
    579  nsCOMPtr<nsIInputStream> stringStream;
    580  nsresult rv =
    581      NS_NewByteInputStream(getter_AddRefs(stringStream),
    582                            Span(aData.get(), aCount), NS_ASSIGNMENT_DEPEND);
    583 
    584  if (NS_FAILED(rv)) {
    585    CancelOnMainThread(rv);
    586    return;
    587  }
    588 
    589  mOnDataAvailableStartTime = aOnDataAvailableStartTime;
    590  AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    591  rv = mChannel->OnDataAvailable(this, stringStream, aOffset, aCount);
    592  if (NS_FAILED(rv)) {
    593    CancelOnMainThread(rv);
    594  }
    595 }
    596 
    597 // Note: Copied from HttpChannelChild.
    598 void HttpTransactionParent::CancelOnMainThread(nsresult aRv) {
    599  LOG(("HttpTransactionParent::CancelOnMainThread [this=%p]", this));
    600 
    601  if (NS_IsMainThread()) {
    602    Cancel(aRv);
    603    return;
    604  }
    605 
    606  mEventQ->Suspend();
    607  // Cancel is expected to preempt any other channel events, thus we put this
    608  // event in the front of mEventQ to make sure nsIStreamListener not receiving
    609  // any ODA/OnStopRequest callbacks.
    610  mEventQ->PrependEvent(MakeUnique<NeckoTargetChannelFunctionEvent>(
    611      this, [self = UnsafePtr<HttpTransactionParent>(this), aRv]() {
    612        self->Cancel(aRv);
    613      }));
    614  mEventQ->Resume();
    615 }
    616 
    617 mozilla::ipc::IPCResult HttpTransactionParent::RecvOnStopRequest(
    618    const nsresult& aStatus, const bool& aResponseIsComplete,
    619    const int64_t& aTransferSize, const TimingStructArgs& aTimings,
    620    const Maybe<nsHttpHeaderArray>& aResponseTrailers,
    621    Maybe<TransactionObserverResult>&& aTransactionObserverResult,
    622    const TimeStamp& aLastActiveTabOptHit,
    623    const TimeStamp& aOnStopRequestStartTime) {
    624  LOG(("HttpTransactionParent::RecvOnStopRequest [this=%p status=%" PRIx32
    625       "]\n",
    626       this, static_cast<uint32_t>(aStatus)));
    627 
    628  nsHttp::SetLastActiveTabLoadOptimizationHit(aLastActiveTabOptHit);
    629 
    630  if (mCanceled) {
    631    return IPC_OK();
    632  }
    633 
    634  mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
    635      this, [self = UnsafePtr<HttpTransactionParent>(this), aStatus,
    636             aResponseIsComplete, aTransferSize, aTimings, aResponseTrailers,
    637             aTransactionObserverResult{std::move(aTransactionObserverResult)},
    638             aOnStopRequestStartTime]() mutable {
    639        self->DoOnStopRequest(aStatus, aResponseIsComplete, aTransferSize,
    640                              aTimings, aResponseTrailers,
    641                              std::move(aTransactionObserverResult),
    642                              aOnStopRequestStartTime);
    643      }));
    644  return IPC_OK();
    645 }
    646 
    647 void HttpTransactionParent::DoOnStopRequest(
    648    const nsresult& aStatus, const bool& aResponseIsComplete,
    649    const int64_t& aTransferSize, const TimingStructArgs& aTimings,
    650    const Maybe<nsHttpHeaderArray>& aResponseTrailers,
    651    Maybe<TransactionObserverResult>&& aTransactionObserverResult,
    652    const TimeStamp& aOnStopRequestStartTime) {
    653  LOG(("HttpTransactionParent::DoOnStopRequest [this=%p]\n", this));
    654  if (mCanceled) {
    655    return;
    656  }
    657 
    658  MOZ_ASSERT(!mOnStopRequestCalled, "We should not call OnStopRequest twice");
    659 
    660  mStatus = aStatus;
    661 
    662  nsCOMPtr<nsIRequest> deathGrip = this;
    663 
    664  mResponseIsComplete = aResponseIsComplete;
    665  mTransferSize = aTransferSize;
    666  mOnStopRequestStartTime = aOnStopRequestStartTime;
    667 
    668  TimingStructArgsToTimingsStruct(aTimings, mTimings);
    669 
    670  if (aResponseTrailers.isSome()) {
    671    mResponseTrailers = MakeUnique<nsHttpHeaderArray>(aResponseTrailers.ref());
    672  }
    673 
    674  if (aTransactionObserverResult.isSome()) {
    675    TransactionObserverFunc obs = nullptr;
    676    std::swap(obs, mTransactionObserver);
    677    obs(std::move(*aTransactionObserverResult));
    678  }
    679 
    680  AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    681  (void)mChannel->OnStopRequest(this, mStatus);
    682  mOnStopRequestCalled = true;
    683 }
    684 
    685 mozilla::ipc::IPCResult HttpTransactionParent::RecvOnInitFailed(
    686    const nsresult& aStatus) {
    687  nsCOMPtr<nsIRequest> request = do_QueryInterface(mEventsink);
    688  if (request) {
    689    request->Cancel(aStatus);
    690  }
    691  return IPC_OK();
    692 }
    693 
    694 mozilla::ipc::IPCResult HttpTransactionParent::RecvEarlyHint(
    695    const nsCString& aValue, const nsACString& aReferrerPolicy,
    696    const nsACString& aCSPHeader) {
    697  LOG(
    698      ("HttpTransactionParent::RecvEarlyHint header=%s aReferrerPolicy=%s "
    699       "aCSPHeader=%s",
    700       PromiseFlatCString(aValue).get(),
    701       PromiseFlatCString(aReferrerPolicy).get(),
    702       PromiseFlatCString(aCSPHeader).get()));
    703  nsCOMPtr<nsIEarlyHintObserver> obs = do_QueryInterface(mChannel);
    704  if (obs) {
    705    (void)obs->EarlyHint(aValue, aReferrerPolicy, aCSPHeader);
    706  }
    707 
    708  return IPC_OK();
    709 }
    710 
    711 //-----------------------------------------------------------------------------
    712 // HttpTransactionParent <nsIRequest>
    713 //-----------------------------------------------------------------------------
    714 
    715 NS_IMETHODIMP
    716 HttpTransactionParent::GetName(nsACString& aResult) {
    717  aResult.Truncate();
    718  return NS_OK;
    719 }
    720 
    721 NS_IMETHODIMP
    722 HttpTransactionParent::IsPending(bool* aRetval) {
    723  *aRetval = false;
    724  return NS_OK;
    725 }
    726 
    727 NS_IMETHODIMP
    728 HttpTransactionParent::GetStatus(nsresult* aStatus) {
    729  *aStatus = mStatus;
    730  return NS_OK;
    731 }
    732 
    733 NS_IMETHODIMP HttpTransactionParent::SetCanceledReason(
    734    const nsACString& aReason) {
    735  return SetCanceledReasonImpl(aReason);
    736 }
    737 
    738 NS_IMETHODIMP HttpTransactionParent::GetCanceledReason(nsACString& aReason) {
    739  return GetCanceledReasonImpl(aReason);
    740 }
    741 
    742 NS_IMETHODIMP HttpTransactionParent::CancelWithReason(
    743    nsresult aStatus, const nsACString& aReason) {
    744  return CancelWithReasonImpl(aStatus, aReason);
    745 }
    746 
    747 NS_IMETHODIMP
    748 HttpTransactionParent::Cancel(nsresult aStatus) {
    749  MOZ_ASSERT(NS_IsMainThread());
    750 
    751  LOG(("HttpTransactionParent::Cancel [this=%p status=%" PRIx32 "]\n", this,
    752       static_cast<uint32_t>(aStatus)));
    753 
    754  if (mCanceled) {
    755    LOG(("  already canceled\n"));
    756    return NS_OK;
    757  }
    758 
    759  MOZ_ASSERT(NS_FAILED(aStatus), "cancel with non-failure status code");
    760 
    761  mCanceled = true;
    762  mStatus = aStatus;
    763  if (CanSend()) {
    764    (void)SendCancelPump(mStatus);
    765  }
    766 
    767  // Put DoNotifyListener() in front of the queue to avoid OnDataAvailable
    768  // being called after cancellation. Note that
    769  // HttpTransactionParent::OnStart/StopRequest are driven by IPC messages and
    770  // HttpTransactionChild won't send IPC if already canceled. That's why we have
    771  // to call DoNotifyListener().
    772  mEventQ->Suspend();
    773  mEventQ->PrependEvent(MakeUnique<NeckoTargetChannelFunctionEvent>(
    774      this, [self = UnsafePtr<HttpTransactionParent>(this)]() {
    775        self->DoNotifyListener();
    776      }));
    777  mEventQ->Resume();
    778  return NS_OK;
    779 }
    780 
    781 void HttpTransactionParent::DoNotifyListener() {
    782  LOG(("HttpTransactionParent::DoNotifyListener this=%p", this));
    783  MOZ_ASSERT(NS_IsMainThread());
    784 
    785  if (mChannel && !mOnStartRequestCalled) {
    786    nsCOMPtr<nsIStreamListener> listener = mChannel;
    787    mOnStartRequestCalled = true;
    788    listener->OnStartRequest(this);
    789  }
    790  mOnStartRequestCalled = true;
    791 
    792  // This is to make sure that ODA in the event queue can be processed before
    793  // OnStopRequest.
    794  mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
    795      this, [self = UnsafePtr<HttpTransactionParent>(this)] {
    796        self->ContinueDoNotifyListener();
    797      }));
    798 }
    799 
    800 void HttpTransactionParent::ContinueDoNotifyListener() {
    801  LOG(("HttpTransactionParent::ContinueDoNotifyListener this=%p", this));
    802  MOZ_ASSERT(NS_IsMainThread());
    803 
    804  if (mChannel && !mOnStopRequestCalled) {
    805    nsCOMPtr<nsIStreamListener> listener = mChannel;
    806    mOnStopRequestCalled = true;  // avoid reentrancy bugs by setting this now
    807    listener->OnStopRequest(this, mStatus);
    808  }
    809  mOnStopRequestCalled = true;
    810 
    811  mChannel = nullptr;
    812 }
    813 
    814 NS_IMETHODIMP
    815 HttpTransactionParent::Suspend() {
    816  MOZ_ASSERT(NS_IsMainThread());
    817 
    818  // SendSuspend only once, when suspend goes from 0 to 1.
    819  if (!mSuspendCount++ && CanSend()) {
    820    (void)SendSuspendPump();
    821  }
    822  mEventQ->Suspend();
    823  return NS_OK;
    824 }
    825 
    826 NS_IMETHODIMP
    827 HttpTransactionParent::Resume() {
    828  MOZ_ASSERT(NS_IsMainThread());
    829  MOZ_ASSERT(mSuspendCount, "Resume called more than Suspend");
    830 
    831  // SendResume only once, when suspend count drops to 0.
    832  if (mSuspendCount && !--mSuspendCount) {
    833    if (CanSend()) {
    834      (void)SendResumePump();
    835    }
    836 
    837    if (mCallOnResume) {
    838      nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
    839      MOZ_ASSERT(neckoTarget);
    840 
    841      RefPtr<HttpTransactionParent> self = this;
    842      std::function<void()> callOnResume = nullptr;
    843      std::swap(callOnResume, mCallOnResume);
    844      neckoTarget->Dispatch(
    845          NS_NewRunnableFunction("net::HttpTransactionParent::mCallOnResume",
    846                                 [callOnResume]() { callOnResume(); }),
    847          NS_DISPATCH_NORMAL);
    848    }
    849  }
    850  mEventQ->Resume();
    851  return NS_OK;
    852 }
    853 
    854 NS_IMETHODIMP
    855 HttpTransactionParent::GetLoadGroup(nsILoadGroup** aLoadGroup) {
    856  MOZ_ASSERT(false, "Should not be called.");
    857  return NS_ERROR_NOT_IMPLEMENTED;
    858 }
    859 
    860 NS_IMETHODIMP
    861 HttpTransactionParent::SetLoadGroup(nsILoadGroup* aLoadGroup) {
    862  MOZ_ASSERT(false, "Should not be called.");
    863  return NS_ERROR_NOT_IMPLEMENTED;
    864 }
    865 
    866 NS_IMETHODIMP
    867 HttpTransactionParent::GetLoadFlags(nsLoadFlags* aLoadFlags) {
    868  MOZ_ASSERT(false, "Should not be called.");
    869  return NS_ERROR_NOT_IMPLEMENTED;
    870 }
    871 
    872 NS_IMETHODIMP
    873 HttpTransactionParent::SetLoadFlags(nsLoadFlags aLoadFlags) {
    874  MOZ_ASSERT(false, "Should not be called.");
    875  return NS_ERROR_NOT_IMPLEMENTED;
    876 }
    877 
    878 NS_IMETHODIMP
    879 HttpTransactionParent::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
    880  MOZ_ASSERT(false, "Should not be called.");
    881  return NS_ERROR_NOT_IMPLEMENTED;
    882 }
    883 
    884 NS_IMETHODIMP
    885 HttpTransactionParent::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
    886  MOZ_ASSERT(false, "Should not be called.");
    887  return NS_ERROR_NOT_IMPLEMENTED;
    888 }
    889 
    890 void HttpTransactionParent::ActorDestroy(ActorDestroyReason aWhy) {
    891  LOG(("HttpTransactionParent::ActorDestroy [this=%p]\n", this));
    892  if (aWhy != Deletion) {
    893    // Make sure all the messages are processed.
    894    AutoEventEnqueuer ensureSerialDispatch(mEventQ);
    895 
    896    mStatus = NS_ERROR_FAILURE;
    897    HandleAsyncAbort();
    898 
    899    mCanceled = true;
    900  }
    901 }
    902 
    903 void HttpTransactionParent::HandleAsyncAbort() {
    904  MOZ_ASSERT(!mCallOnResume, "How did that happen?");
    905 
    906  if (mSuspendCount) {
    907    LOG(
    908        ("HttpTransactionParent Waiting until resume to do async notification "
    909         "[this=%p]\n",
    910         this));
    911    RefPtr<HttpTransactionParent> self = this;
    912    mCallOnResume = [self]() { self->HandleAsyncAbort(); };
    913    return;
    914  }
    915 
    916  DoNotifyListener();
    917 }
    918 
    919 bool HttpTransactionParent::GetSupportsHTTP3() { return mSupportsHTTP3; }
    920 
    921 void HttpTransactionParent::SetIsForWebTransport(bool SetIsForWebTransport) {
    922  // TODO: bug 1791727
    923 }
    924 
    925 mozilla::TimeStamp HttpTransactionParent::GetPendingTime() {
    926  return mTimings.transactionPending;
    927 }
    928 
    929 }  // namespace mozilla::net