1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 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/. */
8 #include "mozilla/DebugOnly.h"
9 #include "mozilla/TaskQueue.h"
10 #include "mozilla/dom/FetchDriver.h"
12 #include "mozilla/dom/ReferrerInfo.h"
13 #include "nsIAsyncVerifyRedirectCallback.h"
14 #include "mozilla/dom/Document.h"
15 #include "nsICookieJarSettings.h"
17 #include "nsIInputStream.h"
18 #include "nsIInterceptionInfo.h"
19 #include "nsIOutputStream.h"
20 #include "nsIFileChannel.h"
21 #include "nsIHttpChannel.h"
22 #include "nsIHttpChannelInternal.h"
23 #include "nsISupportsPriority.h"
24 #include "nsIThreadRetargetableRequest.h"
25 #include "nsIUploadChannel2.h"
26 #include "nsIInterfaceRequestorUtils.h"
28 #include "nsIRedirectHistoryEntry.h"
30 #include "nsBaseChannel.h"
31 #include "nsContentPolicyUtils.h"
32 #include "nsDataChannel.h"
33 #include "nsDataHandler.h"
34 #include "nsNetUtil.h"
35 #include "nsPrintfCString.h"
36 #include "nsProxyRelease.h"
37 #include "nsStreamUtils.h"
38 #include "nsStringStream.h"
39 #include "nsHttpChannel.h"
41 #include "mozilla/dom/BlobURLProtocolHandler.h"
42 #include "mozilla/dom/File.h"
43 #include "mozilla/dom/PerformanceStorage.h"
44 #include "mozilla/dom/PerformanceTiming.h"
45 #include "mozilla/dom/ServiceWorkerInterceptController.h"
46 #include "mozilla/dom/UserActivation.h"
47 #include "mozilla/dom/WorkerCommon.h"
48 #include "mozilla/PreloaderBase.h"
49 #include "mozilla/net/InterceptionInfo.h"
50 #include "mozilla/net/NeckoChannelParams.h"
51 #include "mozilla/ipc/PBackgroundSharedTypes.h"
52 #include "mozilla/StaticPrefs_browser.h"
53 #include "mozilla/StaticPrefs_network.h"
54 #include "mozilla/StaticPrefs_privacy.h"
55 #include "mozilla/StaticPrefs_javascript.h"
56 #include "mozilla/Unused.h"
59 #include "FetchUtil.h"
60 #include "InternalRequest.h"
61 #include "InternalResponse.h"
63 namespace mozilla::dom
{
67 void GetBlobURISpecFromChannel(nsIRequest
* aRequest
, nsCString
& aBlobURISpec
) {
70 aBlobURISpec
.SetIsVoid(true);
72 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
78 nsresult rv
= NS_GetFinalChannelURI(channel
, getter_AddRefs(uri
));
83 if (!dom::IsBlobURI(uri
)) {
87 uri
->GetSpec(aBlobURISpec
);
90 bool ShouldCheckSRI(const InternalRequest
& aRequest
,
91 const InternalResponse
& aResponse
) {
92 return !aRequest
.GetIntegrity().IsEmpty() &&
93 aResponse
.Type() != ResponseType::Error
;
96 } // anonymous namespace
98 //-----------------------------------------------------------------------------
99 // AlternativeDataStreamListener
100 //-----------------------------------------------------------------------------
101 class AlternativeDataStreamListener final
102 : public nsIStreamListener
,
103 public nsIThreadRetargetableStreamListener
{
105 NS_DECL_THREADSAFE_ISUPPORTS
106 NS_DECL_NSIREQUESTOBSERVER
107 NS_DECL_NSISTREAMLISTENER
108 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
110 // The status of AlternativeDataStreamListener
111 // LOADING: is the initial status, loading the alternative data
112 // COMPLETED: Alternative data loading is completed
113 // CANCELED: Alternative data loading is canceled, this would make
114 // AlternativeDataStreamListener ignore all channel callbacks
115 // FALLBACK: fallback the channel callbacks to FetchDriver
116 // Depends on different situaions, the status transition could be followings
117 // 1. LOADING->COMPLETED
118 // This is the normal status transition for alternative data loading
120 // 2. LOADING->CANCELED
121 // LOADING->COMPLETED->CANCELED
122 // Alternative data loading could be canceled when cacheId from alternative
123 // data channel does not match with from main data channel(The cacheID
124 // checking is in FetchDriver::OnStartRequest).
125 // Notice the alternative data loading could finish before the cacheID
126 // checking, so the statust transition could be
127 // LOADING->COMPLETED->CANCELED
129 // 3. LOADING->FALLBACK
130 // For the case that alternative data loading could not be initialized,
131 // i.e. alternative data does not exist or no preferred alternative data
132 // type is requested. Once the status becomes FALLBACK,
133 // AlternativeDataStreamListener transits the channel callback request to
134 // FetchDriver, and the status should not go back to LOADING, COMPLETED, or
136 enum eStatus
{ LOADING
= 0, COMPLETED
, CANCELED
, FALLBACK
};
138 AlternativeDataStreamListener(FetchDriver
* aFetchDriver
, nsIChannel
* aChannel
,
139 const nsACString
& aAlternativeDataType
);
142 uint64_t GetAlternativeDataCacheEntryId();
143 const nsACString
& GetAlternativeDataType() const;
144 already_AddRefed
<nsICacheInfoChannel
> GetCacheInfoChannel();
145 already_AddRefed
<nsIInputStream
> GetAlternativeInputStream();
148 ~AlternativeDataStreamListener() = default;
150 // This creates a strong reference cycle with FetchDriver and its
151 // mAltDataListener. We need to clear at least one reference of them once the
152 // data loading finishes.
153 RefPtr
<FetchDriver
> mFetchDriver
;
154 nsCString mAlternativeDataType
;
155 nsCOMPtr
<nsIInputStream
> mPipeAlternativeInputStream
;
156 nsCOMPtr
<nsIOutputStream
> mPipeAlternativeOutputStream
;
157 uint64_t mAlternativeDataCacheEntryId
;
158 nsCOMPtr
<nsICacheInfoChannel
> mCacheInfoChannel
;
159 nsCOMPtr
<nsIChannel
> mChannel
;
160 Atomic
<eStatus
> mStatus
;
163 NS_IMPL_ISUPPORTS(AlternativeDataStreamListener
, nsIStreamListener
,
164 nsIThreadRetargetableStreamListener
)
166 AlternativeDataStreamListener::AlternativeDataStreamListener(
167 FetchDriver
* aFetchDriver
, nsIChannel
* aChannel
,
168 const nsACString
& aAlternativeDataType
)
169 : mFetchDriver(aFetchDriver
),
170 mAlternativeDataType(aAlternativeDataType
),
171 mAlternativeDataCacheEntryId(0),
173 mStatus(AlternativeDataStreamListener::LOADING
) {
174 MOZ_DIAGNOSTIC_ASSERT(mFetchDriver
);
175 MOZ_DIAGNOSTIC_ASSERT(mChannel
);
178 AlternativeDataStreamListener::eStatus
AlternativeDataStreamListener::Status() {
182 void AlternativeDataStreamListener::Cancel() {
183 mAlternativeDataCacheEntryId
= 0;
184 mCacheInfoChannel
= nullptr;
185 mPipeAlternativeOutputStream
= nullptr;
186 mPipeAlternativeInputStream
= nullptr;
187 if (mChannel
&& mStatus
!= AlternativeDataStreamListener::FALLBACK
) {
188 // if mStatus is fallback, we need to keep channel to forward request back
190 mChannel
->CancelWithReason(NS_BINDING_ABORTED
,
191 "AlternativeDataStreamListener::Cancel"_ns
);
194 mStatus
= AlternativeDataStreamListener::CANCELED
;
197 uint64_t AlternativeDataStreamListener::GetAlternativeDataCacheEntryId() {
198 return mAlternativeDataCacheEntryId
;
201 const nsACString
& AlternativeDataStreamListener::GetAlternativeDataType()
203 return mAlternativeDataType
;
206 already_AddRefed
<nsIInputStream
>
207 AlternativeDataStreamListener::GetAlternativeInputStream() {
208 nsCOMPtr
<nsIInputStream
> inputStream
= mPipeAlternativeInputStream
;
209 return inputStream
.forget();
212 already_AddRefed
<nsICacheInfoChannel
>
213 AlternativeDataStreamListener::GetCacheInfoChannel() {
214 nsCOMPtr
<nsICacheInfoChannel
> channel
= mCacheInfoChannel
;
215 return channel
.forget();
219 AlternativeDataStreamListener::OnStartRequest(nsIRequest
* aRequest
) {
220 AssertIsOnMainThread();
221 MOZ_ASSERT(!mAlternativeDataType
.IsEmpty());
222 // Checking the alternative data type is the same between we asked and the
223 // saved in the channel.
224 nsAutoCString alternativeDataType
;
225 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(aRequest
);
226 mStatus
= AlternativeDataStreamListener::LOADING
;
227 if (cic
&& NS_SUCCEEDED(cic
->GetAlternativeDataType(alternativeDataType
)) &&
228 mAlternativeDataType
.Equals(alternativeDataType
) &&
229 NS_SUCCEEDED(cic
->GetCacheEntryId(&mAlternativeDataCacheEntryId
))) {
230 MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeInputStream
);
231 MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeOutputStream
);
232 NS_NewPipe(getter_AddRefs(mPipeAlternativeInputStream
),
233 getter_AddRefs(mPipeAlternativeOutputStream
),
234 0 /* default segment size */, UINT32_MAX
/* infinite pipe */,
235 true /* non-blocking input, otherwise you deadlock */,
236 false /* blocking output, since the pipe is 'in'finite */);
238 MOZ_DIAGNOSTIC_ASSERT(!mCacheInfoChannel
);
239 mCacheInfoChannel
= cic
;
241 // call FetchDriver::HttpFetch to load main body
242 MOZ_ASSERT(mFetchDriver
);
243 return mFetchDriver
->HttpFetch();
245 // Needn't load alternative data, since alternative data does not exist.
246 // Set status to FALLBACK to reuse the opened channel to load main body,
247 // then call FetchDriver::OnStartRequest to continue the work. Unfortunately
248 // can't change the stream listener to mFetchDriver, need to keep
249 // AlternativeDataStreamListener alive to redirect OnDataAvailable and
250 // OnStopRequest to mFetchDriver.
251 MOZ_ASSERT(alternativeDataType
.IsEmpty());
252 mStatus
= AlternativeDataStreamListener::FALLBACK
;
253 mAlternativeDataCacheEntryId
= 0;
254 MOZ_ASSERT(mFetchDriver
);
255 return mFetchDriver
->OnStartRequest(aRequest
);
259 AlternativeDataStreamListener::OnDataAvailable(nsIRequest
* aRequest
,
260 nsIInputStream
* aInputStream
,
263 if (mStatus
== AlternativeDataStreamListener::LOADING
) {
264 MOZ_ASSERT(mPipeAlternativeOutputStream
);
266 return aInputStream
->ReadSegments(
267 NS_CopySegmentToStream
, mPipeAlternativeOutputStream
, aCount
, &read
);
269 if (mStatus
== AlternativeDataStreamListener::FALLBACK
) {
270 MOZ_ASSERT(mFetchDriver
);
271 return mFetchDriver
->OnDataAvailable(aRequest
, aInputStream
, aOffset
,
278 AlternativeDataStreamListener::OnStopRequest(nsIRequest
* aRequest
,
279 nsresult aStatusCode
) {
280 AssertIsOnMainThread();
282 // Alternative data loading is going to finish, breaking the reference cycle
283 // here by taking the ownership to a loacl variable.
284 RefPtr
<FetchDriver
> fetchDriver
= std::move(mFetchDriver
);
286 if (mStatus
== AlternativeDataStreamListener::CANCELED
) {
291 if (mStatus
== AlternativeDataStreamListener::FALLBACK
) {
292 MOZ_ASSERT(fetchDriver
);
293 return fetchDriver
->OnStopRequest(aRequest
, aStatusCode
);
296 MOZ_DIAGNOSTIC_ASSERT(mStatus
== AlternativeDataStreamListener::LOADING
);
298 MOZ_ASSERT(!mAlternativeDataType
.IsEmpty() && mPipeAlternativeOutputStream
&&
299 mPipeAlternativeInputStream
);
301 mPipeAlternativeOutputStream
->Close();
302 mPipeAlternativeOutputStream
= nullptr;
304 // Cleanup the states for alternative data if needed.
305 if (NS_FAILED(aStatusCode
)) {
306 mAlternativeDataCacheEntryId
= 0;
307 mCacheInfoChannel
= nullptr;
308 mPipeAlternativeInputStream
= nullptr;
310 mStatus
= AlternativeDataStreamListener::COMPLETED
;
311 // alternative data loading finish, call FetchDriver::FinishOnStopRequest to
312 // continue the final step for the case FetchDriver::OnStopRequest is called
313 // earlier than AlternativeDataStreamListener::OnStopRequest
314 MOZ_ASSERT(fetchDriver
);
315 fetchDriver
->FinishOnStopRequest(this);
320 AlternativeDataStreamListener::CheckListenerChain() { return NS_OK
; }
321 //-----------------------------------------------------------------------------
323 //-----------------------------------------------------------------------------
325 NS_IMPL_ISUPPORTS(FetchDriver
, nsIStreamListener
, nsIChannelEventSink
,
326 nsIInterfaceRequestor
, nsIThreadRetargetableStreamListener
,
327 nsINetworkInterceptController
)
329 FetchDriver::FetchDriver(SafeRefPtr
<InternalRequest
> aRequest
,
330 nsIPrincipal
* aPrincipal
, nsILoadGroup
* aLoadGroup
,
331 nsIEventTarget
* aMainThreadEventTarget
,
332 nsICookieJarSettings
* aCookieJarSettings
,
333 PerformanceStorage
* aPerformanceStorage
,
334 bool aIsTrackingFetch
)
335 : mPrincipal(aPrincipal
),
336 mLoadGroup(aLoadGroup
),
337 mRequest(std::move(aRequest
)),
338 mMainThreadEventTarget(aMainThreadEventTarget
),
339 mCookieJarSettings(aCookieJarSettings
),
340 mPerformanceStorage(aPerformanceStorage
),
341 mNeedToObserveOnDataAvailable(false),
342 mIsTrackingFetch(aIsTrackingFetch
),
343 mOnStopRequestCalled(false)
346 mResponseAvailableCalled(false),
350 AssertIsOnMainThread();
352 MOZ_ASSERT(mRequest
);
353 MOZ_ASSERT(aPrincipal
);
354 MOZ_ASSERT(aMainThreadEventTarget
);
357 FetchDriver::~FetchDriver() {
358 AssertIsOnMainThread();
360 // We assert this since even on failures, we should call
361 // FailWithNetworkError().
362 MOZ_ASSERT(mResponseAvailableCalled
);
365 already_AddRefed
<PreloaderBase
> FetchDriver::FindPreload(nsIURI
* aURI
) {
366 // Decide if we allow reuse of an existing <link rel=preload as=fetch>
367 // response for this request. First examine this fetch requets itself if it
368 // is 'pure' enough to use the response and then try to find a preload.
371 // Preloads are mapped on the document, no document, no preload.
375 switch (mRequest
->Mode()) {
376 case RequestMode::No_cors
:
377 cors
= CORSMode::CORS_NONE
;
379 case RequestMode::Cors
:
380 cors
= mRequest
->GetCredentialsMode() == RequestCredentials::Include
381 ? CORSMode::CORS_USE_CREDENTIALS
382 : CORSMode::CORS_ANONYMOUS
;
385 // Can't be satisfied by a preload because preload cannot define any of
389 if (!mRequest
->Headers()->HasOnlySimpleHeaders()) {
390 // Preload can't set any headers.
393 if (!mRequest
->GetIntegrity().IsEmpty()) {
394 // There is currently no support for SRI checking in the fetch preloader.
397 if (mRequest
->GetCacheMode() != RequestCache::Default
) {
398 // Preload can only go with the default caching mode.
401 if (mRequest
->SkipServiceWorker()) {
402 // Preload can't be forbidden interception.
405 if (mRequest
->GetRedirectMode() != RequestRedirect::Follow
) {
406 // Preload always follows redirects.
409 nsAutoCString method
;
410 mRequest
->GetMethod(method
);
411 if (!method
.EqualsLiteral("GET")) {
412 // Preload can only do GET, this also eliminates the case we do upload, so
413 // no need to check if the request has any body to send out.
417 // OK, this request can be satisfied by a preloaded response, try to find one.
419 auto preloadKey
= PreloadHashKey::CreateAsFetch(aURI
, cors
);
420 return mDocument
->Preloads().LookupPreload(preloadKey
);
423 void FetchDriver::UpdateReferrerInfoFromNewChannel(nsIChannel
* aChannel
) {
424 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
429 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= httpChannel
->GetReferrerInfo();
434 nsAutoString computedReferrerSpec
;
435 mRequest
->SetReferrerPolicy(referrerInfo
->ReferrerPolicy());
436 Unused
<< referrerInfo
->GetComputedReferrerSpec(computedReferrerSpec
);
437 mRequest
->SetReferrer(computedReferrerSpec
);
440 nsresult
FetchDriver::Fetch(AbortSignalImpl
* aSignalImpl
,
441 FetchDriverObserver
* aObserver
) {
442 AssertIsOnMainThread();
444 MOZ_ASSERT(!mFetchCalled
);
448 mObserver
= aObserver
;
450 // FIXME(nsm): Deal with HSTS.
452 MOZ_RELEASE_ASSERT(!mRequest
->IsSynchronous(),
453 "Synchronous fetch not supported");
455 UniquePtr
<mozilla::ipc::PrincipalInfo
> principalInfo(
456 new mozilla::ipc::PrincipalInfo());
457 nsresult rv
= PrincipalToPrincipalInfo(mPrincipal
, principalInfo
.get());
458 if (NS_WARN_IF(NS_FAILED(rv
))) {
462 mRequest
->SetPrincipalInfo(std::move(principalInfo
));
464 // If the signal is aborted, it's time to inform the observer and terminate
467 if (aSignalImpl
->Aborted()) {
468 FetchDriverAbortActions(aSignalImpl
);
475 rv
= HttpFetch(mRequest
->GetPreferredAlternativeDataType());
477 FailWithNetworkError(rv
);
480 // Any failure is handled by FailWithNetworkError notifying the aObserver.
484 // This function implements the "HTTP Fetch" algorithm from the Fetch spec.
485 // Functionality is often split between here, the CORS listener proxy and the
486 // Necko HTTP implementation.
487 nsresult
FetchDriver::HttpFetch(
488 const nsACString
& aPreferredAlternativeDataType
) {
489 MOZ_ASSERT(NS_IsMainThread());
491 // Step 1. "Let response be null."
493 mOnStopRequestCalled
= false;
496 nsCOMPtr
<nsIIOService
> ios
= do_GetIOService(&rv
);
497 NS_ENSURE_SUCCESS(rv
, rv
);
500 mRequest
->GetURL(url
);
501 nsCOMPtr
<nsIURI
> uri
;
502 rv
= NS_NewURI(getter_AddRefs(uri
), url
);
503 NS_ENSURE_SUCCESS(rv
, rv
);
505 // Unsafe requests aren't allowed with when using no-core mode.
506 if (mRequest
->Mode() == RequestMode::No_cors
&& mRequest
->UnsafeRequest() &&
507 (!mRequest
->HasSimpleMethod() ||
508 !mRequest
->Headers()->HasOnlySimpleHeaders())) {
509 MOZ_ASSERT(false, "The API should have caught this");
510 return NS_ERROR_DOM_BAD_URI
;
513 // non-GET requests aren't allowed for blob.
514 if (IsBlobURI(uri
)) {
515 nsAutoCString method
;
516 mRequest
->GetMethod(method
);
517 if (!method
.EqualsLiteral("GET")) {
518 return NS_ERROR_DOM_NETWORK_ERR
;
522 RefPtr
<PreloaderBase
> fetchPreload
= FindPreload(uri
);
524 fetchPreload
->RemoveSelf(mDocument
);
525 fetchPreload
->NotifyUsage(mDocument
, PreloaderBase::LoadBackground::Keep
);
527 rv
= fetchPreload
->AsyncConsume(this);
528 if (NS_SUCCEEDED(rv
)) {
531 mChannel
= fetchPreload
->Channel();
532 MOZ_ASSERT(mChannel
);
533 mChannel
->SetNotificationCallbacks(this);
535 // Copied from AsyncOnChannelRedirect.
536 for (const auto& redirect
: fetchPreload
->Redirects()) {
537 if (redirect
.Flags() & nsIChannelEventSink::REDIRECT_INTERNAL
) {
538 mRequest
->SetURLForInternalRedirect(redirect
.Flags(), redirect
.Spec(),
539 redirect
.Fragment());
541 mRequest
->AddURL(redirect
.Spec(), redirect
.Fragment());
548 // The preload failed to be consumed. Behave like there were no preload.
549 fetchPreload
= nullptr;
552 // Step 2 deals with letting ServiceWorkers intercept requests. This is
553 // handled by Necko after the channel is opened.
554 // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
555 // set based on the Request's flag.
557 // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
558 // true..." is handled by the CORS proxy.
560 // Step 3.2 "Set request's skip service worker flag." This isn't required
561 // since Necko will fall back to the network if the ServiceWorker does not
562 // respond with a valid Response.
564 // NS_StartCORSPreflight() will automatically kick off the original request
565 // if it succeeds, so we need to have everything setup for the original
568 // Step 3.3 "Let credentials flag be set if one of
569 // - request's credentials mode is "include"
570 // - request's credentials mode is "same-origin" and either the CORS flag
571 // is unset or response tainting is "opaque"
572 // is true, and unset otherwise."
574 // Set skip serviceworker flag.
575 // While the spec also gates on the client being a ServiceWorker, we can't
576 // infer that here. Instead we rely on callers to set the flag correctly.
577 const nsLoadFlags bypassFlag
= mRequest
->SkipServiceWorker()
578 ? nsIChannel::LOAD_BYPASS_SERVICE_WORKER
581 nsSecurityFlags secFlags
= 0;
582 if (mRequest
->Mode() == RequestMode::Cors
) {
583 secFlags
|= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
;
584 } else if (mRequest
->Mode() == RequestMode::Same_origin
||
585 mRequest
->Mode() == RequestMode::Navigate
) {
586 secFlags
|= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT
;
587 } else if (mRequest
->Mode() == RequestMode::No_cors
) {
588 secFlags
|= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
;
590 MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
591 return NS_ERROR_UNEXPECTED
;
594 if (mRequest
->GetRedirectMode() != RequestRedirect::Follow
) {
595 secFlags
|= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS
;
598 // This handles the use credentials flag in "HTTP
599 // network or cache fetch" in the spec and decides whether to transmit
600 // cookies and other identifying information.
601 if (mRequest
->GetCredentialsMode() == RequestCredentials::Include
) {
602 secFlags
|= nsILoadInfo::SEC_COOKIES_INCLUDE
;
603 } else if (mRequest
->GetCredentialsMode() == RequestCredentials::Omit
) {
604 secFlags
|= nsILoadInfo::SEC_COOKIES_OMIT
;
605 } else if (mRequest
->GetCredentialsMode() ==
606 RequestCredentials::Same_origin
) {
607 secFlags
|= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN
;
609 MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!");
610 return NS_ERROR_UNEXPECTED
;
613 // From here on we create a channel and set its properties with the
614 // information from the InternalRequest. This is an implementation detail.
615 MOZ_ASSERT(mLoadGroup
);
616 nsCOMPtr
<nsIChannel
> chan
;
618 nsLoadFlags loadFlags
= nsIRequest::LOAD_BACKGROUND
| bypassFlag
;
620 MOZ_ASSERT(mDocument
->NodePrincipal() == mPrincipal
);
621 MOZ_ASSERT(mDocument
->CookieJarSettings() == mCookieJarSettings
);
622 rv
= NS_NewChannel(getter_AddRefs(chan
), uri
, mDocument
, secFlags
,
623 mRequest
->ContentPolicyType(),
624 nullptr, /* aPerformanceStorage */
625 mLoadGroup
, nullptr, /* aCallbacks */
627 } else if (mClientInfo
.isSome()) {
628 rv
= NS_NewChannel(getter_AddRefs(chan
), uri
, mPrincipal
, mClientInfo
.ref(),
629 mController
, secFlags
, mRequest
->ContentPolicyType(),
630 mCookieJarSettings
, mPerformanceStorage
, mLoadGroup
,
631 nullptr, /* aCallbacks */
635 NS_NewChannel(getter_AddRefs(chan
), uri
, mPrincipal
, secFlags
,
636 mRequest
->ContentPolicyType(), mCookieJarSettings
,
637 mPerformanceStorage
, mLoadGroup
, nullptr, /* aCallbacks */
640 NS_ENSURE_SUCCESS(rv
, rv
);
642 if (mCSPEventListener
) {
643 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
644 rv
= loadInfo
->SetCspEventListener(mCSPEventListener
);
645 NS_ENSURE_SUCCESS(rv
, rv
);
649 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
650 rv
= loadInfo
->SetLoadingEmbedderPolicy(mRequest
->GetEmbedderPolicy());
651 NS_ENSURE_SUCCESS(rv
, rv
);
654 if (mAssociatedBrowsingContextID
) {
655 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
656 rv
= loadInfo
->SetWorkerAssociatedBrowsingContextID(
657 mAssociatedBrowsingContextID
);
660 // If the fetch is created by FetchEvent.request or NavigationPreload request,
661 // corresponding InterceptedHttpChannel information need to propagte to the
662 // channel of the fetch.
663 if (mRequest
->GetInterceptionTriggeringPrincipalInfo()) {
664 auto principalOrErr
= mozilla::ipc::PrincipalInfoToPrincipal(
665 *(mRequest
->GetInterceptionTriggeringPrincipalInfo().get()));
666 if (!principalOrErr
.isErr()) {
667 nsCOMPtr
<nsIPrincipal
> principal
= principalOrErr
.unwrap();
669 nsTArray
<nsCOMPtr
<nsIRedirectHistoryEntry
>> redirectChain
;
670 if (!mRequest
->InterceptionRedirectChain().IsEmpty()) {
671 for (const RedirectHistoryEntryInfo
& entryInfo
:
672 mRequest
->InterceptionRedirectChain()) {
673 nsCOMPtr
<nsIRedirectHistoryEntry
> entry
=
674 mozilla::ipc::RHEntryInfoToRHEntry(entryInfo
);
675 redirectChain
.AppendElement(entry
);
679 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
680 MOZ_ASSERT(loadInfo
);
681 loadInfo
->SetInterceptionInfo(new mozilla::net::InterceptionInfo(
682 principal
, mRequest
->InterceptionContentPolicyType(), redirectChain
,
683 mRequest
->InterceptionFromThirdParty()));
687 if (mDocument
&& mDocument
->GetEmbedderElement() &&
688 mDocument
->GetEmbedderElement()->IsAnyOfHTMLElements(nsGkAtoms::object
,
690 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
691 rv
= loadInfo
->SetIsFromObjectOrEmbed(true);
692 NS_ENSURE_SUCCESS(rv
, rv
);
695 // Insert ourselves into the notification callbacks chain so we can set
696 // headers on redirects.
699 nsCOMPtr
<nsIInterfaceRequestor
> notificationCallbacks
;
700 chan
->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks
));
701 MOZ_ASSERT(!notificationCallbacks
);
704 chan
->SetNotificationCallbacks(this);
706 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(chan
));
707 // Mark channel as urgent-start if the Fetch is triggered by user input
709 if (cos
&& UserActivation::IsHandlingUserInput()) {
710 cos
->AddClassFlags(nsIClassOfService::UrgentStart
);
713 // Step 3.5 begins "HTTP network or cache fetch".
714 // HTTP network or cache fetch
715 // ---------------------------
716 // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
717 nsCOMPtr
<nsIHttpChannel
> httpChan
= do_QueryInterface(chan
);
720 nsAutoCString method
;
721 mRequest
->GetMethod(method
);
722 rv
= httpChan
->SetRequestMethod(method
);
723 NS_ENSURE_SUCCESS(rv
, rv
);
725 // Set the same headers.
726 SetRequestHeaders(httpChan
, false, false);
728 // Step 5 of https://fetch.spec.whatwg.org/#main-fetch
729 // If request's referrer policy is the empty string and request's client is
730 // non-null, then set request's referrer policy to request's client's
731 // associated referrer policy.
732 // Basically, "client" is not in our implementation, we use
733 // EnvironmentReferrerPolicy of the worker or document context
734 ReferrerPolicy referrerPolicy
= mRequest
->GetEnvironmentReferrerPolicy();
735 if (mRequest
->ReferrerPolicy_() == ReferrerPolicy::_empty
) {
736 mRequest
->SetReferrerPolicy(referrerPolicy
);
738 // Step 6 of https://fetch.spec.whatwg.org/#main-fetch
739 // If request’s referrer policy is the empty string,
740 // then set request’s referrer policy to the user-set default policy.
741 if (mRequest
->ReferrerPolicy_() == ReferrerPolicy::_empty
) {
742 nsCOMPtr
<nsILoadInfo
> loadInfo
= httpChan
->LoadInfo();
743 bool isPrivate
= loadInfo
->GetOriginAttributes().mPrivateBrowsingId
> 0;
745 ReferrerInfo::GetDefaultReferrerPolicy(httpChan
, uri
, isPrivate
);
746 mRequest
->SetReferrerPolicy(referrerPolicy
);
749 rv
= FetchUtil::SetRequestReferrer(mPrincipal
, mDocument
, httpChan
,
751 NS_ENSURE_SUCCESS(rv
, rv
);
753 // Bug 1120722 - Authorization will be handled later.
754 // Auth may require prompting, we don't support it yet.
755 // The next patch in this same bug prevents this from aborting the request.
756 // Credentials checks for CORS are handled by nsCORSListenerProxy,
758 nsCOMPtr
<nsIHttpChannelInternal
> internalChan
= do_QueryInterface(httpChan
);
760 rv
= internalChan
->SetRequestMode(mRequest
->Mode());
761 MOZ_ASSERT(NS_SUCCEEDED(rv
));
762 // Conversion between enumerations is safe due to static asserts in
763 // dom/workers/ServiceWorkerManager.cpp
764 rv
= internalChan
->SetRedirectMode(
765 static_cast<uint32_t>(mRequest
->GetRedirectMode()));
766 MOZ_ASSERT(NS_SUCCEEDED(rv
));
767 mRequest
->MaybeSkipCacheIfPerformingRevalidation();
768 rv
= internalChan
->SetFetchCacheMode(
769 static_cast<uint32_t>(mRequest
->GetCacheMode()));
770 MOZ_ASSERT(NS_SUCCEEDED(rv
));
771 rv
= internalChan
->SetIntegrityMetadata(mRequest
->GetIntegrity());
772 MOZ_ASSERT(NS_SUCCEEDED(rv
));
774 // Set the initiator type
775 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChan
));
777 timedChannel
->SetInitiatorType(u
"fetch"_ns
);
781 // Step 5. Proxy authentication will be handled by Necko.
783 // Continue setting up 'HTTPRequest'. Content-Type and body data.
784 nsCOMPtr
<nsIUploadChannel2
> uploadChan
= do_QueryInterface(chan
);
786 nsAutoCString contentType
;
788 mRequest
->Headers()->GetFirst("content-type"_ns
, contentType
, result
);
789 // We don't actually expect "result" to have failed here: that only happens
790 // for invalid header names. But if for some reason it did, just propagate
792 if (result
.Failed()) {
793 return result
.StealNSResult();
796 // Now contentType is the header that was set in mRequest->Headers(), or a
797 // void string if no header was set.
799 bool hasContentTypeHeader
=
800 mRequest
->Headers()->Has("content-type"_ns
, result
);
801 MOZ_ASSERT(!result
.Failed());
802 MOZ_ASSERT_IF(!hasContentTypeHeader
, contentType
.IsVoid());
806 nsCOMPtr
<nsIInputStream
> bodyStream
;
807 mRequest
->GetBody(getter_AddRefs(bodyStream
), &bodyLength
);
809 nsAutoCString method
;
810 mRequest
->GetMethod(method
);
811 rv
= uploadChan
->ExplicitSetUploadStream(bodyStream
, contentType
,
813 false /* aStreamHasHeaders */);
814 NS_ENSURE_SUCCESS(rv
, rv
);
818 // If preflight is required, start a "CORS preflight fetch"
819 // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
820 // implementation is handled by the http channel calling into
821 // nsCORSListenerProxy. We just inform it which unsafe headers are included
823 if (mRequest
->Mode() == RequestMode::Cors
) {
824 AutoTArray
<nsCString
, 5> unsafeHeaders
;
825 mRequest
->Headers()->GetUnsafeHeaders(unsafeHeaders
);
826 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
827 loadInfo
->SetCorsPreflightInfo(unsafeHeaders
, false);
830 if (mIsTrackingFetch
&& StaticPrefs::network_http_tailing_enabled() && cos
) {
831 cos
->AddClassFlags(nsIClassOfService::Throttleable
|
832 nsIClassOfService::Tail
);
835 if (mIsTrackingFetch
&&
836 StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
837 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(chan
);
839 p
->SetPriority(nsISupportsPriority::PRIORITY_LOWEST
);
843 NotifyNetworkMonitorAlternateStack(chan
, std::move(mOriginStack
));
844 if (mObserver
&& httpChan
) {
845 mObserver
->OnNotifyNetworkMonitorAlternateStack(httpChan
->ChannelId());
848 // Should set a Content-Range header for blob scheme, and also slice the
849 // blob appropriately, so we process the Range header here for later use.
850 if (IsBlobURI(uri
)) {
853 mRequest
->Headers()->Get("Range"_ns
, range
, result
);
854 MOZ_ASSERT(!result
.Failed());
855 if (!range
.IsVoid()) {
856 rv
= NS_SetChannelContentRangeForBlobURI(chan
, uri
, range
);
863 // if the preferred alternative data type in InternalRequest is not empty, set
864 // the data type on the created channel and also create a
865 // AlternativeDataStreamListener to be the stream listener of the channel.
866 if (!aPreferredAlternativeDataType
.IsEmpty()) {
867 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(chan
);
869 cic
->PreferAlternativeDataType(
870 aPreferredAlternativeDataType
, ""_ns
,
871 nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::ASYNC
);
872 MOZ_ASSERT(!mAltDataListener
);
873 mAltDataListener
= new AlternativeDataStreamListener(
874 this, chan
, aPreferredAlternativeDataType
);
875 rv
= chan
->AsyncOpen(mAltDataListener
);
877 rv
= chan
->AsyncOpen(this);
880 // Integrity check cannot be done on alt-data yet.
881 if (mRequest
->GetIntegrity().IsEmpty()) {
882 MOZ_ASSERT(!FetchUtil::WasmAltDataType
.IsEmpty());
883 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(chan
);
884 if (cic
&& StaticPrefs::javascript_options_wasm_caching() &&
885 !mRequest
->SkipWasmCaching()) {
886 cic
->PreferAlternativeDataType(
887 FetchUtil::WasmAltDataType
, nsLiteralCString(WASM_CONTENT_TYPE
),
888 nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::
893 rv
= chan
->AsyncOpen(this);
900 // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
906 SafeRefPtr
<InternalResponse
> FetchDriver::BeginAndGetFilteredResponse(
907 SafeRefPtr
<InternalResponse
> aResponse
, bool aFoundOpaqueRedirect
) {
908 MOZ_ASSERT(aResponse
);
909 AutoTArray
<nsCString
, 4> reqURLList
;
910 mRequest
->GetURLListWithoutFragment(reqURLList
);
911 MOZ_ASSERT(!reqURLList
.IsEmpty());
912 aResponse
->SetURLList(reqURLList
);
913 SafeRefPtr
<InternalResponse
> filteredResponse
;
914 if (aFoundOpaqueRedirect
) {
915 filteredResponse
= aResponse
->OpaqueRedirectResponse();
917 switch (mRequest
->GetResponseTainting()) {
918 case LoadTainting::Basic
:
919 filteredResponse
= aResponse
->BasicResponse();
921 case LoadTainting::CORS
:
922 filteredResponse
= aResponse
->CORSResponse();
924 case LoadTainting::Opaque
: {
925 filteredResponse
= aResponse
->OpaqueResponse();
926 nsresult rv
= filteredResponse
->GeneratePaddingInfo();
927 if (NS_WARN_IF(NS_FAILED(rv
))) {
933 MOZ_CRASH("Unexpected case");
937 MOZ_ASSERT(filteredResponse
);
938 MOZ_ASSERT(mObserver
);
939 MOZ_ASSERT(filteredResponse
);
940 if (!ShouldCheckSRI(*mRequest
, *filteredResponse
)) {
941 // Need to keep mObserver alive.
942 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
943 observer
->OnResponseAvailable(filteredResponse
.clonePtr());
945 mResponseAvailableCalled
= true;
949 return filteredResponse
;
952 void FetchDriver::FailWithNetworkError(nsresult rv
) {
953 AssertIsOnMainThread();
955 // Need to keep mObserver alive.
956 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
957 observer
->OnResponseAvailable(InternalResponse::NetworkError(rv
));
959 mResponseAvailableCalled
= true;
963 // mObserver could be null after OnResponseAvailable().
965 mObserver
->OnResponseEnd(FetchDriverObserver::eByNetworking
,
966 JS::UndefinedHandleValue
);
975 FetchDriver::OnStartRequest(nsIRequest
* aRequest
) {
976 AssertIsOnMainThread();
978 // Note, this can be called multiple times if we are doing an opaqueredirect.
979 // In that case we will get a simulated OnStartRequest() and then the real
980 // channel will call in with an errored OnStartRequest().
982 if (mFromPreload
&& mAborted
) {
983 aRequest
->CancelWithReason(NS_BINDING_ABORTED
,
984 "FetchDriver::OnStartRequest aborted"_ns
);
985 return NS_BINDING_ABORTED
;
989 MOZ_ASSERT(!mObserver
);
990 return NS_BINDING_ABORTED
;
994 aRequest
->GetStatus(&rv
);
996 FailWithNetworkError(rv
);
1000 // We should only get to the following code once.
1001 MOZ_ASSERT(!mPipeOutputStream
);
1004 MOZ_ASSERT(false, "We should have mObserver here.");
1005 FailWithNetworkError(NS_ERROR_UNEXPECTED
);
1006 return NS_ERROR_UNEXPECTED
;
1009 mNeedToObserveOnDataAvailable
= mObserver
->NeedOnDataAvailable();
1011 SafeRefPtr
<InternalResponse
> response
;
1012 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
1013 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequest
);
1015 // On a successful redirect we perform the following substeps of HTTP Fetch,
1016 // step 5, "redirect status", step 11.
1018 bool foundOpaqueRedirect
= false;
1020 nsAutoCString contentType
;
1022 int64_t contentLength
= InternalResponse::UNKNOWN_BODY_SIZE
;
1023 rv
= channel
->GetContentLength(&contentLength
);
1024 MOZ_ASSERT_IF(NS_FAILED(rv
),
1025 contentLength
== InternalResponse::UNKNOWN_BODY_SIZE
);
1028 channel
->GetContentType(contentType
);
1030 uint32_t responseStatus
= 0;
1031 rv
= httpChannel
->GetResponseStatus(&responseStatus
);
1032 if (NS_FAILED(rv
)) {
1033 FailWithNetworkError(rv
);
1037 if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus
)) {
1038 if (mRequest
->GetRedirectMode() == RequestRedirect::Error
) {
1039 FailWithNetworkError(NS_BINDING_ABORTED
);
1040 return NS_BINDING_FAILED
;
1042 if (mRequest
->GetRedirectMode() == RequestRedirect::Manual
) {
1043 foundOpaqueRedirect
= true;
1047 nsAutoCString statusText
;
1048 rv
= httpChannel
->GetResponseStatusText(statusText
);
1049 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1051 response
= MakeSafeRefPtr
<InternalResponse
>(responseStatus
, statusText
,
1052 mRequest
->GetCredentialsMode());
1054 UniquePtr
<mozilla::ipc::PrincipalInfo
> principalInfo(
1055 new mozilla::ipc::PrincipalInfo());
1056 nsresult rv
= PrincipalToPrincipalInfo(mPrincipal
, principalInfo
.get());
1057 if (NS_WARN_IF(NS_FAILED(rv
))) {
1061 response
->SetPrincipalInfo(std::move(principalInfo
));
1063 response
->Headers()->FillResponseHeaders(httpChannel
);
1065 // If Content-Encoding or Transfer-Encoding headers are set, then the actual
1066 // Content-Length (which refer to the decoded data) is obscured behind the
1069 if (response
->Headers()->Has("content-encoding"_ns
, result
) ||
1070 response
->Headers()->Has("transfer-encoding"_ns
, result
)) {
1071 // We cannot trust the content-length when content-encoding or
1072 // transfer-encoding are set. There are many servers which just
1074 contentLength
= InternalResponse::UNKNOWN_BODY_SIZE
;
1076 MOZ_ASSERT(!result
.Failed());
1078 // Should set a Content-Range header for blob scheme
1079 // (https://fetch.spec.whatwg.org/#scheme-fetch)
1080 nsAutoCString
contentRange(VoidCString());
1081 nsCOMPtr
<nsIURI
> uri
;
1082 channel
->GetURI(getter_AddRefs(uri
));
1083 if (IsBlobURI(uri
)) {
1084 nsBaseChannel
* bchan
= static_cast<nsBaseChannel
*>(channel
.get());
1086 Maybe
<nsBaseChannel::ContentRange
> range
= bchan
->GetContentRange();
1087 if (range
.isSome()) {
1088 range
->AsHeader(contentRange
);
1092 response
= MakeSafeRefPtr
<InternalResponse
>(
1093 contentRange
.IsVoid() ? 200 : 206,
1094 contentRange
.IsVoid() ? "OK"_ns
: "Partial Content"_ns
,
1095 mRequest
->GetCredentialsMode());
1097 IgnoredErrorResult result
;
1098 if (!contentRange
.IsVoid()) {
1099 response
->Headers()->Append("Content-Range"_ns
, contentRange
, result
);
1100 MOZ_ASSERT(!result
.Failed());
1103 if (uri
&& uri
->SchemeIs("data")) {
1104 nsDataChannel
* dchan
= static_cast<nsDataChannel
*>(channel
.get());
1106 contentType
.Assign(dchan
->MimeType());
1108 channel
->GetContentType(contentType
);
1109 if (!contentType
.IsEmpty()) {
1110 nsAutoCString contentCharset
;
1111 channel
->GetContentCharset(contentCharset
);
1112 if (NS_SUCCEEDED(rv
) && !contentCharset
.IsEmpty()) {
1113 contentType
+= ";charset="_ns
+ contentCharset
;
1118 response
->Headers()->Append("Content-Type"_ns
, contentType
, result
);
1119 MOZ_ASSERT(!result
.Failed());
1121 if (contentLength
>= 0) {
1122 nsAutoCString contentLenStr
;
1123 contentLenStr
.AppendInt(contentLength
);
1125 IgnoredErrorResult result
;
1126 response
->Headers()->Append("Content-Length"_ns
, contentLenStr
, result
);
1127 MOZ_ASSERT(!result
.Failed());
1131 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(aRequest
);
1133 if (mAltDataListener
) {
1134 // Skip the case that mAltDataListener->Status() equals to FALLBACK, that
1135 // means the opened channel for alternative data loading is reused for
1136 // loading the main data.
1137 if (mAltDataListener
->Status() !=
1138 AlternativeDataStreamListener::FALLBACK
) {
1139 // Verify the cache ID is the same with from alternative data cache.
1140 // If the cache ID is different, droping the alternative data loading,
1141 // otherwise setup the response's alternative body and cacheInfoChannel.
1142 uint64_t cacheEntryId
= 0;
1143 if (NS_SUCCEEDED(cic
->GetCacheEntryId(&cacheEntryId
)) &&
1145 mAltDataListener
->GetAlternativeDataCacheEntryId()) {
1146 mAltDataListener
->Cancel();
1148 // AlternativeDataStreamListener::OnStartRequest had already been
1149 // called, the alternative data input stream and cacheInfo channel
1151 nsCOMPtr
<nsICacheInfoChannel
> cacheInfo
=
1152 mAltDataListener
->GetCacheInfoChannel();
1153 nsCOMPtr
<nsIInputStream
> altInputStream
=
1154 mAltDataListener
->GetAlternativeInputStream();
1155 MOZ_ASSERT(altInputStream
&& cacheInfo
);
1156 response
->SetAlternativeBody(altInputStream
);
1157 nsMainThreadPtrHandle
<nsICacheInfoChannel
> handle(
1158 new nsMainThreadPtrHolder
<nsICacheInfoChannel
>(
1159 "nsICacheInfoChannel", cacheInfo
, false));
1160 response
->SetCacheInfoChannel(handle
);
1162 } else if (!mAltDataListener
->GetAlternativeDataType().IsEmpty()) {
1163 // If the status is FALLBACK and the
1164 // mAltDataListener::mAlternativeDataType is not empty, that means the
1165 // data need to be saved into cache, setup the response's
1166 // nsICacheInfoChannel for caching the data after loading.
1167 nsMainThreadPtrHandle
<nsICacheInfoChannel
> handle(
1168 new nsMainThreadPtrHolder
<nsICacheInfoChannel
>(
1169 "nsICacheInfoChannel", cic
, false));
1170 response
->SetCacheInfoChannel(handle
);
1172 } else if (!cic
->PreferredAlternativeDataTypes().IsEmpty()) {
1173 MOZ_ASSERT(cic
->PreferredAlternativeDataTypes().Length() == 1);
1174 MOZ_ASSERT(cic
->PreferredAlternativeDataTypes()[0].type().Equals(
1175 FetchUtil::WasmAltDataType
));
1177 cic
->PreferredAlternativeDataTypes()[0].contentType().EqualsLiteral(
1178 WASM_CONTENT_TYPE
));
1180 if (contentType
.EqualsLiteral(WASM_CONTENT_TYPE
)) {
1181 // We want to attach the CacheInfoChannel to the response object such
1182 // that we can track its origin when the Response object is manipulated
1183 // by JavaScript code. This is important for WebAssembly, which uses
1184 // fetch to query its sources in JavaScript and transfer the Response
1185 // object to other function responsible for storing the alternate data
1186 // using the CacheInfoChannel.
1187 nsMainThreadPtrHandle
<nsICacheInfoChannel
> handle(
1188 new nsMainThreadPtrHolder
<nsICacheInfoChannel
>(
1189 "nsICacheInfoChannel", cic
, false));
1190 response
->SetCacheInfoChannel(handle
);
1195 // Fetch spec Main Fetch step 21: ignore body for head/connect methods.
1196 nsAutoCString method
;
1197 mRequest
->GetMethod(method
);
1198 if (!(method
.EqualsLiteral("HEAD") || method
.EqualsLiteral("CONNECT"))) {
1199 // We open a pipe so that we can immediately set the pipe's read end as the
1200 // response's body. Setting the segment size to UINT32_MAX means that the
1201 // pipe has infinite space. The nsIChannel will continue to buffer data in
1202 // xpcom events even if we block on a fixed size pipe. It might be possible
1203 // to suspend the channel and then resume when there is space available, but
1204 // for now use an infinite pipe to avoid blocking.
1205 nsCOMPtr
<nsIInputStream
> pipeInputStream
;
1206 NS_NewPipe(getter_AddRefs(pipeInputStream
),
1207 getter_AddRefs(mPipeOutputStream
), 0, /* default segment size */
1208 UINT32_MAX
/* infinite pipe */,
1209 true /* non-blocking input, otherwise you deadlock */,
1210 false /* blocking output, since the pipe is 'in'finite */);
1211 response
->SetBody(pipeInputStream
, contentLength
);
1214 // If the request is a file channel, then remember the local path to
1215 // that file so we can later create File blobs rather than plain ones.
1216 nsCOMPtr
<nsIFileChannel
> fc
= do_QueryInterface(aRequest
);
1218 nsCOMPtr
<nsIFile
> file
;
1219 rv
= fc
->GetFile(getter_AddRefs(file
));
1220 if (!NS_WARN_IF(NS_FAILED(rv
))) {
1222 file
->GetPath(path
);
1223 response
->SetBodyLocalPath(path
);
1226 // If the request is a blob URI, then remember that URI so that we
1227 // can later just use that blob instance instead of cloning it.
1228 nsCString blobURISpec
;
1229 GetBlobURISpecFromChannel(aRequest
, blobURISpec
);
1230 if (!blobURISpec
.IsVoid()) {
1231 response
->SetBodyBlobURISpec(blobURISpec
);
1235 response
->InitChannelInfo(channel
);
1237 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
1238 // Propagate any tainting from the channel back to our response here. This
1239 // step is not reflected in the spec because the spec is written such that
1240 // FetchEvent.respondWith() just passes the already-tainted Response back to
1241 // the outer fetch(). In gecko, however, we serialize the Response through
1242 // the channel and must regenerate the tainting from the channel in the
1243 // interception case.
1244 mRequest
->MaybeIncreaseResponseTainting(loadInfo
->GetTainting());
1246 // Resolves fetch() promise which may trigger code running in a worker. Make
1247 // sure the Response is fully initialized before calling this.
1249 BeginAndGetFilteredResponse(std::move(response
), foundOpaqueRedirect
);
1250 if (NS_WARN_IF(!mResponse
)) {
1251 // Fail to generate a paddingInfo for opaque response.
1252 MOZ_DIAGNOSTIC_ASSERT(mRequest
->GetResponseTainting() ==
1253 LoadTainting::Opaque
&&
1254 !foundOpaqueRedirect
);
1255 FailWithNetworkError(NS_ERROR_UNEXPECTED
);
1256 return NS_ERROR_UNEXPECTED
;
1259 // From "Main Fetch" step 19: SRI-part1.
1260 if (ShouldCheckSRI(*mRequest
, *mResponse
) && mSRIMetadata
.IsEmpty()) {
1261 nsIConsoleReportCollector
* reporter
= nullptr;
1263 reporter
= mObserver
->GetReporter();
1266 nsAutoCString sourceUri
;
1267 if (mDocument
&& mDocument
->GetDocumentURI()) {
1268 mDocument
->GetDocumentURI()->GetAsciiSpec(sourceUri
);
1269 } else if (!mWorkerScript
.IsEmpty()) {
1270 sourceUri
.Assign(mWorkerScript
);
1272 SRICheck::IntegrityMetadata(mRequest
->GetIntegrity(), sourceUri
, reporter
,
1275 MakeUnique
<SRICheckDataVerifier
>(mSRIMetadata
, sourceUri
, reporter
);
1277 // Do not retarget off main thread when using SRI API.
1281 nsCOMPtr
<nsIEventTarget
> sts
=
1282 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
, &rv
);
1283 if (NS_WARN_IF(NS_FAILED(rv
))) {
1284 FailWithNetworkError(rv
);
1289 // Try to retarget off main thread.
1290 if (nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(aRequest
)) {
1291 RefPtr
<TaskQueue
> queue
=
1292 TaskQueue::Create(sts
.forget(), "FetchDriver STS Delivery Queue");
1293 Unused
<< NS_WARN_IF(NS_FAILED(rr
->RetargetDeliveryTo(queue
)));
1300 // Runnable to call the observer OnDataAvailable on the main-thread.
1301 class DataAvailableRunnable final
: public Runnable
{
1302 RefPtr
<FetchDriverObserver
> mObserver
;
1305 explicit DataAvailableRunnable(FetchDriverObserver
* aObserver
)
1306 : Runnable("dom::DataAvailableRunnable"), mObserver(aObserver
) {
1307 MOZ_ASSERT(aObserver
);
1312 mObserver
->OnDataAvailable();
1313 mObserver
= nullptr;
1318 struct SRIVerifierAndOutputHolder
{
1319 SRIVerifierAndOutputHolder(SRICheckDataVerifier
* aVerifier
,
1320 nsIOutputStream
* aOutputStream
)
1321 : mVerifier(aVerifier
), mOutputStream(aOutputStream
) {}
1323 SRICheckDataVerifier
* mVerifier
;
1324 nsIOutputStream
* mOutputStream
;
1327 SRIVerifierAndOutputHolder() = delete;
1330 // Just like NS_CopySegmentToStream, but also sends the data into an
1331 // SRICheckDataVerifier.
1332 nsresult
CopySegmentToStreamAndSRI(nsIInputStream
* aInStr
, void* aClosure
,
1333 const char* aBuffer
, uint32_t aOffset
,
1334 uint32_t aCount
, uint32_t* aCountWritten
) {
1335 auto holder
= static_cast<SRIVerifierAndOutputHolder
*>(aClosure
);
1336 MOZ_DIAGNOSTIC_ASSERT(holder
&& holder
->mVerifier
&& holder
->mOutputStream
,
1338 nsresult rv
= holder
->mVerifier
->Update(
1339 aCount
, reinterpret_cast<const uint8_t*>(aBuffer
));
1340 NS_ENSURE_SUCCESS(rv
, rv
);
1342 // The rest is just like NS_CopySegmentToStream.
1346 rv
= holder
->mOutputStream
->Write(aBuffer
, aCount
, &n
);
1347 if (NS_FAILED(rv
)) {
1352 *aCountWritten
+= n
;
1357 } // anonymous namespace
1360 FetchDriver::OnDataAvailable(nsIRequest
* aRequest
, nsIInputStream
* aInputStream
,
1361 uint64_t aOffset
, uint32_t aCount
) {
1362 // NB: This can be called on any thread! But we're guaranteed that it is
1363 // called between OnStartRequest and OnStopRequest, so we don't need to worry
1366 if (!mPipeOutputStream
) {
1367 // We ignore the body for HEAD/CONNECT requests.
1368 // nsIStreamListener mandates reading from the stream before returning.
1370 nsresult rv
= aInputStream
->ReadSegments(NS_DiscardSegment
, nullptr, aCount
,
1372 NS_ENSURE_SUCCESS(rv
, rv
);
1376 if (mNeedToObserveOnDataAvailable
) {
1377 mNeedToObserveOnDataAvailable
= false;
1379 // Need to keep mObserver alive.
1380 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
1381 if (NS_IsMainThread()) {
1382 observer
->OnDataAvailable();
1384 RefPtr
<Runnable
> runnable
= new DataAvailableRunnable(observer
);
1385 nsresult rv
= mMainThreadEventTarget
->Dispatch(runnable
.forget(),
1386 NS_DISPATCH_NORMAL
);
1387 if (NS_WARN_IF(NS_FAILED(rv
))) {
1396 return NS_ERROR_UNEXPECTED
;
1399 // Needs to be initialized to 0 because in some cases nsStringInputStream may
1400 // not write to aRead.
1402 MOZ_ASSERT(mPipeOutputStream
);
1404 // From "Main Fetch" step 19: SRI-part2.
1405 // Note: Avoid checking the hidden opaque body.
1407 if (mResponse
->Type() != ResponseType::Opaque
&&
1408 ShouldCheckSRI(*mRequest
, *mResponse
)) {
1409 MOZ_ASSERT(mSRIDataVerifier
);
1411 SRIVerifierAndOutputHolder
holder(mSRIDataVerifier
.get(),
1413 rv
= aInputStream
->ReadSegments(CopySegmentToStreamAndSRI
, &holder
, aCount
,
1416 rv
= aInputStream
->ReadSegments(NS_CopySegmentToStream
, mPipeOutputStream
,
1420 // If no data was read, it's possible the output stream is closed but the
1421 // ReadSegments call followed its contract of returning NS_OK despite write
1422 // errors. Unfortunately, nsIOutputStream has an ill-conceived contract when
1423 // taken together with ReadSegments' contract, because the pipe will just
1424 // NS_OK if we try and invoke its Write* functions ourselves with a 0 count.
1425 // So we must just assume the pipe is broken.
1426 if (aRead
== 0 && aCount
!= 0) {
1427 return NS_BASE_STREAM_CLOSED
;
1433 FetchDriver::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatusCode
) {
1434 AssertIsOnMainThread();
1436 MOZ_DIAGNOSTIC_ASSERT(!mOnStopRequestCalled
);
1437 mOnStopRequestCalled
= true;
1439 // main data loading is going to finish, breaking the reference cycle.
1440 RefPtr
<AlternativeDataStreamListener
> altDataListener
=
1441 std::move(mAltDataListener
);
1443 // For PFetch and ServiceWorker navigationPreload, resource timing should be
1444 // reported before the body stream closing.
1446 mObserver
->OnReportPerformanceTiming();
1449 // We need to check mObserver, which is nulled by FailWithNetworkError(),
1450 // because in the case of "error" redirect mode, aStatusCode may be NS_OK but
1451 // mResponse will definitely be null so we must not take the else branch.
1452 if (NS_FAILED(aStatusCode
) || !mObserver
) {
1453 nsCOMPtr
<nsIAsyncOutputStream
> outputStream
=
1454 do_QueryInterface(mPipeOutputStream
);
1456 outputStream
->CloseWithStatus(NS_FAILED(aStatusCode
) ? aStatusCode
1457 : NS_BINDING_FAILED
);
1459 if (altDataListener
) {
1460 altDataListener
->Cancel();
1463 // We proceed as usual here, since we've already created a successful
1464 // response from OnStartRequest.
1466 MOZ_ASSERT(mResponse
);
1467 MOZ_ASSERT(!mResponse
->IsError());
1469 // From "Main Fetch" step 19: SRI-part3.
1470 if (ShouldCheckSRI(*mRequest
, *mResponse
)) {
1471 MOZ_ASSERT(mSRIDataVerifier
);
1473 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
1475 nsIConsoleReportCollector
* reporter
= nullptr;
1477 reporter
= mObserver
->GetReporter();
1480 nsAutoCString sourceUri
;
1481 if (mDocument
&& mDocument
->GetDocumentURI()) {
1482 mDocument
->GetDocumentURI()->GetAsciiSpec(sourceUri
);
1483 } else if (!mWorkerScript
.IsEmpty()) {
1484 sourceUri
.Assign(mWorkerScript
);
1487 mSRIDataVerifier
->Verify(mSRIMetadata
, channel
, sourceUri
, reporter
);
1488 if (NS_FAILED(rv
)) {
1489 if (altDataListener
) {
1490 altDataListener
->Cancel();
1492 FailWithNetworkError(rv
);
1498 if (mPipeOutputStream
) {
1499 mPipeOutputStream
->Close();
1503 FinishOnStopRequest(altDataListener
);
1507 void FetchDriver::FinishOnStopRequest(
1508 AlternativeDataStreamListener
* aAltDataListener
) {
1509 AssertIsOnMainThread();
1510 // OnStopRequest is not called from channel, that means the main data loading
1511 // does not finish yet. Reaching here since alternative data loading finishes.
1512 if (!mOnStopRequestCalled
) {
1516 MOZ_DIAGNOSTIC_ASSERT(!mAltDataListener
);
1517 // Wait for alternative data loading finish if we needed it.
1518 if (aAltDataListener
&&
1519 aAltDataListener
->Status() == AlternativeDataStreamListener::LOADING
) {
1520 // For LOADING case, channel holds the reference of altDataListener, no need
1521 // to restore it to mAltDataListener.
1526 // From "Main Fetch" step 19.1, 19.2: Process response.
1527 if (ShouldCheckSRI(*mRequest
, *mResponse
)) {
1528 MOZ_ASSERT(mResponse
);
1529 // Need to keep mObserver alive.
1530 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
1531 observer
->OnResponseAvailable(mResponse
.clonePtr());
1533 mResponseAvailableCalled
= true;
1539 mObserver
->OnResponseEnd(FetchDriverObserver::eByNetworking
,
1540 JS::UndefinedHandleValue
);
1541 mObserver
= nullptr;
1549 FetchDriver::ShouldPrepareForIntercept(nsIURI
* aURI
, nsIChannel
* aChannel
,
1550 bool* aShouldIntercept
) {
1551 MOZ_ASSERT(aChannel
);
1553 if (mInterceptController
) {
1554 MOZ_ASSERT(XRE_IsParentProcess());
1555 return mInterceptController
->ShouldPrepareForIntercept(aURI
, aChannel
,
1559 nsCOMPtr
<nsINetworkInterceptController
> controller
;
1560 NS_QueryNotificationCallbacks(nullptr, mLoadGroup
,
1561 NS_GET_IID(nsINetworkInterceptController
),
1562 getter_AddRefs(controller
));
1564 return controller
->ShouldPrepareForIntercept(aURI
, aChannel
,
1568 *aShouldIntercept
= false;
1573 FetchDriver::ChannelIntercepted(nsIInterceptedChannel
* aChannel
) {
1574 if (mInterceptController
) {
1575 MOZ_ASSERT(XRE_IsParentProcess());
1576 return mInterceptController
->ChannelIntercepted(aChannel
);
1579 nsCOMPtr
<nsINetworkInterceptController
> controller
;
1580 NS_QueryNotificationCallbacks(nullptr, mLoadGroup
,
1581 NS_GET_IID(nsINetworkInterceptController
),
1582 getter_AddRefs(controller
));
1584 return controller
->ChannelIntercepted(aChannel
);
1590 void FetchDriver::EnableNetworkInterceptControl() {
1591 MOZ_ASSERT(XRE_IsParentProcess());
1592 MOZ_ASSERT(NS_IsMainThread());
1593 MOZ_ASSERT(!mInterceptController
);
1594 mInterceptController
= new ServiceWorkerInterceptController();
1598 FetchDriver::AsyncOnChannelRedirect(nsIChannel
* aOldChannel
,
1599 nsIChannel
* aNewChannel
, uint32_t aFlags
,
1600 nsIAsyncVerifyRedirectCallback
* aCallback
) {
1601 nsCOMPtr
<nsIHttpChannel
> oldHttpChannel
= do_QueryInterface(aOldChannel
);
1602 nsCOMPtr
<nsIHttpChannel
> newHttpChannel
= do_QueryInterface(aNewChannel
);
1603 if (oldHttpChannel
&& newHttpChannel
) {
1604 nsAutoCString method
;
1605 mRequest
->GetMethod(method
);
1608 bool rewriteToGET
= false;
1609 Unused
<< oldHttpChannel
->ShouldStripRequestBodyHeader(method
,
1612 // we need to strip Authentication headers for cross-origin requests
1613 // Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch
1614 bool skipAuthHeader
=
1615 (StaticPrefs::network_fetch_redirect_stripAuthHeader() &&
1616 NS_ShouldRemoveAuthHeaderOnRedirect(aOldChannel
, aNewChannel
, aFlags
));
1618 SetRequestHeaders(newHttpChannel
, rewriteToGET
, skipAuthHeader
);
1621 // "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list."
1622 // However, ignore internal redirects here. We don't want to flip
1623 // Response.redirected to true if an internal redirect occurs. These
1624 // should be transparent to script.
1625 nsCOMPtr
<nsIURI
> uri
;
1626 MOZ_ALWAYS_SUCCEEDS(NS_GetFinalChannelURI(aNewChannel
, getter_AddRefs(uri
)));
1628 nsCOMPtr
<nsIURI
> uriClone
;
1629 nsresult rv
= NS_GetURIWithoutRef(uri
, getter_AddRefs(uriClone
));
1630 if (NS_WARN_IF(NS_FAILED(rv
))) {
1634 rv
= uriClone
->GetSpec(spec
);
1635 if (NS_WARN_IF(NS_FAILED(rv
))) {
1639 rv
= uri
->GetRef(fragment
);
1640 if (NS_WARN_IF(NS_FAILED(rv
))) {
1644 if (!(aFlags
& nsIChannelEventSink::REDIRECT_INTERNAL
)) {
1645 mRequest
->AddURL(spec
, fragment
);
1647 // Overwrite the URL only when the request is redirected by a service
1649 mRequest
->SetURLForInternalRedirect(aFlags
, spec
, fragment
);
1652 // In redirect, httpChannel already took referrer-policy into account, so
1653 // updates request’s associated referrer policy from channel.
1654 UpdateReferrerInfoFromNewChannel(aNewChannel
);
1656 aCallback
->OnRedirectVerifyCallback(NS_OK
);
1661 FetchDriver::CheckListenerChain() { return NS_OK
; }
1664 FetchDriver::GetInterface(const nsIID
& aIID
, void** aResult
) {
1665 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
1666 *aResult
= static_cast<nsIChannelEventSink
*>(this);
1670 if (aIID
.Equals(NS_GET_IID(nsIStreamListener
))) {
1671 *aResult
= static_cast<nsIStreamListener
*>(this);
1675 if (aIID
.Equals(NS_GET_IID(nsIRequestObserver
))) {
1676 *aResult
= static_cast<nsIRequestObserver
*>(this);
1681 return QueryInterface(aIID
, aResult
);
1684 void FetchDriver::SetDocument(Document
* aDocument
) {
1685 // Cannot set document after Fetch() has been called.
1686 MOZ_ASSERT(!mFetchCalled
);
1687 mDocument
= aDocument
;
1690 void FetchDriver::SetCSPEventListener(nsICSPEventListener
* aCSPEventListener
) {
1691 MOZ_ASSERT(!mFetchCalled
);
1692 mCSPEventListener
= aCSPEventListener
;
1695 void FetchDriver::SetClientInfo(const ClientInfo
& aClientInfo
) {
1696 MOZ_ASSERT(!mFetchCalled
);
1697 mClientInfo
.emplace(aClientInfo
);
1700 void FetchDriver::SetController(
1701 const Maybe
<ServiceWorkerDescriptor
>& aController
) {
1702 MOZ_ASSERT(!mFetchCalled
);
1703 mController
= aController
;
1706 PerformanceTimingData
* FetchDriver::GetPerformanceTimingData(
1707 nsAString
& aInitiatorType
, nsAString
& aEntryName
) {
1708 MOZ_ASSERT(XRE_IsParentProcess());
1713 nsCOMPtr
<nsITimedChannel
> timedChannel
= do_QueryInterface(mChannel
);
1714 if (!timedChannel
) {
1717 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(mChannel
);
1721 return dom::PerformanceTimingData::Create(timedChannel
, httpChannel
, 0,
1722 aInitiatorType
, aEntryName
);
1725 void FetchDriver::SetRequestHeaders(nsIHttpChannel
* aChannel
,
1726 bool aStripRequestBodyHeader
,
1727 bool aStripAuthHeader
) const {
1728 MOZ_ASSERT(aChannel
);
1730 // nsIHttpChannel has a set of pre-configured headers (Accept,
1731 // Accept-Languages, ...) and we don't want to merge the Request's headers
1732 // with them. This array is used to know if the current header has been aleady
1733 // set, if yes, we ask necko to merge it with the previous one, otherwise, we
1734 // don't want the merge.
1735 nsTArray
<nsCString
> headersSet
;
1737 AutoTArray
<InternalHeaders::Entry
, 5> headers
;
1738 mRequest
->Headers()->GetEntries(headers
);
1739 for (uint32_t i
= 0; i
< headers
.Length(); ++i
) {
1740 if (aStripRequestBodyHeader
&&
1741 (headers
[i
].mName
.LowerCaseEqualsASCII("content-type") ||
1742 headers
[i
].mName
.LowerCaseEqualsASCII("content-encoding") ||
1743 headers
[i
].mName
.LowerCaseEqualsASCII("content-language") ||
1744 headers
[i
].mName
.LowerCaseEqualsASCII("content-location"))) {
1748 if (aStripAuthHeader
&&
1749 headers
[i
].mName
.LowerCaseEqualsASCII("authorization")) {
1753 bool alreadySet
= headersSet
.Contains(headers
[i
].mName
);
1755 headersSet
.AppendElement(headers
[i
].mName
);
1758 if (headers
[i
].mValue
.IsEmpty()) {
1759 DebugOnly
<nsresult
> rv
=
1760 aChannel
->SetEmptyRequestHeader(headers
[i
].mName
);
1761 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1763 DebugOnly
<nsresult
> rv
= aChannel
->SetRequestHeader(
1764 headers
[i
].mName
, headers
[i
].mValue
, alreadySet
/* merge */);
1765 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1770 void FetchDriver::RunAbortAlgorithm() { FetchDriverAbortActions(Signal()); }
1772 void FetchDriver::FetchDriverAbortActions(AbortSignalImpl
* aSignalImpl
) {
1773 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
1777 mResponseAvailableCalled
= true;
1779 JS::Rooted
<JS::Value
> reason(RootingCx());
1781 reason
.set(aSignalImpl
->RawReason());
1783 mObserver
->OnResponseEnd(FetchDriverObserver::eAborted
, reason
);
1784 mObserver
= nullptr;
1788 mChannel
->CancelWithReason(NS_BINDING_ABORTED
,
1789 "FetchDriver::RunAbortAlgorithm"_ns
);
1796 } // namespace mozilla::dom