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