Backed out 2 changesets (bug 1849864) for causing multiple failures. CLOSED TREE
[gecko.git] / netwerk / protocol / http / HttpChannelParent.cpp
blob635d9372c4ef88b32570df5ae38ab3ba9c797b83
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // HttpLog.h should generally be included first
8 #include "ErrorList.h"
9 #include "HttpLog.h"
11 #include "mozilla/ConsoleReportCollector.h"
12 #include "mozilla/dom/BrowsingContext.h"
13 #include "mozilla/ipc/IPCStreamUtils.h"
14 #include "mozilla/net/EarlyHintRegistrar.h"
15 #include "mozilla/net/HttpChannelParent.h"
16 #include "mozilla/dom/ContentParent.h"
17 #include "mozilla/dom/ContentProcessManager.h"
18 #include "mozilla/dom/Element.h"
19 #include "mozilla/dom/ServiceWorkerUtils.h"
20 #include "mozilla/dom/BrowserParent.h"
21 #include "mozilla/dom/WindowGlobalParent.h"
22 #include "mozilla/net/NeckoParent.h"
23 #include "mozilla/net/CookieServiceParent.h"
24 #include "mozilla/InputStreamLengthHelper.h"
25 #include "mozilla/IntegerPrintfMacros.h"
26 #include "mozilla/Preferences.h"
27 #include "mozilla/ProfilerLabels.h"
28 #include "mozilla/ProfilerMarkers.h"
29 #include "mozilla/StoragePrincipalHelper.h"
30 #include "mozilla/UniquePtr.h"
31 #include "mozilla/Unused.h"
32 #include "HttpBackgroundChannelParent.h"
33 #include "ParentChannelListener.h"
34 #include "nsDebug.h"
35 #include "nsICacheInfoChannel.h"
36 #include "nsHttpHandler.h"
37 #include "nsNetCID.h"
38 #include "nsNetUtil.h"
39 #include "nsISupportsPriority.h"
40 #include "mozilla/net/BackgroundChannelRegistrar.h"
41 #include "nsSerializationHelper.h"
42 #include "nsISerializable.h"
43 #include "mozilla/ipc/InputStreamUtils.h"
44 #include "mozilla/ipc/URIUtils.h"
45 #include "SerializedLoadContext.h"
46 #include "nsIAuthPrompt.h"
47 #include "nsIAuthPrompt2.h"
48 #include "mozilla/ipc/BackgroundUtils.h"
49 #include "mozilla/LoadInfo.h"
50 #include "nsQueryObject.h"
51 #include "mozilla/BasePrincipal.h"
52 #include "nsCORSListenerProxy.h"
53 #include "nsIIPCSerializableInputStream.h"
54 #include "nsIPrompt.h"
55 #include "nsIPromptFactory.h"
56 #include "mozilla/net/ChannelEventQueue.h"
57 #include "mozilla/net/RedirectChannelRegistrar.h"
58 #include "nsIWindowWatcher.h"
59 #include "mozilla/dom/Document.h"
60 #include "nsISecureBrowserUI.h"
61 #include "nsStreamUtils.h"
62 #include "nsStringStream.h"
63 #include "nsThreadUtils.h"
64 #include "nsQueryObject.h"
65 #include "nsIMultiPartChannel.h"
66 #include "nsIViewSourceChannel.h"
68 using namespace mozilla;
70 namespace geckoprofiler::markers {
72 struct ChannelMarker {
73 static constexpr Span<const char> MarkerTypeName() {
74 return MakeStringSpan("ChannelMarker");
76 static void StreamJSONMarkerData(
77 mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
78 const mozilla::ProfilerString8View& aURL, uint64_t aChannelId) {
79 if (aURL.Length() != 0) {
80 aWriter.StringProperty("url", aURL);
82 aWriter.IntProperty("channelId", static_cast<int64_t>(aChannelId));
84 static MarkerSchema MarkerTypeDisplay() {
85 using MS = MarkerSchema;
86 MS schema(MS::Location::MarkerChart, MS::Location::MarkerTable);
87 schema.SetTableLabel("{marker.name} - {marker.data.url}");
88 schema.AddKeyFormatSearchable("url", MS::Format::Url,
89 MS::Searchable::Searchable);
90 schema.AddStaticLabelValue(
91 "Description",
92 "Timestamp capturing various phases of a network channel's lifespan.");
93 return schema;
97 } // namespace geckoprofiler::markers
99 using mozilla::BasePrincipal;
100 using namespace mozilla::dom;
101 using namespace mozilla::ipc;
103 namespace mozilla::net {
105 HttpChannelParent::HttpChannelParent(dom::BrowserParent* iframeEmbedding,
106 nsILoadContext* aLoadContext,
107 PBOverrideStatus aOverrideStatus)
108 : mLoadContext(aLoadContext),
109 mIPCClosed(false),
110 mPBOverride(aOverrideStatus),
111 mStatus(NS_OK),
112 mIgnoreProgress(false),
113 mHasSuspendedByBackPressure(false),
114 mCacheNeedFlowControlInitialized(false),
115 mNeedFlowControl(true),
116 mSuspendedForFlowControl(false),
117 mAfterOnStartRequestBegun(false),
118 mDataSentToChildProcess(false) {
119 LOG(("Creating HttpChannelParent [this=%p]\n", this));
121 // Ensure gHttpHandler is initialized: we need the atom table up and running.
122 nsCOMPtr<nsIHttpProtocolHandler> dummyInitializer =
123 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http");
125 MOZ_ASSERT(gHttpHandler);
126 mHttpHandler = gHttpHandler;
128 mBrowserParent = iframeEmbedding;
130 mSendWindowSize = gHttpHandler->SendWindowSize();
132 mEventQ =
133 new ChannelEventQueue(static_cast<nsIParentRedirectingChannel*>(this));
136 HttpChannelParent::~HttpChannelParent() {
137 LOG(("Destroying HttpChannelParent [this=%p]\n", this));
138 CleanupBackgroundChannel();
140 MOZ_ASSERT(!mRedirectCallback);
141 if (NS_WARN_IF(mRedirectCallback)) {
142 mRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_UNEXPECTED);
143 mRedirectCallback = nullptr;
145 mEventQ->NotifyReleasingOwner();
148 void HttpChannelParent::ActorDestroy(ActorDestroyReason why) {
149 // We may still have refcount>0 if nsHttpChannel hasn't called OnStopRequest
150 // yet, but child process has crashed. We must not try to send any more msgs
151 // to child, or IPDL will kill chrome process, too.
152 mIPCClosed = true;
153 CleanupBackgroundChannel();
156 bool HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs) {
157 LOG(("HttpChannelParent::Init [this=%p]\n", this));
158 AUTO_PROFILER_LABEL("HttpChannelParent::Init", NETWORK);
159 switch (aArgs.type()) {
160 case HttpChannelCreationArgs::THttpChannelOpenArgs: {
161 const HttpChannelOpenArgs& a = aArgs.get_HttpChannelOpenArgs();
162 return DoAsyncOpen(
163 a.uri(), a.original(), a.doc(), a.referrerInfo(), a.apiRedirectTo(),
164 a.topWindowURI(), a.loadFlags(), a.requestHeaders(),
165 a.requestMethod(), a.uploadStream(), a.uploadStreamHasHeaders(),
166 a.priority(), a.classOfService(), a.redirectionLimit(), a.allowSTS(),
167 a.thirdPartyFlags(), a.resumeAt(), a.startPos(), a.entityID(),
168 a.allowSpdy(), a.allowHttp3(), a.allowAltSvc(), a.beConservative(),
169 a.bypassProxy(), a.tlsFlags(), a.loadInfo(), a.cacheKey(),
170 a.requestContextID(), a.preflightArgs(), a.initialRwin(),
171 a.blockAuthPrompt(), a.allowStaleCacheContent(),
172 a.preferCacheLoadOverBypass(), a.contentTypeHint(), a.requestMode(),
173 a.redirectMode(), a.channelId(), a.integrityMetadata(),
174 a.contentWindowId(), a.preferredAlternativeTypes(), a.browserId(),
175 a.launchServiceWorkerStart(), a.launchServiceWorkerEnd(),
176 a.dispatchFetchEventStart(), a.dispatchFetchEventEnd(),
177 a.handleFetchEventStart(), a.handleFetchEventEnd(),
178 a.forceMainDocumentChannel(), a.navigationStartTimeStamp(),
179 a.earlyHintPreloaderId(), a.classicScriptHintCharset(),
180 a.documentCharacterSet());
182 case HttpChannelCreationArgs::THttpChannelConnectArgs: {
183 const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs();
184 return ConnectChannel(cArgs.registrarId());
186 default:
187 MOZ_ASSERT_UNREACHABLE("unknown open type");
188 return false;
192 void HttpChannelParent::TryInvokeAsyncOpen(nsresult aRv) {
193 LOG(("HttpChannelParent::TryInvokeAsyncOpen [this=%p barrier=%u rv=%" PRIx32
194 "]\n",
195 this, mAsyncOpenBarrier, static_cast<uint32_t>(aRv)));
196 MOZ_ASSERT(NS_IsMainThread());
197 AUTO_PROFILER_LABEL("HttpChannelParent::TryInvokeAsyncOpen", NETWORK);
199 // TryInvokeAsyncOpen is called more than we expected.
200 // Assert in nightly build but ignore it in release channel.
201 MOZ_DIAGNOSTIC_ASSERT(mAsyncOpenBarrier > 0);
202 if (NS_WARN_IF(!mAsyncOpenBarrier)) {
203 return;
206 if (--mAsyncOpenBarrier > 0 && NS_SUCCEEDED(aRv)) {
207 // Need to wait for more events.
208 return;
211 InvokeAsyncOpen(aRv);
214 void HttpChannelParent::OnBackgroundParentReady(
215 HttpBackgroundChannelParent* aBgParent) {
216 LOG(("HttpChannelParent::OnBackgroundParentReady [this=%p bgParent=%p]\n",
217 this, aBgParent));
218 MOZ_ASSERT(NS_IsMainThread());
219 MOZ_ASSERT(!mBgParent);
221 mBgParent = aBgParent;
223 mPromise.ResolveIfExists(true, __func__);
226 void HttpChannelParent::OnBackgroundParentDestroyed() {
227 LOG(("HttpChannelParent::OnBackgroundParentDestroyed [this=%p]\n", this));
228 MOZ_ASSERT(NS_IsMainThread());
230 if (!mPromise.IsEmpty()) {
231 MOZ_ASSERT(!mBgParent);
232 mPromise.Reject(NS_ERROR_FAILURE, __func__);
233 return;
236 if (!mBgParent) {
237 return;
240 // Background channel is closed unexpectly, abort PHttpChannel operation.
241 mBgParent = nullptr;
242 Delete();
245 void HttpChannelParent::CleanupBackgroundChannel() {
246 LOG(("HttpChannelParent::CleanupBackgroundChannel [this=%p bgParent=%p]\n",
247 this, mBgParent.get()));
248 MOZ_ASSERT(NS_IsMainThread());
250 if (mBgParent) {
251 RefPtr<HttpBackgroundChannelParent> bgParent = std::move(mBgParent);
252 bgParent->OnChannelClosed();
253 return;
256 // The nsHttpChannel may have a reference to this parent, release it
257 // to avoid circular references.
258 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
259 if (httpChannelImpl) {
260 httpChannelImpl->SetWarningReporter(nullptr);
263 if (!mPromise.IsEmpty()) {
264 mRequest.DisconnectIfExists();
265 mPromise.Reject(NS_ERROR_FAILURE, __func__);
267 if (!mChannel) {
268 return;
271 // This HttpChannelParent might still have a reference from
272 // BackgroundChannelRegistrar.
273 nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
274 BackgroundChannelRegistrar::GetOrCreate();
275 MOZ_ASSERT(registrar);
277 registrar->DeleteChannel(mChannel->ChannelId());
279 // If mAsyncOpenBarrier is greater than zero, it means AsyncOpen procedure
280 // is still on going. we need to abort AsyncOpen with failure to destroy
281 // PHttpChannel actor.
282 if (mAsyncOpenBarrier) {
283 TryInvokeAsyncOpen(NS_ERROR_FAILURE);
288 base::ProcessId HttpChannelParent::OtherPid() const {
289 if (mIPCClosed) {
290 return 0;
292 return PHttpChannelParent::OtherPid();
295 //-----------------------------------------------------------------------------
296 // HttpChannelParent::nsISupports
297 //-----------------------------------------------------------------------------
299 NS_IMPL_ADDREF(HttpChannelParent)
300 NS_IMPL_RELEASE(HttpChannelParent)
301 NS_INTERFACE_MAP_BEGIN(HttpChannelParent)
302 NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
303 NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
304 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
305 NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
306 NS_INTERFACE_MAP_ENTRY(nsIParentChannel)
307 NS_INTERFACE_MAP_ENTRY(nsIParentRedirectingChannel)
308 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectReadyCallback)
309 NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
310 NS_INTERFACE_MAP_ENTRY(nsIRedirectResultListener)
311 NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannelListener)
312 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIParentRedirectingChannel)
313 NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpChannelParent)
314 NS_INTERFACE_MAP_END
316 //-----------------------------------------------------------------------------
317 // HttpChannelParent::nsIInterfaceRequestor
318 //-----------------------------------------------------------------------------
320 NS_IMETHODIMP
321 HttpChannelParent::GetInterface(const nsIID& aIID, void** result) {
322 // A system XHR can be created without reference to a window, hence mTabParent
323 // may be null. In that case we want to let the window watcher pick a prompt
324 // directly.
325 if (!mBrowserParent && (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
326 aIID.Equals(NS_GET_IID(nsIAuthPrompt2)))) {
327 nsresult rv;
328 nsCOMPtr<nsIWindowWatcher> wwatch =
329 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
330 NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
332 bool hasWindowCreator = false;
333 Unused << wwatch->HasWindowCreator(&hasWindowCreator);
334 if (!hasWindowCreator) {
335 return NS_ERROR_NO_INTERFACE;
338 nsCOMPtr<nsIPromptFactory> factory = do_QueryInterface(wwatch);
339 if (!factory) {
340 return NS_ERROR_NO_INTERFACE;
342 rv = factory->GetPrompt(nullptr, aIID, reinterpret_cast<void**>(result));
343 if (NS_FAILED(rv)) {
344 return NS_ERROR_NO_INTERFACE;
346 return NS_OK;
349 // Only support nsILoadContext if child channel's callbacks did too
350 if (aIID.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) {
351 nsCOMPtr<nsILoadContext> copy = mLoadContext;
352 copy.forget(result);
353 return NS_OK;
356 return QueryInterface(aIID, result);
359 //-----------------------------------------------------------------------------
360 // HttpChannelParent::PHttpChannelParent
361 //-----------------------------------------------------------------------------
363 void HttpChannelParent::AsyncOpenFailed(nsresult aRv) {
364 MOZ_ASSERT(NS_IsMainThread());
365 MOZ_ASSERT(NS_FAILED(aRv));
367 // Break the reference cycle among HttpChannelParent,
368 // ParentChannelListener, and nsHttpChannel to avoid memory leakage.
369 mChannel = nullptr;
370 mParentListener = nullptr;
372 if (!mIPCClosed) {
373 Unused << SendFailedAsyncOpen(aRv);
377 void HttpChannelParent::InvokeAsyncOpen(nsresult rv) {
378 LOG(("HttpChannelParent::InvokeAsyncOpen [this=%p rv=%" PRIx32 "]\n", this,
379 static_cast<uint32_t>(rv)));
380 MOZ_ASSERT(NS_IsMainThread());
382 if (NS_FAILED(rv)) {
383 AsyncOpenFailed(rv);
384 return;
387 rv = mChannel->AsyncOpen(mParentListener);
388 if (NS_FAILED(rv)) {
389 AsyncOpenFailed(rv);
393 void HttpChannelParent::InvokeEarlyHintPreloader(
394 nsresult rv, uint64_t aEarlyHintPreloaderId) {
395 LOG(("HttpChannelParent::InvokeEarlyHintPreloader [this=%p rv=%" PRIx32 "]\n",
396 this, static_cast<uint32_t>(rv)));
397 MOZ_ASSERT(NS_IsMainThread());
399 ContentParentId cpId =
400 static_cast<ContentParent*>(Manager()->Manager())->ChildID();
402 RefPtr<EarlyHintRegistrar> ehr = EarlyHintRegistrar::GetOrCreate();
403 if (NS_SUCCEEDED(rv)) {
404 rv = ehr->LinkParentChannel(cpId, aEarlyHintPreloaderId, this)
405 ? NS_OK
406 : NS_ERROR_FAILURE;
409 if (NS_FAILED(rv)) {
410 ehr->DeleteEntry(cpId, aEarlyHintPreloaderId);
411 AsyncOpenFailed(NS_ERROR_FAILURE);
415 bool HttpChannelParent::DoAsyncOpen(
416 nsIURI* aURI, nsIURI* aOriginalURI, nsIURI* aDocURI,
417 nsIReferrerInfo* aReferrerInfo, nsIURI* aAPIRedirectToURI,
418 nsIURI* aTopWindowURI, const uint32_t& aLoadFlags,
419 const RequestHeaderTuples& requestHeaders, const nsCString& requestMethod,
420 const Maybe<IPCStream>& uploadStream, const bool& uploadStreamHasHeaders,
421 const int16_t& priority, const ClassOfService& classOfService,
422 const uint8_t& redirectionLimit, const bool& allowSTS,
423 const uint32_t& thirdPartyFlags, const bool& doResumeAt,
424 const uint64_t& startPos, const nsCString& entityID, const bool& allowSpdy,
425 const bool& allowHttp3, const bool& allowAltSvc, const bool& beConservative,
426 const bool& bypassProxy, const uint32_t& tlsFlags,
427 const LoadInfoArgs& aLoadInfoArgs, const uint32_t& aCacheKey,
428 const uint64_t& aRequestContextID,
429 const Maybe<CorsPreflightArgs>& aCorsPreflightArgs,
430 const uint32_t& aInitialRwin, const bool& aBlockAuthPrompt,
431 const bool& aAllowStaleCacheContent, const bool& aPreferCacheLoadOverBypass,
432 const nsCString& aContentTypeHint, const dom::RequestMode& aRequestMode,
433 const uint32_t& aRedirectMode, const uint64_t& aChannelId,
434 const nsString& aIntegrityMetadata, const uint64_t& aContentWindowId,
435 const nsTArray<PreferredAlternativeDataTypeParams>&
436 aPreferredAlternativeTypes,
437 const uint64_t& aBrowserId, const TimeStamp& aLaunchServiceWorkerStart,
438 const TimeStamp& aLaunchServiceWorkerEnd,
439 const TimeStamp& aDispatchFetchEventStart,
440 const TimeStamp& aDispatchFetchEventEnd,
441 const TimeStamp& aHandleFetchEventStart,
442 const TimeStamp& aHandleFetchEventEnd,
443 const bool& aForceMainDocumentChannel,
444 const TimeStamp& aNavigationStartTimeStamp,
445 const uint64_t& aEarlyHintPreloaderId,
446 const nsAString& aClassicScriptHintCharset,
447 const nsAString& aDocumentCharacterSet) {
448 MOZ_ASSERT(aURI, "aURI should not be NULL");
450 if (aEarlyHintPreloaderId) {
451 // Wait for HttpBackgrounChannel to continue the async open procedure.
452 mEarlyHintPreloaderId = aEarlyHintPreloaderId;
453 RefPtr<HttpChannelParent> self = this;
454 WaitForBgParent(aChannelId)
455 ->Then(
456 GetMainThreadSerialEventTarget(), __func__,
457 [self, aEarlyHintPreloaderId]() {
458 self->mRequest.Complete();
459 self->InvokeEarlyHintPreloader(NS_OK, aEarlyHintPreloaderId);
461 [self, aEarlyHintPreloaderId](nsresult aStatus) {
462 self->mRequest.Complete();
463 self->InvokeEarlyHintPreloader(aStatus, aEarlyHintPreloaderId);
465 ->Track(mRequest);
466 return true;
469 if (!aURI) {
470 // this check is neccessary to prevent null deref
471 // in opt builds
472 return false;
475 LOG(("HttpChannelParent RecvAsyncOpen [this=%p uri=%s, gid=%" PRIu64
476 " browserid=%" PRIx64 "]\n",
477 this, aURI->GetSpecOrDefault().get(), aChannelId, aBrowserId));
479 PROFILER_MARKER("Receive AsyncOpen in Parent", NETWORK, {}, ChannelMarker,
480 aURI->GetSpecOrDefault(), aChannelId);
482 nsresult rv;
484 nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
485 if (NS_FAILED(rv)) {
486 return SendFailedAsyncOpen(rv);
489 nsAutoCString remoteType;
490 rv = GetRemoteType(remoteType);
491 if (NS_FAILED(rv)) {
492 return SendFailedAsyncOpen(rv);
495 nsCOMPtr<nsILoadInfo> loadInfo;
496 rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfoArgs, remoteType,
497 getter_AddRefs(loadInfo));
498 if (NS_FAILED(rv)) {
499 return SendFailedAsyncOpen(rv);
502 nsCOMPtr<nsIChannel> channel;
503 rv = NS_NewChannelInternal(getter_AddRefs(channel), aURI, loadInfo, nullptr,
504 nullptr, nullptr, aLoadFlags, ios);
505 if (NS_FAILED(rv)) {
506 return SendFailedAsyncOpen(rv);
509 RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(channel, &rv);
510 if (NS_FAILED(rv)) {
511 return SendFailedAsyncOpen(rv);
514 // Set attributes needed to create a FetchEvent from this channel.
515 httpChannel->SetRequestMode(aRequestMode);
516 httpChannel->SetRedirectMode(aRedirectMode);
518 // Set the channelId allocated in child to the parent instance
519 httpChannel->SetChannelId(aChannelId);
520 httpChannel->SetTopLevelContentWindowId(aContentWindowId);
521 httpChannel->SetBrowserId(aBrowserId);
523 httpChannel->SetIntegrityMetadata(aIntegrityMetadata);
525 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(httpChannel);
526 if (httpChannelImpl) {
527 httpChannelImpl->SetWarningReporter(this);
529 httpChannel->SetTimingEnabled(true);
530 if (mPBOverride != kPBOverride_Unset) {
531 httpChannel->SetPrivate(mPBOverride == kPBOverride_Private);
534 if (doResumeAt) httpChannel->ResumeAt(startPos, entityID);
536 if (aOriginalURI) {
537 httpChannel->SetOriginalURI(aOriginalURI);
540 if (aDocURI) {
541 httpChannel->SetDocumentURI(aDocURI);
544 if (aReferrerInfo) {
545 // Referrer header is computed in child no need to recompute here
546 rv =
547 httpChannel->SetReferrerInfoInternal(aReferrerInfo, false, false, true);
548 MOZ_ASSERT(NS_SUCCEEDED(rv));
551 httpChannel->SetClassicScriptHintCharset(aClassicScriptHintCharset);
552 httpChannel->SetDocumentCharacterSet(aDocumentCharacterSet);
554 if (aAPIRedirectToURI) {
555 httpChannel->RedirectTo(aAPIRedirectToURI);
558 if (aTopWindowURI) {
559 httpChannel->SetTopWindowURI(aTopWindowURI);
562 if (aLoadFlags != nsIRequest::LOAD_NORMAL) {
563 httpChannel->SetLoadFlags(aLoadFlags);
566 if (aForceMainDocumentChannel) {
567 httpChannel->SetIsMainDocumentChannel(true);
570 for (uint32_t i = 0; i < requestHeaders.Length(); i++) {
571 if (requestHeaders[i].mEmpty) {
572 httpChannel->SetEmptyRequestHeader(requestHeaders[i].mHeader);
573 } else {
574 httpChannel->SetRequestHeader(requestHeaders[i].mHeader,
575 requestHeaders[i].mValue,
576 requestHeaders[i].mMerge);
580 RefPtr<ParentChannelListener> parentListener = new ParentChannelListener(
581 this, mBrowserParent ? mBrowserParent->GetBrowsingContext() : nullptr);
583 httpChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
585 if (aCorsPreflightArgs.isSome()) {
586 const CorsPreflightArgs& args = aCorsPreflightArgs.ref();
587 httpChannel->SetCorsPreflightParameters(args.unsafeHeaders(), false);
590 nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(uploadStream);
591 if (stream) {
592 rv = httpChannel->InternalSetUploadStream(stream);
593 if (NS_FAILED(rv)) {
594 return SendFailedAsyncOpen(rv);
597 httpChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
600 nsCOMPtr<nsICacheInfoChannel> cacheChannel =
601 do_QueryInterface(static_cast<nsIChannel*>(httpChannel.get()));
602 if (cacheChannel) {
603 cacheChannel->SetCacheKey(aCacheKey);
604 for (const auto& data : aPreferredAlternativeTypes) {
605 cacheChannel->PreferAlternativeDataType(data.type(), data.contentType(),
606 data.deliverAltData());
609 cacheChannel->SetAllowStaleCacheContent(aAllowStaleCacheContent);
610 cacheChannel->SetPreferCacheLoadOverBypass(aPreferCacheLoadOverBypass);
612 // This is to mark that the results are going to the content process.
613 if (httpChannelImpl) {
614 httpChannelImpl->SetAltDataForChild(true);
618 httpChannel->SetContentType(aContentTypeHint);
620 if (priority != nsISupportsPriority::PRIORITY_NORMAL) {
621 httpChannel->SetPriority(priority);
623 if (classOfService.Flags() || classOfService.Incremental()) {
624 httpChannel->SetClassOfService(classOfService);
626 httpChannel->SetRedirectionLimit(redirectionLimit);
627 httpChannel->SetAllowSTS(allowSTS);
628 httpChannel->SetThirdPartyFlags(thirdPartyFlags);
629 httpChannel->SetAllowSpdy(allowSpdy);
630 httpChannel->SetAllowHttp3(allowHttp3);
631 httpChannel->SetAllowAltSvc(allowAltSvc);
632 httpChannel->SetBeConservative(beConservative);
633 httpChannel->SetTlsFlags(tlsFlags);
634 httpChannel->SetInitialRwin(aInitialRwin);
635 httpChannel->SetBlockAuthPrompt(aBlockAuthPrompt);
637 httpChannel->SetLaunchServiceWorkerStart(aLaunchServiceWorkerStart);
638 httpChannel->SetLaunchServiceWorkerEnd(aLaunchServiceWorkerEnd);
639 httpChannel->SetDispatchFetchEventStart(aDispatchFetchEventStart);
640 httpChannel->SetDispatchFetchEventEnd(aDispatchFetchEventEnd);
641 httpChannel->SetHandleFetchEventStart(aHandleFetchEventStart);
642 httpChannel->SetHandleFetchEventEnd(aHandleFetchEventEnd);
644 httpChannel->SetNavigationStartTimeStamp(aNavigationStartTimeStamp);
645 httpChannel->SetRequestContextID(aRequestContextID);
647 // Store the strong reference of channel and parent listener object until
648 // all the initialization procedure is complete without failure, to remove
649 // cycle reference in fail case and to avoid memory leakage.
650 mChannel = std::move(httpChannel);
651 mParentListener = std::move(parentListener);
652 mChannel->SetNotificationCallbacks(mParentListener);
654 MOZ_ASSERT(!mBgParent);
655 MOZ_ASSERT(mPromise.IsEmpty());
656 // Wait for HttpBackgrounChannel to continue the async open procedure.
657 ++mAsyncOpenBarrier;
658 RefPtr<HttpChannelParent> self = this;
659 WaitForBgParent(mChannel->ChannelId())
660 ->Then(
661 GetMainThreadSerialEventTarget(), __func__,
662 [self]() {
663 self->mRequest.Complete();
664 self->TryInvokeAsyncOpen(NS_OK);
666 [self](nsresult aStatus) {
667 self->mRequest.Complete();
668 self->TryInvokeAsyncOpen(aStatus);
670 ->Track(mRequest);
671 return true;
674 RefPtr<GenericNonExclusivePromise> HttpChannelParent::WaitForBgParent(
675 uint64_t aChannelId) {
676 LOG(("HttpChannelParent::WaitForBgParent [this=%p]\n", this));
677 MOZ_ASSERT(!mBgParent);
679 if (!mChannel && !mEarlyHintPreloaderId) {
680 return GenericNonExclusivePromise::CreateAndReject(NS_ERROR_FAILURE,
681 __func__);
684 nsCOMPtr<nsIBackgroundChannelRegistrar> registrar =
685 BackgroundChannelRegistrar::GetOrCreate();
686 MOZ_ASSERT(registrar);
687 registrar->LinkHttpChannel(aChannelId, this);
689 if (mBgParent) {
690 return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
693 return mPromise.Ensure(__func__);
696 bool HttpChannelParent::ConnectChannel(const uint32_t& registrarId) {
697 nsresult rv;
699 LOG(
700 ("HttpChannelParent::ConnectChannel: Looking for a registered channel "
701 "[this=%p, id=%" PRIu32 "]\n",
702 this, registrarId));
703 nsCOMPtr<nsIChannel> channel;
704 rv = NS_LinkRedirectChannels(registrarId, this, getter_AddRefs(channel));
705 if (NS_FAILED(rv)) {
706 NS_WARNING("Could not find the http channel to connect its IPC parent");
707 // This makes the channel delete itself safely. It's the only thing
708 // we can do now, since this parent channel cannot be used and there is
709 // no other way to tell the child side there were something wrong.
710 Delete();
711 return true;
714 LOG((" found channel %p, rv=%08" PRIx32, channel.get(),
715 static_cast<uint32_t>(rv)));
716 mChannel = do_QueryObject(channel);
717 if (!mChannel) {
718 LOG((" but it's not HttpBaseChannel"));
719 Delete();
720 return true;
723 LOG((" and it is HttpBaseChannel %p", mChannel.get()));
725 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
726 if (httpChannelImpl) {
727 httpChannelImpl->SetWarningReporter(this);
730 if (mPBOverride != kPBOverride_Unset) {
731 // redirected-to channel may not support PB
732 nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel);
733 if (pbChannel) {
734 pbChannel->SetPrivate(mPBOverride == kPBOverride_Private);
738 MOZ_ASSERT(!mBgParent);
739 MOZ_ASSERT(mPromise.IsEmpty());
740 // Waiting for background channel
741 RefPtr<HttpChannelParent> self = this;
742 WaitForBgParent(mChannel->ChannelId())
743 ->Then(
744 GetMainThreadSerialEventTarget(), __func__,
745 [self]() { self->mRequest.Complete(); },
746 [self](const nsresult& aResult) {
747 NS_ERROR("failed to establish the background channel");
748 self->mRequest.Complete();
750 ->Track(mRequest);
751 return true;
754 mozilla::ipc::IPCResult HttpChannelParent::RecvSetPriority(
755 const int16_t& priority) {
756 LOG(("HttpChannelParent::RecvSetPriority [this=%p, priority=%d]\n", this,
757 priority));
758 AUTO_PROFILER_LABEL("HttpChannelParent::RecvSetPriority", NETWORK);
760 if (mChannel) {
761 mChannel->SetPriority(priority);
764 nsCOMPtr<nsISupportsPriority> priorityRedirectChannel =
765 do_QueryInterface(mRedirectChannel);
766 if (priorityRedirectChannel) priorityRedirectChannel->SetPriority(priority);
768 return IPC_OK();
771 mozilla::ipc::IPCResult HttpChannelParent::RecvSetClassOfService(
772 const ClassOfService& cos) {
773 if (mChannel) {
774 mChannel->SetClassOfService(cos);
776 return IPC_OK();
779 mozilla::ipc::IPCResult HttpChannelParent::RecvSuspend() {
780 LOG(("HttpChannelParent::RecvSuspend [this=%p]\n", this));
782 if (mChannel) {
783 mChannel->Suspend();
785 return IPC_OK();
788 mozilla::ipc::IPCResult HttpChannelParent::RecvResume() {
789 LOG(("HttpChannelParent::RecvResume [this=%p]\n", this));
791 if (mChannel) {
792 mChannel->Resume();
794 return IPC_OK();
797 mozilla::ipc::IPCResult HttpChannelParent::RecvCancel(
798 const nsresult& status, const uint32_t& requestBlockingReason,
799 const nsACString& reason, const mozilla::Maybe<nsCString>& logString) {
800 LOG(("HttpChannelParent::RecvCancel [this=%p, reason=%s]\n", this,
801 PromiseFlatCString(reason).get()));
803 // logging child cancel reason on the parent side
804 if (logString.isSome()) {
805 LOG(("HttpChannelParent::RecvCancel: %s", logString->get()));
808 // May receive cancel before channel has been constructed!
809 if (mChannel) {
810 mChannel->CancelWithReason(status, reason);
812 if (MOZ_UNLIKELY(requestBlockingReason !=
813 nsILoadInfo::BLOCKING_REASON_NONE)) {
814 nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
815 loadInfo->SetRequestBlockingReason(requestBlockingReason);
818 // Once we receive |Cancel|, child will stop sending RecvBytesRead. Force
819 // the channel resumed if needed.
820 if (mSuspendedForFlowControl) {
821 LOG((" resume the channel due to e10s backpressure relief by cancel"));
822 Unused << mChannel->Resume();
823 mSuspendedForFlowControl = false;
825 } else if (!mIPCClosed) {
826 // Make sure that the child correctly delivers all stream listener
827 // notifications.
828 Unused << SendFailedAsyncOpen(status);
831 // We won't need flow control anymore. Toggle the flag to avoid |Suspend|
832 // since OnDataAvailable could be off-main-thread.
833 mCacheNeedFlowControlInitialized = true;
834 mNeedFlowControl = false;
836 // If the channel is cancelled before the redirect is completed
837 // RecvRedirect2Verify will not be called, so we must clear the callback.
838 if (mRedirectCallback) {
839 mRedirectCallback->OnRedirectVerifyCallback(NS_ERROR_UNEXPECTED);
840 mRedirectCallback = nullptr;
843 return IPC_OK();
846 mozilla::ipc::IPCResult HttpChannelParent::RecvRedirect2Verify(
847 const nsresult& aResult, const RequestHeaderTuples& changedHeaders,
848 const uint32_t& aSourceRequestBlockingReason,
849 const Maybe<ChildLoadInfoForwarderArgs>& aTargetLoadInfoForwarder,
850 const uint32_t& loadFlags, nsIReferrerInfo* aReferrerInfo,
851 nsIURI* aAPIRedirectURI,
852 const Maybe<CorsPreflightArgs>& aCorsPreflightArgs) {
853 LOG(("HttpChannelParent::RecvRedirect2Verify [this=%p result=%" PRIx32 "]\n",
854 this, static_cast<uint32_t>(aResult)));
856 // Result from the child. If something fails here, we might overwrite a
857 // success with a further failure.
858 nsresult result = aResult;
860 // Local results.
861 nsresult rv;
863 if (NS_SUCCEEDED(result)) {
864 nsCOMPtr<nsIHttpChannel> newHttpChannel =
865 do_QueryInterface(mRedirectChannel);
867 if (newHttpChannel) {
868 if (aAPIRedirectURI) {
869 rv = newHttpChannel->RedirectTo(aAPIRedirectURI);
870 MOZ_ASSERT(NS_SUCCEEDED(rv));
873 for (uint32_t i = 0; i < changedHeaders.Length(); i++) {
874 if (changedHeaders[i].mEmpty) {
875 rv = newHttpChannel->SetEmptyRequestHeader(changedHeaders[i].mHeader);
876 } else {
877 rv = newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader,
878 changedHeaders[i].mValue,
879 changedHeaders[i].mMerge);
881 MOZ_ASSERT(NS_SUCCEEDED(rv));
884 // A successfully redirected channel must have the LOAD_REPLACE flag.
885 MOZ_ASSERT(loadFlags & nsIChannel::LOAD_REPLACE);
886 if (loadFlags & nsIChannel::LOAD_REPLACE) {
887 newHttpChannel->SetLoadFlags(loadFlags);
890 if (aCorsPreflightArgs.isSome()) {
891 nsCOMPtr<nsIHttpChannelInternal> newInternalChannel =
892 do_QueryInterface(newHttpChannel);
893 MOZ_RELEASE_ASSERT(newInternalChannel);
894 const CorsPreflightArgs& args = aCorsPreflightArgs.ref();
895 newInternalChannel->SetCorsPreflightParameters(args.unsafeHeaders(),
896 false);
899 if (aReferrerInfo) {
900 RefPtr<HttpBaseChannel> baseChannel = do_QueryObject(newHttpChannel);
901 MOZ_ASSERT(baseChannel);
902 if (baseChannel) {
903 // Referrer header is computed in child no need to recompute here
904 rv = baseChannel->SetReferrerInfoInternal(aReferrerInfo, false, false,
905 true);
906 MOZ_ASSERT(NS_SUCCEEDED(rv));
910 if (aTargetLoadInfoForwarder.isSome()) {
911 nsCOMPtr<nsILoadInfo> newLoadInfo = newHttpChannel->LoadInfo();
912 rv = MergeChildLoadInfoForwarder(aTargetLoadInfoForwarder.ref(),
913 newLoadInfo);
914 if (NS_FAILED(rv) && NS_SUCCEEDED(result)) {
915 result = rv;
921 // If the redirect is vetoed, reason is set on the source (current) channel's
922 // load info, so we must carry iver the change.
923 // The channel may have already been cleaned up, so there is nothing we can
924 // do.
925 if (MOZ_UNLIKELY(aSourceRequestBlockingReason !=
926 nsILoadInfo::BLOCKING_REASON_NONE) &&
927 mChannel) {
928 nsCOMPtr<nsILoadInfo> sourceLoadInfo = mChannel->LoadInfo();
929 sourceLoadInfo->SetRequestBlockingReason(aSourceRequestBlockingReason);
932 // Continue the verification procedure if child has veto the redirection.
933 if (NS_FAILED(result)) {
934 ContinueRedirect2Verify(result);
935 return IPC_OK();
938 // Wait for background channel ready on target channel
939 nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
940 RedirectChannelRegistrar::GetOrCreate();
941 MOZ_ASSERT(redirectReg);
943 nsCOMPtr<nsIParentChannel> redirectParentChannel;
944 rv = redirectReg->GetParentChannel(mRedirectChannelId,
945 getter_AddRefs(redirectParentChannel));
946 if (!redirectParentChannel) {
947 ContinueRedirect2Verify(rv);
948 return IPC_OK();
951 nsCOMPtr<nsIParentRedirectingChannel> redirectedParent =
952 do_QueryInterface(redirectParentChannel);
953 if (!redirectedParent) {
954 // Continue verification procedure if redirecting to non-Http protocol
955 ContinueRedirect2Verify(result);
956 return IPC_OK();
959 // Ask redirected channel if verification can proceed.
960 // ContinueRedirect2Verify will be invoked when redirected channel is ready.
961 redirectedParent->ContinueVerification(this);
963 return IPC_OK();
966 // from nsIParentRedirectingChannel
967 NS_IMETHODIMP
968 HttpChannelParent::ContinueVerification(
969 nsIAsyncVerifyRedirectReadyCallback* aCallback) {
970 LOG(("HttpChannelParent::ContinueVerification [this=%p callback=%p]\n", this,
971 aCallback));
973 MOZ_ASSERT(NS_IsMainThread());
974 MOZ_ASSERT(aCallback);
976 // Continue the verification procedure if background channel is ready.
977 if (mBgParent) {
978 aCallback->ReadyToVerify(NS_OK);
979 return NS_OK;
982 // ConnectChannel must be received before Redirect2Verify.
983 MOZ_ASSERT(!mPromise.IsEmpty());
985 // Otherwise, wait for the background channel.
986 nsCOMPtr<nsIAsyncVerifyRedirectReadyCallback> callback = aCallback;
987 if (mChannel) {
988 WaitForBgParent(mChannel->ChannelId())
989 ->Then(
990 GetMainThreadSerialEventTarget(), __func__,
991 [callback]() { callback->ReadyToVerify(NS_OK); },
992 [callback](const nsresult& aResult) {
993 NS_ERROR("failed to establish the background channel");
994 callback->ReadyToVerify(aResult);
996 } else {
997 // mChannel can be null for several reasons (AsyncOpenFailed, etc)
998 NS_ERROR("No channel for ContinueVerification");
999 GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
1000 __func__, [callback] { callback->ReadyToVerify(NS_ERROR_FAILURE); }));
1002 return NS_OK;
1005 void HttpChannelParent::ContinueRedirect2Verify(const nsresult& aResult) {
1006 LOG(
1007 ("HttpChannelParent::ContinueRedirect2Verify "
1008 "[this=%p result=%" PRIx32 "]\n",
1009 this, static_cast<uint32_t>(aResult)));
1011 if (mRedirectCallback) {
1012 LOG(
1013 ("HttpChannelParent::ContinueRedirect2Verify call "
1014 "OnRedirectVerifyCallback"
1015 " [this=%p result=%" PRIx32 ", mRedirectCallback=%p]\n",
1016 this, static_cast<uint32_t>(aResult), mRedirectCallback.get()));
1017 mRedirectCallback->OnRedirectVerifyCallback(aResult);
1018 mRedirectCallback = nullptr;
1019 } else {
1020 LOG(
1021 ("RecvRedirect2Verify[%p]: NO CALLBACKS! | "
1022 "mRedirectChannelId: %" PRIx64 ", mRedirectChannel: %p",
1023 this, mRedirectChannelId, mRedirectChannel.get()));
1027 mozilla::ipc::IPCResult HttpChannelParent::RecvDocumentChannelCleanup(
1028 const bool& clearCacheEntry) {
1029 CleanupBackgroundChannel(); // Background channel can be closed.
1030 mChannel = nullptr; // Reclaim some memory sooner.
1031 if (clearCacheEntry) {
1032 mCacheEntry = nullptr; // Else we'll block other channels reading same URI
1034 return IPC_OK();
1037 mozilla::ipc::IPCResult HttpChannelParent::RecvRemoveCorsPreflightCacheEntry(
1038 nsIURI* uri, const mozilla::ipc::PrincipalInfo& requestingPrincipal,
1039 const OriginAttributes& originAttributes) {
1040 if (!uri) {
1041 return IPC_FAIL_NO_REASON(this);
1043 auto principalOrErr = PrincipalInfoToPrincipal(requestingPrincipal);
1044 if (NS_WARN_IF(principalOrErr.isErr())) {
1045 return IPC_FAIL_NO_REASON(this);
1047 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
1048 nsCORSListenerProxy::RemoveFromCorsPreflightCache(uri, principal,
1049 originAttributes);
1050 return IPC_OK();
1053 mozilla::ipc::IPCResult HttpChannelParent::RecvSetCookies(
1054 const nsACString& aBaseDomain, const OriginAttributes& aOriginAttributes,
1055 nsIURI* aHost, const bool& aFromHttp, nsTArray<CookieStruct>&& aCookies) {
1056 net::PCookieServiceParent* csParent =
1057 LoneManagedOrNullAsserts(Manager()->ManagedPCookieServiceParent());
1058 NS_ENSURE_TRUE(csParent, IPC_OK());
1060 auto* cs = static_cast<net::CookieServiceParent*>(csParent);
1062 BrowsingContext* browsingContext = nullptr;
1063 if (mBrowserParent) {
1064 browsingContext = mBrowserParent->GetBrowsingContext();
1067 return cs->SetCookies(nsCString(aBaseDomain), aOriginAttributes, aHost,
1068 aFromHttp, aCookies, browsingContext);
1071 //-----------------------------------------------------------------------------
1072 // HttpChannelParent::nsIRequestObserver
1073 //-----------------------------------------------------------------------------
1075 static ResourceTimingStructArgs GetTimingAttributes(HttpBaseChannel* aChannel) {
1076 ResourceTimingStructArgs args;
1077 TimeStamp timeStamp;
1078 aChannel->GetDomainLookupStart(&timeStamp);
1079 args.domainLookupStart() = timeStamp;
1080 aChannel->GetDomainLookupEnd(&timeStamp);
1081 args.domainLookupEnd() = timeStamp;
1082 aChannel->GetConnectStart(&timeStamp);
1083 args.connectStart() = timeStamp;
1084 aChannel->GetTcpConnectEnd(&timeStamp);
1085 args.tcpConnectEnd() = timeStamp;
1086 aChannel->GetSecureConnectionStart(&timeStamp);
1087 args.secureConnectionStart() = timeStamp;
1088 aChannel->GetConnectEnd(&timeStamp);
1089 args.connectEnd() = timeStamp;
1090 aChannel->GetRequestStart(&timeStamp);
1091 args.requestStart() = timeStamp;
1092 aChannel->GetResponseStart(&timeStamp);
1093 args.responseStart() = timeStamp;
1094 aChannel->GetResponseEnd(&timeStamp);
1095 args.responseEnd() = timeStamp;
1096 aChannel->GetAsyncOpen(&timeStamp);
1097 args.fetchStart() = timeStamp;
1098 aChannel->GetRedirectStart(&timeStamp);
1099 args.redirectStart() = timeStamp;
1100 aChannel->GetRedirectEnd(&timeStamp);
1101 args.redirectEnd() = timeStamp;
1103 uint64_t size = 0;
1104 aChannel->GetTransferSize(&size);
1105 args.transferSize() = size;
1107 aChannel->GetEncodedBodySize(&size);
1108 args.encodedBodySize() = size;
1109 // decodedBodySize can be computed in the child process so it doesn't need
1110 // to be passed down.
1112 aChannel->GetCacheReadStart(&timeStamp);
1113 args.cacheReadStart() = timeStamp;
1115 aChannel->GetCacheReadEnd(&timeStamp);
1116 args.cacheReadEnd() = timeStamp;
1118 aChannel->GetTransactionPending(&timeStamp);
1119 args.transactionPending() = timeStamp;
1120 return args;
1123 NS_IMETHODIMP
1124 HttpChannelParent::OnStartRequest(nsIRequest* aRequest) {
1125 nsresult rv;
1127 LOG(("HttpChannelParent::OnStartRequest [this=%p, aRequest=%p]\n", this,
1128 aRequest));
1129 MOZ_ASSERT(NS_IsMainThread());
1131 Maybe<uint32_t> multiPartID;
1132 bool isFirstPartOfMultiPart = false;
1133 bool isLastPartOfMultiPart = false;
1134 DebugOnly<bool> isMultiPart = false;
1136 RefPtr<HttpBaseChannel> chan = do_QueryObject(aRequest);
1137 if (!chan) {
1138 if (nsCOMPtr<nsIMultiPartChannel> multiPartChannel =
1139 do_QueryInterface(aRequest)) {
1140 isMultiPart = true;
1141 nsCOMPtr<nsIChannel> baseChannel;
1142 multiPartChannel->GetBaseChannel(getter_AddRefs(baseChannel));
1143 chan = do_QueryObject(baseChannel);
1145 uint32_t partID = 0;
1146 multiPartChannel->GetPartID(&partID);
1147 multiPartID = Some(partID);
1148 multiPartChannel->GetIsFirstPart(&isFirstPartOfMultiPart);
1149 multiPartChannel->GetIsLastPart(&isLastPartOfMultiPart);
1150 } else if (nsCOMPtr<nsIViewSourceChannel> viewSourceChannel =
1151 do_QueryInterface(aRequest)) {
1152 chan = do_QueryObject(viewSourceChannel->GetInnerChannel());
1155 MOZ_ASSERT(multiPartID || !isMultiPart, "Changed multi-part state?");
1157 if (!chan) {
1158 LOG((" aRequest is not HttpBaseChannel"));
1159 NS_ERROR(
1160 "Expecting only HttpBaseChannel as aRequest in "
1161 "HttpChannelParent::OnStartRequest");
1162 return NS_ERROR_UNEXPECTED;
1165 mAfterOnStartRequestBegun = true;
1167 // Todo: re-enable when bug 1589749 is fixed.
1168 /*MOZ_ASSERT(mChannel == chan,
1169 "HttpChannelParent getting OnStartRequest from a different "
1170 "HttpBaseChannel instance");*/
1172 HttpChannelOnStartRequestArgs args;
1174 // Send down any permissions/cookies which are relevant to this URL if we are
1175 // performing a document load. We can't do that if mIPCClosed is set.
1176 if (!mIPCClosed) {
1177 PContentParent* pcp = Manager()->Manager();
1178 MOZ_ASSERT(pcp, "We should have a manager if our IPC isn't closed");
1179 DebugOnly<nsresult> rv =
1180 static_cast<ContentParent*>(pcp)->AboutToLoadHttpFtpDocumentForChild(
1181 chan, &args.shouldWaitForOnStartRequestSent());
1182 MOZ_ASSERT(NS_SUCCEEDED(rv));
1185 args.multiPartID() = multiPartID;
1186 args.isFirstPartOfMultiPart() = isFirstPartOfMultiPart;
1187 args.isLastPartOfMultiPart() = isLastPartOfMultiPart;
1189 args.cacheExpirationTime() = nsICacheEntry::NO_EXPIRATION_TIME;
1191 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(chan);
1193 if (httpChannelImpl) {
1194 httpChannelImpl->IsFromCache(&args.isFromCache());
1195 httpChannelImpl->IsRacing(&args.isRacing());
1196 httpChannelImpl->GetCacheEntryId(&args.cacheEntryId());
1197 httpChannelImpl->GetCacheTokenFetchCount(&args.cacheFetchCount());
1198 httpChannelImpl->GetCacheTokenExpirationTime(&args.cacheExpirationTime());
1199 httpChannelImpl->GetProtocolVersion(args.protocolVersion());
1201 mDataSentToChildProcess = httpChannelImpl->DataSentToChildProcess();
1203 // If RCWN is enabled and cache wins, we can't use the ODA from socket
1204 // process.
1205 if (args.isRacing()) {
1206 mDataSentToChildProcess =
1207 httpChannelImpl->DataSentToChildProcess() && !args.isFromCache();
1209 args.dataFromSocketProcess() = mDataSentToChildProcess;
1212 // Propagate whether or not conversion should occur from the parent-side
1213 // channel to the child-side channel. Then disable the parent-side
1214 // conversion so that it only occurs in the child.
1215 Unused << chan->GetApplyConversion(&args.applyConversion());
1216 chan->SetApplyConversion(false);
1218 // If we've already applied the conversion (as can happen if we installed
1219 // a multipart converted), then don't apply it again on the child.
1220 if (chan->HasAppliedConversion()) {
1221 args.applyConversion() = false;
1224 chan->GetStatus(&args.channelStatus());
1226 // Keep the cache entry for future use when opening alternative streams.
1227 // It could be already released by nsHttpChannel at that time.
1228 nsCOMPtr<nsISupports> cacheEntry;
1230 if (httpChannelImpl) {
1231 httpChannelImpl->GetCacheToken(getter_AddRefs(cacheEntry));
1232 mCacheEntry = do_QueryInterface(cacheEntry);
1233 args.cacheEntryAvailable() = static_cast<bool>(mCacheEntry);
1235 httpChannelImpl->GetCacheKey(&args.cacheKey());
1236 httpChannelImpl->GetAlternativeDataType(args.altDataType());
1239 args.altDataLength() = chan->GetAltDataLength();
1240 args.deliveringAltData() = chan->IsDeliveringAltData();
1242 args.securityInfo() = SecurityInfo();
1244 chan->GetRedirectCount(&args.redirectCount());
1245 chan->GetHasHTTPSRR(&args.hasHTTPSRR());
1247 chan->GetIsProxyUsed(&args.isProxyUsed());
1249 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
1250 mozilla::ipc::LoadInfoToParentLoadInfoForwarder(loadInfo,
1251 &args.loadInfoForwarder());
1253 nsHttpResponseHead* responseHead = chan->GetResponseHead();
1254 bool useResponseHead = !!responseHead;
1255 nsHttpResponseHead cleanedUpResponseHead;
1257 if (responseHead &&
1258 (responseHead->HasHeader(nsHttp::Set_Cookie) || multiPartID)) {
1259 cleanedUpResponseHead = *responseHead;
1260 cleanedUpResponseHead.ClearHeader(nsHttp::Set_Cookie);
1261 if (multiPartID) {
1262 nsCOMPtr<nsIChannel> multiPartChannel = do_QueryInterface(aRequest);
1263 // For the multipart channel, use the parsed subtype instead. Note that
1264 // `chan` is the underlying base channel of the multipart channel in this
1265 // case, which is different from `multiPartChannel`.
1266 MOZ_ASSERT(multiPartChannel);
1267 nsAutoCString contentType;
1268 multiPartChannel->GetContentType(contentType);
1269 cleanedUpResponseHead.SetContentType(contentType);
1271 responseHead = &cleanedUpResponseHead;
1274 if (!responseHead) {
1275 responseHead = &cleanedUpResponseHead;
1278 if (chan->ChannelBlockedByOpaqueResponse() &&
1279 chan->CachedOpaqueResponseBlockingPref()) {
1280 responseHead->ClearHeaders();
1283 chan->GetIsResolvedByTRR(&args.isResolvedByTRR());
1284 chan->GetAllRedirectsSameOrigin(&args.allRedirectsSameOrigin());
1285 chan->GetCrossOriginOpenerPolicy(&args.openerPolicy());
1286 args.selfAddr() = chan->GetSelfAddr();
1287 args.peerAddr() = chan->GetPeerAddr();
1288 args.timing() = GetTimingAttributes(mChannel);
1289 if (mOverrideReferrerInfo) {
1290 args.overrideReferrerInfo() = ToRefPtr(std::move(mOverrideReferrerInfo));
1292 if (!mCookie.IsEmpty()) {
1293 args.cookie() = std::move(mCookie);
1296 nsHttpRequestHead* requestHead = chan->GetRequestHead();
1297 // !!! We need to lock headers and please don't forget to unlock them !!!
1298 requestHead->Enter();
1300 nsHttpHeaderArray cleanedUpRequestHeaders;
1301 bool cleanedUpRequest = false;
1302 if (requestHead->HasHeader(nsHttp::Cookie)) {
1303 cleanedUpRequestHeaders = requestHead->Headers();
1304 cleanedUpRequestHeaders.ClearHeader(nsHttp::Cookie);
1305 cleanedUpRequest = true;
1308 rv = NS_OK;
1310 nsCOMPtr<nsICacheEntry> altDataSource;
1311 nsCOMPtr<nsICacheInfoChannel> cacheChannel =
1312 do_QueryInterface(static_cast<nsIChannel*>(mChannel.get()));
1313 if (cacheChannel) {
1314 for (const auto& pref : cacheChannel->PreferredAlternativeDataTypes()) {
1315 if (pref.type() == args.altDataType() &&
1316 pref.deliverAltData() ==
1317 nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::
1318 SERIALIZE) {
1319 altDataSource = mCacheEntry;
1320 break;
1325 nsIRequest::TRRMode effectiveMode = nsIRequest::TRR_DEFAULT_MODE;
1326 mChannel->GetEffectiveTRRMode(&effectiveMode);
1327 args.effectiveTRRMode() = effectiveMode;
1329 TRRSkippedReason reason = TRRSkippedReason::TRR_UNSET;
1330 mChannel->GetTrrSkipReason(&reason);
1331 args.trrSkipReason() = reason;
1333 if (mIPCClosed ||
1334 !mBgParent->OnStartRequest(
1335 *responseHead, useResponseHead,
1336 cleanedUpRequest ? cleanedUpRequestHeaders : requestHead->Headers(),
1337 args, altDataSource, chan->GetOnStartRequestStartTime())) {
1338 rv = NS_ERROR_UNEXPECTED;
1340 requestHead->Exit();
1342 // Need to wait for the cookies/permissions to content process, which is sent
1343 // via PContent in AboutToLoadHttpFtpDocumentForChild. For multipart channel,
1344 // send only one time since the cookies/permissions are the same.
1345 if (NS_SUCCEEDED(rv) && args.shouldWaitForOnStartRequestSent() &&
1346 multiPartID.valueOr(0) == 0) {
1347 LOG(("HttpChannelParent::SendOnStartRequestSent\n"));
1348 Unused << SendOnStartRequestSent();
1351 if (!args.timing().domainLookupEnd().IsNull() &&
1352 !args.timing().connectStart().IsNull()) {
1353 nsAutoCString protocolVersion;
1354 mChannel->GetProtocolVersion(protocolVersion);
1355 uint32_t classOfServiceFlags = 0;
1356 mChannel->GetClassFlags(&classOfServiceFlags);
1357 nsAutoCString cosString;
1358 ClassOfService::ToString(classOfServiceFlags, cosString);
1359 nsAutoCString key(
1360 nsPrintfCString("%s_%s", protocolVersion.get(), cosString.get()));
1361 Telemetry::AccumulateTimeDelta(
1362 Telemetry::NETWORK_DNS_END_TO_CONNECT_START_EXP_MS, key,
1363 args.timing().domainLookupEnd(), args.timing().connectStart());
1366 return rv;
1369 NS_IMETHODIMP
1370 HttpChannelParent::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
1371 LOG(("HttpChannelParent::OnStopRequest: [this=%p aRequest=%p status=%" PRIx32
1372 "]\n",
1373 this, aRequest, static_cast<uint32_t>(aStatusCode)));
1374 MOZ_ASSERT(NS_IsMainThread());
1376 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
1377 if (httpChannelImpl) {
1378 httpChannelImpl->SetWarningReporter(nullptr);
1381 nsHttpHeaderArray* responseTrailer = mChannel->GetResponseTrailers();
1383 nsTArray<ConsoleReportCollected> consoleReports;
1385 RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(mChannel);
1386 TimeStamp onStopRequestStart;
1387 if (httpChannel) {
1388 httpChannel->StealConsoleReports(consoleReports);
1389 onStopRequestStart = httpChannel->GetOnStopRequestStartTime();
1392 // Either IPC channel is closed or background channel
1393 // is ready to send OnStopRequest.
1394 MOZ_ASSERT(mIPCClosed || mBgParent);
1396 if (mDataSentToChildProcess) {
1397 if (mIPCClosed || !mBgParent ||
1398 !mBgParent->OnConsoleReport(consoleReports)) {
1399 return NS_ERROR_UNEXPECTED;
1401 return NS_OK;
1404 // If we're handling a multi-part stream, then send this directly
1405 // over PHttpChannel to make synchronization easier.
1406 if (mIPCClosed || !mBgParent ||
1407 !mBgParent->OnStopRequest(
1408 aStatusCode, GetTimingAttributes(mChannel),
1409 responseTrailer ? *responseTrailer : nsHttpHeaderArray(),
1410 consoleReports, onStopRequestStart)) {
1411 return NS_ERROR_UNEXPECTED;
1414 if (NeedFlowControl()) {
1415 bool isLocal = false;
1416 NetAddr peerAddr = mChannel->GetPeerAddr();
1418 #if defined(XP_UNIX)
1419 // Unix-domain sockets are always local.
1420 isLocal = (peerAddr.raw.family == PR_AF_LOCAL);
1421 #endif
1423 isLocal = isLocal || peerAddr.IsLoopbackAddr();
1425 if (!isLocal) {
1426 if (!mHasSuspendedByBackPressure) {
1427 AccumulateCategorical(
1428 Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1429 NotSuspended);
1430 } else {
1431 AccumulateCategorical(
1432 Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1433 Suspended);
1435 // Only analyze non-local suspended cases, which we are interested in.
1436 nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
1437 Telemetry::Accumulate(
1438 Telemetry::NETWORK_BACK_PRESSURE_SUSPENSION_CP_TYPE,
1439 loadInfo->InternalContentPolicyType());
1441 } else {
1442 if (!mHasSuspendedByBackPressure) {
1443 AccumulateCategorical(
1444 Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1445 NotSuspendedLocal);
1446 } else {
1447 AccumulateCategorical(
1448 Telemetry::LABELS_NETWORK_BACK_PRESSURE_SUSPENSION_RATE_V2::
1449 SuspendedLocal);
1453 return NS_OK;
1456 //-----------------------------------------------------------------------------
1457 // HttpChannelParent::nsIMultiPartChannelListener
1458 //-----------------------------------------------------------------------------
1460 NS_IMETHODIMP
1461 HttpChannelParent::OnAfterLastPart(nsresult aStatus) {
1462 LOG(("HttpChannelParent::OnAfterLastPart [this=%p]\n", this));
1463 MOZ_ASSERT(NS_IsMainThread());
1465 // If IPC channel is closed, there is nothing we can do. Just return NS_OK.
1466 if (mIPCClosed) {
1467 return NS_OK;
1470 // If IPC channel is open, background channel should be ready to send
1471 // OnAfterLastPart.
1472 MOZ_ASSERT(mBgParent);
1474 if (!mBgParent || !mBgParent->OnAfterLastPart(aStatus)) {
1475 return NS_ERROR_UNEXPECTED;
1478 return NS_OK;
1481 //-----------------------------------------------------------------------------
1482 // HttpChannelParent::nsIStreamListener
1483 //-----------------------------------------------------------------------------
1485 NS_IMETHODIMP
1486 HttpChannelParent::OnDataAvailable(nsIRequest* aRequest,
1487 nsIInputStream* aInputStream,
1488 uint64_t aOffset, uint32_t aCount) {
1489 LOG(("HttpChannelParent::OnDataAvailable [this=%p aRequest=%p offset=%" PRIu64
1490 " count=%" PRIu32 "]\n",
1491 this, aRequest, aOffset, aCount));
1492 MOZ_ASSERT(NS_IsMainThread());
1494 if (mDataSentToChildProcess) {
1495 uint32_t n;
1496 return aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &n);
1499 nsresult channelStatus = NS_OK;
1500 mChannel->GetStatus(&channelStatus);
1502 nsresult transportStatus = NS_NET_STATUS_RECEIVING_FROM;
1503 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
1504 TimeStamp onDataAvailableStart = TimeStamp::Now();
1505 if (httpChannelImpl) {
1506 if (httpChannelImpl->IsReadingFromCache()) {
1507 transportStatus = NS_NET_STATUS_READING;
1509 onDataAvailableStart = httpChannelImpl->GetDataAvailableStartTime();
1512 nsCString data;
1513 nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
1514 if (NS_FAILED(rv)) {
1515 return rv;
1518 // Either IPC channel is closed or background channel
1519 // is ready to send OnTransportAndData.
1520 MOZ_ASSERT(mIPCClosed || mBgParent);
1522 if (mIPCClosed || !mBgParent ||
1523 !mBgParent->OnTransportAndData(channelStatus, transportStatus, aOffset,
1524 aCount, data, onDataAvailableStart)) {
1525 return NS_ERROR_UNEXPECTED;
1528 int32_t count = static_cast<int32_t>(aCount);
1530 if (NeedFlowControl()) {
1531 // We're going to run out of sending window size
1532 if (mSendWindowSize > 0 && mSendWindowSize <= count) {
1533 MOZ_ASSERT(!mSuspendedForFlowControl);
1534 LOG((" suspend the channel due to e10s backpressure"));
1535 Unused << mChannel->Suspend();
1536 mSuspendedForFlowControl = true;
1537 mHasSuspendedByBackPressure = true;
1538 } else if (!mResumedTimestamp.IsNull()) {
1539 // Calculate the delay when the first packet arrived after resume
1540 Telemetry::AccumulateTimeDelta(
1541 Telemetry::NETWORK_BACK_PRESSURE_SUSPENSION_DELAY_TIME_MS,
1542 mResumedTimestamp);
1543 mResumedTimestamp = TimeStamp();
1545 mSendWindowSize -= count;
1548 return NS_OK;
1551 bool HttpChannelParent::NeedFlowControl() {
1552 if (mCacheNeedFlowControlInitialized) {
1553 return mNeedFlowControl;
1556 int64_t contentLength = -1;
1558 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
1560 // By design, we won't trigger the flow control if
1561 // a. pref-out
1562 // b. the resource is from cache or partial cache
1563 // c. the resource is small
1564 // d. data will be sent from socket process to child process directly
1565 // Note that we served the cached resource first for partical cache, which is
1566 // ignored here since we only take the first ODA into consideration.
1567 if (gHttpHandler->SendWindowSize() == 0 || !httpChannelImpl ||
1568 httpChannelImpl->IsReadingFromCache() ||
1569 NS_FAILED(httpChannelImpl->GetContentLength(&contentLength)) ||
1570 contentLength < gHttpHandler->SendWindowSize() ||
1571 mDataSentToChildProcess) {
1572 mNeedFlowControl = false;
1574 mCacheNeedFlowControlInitialized = true;
1575 return mNeedFlowControl;
1578 mozilla::ipc::IPCResult HttpChannelParent::RecvBytesRead(
1579 const int32_t& aCount) {
1580 if (!NeedFlowControl()) {
1581 return IPC_OK();
1584 LOG(("HttpChannelParent::RecvBytesRead [this=%p count=%" PRId32 "]\n", this,
1585 aCount));
1587 if (mSendWindowSize <= 0 && mSendWindowSize + aCount > 0) {
1588 MOZ_ASSERT(mSuspendedForFlowControl);
1589 LOG((" resume the channel due to e10s backpressure relief"));
1590 Unused << mChannel->Resume();
1591 mSuspendedForFlowControl = false;
1593 mResumedTimestamp = TimeStamp::Now();
1595 mSendWindowSize += aCount;
1596 return IPC_OK();
1599 mozilla::ipc::IPCResult HttpChannelParent::RecvOpenOriginalCacheInputStream() {
1600 if (mIPCClosed) {
1601 return IPC_OK();
1603 Maybe<IPCStream> ipcStream;
1604 if (mCacheEntry) {
1605 nsCOMPtr<nsIInputStream> inputStream;
1606 nsresult rv = mCacheEntry->OpenInputStream(0, getter_AddRefs(inputStream));
1607 if (NS_SUCCEEDED(rv)) {
1608 Unused << mozilla::ipc::SerializeIPCStream(
1609 inputStream.forget(), ipcStream, /* aAllowLazy */ false);
1613 Unused << SendOriginalCacheInputStreamAvailable(ipcStream);
1614 return IPC_OK();
1617 //-----------------------------------------------------------------------------
1618 // HttpChannelParent::nsIProgressEventSink
1619 //-----------------------------------------------------------------------------
1621 NS_IMETHODIMP
1622 HttpChannelParent::OnProgress(nsIRequest* aRequest, int64_t aProgress,
1623 int64_t aProgressMax) {
1624 LOG(("HttpChannelParent::OnProgress [this=%p progress=%" PRId64 "max=%" PRId64
1625 "]\n",
1626 this, aProgress, aProgressMax));
1627 MOZ_ASSERT(NS_IsMainThread());
1629 // If IPC channel is closed, there is nothing we can do. Just return NS_OK.
1630 if (mIPCClosed) {
1631 return NS_OK;
1634 // If it indicates this precedes OnDataAvailable, child can derive the value
1635 // in ODA.
1636 if (mIgnoreProgress) {
1637 mIgnoreProgress = false;
1638 return NS_OK;
1641 // If IPC channel is open, background channel should be ready to send
1642 // OnProgress.
1643 MOZ_ASSERT(mBgParent);
1645 // Send OnProgress events to the child for data upload progress notifications
1646 // (i.e. status == NS_NET_STATUS_SENDING_TO) or if the channel has
1647 // LOAD_BACKGROUND set.
1648 if (!mBgParent || !mBgParent->OnProgress(aProgress, aProgressMax)) {
1649 return NS_ERROR_UNEXPECTED;
1652 return NS_OK;
1655 NS_IMETHODIMP
1656 HttpChannelParent::OnStatus(nsIRequest* aRequest, nsresult aStatus,
1657 const char16_t* aStatusArg) {
1658 LOG(("HttpChannelParent::OnStatus [this=%p status=%" PRIx32 "]\n", this,
1659 static_cast<uint32_t>(aStatus)));
1660 MOZ_ASSERT(NS_IsMainThread());
1662 // If IPC channel is closed, there is nothing we can do. Just return NS_OK.
1663 if (mIPCClosed) {
1664 return NS_OK;
1667 // If this precedes OnDataAvailable, transportStatus will be derived in ODA.
1668 if (aStatus == NS_NET_STATUS_RECEIVING_FROM ||
1669 aStatus == NS_NET_STATUS_READING) {
1670 // The transport status and progress generated by ODA will be coalesced
1671 // into one IPC message. Therefore, we can ignore the next OnProgress event
1672 // since it is generated by ODA as well.
1673 mIgnoreProgress = true;
1674 return NS_OK;
1677 // If IPC channel is open, background channel should be ready to send
1678 // OnStatus.
1679 MOZ_ASSERT(mIPCClosed || mBgParent);
1681 // Otherwise, send to child now
1682 if (!mBgParent || !mBgParent->OnStatus(aStatus)) {
1683 return NS_ERROR_UNEXPECTED;
1686 return NS_OK;
1689 //-----------------------------------------------------------------------------
1690 // HttpChannelParent::nsIParentChannel
1691 //-----------------------------------------------------------------------------
1693 NS_IMETHODIMP
1694 HttpChannelParent::SetParentListener(ParentChannelListener* aListener) {
1695 LOG(("HttpChannelParent::SetParentListener [this=%p aListener=%p]\n", this,
1696 aListener));
1697 MOZ_ASSERT(aListener);
1698 MOZ_ASSERT(!mParentListener,
1699 "SetParentListener should only be called for "
1700 "new HttpChannelParents after a redirect, when "
1701 "mParentListener is null.");
1702 mParentListener = aListener;
1703 return NS_OK;
1706 NS_IMETHODIMP
1707 HttpChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
1708 const nsACString& aProvider,
1709 const nsACString& aFullHash) {
1710 LOG(("HttpChannelParent::SetClassifierMatchedInfo [this=%p]\n", this));
1711 if (!mIPCClosed) {
1712 MOZ_ASSERT(mBgParent);
1713 Unused << mBgParent->OnSetClassifierMatchedInfo(aList, aProvider,
1714 aFullHash);
1716 return NS_OK;
1719 NS_IMETHODIMP
1720 HttpChannelParent::SetClassifierMatchedTrackingInfo(
1721 const nsACString& aLists, const nsACString& aFullHashes) {
1722 LOG(("HttpChannelParent::SetClassifierMatchedTrackingInfo [this=%p]\n",
1723 this));
1724 if (!mIPCClosed) {
1725 MOZ_ASSERT(mBgParent);
1726 Unused << mBgParent->OnSetClassifierMatchedTrackingInfo(aLists,
1727 aFullHashes);
1729 return NS_OK;
1732 NS_IMETHODIMP
1733 HttpChannelParent::NotifyClassificationFlags(uint32_t aClassificationFlags,
1734 bool aIsThirdParty) {
1735 LOG(
1736 ("HttpChannelParent::NotifyClassificationFlags "
1737 "classificationFlags=%" PRIu32 ", thirdparty=%d [this=%p]\n",
1738 aClassificationFlags, static_cast<int>(aIsThirdParty), this));
1739 if (!mIPCClosed) {
1740 MOZ_ASSERT(mBgParent);
1741 Unused << mBgParent->OnNotifyClassificationFlags(aClassificationFlags,
1742 aIsThirdParty);
1744 return NS_OK;
1747 NS_IMETHODIMP
1748 HttpChannelParent::Delete() {
1749 if (!mIPCClosed) Unused << DoSendDeleteSelf();
1751 return NS_OK;
1754 NS_IMETHODIMP
1755 HttpChannelParent::GetRemoteType(nsACString& aRemoteType) {
1756 if (!CanSend()) {
1757 return NS_ERROR_UNEXPECTED;
1760 dom::PContentParent* pcp = Manager()->Manager();
1761 aRemoteType = static_cast<dom::ContentParent*>(pcp)->GetRemoteType();
1762 return NS_OK;
1765 bool HttpChannelParent::IsRedirectDueToAuthRetry(uint32_t redirectFlags) {
1766 return (redirectFlags & nsIChannelEventSink::REDIRECT_AUTH_RETRY);
1769 //-----------------------------------------------------------------------------
1770 // HttpChannelParent::nsIParentRedirectingChannel
1771 //-----------------------------------------------------------------------------
1773 NS_IMETHODIMP
1774 HttpChannelParent::StartRedirect(nsIChannel* newChannel, uint32_t redirectFlags,
1775 nsIAsyncVerifyRedirectCallback* callback) {
1776 nsresult rv;
1778 LOG(("HttpChannelParent::StartRedirect [this=%p, newChannel=%p callback=%p]",
1779 this, newChannel, callback));
1781 // Register the new channel and obtain id for it
1782 nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
1783 RedirectChannelRegistrar::GetOrCreate();
1784 MOZ_ASSERT(registrar);
1786 mRedirectChannelId = nsContentUtils::GenerateLoadIdentifier();
1787 rv = registrar->RegisterChannel(newChannel, mRedirectChannelId);
1788 NS_ENSURE_SUCCESS(rv, rv);
1790 LOG(("Registered %p channel under id=%" PRIx64, newChannel,
1791 mRedirectChannelId));
1793 if (mIPCClosed) {
1794 return NS_BINDING_ABORTED;
1797 // If this is an internal redirect for service worker interception or
1798 // internal redirect due to auth retries, then hide it from the child
1799 // process. The original e10s interception code was not designed with this
1800 // in mind and its not necessary to replace the HttpChannelChild/Parent
1801 // objects in this case.
1802 if (redirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
1803 nsCOMPtr<nsIInterceptedChannel> oldIntercepted =
1804 do_QueryInterface(static_cast<nsIChannel*>(mChannel.get()));
1805 nsCOMPtr<nsIInterceptedChannel> newIntercepted =
1806 do_QueryInterface(newChannel);
1808 // 1. We only want to hide the special internal redirects from
1809 // nsHttpChannel to InterceptedHttpChannel.
1810 // 2. We want to allow through internal redirects
1811 // initiated from the InterceptedHttpChannel even if they are to another
1812 // InterceptedHttpChannel, except the interception reset, since
1813 // corresponding HttpChannelChild/Parent objects can be reused for reset
1814 // case.
1815 // 3. If this is an internal redirect due to auth retry then we will
1816 // hide it from the child process
1818 if ((!oldIntercepted && newIntercepted) ||
1819 (oldIntercepted && !newIntercepted && oldIntercepted->IsReset()) ||
1820 (IsRedirectDueToAuthRetry(redirectFlags))) {
1821 // We need to move across the reserved and initial client information
1822 // to the new channel. Normally this would be handled by the child
1823 // ClientChannelHelper, but that is not notified of this redirect since
1824 // we're not propagating it back to the child process.
1825 nsCOMPtr<nsILoadInfo> oldLoadInfo = mChannel->LoadInfo();
1827 nsCOMPtr<nsILoadInfo> newLoadInfo = newChannel->LoadInfo();
1829 Maybe<ClientInfo> reservedClientInfo(
1830 oldLoadInfo->GetReservedClientInfo());
1831 if (reservedClientInfo.isSome()) {
1832 newLoadInfo->SetReservedClientInfo(reservedClientInfo.ref());
1835 Maybe<ClientInfo> initialClientInfo(oldLoadInfo->GetInitialClientInfo());
1836 if (initialClientInfo.isSome()) {
1837 newLoadInfo->SetInitialClientInfo(initialClientInfo.ref());
1840 // If this is ServiceWorker fallback redirect, info HttpChannelChild to
1841 // detach StreamFilters. Otherwise StreamFilters will be attached twice
1842 // on the same HttpChannelChild when opening the new nsHttpChannel.
1843 if (oldIntercepted) {
1844 Unused << DetachStreamFilters();
1847 // Re-link the HttpChannelParent to the new channel.
1848 nsCOMPtr<nsIChannel> linkedChannel;
1849 rv = NS_LinkRedirectChannels(mRedirectChannelId, this,
1850 getter_AddRefs(linkedChannel));
1851 NS_ENSURE_SUCCESS(rv, rv);
1852 MOZ_ASSERT(linkedChannel == newChannel);
1854 // We immediately store the channel as our nested mChannel.
1855 // None of the redirect IPC messaging takes place.
1856 mChannel = do_QueryObject(newChannel);
1858 callback->OnRedirectVerifyCallback(NS_OK);
1859 return NS_OK;
1863 // Sending down the original URI, because that is the URI we have
1864 // to construct the channel from - this is the URI we've been actually
1865 // redirected to. URI of the channel may be an inner channel URI.
1866 // URI of the channel will be reconstructed by the protocol handler
1867 // on the child process, no need to send it then.
1868 nsCOMPtr<nsIURI> newOriginalURI;
1869 newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
1871 uint32_t newLoadFlags = nsIRequest::LOAD_NORMAL;
1872 MOZ_ALWAYS_SUCCEEDS(newChannel->GetLoadFlags(&newLoadFlags));
1874 nsCOMPtr<nsITransportSecurityInfo> securityInfo(SecurityInfo());
1876 // If the channel is a HTTP channel, we also want to inform the child
1877 // about the parent's channelId attribute, so that both parent and child
1878 // share the same ID. Useful for monitoring channel activity in devtools.
1879 uint64_t channelId = 0;
1880 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
1881 if (httpChannel) {
1882 rv = httpChannel->GetChannelId(&channelId);
1883 NS_ENSURE_SUCCESS(rv, NS_BINDING_ABORTED);
1886 nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
1888 ParentLoadInfoForwarderArgs loadInfoForwarderArg;
1889 mozilla::ipc::LoadInfoToParentLoadInfoForwarder(loadInfo,
1890 &loadInfoForwarderArg);
1892 nsHttpResponseHead* responseHead = mChannel->GetResponseHead();
1894 nsHttpResponseHead cleanedUpResponseHead;
1895 if (responseHead && responseHead->HasHeader(nsHttp::Set_Cookie)) {
1896 cleanedUpResponseHead = *responseHead;
1897 cleanedUpResponseHead.ClearHeader(nsHttp::Set_Cookie);
1898 responseHead = &cleanedUpResponseHead;
1901 if (!responseHead) {
1902 responseHead = &cleanedUpResponseHead;
1905 if (!mIPCClosed) {
1906 if (!SendRedirect1Begin(mRedirectChannelId, newOriginalURI, newLoadFlags,
1907 redirectFlags, loadInfoForwarderArg, *responseHead,
1908 securityInfo, channelId, mChannel->GetPeerAddr(),
1909 GetTimingAttributes(mChannel))) {
1910 return NS_BINDING_ABORTED;
1914 // Result is handled in RecvRedirect2Verify above
1916 mRedirectChannel = newChannel;
1917 mRedirectCallback = callback;
1918 return NS_OK;
1921 NS_IMETHODIMP
1922 HttpChannelParent::CompleteRedirect(nsresult status) {
1923 LOG(("HttpChannelParent::CompleteRedirect [this=%p status=0x%X]\n", this,
1924 static_cast<uint32_t>(status)));
1926 // If this was an internal redirect for a service worker interception then
1927 // we will not have a redirecting channel here. Hide this redirect from
1928 // the child.
1929 if (!mRedirectChannel) {
1930 return NS_OK;
1933 if (!mIPCClosed) {
1934 // TODO: check return value: assume child dead if failed
1935 if (NS_SUCCEEDED(status)) {
1936 Unused << SendRedirect3Complete();
1937 } else {
1938 Unused << SendRedirectFailed(status);
1942 mRedirectChannel = nullptr;
1943 return NS_OK;
1946 nsresult HttpChannelParent::OpenAlternativeOutputStream(
1947 const nsACString& type, int64_t predictedSize,
1948 nsIAsyncOutputStream** _retval) {
1949 // We need to make sure the child does not call SendDocumentChannelCleanup()
1950 // before opening the altOutputStream, because that clears mCacheEntry.
1951 if (!mCacheEntry) {
1952 return NS_ERROR_NOT_AVAILABLE;
1954 nsresult rv =
1955 mCacheEntry->OpenAlternativeOutputStream(type, predictedSize, _retval);
1956 if (NS_SUCCEEDED(rv)) {
1957 mCacheEntry->SetMetaDataElement("alt-data-from-child", "1");
1959 return rv;
1962 already_AddRefed<nsITransportSecurityInfo> HttpChannelParent::SecurityInfo() {
1963 if (!mChannel) {
1964 return nullptr;
1966 nsCOMPtr<nsITransportSecurityInfo> securityInfo;
1967 mChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
1968 return securityInfo.forget();
1971 bool HttpChannelParent::DoSendDeleteSelf() {
1972 mIPCClosed = true;
1973 bool rv = SendDeleteSelf();
1975 CleanupBackgroundChannel();
1977 return rv;
1980 mozilla::ipc::IPCResult HttpChannelParent::RecvDeletingChannel() {
1981 // We need to ensure that the parent channel will not be sending any more IPC
1982 // messages after this, as the child is going away. DoSendDeleteSelf will
1983 // set mIPCClosed = true;
1984 if (!DoSendDeleteSelf()) {
1985 return IPC_FAIL_NO_REASON(this);
1987 return IPC_OK();
1990 //-----------------------------------------------------------------------------
1991 // HttpChannelSecurityWarningReporter
1992 //-----------------------------------------------------------------------------
1994 nsresult HttpChannelParent::ReportSecurityMessage(
1995 const nsAString& aMessageTag, const nsAString& aMessageCategory) {
1996 if (mIPCClosed || NS_WARN_IF(!SendReportSecurityMessage(
1997 nsString(aMessageTag), nsString(aMessageCategory)))) {
1998 return NS_ERROR_UNEXPECTED;
2000 return NS_OK;
2003 //-----------------------------------------------------------------------------
2004 // nsIAsyncVerifyRedirectReadyCallback
2005 //-----------------------------------------------------------------------------
2007 NS_IMETHODIMP
2008 HttpChannelParent::ReadyToVerify(nsresult aResult) {
2009 LOG(("HttpChannelParent::ReadyToVerify [this=%p result=%" PRIx32 "]\n", this,
2010 static_cast<uint32_t>(aResult)));
2011 MOZ_ASSERT(NS_IsMainThread());
2013 ContinueRedirect2Verify(aResult);
2015 return NS_OK;
2018 void HttpChannelParent::DoSendSetPriority(int16_t aValue) {
2019 if (!mIPCClosed) {
2020 Unused << SendSetPriority(aValue);
2024 nsresult HttpChannelParent::LogBlockedCORSRequest(const nsAString& aMessage,
2025 const nsACString& aCategory,
2026 bool aIsWarning) {
2027 if (mIPCClosed ||
2028 NS_WARN_IF(!SendLogBlockedCORSRequest(
2029 nsString(aMessage), nsCString(aCategory), aIsWarning))) {
2030 return NS_ERROR_UNEXPECTED;
2032 return NS_OK;
2035 nsresult HttpChannelParent::LogMimeTypeMismatch(const nsACString& aMessageName,
2036 bool aWarning,
2037 const nsAString& aURL,
2038 const nsAString& aContentType) {
2039 if (mIPCClosed || NS_WARN_IF(!SendLogMimeTypeMismatch(
2040 nsCString(aMessageName), aWarning, nsString(aURL),
2041 nsString(aContentType)))) {
2042 return NS_ERROR_UNEXPECTED;
2044 return NS_OK;
2047 //-----------------------------------------------------------------------------
2048 // nsIChannelEventSink
2049 //-----------------------------------------------------------------------------
2051 NS_IMETHODIMP
2052 HttpChannelParent::AsyncOnChannelRedirect(
2053 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aRedirectFlags,
2054 nsIAsyncVerifyRedirectCallback* aCallback) {
2055 LOG(
2056 ("HttpChannelParent::AsyncOnChannelRedirect [this=%p, old=%p, "
2057 "new=%p, flags=%u]",
2058 this, aOldChannel, aNewChannel, aRedirectFlags));
2060 return StartRedirect(aNewChannel, aRedirectFlags, aCallback);
2063 //-----------------------------------------------------------------------------
2064 // nsIRedirectResultListener
2065 //-----------------------------------------------------------------------------
2067 NS_IMETHODIMP
2068 HttpChannelParent::OnRedirectResult(nsresult status) {
2069 LOG(("HttpChannelParent::OnRedirectResult [this=%p, status=0x%X]", this,
2070 static_cast<uint32_t>(status)));
2072 nsresult rv = NS_OK;
2074 nsCOMPtr<nsIParentChannel> redirectChannel;
2075 if (mRedirectChannelId) {
2076 nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
2077 RedirectChannelRegistrar::GetOrCreate();
2078 MOZ_ASSERT(registrar);
2080 rv = registrar->GetParentChannel(mRedirectChannelId,
2081 getter_AddRefs(redirectChannel));
2082 if (NS_FAILED(rv) || !redirectChannel) {
2083 // Redirect might get canceled before we got AsyncOnChannelRedirect
2084 LOG(("Registered parent channel not found under id=%" PRIx64,
2085 mRedirectChannelId));
2087 nsCOMPtr<nsIChannel> newChannel;
2088 rv = registrar->GetRegisteredChannel(mRedirectChannelId,
2089 getter_AddRefs(newChannel));
2090 MOZ_ASSERT(newChannel, "Already registered channel not found");
2092 if (NS_SUCCEEDED(rv)) {
2093 newChannel->Cancel(NS_BINDING_ABORTED);
2097 // Release all previously registered channels, they are no longer need to be
2098 // kept in the registrar from this moment.
2099 registrar->DeregisterChannels(mRedirectChannelId);
2101 mRedirectChannelId = 0;
2104 if (!redirectChannel) {
2105 if (NS_FAILED(rv)) {
2106 status = rv;
2107 } else {
2108 status = NS_ERROR_NULL_POINTER;
2112 CompleteRedirect(status);
2114 if (NS_SUCCEEDED(status)) {
2115 if (!SameCOMIdentity(redirectChannel,
2116 static_cast<nsIParentRedirectingChannel*>(this))) {
2117 Delete();
2118 mParentListener->SetListenerAfterRedirect(redirectChannel);
2119 redirectChannel->SetParentListener(mParentListener);
2121 } else if (redirectChannel) {
2122 // Delete the redirect target channel: continue using old channel
2123 redirectChannel->Delete();
2126 return NS_OK;
2129 void HttpChannelParent::OverrideReferrerInfoDuringBeginConnect(
2130 nsIReferrerInfo* aReferrerInfo) {
2131 MOZ_ASSERT(aReferrerInfo);
2132 MOZ_ASSERT(!mAfterOnStartRequestBegun);
2134 mOverrideReferrerInfo = aReferrerInfo;
2137 auto HttpChannelParent::AttachStreamFilter(
2138 Endpoint<extensions::PStreamFilterParent>&& aParentEndpoint,
2139 Endpoint<extensions::PStreamFilterChild>&& aChildEndpoint)
2140 -> RefPtr<ChildEndpointPromise> {
2141 LOG(("HttpChannelParent::AttachStreamFilter [this=%p]", this));
2142 MOZ_ASSERT(!mAfterOnStartRequestBegun);
2144 if (mIPCClosed) {
2145 return ChildEndpointPromise::CreateAndReject(false, __func__);
2148 // If IPC channel is open, background channel should be ready to send
2149 // SendAttachStreamFilter.
2150 MOZ_ASSERT(mBgParent);
2151 return InvokeAsync(mBgParent->GetBackgroundTarget(), mBgParent.get(),
2152 __func__, &HttpBackgroundChannelParent::AttachStreamFilter,
2153 std::move(aParentEndpoint), std::move(aChildEndpoint));
2156 auto HttpChannelParent::DetachStreamFilters() -> RefPtr<GenericPromise> {
2157 LOG(("HttpChannelParent::DeattachStreamFilter [this=%p]", this));
2158 MOZ_ASSERT(!mAfterOnStartRequestBegun);
2160 if (NS_WARN_IF(mIPCClosed)) {
2161 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
2164 MOZ_ASSERT(mBgParent);
2165 return InvokeAsync(mBgParent->GetBackgroundTarget(), mBgParent.get(),
2166 __func__,
2167 &HttpBackgroundChannelParent::DetachStreamFilters);
2170 void HttpChannelParent::SetHttpChannelFromEarlyHintPreloader(
2171 HttpBaseChannel* aChannel) {
2172 MOZ_ASSERT(aChannel);
2173 if (mChannel) {
2174 MOZ_ASSERT(false, "SetHttpChannel called with mChannel aready set");
2175 return;
2178 mChannel = aChannel;
2181 void HttpChannelParent::SetCookie(nsCString&& aCookie) {
2182 LOG(("HttpChannelParent::SetCookie [this=%p]", this));
2183 MOZ_ASSERT(!mAfterOnStartRequestBegun);
2184 MOZ_ASSERT(mCookie.IsEmpty());
2186 // The loadGroup of the channel in the parent process could be null in the
2187 // XPCShell content process test, see test_cookiejars_wrap.js. In this case,
2188 // we cannot explicitly set the loadGroup for the parent channel because it's
2189 // created from the content process. To workaround this, we add a testing pref
2190 // to skip this check.
2191 if (!Preferences::GetBool(
2192 "network.cookie.skip_browsing_context_check_in_parent_for_testing") &&
2193 mChannel->IsBrowsingContextDiscarded()) {
2194 return;
2196 mCookie = std::move(aCookie);
2199 } // namespace mozilla::net