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