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/. */
6
7 #include "mozilla/DebugOnly.h"
8 #include "mozilla/dom/FetchDriver.h"
9
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"
24
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"
33
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"
46
47 #include "Fetch.h"
48 #include "FetchUtil.h"
49 #include "InternalRequest.h"
50 #include "InternalResponse.h"
51
52 namespace mozilla::dom {
53
54 namespace {
55
GetBlobURISpecFromChannel(nsIRequest * aRequest,nsCString & aBlobURISpec)56 void GetBlobURISpecFromChannel(nsIRequest* aRequest, nsCString& aBlobURISpec) {
57 MOZ_ASSERT(aRequest);
58
59 aBlobURISpec.SetIsVoid(true);
60
61 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
62 if (!channel) {
63 return;
64 }
65
66 nsCOMPtr<nsIURI> uri;
67 nsresult rv = channel->GetURI(getter_AddRefs(uri));
68 if (NS_FAILED(rv)) {
69 return;
70 }
71
72 if (!dom::IsBlobURI(uri)) {
73 return;
74 }
75
76 uri->GetSpec(aBlobURISpec);
77 }
78
ShouldCheckSRI(const InternalRequest & aRequest,const InternalResponse & aResponse)79 bool ShouldCheckSRI(const InternalRequest& aRequest,
80 const InternalResponse& aResponse) {
81 return !aRequest.GetIntegrity().IsEmpty() &&
82 aResponse.Type() != ResponseType::Error;
83 }
84
85 } // anonymous namespace
86
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
98
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
108 //
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
117 //
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 };
126
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();
135
136 private:
137 ~AlternativeDataStreamListener() = default;
138
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;
150 };
151
NS_IMPL_ISUPPORTS(AlternativeDataStreamListener,nsIStreamListener,nsIThreadRetargetableStreamListener)152 NS_IMPL_ISUPPORTS(AlternativeDataStreamListener, nsIStreamListener,
153 nsIThreadRetargetableStreamListener)
154
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);
165 }
166
Status()167 AlternativeDataStreamListener::eStatus AlternativeDataStreamListener::Status() {
168 return mStatus;
169 }
170
Cancel()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;
181 }
182 mStatus = AlternativeDataStreamListener::CANCELED;
183 }
184
GetAlternativeDataCacheEntryId()185 uint64_t AlternativeDataStreamListener::GetAlternativeDataCacheEntryId() {
186 return mAlternativeDataCacheEntryId;
187 }
188
GetAlternativeDataType() const189 const nsACString& AlternativeDataStreamListener::GetAlternativeDataType()
190 const {
191 return mAlternativeDataType;
192 }
193
194 already_AddRefed<nsIInputStream>
GetAlternativeInputStream()195 AlternativeDataStreamListener::GetAlternativeInputStream() {
196 nsCOMPtr<nsIInputStream> inputStream = mPipeAlternativeInputStream;
197 return inputStream.forget();
198 }
199
200 already_AddRefed<nsICacheInfoChannel>
GetCacheInfoChannel()201 AlternativeDataStreamListener::GetCacheInfoChannel() {
202 nsCOMPtr<nsICacheInfoChannel> channel = mCacheInfoChannel;
203 return channel.forget();
204 }
205
206 NS_IMETHODIMP
OnStartRequest(nsIRequest * aRequest)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 */);
226
227 if (NS_FAILED(rv)) {
228 mFetchDriver->FailWithNetworkError(rv);
229 return rv;
230 }
231
232 MOZ_DIAGNOSTIC_ASSERT(!mCacheInfoChannel);
233 mCacheInfoChannel = cic;
234
235 // call FetchDriver::HttpFetch to load main body
236 MOZ_ASSERT(mFetchDriver);
237 return mFetchDriver->HttpFetch();
238
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);
251 }
252 return NS_OK;
253 }
254
255 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)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);
265 }
266 if (mStatus == AlternativeDataStreamListener::FALLBACK) {
267 MOZ_ASSERT(mFetchDriver);
268 return mFetchDriver->OnDataAvailable(aRequest, aInputStream, aOffset,
269 aCount);
270 }
271 return NS_OK;
272 }
273
274 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatusCode)275 AlternativeDataStreamListener::OnStopRequest(nsIRequest* aRequest,
276 nsresult aStatusCode) {
277 AssertIsOnMainThread();
278
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);
282
283 if (mStatus == AlternativeDataStreamListener::CANCELED) {
284 // do nothing
285 return NS_OK;
286 }
287
288 if (mStatus == AlternativeDataStreamListener::FALLBACK) {
289 MOZ_ASSERT(fetchDriver);
290 return fetchDriver->OnStopRequest(aRequest, aStatusCode);
291 }
292
293 MOZ_DIAGNOSTIC_ASSERT(mStatus == AlternativeDataStreamListener::LOADING);
294
295 MOZ_ASSERT(!mAlternativeDataType.IsEmpty() && mPipeAlternativeOutputStream &&
296 mPipeAlternativeInputStream);
297
298 mPipeAlternativeOutputStream->Close();
299 mPipeAlternativeOutputStream = nullptr;
300
301 // Cleanup the states for alternative data if needed.
302 if (NS_FAILED(aStatusCode)) {
303 mAlternativeDataCacheEntryId = 0;
304 mCacheInfoChannel = nullptr;
305 mPipeAlternativeInputStream = nullptr;
306 }
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;
314 }
315
316 NS_IMETHODIMP
CheckListenerChain()317 AlternativeDataStreamListener::CheckListenerChain() { return NS_OK; }
318 //-----------------------------------------------------------------------------
319 // FetchDriver
320 //-----------------------------------------------------------------------------
321
NS_IMPL_ISUPPORTS(FetchDriver,nsIStreamListener,nsIChannelEventSink,nsIInterfaceRequestor,nsIThreadRetargetableStreamListener)322 NS_IMPL_ISUPPORTS(FetchDriver, nsIStreamListener, nsIChannelEventSink,
323 nsIInterfaceRequestor, nsIThreadRetargetableStreamListener)
324
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
341 ,
342 mResponseAvailableCalled(false),
343 mFetchCalled(false)
344 #endif
345 {
346 AssertIsOnMainThread();
347
348 MOZ_ASSERT(mRequest);
349 MOZ_ASSERT(aPrincipal);
350 MOZ_ASSERT(aMainThreadEventTarget);
351 }
352
~FetchDriver()353 FetchDriver::~FetchDriver() {
354 AssertIsOnMainThread();
355
356 // We assert this since even on failures, we should call
357 // FailWithNetworkError().
358 MOZ_ASSERT(mResponseAvailableCalled);
359 }
360
FindPreload(nsIURI * aURI)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.
365
366 if (!mDocument) {
367 // Preloads are mapped on the document, no document, no preload.
368 return nullptr;
369 }
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;
384 }
385 if (!mRequest->Headers()->HasOnlySimpleHeaders()) {
386 // Preload can't set any headers.
387 return nullptr;
388 }
389 if (!mRequest->GetIntegrity().IsEmpty()) {
390 // There is currently no support for SRI checking in the fetch preloader.
391 return nullptr;
392 }
393 if (mRequest->GetCacheMode() != RequestCache::Default) {
394 // Preload can only go with the default caching mode.
395 return nullptr;
396 }
397 if (mRequest->SkipServiceWorker()) {
398 // Preload can't be forbidden interception.
399 return nullptr;
400 }
401 if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
402 // Preload always follows redirects.
403 return nullptr;
404 }
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;
411 }
412
413 // OK, this request can be satisfied by a preloaded response, try to find one.
414
415 auto preloadKey = PreloadHashKey::CreateAsFetch(aURI, cors);
416 return mDocument->Preloads().LookupPreload(preloadKey);
417 }
418
UpdateReferrerInfoFromNewChannel(nsIChannel * aChannel)419 void FetchDriver::UpdateReferrerInfoFromNewChannel(nsIChannel* aChannel) {
420 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
421 if (!httpChannel) {
422 return;
423 }
424
425 nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
426 if (!referrerInfo) {
427 return;
428 }
429
430 nsAutoString computedReferrerSpec;
431 mRequest->SetReferrerPolicy(referrerInfo->ReferrerPolicy());
432 Unused << referrerInfo->GetComputedReferrerSpec(computedReferrerSpec);
433 mRequest->SetReferrer(computedReferrerSpec);
434 }
435
Fetch(AbortSignalImpl * aSignalImpl,FetchDriverObserver * aObserver)436 nsresult FetchDriver::Fetch(AbortSignalImpl* aSignalImpl,
437 FetchDriverObserver* aObserver) {
438 AssertIsOnMainThread();
439 #ifdef DEBUG
440 MOZ_ASSERT(!mFetchCalled);
441 mFetchCalled = true;
442 #endif
443
444 mObserver = aObserver;
445
446 // FIXME(nsm): Deal with HSTS.
447
448 MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(),
449 "Synchronous fetch not supported");
450
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;
456 }
457
458 mRequest->SetPrincipalInfo(std::move(principalInfo));
459
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;
466 }
467
468 Follow(aSignalImpl);
469 }
470
471 rv = HttpFetch(mRequest->GetPreferredAlternativeDataType());
472 if (NS_FAILED(rv)) {
473 FailWithNetworkError(rv);
474 }
475
476 // Any failure is handled by FailWithNetworkError notifying the aObserver.
477 return NS_OK;
478 }
479
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.
HttpFetch(const nsACString & aPreferredAlternativeDataType)483 nsresult FetchDriver::HttpFetch(
484 const nsACString& aPreferredAlternativeDataType) {
485 MOZ_ASSERT(NS_IsMainThread());
486
487 // Step 1. "Let response be null."
488 mResponse = nullptr;
489 mOnStopRequestCalled = false;
490 nsresult rv;
491
492 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
493 NS_ENSURE_SUCCESS(rv, rv);
494
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);
500
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;
507 }
508
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;
515 }
516 }
517
518 RefPtr<PreloaderBase> fetchPreload = FindPreload(uri);
519 if (fetchPreload) {
520 fetchPreload->RemoveSelf(mDocument);
521 fetchPreload->NotifyUsage(PreloaderBase::LoadBackground::Keep);
522
523 rv = fetchPreload->AsyncConsume(this);
524 if (NS_SUCCEEDED(rv)) {
525 mFromPreload = true;
526
527 mChannel = fetchPreload->Channel();
528 MOZ_ASSERT(mChannel);
529 mChannel->SetNotificationCallbacks(this);
530
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());
538 }
539 }
540
541 return NS_OK;
542 }
543
544 // The preload failed to be consumed. Behave like there were no preload.
545 fetchPreload = nullptr;
546 }
547
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.
552
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.
555 //
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.
559 //
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.
563
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."
569
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;
576
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;
588 }
589
590 if (mRequest->GetRedirectMode() != RequestRedirect::Follow) {
591 secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS;
592 }
593
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;
607 }
608
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;
613
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);
635 }
636 NS_ENSURE_SUCCESS(rv, rv);
637
638 if (mCSPEventListener) {
639 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
640 rv = loadInfo->SetCspEventListener(mCSPEventListener);
641 NS_ENSURE_SUCCESS(rv, rv);
642 }
643
644 {
645 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
646 rv = loadInfo->SetLoadingEmbedderPolicy(mRequest->GetEmbedderPolicy());
647 NS_ENSURE_SUCCESS(rv, rv);
648 }
649
650 if (mDocument && mDocument->GetEmbedderElement() &&
651 mDocument->GetEmbedderElement()->IsAnyOfHTMLElements(nsGkAtoms::object,
652 nsGkAtoms::embed)) {
653 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
654 rv = loadInfo->SetIsFromObjectOrEmbed(true);
655 NS_ENSURE_SUCCESS(rv, rv);
656 }
657
658 // Insert ourselves into the notification callbacks chain so we can set
659 // headers on redirects.
660 #ifdef DEBUG
661 {
662 nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
663 chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
664 MOZ_ASSERT(!notificationCallbacks);
665 }
666 #endif
667 chan->SetNotificationCallbacks(this);
668
669 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(chan));
670 // Mark channel as urgent-start if the Fetch is triggered by user input
671 // events.
672 if (cos && UserActivation::IsHandlingUserInput()) {
673 cos->AddClassFlags(nsIClassOfService::UrgentStart);
674 }
675
676 // Step 3.5 begins "HTTP network or cache fetch".
677 // HTTP network or cache fetch
678 // ---------------------------
679 // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest.
680 nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan);
681 if (httpChan) {
682 // Copy the method.
683 nsAutoCString method;
684 mRequest->GetMethod(method);
685 rv = httpChan->SetRequestMethod(method);
686 NS_ENSURE_SUCCESS(rv, rv);
687
688 // Set the same headers.
689 SetRequestHeaders(httpChan, false);
690
691 // Step 5 of https://fetch.spec.whatwg.org/#main-fetch
692 // If request's referrer policy is the empty string and request's client is
693 // non-null, then set request's referrer policy to request's client's
694 // associated referrer policy.
695 // Basically, "client" is not in our implementation, we use
696 // EnvironmentReferrerPolicy of the worker or document context
697 ReferrerPolicy referrerPolicy = mRequest->GetEnvironmentReferrerPolicy();
698 if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
699 mRequest->SetReferrerPolicy(referrerPolicy);
700 }
701 // Step 6 of https://fetch.spec.whatwg.org/#main-fetch
702 // If request’s referrer policy is the empty string,
703 // then set request’s referrer policy to the user-set default policy.
704 if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) {
705 nsCOMPtr<nsILoadInfo> loadInfo = httpChan->LoadInfo();
706 bool isPrivate = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
707 referrerPolicy =
708 ReferrerInfo::GetDefaultReferrerPolicy(httpChan, uri, isPrivate);
709 mRequest->SetReferrerPolicy(referrerPolicy);
710 }
711
712 rv = FetchUtil::SetRequestReferrer(mPrincipal, mDocument, httpChan,
713 *mRequest);
714 NS_ENSURE_SUCCESS(rv, rv);
715
716 // Bug 1120722 - Authorization will be handled later.
717 // Auth may require prompting, we don't support it yet.
718 // The next patch in this same bug prevents this from aborting the request.
719 // Credentials checks for CORS are handled by nsCORSListenerProxy,
720
721 nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
722
723 // Conversion between enumerations is safe due to static asserts in
724 // dom/workers/ServiceWorkerManager.cpp
725 rv = internalChan->SetCorsMode(static_cast<uint32_t>(mRequest->Mode()));
726 MOZ_ASSERT(NS_SUCCEEDED(rv));
727 rv = internalChan->SetRedirectMode(
728 static_cast<uint32_t>(mRequest->GetRedirectMode()));
729 MOZ_ASSERT(NS_SUCCEEDED(rv));
730 mRequest->MaybeSkipCacheIfPerformingRevalidation();
731 rv = internalChan->SetFetchCacheMode(
732 static_cast<uint32_t>(mRequest->GetCacheMode()));
733 MOZ_ASSERT(NS_SUCCEEDED(rv));
734 rv = internalChan->SetIntegrityMetadata(mRequest->GetIntegrity());
735 MOZ_ASSERT(NS_SUCCEEDED(rv));
736
737 // Set the initiator type
738 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan));
739 if (timedChannel) {
740 timedChannel->SetInitiatorType(u"fetch"_ns);
741 }
742 }
743
744 // Step 5. Proxy authentication will be handled by Necko.
745
746 // Continue setting up 'HTTPRequest'. Content-Type and body data.
747 nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan);
748 if (uploadChan) {
749 nsAutoCString contentType;
750 ErrorResult result;
751 mRequest->Headers()->GetFirst("content-type"_ns, contentType, result);
752 // We don't actually expect "result" to have failed here: that only happens
753 // for invalid header names. But if for some reason it did, just propagate
754 // it out.
755 if (result.Failed()) {
756 return result.StealNSResult();
757 }
758
759 // Now contentType is the header that was set in mRequest->Headers(), or a
760 // void string if no header was set.
761 #ifdef DEBUG
762 bool hasContentTypeHeader =
763 mRequest->Headers()->Has("content-type"_ns, result);
764 MOZ_ASSERT(!result.Failed());
765 MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid());
766 #endif // DEBUG
767
768 int64_t bodyLength;
769 nsCOMPtr<nsIInputStream> bodyStream;
770 mRequest->GetBody(getter_AddRefs(bodyStream), &bodyLength);
771 if (bodyStream) {
772 nsAutoCString method;
773 mRequest->GetMethod(method);
774 rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType,
775 bodyLength, method,
776 false /* aStreamHasHeaders */);
777 NS_ENSURE_SUCCESS(rv, rv);
778 }
779 }
780
781 // If preflight is required, start a "CORS preflight fetch"
782 // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
783 // implementation is handled by the http channel calling into
784 // nsCORSListenerProxy. We just inform it which unsafe headers are included
785 // in the request.
786 if (mRequest->Mode() == RequestMode::Cors) {
787 AutoTArray<nsCString, 5> unsafeHeaders;
788 mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
789 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo();
790 loadInfo->SetCorsPreflightInfo(unsafeHeaders, false);
791 }
792
793 if (mIsTrackingFetch && StaticPrefs::network_http_tailing_enabled() && cos) {
794 cos->AddClassFlags(nsIClassOfService::Throttleable |
795 nsIClassOfService::Tail);
796 }
797
798 if (mIsTrackingFetch &&
799 StaticPrefs::privacy_trackingprotection_lower_network_priority()) {
800 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(chan);
801 if (p) {
802 p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
803 }
804 }
805
806 NotifyNetworkMonitorAlternateStack(chan, std::move(mOriginStack));
807
808 // if the preferred alternative data type in InternalRequest is not empty, set
809 // the data type on the created channel and also create a
810 // AlternativeDataStreamListener to be the stream listener of the channel.
811 if (!aPreferredAlternativeDataType.IsEmpty()) {
812 nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(chan);
813 if (cic) {
814 cic->PreferAlternativeDataType(aPreferredAlternativeDataType, ""_ns,
815 true);
816 MOZ_ASSERT(!mAltDataListener);
817 mAltDataListener = new AlternativeDataStreamListener(
818 this, chan, aPreferredAlternativeDataType);
819 rv = chan->AsyncOpen(mAltDataListener);
820 } else {
821 rv = chan->AsyncOpen(this);
822 }
823 } else {
824 // Integrity check cannot be done on alt-data yet.
825 if (mRequest->GetIntegrity().IsEmpty()) {
826 nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(chan);
827 if (cic) {
828 cic->PreferAlternativeDataType(nsLiteralCString(WASM_ALT_DATA_TYPE_V1),
829 nsLiteralCString(WASM_CONTENT_TYPE),
830 false);
831 }
832 }
833
834 rv = chan->AsyncOpen(this);
835 }
836
837 if (NS_FAILED(rv)) {
838 return rv;
839 }
840
841 // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
842
843 mChannel = chan;
844 return NS_OK;
845 }
BeginAndGetFilteredResponse(InternalResponse * aResponse,bool aFoundOpaqueRedirect)846 already_AddRefed<InternalResponse> FetchDriver::BeginAndGetFilteredResponse(
847 InternalResponse* aResponse, bool aFoundOpaqueRedirect) {
848 MOZ_ASSERT(aResponse);
849 AutoTArray<nsCString, 4> reqURLList;
850 mRequest->GetURLListWithoutFragment(reqURLList);
851 MOZ_ASSERT(!reqURLList.IsEmpty());
852 aResponse->SetURLList(reqURLList);
853 RefPtr<InternalResponse> filteredResponse;
854 if (aFoundOpaqueRedirect) {
855 filteredResponse = aResponse->OpaqueRedirectResponse();
856 } else {
857 switch (mRequest->GetResponseTainting()) {
858 case LoadTainting::Basic:
859 filteredResponse = aResponse->BasicResponse();
860 break;
861 case LoadTainting::CORS:
862 filteredResponse = aResponse->CORSResponse();
863 break;
864 case LoadTainting::Opaque: {
865 filteredResponse = aResponse->OpaqueResponse();
866 nsresult rv = filteredResponse->GeneratePaddingInfo();
867 if (NS_WARN_IF(NS_FAILED(rv))) {
868 return nullptr;
869 }
870 break;
871 }
872 default:
873 MOZ_CRASH("Unexpected case");
874 }
875 }
876
877 MOZ_ASSERT(filteredResponse);
878 MOZ_ASSERT(mObserver);
879 if (!ShouldCheckSRI(*mRequest, *filteredResponse)) {
880 // Need to keep mObserver alive.
881 RefPtr<FetchDriverObserver> observer = mObserver;
882 observer->OnResponseAvailable(filteredResponse);
883 #ifdef DEBUG
884 mResponseAvailableCalled = true;
885 #endif
886 }
887
888 return filteredResponse.forget();
889 }
890
FailWithNetworkError(nsresult rv)891 void FetchDriver::FailWithNetworkError(nsresult rv) {
892 AssertIsOnMainThread();
893 RefPtr<InternalResponse> error = InternalResponse::NetworkError(rv);
894 if (mObserver) {
895 // Need to keep mObserver alive.
896 RefPtr<FetchDriverObserver> observer = mObserver;
897 observer->OnResponseAvailable(error);
898 #ifdef DEBUG
899 mResponseAvailableCalled = true;
900 #endif
901 }
902
903 // mObserver could be null after OnResponseAvailable().
904 if (mObserver) {
905 mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
906 mObserver = nullptr;
907 }
908
909 mChannel = nullptr;
910 }
911
912 NS_IMETHODIMP
OnStartRequest(nsIRequest * aRequest)913 FetchDriver::OnStartRequest(nsIRequest* aRequest) {
914 AssertIsOnMainThread();
915
916 // Note, this can be called multiple times if we are doing an opaqueredirect.
917 // In that case we will get a simulated OnStartRequest() and then the real
918 // channel will call in with an errored OnStartRequest().
919
920 if (mFromPreload && mAborted) {
921 aRequest->Cancel(NS_BINDING_ABORTED);
922 return NS_BINDING_ABORTED;
923 }
924
925 if (!mChannel) {
926 MOZ_ASSERT(!mObserver);
927 return NS_BINDING_ABORTED;
928 }
929
930 nsresult rv;
931 aRequest->GetStatus(&rv);
932 if (NS_FAILED(rv)) {
933 FailWithNetworkError(rv);
934 return rv;
935 }
936
937 // We should only get to the following code once.
938 MOZ_ASSERT(!mPipeOutputStream);
939
940 if (!mObserver) {
941 MOZ_ASSERT(false, "We should have mObserver here.");
942 FailWithNetworkError(NS_ERROR_UNEXPECTED);
943 return NS_ERROR_UNEXPECTED;
944 }
945
946 mNeedToObserveOnDataAvailable = mObserver->NeedOnDataAvailable();
947
948 RefPtr<InternalResponse> response;
949 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
950 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
951
952 // On a successful redirect we perform the following substeps of HTTP Fetch,
953 // step 5, "redirect status", step 11.
954
955 bool foundOpaqueRedirect = false;
956
957 nsAutoCString contentType;
958 channel->GetContentType(contentType);
959
960 int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
961 rv = channel->GetContentLength(&contentLength);
962 MOZ_ASSERT_IF(NS_FAILED(rv),
963 contentLength == InternalResponse::UNKNOWN_BODY_SIZE);
964
965 if (httpChannel) {
966 uint32_t responseStatus = 0;
967 rv = httpChannel->GetResponseStatus(&responseStatus);
968 if (NS_FAILED(rv)) {
969 FailWithNetworkError(rv);
970 return rv;
971 }
972
973 if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
974 if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
975 FailWithNetworkError(NS_BINDING_ABORTED);
976 return NS_BINDING_FAILED;
977 }
978 if (mRequest->GetRedirectMode() == RequestRedirect::Manual) {
979 foundOpaqueRedirect = true;
980 }
981 }
982
983 nsAutoCString statusText;
984 rv = httpChannel->GetResponseStatusText(statusText);
985 MOZ_ASSERT(NS_SUCCEEDED(rv));
986
987 response = new InternalResponse(responseStatus, statusText,
988 mRequest->GetCredentialsMode());
989
990 UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo(
991 new mozilla::ipc::PrincipalInfo());
992 nsresult rv = PrincipalToPrincipalInfo(mPrincipal, principalInfo.get());
993 if (NS_WARN_IF(NS_FAILED(rv))) {
994 return rv;
995 }
996
997 response->SetPrincipalInfo(std::move(principalInfo));
998
999 response->Headers()->FillResponseHeaders(httpChannel);
1000
1001 // If Content-Encoding or Transfer-Encoding headers are set, then the actual
1002 // Content-Length (which refer to the decoded data) is obscured behind the
1003 // encodings.
1004 ErrorResult result;
1005 if (response->Headers()->Has("content-encoding"_ns, result) ||
1006 response->Headers()->Has("transfer-encoding"_ns, result)) {
1007 // We cannot trust the content-length when content-encoding or
1008 // transfer-encoding are set. There are many servers which just
1009 // get this wrong.
1010 contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
1011 }
1012 MOZ_ASSERT(!result.Failed());
1013 } else {
1014 response =
1015 new InternalResponse(200, "OK"_ns, mRequest->GetCredentialsMode());
1016
1017 if (!contentType.IsEmpty()) {
1018 nsAutoCString contentCharset;
1019 channel->GetContentCharset(contentCharset);
1020 if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) {
1021 contentType += ";charset="_ns + contentCharset;
1022 }
1023
1024 IgnoredErrorResult result;
1025 response->Headers()->Append("Content-Type"_ns, contentType, result);
1026 MOZ_ASSERT(!result.Failed());
1027 }
1028
1029 if (contentLength > 0) {
1030 nsAutoCString contentLenStr;
1031 contentLenStr.AppendInt(contentLength);
1032
1033 IgnoredErrorResult result;
1034 response->Headers()->Append("Content-Length"_ns, contentLenStr, result);
1035 MOZ_ASSERT(!result.Failed());
1036 }
1037 }
1038
1039 nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(aRequest);
1040 if (cic) {
1041 if (mAltDataListener) {
1042 // Skip the case that mAltDataListener->Status() equals to FALLBACK, that
1043 // means the opened channel for alternative data loading is reused for
1044 // loading the main data.
1045 if (mAltDataListener->Status() !=
1046 AlternativeDataStreamListener::FALLBACK) {
1047 // Verify the cache ID is the same with from alternative data cache.
1048 // If the cache ID is different, droping the alternative data loading,
1049 // otherwise setup the response's alternative body and cacheInfoChannel.
1050 uint64_t cacheEntryId = 0;
1051 if (NS_SUCCEEDED(cic->GetCacheEntryId(&cacheEntryId)) &&
1052 cacheEntryId !=
1053 mAltDataListener->GetAlternativeDataCacheEntryId()) {
1054 mAltDataListener->Cancel();
1055 } else {
1056 // AlternativeDataStreamListener::OnStartRequest had already been
1057 // called, the alternative data input stream and cacheInfo channel
1058 // must be created.
1059 nsCOMPtr<nsICacheInfoChannel> cacheInfo =
1060 mAltDataListener->GetCacheInfoChannel();
1061 nsCOMPtr<nsIInputStream> altInputStream =
1062 mAltDataListener->GetAlternativeInputStream();
1063 MOZ_ASSERT(altInputStream && cacheInfo);
1064 response->SetAlternativeBody(altInputStream);
1065 nsMainThreadPtrHandle<nsICacheInfoChannel> handle(
1066 new nsMainThreadPtrHolder<nsICacheInfoChannel>(
1067 "nsICacheInfoChannel", cacheInfo, false));
1068 response->SetCacheInfoChannel(handle);
1069 }
1070 } else if (!mAltDataListener->GetAlternativeDataType().IsEmpty()) {
1071 // If the status is FALLBACK and the
1072 // mAltDataListener::mAlternativeDataType is not empty, that means the
1073 // data need to be saved into cache, setup the response's
1074 // nsICacheInfoChannel for caching the data after loading.
1075 nsMainThreadPtrHandle<nsICacheInfoChannel> handle(
1076 new nsMainThreadPtrHolder<nsICacheInfoChannel>(
1077 "nsICacheInfoChannel", cic, false));
1078 response->SetCacheInfoChannel(handle);
1079 }
1080 } else if (!cic->PreferredAlternativeDataTypes().IsEmpty()) {
1081 MOZ_ASSERT(cic->PreferredAlternativeDataTypes().Length() == 1);
1082 MOZ_ASSERT(cic->PreferredAlternativeDataTypes()[0].type().EqualsLiteral(
1083 WASM_ALT_DATA_TYPE_V1));
1084 MOZ_ASSERT(
1085 cic->PreferredAlternativeDataTypes()[0].contentType().EqualsLiteral(
1086 WASM_CONTENT_TYPE));
1087
1088 if (contentType.EqualsLiteral(WASM_CONTENT_TYPE)) {
1089 // We want to attach the CacheInfoChannel to the response object such
1090 // that we can track its origin when the Response object is manipulated
1091 // by JavaScript code. This is important for WebAssembly, which uses
1092 // fetch to query its sources in JavaScript and transfer the Response
1093 // object to other function responsible for storing the alternate data
1094 // using the CacheInfoChannel.
1095 nsMainThreadPtrHandle<nsICacheInfoChannel> handle(
1096 new nsMainThreadPtrHolder<nsICacheInfoChannel>(
1097 "nsICacheInfoChannel", cic, false));
1098 response->SetCacheInfoChannel(handle);
1099 }
1100 }
1101 }
1102
1103 // We open a pipe so that we can immediately set the pipe's read end as the
1104 // response's body. Setting the segment size to UINT32_MAX means that the
1105 // pipe has infinite space. The nsIChannel will continue to buffer data in
1106 // xpcom events even if we block on a fixed size pipe. It might be possible
1107 // to suspend the channel and then resume when there is space available, but
1108 // for now use an infinite pipe to avoid blocking.
1109 nsCOMPtr<nsIInputStream> pipeInputStream;
1110 rv = NS_NewPipe(getter_AddRefs(pipeInputStream),
1111 getter_AddRefs(mPipeOutputStream),
1112 0, /* default segment size */
1113 UINT32_MAX /* infinite pipe */,
1114 true /* non-blocking input, otherwise you deadlock */,
1115 false /* blocking output, since the pipe is 'in'finite */);
1116 if (NS_WARN_IF(NS_FAILED(rv))) {
1117 FailWithNetworkError(rv);
1118 // Cancel request.
1119 return rv;
1120 }
1121 response->SetBody(pipeInputStream, contentLength);
1122
1123 // If the request is a file channel, then remember the local path to
1124 // that file so we can later create File blobs rather than plain ones.
1125 nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aRequest);
1126 if (fc) {
1127 nsCOMPtr<nsIFile> file;
1128 rv = fc->GetFile(getter_AddRefs(file));
1129 if (!NS_WARN_IF(NS_FAILED(rv))) {
1130 nsAutoString path;
1131 file->GetPath(path);
1132 response->SetBodyLocalPath(path);
1133 }
1134 } else {
1135 // If the request is a blob URI, then remember that URI so that we
1136 // can later just use that blob instance instead of cloning it.
1137 nsCString blobURISpec;
1138 GetBlobURISpecFromChannel(aRequest, blobURISpec);
1139 if (!blobURISpec.IsVoid()) {
1140 response->SetBodyBlobURISpec(blobURISpec);
1141 }
1142 }
1143
1144 response->InitChannelInfo(channel);
1145
1146 nsCOMPtr<nsIURI> channelURI;
1147 rv = channel->GetURI(getter_AddRefs(channelURI));
1148 if (NS_WARN_IF(NS_FAILED(rv))) {
1149 FailWithNetworkError(rv);
1150 // Cancel request.
1151 return rv;
1152 }
1153
1154 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
1155 // Propagate any tainting from the channel back to our response here. This
1156 // step is not reflected in the spec because the spec is written such that
1157 // FetchEvent.respondWith() just passes the already-tainted Response back to
1158 // the outer fetch(). In gecko, however, we serialize the Response through
1159 // the channel and must regenerate the tainting from the channel in the
1160 // interception case.
1161 mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting());
1162
1163 // Resolves fetch() promise which may trigger code running in a worker. Make
1164 // sure the Response is fully initialized before calling this.
1165 mResponse = BeginAndGetFilteredResponse(response, foundOpaqueRedirect);
1166 if (NS_WARN_IF(!mResponse)) {
1167 // Fail to generate a paddingInfo for opaque response.
1168 MOZ_DIAGNOSTIC_ASSERT(mRequest->GetResponseTainting() ==
1169 LoadTainting::Opaque &&
1170 !foundOpaqueRedirect);
1171 FailWithNetworkError(NS_ERROR_UNEXPECTED);
1172 return NS_ERROR_UNEXPECTED;
1173 }
1174
1175 // From "Main Fetch" step 19: SRI-part1.
1176 if (ShouldCheckSRI(*mRequest, *mResponse) && mSRIMetadata.IsEmpty()) {
1177 nsIConsoleReportCollector* reporter = nullptr;
1178 if (mObserver) {
1179 reporter = mObserver->GetReporter();
1180 }
1181
1182 nsAutoCString sourceUri;
1183 if (mDocument && mDocument->GetDocumentURI()) {
1184 mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
1185 } else if (!mWorkerScript.IsEmpty()) {
1186 sourceUri.Assign(mWorkerScript);
1187 }
1188 SRICheck::IntegrityMetadata(mRequest->GetIntegrity(), sourceUri, reporter,
1189 &mSRIMetadata);
1190 mSRIDataVerifier =
1191 MakeUnique<SRICheckDataVerifier>(mSRIMetadata, sourceUri, reporter);
1192
1193 // Do not retarget off main thread when using SRI API.
1194 return NS_OK;
1195 }
1196
1197 nsCOMPtr<nsIEventTarget> sts =
1198 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
1199 if (NS_WARN_IF(NS_FAILED(rv))) {
1200 FailWithNetworkError(rv);
1201 // Cancel request.
1202 return rv;
1203 }
1204
1205 // Try to retarget off main thread.
1206 if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) {
1207 Unused << NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts)));
1208 }
1209 return NS_OK;
1210 }
1211
1212 namespace {
1213
1214 // Runnable to call the observer OnDataAvailable on the main-thread.
1215 class DataAvailableRunnable final : public Runnable {
1216 RefPtr<FetchDriverObserver> mObserver;
1217
1218 public:
DataAvailableRunnable(FetchDriverObserver * aObserver)1219 explicit DataAvailableRunnable(FetchDriverObserver* aObserver)
1220 : Runnable("dom::DataAvailableRunnable"), mObserver(aObserver) {
1221 MOZ_ASSERT(aObserver);
1222 }
1223
1224 NS_IMETHOD
Run()1225 Run() override {
1226 mObserver->OnDataAvailable();
1227 mObserver = nullptr;
1228 return NS_OK;
1229 }
1230 };
1231
1232 struct SRIVerifierAndOutputHolder {
SRIVerifierAndOutputHoldermozilla::dom::__anoncefc3aba0211::SRIVerifierAndOutputHolder1233 SRIVerifierAndOutputHolder(SRICheckDataVerifier* aVerifier,
1234 nsIOutputStream* aOutputStream)
1235 : mVerifier(aVerifier), mOutputStream(aOutputStream) {}
1236
1237 SRICheckDataVerifier* mVerifier;
1238 nsIOutputStream* mOutputStream;
1239
1240 private:
1241 SRIVerifierAndOutputHolder() = delete;
1242 };
1243
1244 // Just like NS_CopySegmentToStream, but also sends the data into an
1245 // SRICheckDataVerifier.
CopySegmentToStreamAndSRI(nsIInputStream * aInStr,void * aClosure,const char * aBuffer,uint32_t aOffset,uint32_t aCount,uint32_t * aCountWritten)1246 nsresult CopySegmentToStreamAndSRI(nsIInputStream* aInStr, void* aClosure,
1247 const char* aBuffer, uint32_t aOffset,
1248 uint32_t aCount, uint32_t* aCountWritten) {
1249 auto holder = static_cast<SRIVerifierAndOutputHolder*>(aClosure);
1250 MOZ_DIAGNOSTIC_ASSERT(holder && holder->mVerifier && holder->mOutputStream,
1251 "Bogus holder");
1252 nsresult rv = holder->mVerifier->Update(
1253 aCount, reinterpret_cast<const uint8_t*>(aBuffer));
1254 NS_ENSURE_SUCCESS(rv, rv);
1255
1256 // The rest is just like NS_CopySegmentToStream.
1257 *aCountWritten = 0;
1258 while (aCount) {
1259 uint32_t n = 0;
1260 rv = holder->mOutputStream->Write(aBuffer, aCount, &n);
1261 if (NS_FAILED(rv)) {
1262 return rv;
1263 }
1264 aBuffer += n;
1265 aCount -= n;
1266 *aCountWritten += n;
1267 }
1268 return NS_OK;
1269 }
1270
1271 } // anonymous namespace
1272
1273 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aInputStream,uint64_t aOffset,uint32_t aCount)1274 FetchDriver::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
1275 uint64_t aOffset, uint32_t aCount) {
1276 // NB: This can be called on any thread! But we're guaranteed that it is
1277 // called between OnStartRequest and OnStopRequest, so we don't need to worry
1278 // about races.
1279
1280 if (mNeedToObserveOnDataAvailable) {
1281 mNeedToObserveOnDataAvailable = false;
1282 if (mObserver) {
1283 // Need to keep mObserver alive.
1284 RefPtr<FetchDriverObserver> observer = mObserver;
1285 if (NS_IsMainThread()) {
1286 observer->OnDataAvailable();
1287 } else {
1288 RefPtr<Runnable> runnable = new DataAvailableRunnable(observer);
1289 nsresult rv = mMainThreadEventTarget->Dispatch(runnable.forget(),
1290 NS_DISPATCH_NORMAL);
1291 if (NS_WARN_IF(NS_FAILED(rv))) {
1292 return rv;
1293 }
1294 }
1295 }
1296 }
1297
1298 if (!mResponse) {
1299 MOZ_ASSERT(false);
1300 return NS_ERROR_UNEXPECTED;
1301 }
1302
1303 // Needs to be initialized to 0 because in some cases nsStringInputStream may
1304 // not write to aRead.
1305 uint32_t aRead = 0;
1306 MOZ_ASSERT(mPipeOutputStream);
1307
1308 // From "Main Fetch" step 19: SRI-part2.
1309 // Note: Avoid checking the hidden opaque body.
1310 nsresult rv;
1311 if (mResponse->Type() != ResponseType::Opaque &&
1312 ShouldCheckSRI(*mRequest, *mResponse)) {
1313 MOZ_ASSERT(mSRIDataVerifier);
1314
1315 SRIVerifierAndOutputHolder holder(mSRIDataVerifier.get(),
1316 mPipeOutputStream);
1317 rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI, &holder, aCount,
1318 &aRead);
1319 } else {
1320 rv = aInputStream->ReadSegments(NS_CopySegmentToStream, mPipeOutputStream,
1321 aCount, &aRead);
1322 }
1323
1324 // If no data was read, it's possible the output stream is closed but the
1325 // ReadSegments call followed its contract of returning NS_OK despite write
1326 // errors. Unfortunately, nsIOutputStream has an ill-conceived contract when
1327 // taken together with ReadSegments' contract, because the pipe will just
1328 // NS_OK if we try and invoke its Write* functions ourselves with a 0 count.
1329 // So we must just assume the pipe is broken.
1330 if (aRead == 0 && aCount != 0) {
1331 return NS_BASE_STREAM_CLOSED;
1332 }
1333 return rv;
1334 }
1335
1336 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatusCode)1337 FetchDriver::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
1338 AssertIsOnMainThread();
1339
1340 MOZ_DIAGNOSTIC_ASSERT(!mOnStopRequestCalled);
1341 mOnStopRequestCalled = true;
1342
1343 // main data loading is going to finish, breaking the reference cycle.
1344 RefPtr<AlternativeDataStreamListener> altDataListener =
1345 std::move(mAltDataListener);
1346
1347 // We need to check mObserver, which is nulled by FailWithNetworkError(),
1348 // because in the case of "error" redirect mode, aStatusCode may be NS_OK but
1349 // mResponse will definitely be null so we must not take the else branch.
1350 if (NS_FAILED(aStatusCode) || !mObserver) {
1351 nsCOMPtr<nsIAsyncOutputStream> outputStream =
1352 do_QueryInterface(mPipeOutputStream);
1353 if (outputStream) {
1354 outputStream->CloseWithStatus(NS_FAILED(aStatusCode) ? aStatusCode
1355 : NS_BINDING_FAILED);
1356 }
1357 if (altDataListener) {
1358 altDataListener->Cancel();
1359 }
1360
1361 // We proceed as usual here, since we've already created a successful
1362 // response from OnStartRequest.
1363 } else {
1364 MOZ_ASSERT(mResponse);
1365 MOZ_ASSERT(!mResponse->IsError());
1366
1367 // From "Main Fetch" step 19: SRI-part3.
1368 if (ShouldCheckSRI(*mRequest, *mResponse)) {
1369 MOZ_ASSERT(mSRIDataVerifier);
1370
1371 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
1372
1373 nsIConsoleReportCollector* reporter = nullptr;
1374 if (mObserver) {
1375 reporter = mObserver->GetReporter();
1376 }
1377
1378 nsAutoCString sourceUri;
1379 if (mDocument && mDocument->GetDocumentURI()) {
1380 mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
1381 } else if (!mWorkerScript.IsEmpty()) {
1382 sourceUri.Assign(mWorkerScript);
1383 }
1384 nsresult rv =
1385 mSRIDataVerifier->Verify(mSRIMetadata, channel, sourceUri, reporter);
1386 if (NS_FAILED(rv)) {
1387 if (altDataListener) {
1388 altDataListener->Cancel();
1389 }
1390 FailWithNetworkError(rv);
1391 // Cancel request.
1392 return rv;
1393 }
1394 }
1395
1396 if (mPipeOutputStream) {
1397 mPipeOutputStream->Close();
1398 }
1399 }
1400
1401 FinishOnStopRequest(altDataListener);
1402 return NS_OK;
1403 }
1404
FinishOnStopRequest(AlternativeDataStreamListener * aAltDataListener)1405 void FetchDriver::FinishOnStopRequest(
1406 AlternativeDataStreamListener* aAltDataListener) {
1407 AssertIsOnMainThread();
1408 // OnStopRequest is not called from channel, that means the main data loading
1409 // does not finish yet. Reaching here since alternative data loading finishes.
1410 if (!mOnStopRequestCalled) {
1411 return;
1412 }
1413
1414 MOZ_DIAGNOSTIC_ASSERT(!mAltDataListener);
1415 // Wait for alternative data loading finish if we needed it.
1416 if (aAltDataListener &&
1417 aAltDataListener->Status() == AlternativeDataStreamListener::LOADING) {
1418 // For LOADING case, channel holds the reference of altDataListener, no need
1419 // to restore it to mAltDataListener.
1420 return;
1421 }
1422
1423 if (mObserver) {
1424 // From "Main Fetch" step 19.1, 19.2: Process response.
1425 if (ShouldCheckSRI(*mRequest, *mResponse)) {
1426 MOZ_ASSERT(mResponse);
1427 // Need to keep mObserver alive.
1428 RefPtr<FetchDriverObserver> observer = mObserver;
1429 observer->OnResponseAvailable(mResponse);
1430 #ifdef DEBUG
1431 mResponseAvailableCalled = true;
1432 #endif
1433 }
1434 }
1435
1436 if (mObserver) {
1437 mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking);
1438 mObserver = nullptr;
1439 }
1440
1441 mChannel = nullptr;
1442 }
1443
1444 NS_IMETHODIMP
AsyncOnChannelRedirect(nsIChannel * aOldChannel,nsIChannel * aNewChannel,uint32_t aFlags,nsIAsyncVerifyRedirectCallback * aCallback)1445 FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
1446 nsIChannel* aNewChannel, uint32_t aFlags,
1447 nsIAsyncVerifyRedirectCallback* aCallback) {
1448 nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(aOldChannel);
1449 nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(aNewChannel);
1450 if (oldHttpChannel && newHttpChannel) {
1451 nsAutoCString method;
1452 mRequest->GetMethod(method);
1453
1454 // Fetch 4.4.11
1455 bool rewriteToGET = false;
1456 Unused << oldHttpChannel->ShouldStripRequestBodyHeader(method,
1457 &rewriteToGET);
1458
1459 SetRequestHeaders(newHttpChannel, rewriteToGET);
1460 }
1461
1462 // "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list."
1463 // However, ignore internal redirects here. We don't want to flip
1464 // Response.redirected to true if an internal redirect occurs. These
1465 // should be transparent to script.
1466 nsCOMPtr<nsIURI> uri;
1467 MOZ_ALWAYS_SUCCEEDS(aNewChannel->GetURI(getter_AddRefs(uri)));
1468
1469 nsCOMPtr<nsIURI> uriClone;
1470 nsresult rv = NS_GetURIWithoutRef(uri, getter_AddRefs(uriClone));
1471 if (NS_WARN_IF(NS_FAILED(rv))) {
1472 return rv;
1473 }
1474 nsCString spec;
1475 rv = uriClone->GetSpec(spec);
1476 if (NS_WARN_IF(NS_FAILED(rv))) {
1477 return rv;
1478 }
1479 nsCString fragment;
1480 rv = uri->GetRef(fragment);
1481 if (NS_WARN_IF(NS_FAILED(rv))) {
1482 return rv;
1483 }
1484
1485 if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) {
1486 mRequest->AddURL(spec, fragment);
1487 } else {
1488 // Overwrite the URL only when the request is redirected by a service
1489 // worker.
1490 mRequest->SetURLForInternalRedirect(aFlags, spec, fragment);
1491 }
1492
1493 // In redirect, httpChannel already took referrer-policy into account, so
1494 // updates request’s associated referrer policy from channel.
1495 UpdateReferrerInfoFromNewChannel(aNewChannel);
1496
1497 aCallback->OnRedirectVerifyCallback(NS_OK);
1498 return NS_OK;
1499 }
1500
1501 NS_IMETHODIMP
CheckListenerChain()1502 FetchDriver::CheckListenerChain() { return NS_OK; }
1503
1504 NS_IMETHODIMP
GetInterface(const nsIID & aIID,void ** aResult)1505 FetchDriver::GetInterface(const nsIID& aIID, void** aResult) {
1506 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
1507 *aResult = static_cast<nsIChannelEventSink*>(this);
1508 NS_ADDREF_THIS();
1509 return NS_OK;
1510 }
1511 if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
1512 *aResult = static_cast<nsIStreamListener*>(this);
1513 NS_ADDREF_THIS();
1514 return NS_OK;
1515 }
1516 if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
1517 *aResult = static_cast<nsIRequestObserver*>(this);
1518 NS_ADDREF_THIS();
1519 return NS_OK;
1520 }
1521
1522 return QueryInterface(aIID, aResult);
1523 }
1524
SetDocument(Document * aDocument)1525 void FetchDriver::SetDocument(Document* aDocument) {
1526 // Cannot set document after Fetch() has been called.
1527 MOZ_ASSERT(!mFetchCalled);
1528 mDocument = aDocument;
1529 }
1530
SetCSPEventListener(nsICSPEventListener * aCSPEventListener)1531 void FetchDriver::SetCSPEventListener(nsICSPEventListener* aCSPEventListener) {
1532 MOZ_ASSERT(!mFetchCalled);
1533 mCSPEventListener = aCSPEventListener;
1534 }
1535
SetClientInfo(const ClientInfo & aClientInfo)1536 void FetchDriver::SetClientInfo(const ClientInfo& aClientInfo) {
1537 MOZ_ASSERT(!mFetchCalled);
1538 mClientInfo.emplace(aClientInfo);
1539 }
1540
SetController(const Maybe<ServiceWorkerDescriptor> & aController)1541 void FetchDriver::SetController(
1542 const Maybe<ServiceWorkerDescriptor>& aController) {
1543 MOZ_ASSERT(!mFetchCalled);
1544 mController = aController;
1545 }
1546
SetRequestHeaders(nsIHttpChannel * aChannel,bool aStripRequestBodyHeader) const1547 void FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel,
1548 bool aStripRequestBodyHeader) const {
1549 MOZ_ASSERT(aChannel);
1550
1551 // nsIHttpChannel has a set of pre-configured headers (Accept,
1552 // Accept-Languages, ...) and we don't want to merge the Request's headers
1553 // with them. This array is used to know if the current header has been aleady
1554 // set, if yes, we ask necko to merge it with the previous one, otherwise, we
1555 // don't want the merge.
1556 nsTArray<nsCString> headersSet;
1557
1558 AutoTArray<InternalHeaders::Entry, 5> headers;
1559 mRequest->Headers()->GetEntries(headers);
1560 for (uint32_t i = 0; i < headers.Length(); ++i) {
1561 if (aStripRequestBodyHeader &&
1562 (headers[i].mName.LowerCaseEqualsASCII("content-type") ||
1563 headers[i].mName.LowerCaseEqualsASCII("content-encoding") ||
1564 headers[i].mName.LowerCaseEqualsASCII("content-language") ||
1565 headers[i].mName.LowerCaseEqualsASCII("content-location"))) {
1566 continue;
1567 }
1568
1569 bool alreadySet = headersSet.Contains(headers[i].mName);
1570 if (!alreadySet) {
1571 headersSet.AppendElement(headers[i].mName);
1572 }
1573
1574 if (headers[i].mValue.IsEmpty()) {
1575 DebugOnly<nsresult> rv =
1576 aChannel->SetEmptyRequestHeader(headers[i].mName);
1577 MOZ_ASSERT(NS_SUCCEEDED(rv));
1578 } else {
1579 DebugOnly<nsresult> rv = aChannel->SetRequestHeader(
1580 headers[i].mName, headers[i].mValue, alreadySet /* merge */);
1581 MOZ_ASSERT(NS_SUCCEEDED(rv));
1582 }
1583 }
1584
1585 nsAutoCString method;
1586 mRequest->GetMethod(method);
1587 if (!method.EqualsLiteral("GET") && !method.EqualsLiteral("HEAD")) {
1588 nsAutoString origin;
1589 if (NS_SUCCEEDED(nsContentUtils::GetUTFOrigin(mPrincipal, origin))) {
1590 DebugOnly<nsresult> rv = aChannel->SetRequestHeader(
1591 nsDependentCString(net::nsHttp::Origin),
1592 NS_ConvertUTF16toUTF8(origin), false /* merge */);
1593 MOZ_ASSERT(NS_SUCCEEDED(rv));
1594 }
1595 }
1596 }
1597
RunAbortAlgorithm()1598 void FetchDriver::RunAbortAlgorithm() {
1599 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
1600
1601 if (mObserver) {
1602 #ifdef DEBUG
1603 mResponseAvailableCalled = true;
1604 #endif
1605 mObserver->OnResponseEnd(FetchDriverObserver::eAborted);
1606 mObserver = nullptr;
1607 }
1608
1609 if (mChannel) {
1610 mChannel->Cancel(NS_BINDING_ABORTED);
1611 mChannel = nullptr;
1612 }
1613
1614 mAborted = true;
1615 }
1616
1617 } // namespace mozilla::dom
1618