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