Bug 1848468 - Mark k-rate-dynamics-compressor-connections.html subtest as failing...
[gecko.git] / dom / fetch / FetchDriver.cpp
blob8b741121bd3b146aa5906f61be677d4170d35b4a
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 "js/Value.h"
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"
16 #include "nsIFile.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"
27 #include "nsIPipe.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"
58 #include "Fetch.h"
59 #include "FetchUtil.h"
60 #include "InternalRequest.h"
61 #include "InternalResponse.h"
63 namespace mozilla::dom {
65 namespace {
67 void GetBlobURISpecFromChannel(nsIRequest* aRequest, nsCString& aBlobURISpec) {
68 MOZ_ASSERT(aRequest);
70 aBlobURISpec.SetIsVoid(true);
72 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
73 if (!channel) {
74 return;
77 nsCOMPtr<nsIURI> uri;
78 nsresult rv = NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
79 if (NS_FAILED(rv)) {
80 return;
83 if (!dom::IsBlobURI(uri)) {
84 return;
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 {
104 public:
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
135 // CANCELED anymore.
136 enum eStatus { LOADING = 0, COMPLETED, CANCELED, FALLBACK };
138 AlternativeDataStreamListener(FetchDriver* aFetchDriver, nsIChannel* aChannel,
139 const nsACString& aAlternativeDataType);
140 eStatus Status();
141 void Cancel();
142 uint64_t GetAlternativeDataCacheEntryId();
143 const nsACString& GetAlternativeDataType() const;
144 already_AddRefed<nsICacheInfoChannel> GetCacheInfoChannel();
145 already_AddRefed<nsIInputStream> GetAlternativeInputStream();
147 private:
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),
172 mChannel(aChannel),
173 mStatus(AlternativeDataStreamListener::LOADING) {
174 MOZ_DIAGNOSTIC_ASSERT(mFetchDriver);
175 MOZ_DIAGNOSTIC_ASSERT(mChannel);
178 AlternativeDataStreamListener::eStatus AlternativeDataStreamListener::Status() {
179 return mStatus;
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
189 // to FetchDriver
190 mChannel->CancelWithReason(NS_BINDING_ABORTED,
191 "AlternativeDataStreamListener::Cancel"_ns);
192 mChannel = nullptr;
194 mStatus = AlternativeDataStreamListener::CANCELED;
197 uint64_t AlternativeDataStreamListener::GetAlternativeDataCacheEntryId() {
198 return mAlternativeDataCacheEntryId;
201 const nsACString& AlternativeDataStreamListener::GetAlternativeDataType()
202 const {
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();
218 NS_IMETHODIMP
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);
258 NS_IMETHODIMP
259 AlternativeDataStreamListener::OnDataAvailable(nsIRequest* aRequest,
260 nsIInputStream* aInputStream,
261 uint64_t aOffset,
262 uint32_t aCount) {
263 if (mStatus == AlternativeDataStreamListener::LOADING) {
264 MOZ_ASSERT(mPipeAlternativeOutputStream);
265 uint32_t read = 0;
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,
272 aCount);
274 return NS_OK;
277 NS_IMETHODIMP
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) {
287 // do nothing
288 return NS_OK;
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);
316 return NS_OK;
319 NS_IMETHODIMP
320 AlternativeDataStreamListener::CheckListenerChain() { return NS_OK; }
321 //-----------------------------------------------------------------------------
322 // FetchDriver
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)
344 #ifdef DEBUG
346 mResponseAvailableCalled(false),
347 mFetchCalled(false)
348 #endif
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.
370 if (!mDocument) {
371 // Preloads are mapped on the document, no document, no preload.
372 return nullptr;
374 CORSMode cors;
375 switch (mRequest->Mode()) {
376 case RequestMode::No_cors:
377 cors = CORSMode::CORS_NONE;
378 break;
379 case RequestMode::Cors:
380 cors = mRequest->GetCredentialsMode() == RequestCredentials::Include
381 ? CORSMode::CORS_USE_CREDENTIALS
382 : CORSMode::CORS_ANONYMOUS;
383 break;
384 default:
385 // Can't be satisfied by a preload because preload cannot define any of
386 // remaining modes.
387 return nullptr;
389 if (!mRequest->Headers()->HasOnlySimpleHeaders()) {
390 // Preload can't set any headers.
391 return nullptr;
393 if (!mRequest->GetIntegrity().IsEmpty()) {
394 // There is currently no support for SRI checking in the fetch preloader.
395 return nullptr;
397 if (mRequest->GetCacheMode() != RequestCache::Default) {
398 // Preload can only go with the default caching mode.
399 return nullptr;
401 if (mRequest->SkipServiceWorker()) {
402 // Preload can't be forbidden interception.
403 return nullptr;
405 if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
406 // Preload always follows redirects.
407 return nullptr;
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.
414 return nullptr;
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);
425 if (!httpChannel) {
426 return;
429 nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
430 if (!referrerInfo) {
431 return;
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();
443 #ifdef DEBUG
444 MOZ_ASSERT(!mFetchCalled);
445 mFetchCalled = true;
446 #endif
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))) {
459 return rv;
462 mRequest->SetPrincipalInfo(std::move(principalInfo));
464 // If the signal is aborted, it's time to inform the observer and terminate
465 // the operation.
466 if (aSignalImpl) {
467 if (aSignalImpl->Aborted()) {
468 FetchDriverAbortActions(aSignalImpl);
469 return NS_OK;
472 Follow(aSignalImpl);
475 rv = HttpFetch(mRequest->GetPreferredAlternativeDataType());
476 if (NS_FAILED(rv)) {
477 FailWithNetworkError(rv);
480 // Any failure is handled by FailWithNetworkError notifying the aObserver.
481 return NS_OK;
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."
492 mResponse = nullptr;
493 mOnStopRequestCalled = false;
494 nsresult rv;
496 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
497 NS_ENSURE_SUCCESS(rv, rv);
499 nsAutoCString url;
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);
523 if (fetchPreload) {
524 fetchPreload->RemoveSelf(mDocument);
525 fetchPreload->NotifyUsage(mDocument, PreloaderBase::LoadBackground::Keep);
527 rv = fetchPreload->AsyncConsume(this);
528 if (NS_SUCCEEDED(rv)) {
529 mFromPreload = true;
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());
540 } else {
541 mRequest->AddURL(redirect.Spec(), redirect.Fragment());
545 return NS_OK;
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
566 // request too.
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
579 : 0;
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;
589 } else {
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;
608 } else {
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;
619 if (mDocument) {
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 */
626 loadFlags, ios);
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 */
632 loadFlags, ios);
633 } else {
634 rv =
635 NS_NewChannel(getter_AddRefs(chan), uri, mPrincipal, secFlags,
636 mRequest->ContentPolicyType(), mCookieJarSettings,
637 mPerformanceStorage, mLoadGroup, nullptr, /* aCallbacks */
638 loadFlags, ios);
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,
689 nsGkAtoms::embed)) {
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.
697 #ifdef DEBUG
699 nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
700 chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
701 MOZ_ASSERT(!notificationCallbacks);
703 #endif
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
708 // events.
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);
718 if (httpChan) {
719 // Copy the method.
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;
744 referrerPolicy =
745 ReferrerInfo::GetDefaultReferrerPolicy(httpChan, uri, isPrivate);
746 mRequest->SetReferrerPolicy(referrerPolicy);
749 rv = FetchUtil::SetRequestReferrer(mPrincipal, mDocument, httpChan,
750 *mRequest);
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));
776 if (timedChannel) {
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);
785 if (uploadChan) {
786 nsAutoCString contentType;
787 ErrorResult result;
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
791 // it out.
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.
798 #ifdef DEBUG
799 bool hasContentTypeHeader =
800 mRequest->Headers()->Has("content-type"_ns, result);
801 MOZ_ASSERT(!result.Failed());
802 MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid());
803 #endif // DEBUG
805 int64_t bodyLength;
806 nsCOMPtr<nsIInputStream> bodyStream;
807 mRequest->GetBody(getter_AddRefs(bodyStream), &bodyLength);
808 if (bodyStream) {
809 nsAutoCString method;
810 mRequest->GetMethod(method);
811 rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType,
812 bodyLength, method,
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
822 // in the request.
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);
838 if (p) {
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)) {
851 ErrorResult result;
852 nsAutoCString range;
853 mRequest->Headers()->Get("Range"_ns, range, result);
854 MOZ_ASSERT(!result.Failed());
855 if (!range.IsVoid()) {
856 rv = NS_SetChannelContentRangeForBlobURI(chan, uri, range);
857 if (NS_FAILED(rv)) {
858 return rv;
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);
868 if (cic) {
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);
876 } else {
877 rv = chan->AsyncOpen(this);
879 } else {
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::
889 SERIALIZE);
893 rv = chan->AsyncOpen(this);
896 if (NS_FAILED(rv)) {
897 return rv;
900 // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
902 mChannel = chan;
903 return NS_OK;
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();
916 } else {
917 switch (mRequest->GetResponseTainting()) {
918 case LoadTainting::Basic:
919 filteredResponse = aResponse->BasicResponse();
920 break;
921 case LoadTainting::CORS:
922 filteredResponse = aResponse->CORSResponse();
923 break;
924 case LoadTainting::Opaque: {
925 filteredResponse = aResponse->OpaqueResponse();
926 nsresult rv = filteredResponse->GeneratePaddingInfo();
927 if (NS_WARN_IF(NS_FAILED(rv))) {
928 return nullptr;
930 break;
932 default:
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());
944 #ifdef DEBUG
945 mResponseAvailableCalled = true;
946 #endif
949 return filteredResponse;
952 void FetchDriver::FailWithNetworkError(nsresult rv) {
953 AssertIsOnMainThread();
954 if (mObserver) {
955 // Need to keep mObserver alive.
956 RefPtr<FetchDriverObserver> observer = mObserver;
957 observer->OnResponseAvailable(InternalResponse::NetworkError(rv));
958 #ifdef DEBUG
959 mResponseAvailableCalled = true;
960 #endif
963 // mObserver could be null after OnResponseAvailable().
964 if (mObserver) {
965 mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking,
966 JS::UndefinedHandleValue);
967 mObserver = nullptr;
970 mChannel = nullptr;
971 Unfollow();
974 NS_IMETHODIMP
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;
988 if (!mChannel) {
989 MOZ_ASSERT(!mObserver);
990 return NS_BINDING_ABORTED;
993 nsresult rv;
994 aRequest->GetStatus(&rv);
995 if (NS_FAILED(rv)) {
996 FailWithNetworkError(rv);
997 return rv;
1000 // We should only get to the following code once.
1001 MOZ_ASSERT(!mPipeOutputStream);
1003 if (!mObserver) {
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);
1027 if (httpChannel) {
1028 channel->GetContentType(contentType);
1030 uint32_t responseStatus = 0;
1031 rv = httpChannel->GetResponseStatus(&responseStatus);
1032 if (NS_FAILED(rv)) {
1033 FailWithNetworkError(rv);
1034 return 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))) {
1058 return 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
1067 // encodings.
1068 ErrorResult result;
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
1073 // get this wrong.
1074 contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
1076 MOZ_ASSERT(!result.Failed());
1077 } else {
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());
1085 MOZ_ASSERT(bchan);
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());
1105 MOZ_ASSERT(dchan);
1106 contentType.Assign(dchan->MimeType());
1107 } else {
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);
1132 if (cic) {
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)) &&
1144 cacheEntryId !=
1145 mAltDataListener->GetAlternativeDataCacheEntryId()) {
1146 mAltDataListener->Cancel();
1147 } else {
1148 // AlternativeDataStreamListener::OnStartRequest had already been
1149 // called, the alternative data input stream and cacheInfo channel
1150 // must be created.
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));
1176 MOZ_ASSERT(
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);
1217 if (fc) {
1218 nsCOMPtr<nsIFile> file;
1219 rv = fc->GetFile(getter_AddRefs(file));
1220 if (!NS_WARN_IF(NS_FAILED(rv))) {
1221 nsAutoString path;
1222 file->GetPath(path);
1223 response->SetBodyLocalPath(path);
1225 } else {
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.
1248 mResponse =
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;
1262 if (mObserver) {
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,
1273 &mSRIMetadata);
1274 mSRIDataVerifier =
1275 MakeUnique<SRICheckDataVerifier>(mSRIMetadata, sourceUri, reporter);
1277 // Do not retarget off main thread when using SRI API.
1278 return NS_OK;
1281 nsCOMPtr<nsIEventTarget> sts =
1282 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
1283 if (NS_WARN_IF(NS_FAILED(rv))) {
1284 FailWithNetworkError(rv);
1285 // Cancel request.
1286 return 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)));
1295 return NS_OK;
1298 namespace {
1300 // Runnable to call the observer OnDataAvailable on the main-thread.
1301 class DataAvailableRunnable final : public Runnable {
1302 RefPtr<FetchDriverObserver> mObserver;
1304 public:
1305 explicit DataAvailableRunnable(FetchDriverObserver* aObserver)
1306 : Runnable("dom::DataAvailableRunnable"), mObserver(aObserver) {
1307 MOZ_ASSERT(aObserver);
1310 NS_IMETHOD
1311 Run() override {
1312 mObserver->OnDataAvailable();
1313 mObserver = nullptr;
1314 return NS_OK;
1318 struct SRIVerifierAndOutputHolder {
1319 SRIVerifierAndOutputHolder(SRICheckDataVerifier* aVerifier,
1320 nsIOutputStream* aOutputStream)
1321 : mVerifier(aVerifier), mOutputStream(aOutputStream) {}
1323 SRICheckDataVerifier* mVerifier;
1324 nsIOutputStream* mOutputStream;
1326 private:
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,
1337 "Bogus holder");
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.
1343 *aCountWritten = 0;
1344 while (aCount) {
1345 uint32_t n = 0;
1346 rv = holder->mOutputStream->Write(aBuffer, aCount, &n);
1347 if (NS_FAILED(rv)) {
1348 return rv;
1350 aBuffer += n;
1351 aCount -= n;
1352 *aCountWritten += n;
1354 return NS_OK;
1357 } // anonymous namespace
1359 NS_IMETHODIMP
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
1364 // about races.
1366 if (!mPipeOutputStream) {
1367 // We ignore the body for HEAD/CONNECT requests.
1368 // nsIStreamListener mandates reading from the stream before returning.
1369 uint32_t totalRead;
1370 nsresult rv = aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount,
1371 &totalRead);
1372 NS_ENSURE_SUCCESS(rv, rv);
1373 return NS_OK;
1376 if (mNeedToObserveOnDataAvailable) {
1377 mNeedToObserveOnDataAvailable = false;
1378 if (mObserver) {
1379 // Need to keep mObserver alive.
1380 RefPtr<FetchDriverObserver> observer = mObserver;
1381 if (NS_IsMainThread()) {
1382 observer->OnDataAvailable();
1383 } else {
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))) {
1388 return rv;
1394 if (!mResponse) {
1395 MOZ_ASSERT(false);
1396 return NS_ERROR_UNEXPECTED;
1399 // Needs to be initialized to 0 because in some cases nsStringInputStream may
1400 // not write to aRead.
1401 uint32_t aRead = 0;
1402 MOZ_ASSERT(mPipeOutputStream);
1404 // From "Main Fetch" step 19: SRI-part2.
1405 // Note: Avoid checking the hidden opaque body.
1406 nsresult rv;
1407 if (mResponse->Type() != ResponseType::Opaque &&
1408 ShouldCheckSRI(*mRequest, *mResponse)) {
1409 MOZ_ASSERT(mSRIDataVerifier);
1411 SRIVerifierAndOutputHolder holder(mSRIDataVerifier.get(),
1412 mPipeOutputStream);
1413 rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI, &holder, aCount,
1414 &aRead);
1415 } else {
1416 rv = aInputStream->ReadSegments(NS_CopySegmentToStream, mPipeOutputStream,
1417 aCount, &aRead);
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;
1429 return rv;
1432 NS_IMETHODIMP
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.
1445 if (mObserver) {
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);
1455 if (outputStream) {
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.
1465 } else {
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;
1476 if (mObserver) {
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);
1486 nsresult rv =
1487 mSRIDataVerifier->Verify(mSRIMetadata, channel, sourceUri, reporter);
1488 if (NS_FAILED(rv)) {
1489 if (altDataListener) {
1490 altDataListener->Cancel();
1492 FailWithNetworkError(rv);
1493 // Cancel request.
1494 return rv;
1498 if (mPipeOutputStream) {
1499 mPipeOutputStream->Close();
1503 FinishOnStopRequest(altDataListener);
1504 return NS_OK;
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) {
1513 return;
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.
1522 return;
1525 if (mObserver) {
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());
1532 #ifdef DEBUG
1533 mResponseAvailableCalled = true;
1534 #endif
1538 if (mObserver) {
1539 mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking,
1540 JS::UndefinedHandleValue);
1541 mObserver = nullptr;
1544 mChannel = nullptr;
1545 Unfollow();
1548 NS_IMETHODIMP
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,
1556 aShouldIntercept);
1559 nsCOMPtr<nsINetworkInterceptController> controller;
1560 NS_QueryNotificationCallbacks(nullptr, mLoadGroup,
1561 NS_GET_IID(nsINetworkInterceptController),
1562 getter_AddRefs(controller));
1563 if (controller) {
1564 return controller->ShouldPrepareForIntercept(aURI, aChannel,
1565 aShouldIntercept);
1568 *aShouldIntercept = false;
1569 return NS_OK;
1572 NS_IMETHODIMP
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));
1583 if (controller) {
1584 return controller->ChannelIntercepted(aChannel);
1587 return NS_OK;
1590 void FetchDriver::EnableNetworkInterceptControl() {
1591 MOZ_ASSERT(XRE_IsParentProcess());
1592 MOZ_ASSERT(NS_IsMainThread());
1593 MOZ_ASSERT(!mInterceptController);
1594 mInterceptController = new ServiceWorkerInterceptController();
1597 NS_IMETHODIMP
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);
1607 // Fetch 4.4.11
1608 bool rewriteToGET = false;
1609 Unused << oldHttpChannel->ShouldStripRequestBodyHeader(method,
1610 &rewriteToGET);
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))) {
1631 return rv;
1633 nsCString spec;
1634 rv = uriClone->GetSpec(spec);
1635 if (NS_WARN_IF(NS_FAILED(rv))) {
1636 return rv;
1638 nsCString fragment;
1639 rv = uri->GetRef(fragment);
1640 if (NS_WARN_IF(NS_FAILED(rv))) {
1641 return rv;
1644 if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
1645 mRequest->AddURL(spec, fragment);
1646 } else {
1647 // Overwrite the URL only when the request is redirected by a service
1648 // worker.
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);
1657 return NS_OK;
1660 NS_IMETHODIMP
1661 FetchDriver::CheckListenerChain() { return NS_OK; }
1663 NS_IMETHODIMP
1664 FetchDriver::GetInterface(const nsIID& aIID, void** aResult) {
1665 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1666 *aResult = static_cast<nsIChannelEventSink*>(this);
1667 NS_ADDREF_THIS();
1668 return NS_OK;
1670 if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
1671 *aResult = static_cast<nsIStreamListener*>(this);
1672 NS_ADDREF_THIS();
1673 return NS_OK;
1675 if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
1676 *aResult = static_cast<nsIRequestObserver*>(this);
1677 NS_ADDREF_THIS();
1678 return NS_OK;
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());
1709 if (!mChannel) {
1710 return nullptr;
1713 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel);
1714 if (!timedChannel) {
1715 return nullptr;
1717 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
1718 if (!httpChannel) {
1719 return nullptr;
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"))) {
1745 continue;
1748 if (aStripAuthHeader &&
1749 headers[i].mName.LowerCaseEqualsASCII("authorization")) {
1750 continue;
1753 bool alreadySet = headersSet.Contains(headers[i].mName);
1754 if (!alreadySet) {
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));
1762 } else {
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());
1775 if (mObserver) {
1776 #ifdef DEBUG
1777 mResponseAvailableCalled = true;
1778 #endif
1779 JS::Rooted<JS::Value> reason(RootingCx());
1780 if (aSignalImpl) {
1781 reason.set(aSignalImpl->RawReason());
1783 mObserver->OnResponseEnd(FetchDriverObserver::eAborted, reason);
1784 mObserver = nullptr;
1787 if (mChannel) {
1788 mChannel->CancelWithReason(NS_BINDING_ABORTED,
1789 "FetchDriver::RunAbortAlgorithm"_ns);
1790 mChannel = nullptr;
1793 mAborted = true;
1796 } // namespace mozilla::dom