FetchDriver.cpp (69771B)
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/dom/FetchDriver.h" 8 9 #include "Fetch.h" 10 #include "FetchLog.h" 11 #include "FetchUtil.h" 12 #include "InternalRequest.h" 13 #include "InternalResponse.h" 14 #include "js/Value.h" 15 #include "mozilla/DebugOnly.h" 16 #include "mozilla/PreloaderBase.h" 17 #include "mozilla/StaticPrefs_browser.h" 18 #include "mozilla/StaticPrefs_javascript.h" 19 #include "mozilla/StaticPrefs_network.h" 20 #include "mozilla/StaticPrefs_privacy.h" 21 #include "mozilla/TaskQueue.h" 22 #include "mozilla/dom/BlobURLProtocolHandler.h" 23 #include "mozilla/dom/Document.h" 24 #include "mozilla/dom/FetchPriority.h" 25 #include "mozilla/dom/File.h" 26 #include "mozilla/dom/PerformanceStorage.h" 27 #include "mozilla/dom/PerformanceTiming.h" 28 #include "mozilla/dom/ReferrerInfo.h" 29 #include "mozilla/dom/ServiceWorkerInterceptController.h" 30 #include "mozilla/dom/UserActivation.h" 31 #include "mozilla/dom/WorkerCommon.h" 32 #include "mozilla/ipc/PBackgroundSharedTypes.h" 33 #include "mozilla/net/ContentRange.h" 34 #include "mozilla/net/InterceptionInfo.h" 35 #include "mozilla/net/NeckoChannelParams.h" 36 #include "nsContentPolicyUtils.h" 37 #include "nsDataChannel.h" 38 #include "nsDataHandler.h" 39 #include "nsHttpChannel.h" 40 #include "nsIAsyncVerifyRedirectCallback.h" 41 #include "nsIBaseChannel.h" 42 #include "nsICookieJarSettings.h" 43 #include "nsIFile.h" 44 #include "nsIFileChannel.h" 45 #include "nsIHttpChannel.h" 46 #include "nsIHttpChannelInternal.h" 47 #include "nsIInputStream.h" 48 #include "nsIInterceptionInfo.h" 49 #include "nsIInterfaceRequestorUtils.h" 50 #include "nsIOutputStream.h" 51 #include "nsIPipe.h" 52 #include "nsIRedirectHistoryEntry.h" 53 #include "nsISupportsPriority.h" 54 #include "nsIThreadRetargetableRequest.h" 55 #include "nsIUploadChannel2.h" 56 #include "nsNetUtil.h" 57 #include "nsPrintfCString.h" 58 #include "nsProxyRelease.h" 59 #include "nsStreamUtils.h" 60 #include "nsStringStream.h" 61 62 namespace mozilla::dom { 63 64 namespace { 65 66 void GetBlobURISpecFromChannel(nsIRequest* aRequest, nsCString& aBlobURISpec) { 67 MOZ_ASSERT(aRequest); 68 69 aBlobURISpec.SetIsVoid(true); 70 71 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); 72 if (!channel) { 73 return; 74 } 75 76 nsCOMPtr<nsIURI> uri; 77 nsresult rv = NS_GetFinalChannelURI(channel, getter_AddRefs(uri)); 78 if (NS_FAILED(rv)) { 79 return; 80 } 81 82 if (!dom::IsBlobURI(uri)) { 83 return; 84 } 85 86 uri->GetSpec(aBlobURISpec); 87 } 88 89 bool ShouldCheckSRI(const InternalRequest& aRequest, 90 const InternalResponse& aResponse) { 91 return !aRequest.GetIntegrity().IsEmpty() && 92 aResponse.Type() != ResponseType::Error; 93 } 94 95 } // anonymous namespace 96 97 //----------------------------------------------------------------------------- 98 // AlternativeDataStreamListener 99 //----------------------------------------------------------------------------- 100 class AlternativeDataStreamListener final 101 : public nsIThreadRetargetableStreamListener { 102 public: 103 NS_DECL_THREADSAFE_ISUPPORTS 104 NS_DECL_NSIREQUESTOBSERVER 105 NS_DECL_NSISTREAMLISTENER 106 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER 107 108 // The status of AlternativeDataStreamListener 109 // LOADING: is the initial status, loading the alternative data 110 // COMPLETED: Alternative data loading is completed 111 // CANCELED: Alternative data loading is canceled, this would make 112 // AlternativeDataStreamListener ignore all channel callbacks 113 // FALLBACK: fallback the channel callbacks to FetchDriver 114 // Depends on different situaions, the status transition could be followings 115 // 1. LOADING->COMPLETED 116 // This is the normal status transition for alternative data loading 117 // 118 // 2. LOADING->CANCELED 119 // LOADING->COMPLETED->CANCELED 120 // Alternative data loading could be canceled when cacheId from alternative 121 // data channel does not match with from main data channel(The cacheID 122 // checking is in FetchDriver::OnStartRequest). 123 // Notice the alternative data loading could finish before the cacheID 124 // checking, so the statust transition could be 125 // LOADING->COMPLETED->CANCELED 126 // 127 // 3. LOADING->FALLBACK 128 // For the case that alternative data loading could not be initialized, 129 // i.e. alternative data does not exist or no preferred alternative data 130 // type is requested. Once the status becomes FALLBACK, 131 // AlternativeDataStreamListener transits the channel callback request to 132 // FetchDriver, and the status should not go back to LOADING, COMPLETED, or 133 // CANCELED anymore. 134 enum eStatus { LOADING = 0, COMPLETED, CANCELED, FALLBACK }; 135 136 AlternativeDataStreamListener(FetchDriver* aFetchDriver, nsIChannel* aChannel, 137 const nsACString& aAlternativeDataType); 138 eStatus Status(); 139 void Cancel(); 140 uint64_t GetAlternativeDataCacheEntryId(); 141 const nsACString& GetAlternativeDataType() const; 142 already_AddRefed<nsICacheInfoChannel> GetCacheInfoChannel(); 143 already_AddRefed<nsIInputStream> GetAlternativeInputStream(); 144 145 private: 146 ~AlternativeDataStreamListener() = default; 147 148 // This creates a strong reference cycle with FetchDriver and its 149 // mAltDataListener. We need to clear at least one reference of them once the 150 // data loading finishes. 151 RefPtr<FetchDriver> mFetchDriver; 152 nsCString mAlternativeDataType; 153 nsCOMPtr<nsIInputStream> mPipeAlternativeInputStream; 154 nsCOMPtr<nsIOutputStream> mPipeAlternativeOutputStream; 155 uint64_t mAlternativeDataCacheEntryId; 156 nsCOMPtr<nsICacheInfoChannel> mCacheInfoChannel; 157 nsCOMPtr<nsIChannel> mChannel; 158 Atomic<eStatus> mStatus; 159 }; 160 161 NS_IMPL_ISUPPORTS(AlternativeDataStreamListener, nsIStreamListener, 162 nsIThreadRetargetableStreamListener) 163 164 AlternativeDataStreamListener::AlternativeDataStreamListener( 165 FetchDriver* aFetchDriver, nsIChannel* aChannel, 166 const nsACString& aAlternativeDataType) 167 : mFetchDriver(aFetchDriver), 168 mAlternativeDataType(aAlternativeDataType), 169 mAlternativeDataCacheEntryId(0), 170 mChannel(aChannel), 171 mStatus(AlternativeDataStreamListener::LOADING) { 172 MOZ_DIAGNOSTIC_ASSERT(mFetchDriver); 173 MOZ_DIAGNOSTIC_ASSERT(mChannel); 174 } 175 176 AlternativeDataStreamListener::eStatus AlternativeDataStreamListener::Status() { 177 return mStatus; 178 } 179 180 void AlternativeDataStreamListener::Cancel() { 181 mAlternativeDataCacheEntryId = 0; 182 mCacheInfoChannel = nullptr; 183 mPipeAlternativeOutputStream = nullptr; 184 mPipeAlternativeInputStream = nullptr; 185 if (mChannel && mStatus != AlternativeDataStreamListener::FALLBACK) { 186 // if mStatus is fallback, we need to keep channel to forward request back 187 // to FetchDriver 188 mChannel->CancelWithReason(NS_BINDING_ABORTED, 189 "AlternativeDataStreamListener::Cancel"_ns); 190 mChannel = nullptr; 191 } 192 mStatus = AlternativeDataStreamListener::CANCELED; 193 } 194 195 uint64_t AlternativeDataStreamListener::GetAlternativeDataCacheEntryId() { 196 return mAlternativeDataCacheEntryId; 197 } 198 199 const nsACString& AlternativeDataStreamListener::GetAlternativeDataType() 200 const { 201 return mAlternativeDataType; 202 } 203 204 already_AddRefed<nsIInputStream> 205 AlternativeDataStreamListener::GetAlternativeInputStream() { 206 nsCOMPtr<nsIInputStream> inputStream = mPipeAlternativeInputStream; 207 return inputStream.forget(); 208 } 209 210 already_AddRefed<nsICacheInfoChannel> 211 AlternativeDataStreamListener::GetCacheInfoChannel() { 212 nsCOMPtr<nsICacheInfoChannel> channel = mCacheInfoChannel; 213 return channel.forget(); 214 } 215 216 NS_IMETHODIMP 217 AlternativeDataStreamListener::OnStartRequest(nsIRequest* aRequest) { 218 AssertIsOnMainThread(); 219 MOZ_ASSERT(!mAlternativeDataType.IsEmpty()); 220 // Checking the alternative data type is the same between we asked and the 221 // saved in the channel. 222 nsAutoCString alternativeDataType; 223 nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(aRequest); 224 mStatus = AlternativeDataStreamListener::LOADING; 225 if (cic && NS_SUCCEEDED(cic->GetAlternativeDataType(alternativeDataType)) && 226 mAlternativeDataType.Equals(alternativeDataType) && 227 NS_SUCCEEDED(cic->GetCacheEntryId(&mAlternativeDataCacheEntryId))) { 228 MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeInputStream); 229 MOZ_DIAGNOSTIC_ASSERT(!mPipeAlternativeOutputStream); 230 NS_NewPipe(getter_AddRefs(mPipeAlternativeInputStream), 231 getter_AddRefs(mPipeAlternativeOutputStream), 232 0 /* default segment size */, UINT32_MAX /* infinite pipe */, 233 true /* non-blocking input, otherwise you deadlock */, 234 false /* blocking output, since the pipe is 'in'finite */); 235 236 MOZ_DIAGNOSTIC_ASSERT(!mCacheInfoChannel); 237 mCacheInfoChannel = cic; 238 239 // call FetchDriver::HttpFetch to load main body 240 MOZ_ASSERT(mFetchDriver); 241 return mFetchDriver->HttpFetch(); 242 } 243 // Needn't load alternative data, since alternative data does not exist. 244 // Set status to FALLBACK to reuse the opened channel to load main body, 245 // then call FetchDriver::OnStartRequest to continue the work. Unfortunately 246 // can't change the stream listener to mFetchDriver, need to keep 247 // AlternativeDataStreamListener alive to redirect OnDataAvailable and 248 // OnStopRequest to mFetchDriver. 249 MOZ_ASSERT(alternativeDataType.IsEmpty()); 250 mStatus = AlternativeDataStreamListener::FALLBACK; 251 mAlternativeDataCacheEntryId = 0; 252 MOZ_ASSERT(mFetchDriver); 253 return mFetchDriver->OnStartRequest(aRequest); 254 } 255 256 NS_IMETHODIMP 257 AlternativeDataStreamListener::OnDataAvailable(nsIRequest* aRequest, 258 nsIInputStream* aInputStream, 259 uint64_t aOffset, 260 uint32_t aCount) { 261 FETCH_LOG( 262 ("FetchDriver::OnDataAvailable this=%p, request=%p", this, aRequest)); 263 if (mStatus == AlternativeDataStreamListener::LOADING) { 264 MOZ_ASSERT(mPipeAlternativeOutputStream); 265 uint32_t read = 0; 266 return aInputStream->ReadSegments( 267 NS_CopySegmentToStream, mPipeAlternativeOutputStream, aCount, &read); 268 } 269 if (mStatus == AlternativeDataStreamListener::FALLBACK) { 270 MOZ_ASSERT(mFetchDriver); 271 return mFetchDriver->OnDataAvailable(aRequest, aInputStream, aOffset, 272 aCount); 273 } 274 return NS_OK; 275 } 276 277 NS_IMETHODIMP 278 AlternativeDataStreamListener::OnStopRequest(nsIRequest* aRequest, 279 nsresult aStatusCode) { 280 AssertIsOnMainThread(); 281 282 // Alternative data loading is going to finish, breaking the reference cycle 283 // here by taking the ownership to a loacl variable. 284 RefPtr<FetchDriver> fetchDriver = std::move(mFetchDriver); 285 286 if (mStatus == AlternativeDataStreamListener::CANCELED) { 287 // do nothing 288 return NS_OK; 289 } 290 291 if (mStatus == AlternativeDataStreamListener::FALLBACK) { 292 MOZ_ASSERT(fetchDriver); 293 return fetchDriver->OnStopRequest(aRequest, aStatusCode); 294 } 295 296 MOZ_DIAGNOSTIC_ASSERT(mStatus == AlternativeDataStreamListener::LOADING); 297 298 MOZ_ASSERT(!mAlternativeDataType.IsEmpty() && mPipeAlternativeOutputStream && 299 mPipeAlternativeInputStream); 300 301 mPipeAlternativeOutputStream->Close(); 302 mPipeAlternativeOutputStream = nullptr; 303 304 // Cleanup the states for alternative data if needed. 305 if (NS_FAILED(aStatusCode)) { 306 mAlternativeDataCacheEntryId = 0; 307 mCacheInfoChannel = nullptr; 308 mPipeAlternativeInputStream = nullptr; 309 } 310 mStatus = AlternativeDataStreamListener::COMPLETED; 311 // alternative data loading finish, call FetchDriver::FinishOnStopRequest to 312 // continue the final step for the case FetchDriver::OnStopRequest is called 313 // earlier than AlternativeDataStreamListener::OnStopRequest 314 MOZ_ASSERT(fetchDriver); 315 fetchDriver->FinishOnStopRequest(this); 316 return NS_OK; 317 } 318 319 NS_IMETHODIMP 320 AlternativeDataStreamListener::CheckListenerChain() { return NS_OK; } 321 322 NS_IMETHODIMP 323 AlternativeDataStreamListener::OnDataFinished(nsresult aStatus) { 324 return NS_OK; 325 } 326 327 //----------------------------------------------------------------------------- 328 // FetchDriver 329 //----------------------------------------------------------------------------- 330 331 NS_IMPL_ISUPPORTS(FetchDriver, nsIStreamListener, nsIChannelEventSink, 332 nsIInterfaceRequestor, nsIThreadRetargetableStreamListener, 333 nsINetworkInterceptController) 334 335 FetchDriver::FetchDriver(SafeRefPtr<InternalRequest> aRequest, 336 nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup, 337 nsIEventTarget* aMainThreadEventTarget, 338 nsICookieJarSettings* aCookieJarSettings, 339 PerformanceStorage* aPerformanceStorage, 340 net::ClassificationFlags aTrackingFlags) 341 : mPrincipal(aPrincipal), 342 mLoadGroup(aLoadGroup), 343 mRequest(std::move(aRequest)), 344 mODAMutex("FetchDriver::mODAMutex"), 345 mMainThreadEventTarget(aMainThreadEventTarget), 346 mCookieJarSettings(aCookieJarSettings), 347 mPerformanceStorage(aPerformanceStorage), 348 mNeedToObserveOnDataAvailable(false), 349 mTrackingFlags(aTrackingFlags), 350 mIsOn3PCBExceptionList(false), 351 mOnStopRequestCalled(false) 352 #ifdef DEBUG 353 , 354 mResponseAvailableCalled(false), 355 mFetchCalled(false) 356 #endif 357 { 358 AssertIsOnMainThread(); 359 360 MOZ_ASSERT(mRequest); 361 MOZ_ASSERT(aPrincipal); 362 MOZ_ASSERT(aMainThreadEventTarget); 363 364 mIsTrackingFetch = net::UrlClassifierCommon::IsTrackingClassificationFlag( 365 mTrackingFlags.thirdPartyFlags, false); 366 } 367 368 FetchDriver::~FetchDriver() { 369 AssertIsOnMainThread(); 370 371 // We assert this since even on failures, we should call 372 // FailWithNetworkError(). 373 MOZ_ASSERT(mResponseAvailableCalled); 374 } 375 376 already_AddRefed<PreloaderBase> FetchDriver::FindPreload(nsIURI* aURI) { 377 // Decide if we allow reuse of an existing <link rel=preload as=fetch> 378 // response for this request. First examine this fetch requets itself if it 379 // is 'pure' enough to use the response and then try to find a preload. 380 381 if (!mDocument) { 382 // Preloads are mapped on the document, no document, no preload. 383 return nullptr; 384 } 385 CORSMode cors; 386 switch (mRequest->Mode()) { 387 case RequestMode::No_cors: 388 cors = CORSMode::CORS_NONE; 389 break; 390 case RequestMode::Cors: 391 cors = mRequest->GetCredentialsMode() == RequestCredentials::Include 392 ? CORSMode::CORS_USE_CREDENTIALS 393 : CORSMode::CORS_ANONYMOUS; 394 break; 395 default: 396 // Can't be satisfied by a preload because preload cannot define any of 397 // remaining modes. 398 return nullptr; 399 } 400 if (!mRequest->Headers()->HasOnlySimpleHeaders()) { 401 // Preload can't set any headers. 402 return nullptr; 403 } 404 if (!mRequest->GetIntegrity().IsEmpty()) { 405 // There is currently no support for SRI checking in the fetch preloader. 406 return nullptr; 407 } 408 if (mRequest->GetCacheMode() != RequestCache::Default) { 409 // Preload can only go with the default caching mode. 410 return nullptr; 411 } 412 if (mRequest->SkipServiceWorker()) { 413 // Preload can't be forbidden interception. 414 return nullptr; 415 } 416 if (mRequest->GetRedirectMode() != RequestRedirect::Follow) { 417 // Preload always follows redirects. 418 return nullptr; 419 } 420 nsAutoCString method; 421 mRequest->GetMethod(method); 422 if (!method.EqualsLiteral("GET")) { 423 // Preload can only do GET, this also eliminates the case we do upload, so 424 // no need to check if the request has any body to send out. 425 return nullptr; 426 } 427 428 // OK, this request can be satisfied by a preloaded response, try to find one. 429 430 auto preloadKey = PreloadHashKey::CreateAsFetch(aURI, cors); 431 return mDocument->Preloads().LookupPreload(preloadKey); 432 } 433 434 void FetchDriver::UpdateReferrerInfoFromNewChannel(nsIChannel* aChannel) { 435 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); 436 if (!httpChannel) { 437 return; 438 } 439 440 nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo(); 441 if (!referrerInfo) { 442 return; 443 } 444 445 nsAutoCString computedReferrerSpec; 446 mRequest->SetReferrerPolicy(referrerInfo->ReferrerPolicy()); 447 (void)referrerInfo->GetComputedReferrerSpec(computedReferrerSpec); 448 mRequest->SetReferrer(computedReferrerSpec); 449 } 450 451 nsresult FetchDriver::Fetch(AbortSignalImpl* aSignalImpl, 452 FetchDriverObserver* aObserver) { 453 AssertIsOnMainThread(); 454 #ifdef DEBUG 455 MOZ_ASSERT(!mFetchCalled); 456 mFetchCalled = true; 457 #endif 458 459 mObserver = aObserver; 460 461 // FIXME(nsm): Deal with HSTS. 462 463 MOZ_RELEASE_ASSERT(!mRequest->IsSynchronous(), 464 "Synchronous fetch not supported"); 465 466 UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo( 467 new mozilla::ipc::PrincipalInfo()); 468 nsresult rv = PrincipalToPrincipalInfo(mPrincipal, principalInfo.get()); 469 if (NS_WARN_IF(NS_FAILED(rv))) { 470 return rv; 471 } 472 473 mRequest->SetPrincipalInfo(std::move(principalInfo)); 474 475 // If the signal is aborted, it's time to inform the observer and terminate 476 // the operation. 477 if (aSignalImpl) { 478 if (aSignalImpl->Aborted()) { 479 FetchDriverAbortActions(aSignalImpl); 480 return NS_OK; 481 } 482 483 Follow(aSignalImpl); 484 } 485 486 rv = HttpFetch(mRequest->GetPreferredAlternativeDataType()); 487 if (NS_FAILED(rv)) { 488 FailWithNetworkError(rv); 489 } 490 491 // Any failure is handled by FailWithNetworkError notifying the aObserver. 492 return NS_OK; 493 } 494 495 // This function implements the "HTTP Fetch" algorithm from the Fetch spec. 496 // Functionality is often split between here, the CORS listener proxy and the 497 // Necko HTTP implementation. 498 nsresult FetchDriver::HttpFetch( 499 const nsACString& aPreferredAlternativeDataType) { 500 MOZ_ASSERT(NS_IsMainThread()); 501 502 // Step 1. "Let response be null." 503 mResponse = nullptr; 504 mOnStopRequestCalled = false; 505 nsresult rv; 506 507 nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv); 508 NS_ENSURE_SUCCESS(rv, rv); 509 510 nsAutoCString url; 511 mRequest->GetURL(url); 512 nsCOMPtr<nsIURI> uri; 513 rv = NS_NewURI(getter_AddRefs(uri), url); 514 NS_ENSURE_SUCCESS(rv, rv); 515 516 // Unsafe requests aren't allowed with when using no-core mode. 517 if (mRequest->Mode() == RequestMode::No_cors && mRequest->UnsafeRequest() && 518 (!mRequest->HasSimpleMethod() || 519 !mRequest->Headers()->HasOnlySimpleHeaders())) { 520 MOZ_ASSERT(false, "The API should have caught this"); 521 return NS_ERROR_DOM_BAD_URI; 522 } 523 524 // non-GET requests aren't allowed for blob. 525 if (IsBlobURI(uri)) { 526 nsAutoCString method; 527 mRequest->GetMethod(method); 528 if (!method.EqualsLiteral("GET")) { 529 return NS_ERROR_DOM_NETWORK_ERR; 530 } 531 } 532 533 RefPtr<PreloaderBase> fetchPreload = FindPreload(uri); 534 if (fetchPreload) { 535 fetchPreload->RemoveSelf(mDocument); 536 fetchPreload->NotifyUsage(mDocument, PreloaderBase::LoadBackground::Keep); 537 538 rv = fetchPreload->AsyncConsume(this); 539 if (NS_SUCCEEDED(rv)) { 540 mFromPreload = true; 541 542 mChannel = fetchPreload->Channel(); 543 MOZ_ASSERT(mChannel); 544 mChannel->SetNotificationCallbacks(this); 545 546 // Copied from AsyncOnChannelRedirect. 547 for (const auto& redirect : fetchPreload->Redirects()) { 548 if (redirect.Flags() & nsIChannelEventSink::REDIRECT_INTERNAL) { 549 mRequest->SetURLForInternalRedirect(redirect.Flags(), redirect.Spec(), 550 redirect.Fragment()); 551 } else { 552 mRequest->AddURL(redirect.Spec(), redirect.Fragment()); 553 } 554 } 555 556 return NS_OK; 557 } 558 559 // The preload failed to be consumed. Behave like there were no preload. 560 fetchPreload = nullptr; 561 } 562 563 // Step 2 deals with letting ServiceWorkers intercept requests. This is 564 // handled by Necko after the channel is opened. 565 // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be 566 // set based on the Request's flag. 567 568 // Step 3.1 "If the CORS preflight flag is set and one of these conditions is 569 // true..." is handled by the CORS proxy. 570 // 571 // Step 3.2 "Set request's skip service worker flag." This isn't required 572 // since Necko will fall back to the network if the ServiceWorker does not 573 // respond with a valid Response. 574 // 575 // NS_StartCORSPreflight() will automatically kick off the original request 576 // if it succeeds, so we need to have everything setup for the original 577 // request too. 578 579 // Step 3.3 "Let credentials flag be set if one of 580 // - request's credentials mode is "include" 581 // - request's credentials mode is "same-origin" and either the CORS flag 582 // is unset or response tainting is "opaque" 583 // is true, and unset otherwise." 584 585 // Set skip serviceworker flag. 586 // While the spec also gates on the client being a ServiceWorker, we can't 587 // infer that here. Instead we rely on callers to set the flag correctly. 588 const nsLoadFlags bypassFlag = mRequest->SkipServiceWorker() 589 ? nsIChannel::LOAD_BYPASS_SERVICE_WORKER 590 : 0; 591 592 nsSecurityFlags secFlags = 0; 593 if (mRequest->Mode() == RequestMode::Cors) { 594 secFlags |= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT; 595 } else if (mRequest->Mode() == RequestMode::Same_origin || 596 mRequest->Mode() == RequestMode::Navigate) { 597 secFlags |= nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT; 598 } else if (mRequest->Mode() == RequestMode::No_cors) { 599 secFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT; 600 } else { 601 MOZ_ASSERT_UNREACHABLE("Unexpected request mode!"); 602 return NS_ERROR_UNEXPECTED; 603 } 604 605 if (mRequest->GetRedirectMode() != RequestRedirect::Follow) { 606 secFlags |= nsILoadInfo::SEC_DONT_FOLLOW_REDIRECTS; 607 } 608 609 // This handles the use credentials flag in "HTTP 610 // network or cache fetch" in the spec and decides whether to transmit 611 // cookies and other identifying information. 612 if (mRequest->GetCredentialsMode() == RequestCredentials::Include) { 613 secFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; 614 } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) { 615 secFlags |= nsILoadInfo::SEC_COOKIES_OMIT; 616 } else if (mRequest->GetCredentialsMode() == 617 RequestCredentials::Same_origin) { 618 secFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN; 619 } else { 620 MOZ_ASSERT_UNREACHABLE("Unexpected credentials mode!"); 621 return NS_ERROR_UNEXPECTED; 622 } 623 624 // From here on we create a channel and set its properties with the 625 // information from the InternalRequest. This is an implementation detail. 626 MOZ_ASSERT(mLoadGroup); 627 nsCOMPtr<nsIChannel> chan; 628 629 nsLoadFlags loadFlags = nsIRequest::LOAD_BACKGROUND | bypassFlag; 630 if (mDocument) { 631 MOZ_ASSERT(mDocument->NodePrincipal() == mPrincipal); 632 MOZ_ASSERT(mDocument->CookieJarSettings() == mCookieJarSettings); 633 rv = NS_NewChannel(getter_AddRefs(chan), uri, mDocument, secFlags, 634 mRequest->ContentPolicyType(), 635 nullptr, /* aPerformanceStorage */ 636 mLoadGroup, nullptr, /* aCallbacks */ 637 loadFlags, ios); 638 } else if (mClientInfo.isSome()) { 639 rv = NS_NewChannel(getter_AddRefs(chan), uri, mPrincipal, mClientInfo.ref(), 640 mController, secFlags, mRequest->ContentPolicyType(), 641 mCookieJarSettings, mPerformanceStorage, mLoadGroup, 642 nullptr, /* aCallbacks */ 643 loadFlags, ios); 644 } else { 645 nsCOMPtr<nsIPrincipal> principal = mPrincipal; 646 if (principal->IsSystemPrincipal() && 647 mRequest->GetTriggeringPrincipalOverride()) { 648 rv = NS_NewChannelWithTriggeringPrincipal( 649 getter_AddRefs(chan), uri, mPrincipal, 650 mRequest->GetTriggeringPrincipalOverride(), secFlags, 651 mRequest->ContentPolicyType(), mCookieJarSettings, 652 mPerformanceStorage, mLoadGroup, nullptr, /* aCallbacks */ 653 loadFlags, ios); 654 } else { 655 rv = NS_NewChannel(getter_AddRefs(chan), uri, mPrincipal, secFlags, 656 mRequest->ContentPolicyType(), mCookieJarSettings, 657 mPerformanceStorage, mLoadGroup, 658 nullptr, /* aCallbacks */ 659 loadFlags, ios); 660 } 661 } 662 NS_ENSURE_SUCCESS(rv, rv); 663 664 if (mCSPEventListener) { 665 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); 666 rv = loadInfo->SetCspEventListener(mCSPEventListener); 667 NS_ENSURE_SUCCESS(rv, rv); 668 } 669 670 { 671 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); 672 rv = loadInfo->SetLoadingEmbedderPolicy(mRequest->GetEmbedderPolicy()); 673 NS_ENSURE_SUCCESS(rv, rv); 674 } 675 676 if (mAssociatedBrowsingContextID) { 677 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); 678 rv = loadInfo->SetWorkerAssociatedBrowsingContextID( 679 mAssociatedBrowsingContextID); 680 NS_ENSURE_SUCCESS(rv, rv); 681 } 682 683 if (mIsThirdPartyContext.isSome()) { 684 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); 685 rv = loadInfo->SetIsInThirdPartyContext(mIsThirdPartyContext.ref()); 686 NS_ENSURE_SUCCESS(rv, rv); 687 } 688 689 if (mIsOn3PCBExceptionList) { 690 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); 691 rv = loadInfo->SetIsOn3PCBExceptionList(mIsOn3PCBExceptionList); 692 NS_ENSURE_SUCCESS(rv, rv); 693 } 694 695 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); 696 rv = loadInfo->SetTriggeringFirstPartyClassificationFlags( 697 mTrackingFlags.firstPartyFlags); 698 NS_ENSURE_SUCCESS(rv, rv); 699 rv = loadInfo->SetTriggeringThirdPartyClassificationFlags( 700 mTrackingFlags.thirdPartyFlags); 701 NS_ENSURE_SUCCESS(rv, rv); 702 703 // If the fetch is created by FetchEvent.request or NavigationPreload request, 704 // corresponding InterceptedHttpChannel information need to propagate to the 705 // channel of the fetch. 706 if (mRequest->GetInterceptionTriggeringPrincipalInfo()) { 707 auto principalOrErr = mozilla::ipc::PrincipalInfoToPrincipal( 708 *(mRequest->GetInterceptionTriggeringPrincipalInfo().get())); 709 if (!principalOrErr.isErr()) { 710 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 711 712 nsTArray<nsCOMPtr<nsIRedirectHistoryEntry>> redirectChain; 713 if (!mRequest->InterceptionRedirectChain().IsEmpty()) { 714 for (const RedirectHistoryEntryInfo& entryInfo : 715 mRequest->InterceptionRedirectChain()) { 716 nsCOMPtr<nsIRedirectHistoryEntry> entry = 717 mozilla::ipc::RHEntryInfoToRHEntry(entryInfo); 718 redirectChain.AppendElement(entry); 719 } 720 } 721 722 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); 723 MOZ_ASSERT(loadInfo); 724 loadInfo->SetInterceptionInfo(new mozilla::net::InterceptionInfo( 725 principal, mRequest->InterceptionContentPolicyType(), redirectChain, 726 mRequest->InterceptionFromThirdParty())); 727 } 728 } 729 730 if (mDocument && mDocument->GetEmbedderElement() && 731 mDocument->GetEmbedderElement()->IsAnyOfHTMLElements(nsGkAtoms::object, 732 nsGkAtoms::embed)) { 733 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); 734 rv = loadInfo->SetIsFromObjectOrEmbed(true); 735 NS_ENSURE_SUCCESS(rv, rv); 736 } 737 738 // Insert ourselves into the notification callbacks chain so we can set 739 // headers on redirects. 740 #ifdef DEBUG 741 { 742 nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks; 743 chan->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks)); 744 MOZ_ASSERT(!notificationCallbacks); 745 } 746 #endif 747 chan->SetNotificationCallbacks(this); 748 749 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(chan)); 750 // Mark channel as urgent-start if the Fetch is triggered by user input 751 // events. 752 if (cos && UserActivation::IsHandlingUserInput()) { 753 cos->AddClassFlags(nsIClassOfService::UrgentStart); 754 } 755 756 // Step 3.5 begins "HTTP network or cache fetch". 757 // HTTP network or cache fetch 758 // --------------------------- 759 // Step 1 "Let HTTPRequest..." The channel is the HTTPRequest. 760 nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(chan); 761 if (httpChan) { 762 // Copy the method. 763 nsAutoCString method; 764 mRequest->GetMethod(method); 765 rv = httpChan->SetRequestMethod(method); 766 NS_ENSURE_SUCCESS(rv, rv); 767 768 // Set the same headers. 769 SetRequestHeaders(httpChan, false, false); 770 771 // Step 5 of https://fetch.spec.whatwg.org/#main-fetch 772 // If request's referrer policy is the empty string and request's client is 773 // non-null, then set request's referrer policy to request's client's 774 // associated referrer policy. 775 // Basically, "client" is not in our implementation, we use 776 // EnvironmentReferrerPolicy of the worker or document context 777 ReferrerPolicy referrerPolicy = mRequest->GetEnvironmentReferrerPolicy(); 778 if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) { 779 mRequest->SetReferrerPolicy(referrerPolicy); 780 } 781 // Step 6 of https://fetch.spec.whatwg.org/#main-fetch 782 // If request’s referrer policy is the empty string, 783 // then set request’s referrer policy to the user-set default policy. 784 if (mRequest->ReferrerPolicy_() == ReferrerPolicy::_empty) { 785 nsCOMPtr<nsILoadInfo> loadInfo = httpChan->LoadInfo(); 786 bool isPrivate = loadInfo->GetOriginAttributes().IsPrivateBrowsing(); 787 referrerPolicy = 788 ReferrerInfo::GetDefaultReferrerPolicy(httpChan, uri, isPrivate); 789 mRequest->SetReferrerPolicy(referrerPolicy); 790 } 791 792 rv = FetchUtil::SetRequestReferrer(mPrincipal, mDocument, httpChan, 793 *mRequest); 794 NS_ENSURE_SUCCESS(rv, rv); 795 796 // Bug 1120722 - Authorization will be handled later. 797 // Auth may require prompting, we don't support it yet. 798 // The next patch in this same bug prevents this from aborting the request. 799 // Credentials checks for CORS are handled by nsCORSListenerProxy, 800 801 nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan); 802 803 rv = internalChan->SetRequestMode(mRequest->Mode()); 804 MOZ_ASSERT(NS_SUCCEEDED(rv)); 805 // Conversion between enumerations is safe due to static asserts in 806 // dom/workers/ServiceWorkerManager.cpp 807 rv = internalChan->SetRedirectMode( 808 static_cast<uint32_t>(mRequest->GetRedirectMode())); 809 MOZ_ASSERT(NS_SUCCEEDED(rv)); 810 mRequest->MaybeSkipCacheIfPerformingRevalidation(); 811 rv = internalChan->SetFetchCacheMode( 812 static_cast<uint32_t>(mRequest->GetCacheMode())); 813 MOZ_ASSERT(NS_SUCCEEDED(rv)); 814 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); 815 rv = loadInfo->SetIntegrityMetadata(mRequest->GetIntegrity()); 816 MOZ_ASSERT(NS_SUCCEEDED(rv)); 817 818 // Set the initiator type 819 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChan)); 820 if (timedChannel) { 821 timedChannel->SetInitiatorType(u"fetch"_ns); 822 } 823 } 824 825 // Step 5. Proxy authentication will be handled by Necko. 826 827 // Continue setting up 'HTTPRequest'. Content-Type and body data. 828 nsCOMPtr<nsIUploadChannel2> uploadChan = do_QueryInterface(chan); 829 if (uploadChan) { 830 nsAutoCString contentType; 831 ErrorResult result; 832 mRequest->Headers()->GetFirst("content-type"_ns, contentType, result); 833 // We don't actually expect "result" to have failed here: that only happens 834 // for invalid header names. But if for some reason it did, just propagate 835 // it out. 836 if (result.Failed()) { 837 return result.StealNSResult(); 838 } 839 840 // Now contentType is the header that was set in mRequest->Headers(), or a 841 // void string if no header was set. 842 #ifdef DEBUG 843 bool hasContentTypeHeader = 844 mRequest->Headers()->Has("content-type"_ns, result); 845 MOZ_ASSERT(!result.Failed()); 846 MOZ_ASSERT_IF(!hasContentTypeHeader, contentType.IsVoid()); 847 #endif // DEBUG 848 849 int64_t bodyLength; 850 nsCOMPtr<nsIInputStream> bodyStream; 851 mRequest->GetBody(getter_AddRefs(bodyStream), &bodyLength); 852 if (bodyStream) { 853 nsAutoCString method; 854 mRequest->GetMethod(method); 855 rv = uploadChan->ExplicitSetUploadStream(bodyStream, contentType, 856 bodyLength, method, 857 false /* aStreamHasHeaders */); 858 NS_ENSURE_SUCCESS(rv, rv); 859 } 860 } 861 862 // If preflight is required, start a "CORS preflight fetch" 863 // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the 864 // implementation is handled by the http channel calling into 865 // nsCORSListenerProxy. We just inform it which unsafe headers are included 866 // in the request. 867 if (mRequest->Mode() == RequestMode::Cors) { 868 AutoTArray<nsCString, 5> unsafeHeaders; 869 mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders); 870 nsCOMPtr<nsILoadInfo> loadInfo = chan->LoadInfo(); 871 loadInfo->SetCorsPreflightInfo(unsafeHeaders, false); 872 } 873 874 if (mIsTrackingFetch && StaticPrefs::network_http_tailing_enabled() && cos) { 875 cos->AddClassFlags(nsIClassOfService::Throttleable | 876 nsIClassOfService::Tail); 877 } 878 879 const auto fetchPriority = ToFetchPriority(mRequest->GetPriorityMode()); 880 if (cos) { 881 cos->SetFetchPriorityDOM(fetchPriority); 882 } 883 884 if (nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(chan)) { 885 if (mIsTrackingFetch && 886 StaticPrefs::privacy_trackingprotection_lower_network_priority()) { 887 p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST); 888 } else if (StaticPrefs::network_fetchpriority_enabled()) { 889 // According to step 15 of https://fetch.spec.whatwg.org/#concept-fetch 890 // request’s priority, initiator, destination, and render-blocking are 891 // used in an implementation-defined manner to set the internal priority. 892 // See corresponding preferences in StaticPrefList.yaml for more context. 893 const int32_t supportsPriorityDelta = [this, &fetchPriority]() { 894 auto destination = mRequest->GetInterceptionTriggeringPrincipalInfo() 895 ? mRequest->InterceptionDestination() 896 : mRequest->Destination(); 897 switch (destination) { 898 case RequestDestination::Font: 899 return FETCH_PRIORITY_ADJUSTMENT_FOR(link_preload_font, 900 fetchPriority); 901 case RequestDestination::Style: 902 return FETCH_PRIORITY_ADJUSTMENT_FOR(link_preload_style, 903 fetchPriority); 904 case RequestDestination::Script: 905 case RequestDestination::Audioworklet: 906 case RequestDestination::Paintworklet: 907 case RequestDestination::Sharedworker: 908 case RequestDestination::Worker: 909 case RequestDestination::Xslt: 910 case RequestDestination::Json: 911 return FETCH_PRIORITY_ADJUSTMENT_FOR(link_preload_script, 912 fetchPriority); 913 case RequestDestination::Image: 914 return FETCH_PRIORITY_ADJUSTMENT_FOR(images, fetchPriority); 915 case RequestDestination::Audio: 916 case RequestDestination::Track: 917 case RequestDestination::Video: 918 return FETCH_PRIORITY_ADJUSTMENT_FOR(media, fetchPriority); 919 case RequestDestination::Document: 920 case RequestDestination::Embed: 921 case RequestDestination::Frame: 922 case RequestDestination::Iframe: 923 case RequestDestination::Manifest: 924 case RequestDestination::Object: 925 case RequestDestination::Report: 926 case RequestDestination::_empty: 927 return FETCH_PRIORITY_ADJUSTMENT_FOR(global_fetch_api, 928 fetchPriority); 929 }; 930 MOZ_ASSERT_UNREACHABLE("Unknown destination"); 931 return 0; 932 }(); 933 p->SetPriority(mRequest->InternalPriority()); 934 p->AdjustPriority(supportsPriorityDelta); 935 } 936 } 937 938 NotifyNetworkMonitorAlternateStack(chan, std::move(mOriginStack)); 939 if (mObserver && httpChan) { 940 mObserver->OnNotifyNetworkMonitorAlternateStack(httpChan->ChannelId()); 941 } 942 943 // Should set a Content-Range header for blob scheme, and also slice the 944 // blob appropriately, so we process the Range header here for later use. 945 if (IsBlobURI(uri)) { 946 ErrorResult result; 947 nsAutoCString range; 948 mRequest->Headers()->Get("Range"_ns, range, result); 949 MOZ_ASSERT(!result.Failed()); 950 if (!range.IsVoid()) { 951 rv = NS_SetChannelContentRangeForBlobURI(chan, uri, range); 952 if (NS_FAILED(rv)) { 953 return rv; 954 } 955 } 956 } 957 958 // if the preferred alternative data type in InternalRequest is not empty, set 959 // the data type on the created channel and also create a 960 // AlternativeDataStreamListener to be the stream listener of the channel. 961 if (!aPreferredAlternativeDataType.IsEmpty()) { 962 nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(chan); 963 if (cic) { 964 cic->PreferAlternativeDataType( 965 aPreferredAlternativeDataType, ""_ns, 966 nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::ASYNC); 967 MOZ_ASSERT(!mAltDataListener); 968 mAltDataListener = new AlternativeDataStreamListener( 969 this, chan, aPreferredAlternativeDataType); 970 rv = chan->AsyncOpen(mAltDataListener); 971 } else { 972 rv = chan->AsyncOpen(this); 973 } 974 } else { 975 // Integrity check cannot be done on alt-data yet. 976 if (mRequest->GetIntegrity().IsEmpty()) { 977 nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(chan); 978 if (cic && StaticPrefs::javascript_options_wasm_caching() && 979 !mRequest->SkipWasmCaching()) { 980 cic->PreferAlternativeDataType( 981 FetchUtil::GetWasmAltDataType(), 982 nsLiteralCString(WASM_CONTENT_TYPE), 983 nsICacheInfoChannel::PreferredAlternativeDataDeliveryType:: 984 SERIALIZE); 985 } 986 } 987 988 rv = chan->AsyncOpen(this); 989 } 990 991 if (NS_FAILED(rv)) { 992 return rv; 993 } 994 995 // Step 4 onwards of "HTTP Fetch" is handled internally by Necko. 996 997 mChannel = chan; 998 return NS_OK; 999 } 1000 1001 SafeRefPtr<InternalResponse> FetchDriver::BeginAndGetFilteredResponse( 1002 SafeRefPtr<InternalResponse> aResponse, bool aFoundOpaqueRedirect) { 1003 MOZ_ASSERT(aResponse); 1004 AutoTArray<nsCString, 4> reqURLList; 1005 mRequest->GetURLListWithoutFragment(reqURLList); 1006 MOZ_ASSERT(!reqURLList.IsEmpty()); 1007 aResponse->SetURLList(reqURLList); 1008 SafeRefPtr<InternalResponse> filteredResponse; 1009 if (aFoundOpaqueRedirect) { 1010 filteredResponse = aResponse->OpaqueRedirectResponse(); 1011 } else { 1012 switch (mRequest->GetResponseTainting()) { 1013 case LoadTainting::Basic: 1014 filteredResponse = aResponse->BasicResponse(); 1015 break; 1016 case LoadTainting::CORS: 1017 filteredResponse = aResponse->CORSResponse(); 1018 break; 1019 case LoadTainting::Opaque: { 1020 filteredResponse = aResponse->OpaqueResponse(); 1021 nsresult rv = filteredResponse->GeneratePaddingInfo(); 1022 if (NS_WARN_IF(NS_FAILED(rv))) { 1023 return nullptr; 1024 } 1025 break; 1026 } 1027 default: 1028 MOZ_CRASH("Unexpected case"); 1029 } 1030 } 1031 1032 MOZ_ASSERT(filteredResponse); 1033 MOZ_ASSERT(mObserver); 1034 MOZ_ASSERT(filteredResponse); 1035 if (!ShouldCheckSRI(*mRequest, *filteredResponse)) { 1036 // Need to keep mObserver alive. 1037 RefPtr<FetchDriverObserver> observer = mObserver; 1038 observer->OnResponseAvailable(filteredResponse.clonePtr()); 1039 #ifdef DEBUG 1040 mResponseAvailableCalled = true; 1041 #endif 1042 } 1043 1044 return filteredResponse; 1045 } 1046 1047 void FetchDriver::FailWithNetworkError(nsresult rv) { 1048 AssertIsOnMainThread(); 1049 if (mObserver) { 1050 // Need to keep mObserver alive. 1051 RefPtr<FetchDriverObserver> observer = mObserver; 1052 observer->OnResponseAvailable(InternalResponse::NetworkError(rv)); 1053 #ifdef DEBUG 1054 mResponseAvailableCalled = true; 1055 #endif 1056 } 1057 1058 // mObserver could be null after OnResponseAvailable(). 1059 if (mObserver) { 1060 mObserver->OnReportPerformanceTiming(); 1061 mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking, 1062 JS::UndefinedHandleValue); 1063 mObserver = nullptr; 1064 } 1065 1066 mChannel = nullptr; 1067 Unfollow(); 1068 } 1069 1070 NS_IMETHODIMP 1071 FetchDriver::OnStartRequest(nsIRequest* aRequest) { 1072 FETCH_LOG( 1073 ("FetchDriver::OnStartRequest this=%p, request=%p", this, aRequest)); 1074 AssertIsOnMainThread(); 1075 1076 // Note, this can be called multiple times if we are doing an opaqueredirect. 1077 // In that case we will get a simulated OnStartRequest() and then the real 1078 // channel will call in with an errored OnStartRequest(). 1079 1080 if (mFromPreload && mAborted) { 1081 aRequest->CancelWithReason(NS_BINDING_ABORTED, 1082 "FetchDriver::OnStartRequest aborted"_ns); 1083 return NS_BINDING_ABORTED; 1084 } 1085 1086 if (!mChannel) { 1087 MOZ_ASSERT(!mObserver); 1088 return NS_BINDING_ABORTED; 1089 } 1090 1091 nsresult rv; 1092 aRequest->GetStatus(&rv); 1093 if (NS_FAILED(rv)) { 1094 FailWithNetworkError(rv); 1095 return rv; 1096 } 1097 1098 // We should only get to the following code once. 1099 MOZ_ASSERT(!mPipeOutputStream); 1100 1101 if (!mObserver) { 1102 MOZ_ASSERT(false, "We should have mObserver here."); 1103 FailWithNetworkError(NS_ERROR_UNEXPECTED); 1104 return NS_ERROR_UNEXPECTED; 1105 } 1106 1107 mNeedToObserveOnDataAvailable = mObserver->NeedOnDataAvailable(); 1108 1109 SafeRefPtr<InternalResponse> response; 1110 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); 1111 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest); 1112 1113 // On a successful redirect we perform the following substeps of HTTP Fetch, 1114 // step 5, "redirect status", step 11. 1115 1116 bool foundOpaqueRedirect = false; 1117 1118 nsAutoCString contentType(VoidCString()); 1119 1120 int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE; 1121 rv = channel->GetContentLength(&contentLength); 1122 MOZ_ASSERT_IF(NS_FAILED(rv), 1123 contentLength == InternalResponse::UNKNOWN_BODY_SIZE); 1124 1125 if (httpChannel) { 1126 channel->GetContentType(contentType); 1127 1128 uint32_t responseStatus = 0; 1129 rv = httpChannel->GetResponseStatus(&responseStatus); 1130 if (NS_FAILED(rv)) { 1131 FailWithNetworkError(rv); 1132 return rv; 1133 } 1134 1135 if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) { 1136 if (mRequest->GetRedirectMode() == RequestRedirect::Error) { 1137 FailWithNetworkError(NS_BINDING_ABORTED); 1138 return NS_BINDING_FAILED; 1139 } 1140 if (mRequest->GetRedirectMode() == RequestRedirect::Manual) { 1141 foundOpaqueRedirect = true; 1142 } 1143 } 1144 1145 nsAutoCString statusText; 1146 rv = httpChannel->GetResponseStatusText(statusText); 1147 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1148 1149 response = MakeSafeRefPtr<InternalResponse>(responseStatus, statusText, 1150 mRequest->GetCredentialsMode()); 1151 1152 UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo( 1153 new mozilla::ipc::PrincipalInfo()); 1154 nsresult rv = PrincipalToPrincipalInfo(mPrincipal, principalInfo.get()); 1155 if (NS_WARN_IF(NS_FAILED(rv))) { 1156 return rv; 1157 } 1158 1159 response->SetPrincipalInfo(std::move(principalInfo)); 1160 1161 response->Headers()->FillResponseHeaders(httpChannel); 1162 1163 // If Content-Encoding or Transfer-Encoding headers are set, then the actual 1164 // Content-Length (which refer to the decoded data) is obscured behind the 1165 // encodings. 1166 ErrorResult result; 1167 if (response->Headers()->Has("content-encoding"_ns, result) || 1168 response->Headers()->Has("transfer-encoding"_ns, result)) { 1169 // We cannot trust the content-length when content-encoding or 1170 // transfer-encoding are set. There are many servers which just 1171 // get this wrong. 1172 contentLength = InternalResponse::UNKNOWN_BODY_SIZE; 1173 } 1174 MOZ_ASSERT(!result.Failed()); 1175 } else { 1176 // Should set a Content-Range header for blob scheme 1177 // (https://fetch.spec.whatwg.org/#scheme-fetch) 1178 nsAutoCString contentRange(VoidCString()); 1179 nsCOMPtr<nsIBaseChannel> baseChan = do_QueryInterface(mChannel); 1180 if (baseChan) { 1181 RefPtr<mozilla::net::ContentRange> range = baseChan->ContentRange(); 1182 if (range) { 1183 range->AsHeader(contentRange); 1184 } 1185 } 1186 1187 response = MakeSafeRefPtr<InternalResponse>( 1188 contentRange.IsVoid() ? 200 : 206, 1189 contentRange.IsVoid() ? "OK"_ns : "Partial Content"_ns, 1190 mRequest->GetCredentialsMode()); 1191 1192 IgnoredErrorResult result; 1193 if (!contentRange.IsVoid()) { 1194 response->Headers()->Append("Content-Range"_ns, contentRange, result); 1195 MOZ_ASSERT(!result.Failed()); 1196 } 1197 1198 if (baseChan) { 1199 RefPtr<CMimeType> fullMimeType(baseChan->FullMimeType()); 1200 if (fullMimeType) { 1201 fullMimeType->Serialize(contentType); 1202 } 1203 } 1204 if (contentType.IsVoid()) { 1205 channel->GetContentType(contentType); 1206 if (!contentType.IsEmpty()) { 1207 nsAutoCString contentCharset; 1208 channel->GetContentCharset(contentCharset); 1209 if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) { 1210 contentType += ";charset="_ns + contentCharset; 1211 } 1212 } 1213 } 1214 1215 response->Headers()->Append("Content-Type"_ns, contentType, result); 1216 MOZ_ASSERT(!result.Failed()); 1217 1218 if (contentLength >= 0) { 1219 nsAutoCString contentLenStr; 1220 contentLenStr.AppendInt(contentLength); 1221 1222 IgnoredErrorResult result; 1223 response->Headers()->Append("Content-Length"_ns, contentLenStr, result); 1224 MOZ_ASSERT(!result.Failed()); 1225 } 1226 } 1227 1228 nsCOMPtr<nsICacheInfoChannel> cic = do_QueryInterface(aRequest); 1229 if (cic) { 1230 if (mAltDataListener) { 1231 // Skip the case that mAltDataListener->Status() equals to FALLBACK, that 1232 // means the opened channel for alternative data loading is reused for 1233 // loading the main data. 1234 if (mAltDataListener->Status() != 1235 AlternativeDataStreamListener::FALLBACK) { 1236 // Verify the cache ID is the same with from alternative data cache. 1237 // If the cache ID is different, droping the alternative data loading, 1238 // otherwise setup the response's alternative body and cacheInfoChannel. 1239 uint64_t cacheEntryId = 0; 1240 if (NS_SUCCEEDED(cic->GetCacheEntryId(&cacheEntryId)) && 1241 cacheEntryId != 1242 mAltDataListener->GetAlternativeDataCacheEntryId()) { 1243 mAltDataListener->Cancel(); 1244 } else { 1245 // AlternativeDataStreamListener::OnStartRequest had already been 1246 // called, the alternative data input stream and cacheInfo channel 1247 // must be created. 1248 nsCOMPtr<nsICacheInfoChannel> cacheInfo = 1249 mAltDataListener->GetCacheInfoChannel(); 1250 nsCOMPtr<nsIInputStream> altInputStream = 1251 mAltDataListener->GetAlternativeInputStream(); 1252 MOZ_ASSERT(altInputStream && cacheInfo); 1253 response->SetAlternativeBody(altInputStream); 1254 nsMainThreadPtrHandle<nsICacheInfoChannel> handle( 1255 new nsMainThreadPtrHolder<nsICacheInfoChannel>( 1256 "nsICacheInfoChannel", cacheInfo, false)); 1257 response->SetCacheInfoChannel(handle); 1258 } 1259 } else if (!mAltDataListener->GetAlternativeDataType().IsEmpty()) { 1260 // If the status is FALLBACK and the 1261 // mAltDataListener::mAlternativeDataType is not empty, that means the 1262 // data need to be saved into cache, setup the response's 1263 // nsICacheInfoChannel for caching the data after loading. 1264 nsMainThreadPtrHandle<nsICacheInfoChannel> handle( 1265 new nsMainThreadPtrHolder<nsICacheInfoChannel>( 1266 "nsICacheInfoChannel", cic, false)); 1267 response->SetCacheInfoChannel(handle); 1268 } 1269 } else if (!cic->PreferredAlternativeDataTypes().IsEmpty()) { 1270 MOZ_ASSERT(cic->PreferredAlternativeDataTypes().Length() == 1); 1271 MOZ_ASSERT(cic->PreferredAlternativeDataTypes()[0].type().Equals( 1272 FetchUtil::GetWasmAltDataType())); 1273 MOZ_ASSERT( 1274 cic->PreferredAlternativeDataTypes()[0].contentType().EqualsLiteral( 1275 WASM_CONTENT_TYPE)); 1276 1277 if (contentType.EqualsLiteral(WASM_CONTENT_TYPE)) { 1278 // We want to attach the CacheInfoChannel to the response object such 1279 // that we can track its origin when the Response object is manipulated 1280 // by JavaScript code. This is important for WebAssembly, which uses 1281 // fetch to query its sources in JavaScript and transfer the Response 1282 // object to other function responsible for storing the alternate data 1283 // using the CacheInfoChannel. 1284 nsMainThreadPtrHandle<nsICacheInfoChannel> handle( 1285 new nsMainThreadPtrHolder<nsICacheInfoChannel>( 1286 "nsICacheInfoChannel", cic, false)); 1287 response->SetCacheInfoChannel(handle); 1288 } 1289 } 1290 } 1291 1292 // Fetch spec Main Fetch step 21: ignore body for head/connect methods. 1293 nsAutoCString method; 1294 mRequest->GetMethod(method); 1295 if (!(method.EqualsLiteral("HEAD") || method.EqualsLiteral("CONNECT"))) { 1296 // We open a pipe so that we can immediately set the pipe's read end as the 1297 // response's body. Setting the segment size to UINT32_MAX means that the 1298 // pipe has infinite space. The nsIChannel will continue to buffer data in 1299 // xpcom events even if we block on a fixed size pipe. It might be possible 1300 // to suspend the channel and then resume when there is space available, but 1301 // for now use an infinite pipe to avoid blocking. 1302 nsCOMPtr<nsIInputStream> pipeInputStream; 1303 NS_NewPipe(getter_AddRefs(pipeInputStream), 1304 getter_AddRefs(mPipeOutputStream), 0, /* default segment size */ 1305 UINT32_MAX /* infinite pipe */, 1306 true /* non-blocking input, otherwise you deadlock */, 1307 false /* blocking output, since the pipe is 'in'finite */); 1308 response->SetBody(pipeInputStream, contentLength); 1309 } 1310 1311 // If the request is a file channel, then remember the local path to 1312 // that file so we can later create File blobs rather than plain ones. 1313 nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aRequest); 1314 if (fc) { 1315 nsCOMPtr<nsIFile> file; 1316 rv = fc->GetFile(getter_AddRefs(file)); 1317 if (!NS_WARN_IF(NS_FAILED(rv))) { 1318 nsAutoString path; 1319 file->GetPath(path); 1320 response->SetBodyLocalPath(path); 1321 } 1322 } else { 1323 // If the request is a blob URI, then remember that URI so that we 1324 // can later just use that blob instance instead of cloning it. 1325 nsCString blobURISpec; 1326 GetBlobURISpecFromChannel(aRequest, blobURISpec); 1327 if (!blobURISpec.IsVoid()) { 1328 response->SetBodyBlobURISpec(blobURISpec); 1329 } 1330 } 1331 1332 response->InitChannelInfo(channel); 1333 1334 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); 1335 // Propagate any tainting from the channel back to our response here. This 1336 // step is not reflected in the spec because the spec is written such that 1337 // FetchEvent.respondWith() just passes the already-tainted Response back to 1338 // the outer fetch(). In gecko, however, we serialize the Response through 1339 // the channel and must regenerate the tainting from the channel in the 1340 // interception case. 1341 mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting()); 1342 1343 // Resolves fetch() promise which may trigger code running in a worker. Make 1344 // sure the Response is fully initialized before calling this. 1345 mResponse = 1346 BeginAndGetFilteredResponse(std::move(response), foundOpaqueRedirect); 1347 if (NS_WARN_IF(!mResponse)) { 1348 // Fail to generate a paddingInfo for opaque response. 1349 MOZ_DIAGNOSTIC_ASSERT(mRequest->GetResponseTainting() == 1350 LoadTainting::Opaque && 1351 !foundOpaqueRedirect); 1352 FailWithNetworkError(NS_ERROR_UNEXPECTED); 1353 return NS_ERROR_UNEXPECTED; 1354 } 1355 1356 // From "Main Fetch" step 19: SRI-part1. 1357 if (ShouldCheckSRI(*mRequest, *mResponse) && mSRIMetadata.IsEmpty()) { 1358 nsIConsoleReportCollector* reporter = nullptr; 1359 if (mObserver) { 1360 reporter = mObserver->GetReporter(); 1361 } 1362 1363 nsAutoCString sourceUri; 1364 if (mDocument && mDocument->GetDocumentURI()) { 1365 mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri); 1366 } else if (!mWorkerScript.IsEmpty()) { 1367 sourceUri.Assign(mWorkerScript); 1368 } 1369 SRICheck::IntegrityMetadata(mRequest->GetIntegrity(), sourceUri, reporter, 1370 &mSRIMetadata); 1371 mSRIDataVerifier = 1372 MakeUnique<SRICheckDataVerifier>(mSRIMetadata, channel, reporter); 1373 1374 // Do not retarget off main thread when using SRI API. 1375 return NS_OK; 1376 } 1377 1378 // Only retarget if not already retargeted 1379 nsCOMPtr<nsISerialEventTarget> target; 1380 nsCOMPtr<nsIThreadRetargetableRequest> req = do_QueryInterface(aRequest); 1381 if (req) { 1382 rv = req->GetDeliveryTarget(getter_AddRefs(target)); 1383 if (NS_SUCCEEDED(rv) && target && !target->IsOnCurrentThread()) { 1384 FETCH_LOG( 1385 ("FetchDriver::OnStartRequest this=%p, request=%p already retargeted", 1386 this, aRequest)); 1387 return NS_OK; 1388 } 1389 } 1390 1391 nsCOMPtr<nsIEventTarget> sts = 1392 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); 1393 if (NS_WARN_IF(NS_FAILED(rv))) { 1394 FailWithNetworkError(rv); 1395 // Cancel request. 1396 return rv; 1397 } 1398 1399 FETCH_LOG(("FetchDriver retargeting: request %p", aRequest)); 1400 // Try to retarget off main thread. 1401 if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) { 1402 RefPtr<TaskQueue> queue = 1403 TaskQueue::Create(sts.forget(), "FetchDriver STS Delivery Queue"); 1404 (void)NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(queue))); 1405 } 1406 return NS_OK; 1407 } 1408 1409 namespace { 1410 1411 // Runnable to call the observer OnDataAvailable on the main-thread. 1412 class DataAvailableRunnable final : public Runnable { 1413 RefPtr<FetchDriverObserver> mObserver; 1414 1415 public: 1416 explicit DataAvailableRunnable(FetchDriverObserver* aObserver) 1417 : Runnable("dom::DataAvailableRunnable"), mObserver(aObserver) { 1418 MOZ_ASSERT(aObserver); 1419 } 1420 1421 NS_IMETHOD 1422 Run() override { 1423 mObserver->OnDataAvailable(); 1424 mObserver = nullptr; 1425 return NS_OK; 1426 } 1427 }; 1428 1429 struct SRIVerifierAndOutputHolder { 1430 SRIVerifierAndOutputHolder(SRICheckDataVerifier* aVerifier, 1431 nsIOutputStream* aOutputStream) 1432 : mVerifier(aVerifier), mOutputStream(aOutputStream) {} 1433 1434 SRICheckDataVerifier* mVerifier; 1435 nsIOutputStream* mOutputStream; 1436 1437 private: 1438 SRIVerifierAndOutputHolder() = delete; 1439 }; 1440 1441 // Just like NS_CopySegmentToStream, but also sends the data into an 1442 // SRICheckDataVerifier. 1443 nsresult CopySegmentToStreamAndSRI(nsIInputStream* aInStr, void* aClosure, 1444 const char* aBuffer, uint32_t aOffset, 1445 uint32_t aCount, uint32_t* aCountWritten) { 1446 auto holder = static_cast<SRIVerifierAndOutputHolder*>(aClosure); 1447 MOZ_DIAGNOSTIC_ASSERT(holder && holder->mVerifier && holder->mOutputStream, 1448 "Bogus holder"); 1449 nsresult rv = holder->mVerifier->Update( 1450 aCount, reinterpret_cast<const uint8_t*>(aBuffer)); 1451 NS_ENSURE_SUCCESS(rv, rv); 1452 1453 // The rest is just like NS_CopySegmentToStream. 1454 *aCountWritten = 0; 1455 while (aCount) { 1456 uint32_t n = 0; 1457 rv = holder->mOutputStream->Write(aBuffer, aCount, &n); 1458 if (NS_FAILED(rv)) { 1459 return rv; 1460 } 1461 aBuffer += n; 1462 aCount -= n; 1463 *aCountWritten += n; 1464 } 1465 return NS_OK; 1466 } 1467 1468 } // anonymous namespace 1469 1470 NS_IMETHODIMP 1471 FetchDriver::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream, 1472 uint64_t aOffset, uint32_t aCount) { 1473 // NB: This can be called on any thread! But we're guaranteed that it is 1474 // called between OnStartRequest and OnStopRequest, so we don't need to worry 1475 // about races for the members accessed in OnStartRequest, OnStopRequest, 1476 // FailWithNetworkError and member functions accessed before opening the 1477 // channel. However, we have a possibility of a race from 1478 // FetchDriverAbortActions 1479 1480 if (!mPipeOutputStream) { 1481 // We ignore the body for HEAD/CONNECT requests. 1482 // nsIStreamListener mandates reading from the stream before returning. 1483 uint32_t totalRead; 1484 nsresult rv = aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, 1485 &totalRead); 1486 NS_ENSURE_SUCCESS(rv, rv); 1487 return NS_OK; 1488 } 1489 1490 if (mNeedToObserveOnDataAvailable) { 1491 mNeedToObserveOnDataAvailable = false; 1492 RefPtr<FetchDriverObserver> observer; 1493 { 1494 MutexAutoLock lock(mODAMutex); 1495 // Need to keep mObserver alive. 1496 observer = mObserver; 1497 } 1498 if (observer) { 1499 if (NS_IsMainThread()) { 1500 observer->OnDataAvailable(); 1501 } else { 1502 RefPtr<Runnable> runnable = new DataAvailableRunnable(observer); 1503 nsresult rv = mMainThreadEventTarget->Dispatch(runnable.forget(), 1504 NS_DISPATCH_NORMAL); 1505 if (NS_WARN_IF(NS_FAILED(rv))) { 1506 return rv; 1507 } 1508 } 1509 } 1510 } 1511 1512 if (!mResponse) { 1513 MOZ_ASSERT(false); 1514 return NS_ERROR_UNEXPECTED; 1515 } 1516 1517 // Needs to be initialized to 0 because in some cases nsStringInputStream may 1518 // not write to aRead. 1519 uint32_t aRead = 0; 1520 MOZ_ASSERT(mPipeOutputStream); 1521 1522 // From "Main Fetch" step 19: SRI-part2. 1523 // Note: Avoid checking the hidden opaque body. 1524 nsresult rv; 1525 if (mResponse->Type() != ResponseType::Opaque && 1526 ShouldCheckSRI(*mRequest, *mResponse)) { 1527 MOZ_ASSERT(mSRIDataVerifier); 1528 1529 SRIVerifierAndOutputHolder holder(mSRIDataVerifier.get(), 1530 mPipeOutputStream); 1531 rv = aInputStream->ReadSegments(CopySegmentToStreamAndSRI, &holder, aCount, 1532 &aRead); 1533 } else { 1534 rv = aInputStream->ReadSegments(NS_CopySegmentToStream, mPipeOutputStream, 1535 aCount, &aRead); 1536 } 1537 1538 // If no data was read, it's possible the output stream is closed but the 1539 // ReadSegments call followed its contract of returning NS_OK despite write 1540 // errors. Unfortunately, nsIOutputStream has an ill-conceived contract when 1541 // taken together with ReadSegments' contract, because the pipe will just 1542 // NS_OK if we try and invoke its Write* functions ourselves with a 0 count. 1543 // So we must just assume the pipe is broken. 1544 if (aRead == 0 && aCount != 0) { 1545 return NS_BASE_STREAM_CLOSED; 1546 } 1547 return rv; 1548 } 1549 1550 NS_IMETHODIMP 1551 FetchDriver::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { 1552 FETCH_LOG(("FetchDriver::OnStopRequest this=%p, request=%p", this, aRequest)); 1553 AssertIsOnMainThread(); 1554 1555 MOZ_DIAGNOSTIC_ASSERT(!mOnStopRequestCalled); 1556 mOnStopRequestCalled = true; 1557 1558 // main data loading is going to finish, breaking the reference cycle. 1559 RefPtr<AlternativeDataStreamListener> altDataListener = 1560 std::move(mAltDataListener); 1561 1562 // For PFetch and ServiceWorker navigationPreload, resource timing should be 1563 // reported before the body stream closing. 1564 if (mObserver) { 1565 mObserver->OnReportPerformanceTiming(); 1566 } 1567 1568 // We need to check mObserver, which is nulled by FailWithNetworkError(), 1569 // because in the case of "error" redirect mode, aStatusCode may be NS_OK but 1570 // mResponse will definitely be null so we must not take the else branch. 1571 if (NS_FAILED(aStatusCode) || !mObserver) { 1572 nsCOMPtr<nsIAsyncOutputStream> outputStream = 1573 do_QueryInterface(mPipeOutputStream); 1574 if (outputStream) { 1575 outputStream->CloseWithStatus(NS_FAILED(aStatusCode) ? aStatusCode 1576 : NS_BINDING_FAILED); 1577 } 1578 if (altDataListener) { 1579 altDataListener->Cancel(); 1580 } 1581 1582 // We proceed as usual here, since we've already created a successful 1583 // response from OnStartRequest. 1584 } else { 1585 MOZ_ASSERT(mResponse); 1586 MOZ_ASSERT(!mResponse->IsError()); 1587 1588 // From "Main Fetch" step 19: SRI-part3. 1589 if (ShouldCheckSRI(*mRequest, *mResponse)) { 1590 MOZ_ASSERT(mSRIDataVerifier); 1591 1592 nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); 1593 1594 nsIConsoleReportCollector* reporter = nullptr; 1595 if (mObserver) { 1596 reporter = mObserver->GetReporter(); 1597 } 1598 1599 nsresult rv = mSRIDataVerifier->Verify(mSRIMetadata, channel, reporter); 1600 if (NS_FAILED(rv)) { 1601 if (altDataListener) { 1602 altDataListener->Cancel(); 1603 } 1604 FailWithNetworkError(rv); 1605 // Cancel request. 1606 return rv; 1607 } 1608 } 1609 1610 if (mPipeOutputStream) { 1611 mPipeOutputStream->Close(); 1612 } 1613 } 1614 1615 FinishOnStopRequest(altDataListener); 1616 return NS_OK; 1617 } 1618 1619 void FetchDriver::FinishOnStopRequest( 1620 AlternativeDataStreamListener* aAltDataListener) { 1621 AssertIsOnMainThread(); 1622 // OnStopRequest is not called from channel, that means the main data loading 1623 // does not finish yet. Reaching here since alternative data loading finishes. 1624 if (!mOnStopRequestCalled) { 1625 return; 1626 } 1627 1628 MOZ_DIAGNOSTIC_ASSERT(!mAltDataListener); 1629 // Wait for alternative data loading finish if we needed it. 1630 if (aAltDataListener && 1631 aAltDataListener->Status() == AlternativeDataStreamListener::LOADING) { 1632 // For LOADING case, channel holds the reference of altDataListener, no need 1633 // to restore it to mAltDataListener. 1634 return; 1635 } 1636 1637 if (mObserver) { 1638 // From "Main Fetch" step 19.1, 19.2: Process response. 1639 if (ShouldCheckSRI(*mRequest, *mResponse)) { 1640 MOZ_ASSERT(mResponse); 1641 // Need to keep mObserver alive. 1642 RefPtr<FetchDriverObserver> observer = mObserver; 1643 observer->OnResponseAvailable(mResponse.clonePtr()); 1644 #ifdef DEBUG 1645 mResponseAvailableCalled = true; 1646 #endif 1647 } 1648 } 1649 1650 if (mObserver) { 1651 mObserver->OnResponseEnd(FetchDriverObserver::eByNetworking, 1652 JS::UndefinedHandleValue); 1653 mObserver = nullptr; 1654 } 1655 1656 mChannel = nullptr; 1657 Unfollow(); 1658 } 1659 1660 NS_IMETHODIMP 1661 FetchDriver::ShouldPrepareForIntercept(nsIURI* aURI, nsIChannel* aChannel, 1662 bool* aShouldIntercept) { 1663 MOZ_ASSERT(aChannel); 1664 1665 if (mInterceptController) { 1666 MOZ_ASSERT(XRE_IsParentProcess()); 1667 return mInterceptController->ShouldPrepareForIntercept(aURI, aChannel, 1668 aShouldIntercept); 1669 } 1670 1671 nsCOMPtr<nsINetworkInterceptController> controller; 1672 NS_QueryNotificationCallbacks(nullptr, mLoadGroup, 1673 NS_GET_IID(nsINetworkInterceptController), 1674 getter_AddRefs(controller)); 1675 if (controller) { 1676 return controller->ShouldPrepareForIntercept(aURI, aChannel, 1677 aShouldIntercept); 1678 } 1679 1680 *aShouldIntercept = false; 1681 return NS_OK; 1682 } 1683 1684 NS_IMETHODIMP 1685 FetchDriver::ChannelIntercepted(nsIInterceptedChannel* aChannel) { 1686 if (mInterceptController) { 1687 MOZ_ASSERT(XRE_IsParentProcess()); 1688 return mInterceptController->ChannelIntercepted(aChannel); 1689 } 1690 1691 nsCOMPtr<nsINetworkInterceptController> controller; 1692 NS_QueryNotificationCallbacks(nullptr, mLoadGroup, 1693 NS_GET_IID(nsINetworkInterceptController), 1694 getter_AddRefs(controller)); 1695 if (controller) { 1696 return controller->ChannelIntercepted(aChannel); 1697 } 1698 1699 return NS_OK; 1700 } 1701 1702 void FetchDriver::EnableNetworkInterceptControl() { 1703 MOZ_ASSERT(XRE_IsParentProcess()); 1704 MOZ_ASSERT(NS_IsMainThread()); 1705 MOZ_ASSERT(!mInterceptController); 1706 mInterceptController = new ServiceWorkerInterceptController(); 1707 } 1708 1709 NS_IMETHODIMP 1710 FetchDriver::AsyncOnChannelRedirect(nsIChannel* aOldChannel, 1711 nsIChannel* aNewChannel, uint32_t aFlags, 1712 nsIAsyncVerifyRedirectCallback* aCallback) { 1713 nsCOMPtr<nsIHttpChannel> oldHttpChannel = do_QueryInterface(aOldChannel); 1714 nsCOMPtr<nsIHttpChannel> newHttpChannel = do_QueryInterface(aNewChannel); 1715 if (oldHttpChannel && newHttpChannel) { 1716 nsAutoCString method; 1717 mRequest->GetMethod(method); 1718 1719 // Fetch 4.4.11 1720 bool rewriteToGET = false; 1721 (void)oldHttpChannel->ShouldStripRequestBodyHeader(method, &rewriteToGET); 1722 1723 // we need to strip Authentication headers for cross-origin requests 1724 // Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch 1725 bool skipAuthHeader = 1726 NS_ShouldRemoveAuthHeaderOnRedirect(aOldChannel, aNewChannel, aFlags); 1727 1728 SetRequestHeaders(newHttpChannel, rewriteToGET, skipAuthHeader); 1729 } 1730 1731 // "HTTP-redirect fetch": step 14 "Append locationURL to request's URL list." 1732 // However, ignore internal redirects here. We don't want to flip 1733 // Response.redirected to true if an internal redirect occurs. These 1734 // should be transparent to script. 1735 nsCOMPtr<nsIURI> uri; 1736 MOZ_ALWAYS_SUCCEEDS(NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(uri))); 1737 1738 nsCOMPtr<nsIURI> uriClone; 1739 nsresult rv = NS_GetURIWithoutRef(uri, getter_AddRefs(uriClone)); 1740 if (NS_WARN_IF(NS_FAILED(rv))) { 1741 return rv; 1742 } 1743 nsCString spec; 1744 rv = uriClone->GetSpec(spec); 1745 if (NS_WARN_IF(NS_FAILED(rv))) { 1746 return rv; 1747 } 1748 nsCString fragment; 1749 rv = uri->GetRef(fragment); 1750 if (NS_WARN_IF(NS_FAILED(rv))) { 1751 return rv; 1752 } 1753 1754 if (!(aFlags & nsIChannelEventSink::REDIRECT_INTERNAL)) { 1755 mRequest->AddURL(spec, fragment); 1756 } else { 1757 // Overwrite the URL only when the request is redirected by a service 1758 // worker. 1759 mRequest->SetURLForInternalRedirect(aFlags, spec, fragment); 1760 } 1761 1762 // In redirect, httpChannel already took referrer-policy into account, so 1763 // updates request’s associated referrer policy from channel. 1764 UpdateReferrerInfoFromNewChannel(aNewChannel); 1765 1766 aCallback->OnRedirectVerifyCallback(NS_OK); 1767 return NS_OK; 1768 } 1769 1770 NS_IMETHODIMP 1771 FetchDriver::CheckListenerChain() { return NS_OK; } 1772 1773 NS_IMETHODIMP 1774 FetchDriver::OnDataFinished(nsresult) { return NS_OK; } 1775 1776 NS_IMETHODIMP 1777 FetchDriver::GetInterface(const nsIID& aIID, void** aResult) { 1778 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { 1779 *aResult = static_cast<nsIChannelEventSink*>(this); 1780 NS_ADDREF_THIS(); 1781 return NS_OK; 1782 } 1783 if (aIID.Equals(NS_GET_IID(nsIStreamListener))) { 1784 *aResult = static_cast<nsIStreamListener*>(this); 1785 NS_ADDREF_THIS(); 1786 return NS_OK; 1787 } 1788 if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) { 1789 *aResult = static_cast<nsIRequestObserver*>(this); 1790 NS_ADDREF_THIS(); 1791 return NS_OK; 1792 } 1793 1794 return QueryInterface(aIID, aResult); 1795 } 1796 1797 void FetchDriver::SetDocument(Document* aDocument) { 1798 // Cannot set document after Fetch() has been called. 1799 MOZ_ASSERT(!mFetchCalled); 1800 mDocument = aDocument; 1801 } 1802 1803 void FetchDriver::SetCSPEventListener(nsICSPEventListener* aCSPEventListener) { 1804 MOZ_ASSERT(!mFetchCalled); 1805 mCSPEventListener = aCSPEventListener; 1806 } 1807 1808 void FetchDriver::SetClientInfo(const ClientInfo& aClientInfo) { 1809 MOZ_ASSERT(!mFetchCalled); 1810 mClientInfo.emplace(aClientInfo); 1811 } 1812 1813 void FetchDriver::SetController( 1814 const Maybe<ServiceWorkerDescriptor>& aController) { 1815 MOZ_ASSERT(!mFetchCalled); 1816 mController = aController; 1817 } 1818 1819 PerformanceTimingData* FetchDriver::GetPerformanceTimingData( 1820 nsAString& aInitiatorType, nsAString& aEntryName) { 1821 MOZ_ASSERT(XRE_IsParentProcess()); 1822 if (!mChannel) { 1823 return nullptr; 1824 } 1825 1826 nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(mChannel); 1827 if (!timedChannel) { 1828 return nullptr; 1829 } 1830 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel); 1831 if (!httpChannel) { 1832 return nullptr; 1833 } 1834 return dom::PerformanceTimingData::Create(timedChannel, httpChannel, 0, 1835 aInitiatorType, aEntryName); 1836 } 1837 1838 void FetchDriver::SetRequestHeaders(nsIHttpChannel* aChannel, 1839 bool aStripRequestBodyHeader, 1840 bool aStripAuthHeader) const { 1841 MOZ_ASSERT(aChannel); 1842 1843 // nsIHttpChannel has a set of pre-configured headers (Accept, 1844 // Accept-Languages, ...) and we don't want to merge the Request's headers 1845 // with them. This array is used to know if the current header has been aleady 1846 // set, if yes, we ask necko to merge it with the previous one, otherwise, we 1847 // don't want the merge. 1848 nsTArray<nsCString> headersSet; 1849 1850 AutoTArray<InternalHeaders::Entry, 5> headers; 1851 mRequest->Headers()->GetEntries(headers); 1852 for (uint32_t i = 0; i < headers.Length(); ++i) { 1853 if (aStripRequestBodyHeader && 1854 (headers[i].mName.LowerCaseEqualsASCII("content-type") || 1855 headers[i].mName.LowerCaseEqualsASCII("content-encoding") || 1856 headers[i].mName.LowerCaseEqualsASCII("content-language") || 1857 headers[i].mName.LowerCaseEqualsASCII("content-location"))) { 1858 continue; 1859 } 1860 1861 if (aStripAuthHeader && 1862 headers[i].mName.LowerCaseEqualsASCII("authorization")) { 1863 continue; 1864 } 1865 1866 bool alreadySet = headersSet.Contains(headers[i].mName); 1867 if (!alreadySet) { 1868 headersSet.AppendElement(headers[i].mName); 1869 } 1870 1871 if (headers[i].mValue.IsEmpty()) { 1872 DebugOnly<nsresult> rv = 1873 aChannel->SetEmptyRequestHeader(headers[i].mName); 1874 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1875 } else { 1876 DebugOnly<nsresult> rv = aChannel->SetRequestHeader( 1877 headers[i].mName, headers[i].mValue, alreadySet /* merge */); 1878 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1879 } 1880 } 1881 } 1882 1883 void FetchDriver::RunAbortAlgorithm() { FetchDriverAbortActions(Signal()); } 1884 1885 void FetchDriver::FetchDriverAbortActions(AbortSignalImpl* aSignalImpl) { 1886 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread()); 1887 RefPtr<FetchDriverObserver> observer; 1888 { 1889 MutexAutoLock lock(mODAMutex); 1890 observer = std::move(mObserver); 1891 } 1892 1893 if (observer) { 1894 #ifdef DEBUG 1895 mResponseAvailableCalled = true; 1896 #endif 1897 JS::Rooted<JS::Value> reason(RootingCx()); 1898 if (aSignalImpl) { 1899 reason.set(aSignalImpl->RawReason()); 1900 } 1901 observer->OnResponseEnd(FetchDriverObserver::eAborted, reason); 1902 } 1903 1904 if (mChannel) { 1905 mChannel->CancelWithReason(NS_BINDING_ABORTED, 1906 "FetchDriver::RunAbortAlgorithm"_ns); 1907 mChannel = nullptr; 1908 } 1909 1910 mAborted = true; 1911 } 1912 1913 } // namespace mozilla::dom