Bug 1700051: part 26) Correct typo in comment of `mozInlineSpellWordUtil::BuildSoftTe...
[gecko.git] / dom / fetch / FetchDriver.cpp
blobb87c9115152f93d3e68d661ad9cec9a0faaaecdf
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"
13 #include "nsIFile.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"
23 #include "nsIPipe.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"
47 #include "Fetch.h"
48 #include "FetchUtil.h"
49 #include "InternalRequest.h"
50 #include "InternalResponse.h"
52 namespace mozilla::dom {
54 namespace {
56 void GetBlobURISpecFromChannel(nsIRequest* aRequest, nsCString& aBlobURISpec) {
57 MOZ_ASSERT(aRequest);
59 aBlobURISpec.SetIsVoid(true);
61 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
62 if (!channel) {
63 return;
66 nsCOMPtr<nsIURI> uri;
67 nsresult rv = channel->GetURI(getter_AddRefs(uri));
68 if (NS_FAILED(rv)) {
69 return;
72 if (!dom::IsBlobURI(uri)) {
73 return;
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 {
93 public:
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
124 // CANCELED anymore.
125 enum eStatus { LOADING = 0, COMPLETED, CANCELED, FALLBACK };
127 AlternativeDataStreamListener(FetchDriver* aFetchDriver, nsIChannel* aChannel,
128 const nsACString& aAlternativeDataType);
129 eStatus Status();
130 void Cancel();
131 uint64_t GetAlternativeDataCacheEntryId();
132 const nsACString& GetAlternativeDataType() const;
133 already_AddRefed<nsICacheInfoChannel> GetCacheInfoChannel();
134 already_AddRefed<nsIInputStream> GetAlternativeInputStream();
136 private:
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),
161 mChannel(aChannel),
162 mStatus(AlternativeDataStreamListener::LOADING) {
163 MOZ_DIAGNOSTIC_ASSERT(mFetchDriver);
164 MOZ_DIAGNOSTIC_ASSERT(mChannel);
167 AlternativeDataStreamListener::eStatus AlternativeDataStreamListener::Status() {
168 return mStatus;
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
178 // to FetchDriver
179 mChannel->Cancel(NS_BINDING_ABORTED);
180 mChannel = nullptr;
182 mStatus = AlternativeDataStreamListener::CANCELED;
185 uint64_t AlternativeDataStreamListener::GetAlternativeDataCacheEntryId() {
186 return mAlternativeDataCacheEntryId;
189 const nsACString& AlternativeDataStreamListener::GetAlternativeDataType()
190 const {
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();
206 NS_IMETHODIMP
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);
220 nsresult rv =
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 */);
227 if (NS_FAILED(rv)) {
228 mFetchDriver->FailWithNetworkError(rv);
229 return 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();
239 } else {
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);
252 return NS_OK;
255 NS_IMETHODIMP
256 AlternativeDataStreamListener::OnDataAvailable(nsIRequest* aRequest,
257 nsIInputStream* aInputStream,
258 uint64_t aOffset,
259 uint32_t aCount) {
260 if (mStatus == AlternativeDataStreamListener::LOADING) {
261 MOZ_ASSERT(mPipeAlternativeOutputStream);
262 uint32_t read = 0;
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,
269 aCount);
271 return NS_OK;
274 NS_IMETHODIMP
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) {
284 // do nothing
285 return NS_OK;
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);
313 return NS_OK;
316 NS_IMETHODIMP
317 AlternativeDataStreamListener::CheckListenerChain() { return NS_OK; }
318 //-----------------------------------------------------------------------------
319 // FetchDriver
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)
340 #ifdef DEBUG
342 mResponseAvailableCalled(false),
343 mFetchCalled(false)
344 #endif
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.
366 if (!mDocument) {
367 // Preloads are mapped on the document, no document, no preload.
368 return nullptr;
370 CORSMode cors;
371 switch (mRequest->Mode()) {
372 case RequestMode::No_cors:
373 cors = CORSMode::CORS_NONE;
374 break;
375 case RequestMode::Cors:
376 cors = mRequest->GetCredentialsMode() == RequestCredentials::Include
377 ? CORSMode::CORS_USE_CREDENTIALS
378 : CORSMode::CORS_ANONYMOUS;
379 break;
380 default:
381 // Can't be satisfied by a preload because preload cannot define any of
382 // remaining modes.
383 return nullptr;
385 if (!mRequest->Headers()->HasOnlySimpleHeaders()) {
386 // Preload can't set any headers.
387 return nullptr;
389 if (!mRequest->GetIntegrity().IsEmpty()) {
390 // There is currently no support for SRI checking in the fetch preloader.
391 return nullptr;
393 if (mRequest->GetCacheMode() != RequestCache::Default) {
394 // Preload can only go with the default caching mode.
395 return nullptr;
397 if (mRequest->SkipServiceWorker()) {
398 // Preload can't be forbidden interception.
399 return nullptr;
401 if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
402 // Preload always follows redirects.
403 return nullptr;
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.
410 return nullptr;
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);
421 if (!httpChannel) {
422 return;
425 nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
426 if (!referrerInfo) {
427 return;
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();
439 #ifdef DEBUG
440 MOZ_ASSERT(!mFetchCalled);
441 mFetchCalled = true;
442 #endif
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))) {
455 return rv;
458 mRequest->SetPrincipalInfo(std::move(principalInfo));
460 // If the signal is aborted, it's time to inform the observer and terminate
461 // the operation.
462 if (aSignalImpl) {
463 if (aSignalImpl->Aborted()) {
464 RunAbortAlgorithm();
465 return NS_OK;
468 Follow(aSignalImpl);
471 rv = HttpFetch(mRequest->GetPreferredAlternativeDataType());
472 if (NS_FAILED(rv)) {
473 FailWithNetworkError(rv);
476 // Any failure is handled by FailWithNetworkError notifying the aObserver.
477 return NS_OK;
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."
488 mResponse = nullptr;
489 mOnStopRequestCalled = false;
490 nsresult rv;
492 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
493 NS_ENSURE_SUCCESS(rv, rv);
495 nsAutoCString url;
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);
519 if (fetchPreload) {
520 fetchPreload->RemoveSelf(mDocument);
521 fetchPreload->NotifyUsage(PreloaderBase::LoadBackground::Keep);
523 rv = fetchPreload->AsyncConsume(this);
524 if (NS_SUCCEEDED(rv)) {
525 mFromPreload = true;
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());
536 } else {
537 mRequest->AddURL(redirect.Spec(), redirect.Fragment());
541 return NS_OK;
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
562 // request too.
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
575 : 0;
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;
585 } else {
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;
604 } else {
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;
615 if (mDocument) {
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 */
622 loadFlags, ios);
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 */
628 loadFlags, ios);
629 } else {
630 rv =
631 NS_NewChannel(getter_AddRefs(chan), uri, mPrincipal, secFlags,
632 mRequest->ContentPolicyType(), mCookieJarSettings,
633 mPerformanceStorage, mLoadGroup, nullptr, /* aCallbacks */
634 loadFlags, ios);
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.
652 #ifdef DEBUG
654 nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
655 chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
656 MOZ_ASSERT(!notificationCallbacks);
658 #endif
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
663 // events.
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);
673 if (httpChan) {
674 // Copy the method.
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;
699 referrerPolicy =
700 ReferrerInfo::GetDefaultReferrerPolicy(httpChan, uri, isPrivate);
701 mRequest->SetReferrerPolicy(referrerPolicy);
704 rv = FetchUtil::SetRequestReferrer(mPrincipal, mDocument, httpChan,
705 *mRequest);
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));
731 if (timedChannel) {
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);
740 if (uploadChan) {
741 nsAutoCString contentType;
742 ErrorResult result;
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
746 // it out.
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.
753 #ifdef DEBUG
754 bool hasContentTypeHeader =
755 mRequest->Headers()->Has("content-type"_ns, result);
756 MOZ_ASSERT(!result.Failed());
757 MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid());
758 #endif // DEBUG
760 int64_t bodyLength;
761 nsCOMPtr<nsIInputStream> bodyStream;
762 mRequest->GetBody(getter_AddRefs(bodyStream), &bodyLength);
763 if (bodyStream) {
764 nsAutoCString method;
765 mRequest->GetMethod(method);
766 rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType,
767 bodyLength, method,
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
777 // in the request.
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);
793 if (p) {
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);
805 if (cic) {
806 cic->PreferAlternativeDataType(aPreferredAlternativeDataType, ""_ns,
807 true);
808 MOZ_ASSERT(!mAltDataListener);
809 mAltDataListener = new AlternativeDataStreamListener(
810 this, chan, aPreferredAlternativeDataType);
811 rv = chan->AsyncOpen(mAltDataListener);
812 } else {
813 rv = chan->AsyncOpen(this);
815 } else {
816 // Integrity check cannot be done on alt-data yet.
817 if (mRequest->GetIntegrity().IsEmpty()) {
818 nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(chan);
819 if (cic) {
820 cic->PreferAlternativeDataType(nsLiteralCString(WASM_ALT_DATA_TYPE_V1),
821 nsLiteralCString(WASM_CONTENT_TYPE),
822 false);
826 rv = chan->AsyncOpen(this);
829 if (NS_FAILED(rv)) {
830 return rv;
833 // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
835 mChannel = chan;
836 return NS_OK;
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();
848 } else {
849 switch (mRequest->GetResponseTainting()) {
850 case LoadTainting::Basic:
851 filteredResponse = aResponse->BasicResponse();
852 break;
853 case LoadTainting::CORS:
854 filteredResponse = aResponse->CORSResponse();
855 break;
856 case LoadTainting::Opaque: {
857 filteredResponse = aResponse->OpaqueResponse();
858 nsresult rv = filteredResponse->GeneratePaddingInfo();
859 if (NS_WARN_IF(NS_FAILED(rv))) {
860 return nullptr;
862 break;
864 default:
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);
875 #ifdef DEBUG
876 mResponseAvailableCalled = true;
877 #endif
880 return filteredResponse.forget();
883 void FetchDriver::FailWithNetworkError(nsresult rv) {
884 AssertIsOnMainThread();
885 RefPtr<InternalResponse> error = InternalResponse::NetworkError(rv);
886 if (mObserver) {
887 // Need to keep mObserver alive.
888 RefPtr<FetchDriverObserver> observer = mObserver;
889 observer->OnResponseAvailable(error);
890 #ifdef DEBUG
891 mResponseAvailableCalled = true;
892 #endif
895 // mObserver could be null after OnResponseAvailable().
896 if (mObserver) {
897 mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
898 mObserver = nullptr;
901 mChannel = nullptr;
904 NS_IMETHODIMP
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;
917 if (!mChannel) {
918 MOZ_ASSERT(!mObserver);
919 return NS_BINDING_ABORTED;
922 nsresult rv;
923 aRequest->GetStatus(&rv);
924 if (NS_FAILED(rv)) {
925 FailWithNetworkError(rv);
926 return rv;
929 // We should only get to the following code once.
930 MOZ_ASSERT(!mPipeOutputStream);
932 if (!mObserver) {
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);
957 if (httpChannel) {
958 uint32_t responseStatus = 0;
959 rv = httpChannel->GetResponseStatus(&responseStatus);
960 if (NS_FAILED(rv)) {
961 FailWithNetworkError(rv);
962 return 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))) {
986 return 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
995 // encodings.
996 ErrorResult result;
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
1001 // get this wrong.
1002 contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
1004 MOZ_ASSERT(!result.Failed());
1005 } else {
1006 response =
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);
1032 if (cic) {
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)) &&
1044 cacheEntryId !=
1045 mAltDataListener->GetAlternativeDataCacheEntryId()) {
1046 mAltDataListener->Cancel();
1047 } else {
1048 // AlternativeDataStreamListener::OnStartRequest had already been
1049 // called, the alternative data input stream and cacheInfo channel
1050 // must be created.
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));
1076 MOZ_ASSERT(
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);
1110 // Cancel request.
1111 return 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);
1118 if (fc) {
1119 nsCOMPtr<nsIFile> file;
1120 rv = fc->GetFile(getter_AddRefs(file));
1121 if (!NS_WARN_IF(NS_FAILED(rv))) {
1122 nsAutoString path;
1123 file->GetPath(path);
1124 response->SetBodyLocalPath(path);
1126 } else {
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);
1142 // Cancel request.
1143 return 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;
1170 if (mObserver) {
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,
1181 &mSRIMetadata);
1182 mSRIDataVerifier =
1183 MakeUnique<SRICheckDataVerifier>(mSRIMetadata, sourceUri, reporter);
1185 // Do not retarget off main thread when using SRI API.
1186 return NS_OK;
1189 nsCOMPtr<nsIEventTarget> sts =
1190 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
1191 if (NS_WARN_IF(NS_FAILED(rv))) {
1192 FailWithNetworkError(rv);
1193 // Cancel request.
1194 return 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)));
1201 return NS_OK;
1204 namespace {
1206 // Runnable to call the observer OnDataAvailable on the main-thread.
1207 class DataAvailableRunnable final : public Runnable {
1208 RefPtr<FetchDriverObserver> mObserver;
1210 public:
1211 explicit DataAvailableRunnable(FetchDriverObserver* aObserver)
1212 : Runnable("dom::DataAvailableRunnable"), mObserver(aObserver) {
1213 MOZ_ASSERT(aObserver);
1216 NS_IMETHOD
1217 Run() override {
1218 mObserver->OnDataAvailable();
1219 mObserver = nullptr;
1220 return NS_OK;
1224 struct SRIVerifierAndOutputHolder {
1225 SRIVerifierAndOutputHolder(SRICheckDataVerifier* aVerifier,
1226 nsIOutputStream* aOutputStream)
1227 : mVerifier(aVerifier), mOutputStream(aOutputStream) {}
1229 SRICheckDataVerifier* mVerifier;
1230 nsIOutputStream* mOutputStream;
1232 private:
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,
1243 "Bogus holder");
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.
1249 *aCountWritten = 0;
1250 while (aCount) {
1251 uint32_t n = 0;
1252 rv = holder->mOutputStream->Write(aBuffer, aCount, &n);
1253 if (NS_FAILED(rv)) {
1254 return rv;
1256 aBuffer += n;
1257 aCount -= n;
1258 *aCountWritten += n;
1260 return NS_OK;
1263 } // anonymous namespace
1265 NS_IMETHODIMP
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
1270 // about races.
1272 if (mNeedToObserveOnDataAvailable) {
1273 mNeedToObserveOnDataAvailable = false;
1274 if (mObserver) {
1275 // Need to keep mObserver alive.
1276 RefPtr<FetchDriverObserver> observer = mObserver;
1277 if (NS_IsMainThread()) {
1278 observer->OnDataAvailable();
1279 } else {
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))) {
1284 return rv;
1290 // Needs to be initialized to 0 because in some cases nsStringInputStream may
1291 // not write to aRead.
1292 uint32_t aRead = 0;
1293 MOZ_ASSERT(mResponse);
1294 MOZ_ASSERT(mPipeOutputStream);
1296 // From "Main Fetch" step 19: SRI-part2.
1297 // Note: Avoid checking the hidden opaque body.
1298 nsresult rv;
1299 if (mResponse->Type() != ResponseType::Opaque &&
1300 ShouldCheckSRI(*mRequest, *mResponse)) {
1301 MOZ_ASSERT(mSRIDataVerifier);
1303 SRIVerifierAndOutputHolder holder(mSRIDataVerifier.get(),
1304 mPipeOutputStream);
1305 rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI, &holder, aCount,
1306 &aRead);
1307 } else {
1308 rv = aInputStream->ReadSegments(NS_CopySegmentToStream, mPipeOutputStream,
1309 aCount, &aRead);
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;
1321 return rv;
1324 NS_IMETHODIMP
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);
1341 if (outputStream) {
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.
1351 } else {
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;
1362 if (mObserver) {
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);
1372 nsresult rv =
1373 mSRIDataVerifier->Verify(mSRIMetadata, channel, sourceUri, reporter);
1374 if (NS_FAILED(rv)) {
1375 if (altDataListener) {
1376 altDataListener->Cancel();
1378 FailWithNetworkError(rv);
1379 // Cancel request.
1380 return rv;
1384 if (mPipeOutputStream) {
1385 mPipeOutputStream->Close();
1389 FinishOnStopRequest(altDataListener);
1390 return NS_OK;
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) {
1399 return;
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.
1408 return;
1411 if (mObserver) {
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);
1418 #ifdef DEBUG
1419 mResponseAvailableCalled = true;
1420 #endif
1424 if (mObserver) {
1425 mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
1426 mObserver = nullptr;
1429 mChannel = nullptr;
1432 NS_IMETHODIMP
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);
1442 // Fetch 4.4.11
1443 bool rewriteToGET = false;
1444 Unused << oldHttpChannel->ShouldStripRequestBodyHeader(method,
1445 &rewriteToGET);
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))) {
1460 return rv;
1462 nsCString spec;
1463 rv = uriClone->GetSpec(spec);
1464 if (NS_WARN_IF(NS_FAILED(rv))) {
1465 return rv;
1467 nsCString fragment;
1468 rv = uri->GetRef(fragment);
1469 if (NS_WARN_IF(NS_FAILED(rv))) {
1470 return rv;
1473 if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
1474 mRequest->AddURL(spec, fragment);
1475 } else {
1476 // Overwrite the URL only when the request is redirected by a service
1477 // worker.
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);
1486 return NS_OK;
1489 NS_IMETHODIMP
1490 FetchDriver::CheckListenerChain() { return NS_OK; }
1492 NS_IMETHODIMP
1493 FetchDriver::GetInterface(const nsIID& aIID, void** aResult) {
1494 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1495 *aResult = static_cast<nsIChannelEventSink*>(this);
1496 NS_ADDREF_THIS();
1497 return NS_OK;
1499 if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
1500 *aResult = static_cast<nsIStreamListener*>(this);
1501 NS_ADDREF_THIS();
1502 return NS_OK;
1504 if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
1505 *aResult = static_cast<nsIRequestObserver*>(this);
1506 NS_ADDREF_THIS();
1507 return NS_OK;
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"))) {
1554 continue;
1557 bool alreadySet = headersSet.Contains(headers[i].mName);
1558 if (!alreadySet) {
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));
1566 } else {
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());
1589 if (mObserver) {
1590 #ifdef DEBUG
1591 mResponseAvailableCalled = true;
1592 #endif
1593 mObserver->OnResponseEnd(FetchDriverObserver::eAborted);
1594 mObserver = nullptr;
1597 if (mChannel) {
1598 mChannel->Cancel(NS_BINDING_ABORTED);
1599 mChannel = nullptr;
1602 mAborted = true;
1605 } // namespace mozilla::dom