Bug 1814798 - pt 2. Add a PHCManager component to control PHC r=glandium,emilio
[gecko.git] / netwerk / protocol / http / HttpTransactionParent.cpp
blobe8a38e3439a681daf8d3ca8a9918a31a9fe0ab13
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/. */
7 // HttpLog.h should generally be included first
8 #include "HttpLog.h"
10 #include "HttpTransactionParent.h"
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"
26 namespace mozilla::net {
28 NS_IMPL_ADDREF(HttpTransactionParent)
29 NS_INTERFACE_MAP_BEGIN(HttpTransactionParent)
30 NS_INTERFACE_MAP_ENTRY(nsIRequest)
31 NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
32 NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpTransactionParent)
33 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequest)
34 NS_INTERFACE_MAP_END
36 NS_IMETHODIMP_(MozExternalRefCountType) HttpTransactionParent::Release(void) {
37 MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
38 nsrefcnt count = --mRefCnt;
39 NS_LOG_RELEASE(this, count, "HttpTransactionParent");
40 if (count == 0) {
41 mRefCnt = 1; /* stabilize */
42 delete (this);
43 return 0;
46 // When ref count goes down to 1 (held internally by IPDL), it means that
47 // we are done with this transaction. We should send a delete message
48 // to delete the transaction child in socket process.
49 if (count == 1 && CanSend()) {
50 if (!NS_IsMainThread()) {
51 RefPtr<HttpTransactionParent> self = this;
52 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(
53 NS_NewRunnableFunction("HttpTransactionParent::Release", [self]() {
54 mozilla::Unused << self->Send__delete__(self);
55 // Make sure we can not send IPC after Send__delete__().
56 MOZ_ASSERT(!self->CanSend());
57 })));
58 } else {
59 mozilla::Unused << Send__delete__(this);
61 return 1;
63 return count;
66 //-----------------------------------------------------------------------------
67 // HttpTransactionParent <public>
68 //-----------------------------------------------------------------------------
70 HttpTransactionParent::HttpTransactionParent(bool aIsDocumentLoad)
71 : mIsDocumentLoad(aIsDocumentLoad) {
72 LOG(("Creating HttpTransactionParent @%p\n", this));
73 mEventQ = new ChannelEventQueue(static_cast<nsIRequest*>(this));
76 HttpTransactionParent::~HttpTransactionParent() {
77 LOG(("Destroying HttpTransactionParent @%p\n", this));
78 mEventQ->NotifyReleasingOwner();
81 //-----------------------------------------------------------------------------
82 // HttpTransactionParent <nsAHttpTransactionShell>
83 //-----------------------------------------------------------------------------
85 // Let socket process init the *real* nsHttpTransaction.
86 nsresult HttpTransactionParent::Init(
87 uint32_t caps, nsHttpConnectionInfo* cinfo, nsHttpRequestHead* requestHead,
88 nsIInputStream* requestBody, uint64_t requestContentLength,
89 bool requestBodyHasHeaders, nsIEventTarget* target,
90 nsIInterfaceRequestor* callbacks, nsITransportEventSink* eventsink,
91 uint64_t browserId, HttpTrafficCategory trafficCategory,
92 nsIRequestContext* requestContext, ClassOfService classOfService,
93 uint32_t initialRwin, bool responseTimeoutEnabled, uint64_t channelId,
94 TransactionObserverFunc&& transactionObserver,
95 OnPushCallback&& aOnPushCallback,
96 HttpTransactionShell* aTransWithPushedStream, uint32_t aPushedStreamId) {
97 LOG(("HttpTransactionParent::Init [this=%p caps=%x]\n", this, caps));
99 if (!CanSend()) {
100 return NS_ERROR_FAILURE;
103 mEventsink = eventsink;
104 mTargetThread = GetCurrentSerialEventTarget();
105 mChannelId = channelId;
106 mTransactionObserver = std::move(transactionObserver);
107 mOnPushCallback = std::move(aOnPushCallback);
108 mCaps = caps;
109 mConnInfo = cinfo->Clone();
110 mIsHttp3Used = cinfo->IsHttp3();
112 HttpConnectionInfoCloneArgs infoArgs;
113 nsHttpConnectionInfo::SerializeHttpConnectionInfo(cinfo, infoArgs);
115 Maybe<mozilla::ipc::IPCStream> ipcStream;
116 if (!mozilla::ipc::SerializeIPCStream(do_AddRef(requestBody), ipcStream,
117 /* aAllowLazy */ false)) {
118 return NS_ERROR_FAILURE;
121 uint64_t requestContextID = requestContext ? requestContext->GetID() : 0;
123 Maybe<H2PushedStreamArg> pushedStreamArg;
124 if (aTransWithPushedStream && aPushedStreamId) {
125 MOZ_ASSERT(aTransWithPushedStream->AsHttpTransactionParent());
126 pushedStreamArg.emplace(
127 WrapNotNull(aTransWithPushedStream->AsHttpTransactionParent()),
128 aPushedStreamId);
131 nsCOMPtr<nsIThrottledInputChannel> throttled = do_QueryInterface(mEventsink);
132 Maybe<NotNull<PInputChannelThrottleQueueParent*>> throttleQueue;
133 if (throttled) {
134 nsCOMPtr<nsIInputChannelThrottleQueue> queue;
135 nsresult rv = throttled->GetThrottleQueue(getter_AddRefs(queue));
136 // In case of failure, just carry on without throttling.
137 if (NS_SUCCEEDED(rv) && queue) {
138 LOG1(("HttpTransactionParent::Init %p using throttle queue %p\n", this,
139 queue.get()));
140 RefPtr<InputChannelThrottleQueueParent> tqParent = do_QueryObject(queue);
141 MOZ_ASSERT(tqParent);
142 throttleQueue.emplace(WrapNotNull(tqParent.get()));
146 // TODO: Figure out if we have to implement nsIThreadRetargetableRequest in
147 // bug 1544378.
148 if (!SendInit(caps, infoArgs, *requestHead, ipcStream, requestContentLength,
149 requestBodyHasHeaders, browserId,
150 static_cast<uint8_t>(trafficCategory), requestContextID,
151 classOfService, initialRwin, responseTimeoutEnabled, mChannelId,
152 !!mTransactionObserver, pushedStreamArg, throttleQueue,
153 mIsDocumentLoad, mRedirectStart, mRedirectEnd)) {
154 return NS_ERROR_FAILURE;
157 nsCString reqHeaderBuf = nsHttp::ConvertRequestHeadToString(
158 *requestHead, !!requestBody, requestBodyHasHeaders,
159 cinfo->UsingConnect());
160 requestContentLength += reqHeaderBuf.Length();
162 mRequestSize = InScriptableRange(requestContentLength)
163 ? static_cast<int64_t>(requestContentLength)
164 : -1;
166 return NS_OK;
169 nsresult HttpTransactionParent::AsyncRead(nsIStreamListener* listener,
170 nsIRequest** pump) {
171 MOZ_ASSERT(pump);
173 *pump = do_AddRef(this).take();
174 mChannel = listener;
175 return NS_OK;
178 UniquePtr<nsHttpResponseHead> HttpTransactionParent::TakeResponseHead() {
179 MOZ_ASSERT(NS_IsMainThread());
180 MOZ_ASSERT(!mResponseHeadTaken, "TakeResponseHead called 2x");
182 mResponseHeadTaken = true;
183 return std::move(mResponseHead);
186 UniquePtr<nsHttpHeaderArray> HttpTransactionParent::TakeResponseTrailers() {
187 MOZ_ASSERT(NS_IsMainThread());
188 MOZ_ASSERT(!mResponseTrailersTaken, "TakeResponseTrailers called 2x");
190 mResponseTrailersTaken = true;
191 return std::move(mResponseTrailers);
194 void HttpTransactionParent::SetSniffedTypeToChannel(
195 nsInputStreamPump::PeekSegmentFun aCallTypeSniffers, nsIChannel* aChannel) {
196 if (!mDataForSniffer.IsEmpty()) {
197 aCallTypeSniffers(aChannel, mDataForSniffer.Elements(),
198 mDataForSniffer.Length());
202 NS_IMETHODIMP
203 HttpTransactionParent::GetDeliveryTarget(nsISerialEventTarget** aEventTarget) {
204 MutexAutoLock lock(mEventTargetMutex);
206 nsCOMPtr<nsISerialEventTarget> target = mODATarget;
207 if (!mODATarget) {
208 target = mTargetThread;
210 target.forget(aEventTarget);
211 return NS_OK;
214 already_AddRefed<nsISerialEventTarget> HttpTransactionParent::GetODATarget() {
215 nsCOMPtr<nsISerialEventTarget> target;
217 MutexAutoLock lock(mEventTargetMutex);
218 target = mODATarget ? mODATarget : mTargetThread;
221 if (!target) {
222 target = GetMainThreadSerialEventTarget();
224 return target.forget();
227 NS_IMETHODIMP HttpTransactionParent::RetargetDeliveryTo(
228 nsISerialEventTarget* aEventTarget) {
229 LOG(("HttpTransactionParent::RetargetDeliveryTo [this=%p, aTarget=%p]", this,
230 aEventTarget));
232 MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only");
233 MOZ_ASSERT(!mODATarget);
234 NS_ENSURE_ARG(aEventTarget);
236 if (aEventTarget->IsOnCurrentThread()) {
237 NS_WARNING("Retargeting delivery to same thread");
238 return NS_OK;
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;
249 rv = retargetableListener->CheckListenerChain();
250 if (NS_FAILED(rv)) {
251 NS_WARNING("Subsequent listeners are not retargetable");
252 return rv;
256 MutexAutoLock lock(mEventTargetMutex);
257 mODATarget = aEventTarget;
260 return NS_OK;
263 void HttpTransactionParent::SetDNSWasRefreshed() {
264 MOZ_ASSERT(NS_IsMainThread(), "SetDNSWasRefreshed on main thread only!");
265 Unused << SendSetDNSWasRefreshed();
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;
280 bool HttpTransactionParent::HasStickyConnection() const {
281 return mCaps & NS_HTTP_STICKY_CONNECTION;
284 mozilla::TimeStamp HttpTransactionParent::GetDomainLookupStart() {
285 return mTimings.domainLookupStart;
288 mozilla::TimeStamp HttpTransactionParent::GetDomainLookupEnd() {
289 return mTimings.domainLookupEnd;
292 mozilla::TimeStamp HttpTransactionParent::GetConnectStart() {
293 return mTimings.connectStart;
296 mozilla::TimeStamp HttpTransactionParent::GetTcpConnectEnd() {
297 return mTimings.tcpConnectEnd;
300 mozilla::TimeStamp HttpTransactionParent::GetSecureConnectionStart() {
301 return mTimings.secureConnectionStart;
304 mozilla::TimeStamp HttpTransactionParent::GetConnectEnd() {
305 return mTimings.connectEnd;
308 mozilla::TimeStamp HttpTransactionParent::GetRequestStart() {
309 return mTimings.requestStart;
312 mozilla::TimeStamp HttpTransactionParent::GetResponseStart() {
313 return mTimings.responseStart;
316 mozilla::TimeStamp HttpTransactionParent::GetResponseEnd() {
317 return mTimings.responseEnd;
320 TimingStruct HttpTransactionParent::Timings() { return mTimings; }
322 bool HttpTransactionParent::ResponseIsComplete() { return mResponseIsComplete; }
324 int64_t HttpTransactionParent::GetTransferSize() { return mTransferSize; }
326 int64_t HttpTransactionParent::GetRequestSize() { return mRequestSize; }
328 bool HttpTransactionParent::IsHttp3Used() { return mIsHttp3Used; }
330 bool HttpTransactionParent::DataSentToChildProcess() {
331 return mDataSentToChildProcess;
334 already_AddRefed<nsITransportSecurityInfo>
335 HttpTransactionParent::SecurityInfo() {
336 return do_AddRef(mSecurityInfo);
339 bool HttpTransactionParent::ProxyConnectFailed() { return mProxyConnectFailed; }
341 bool HttpTransactionParent::TakeRestartedState() {
342 bool result = mRestarted;
343 mRestarted = false;
344 return result;
347 uint32_t HttpTransactionParent::HTTPSSVCReceivedStage() {
348 return mHTTPSSVCReceivedStage;
351 void HttpTransactionParent::DontReuseConnection() {
352 MOZ_ASSERT(NS_IsMainThread());
353 Unused << SendDontReuseConnection();
356 void HttpTransactionParent::SetH2WSConnRefTaken() {
357 MOZ_ASSERT(NS_IsMainThread());
358 Unused << SendSetH2WSConnRefTaken();
361 void HttpTransactionParent::SetSecurityCallbacks(
362 nsIInterfaceRequestor* aCallbacks) {
363 // TODO: we might don't need to implement this.
364 // Will figure out in bug 1512479.
367 void HttpTransactionParent::SetDomainLookupStart(mozilla::TimeStamp timeStamp,
368 bool onlyIfNull) {
369 mDomainLookupStart = timeStamp;
370 mTimings.domainLookupStart = mDomainLookupStart;
372 void HttpTransactionParent::SetDomainLookupEnd(mozilla::TimeStamp timeStamp,
373 bool onlyIfNull) {
374 mDomainLookupEnd = timeStamp;
375 mTimings.domainLookupEnd = mDomainLookupEnd;
378 nsHttpTransaction* HttpTransactionParent::AsHttpTransaction() {
379 return nullptr;
382 HttpTransactionParent* HttpTransactionParent::AsHttpTransactionParent() {
383 return this;
386 int32_t HttpTransactionParent::GetProxyConnectResponseCode() {
387 return mProxyConnectResponseCode;
390 bool HttpTransactionParent::Http2Disabled() const {
391 return mCaps & NS_HTTP_DISALLOW_SPDY;
394 bool HttpTransactionParent::Http3Disabled() const {
395 return mCaps & NS_HTTP_DISALLOW_HTTP3;
398 already_AddRefed<nsHttpConnectionInfo> HttpTransactionParent::GetConnInfo()
399 const {
400 RefPtr<nsHttpConnectionInfo> connInfo = mConnInfo->Clone();
401 return connInfo.forget();
404 already_AddRefed<nsIEventTarget> HttpTransactionParent::GetNeckoTarget() {
405 nsCOMPtr<nsIEventTarget> target = GetMainThreadSerialEventTarget();
406 return target.forget();
409 mozilla::ipc::IPCResult HttpTransactionParent::RecvOnStartRequest(
410 const nsresult& aStatus, const Maybe<nsHttpResponseHead>& aResponseHead,
411 nsITransportSecurityInfo* aSecurityInfo, const bool& aProxyConnectFailed,
412 const TimingStructArgs& aTimings, const int32_t& aProxyConnectResponseCode,
413 nsTArray<uint8_t>&& aDataForSniffer, const Maybe<nsCString>& aAltSvcUsed,
414 const bool& aDataToChildProcess, const bool& aRestarted,
415 const uint32_t& aHTTPSSVCReceivedStage, const bool& aSupportsHttp3,
416 const nsIRequest::TRRMode& aMode, const TRRSkippedReason& aTrrSkipReason,
417 const uint32_t& aCaps) {
418 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
419 this,
420 [self = UnsafePtr<HttpTransactionParent>(this), aStatus, aResponseHead,
421 securityInfo = nsCOMPtr{aSecurityInfo}, aProxyConnectFailed, aTimings,
422 aProxyConnectResponseCode,
423 aDataForSniffer = CopyableTArray{std::move(aDataForSniffer)},
424 aAltSvcUsed, aDataToChildProcess, aRestarted, aHTTPSSVCReceivedStage,
425 aSupportsHttp3, aMode, aTrrSkipReason, aCaps]() mutable {
426 self->DoOnStartRequest(
427 aStatus, aResponseHead, securityInfo, aProxyConnectFailed, aTimings,
428 aProxyConnectResponseCode, std::move(aDataForSniffer), aAltSvcUsed,
429 aDataToChildProcess, aRestarted, aHTTPSSVCReceivedStage,
430 aSupportsHttp3, aMode, aTrrSkipReason, aCaps);
431 }));
432 return IPC_OK();
435 static void TimingStructArgsToTimingsStruct(const TimingStructArgs& aArgs,
436 TimingStruct& aTimings) {
437 // If domainLookupStart/End was set by the channel before, we use these
438 // timestamps instead the ones from the transaction.
439 if (aTimings.domainLookupStart.IsNull() &&
440 aTimings.domainLookupEnd.IsNull()) {
441 aTimings.domainLookupStart = aArgs.domainLookupStart();
442 aTimings.domainLookupEnd = aArgs.domainLookupEnd();
444 aTimings.connectStart = aArgs.connectStart();
445 aTimings.tcpConnectEnd = aArgs.tcpConnectEnd();
446 aTimings.secureConnectionStart = aArgs.secureConnectionStart();
447 aTimings.connectEnd = aArgs.connectEnd();
448 aTimings.requestStart = aArgs.requestStart();
449 aTimings.responseStart = aArgs.responseStart();
450 aTimings.responseEnd = aArgs.responseEnd();
451 aTimings.transactionPending = aArgs.transactionPending();
454 void HttpTransactionParent::DoOnStartRequest(
455 const nsresult& aStatus, const Maybe<nsHttpResponseHead>& aResponseHead,
456 nsITransportSecurityInfo* aSecurityInfo, const bool& aProxyConnectFailed,
457 const TimingStructArgs& aTimings, const int32_t& aProxyConnectResponseCode,
458 nsTArray<uint8_t>&& aDataForSniffer, const Maybe<nsCString>& aAltSvcUsed,
459 const bool& aDataToChildProcess, const bool& aRestarted,
460 const uint32_t& aHTTPSSVCReceivedStage, const bool& aSupportsHttp3,
461 const nsIRequest::TRRMode& aMode, const TRRSkippedReason& aSkipReason,
462 const uint32_t& aCaps) {
463 LOG(("HttpTransactionParent::DoOnStartRequest [this=%p aStatus=%" PRIx32
464 "]\n",
465 this, static_cast<uint32_t>(aStatus)));
467 if (mCanceled) {
468 return;
471 MOZ_ASSERT(!mOnStartRequestCalled);
473 mStatus = aStatus;
474 mDataSentToChildProcess = aDataToChildProcess;
475 mHTTPSSVCReceivedStage = aHTTPSSVCReceivedStage;
476 mSupportsHTTP3 = aSupportsHttp3;
477 mEffectiveTRRMode = aMode;
478 mTRRSkipReason = aSkipReason;
479 mCaps = aCaps;
480 mSecurityInfo = aSecurityInfo;
482 if (aResponseHead.isSome()) {
483 mResponseHead = MakeUnique<nsHttpResponseHead>(aResponseHead.ref());
485 mProxyConnectFailed = aProxyConnectFailed;
486 TimingStructArgsToTimingsStruct(aTimings, mTimings);
488 mProxyConnectResponseCode = aProxyConnectResponseCode;
489 mDataForSniffer = std::move(aDataForSniffer);
490 mRestarted = aRestarted;
492 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
493 MOZ_ASSERT(httpChannel, "mChannel is expected to implement nsIHttpChannel");
494 if (httpChannel) {
495 if (aAltSvcUsed.isSome()) {
496 Unused << httpChannel->SetRequestHeader(
497 nsHttp::Alternate_Service_Used.val(), aAltSvcUsed.ref(), false);
501 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
502 nsresult rv = mChannel->OnStartRequest(this);
503 mOnStartRequestCalled = true;
504 if (NS_FAILED(rv)) {
505 Cancel(rv);
509 mozilla::ipc::IPCResult HttpTransactionParent::RecvOnTransportStatus(
510 const nsresult& aStatus, const int64_t& aProgress,
511 const int64_t& aProgressMax,
512 Maybe<NetworkAddressArg>&& aNetworkAddressArg) {
513 if (aNetworkAddressArg) {
514 mSelfAddr = aNetworkAddressArg->selfAddr();
515 mPeerAddr = aNetworkAddressArg->peerAddr();
516 mResolvedByTRR = aNetworkAddressArg->resolvedByTRR();
517 mEffectiveTRRMode = aNetworkAddressArg->mode();
518 mTRRSkipReason = aNetworkAddressArg->trrSkipReason();
519 mEchConfigUsed = aNetworkAddressArg->echConfigUsed();
521 mEventsink->OnTransportStatus(nullptr, aStatus, aProgress, aProgressMax);
522 return IPC_OK();
525 mozilla::ipc::IPCResult HttpTransactionParent::RecvOnDataAvailable(
526 const nsCString& aData, const uint64_t& aOffset, const uint32_t& aCount) {
527 LOG(("HttpTransactionParent::RecvOnDataAvailable [this=%p, aOffset= %" PRIu64
528 " aCount=%" PRIu32,
529 this, aOffset, aCount));
531 // The final transfer size is updated in OnStopRequest ipc message, but in the
532 // case that the socket process is crashed or something went wrong, we might
533 // not get the OnStopRequest. So, let's update the transfer size here.
534 mTransferSize += aCount;
536 if (mCanceled) {
537 return IPC_OK();
540 mEventQ->RunOrEnqueue(new ChannelFunctionEvent(
541 [self = UnsafePtr<HttpTransactionParent>(this)]() {
542 return self->GetODATarget();
544 [self = UnsafePtr<HttpTransactionParent>(this), aData, aOffset,
545 aCount]() { self->DoOnDataAvailable(aData, aOffset, aCount); }));
546 return IPC_OK();
549 void HttpTransactionParent::DoOnDataAvailable(const nsCString& aData,
550 const uint64_t& aOffset,
551 const uint32_t& aCount) {
552 LOG(("HttpTransactionParent::DoOnDataAvailable [this=%p]\n", this));
553 if (mCanceled) {
554 return;
557 nsCOMPtr<nsIInputStream> stringStream;
558 nsresult rv =
559 NS_NewByteInputStream(getter_AddRefs(stringStream),
560 Span(aData.get(), aCount), NS_ASSIGNMENT_DEPEND);
562 if (NS_FAILED(rv)) {
563 CancelOnMainThread(rv);
564 return;
567 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
568 rv = mChannel->OnDataAvailable(this, stringStream, aOffset, aCount);
569 if (NS_FAILED(rv)) {
570 CancelOnMainThread(rv);
574 // Note: Copied from HttpChannelChild.
575 void HttpTransactionParent::CancelOnMainThread(nsresult aRv) {
576 LOG(("HttpTransactionParent::CancelOnMainThread [this=%p]", this));
578 if (NS_IsMainThread()) {
579 Cancel(aRv);
580 return;
583 mEventQ->Suspend();
584 // Cancel is expected to preempt any other channel events, thus we put this
585 // event in the front of mEventQ to make sure nsIStreamListener not receiving
586 // any ODA/OnStopRequest callbacks.
587 mEventQ->PrependEvent(MakeUnique<NeckoTargetChannelFunctionEvent>(
588 this, [self = UnsafePtr<HttpTransactionParent>(this), aRv]() {
589 self->Cancel(aRv);
590 }));
591 mEventQ->Resume();
594 mozilla::ipc::IPCResult HttpTransactionParent::RecvOnStopRequest(
595 const nsresult& aStatus, const bool& aResponseIsComplete,
596 const int64_t& aTransferSize, const TimingStructArgs& aTimings,
597 const Maybe<nsHttpHeaderArray>& aResponseTrailers,
598 Maybe<TransactionObserverResult>&& aTransactionObserverResult,
599 const TimeStamp& aLastActiveTabOptHit,
600 const HttpConnectionInfoCloneArgs& aArgs) {
601 LOG(("HttpTransactionParent::RecvOnStopRequest [this=%p status=%" PRIx32
602 "]\n",
603 this, static_cast<uint32_t>(aStatus)));
605 nsHttp::SetLastActiveTabLoadOptimizationHit(aLastActiveTabOptHit);
607 if (mCanceled) {
608 return IPC_OK();
610 RefPtr<nsHttpConnectionInfo> cinfo =
611 nsHttpConnectionInfo::DeserializeHttpConnectionInfoCloneArgs(aArgs);
612 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
613 this, [self = UnsafePtr<HttpTransactionParent>(this), aStatus,
614 aResponseIsComplete, aTransferSize, aTimings, aResponseTrailers,
615 aTransactionObserverResult{std::move(aTransactionObserverResult)},
616 cinfo{std::move(cinfo)}]() mutable {
617 self->DoOnStopRequest(aStatus, aResponseIsComplete, aTransferSize,
618 aTimings, aResponseTrailers,
619 std::move(aTransactionObserverResult), cinfo);
620 }));
621 return IPC_OK();
624 void HttpTransactionParent::DoOnStopRequest(
625 const nsresult& aStatus, const bool& aResponseIsComplete,
626 const int64_t& aTransferSize, const TimingStructArgs& aTimings,
627 const Maybe<nsHttpHeaderArray>& aResponseTrailers,
628 Maybe<TransactionObserverResult>&& aTransactionObserverResult,
629 nsHttpConnectionInfo* aConnInfo) {
630 LOG(("HttpTransactionParent::DoOnStopRequest [this=%p]\n", this));
631 if (mCanceled) {
632 return;
635 MOZ_ASSERT(!mOnStopRequestCalled, "We should not call OnStopRequest twice");
637 mStatus = aStatus;
639 nsCOMPtr<nsIRequest> deathGrip = this;
641 mResponseIsComplete = aResponseIsComplete;
642 mTransferSize = aTransferSize;
644 TimingStructArgsToTimingsStruct(aTimings, mTimings);
646 if (aResponseTrailers.isSome()) {
647 mResponseTrailers = MakeUnique<nsHttpHeaderArray>(aResponseTrailers.ref());
649 mConnInfo = aConnInfo;
650 if (aTransactionObserverResult.isSome()) {
651 TransactionObserverFunc obs = nullptr;
652 std::swap(obs, mTransactionObserver);
653 obs(std::move(*aTransactionObserverResult));
656 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
657 Unused << mChannel->OnStopRequest(this, mStatus);
658 mOnStopRequestCalled = true;
661 mozilla::ipc::IPCResult HttpTransactionParent::RecvOnInitFailed(
662 const nsresult& aStatus) {
663 nsCOMPtr<nsIRequest> request = do_QueryInterface(mEventsink);
664 if (request) {
665 request->Cancel(aStatus);
667 return IPC_OK();
670 mozilla::ipc::IPCResult HttpTransactionParent::RecvOnH2PushStream(
671 const uint32_t& aPushedStreamId, const nsCString& aResourceUrl,
672 const nsCString& aRequestString) {
673 MOZ_ASSERT(mOnPushCallback);
675 mOnPushCallback(aPushedStreamId, aResourceUrl, aRequestString, this);
676 return IPC_OK();
677 } // namespace net
679 mozilla::ipc::IPCResult HttpTransactionParent::RecvEarlyHint(
680 const nsCString& aValue, const nsACString& aReferrerPolicy,
681 const nsACString& aCSPHeader) {
682 LOG(
683 ("HttpTransactionParent::RecvEarlyHint header=%s aReferrerPolicy=%s "
684 "aCSPHeader=%s",
685 PromiseFlatCString(aValue).get(),
686 PromiseFlatCString(aReferrerPolicy).get(),
687 PromiseFlatCString(aCSPHeader).get()));
688 nsCOMPtr<nsIEarlyHintObserver> obs = do_QueryInterface(mChannel);
689 if (obs) {
690 Unused << obs->EarlyHint(aValue, aReferrerPolicy, aCSPHeader);
693 return IPC_OK();
696 //-----------------------------------------------------------------------------
697 // HttpTransactionParent <nsIRequest>
698 //-----------------------------------------------------------------------------
700 NS_IMETHODIMP
701 HttpTransactionParent::GetName(nsACString& aResult) {
702 aResult.Truncate();
703 return NS_OK;
706 NS_IMETHODIMP
707 HttpTransactionParent::IsPending(bool* aRetval) {
708 *aRetval = false;
709 return NS_OK;
712 NS_IMETHODIMP
713 HttpTransactionParent::GetStatus(nsresult* aStatus) {
714 *aStatus = mStatus;
715 return NS_OK;
718 NS_IMETHODIMP HttpTransactionParent::SetCanceledReason(
719 const nsACString& aReason) {
720 return SetCanceledReasonImpl(aReason);
723 NS_IMETHODIMP HttpTransactionParent::GetCanceledReason(nsACString& aReason) {
724 return GetCanceledReasonImpl(aReason);
727 NS_IMETHODIMP HttpTransactionParent::CancelWithReason(
728 nsresult aStatus, const nsACString& aReason) {
729 return CancelWithReasonImpl(aStatus, aReason);
732 NS_IMETHODIMP
733 HttpTransactionParent::Cancel(nsresult aStatus) {
734 MOZ_ASSERT(NS_IsMainThread());
736 LOG(("HttpTransactionParent::Cancel [this=%p status=%" PRIx32 "]\n", this,
737 static_cast<uint32_t>(aStatus)));
739 if (mCanceled) {
740 LOG((" already canceled\n"));
741 return NS_OK;
744 MOZ_ASSERT(NS_FAILED(aStatus), "cancel with non-failure status code");
746 mCanceled = true;
747 mStatus = aStatus;
748 if (CanSend()) {
749 Unused << SendCancelPump(mStatus);
752 // Put DoNotifyListener() in front of the queue to avoid OnDataAvailable
753 // being called after cancellation. Note that
754 // HttpTransactionParent::OnStart/StopRequest are driven by IPC messages and
755 // HttpTransactionChild won't send IPC if already canceled. That's why we have
756 // to call DoNotifyListener().
757 mEventQ->Suspend();
758 mEventQ->PrependEvent(MakeUnique<NeckoTargetChannelFunctionEvent>(
759 this, [self = UnsafePtr<HttpTransactionParent>(this)]() {
760 self->DoNotifyListener();
761 }));
762 mEventQ->Resume();
763 return NS_OK;
766 void HttpTransactionParent::DoNotifyListener() {
767 LOG(("HttpTransactionParent::DoNotifyListener this=%p", this));
768 MOZ_ASSERT(NS_IsMainThread());
770 if (mChannel && !mOnStartRequestCalled) {
771 nsCOMPtr<nsIStreamListener> listener = mChannel;
772 mOnStartRequestCalled = true;
773 listener->OnStartRequest(this);
775 mOnStartRequestCalled = true;
777 // This is to make sure that ODA in the event queue can be processed before
778 // OnStopRequest.
779 mEventQ->RunOrEnqueue(new NeckoTargetChannelFunctionEvent(
780 this, [self = UnsafePtr<HttpTransactionParent>(this)] {
781 self->ContinueDoNotifyListener();
782 }));
785 void HttpTransactionParent::ContinueDoNotifyListener() {
786 LOG(("HttpTransactionParent::ContinueDoNotifyListener this=%p", this));
787 MOZ_ASSERT(NS_IsMainThread());
789 if (mChannel && !mOnStopRequestCalled) {
790 nsCOMPtr<nsIStreamListener> listener = mChannel;
791 mOnStopRequestCalled = true; // avoid reentrancy bugs by setting this now
792 listener->OnStopRequest(this, mStatus);
794 mOnStopRequestCalled = true;
796 mChannel = nullptr;
799 NS_IMETHODIMP
800 HttpTransactionParent::Suspend() {
801 MOZ_ASSERT(NS_IsMainThread());
803 // SendSuspend only once, when suspend goes from 0 to 1.
804 if (!mSuspendCount++ && CanSend()) {
805 Unused << SendSuspendPump();
807 mEventQ->Suspend();
808 return NS_OK;
811 NS_IMETHODIMP
812 HttpTransactionParent::Resume() {
813 MOZ_ASSERT(NS_IsMainThread());
814 MOZ_ASSERT(mSuspendCount, "Resume called more than Suspend");
816 // SendResume only once, when suspend count drops to 0.
817 if (mSuspendCount && !--mSuspendCount) {
818 if (CanSend()) {
819 Unused << SendResumePump();
822 if (mCallOnResume) {
823 nsCOMPtr<nsIEventTarget> neckoTarget = GetNeckoTarget();
824 MOZ_ASSERT(neckoTarget);
826 RefPtr<HttpTransactionParent> self = this;
827 std::function<void()> callOnResume = nullptr;
828 std::swap(callOnResume, mCallOnResume);
829 neckoTarget->Dispatch(
830 NS_NewRunnableFunction("net::HttpTransactionParent::mCallOnResume",
831 [callOnResume]() { callOnResume(); }),
832 NS_DISPATCH_NORMAL);
835 mEventQ->Resume();
836 return NS_OK;
839 NS_IMETHODIMP
840 HttpTransactionParent::GetLoadGroup(nsILoadGroup** aLoadGroup) {
841 MOZ_ASSERT(false, "Should not be called.");
842 return NS_ERROR_NOT_IMPLEMENTED;
845 NS_IMETHODIMP
846 HttpTransactionParent::SetLoadGroup(nsILoadGroup* aLoadGroup) {
847 MOZ_ASSERT(false, "Should not be called.");
848 return NS_ERROR_NOT_IMPLEMENTED;
851 NS_IMETHODIMP
852 HttpTransactionParent::GetLoadFlags(nsLoadFlags* aLoadFlags) {
853 MOZ_ASSERT(false, "Should not be called.");
854 return NS_ERROR_NOT_IMPLEMENTED;
857 NS_IMETHODIMP
858 HttpTransactionParent::SetLoadFlags(nsLoadFlags aLoadFlags) {
859 MOZ_ASSERT(false, "Should not be called.");
860 return NS_ERROR_NOT_IMPLEMENTED;
863 NS_IMETHODIMP
864 HttpTransactionParent::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
865 MOZ_ASSERT(false, "Should not be called.");
866 return NS_ERROR_NOT_IMPLEMENTED;
869 NS_IMETHODIMP
870 HttpTransactionParent::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
871 MOZ_ASSERT(false, "Should not be called.");
872 return NS_ERROR_NOT_IMPLEMENTED;
875 void HttpTransactionParent::ActorDestroy(ActorDestroyReason aWhy) {
876 LOG(("HttpTransactionParent::ActorDestroy [this=%p]\n", this));
877 if (aWhy != Deletion) {
878 // Make sure all the messages are processed.
879 AutoEventEnqueuer ensureSerialDispatch(mEventQ);
881 mStatus = NS_ERROR_FAILURE;
882 HandleAsyncAbort();
884 mCanceled = true;
888 void HttpTransactionParent::HandleAsyncAbort() {
889 MOZ_ASSERT(!mCallOnResume, "How did that happen?");
891 if (mSuspendCount) {
892 LOG(
893 ("HttpTransactionParent Waiting until resume to do async notification "
894 "[this=%p]\n",
895 this));
896 RefPtr<HttpTransactionParent> self = this;
897 mCallOnResume = [self]() { self->HandleAsyncAbort(); };
898 return;
901 DoNotifyListener();
904 bool HttpTransactionParent::GetSupportsHTTP3() { return mSupportsHTTP3; }
906 void HttpTransactionParent::SetIsForWebTransport(bool SetIsForWebTransport) {
907 // TODO: bug 1791727
910 mozilla::TimeStamp HttpTransactionParent::GetPendingTime() {
911 return mTimings.transactionPending;
914 } // namespace mozilla::net