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/. */
7 #include "mozilla/DebugOnly.h"
8 #include "mozilla/dom/FetchDriver.h"
10 #include "nsIAsyncVerifyRedirectCallback.h"
11 #include "mozilla/dom/Document.h"
12 #include "nsICookieJarSettings.h"
14 #include "nsIInputStream.h"
15 #include "nsIOutputStream.h"
16 #include "nsIFileChannel.h"
17 #include "nsIHttpChannel.h"
18 #include "nsIHttpChannelInternal.h"
19 #include "nsISupportsPriority.h"
20 #include "nsIThreadRetargetableRequest.h"
21 #include "nsIUploadChannel2.h"
22 #include "nsIInterfaceRequestorUtils.h"
25 #include "nsContentPolicyUtils.h"
26 #include "nsDataHandler.h"
27 #include "nsNetUtil.h"
28 #include "nsPrintfCString.h"
29 #include "nsProxyRelease.h"
30 #include "nsStreamUtils.h"
31 #include "nsStringStream.h"
32 #include "nsHttpChannel.h"
34 #include "mozilla/dom/BlobURLProtocolHandler.h"
35 #include "mozilla/dom/File.h"
36 #include "mozilla/dom/PerformanceStorage.h"
37 #include "mozilla/dom/UserActivation.h"
38 #include "mozilla/dom/WorkerCommon.h"
39 #include "mozilla/PreloaderBase.h"
40 #include "mozilla/net/NeckoChannelParams.h"
41 #include "mozilla/ipc/PBackgroundSharedTypes.h"
42 #include "mozilla/StaticPrefs_browser.h"
43 #include "mozilla/StaticPrefs_network.h"
44 #include "mozilla/StaticPrefs_privacy.h"
45 #include "mozilla/Unused.h"
48 #include "FetchUtil.h"
49 #include "InternalRequest.h"
50 #include "InternalResponse.h"
52 namespace mozilla::dom
{
56 void GetBlobURISpecFromChannel(nsIRequest
* aRequest
, nsCString
& aBlobURISpec
) {
59 aBlobURISpec
.SetIsVoid(true);
61 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
67 nsresult rv
= channel
->GetURI(getter_AddRefs(uri
));
72 if (!dom::IsBlobURI(uri
)) {
76 uri
->GetSpec(aBlobURISpec
);
79 bool ShouldCheckSRI(const InternalRequest
& aRequest
,
80 const InternalResponse
& aResponse
) {
81 return !aRequest
.GetIntegrity().IsEmpty() &&
82 aResponse
.Type() != ResponseType::Error
;
85 } // anonymous namespace
87 //-----------------------------------------------------------------------------
88 // AlternativeDataStreamListener
89 //-----------------------------------------------------------------------------
90 class AlternativeDataStreamListener final
91 : public nsIStreamListener
,
92 public nsIThreadRetargetableStreamListener
{
94 NS_DECL_THREADSAFE_ISUPPORTS
95 NS_DECL_NSIREQUESTOBSERVER
96 NS_DECL_NSISTREAMLISTENER
97 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
99 // The status of AlternativeDataStreamListener
100 // LOADING: is the initial status, loading the alternative data
101 // COMPLETED: Alternative data loading is completed
102 // CANCELED: Alternative data loading is canceled, this would make
103 // AlternativeDataStreamListener ignore all channel callbacks
104 // FALLBACK: fallback the channel callbacks to FetchDriver
105 // Depends on different situaions, the status transition could be followings
106 // 1. LOADING->COMPLETED
107 // This is the normal status transition for alternative data loading
109 // 2. LOADING->CANCELED
110 // LOADING->COMPLETED->CANCELED
111 // Alternative data loading could be canceled when cacheId from alternative
112 // data channel does not match with from main data channel(The cacheID
113 // checking is in FetchDriver::OnStartRequest).
114 // Notice the alternative data loading could finish before the cacheID
115 // checking, so the statust transition could be
116 // LOADING->COMPLETED->CANCELED
118 // 3. LOADING->FALLBACK
119 // For the case that alternative data loading could not be initialized,
120 // i.e. alternative data does not exist or no preferred alternative data
121 // type is requested. Once the status becomes FALLBACK,
122 // AlternativeDataStreamListener transits the channel callback request to
123 // FetchDriver, and the status should not go back to LOADING, COMPLETED, or
125 enum eStatus
{ LOADING
= 0, COMPLETED
, CANCELED
, FALLBACK
};
127 AlternativeDataStreamListener(FetchDriver
* aFetchDriver
, nsIChannel
* aChannel
,
128 const nsACString
& aAlternativeDataType
);
131 uint64_t GetAlternativeDataCacheEntryId();
132 const nsACString
& GetAlternativeDataType() const;
133 already_AddRefed
<nsICacheInfoChannel
> GetCacheInfoChannel();
134 already_AddRefed
<nsIInputStream
> GetAlternativeInputStream();
137 ~AlternativeDataStreamListener() = default;
139 // This creates a strong reference cycle with FetchDriver and its
140 // mAltDataListener. We need to clear at least one reference of them once the
141 // data loading finishes.
142 RefPtr
<FetchDriver
> mFetchDriver
;
143 nsCString mAlternativeDataType
;
144 nsCOMPtr
<nsIInputStream
> mPipeAlternativeInputStream
;
145 nsCOMPtr
<nsIOutputStream
> mPipeAlternativeOutputStream
;
146 uint64_t mAlternativeDataCacheEntryId
;
147 nsCOMPtr
<nsICacheInfoChannel
> mCacheInfoChannel
;
148 nsCOMPtr
<nsIChannel
> mChannel
;
149 Atomic
<eStatus
> mStatus
;
152 NS_IMPL_ISUPPORTS(AlternativeDataStreamListener
, nsIStreamListener
,
153 nsIThreadRetargetableStreamListener
)
155 AlternativeDataStreamListener::AlternativeDataStreamListener(
156 FetchDriver
* aFetchDriver
, nsIChannel
* aChannel
,
157 const nsACString
& aAlternativeDataType
)
158 : mFetchDriver(aFetchDriver
),
159 mAlternativeDataType(aAlternativeDataType
),
160 mAlternativeDataCacheEntryId(0),
162 mStatus(AlternativeDataStreamListener::LOADING
) {
163 MOZ_DIAGNOSTIC_ASSERT(mFetchDriver
);
164 MOZ_DIAGNOSTIC_ASSERT(mChannel
);
167 AlternativeDataStreamListener::eStatus
AlternativeDataStreamListener::Status() {
171 void AlternativeDataStreamListener::Cancel() {
172 mAlternativeDataCacheEntryId
= 0;
173 mCacheInfoChannel
= nullptr;
174 mPipeAlternativeOutputStream
= nullptr;
175 mPipeAlternativeInputStream
= nullptr;
176 if (mChannel
&& mStatus
!= AlternativeDataStreamListener::FALLBACK
) {
177 // if mStatus is fallback, we need to keep channel to forward request back
179 mChannel
->Cancel(NS_BINDING_ABORTED
);
182 mStatus
= AlternativeDataStreamListener::CANCELED
;
185 uint64_t AlternativeDataStreamListener::GetAlternativeDataCacheEntryId() {
186 return mAlternativeDataCacheEntryId
;
189 const nsACString
& AlternativeDataStreamListener::GetAlternativeDataType()
191 return mAlternativeDataType
;
194 already_AddRefed
<nsIInputStream
>
195 AlternativeDataStreamListener::GetAlternativeInputStream() {
196 nsCOMPtr
<nsIInputStream
> inputStream
= mPipeAlternativeInputStream
;
197 return inputStream
.forget();
200 already_AddRefed
<nsICacheInfoChannel
>
201 AlternativeDataStreamListener::GetCacheInfoChannel() {
202 nsCOMPtr
<nsICacheInfoChannel
> channel
= mCacheInfoChannel
;
203 return channel
.forget();
207 AlternativeDataStreamListener::OnStartRequest(nsIRequest
* aRequest
) {
208 AssertIsOnMainThread();
209 MOZ_ASSERT(!mAlternativeDataType
.IsEmpty());
210 // Checking the alternative data type is the same between we asked and the
211 // saved in the channel.
212 nsAutoCString alternativeDataType
;
213 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(aRequest
);
214 mStatus
= AlternativeDataStreamListener::LOADING
;
215 if (cic
&& NS_SUCCEEDED(cic
->GetAlternativeDataType(alternativeDataType
)) &&
216 mAlternativeDataType
.Equals(alternativeDataType
) &&
217 NS_SUCCEEDED(cic
->GetCacheEntryId(&mAlternativeDataCacheEntryId
))) {
218 MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeInputStream
);
219 MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeOutputStream
);
221 NS_NewPipe(getter_AddRefs(mPipeAlternativeInputStream
),
222 getter_AddRefs(mPipeAlternativeOutputStream
),
223 0 /* default segment size */, UINT32_MAX
/* infinite pipe */,
224 true /* non-blocking input, otherwise you deadlock */,
225 false /* blocking output, since the pipe is 'in'finite */);
228 mFetchDriver
->FailWithNetworkError(rv
);
232 MOZ_DIAGNOSTIC_ASSERT(!mCacheInfoChannel
);
233 mCacheInfoChannel
= cic
;
235 // call FetchDriver::HttpFetch to load main body
236 MOZ_ASSERT(mFetchDriver
);
237 return mFetchDriver
->HttpFetch();
240 // Needn't load alternative data, since alternative data does not exist.
241 // Set status to FALLBACK to reuse the opened channel to load main body,
242 // then call FetchDriver::OnStartRequest to continue the work. Unfortunately
243 // can't change the stream listener to mFetchDriver, need to keep
244 // AlternativeDataStreamListener alive to redirect OnDataAvailable and
245 // OnStopRequest to mFetchDriver.
246 MOZ_ASSERT(alternativeDataType
.IsEmpty());
247 mStatus
= AlternativeDataStreamListener::FALLBACK
;
248 mAlternativeDataCacheEntryId
= 0;
249 MOZ_ASSERT(mFetchDriver
);
250 return mFetchDriver
->OnStartRequest(aRequest
);
256 AlternativeDataStreamListener::OnDataAvailable(nsIRequest
* aRequest
,
257 nsIInputStream
* aInputStream
,
260 if (mStatus
== AlternativeDataStreamListener::LOADING
) {
261 MOZ_ASSERT(mPipeAlternativeOutputStream
);
263 return aInputStream
->ReadSegments(
264 NS_CopySegmentToStream
, mPipeAlternativeOutputStream
, aCount
, &read
);
266 if (mStatus
== AlternativeDataStreamListener::FALLBACK
) {
267 MOZ_ASSERT(mFetchDriver
);
268 return mFetchDriver
->OnDataAvailable(aRequest
, aInputStream
, aOffset
,
275 AlternativeDataStreamListener::OnStopRequest(nsIRequest
* aRequest
,
276 nsresult aStatusCode
) {
277 AssertIsOnMainThread();
279 // Alternative data loading is going to finish, breaking the reference cycle
280 // here by taking the ownership to a loacl variable.
281 RefPtr
<FetchDriver
> fetchDriver
= std::move(mFetchDriver
);
283 if (mStatus
== AlternativeDataStreamListener::CANCELED
) {
288 if (mStatus
== AlternativeDataStreamListener::FALLBACK
) {
289 MOZ_ASSERT(fetchDriver
);
290 return fetchDriver
->OnStopRequest(aRequest
, aStatusCode
);
293 MOZ_DIAGNOSTIC_ASSERT(mStatus
== AlternativeDataStreamListener::LOADING
);
295 MOZ_ASSERT(!mAlternativeDataType
.IsEmpty() && mPipeAlternativeOutputStream
&&
296 mPipeAlternativeInputStream
);
298 mPipeAlternativeOutputStream
->Close();
299 mPipeAlternativeOutputStream
= nullptr;
301 // Cleanup the states for alternative data if needed.
302 if (NS_FAILED(aStatusCode
)) {
303 mAlternativeDataCacheEntryId
= 0;
304 mCacheInfoChannel
= nullptr;
305 mPipeAlternativeInputStream
= nullptr;
307 mStatus
= AlternativeDataStreamListener::COMPLETED
;
308 // alternative data loading finish, call FetchDriver::FinishOnStopRequest to
309 // continue the final step for the case FetchDriver::OnStopRequest is called
310 // earlier than AlternativeDataStreamListener::OnStopRequest
311 MOZ_ASSERT(fetchDriver
);
312 fetchDriver
->FinishOnStopRequest(this);
317 AlternativeDataStreamListener::CheckListenerChain() { return NS_OK
; }
318 //-----------------------------------------------------------------------------
320 //-----------------------------------------------------------------------------
322 NS_IMPL_ISUPPORTS(FetchDriver
, nsIStreamListener
, nsIChannelEventSink
,
323 nsIInterfaceRequestor
, nsIThreadRetargetableStreamListener
)
325 FetchDriver::FetchDriver(SafeRefPtr
<InternalRequest
> aRequest
,
326 nsIPrincipal
* aPrincipal
, nsILoadGroup
* aLoadGroup
,
327 nsIEventTarget
* aMainThreadEventTarget
,
328 nsICookieJarSettings
* aCookieJarSettings
,
329 PerformanceStorage
* aPerformanceStorage
,
330 bool aIsTrackingFetch
)
331 : mPrincipal(aPrincipal
),
332 mLoadGroup(aLoadGroup
),
333 mRequest(std::move(aRequest
)),
334 mMainThreadEventTarget(aMainThreadEventTarget
),
335 mCookieJarSettings(aCookieJarSettings
),
336 mPerformanceStorage(aPerformanceStorage
),
337 mNeedToObserveOnDataAvailable(false),
338 mIsTrackingFetch(aIsTrackingFetch
),
339 mOnStopRequestCalled(false)
342 mResponseAvailableCalled(false),
346 AssertIsOnMainThread();
348 MOZ_ASSERT(mRequest
);
349 MOZ_ASSERT(aPrincipal
);
350 MOZ_ASSERT(aMainThreadEventTarget
);
353 FetchDriver::~FetchDriver() {
354 AssertIsOnMainThread();
356 // We assert this since even on failures, we should call
357 // FailWithNetworkError().
358 MOZ_ASSERT(mResponseAvailableCalled
);
361 already_AddRefed
<PreloaderBase
> FetchDriver::FindPreload(nsIURI
* aURI
) {
362 // Decide if we allow reuse of an existing <link rel=preload as=fetch>
363 // response for this request. First examine this fetch requets itself if it
364 // is 'pure' enough to use the response and then try to find a preload.
367 // Preloads are mapped on the document, no document, no preload.
371 switch (mRequest
->Mode()) {
372 case RequestMode::No_cors
:
373 cors
= CORSMode::CORS_NONE
;
375 case RequestMode::Cors
:
376 cors
= mRequest
->GetCredentialsMode() == RequestCredentials::Include
377 ? CORSMode::CORS_USE_CREDENTIALS
378 : CORSMode::CORS_ANONYMOUS
;
381 // Can't be satisfied by a preload because preload cannot define any of
385 if (!mRequest
->Headers()->HasOnlySimpleHeaders()) {
386 // Preload can't set any headers.
389 if (!mRequest
->GetIntegrity().IsEmpty()) {
390 // There is currently no support for SRI checking in the fetch preloader.
393 if (mRequest
->GetCacheMode() != RequestCache::Default
) {
394 // Preload can only go with the default caching mode.
397 if (mRequest
->SkipServiceWorker()) {
398 // Preload can't be forbidden interception.
401 if (mRequest
->GetRedirectMode() != RequestRedirect::Follow
) {
402 // Preload always follows redirects.
405 nsAutoCString method
;
406 mRequest
->GetMethod(method
);
407 if (!method
.EqualsLiteral("GET")) {
408 // Preload can only do GET, this also eliminates the case we do upload, so
409 // no need to check if the request has any body to send out.
413 // OK, this request can be satisfied by a preloaded response, try to find one.
415 auto preloadKey
= PreloadHashKey::CreateAsFetch(aURI
, cors
);
416 return mDocument
->Preloads().LookupPreload(preloadKey
);
419 void FetchDriver::UpdateReferrerInfoFromNewChannel(nsIChannel
* aChannel
) {
420 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
425 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= httpChannel
->GetReferrerInfo();
430 nsAutoString computedReferrerSpec
;
431 mRequest
->SetReferrerPolicy(referrerInfo
->ReferrerPolicy());
432 Unused
<< referrerInfo
->GetComputedReferrerSpec(computedReferrerSpec
);
433 mRequest
->SetReferrer(computedReferrerSpec
);
436 nsresult
FetchDriver::Fetch(AbortSignalImpl
* aSignalImpl
,
437 FetchDriverObserver
* aObserver
) {
438 AssertIsOnMainThread();
440 MOZ_ASSERT(!mFetchCalled
);
444 mObserver
= aObserver
;
446 // FIXME(nsm): Deal with HSTS.
448 MOZ_RELEASE_ASSERT(!mRequest
->IsSynchronous(),
449 "Synchronous fetch not supported");
451 UniquePtr
<mozilla::ipc::PrincipalInfo
> principalInfo(
452 new mozilla::ipc::PrincipalInfo());
453 nsresult rv
= PrincipalToPrincipalInfo(mPrincipal
, principalInfo
.get());
454 if (NS_WARN_IF(NS_FAILED(rv
))) {
458 mRequest
->SetPrincipalInfo(std::move(principalInfo
));
460 // If the signal is aborted, it's time to inform the observer and terminate
463 if (aSignalImpl
->Aborted()) {
471 rv
= HttpFetch(mRequest
->GetPreferredAlternativeDataType());
473 FailWithNetworkError(rv
);
476 // Any failure is handled by FailWithNetworkError notifying the aObserver.
480 // This function implements the "HTTP Fetch" algorithm from the Fetch spec.
481 // Functionality is often split between here, the CORS listener proxy and the
482 // Necko HTTP implementation.
483 nsresult
FetchDriver::HttpFetch(
484 const nsACString
& aPreferredAlternativeDataType
) {
485 MOZ_ASSERT(NS_IsMainThread());
487 // Step 1. "Let response be null."
489 mOnStopRequestCalled
= false;
492 nsCOMPtr
<nsIIOService
> ios
= do_GetIOService(&rv
);
493 NS_ENSURE_SUCCESS(rv
, rv
);
496 mRequest
->GetURL(url
);
497 nsCOMPtr
<nsIURI
> uri
;
498 rv
= NS_NewURI(getter_AddRefs(uri
), url
);
499 NS_ENSURE_SUCCESS(rv
, rv
);
501 // Unsafe requests aren't allowed with when using no-core mode.
502 if (mRequest
->Mode() == RequestMode::No_cors
&& mRequest
->UnsafeRequest() &&
503 (!mRequest
->HasSimpleMethod() ||
504 !mRequest
->Headers()->HasOnlySimpleHeaders())) {
505 MOZ_ASSERT(false, "The API should have caught this");
506 return NS_ERROR_DOM_BAD_URI
;
509 // non-GET requests aren't allowed for blob.
510 if (IsBlobURI(uri
)) {
511 nsAutoCString method
;
512 mRequest
->GetMethod(method
);
513 if (!method
.EqualsLiteral("GET")) {
514 return NS_ERROR_DOM_NETWORK_ERR
;
518 RefPtr
<PreloaderBase
> fetchPreload
= FindPreload(uri
);
520 fetchPreload
->RemoveSelf(mDocument
);
521 fetchPreload
->NotifyUsage(PreloaderBase::LoadBackground::Keep
);
523 rv
= fetchPreload
->AsyncConsume(this);
524 if (NS_SUCCEEDED(rv
)) {
527 mChannel
= fetchPreload
->Channel();
528 MOZ_ASSERT(mChannel
);
529 mChannel
->SetNotificationCallbacks(this);
531 // Copied from AsyncOnChannelRedirect.
532 for (const auto& redirect
: fetchPreload
->Redirects()) {
533 if (redirect
.Flags() & nsIChannelEventSink::REDIRECT_INTERNAL
) {
534 mRequest
->SetURLForInternalRedirect(redirect
.Flags(), redirect
.Spec(),
535 redirect
.Fragment());
537 mRequest
->AddURL(redirect
.Spec(), redirect
.Fragment());
544 // The preload failed to be consumed. Behave like there were no preload.
545 fetchPreload
= nullptr;
548 // Step 2 deals with letting ServiceWorkers intercept requests. This is
549 // handled by Necko after the channel is opened.
550 // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
551 // set based on the Request's flag.
553 // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
554 // true..." is handled by the CORS proxy.
556 // Step 3.2 "Set request's skip service worker flag." This isn't required
557 // since Necko will fall back to the network if the ServiceWorker does not
558 // respond with a valid Response.
560 // NS_StartCORSPreflight() will automatically kick off the original request
561 // if it succeeds, so we need to have everything setup for the original
564 // Step 3.3 "Let credentials flag be set if one of
565 // - request's credentials mode is "include"
566 // - request's credentials mode is "same-origin" and either the CORS flag
567 // is unset or response tainting is "opaque"
568 // is true, and unset otherwise."
570 // Set skip serviceworker flag.
571 // While the spec also gates on the client being a ServiceWorker, we can't
572 // infer that here. Instead we rely on callers to set the flag correctly.
573 const nsLoadFlags bypassFlag
= mRequest
->SkipServiceWorker()
574 ? nsIChannel::LOAD_BYPASS_SERVICE_WORKER
577 nsSecurityFlags secFlags
= 0;
578 if (mRequest
->Mode() == RequestMode::Cors
) {
579 secFlags
|= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
;
580 } else if (mRequest
->Mode() == RequestMode::Same_origin
||
581 mRequest
->Mode() == RequestMode::Navigate
) {
582 secFlags
|= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT
;
583 } else if (mRequest
->Mode() == RequestMode::No_cors
) {
584 secFlags
|= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT
;
586 MOZ_ASSERT_UNREACHABLE("Unexpected request mode!");
587 return NS_ERROR_UNEXPECTED
;
590 if (mRequest
->GetRedirectMode() != RequestRedirect::Follow
) {
591 secFlags
|= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS
;
594 // This is handles the use credentials flag in "HTTP
595 // network or cache fetch" in the spec and decides whether to transmit
596 // cookies and other identifying information.
597 if (mRequest
->GetCredentialsMode() == RequestCredentials::Include
) {
598 secFlags
|= nsILoadInfo::SEC_COOKIES_INCLUDE
;
599 } else if (mRequest
->GetCredentialsMode() == RequestCredentials::Omit
) {
600 secFlags
|= nsILoadInfo::SEC_COOKIES_OMIT
;
601 } else if (mRequest
->GetCredentialsMode() ==
602 RequestCredentials::Same_origin
) {
603 secFlags
|= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN
;
605 MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!");
606 return NS_ERROR_UNEXPECTED
;
609 // From here on we create a channel and set its properties with the
610 // information from the InternalRequest. This is an implementation detail.
611 MOZ_ASSERT(mLoadGroup
);
612 nsCOMPtr
<nsIChannel
> chan
;
614 nsLoadFlags loadFlags
= nsIRequest::LOAD_BACKGROUND
| bypassFlag
;
616 MOZ_ASSERT(mDocument
->NodePrincipal() == mPrincipal
);
617 MOZ_ASSERT(mDocument
->CookieJarSettings() == mCookieJarSettings
);
618 rv
= NS_NewChannel(getter_AddRefs(chan
), uri
, mDocument
, secFlags
,
619 mRequest
->ContentPolicyType(),
620 nullptr, /* aPerformanceStorage */
621 mLoadGroup
, nullptr, /* aCallbacks */
623 } else if (mClientInfo
.isSome()) {
624 rv
= NS_NewChannel(getter_AddRefs(chan
), uri
, mPrincipal
, mClientInfo
.ref(),
625 mController
, secFlags
, mRequest
->ContentPolicyType(),
626 mCookieJarSettings
, mPerformanceStorage
, mLoadGroup
,
627 nullptr, /* aCallbacks */
631 NS_NewChannel(getter_AddRefs(chan
), uri
, mPrincipal
, secFlags
,
632 mRequest
->ContentPolicyType(), mCookieJarSettings
,
633 mPerformanceStorage
, mLoadGroup
, nullptr, /* aCallbacks */
636 NS_ENSURE_SUCCESS(rv
, rv
);
638 if (mCSPEventListener
) {
639 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
640 rv
= loadInfo
->SetCspEventListener(mCSPEventListener
);
641 NS_ENSURE_SUCCESS(rv
, rv
);
645 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
646 rv
= loadInfo
->SetLoadingEmbedderPolicy(mRequest
->GetEmbedderPolicy());
647 NS_ENSURE_SUCCESS(rv
, rv
);
650 // Insert ourselves into the notification callbacks chain so we can set
651 // headers on redirects.
654 nsCOMPtr
<nsIInterfaceRequestor
> notificationCallbacks
;
655 chan
->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks
));
656 MOZ_ASSERT(!notificationCallbacks
);
659 chan
->SetNotificationCallbacks(this);
661 nsCOMPtr
<nsIClassOfService
> cos(do_QueryInterface(chan
));
662 // Mark channel as urgent-start if the Fetch is triggered by user input
664 if (cos
&& UserActivation::IsHandlingUserInput()) {
665 cos
->AddClassFlags(nsIClassOfService::UrgentStart
);
668 // Step 3.5 begins "HTTP network or cache fetch".
669 // HTTP network or cache fetch
670 // ---------------------------
671 // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
672 nsCOMPtr
<nsIHttpChannel
> httpChan
= do_QueryInterface(chan
);
675 nsAutoCString method
;
676 mRequest
->GetMethod(method
);
677 rv
= httpChan
->SetRequestMethod(method
);
678 NS_ENSURE_SUCCESS(rv
, rv
);
680 // Set the same headers.
681 SetRequestHeaders(httpChan
, false);
683 // Step 5 of https://fetch.spec.whatwg.org/#main-fetch
684 // If request's referrer policy is the empty string and request's client is
685 // non-null, then set request's referrer policy to request's client's
686 // associated referrer policy.
687 // Basically, "client" is not in our implementation, we use
688 // EnvironmentReferrerPolicy of the worker or document context
689 ReferrerPolicy referrerPolicy
= mRequest
->GetEnvironmentReferrerPolicy();
690 if (mRequest
->ReferrerPolicy_() == ReferrerPolicy::_empty
) {
691 mRequest
->SetReferrerPolicy(referrerPolicy
);
693 // Step 6 of https://fetch.spec.whatwg.org/#main-fetch
694 // If request’s referrer policy is the empty string,
695 // then set request’s referrer policy to the user-set default policy.
696 if (mRequest
->ReferrerPolicy_() == ReferrerPolicy::_empty
) {
697 nsCOMPtr
<nsILoadInfo
> loadInfo
= httpChan
->LoadInfo();
698 bool isPrivate
= loadInfo
->GetOriginAttributes().mPrivateBrowsingId
> 0;
700 ReferrerInfo::GetDefaultReferrerPolicy(httpChan
, uri
, isPrivate
);
701 mRequest
->SetReferrerPolicy(referrerPolicy
);
704 rv
= FetchUtil::SetRequestReferrer(mPrincipal
, mDocument
, httpChan
,
706 NS_ENSURE_SUCCESS(rv
, rv
);
708 // Bug 1120722 - Authorization will be handled later.
709 // Auth may require prompting, we don't support it yet.
710 // The next patch in this same bug prevents this from aborting the request.
711 // Credentials checks for CORS are handled by nsCORSListenerProxy,
713 nsCOMPtr
<nsIHttpChannelInternal
> internalChan
= do_QueryInterface(httpChan
);
715 // Conversion between enumerations is safe due to static asserts in
716 // dom/workers/ServiceWorkerManager.cpp
717 rv
= internalChan
->SetCorsMode(static_cast<uint32_t>(mRequest
->Mode()));
718 MOZ_ASSERT(NS_SUCCEEDED(rv
));
719 rv
= internalChan
->SetRedirectMode(
720 static_cast<uint32_t>(mRequest
->GetRedirectMode()));
721 MOZ_ASSERT(NS_SUCCEEDED(rv
));
722 mRequest
->MaybeSkipCacheIfPerformingRevalidation();
723 rv
= internalChan
->SetFetchCacheMode(
724 static_cast<uint32_t>(mRequest
->GetCacheMode()));
725 MOZ_ASSERT(NS_SUCCEEDED(rv
));
726 rv
= internalChan
->SetIntegrityMetadata(mRequest
->GetIntegrity());
727 MOZ_ASSERT(NS_SUCCEEDED(rv
));
729 // Set the initiator type
730 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(httpChan
));
732 timedChannel
->SetInitiatorType(u
"fetch"_ns
);
736 // Step 5. Proxy authentication will be handled by Necko.
738 // Continue setting up 'HTTPRequest'. Content-Type and body data.
739 nsCOMPtr
<nsIUploadChannel2
> uploadChan
= do_QueryInterface(chan
);
741 nsAutoCString contentType
;
743 mRequest
->Headers()->GetFirst("content-type"_ns
, contentType
, result
);
744 // We don't actually expect "result" to have failed here: that only happens
745 // for invalid header names. But if for some reason it did, just propagate
747 if (result
.Failed()) {
748 return result
.StealNSResult();
751 // Now contentType is the header that was set in mRequest->Headers(), or a
752 // void string if no header was set.
754 bool hasContentTypeHeader
=
755 mRequest
->Headers()->Has("content-type"_ns
, result
);
756 MOZ_ASSERT(!result
.Failed());
757 MOZ_ASSERT_IF(!hasContentTypeHeader
, contentType
.IsVoid());
761 nsCOMPtr
<nsIInputStream
> bodyStream
;
762 mRequest
->GetBody(getter_AddRefs(bodyStream
), &bodyLength
);
764 nsAutoCString method
;
765 mRequest
->GetMethod(method
);
766 rv
= uploadChan
->ExplicitSetUploadStream(bodyStream
, contentType
,
768 false /* aStreamHasHeaders */);
769 NS_ENSURE_SUCCESS(rv
, rv
);
773 // If preflight is required, start a "CORS preflight fetch"
774 // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
775 // implementation is handled by the http channel calling into
776 // nsCORSListenerProxy. We just inform it which unsafe headers are included
778 if (mRequest
->Mode() == RequestMode::Cors
) {
779 AutoTArray
<nsCString
, 5> unsafeHeaders
;
780 mRequest
->Headers()->GetUnsafeHeaders(unsafeHeaders
);
781 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
->LoadInfo();
782 loadInfo
->SetCorsPreflightInfo(unsafeHeaders
, false);
785 if (mIsTrackingFetch
&& StaticPrefs::network_http_tailing_enabled() && cos
) {
786 cos
->AddClassFlags(nsIClassOfService::Throttleable
|
787 nsIClassOfService::Tail
);
790 if (mIsTrackingFetch
&&
791 StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
792 nsCOMPtr
<nsISupportsPriority
> p
= do_QueryInterface(chan
);
794 p
->SetPriority(nsISupportsPriority::PRIORITY_LOWEST
);
798 NotifyNetworkMonitorAlternateStack(chan
, std::move(mOriginStack
));
800 // if the preferred alternative data type in InternalRequest is not empty, set
801 // the data type on the created channel and also create a
802 // AlternativeDataStreamListener to be the stream listener of the channel.
803 if (!aPreferredAlternativeDataType
.IsEmpty()) {
804 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(chan
);
806 cic
->PreferAlternativeDataType(aPreferredAlternativeDataType
, ""_ns
,
808 MOZ_ASSERT(!mAltDataListener
);
809 mAltDataListener
= new AlternativeDataStreamListener(
810 this, chan
, aPreferredAlternativeDataType
);
811 rv
= chan
->AsyncOpen(mAltDataListener
);
813 rv
= chan
->AsyncOpen(this);
816 // Integrity check cannot be done on alt-data yet.
817 if (mRequest
->GetIntegrity().IsEmpty()) {
818 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(chan
);
820 cic
->PreferAlternativeDataType(nsLiteralCString(WASM_ALT_DATA_TYPE_V1
),
821 nsLiteralCString(WASM_CONTENT_TYPE
),
826 rv
= chan
->AsyncOpen(this);
833 // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
838 already_AddRefed
<InternalResponse
> FetchDriver::BeginAndGetFilteredResponse(
839 InternalResponse
* aResponse
, bool aFoundOpaqueRedirect
) {
840 MOZ_ASSERT(aResponse
);
841 AutoTArray
<nsCString
, 4> reqURLList
;
842 mRequest
->GetURLListWithoutFragment(reqURLList
);
843 MOZ_ASSERT(!reqURLList
.IsEmpty());
844 aResponse
->SetURLList(reqURLList
);
845 RefPtr
<InternalResponse
> filteredResponse
;
846 if (aFoundOpaqueRedirect
) {
847 filteredResponse
= aResponse
->OpaqueRedirectResponse();
849 switch (mRequest
->GetResponseTainting()) {
850 case LoadTainting::Basic
:
851 filteredResponse
= aResponse
->BasicResponse();
853 case LoadTainting::CORS
:
854 filteredResponse
= aResponse
->CORSResponse();
856 case LoadTainting::Opaque
: {
857 filteredResponse
= aResponse
->OpaqueResponse();
858 nsresult rv
= filteredResponse
->GeneratePaddingInfo();
859 if (NS_WARN_IF(NS_FAILED(rv
))) {
865 MOZ_CRASH("Unexpected case");
869 MOZ_ASSERT(filteredResponse
);
870 MOZ_ASSERT(mObserver
);
871 if (!ShouldCheckSRI(*mRequest
, *filteredResponse
)) {
872 // Need to keep mObserver alive.
873 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
874 observer
->OnResponseAvailable(filteredResponse
);
876 mResponseAvailableCalled
= true;
880 return filteredResponse
.forget();
883 void FetchDriver::FailWithNetworkError(nsresult rv
) {
884 AssertIsOnMainThread();
885 RefPtr
<InternalResponse
> error
= InternalResponse::NetworkError(rv
);
887 // Need to keep mObserver alive.
888 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
889 observer
->OnResponseAvailable(error
);
891 mResponseAvailableCalled
= true;
895 // mObserver could be null after OnResponseAvailable().
897 mObserver
->OnResponseEnd(FetchDriverObserver::eByNetworking
);
905 FetchDriver::OnStartRequest(nsIRequest
* aRequest
) {
906 AssertIsOnMainThread();
908 // Note, this can be called multiple times if we are doing an opaqueredirect.
909 // In that case we will get a simulated OnStartRequest() and then the real
910 // channel will call in with an errored OnStartRequest().
912 if (mFromPreload
&& mAborted
) {
913 aRequest
->Cancel(NS_BINDING_ABORTED
);
914 return NS_BINDING_ABORTED
;
918 MOZ_ASSERT(!mObserver
);
919 return NS_BINDING_ABORTED
;
923 aRequest
->GetStatus(&rv
);
925 FailWithNetworkError(rv
);
929 // We should only get to the following code once.
930 MOZ_ASSERT(!mPipeOutputStream
);
933 MOZ_ASSERT(false, "We should have mObserver here.");
934 FailWithNetworkError(NS_ERROR_UNEXPECTED
);
935 return NS_ERROR_UNEXPECTED
;
938 mNeedToObserveOnDataAvailable
= mObserver
->NeedOnDataAvailable();
940 RefPtr
<InternalResponse
> response
;
941 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
942 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequest
);
944 // On a successful redirect we perform the following substeps of HTTP Fetch,
945 // step 5, "redirect status", step 11.
947 bool foundOpaqueRedirect
= false;
949 nsAutoCString contentType
;
950 channel
->GetContentType(contentType
);
952 int64_t contentLength
= InternalResponse::UNKNOWN_BODY_SIZE
;
953 rv
= channel
->GetContentLength(&contentLength
);
954 MOZ_ASSERT_IF(NS_FAILED(rv
),
955 contentLength
== InternalResponse::UNKNOWN_BODY_SIZE
);
958 uint32_t responseStatus
= 0;
959 rv
= httpChannel
->GetResponseStatus(&responseStatus
);
961 FailWithNetworkError(rv
);
965 if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus
)) {
966 if (mRequest
->GetRedirectMode() == RequestRedirect::Error
) {
967 FailWithNetworkError(NS_BINDING_ABORTED
);
968 return NS_BINDING_FAILED
;
970 if (mRequest
->GetRedirectMode() == RequestRedirect::Manual
) {
971 foundOpaqueRedirect
= true;
975 nsAutoCString statusText
;
976 rv
= httpChannel
->GetResponseStatusText(statusText
);
977 MOZ_ASSERT(NS_SUCCEEDED(rv
));
979 response
= new InternalResponse(responseStatus
, statusText
,
980 mRequest
->GetCredentialsMode());
982 UniquePtr
<mozilla::ipc::PrincipalInfo
> principalInfo(
983 new mozilla::ipc::PrincipalInfo());
984 nsresult rv
= PrincipalToPrincipalInfo(mPrincipal
, principalInfo
.get());
985 if (NS_WARN_IF(NS_FAILED(rv
))) {
989 response
->SetPrincipalInfo(std::move(principalInfo
));
991 response
->Headers()->FillResponseHeaders(httpChannel
);
993 // If Content-Encoding or Transfer-Encoding headers are set, then the actual
994 // Content-Length (which refer to the decoded data) is obscured behind the
997 if (response
->Headers()->Has("content-encoding"_ns
, result
) ||
998 response
->Headers()->Has("transfer-encoding"_ns
, result
)) {
999 // We cannot trust the content-length when content-encoding or
1000 // transfer-encoding are set. There are many servers which just
1002 contentLength
= InternalResponse::UNKNOWN_BODY_SIZE
;
1004 MOZ_ASSERT(!result
.Failed());
1007 new InternalResponse(200, "OK"_ns
, mRequest
->GetCredentialsMode());
1009 if (!contentType
.IsEmpty()) {
1010 nsAutoCString contentCharset
;
1011 channel
->GetContentCharset(contentCharset
);
1012 if (NS_SUCCEEDED(rv
) && !contentCharset
.IsEmpty()) {
1013 contentType
+= ";charset="_ns
+ contentCharset
;
1016 IgnoredErrorResult result
;
1017 response
->Headers()->Append("Content-Type"_ns
, contentType
, result
);
1018 MOZ_ASSERT(!result
.Failed());
1021 if (contentLength
> 0) {
1022 nsAutoCString contentLenStr
;
1023 contentLenStr
.AppendInt(contentLength
);
1025 IgnoredErrorResult result
;
1026 response
->Headers()->Append("Content-Length"_ns
, contentLenStr
, result
);
1027 MOZ_ASSERT(!result
.Failed());
1031 nsCOMPtr
<nsICacheInfoChannel
> cic
= do_QueryInterface(aRequest
);
1033 if (mAltDataListener
) {
1034 // Skip the case that mAltDataListener->Status() equals to FALLBACK, that
1035 // means the opened channel for alternative data loading is reused for
1036 // loading the main data.
1037 if (mAltDataListener
->Status() !=
1038 AlternativeDataStreamListener::FALLBACK
) {
1039 // Verify the cache ID is the same with from alternative data cache.
1040 // If the cache ID is different, droping the alternative data loading,
1041 // otherwise setup the response's alternative body and cacheInfoChannel.
1042 uint64_t cacheEntryId
= 0;
1043 if (NS_SUCCEEDED(cic
->GetCacheEntryId(&cacheEntryId
)) &&
1045 mAltDataListener
->GetAlternativeDataCacheEntryId()) {
1046 mAltDataListener
->Cancel();
1048 // AlternativeDataStreamListener::OnStartRequest had already been
1049 // called, the alternative data input stream and cacheInfo channel
1051 nsCOMPtr
<nsICacheInfoChannel
> cacheInfo
=
1052 mAltDataListener
->GetCacheInfoChannel();
1053 nsCOMPtr
<nsIInputStream
> altInputStream
=
1054 mAltDataListener
->GetAlternativeInputStream();
1055 MOZ_ASSERT(altInputStream
&& cacheInfo
);
1056 response
->SetAlternativeBody(altInputStream
);
1057 nsMainThreadPtrHandle
<nsICacheInfoChannel
> handle(
1058 new nsMainThreadPtrHolder
<nsICacheInfoChannel
>(
1059 "nsICacheInfoChannel", cacheInfo
, false));
1060 response
->SetCacheInfoChannel(handle
);
1062 } else if (!mAltDataListener
->GetAlternativeDataType().IsEmpty()) {
1063 // If the status is FALLBACK and the
1064 // mAltDataListener::mAlternativeDataType is not empty, that means the
1065 // data need to be saved into cache, setup the response's
1066 // nsICacheInfoChannel for caching the data after loading.
1067 nsMainThreadPtrHandle
<nsICacheInfoChannel
> handle(
1068 new nsMainThreadPtrHolder
<nsICacheInfoChannel
>(
1069 "nsICacheInfoChannel", cic
, false));
1070 response
->SetCacheInfoChannel(handle
);
1072 } else if (!cic
->PreferredAlternativeDataTypes().IsEmpty()) {
1073 MOZ_ASSERT(cic
->PreferredAlternativeDataTypes().Length() == 1);
1074 MOZ_ASSERT(cic
->PreferredAlternativeDataTypes()[0].type().EqualsLiteral(
1075 WASM_ALT_DATA_TYPE_V1
));
1077 cic
->PreferredAlternativeDataTypes()[0].contentType().EqualsLiteral(
1078 WASM_CONTENT_TYPE
));
1080 if (contentType
.EqualsLiteral(WASM_CONTENT_TYPE
)) {
1081 // We want to attach the CacheInfoChannel to the response object such
1082 // that we can track its origin when the Response object is manipulated
1083 // by JavaScript code. This is important for WebAssembly, which uses
1084 // fetch to query its sources in JavaScript and transfer the Response
1085 // object to other function responsible for storing the alternate data
1086 // using the CacheInfoChannel.
1087 nsMainThreadPtrHandle
<nsICacheInfoChannel
> handle(
1088 new nsMainThreadPtrHolder
<nsICacheInfoChannel
>(
1089 "nsICacheInfoChannel", cic
, false));
1090 response
->SetCacheInfoChannel(handle
);
1095 // We open a pipe so that we can immediately set the pipe's read end as the
1096 // response's body. Setting the segment size to UINT32_MAX means that the
1097 // pipe has infinite space. The nsIChannel will continue to buffer data in
1098 // xpcom events even if we block on a fixed size pipe. It might be possible
1099 // to suspend the channel and then resume when there is space available, but
1100 // for now use an infinite pipe to avoid blocking.
1101 nsCOMPtr
<nsIInputStream
> pipeInputStream
;
1102 rv
= NS_NewPipe(getter_AddRefs(pipeInputStream
),
1103 getter_AddRefs(mPipeOutputStream
),
1104 0, /* default segment size */
1105 UINT32_MAX
/* infinite pipe */,
1106 true /* non-blocking input, otherwise you deadlock */,
1107 false /* blocking output, since the pipe is 'in'finite */);
1108 if (NS_WARN_IF(NS_FAILED(rv
))) {
1109 FailWithNetworkError(rv
);
1113 response
->SetBody(pipeInputStream
, contentLength
);
1115 // If the request is a file channel, then remember the local path to
1116 // that file so we can later create File blobs rather than plain ones.
1117 nsCOMPtr
<nsIFileChannel
> fc
= do_QueryInterface(aRequest
);
1119 nsCOMPtr
<nsIFile
> file
;
1120 rv
= fc
->GetFile(getter_AddRefs(file
));
1121 if (!NS_WARN_IF(NS_FAILED(rv
))) {
1123 file
->GetPath(path
);
1124 response
->SetBodyLocalPath(path
);
1127 // If the request is a blob URI, then remember that URI so that we
1128 // can later just use that blob instance instead of cloning it.
1129 nsCString blobURISpec
;
1130 GetBlobURISpecFromChannel(aRequest
, blobURISpec
);
1131 if (!blobURISpec
.IsVoid()) {
1132 response
->SetBodyBlobURISpec(blobURISpec
);
1136 response
->InitChannelInfo(channel
);
1138 nsCOMPtr
<nsIURI
> channelURI
;
1139 rv
= channel
->GetURI(getter_AddRefs(channelURI
));
1140 if (NS_WARN_IF(NS_FAILED(rv
))) {
1141 FailWithNetworkError(rv
);
1146 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
1147 // Propagate any tainting from the channel back to our response here. This
1148 // step is not reflected in the spec because the spec is written such that
1149 // FetchEvent.respondWith() just passes the already-tainted Response back to
1150 // the outer fetch(). In gecko, however, we serialize the Response through
1151 // the channel and must regenerate the tainting from the channel in the
1152 // interception case.
1153 mRequest
->MaybeIncreaseResponseTainting(loadInfo
->GetTainting());
1155 // Resolves fetch() promise which may trigger code running in a worker. Make
1156 // sure the Response is fully initialized before calling this.
1157 mResponse
= BeginAndGetFilteredResponse(response
, foundOpaqueRedirect
);
1158 if (NS_WARN_IF(!mResponse
)) {
1159 // Fail to generate a paddingInfo for opaque response.
1160 MOZ_DIAGNOSTIC_ASSERT(mRequest
->GetResponseTainting() ==
1161 LoadTainting::Opaque
&&
1162 !foundOpaqueRedirect
);
1163 FailWithNetworkError(NS_ERROR_UNEXPECTED
);
1164 return NS_ERROR_UNEXPECTED
;
1167 // From "Main Fetch" step 19: SRI-part1.
1168 if (ShouldCheckSRI(*mRequest
, *mResponse
) && mSRIMetadata
.IsEmpty()) {
1169 nsIConsoleReportCollector
* reporter
= nullptr;
1171 reporter
= mObserver
->GetReporter();
1174 nsAutoCString sourceUri
;
1175 if (mDocument
&& mDocument
->GetDocumentURI()) {
1176 mDocument
->GetDocumentURI()->GetAsciiSpec(sourceUri
);
1177 } else if (!mWorkerScript
.IsEmpty()) {
1178 sourceUri
.Assign(mWorkerScript
);
1180 SRICheck::IntegrityMetadata(mRequest
->GetIntegrity(), sourceUri
, reporter
,
1183 MakeUnique
<SRICheckDataVerifier
>(mSRIMetadata
, sourceUri
, reporter
);
1185 // Do not retarget off main thread when using SRI API.
1189 nsCOMPtr
<nsIEventTarget
> sts
=
1190 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
, &rv
);
1191 if (NS_WARN_IF(NS_FAILED(rv
))) {
1192 FailWithNetworkError(rv
);
1197 // Try to retarget off main thread.
1198 if (nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(aRequest
)) {
1199 Unused
<< NS_WARN_IF(NS_FAILED(rr
->RetargetDeliveryTo(sts
)));
1206 // Runnable to call the observer OnDataAvailable on the main-thread.
1207 class DataAvailableRunnable final
: public Runnable
{
1208 RefPtr
<FetchDriverObserver
> mObserver
;
1211 explicit DataAvailableRunnable(FetchDriverObserver
* aObserver
)
1212 : Runnable("dom::DataAvailableRunnable"), mObserver(aObserver
) {
1213 MOZ_ASSERT(aObserver
);
1218 mObserver
->OnDataAvailable();
1219 mObserver
= nullptr;
1224 struct SRIVerifierAndOutputHolder
{
1225 SRIVerifierAndOutputHolder(SRICheckDataVerifier
* aVerifier
,
1226 nsIOutputStream
* aOutputStream
)
1227 : mVerifier(aVerifier
), mOutputStream(aOutputStream
) {}
1229 SRICheckDataVerifier
* mVerifier
;
1230 nsIOutputStream
* mOutputStream
;
1233 SRIVerifierAndOutputHolder() = delete;
1236 // Just like NS_CopySegmentToStream, but also sends the data into an
1237 // SRICheckDataVerifier.
1238 nsresult
CopySegmentToStreamAndSRI(nsIInputStream
* aInStr
, void* aClosure
,
1239 const char* aBuffer
, uint32_t aOffset
,
1240 uint32_t aCount
, uint32_t* aCountWritten
) {
1241 auto holder
= static_cast<SRIVerifierAndOutputHolder
*>(aClosure
);
1242 MOZ_DIAGNOSTIC_ASSERT(holder
&& holder
->mVerifier
&& holder
->mOutputStream
,
1244 nsresult rv
= holder
->mVerifier
->Update(
1245 aCount
, reinterpret_cast<const uint8_t*>(aBuffer
));
1246 NS_ENSURE_SUCCESS(rv
, rv
);
1248 // The rest is just like NS_CopySegmentToStream.
1252 rv
= holder
->mOutputStream
->Write(aBuffer
, aCount
, &n
);
1253 if (NS_FAILED(rv
)) {
1258 *aCountWritten
+= n
;
1263 } // anonymous namespace
1266 FetchDriver::OnDataAvailable(nsIRequest
* aRequest
, nsIInputStream
* aInputStream
,
1267 uint64_t aOffset
, uint32_t aCount
) {
1268 // NB: This can be called on any thread! But we're guaranteed that it is
1269 // called between OnStartRequest and OnStopRequest, so we don't need to worry
1272 if (mNeedToObserveOnDataAvailable
) {
1273 mNeedToObserveOnDataAvailable
= false;
1275 // Need to keep mObserver alive.
1276 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
1277 if (NS_IsMainThread()) {
1278 observer
->OnDataAvailable();
1280 RefPtr
<Runnable
> runnable
= new DataAvailableRunnable(observer
);
1281 nsresult rv
= mMainThreadEventTarget
->Dispatch(runnable
.forget(),
1282 NS_DISPATCH_NORMAL
);
1283 if (NS_WARN_IF(NS_FAILED(rv
))) {
1290 // Needs to be initialized to 0 because in some cases nsStringInputStream may
1291 // not write to aRead.
1293 MOZ_ASSERT(mResponse
);
1294 MOZ_ASSERT(mPipeOutputStream
);
1296 // From "Main Fetch" step 19: SRI-part2.
1297 // Note: Avoid checking the hidden opaque body.
1299 if (mResponse
->Type() != ResponseType::Opaque
&&
1300 ShouldCheckSRI(*mRequest
, *mResponse
)) {
1301 MOZ_ASSERT(mSRIDataVerifier
);
1303 SRIVerifierAndOutputHolder
holder(mSRIDataVerifier
.get(),
1305 rv
= aInputStream
->ReadSegments(CopySegmentToStreamAndSRI
, &holder
, aCount
,
1308 rv
= aInputStream
->ReadSegments(NS_CopySegmentToStream
, mPipeOutputStream
,
1312 // If no data was read, it's possible the output stream is closed but the
1313 // ReadSegments call followed its contract of returning NS_OK despite write
1314 // errors. Unfortunately, nsIOutputStream has an ill-conceived contract when
1315 // taken together with ReadSegments' contract, because the pipe will just
1316 // NS_OK if we try and invoke its Write* functions ourselves with a 0 count.
1317 // So we must just assume the pipe is broken.
1318 if (aRead
== 0 && aCount
!= 0) {
1319 return NS_BASE_STREAM_CLOSED
;
1325 FetchDriver::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatusCode
) {
1326 AssertIsOnMainThread();
1328 MOZ_DIAGNOSTIC_ASSERT(!mOnStopRequestCalled
);
1329 mOnStopRequestCalled
= true;
1331 // main data loading is going to finish, breaking the reference cycle.
1332 RefPtr
<AlternativeDataStreamListener
> altDataListener
=
1333 std::move(mAltDataListener
);
1335 // We need to check mObserver, which is nulled by FailWithNetworkError(),
1336 // because in the case of "error" redirect mode, aStatusCode may be NS_OK but
1337 // mResponse will definitely be null so we must not take the else branch.
1338 if (NS_FAILED(aStatusCode
) || !mObserver
) {
1339 nsCOMPtr
<nsIAsyncOutputStream
> outputStream
=
1340 do_QueryInterface(mPipeOutputStream
);
1342 outputStream
->CloseWithStatus(NS_FAILED(aStatusCode
) ? aStatusCode
1343 : NS_BINDING_FAILED
);
1345 if (altDataListener
) {
1346 altDataListener
->Cancel();
1349 // We proceed as usual here, since we've already created a successful
1350 // response from OnStartRequest.
1352 MOZ_ASSERT(mResponse
);
1353 MOZ_ASSERT(!mResponse
->IsError());
1355 // From "Main Fetch" step 19: SRI-part3.
1356 if (ShouldCheckSRI(*mRequest
, *mResponse
)) {
1357 MOZ_ASSERT(mSRIDataVerifier
);
1359 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
1361 nsIConsoleReportCollector
* reporter
= nullptr;
1363 reporter
= mObserver
->GetReporter();
1366 nsAutoCString sourceUri
;
1367 if (mDocument
&& mDocument
->GetDocumentURI()) {
1368 mDocument
->GetDocumentURI()->GetAsciiSpec(sourceUri
);
1369 } else if (!mWorkerScript
.IsEmpty()) {
1370 sourceUri
.Assign(mWorkerScript
);
1373 mSRIDataVerifier
->Verify(mSRIMetadata
, channel
, sourceUri
, reporter
);
1374 if (NS_FAILED(rv
)) {
1375 if (altDataListener
) {
1376 altDataListener
->Cancel();
1378 FailWithNetworkError(rv
);
1384 if (mPipeOutputStream
) {
1385 mPipeOutputStream
->Close();
1389 FinishOnStopRequest(altDataListener
);
1393 void FetchDriver::FinishOnStopRequest(
1394 AlternativeDataStreamListener
* aAltDataListener
) {
1395 AssertIsOnMainThread();
1396 // OnStopRequest is not called from channel, that means the main data loading
1397 // does not finish yet. Reaching here since alternative data loading finishes.
1398 if (!mOnStopRequestCalled
) {
1402 MOZ_DIAGNOSTIC_ASSERT(!mAltDataListener
);
1403 // Wait for alternative data loading finish if we needed it.
1404 if (aAltDataListener
&&
1405 aAltDataListener
->Status() == AlternativeDataStreamListener::LOADING
) {
1406 // For LOADING case, channel holds the reference of altDataListener, no need
1407 // to restore it to mAltDataListener.
1412 // From "Main Fetch" step 19.1, 19.2: Process response.
1413 if (ShouldCheckSRI(*mRequest
, *mResponse
)) {
1414 MOZ_ASSERT(mResponse
);
1415 // Need to keep mObserver alive.
1416 RefPtr
<FetchDriverObserver
> observer
= mObserver
;
1417 observer
->OnResponseAvailable(mResponse
);
1419 mResponseAvailableCalled
= true;
1425 mObserver
->OnResponseEnd(FetchDriverObserver::eByNetworking
);
1426 mObserver
= nullptr;
1433 FetchDriver::AsyncOnChannelRedirect(nsIChannel
* aOldChannel
,
1434 nsIChannel
* aNewChannel
, uint32_t aFlags
,
1435 nsIAsyncVerifyRedirectCallback
* aCallback
) {
1436 nsCOMPtr
<nsIHttpChannel
> oldHttpChannel
= do_QueryInterface(aOldChannel
);
1437 nsCOMPtr
<nsIHttpChannel
> newHttpChannel
= do_QueryInterface(aNewChannel
);
1438 if (oldHttpChannel
&& newHttpChannel
) {
1439 nsAutoCString method
;
1440 mRequest
->GetMethod(method
);
1443 bool rewriteToGET
= false;
1444 Unused
<< oldHttpChannel
->ShouldStripRequestBodyHeader(method
,
1447 SetRequestHeaders(newHttpChannel
, rewriteToGET
);
1450 // "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list."
1451 // However, ignore internal redirects here. We don't want to flip
1452 // Response.redirected to true if an internal redirect occurs. These
1453 // should be transparent to script.
1454 nsCOMPtr
<nsIURI
> uri
;
1455 MOZ_ALWAYS_SUCCEEDS(aNewChannel
->GetURI(getter_AddRefs(uri
)));
1457 nsCOMPtr
<nsIURI
> uriClone
;
1458 nsresult rv
= NS_GetURIWithoutRef(uri
, getter_AddRefs(uriClone
));
1459 if (NS_WARN_IF(NS_FAILED(rv
))) {
1463 rv
= uriClone
->GetSpec(spec
);
1464 if (NS_WARN_IF(NS_FAILED(rv
))) {
1468 rv
= uri
->GetRef(fragment
);
1469 if (NS_WARN_IF(NS_FAILED(rv
))) {
1473 if (!(aFlags
& nsIChannelEventSink::REDIRECT_INTERNAL
)) {
1474 mRequest
->AddURL(spec
, fragment
);
1476 // Overwrite the URL only when the request is redirected by a service
1478 mRequest
->SetURLForInternalRedirect(aFlags
, spec
, fragment
);
1481 // In redirect, httpChannel already took referrer-policy into account, so
1482 // updates request’s associated referrer policy from channel.
1483 UpdateReferrerInfoFromNewChannel(aNewChannel
);
1485 aCallback
->OnRedirectVerifyCallback(NS_OK
);
1490 FetchDriver::CheckListenerChain() { return NS_OK
; }
1493 FetchDriver::GetInterface(const nsIID
& aIID
, void** aResult
) {
1494 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
1495 *aResult
= static_cast<nsIChannelEventSink
*>(this);
1499 if (aIID
.Equals(NS_GET_IID(nsIStreamListener
))) {
1500 *aResult
= static_cast<nsIStreamListener
*>(this);
1504 if (aIID
.Equals(NS_GET_IID(nsIRequestObserver
))) {
1505 *aResult
= static_cast<nsIRequestObserver
*>(this);
1510 return QueryInterface(aIID
, aResult
);
1513 void FetchDriver::SetDocument(Document
* aDocument
) {
1514 // Cannot set document after Fetch() has been called.
1515 MOZ_ASSERT(!mFetchCalled
);
1516 mDocument
= aDocument
;
1519 void FetchDriver::SetCSPEventListener(nsICSPEventListener
* aCSPEventListener
) {
1520 MOZ_ASSERT(!mFetchCalled
);
1521 mCSPEventListener
= aCSPEventListener
;
1524 void FetchDriver::SetClientInfo(const ClientInfo
& aClientInfo
) {
1525 MOZ_ASSERT(!mFetchCalled
);
1526 mClientInfo
.emplace(aClientInfo
);
1529 void FetchDriver::SetController(
1530 const Maybe
<ServiceWorkerDescriptor
>& aController
) {
1531 MOZ_ASSERT(!mFetchCalled
);
1532 mController
= aController
;
1535 void FetchDriver::SetRequestHeaders(nsIHttpChannel
* aChannel
,
1536 bool aStripRequestBodyHeader
) const {
1537 MOZ_ASSERT(aChannel
);
1539 // nsIHttpChannel has a set of pre-configured headers (Accept,
1540 // Accept-Languages, ...) and we don't want to merge the Request's headers
1541 // with them. This array is used to know if the current header has been aleady
1542 // set, if yes, we ask necko to merge it with the previous one, otherwise, we
1543 // don't want the merge.
1544 nsTArray
<nsCString
> headersSet
;
1546 AutoTArray
<InternalHeaders::Entry
, 5> headers
;
1547 mRequest
->Headers()->GetEntries(headers
);
1548 for (uint32_t i
= 0; i
< headers
.Length(); ++i
) {
1549 if (aStripRequestBodyHeader
&&
1550 (headers
[i
].mName
.LowerCaseEqualsASCII("content-type") ||
1551 headers
[i
].mName
.LowerCaseEqualsASCII("content-encoding") ||
1552 headers
[i
].mName
.LowerCaseEqualsASCII("content-language") ||
1553 headers
[i
].mName
.LowerCaseEqualsASCII("content-location"))) {
1557 bool alreadySet
= headersSet
.Contains(headers
[i
].mName
);
1559 headersSet
.AppendElement(headers
[i
].mName
);
1562 if (headers
[i
].mValue
.IsEmpty()) {
1563 DebugOnly
<nsresult
> rv
=
1564 aChannel
->SetEmptyRequestHeader(headers
[i
].mName
);
1565 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1567 DebugOnly
<nsresult
> rv
= aChannel
->SetRequestHeader(
1568 headers
[i
].mName
, headers
[i
].mValue
, alreadySet
/* merge */);
1569 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1573 nsAutoCString method
;
1574 mRequest
->GetMethod(method
);
1575 if (!method
.EqualsLiteral("GET") && !method
.EqualsLiteral("HEAD")) {
1576 nsAutoString origin
;
1577 if (NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(mPrincipal
, origin
))) {
1578 DebugOnly
<nsresult
> rv
= aChannel
->SetRequestHeader(
1579 nsDependentCString(net::nsHttp::Origin
),
1580 NS_ConvertUTF16toUTF8(origin
), false /* merge */);
1581 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1586 void FetchDriver::RunAbortAlgorithm() {
1587 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
1591 mResponseAvailableCalled
= true;
1593 mObserver
->OnResponseEnd(FetchDriverObserver::eAborted
);
1594 mObserver
= nullptr;
1598 mChannel
->Cancel(NS_BINDING_ABORTED
);
1605 } // namespace mozilla::dom