HttpBaseChannel.cpp (228831B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set sw=2 ts=8 et tw=80 : */ 3 4 /* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 // HttpLog.h should generally be included first 9 #include "mozilla/net/HttpBaseChannel.h" 10 11 #include <algorithm> 12 #include <utility> 13 14 #include "HttpBaseChannel.h" 15 #include "HttpLog.h" 16 #include "LoadInfo.h" 17 #include "ReferrerInfo.h" 18 #include "mozIRemoteLazyInputStream.h" 19 #include "mozIThirdPartyUtil.h" 20 #include "mozilla/LoadInfo.h" 21 #include "mozilla/AntiTrackingUtils.h" 22 #include "mozilla/BasePrincipal.h" 23 #include "mozilla/BinarySearch.h" 24 #include "mozilla/CompactPair.h" 25 #include "mozilla/ConsoleReportCollector.h" 26 #include "mozilla/DebugOnly.h" 27 #include "mozilla/InputStreamLengthHelper.h" 28 #include "mozilla/Mutex.h" 29 #include "mozilla/NullPrincipal.h" 30 #include "mozilla/PermissionManager.h" 31 #include "mozilla/Components.h" 32 #include "mozilla/StaticPrefs_browser.h" 33 #include "mozilla/StaticPrefs_fission.h" 34 #include "mozilla/StaticPrefs_network.h" 35 #include "mozilla/StaticPrefs_security.h" 36 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h" 37 #include "mozilla/Telemetry.h" 38 #include "mozilla/Tokenizer.h" 39 #include "mozilla/browser/NimbusFeatures.h" 40 #include "mozilla/dom/BrowsingContext.h" 41 #include "mozilla/dom/CanonicalBrowsingContext.h" 42 #include "mozilla/dom/Document.h" 43 #include "mozilla/dom/FetchPriority.h" 44 #include "mozilla/dom/LoadURIOptionsBinding.h" 45 #include "mozilla/dom/nsHTTPSOnlyUtils.h" 46 #include "mozilla/dom/nsMixedContentBlocker.h" 47 #include "mozilla/dom/Performance.h" 48 #include "mozilla/dom/PerformanceStorage.h" 49 #include "mozilla/dom/PolicyContainer.h" 50 #include "mozilla/dom/ProcessIsolation.h" 51 #include "mozilla/dom/RequestBinding.h" 52 #include "mozilla/dom/WindowGlobalParent.h" 53 #include "mozilla/net/OpaqueResponseUtils.h" 54 #include "mozilla/net/UrlClassifierCommon.h" 55 #include "mozilla/net/UrlClassifierFeatureFactory.h" 56 #include "mozilla/StaticPrefs_javascript.h" 57 #include "nsBufferedStreams.h" 58 #include "nsCOMPtr.h" 59 #include "nsCRT.h" 60 #include "nsContentSecurityManager.h" 61 #include "nsContentSecurityUtils.h" 62 #include "nsContentUtils.h" 63 #include "nsDebug.h" 64 #include "nsEscape.h" 65 #include "nsGlobalWindowInner.h" 66 #include "nsGlobalWindowOuter.h" 67 #include "nsHttpChannel.h" 68 #include "nsHTTPCompressConv.h" 69 #include "nsHttpHandler.h" 70 #include "nsICacheInfoChannel.h" 71 #include "nsICachingChannel.h" 72 #include "nsIChannelEventSink.h" 73 #include "nsIConsoleService.h" 74 #include "nsIContentPolicy.h" 75 #include "nsICookieService.h" 76 #include "nsIDOMWindowUtils.h" 77 #include "nsIDocShell.h" 78 #include "nsIDNSService.h" 79 #include "nsIEncodedChannel.h" 80 #include "nsIHttpHeaderVisitor.h" 81 #include "nsILoadGroupChild.h" 82 #include "nsIMIMEInputStream.h" 83 #include "nsIMultiplexInputStream.h" 84 #include "nsIMutableArray.h" 85 #include "nsINetworkInterceptController.h" 86 #include "nsIObserverService.h" 87 #include "nsIPrincipal.h" 88 #include "nsIProtocolProxyService.h" 89 #include "nsIScriptError.h" 90 #include "nsIScriptSecurityManager.h" 91 #include "nsISecurityConsoleMessage.h" 92 #include "nsISeekableStream.h" 93 #include "nsIStorageStream.h" 94 #include "nsIStreamConverterService.h" 95 #include "nsITimedChannel.h" 96 #include "nsITransportSecurityInfo.h" 97 #include "nsIURIMutator.h" 98 #include "nsMimeTypes.h" 99 #include "nsNetCID.h" 100 #include "nsNetUtil.h" 101 #include "nsPIDOMWindow.h" 102 #include "nsProxyRelease.h" 103 #include "nsReadableUtils.h" 104 #include "nsRedirectHistoryEntry.h" 105 #include "nsServerTiming.h" 106 #include "nsStreamListenerWrapper.h" 107 #include "nsStreamUtils.h" 108 #include "nsString.h" 109 #include "nsThreadUtils.h" 110 #include "nsURLHelper.h" 111 #include "mozilla/RemoteLazyInputStreamChild.h" 112 #include "mozilla/net/SFVService.h" 113 #include "mozilla/dom/ContentChild.h" 114 #include "nsQueryObject.h" 115 116 using mozilla::dom::ForceMediaDocument; 117 using mozilla::dom::RequestMode; 118 119 #define LOGORB(msg, ...) \ 120 MOZ_LOG(GetORBLog(), LogLevel::Debug, \ 121 ("%s: %p " msg, __func__, this, ##__VA_ARGS__)) 122 123 namespace mozilla { 124 namespace net { 125 126 static bool IsHeaderBlacklistedForRedirectCopy(nsHttpAtom const& aHeader) { 127 // IMPORTANT: keep this list ASCII-code sorted 128 static nsHttpAtomLiteral const* blackList[] = { 129 &nsHttp::Accept, 130 &nsHttp::Accept_Encoding, 131 &nsHttp::Accept_Language, 132 &nsHttp::Alternate_Service_Used, 133 &nsHttp::Authentication, 134 &nsHttp::Authorization, 135 &nsHttp::Connection, 136 &nsHttp::Content_Length, 137 &nsHttp::Cookie, 138 &nsHttp::Host, 139 &nsHttp::If, 140 &nsHttp::If_Match, 141 &nsHttp::If_Modified_Since, 142 &nsHttp::If_None_Match, 143 &nsHttp::If_None_Match_Any, 144 &nsHttp::If_Range, 145 &nsHttp::If_Unmodified_Since, 146 &nsHttp::Proxy_Authenticate, 147 &nsHttp::Proxy_Authorization, 148 &nsHttp::Range, 149 &nsHttp::TE, 150 &nsHttp::Transfer_Encoding, 151 &nsHttp::Upgrade, 152 &nsHttp::User_Agent, 153 &nsHttp::WWW_Authenticate}; 154 155 class HttpAtomComparator { 156 nsHttpAtom const& mTarget; 157 158 public: 159 explicit HttpAtomComparator(nsHttpAtom const& aTarget) : mTarget(aTarget) {} 160 int operator()(nsHttpAtom const* aVal) const { 161 if (mTarget == *aVal) { 162 return 0; 163 } 164 return strcmp(mTarget.get(), aVal->get()); 165 } 166 int operator()(nsHttpAtomLiteral const* aVal) const { 167 if (mTarget == *aVal) { 168 return 0; 169 } 170 return strcmp(mTarget.get(), aVal->get()); 171 } 172 }; 173 174 size_t unused; 175 return BinarySearchIf(blackList, 0, std::size(blackList), 176 HttpAtomComparator(aHeader), &unused); 177 } 178 179 class AddHeadersToChannelVisitor final : public nsIHttpHeaderVisitor { 180 public: 181 NS_DECL_ISUPPORTS 182 183 explicit AddHeadersToChannelVisitor(nsIHttpChannel* aChannel) 184 : mChannel(aChannel) {} 185 186 NS_IMETHOD VisitHeader(const nsACString& aHeader, 187 const nsACString& aValue) override { 188 nsHttpAtom atom = nsHttp::ResolveAtom(aHeader); 189 if (!IsHeaderBlacklistedForRedirectCopy(atom)) { 190 DebugOnly<nsresult> rv = 191 mChannel->SetRequestHeader(aHeader, aValue, false); 192 MOZ_ASSERT(NS_SUCCEEDED(rv)); 193 } 194 return NS_OK; 195 } 196 197 private: 198 ~AddHeadersToChannelVisitor() = default; 199 200 nsCOMPtr<nsIHttpChannel> mChannel; 201 }; 202 203 NS_IMPL_ISUPPORTS(AddHeadersToChannelVisitor, nsIHttpHeaderVisitor) 204 205 static OpaqueResponseFilterFetch ConfiguredFilterFetchResponseBehaviour() { 206 uint32_t pref = StaticPrefs:: 207 browser_opaqueResponseBlocking_filterFetchResponse_DoNotUseDirectly(); 208 if (NS_WARN_IF(pref > 209 static_cast<uint32_t>(OpaqueResponseFilterFetch::All))) { 210 return OpaqueResponseFilterFetch::All; 211 } 212 213 return static_cast<OpaqueResponseFilterFetch>(pref); 214 } 215 216 HttpBaseChannel::HttpBaseChannel() 217 : mReportCollector(new ConsoleReportCollector()), 218 mHttpHandler(gHttpHandler), 219 mClassOfService(0, false), 220 mRequestMode(RequestMode::No_cors), 221 mRedirectionLimit(gHttpHandler->RedirectionLimit()), 222 mCachedOpaqueResponseBlockingPref( 223 StaticPrefs::browser_opaqueResponseBlocking()) { 224 StoreApplyConversion(true); 225 StoreAllowSTS(true); 226 StoreTracingEnabled(true); 227 StoreReportTiming(true); 228 StoreAllowSpdy(true); 229 StoreAllowHttp3(true); 230 StoreAllowAltSvc(true); 231 StoreResponseTimeoutEnabled(true); 232 StoreAllRedirectsSameOrigin(true); 233 StoreAllRedirectsPassTimingAllowCheck(true); 234 StoreUpgradableToSecure(true); 235 StoreIsUserAgentHeaderModified(false); 236 237 this->mSelfAddr.inet = {}; 238 this->mPeerAddr.inet = {}; 239 LOG(("Creating HttpBaseChannel @%p\n", this)); 240 241 // Subfields of unions cannot be targeted in an initializer list. 242 #ifdef MOZ_VALGRIND 243 // Zero the entire unions so that Valgrind doesn't complain when we send them 244 // to another process. 245 memset(&mSelfAddr, 0, sizeof(NetAddr)); 246 memset(&mPeerAddr, 0, sizeof(NetAddr)); 247 #endif 248 mSelfAddr.raw.family = PR_AF_UNSPEC; 249 mPeerAddr.raw.family = PR_AF_UNSPEC; 250 } 251 252 HttpBaseChannel::~HttpBaseChannel() { 253 LOG(("Destroying HttpBaseChannel @%p\n", this)); 254 255 // Make sure we don't leak 256 CleanRedirectCacheChainIfNecessary(); 257 258 ReleaseMainThreadOnlyReferences(); 259 } 260 261 namespace { // anon 262 263 class NonTailRemover : public nsISupports { 264 NS_DECL_THREADSAFE_ISUPPORTS 265 266 explicit NonTailRemover(nsIRequestContext* rc) : mRequestContext(rc) {} 267 268 private: 269 virtual ~NonTailRemover() { 270 MOZ_ASSERT(NS_IsMainThread()); 271 mRequestContext->RemoveNonTailRequest(); 272 } 273 274 nsCOMPtr<nsIRequestContext> mRequestContext; 275 }; 276 277 NS_IMPL_ISUPPORTS0(NonTailRemover) 278 279 } // namespace 280 281 void HttpBaseChannel::ReleaseMainThreadOnlyReferences() { 282 if (NS_IsMainThread()) { 283 // Already on main thread, let dtor to 284 // take care of releasing references 285 RemoveAsNonTailRequest(); 286 return; 287 } 288 289 nsTArray<nsCOMPtr<nsISupports>> arrayToRelease; 290 arrayToRelease.AppendElement(mLoadGroup.forget()); 291 arrayToRelease.AppendElement(mLoadInfo.forget()); 292 arrayToRelease.AppendElement(mCallbacks.forget()); 293 arrayToRelease.AppendElement(mProgressSink.forget()); 294 arrayToRelease.AppendElement(mPrincipal.forget()); 295 arrayToRelease.AppendElement(mListener.forget()); 296 arrayToRelease.AppendElement(mCompressListener.forget()); 297 arrayToRelease.AppendElement(mORB.forget()); 298 299 if (LoadAddedAsNonTailRequest()) { 300 // RemoveNonTailRequest() on our request context must be called on the main 301 // thread 302 MOZ_RELEASE_ASSERT(mRequestContext, 303 "Someone released rc or set flags w/o having it?"); 304 305 nsCOMPtr<nsISupports> nonTailRemover(new NonTailRemover(mRequestContext)); 306 arrayToRelease.AppendElement(nonTailRemover.forget()); 307 } 308 309 NS_DispatchToMainThread(new ProxyReleaseRunnable(std::move(arrayToRelease))); 310 } 311 312 void HttpBaseChannel::AddClassificationFlags(uint32_t aClassificationFlags, 313 bool aIsThirdParty) { 314 LOG( 315 ("HttpBaseChannel::AddClassificationFlags classificationFlags=%d " 316 "thirdparty=%d %p", 317 aClassificationFlags, static_cast<int>(aIsThirdParty), this)); 318 319 if (aIsThirdParty) { 320 mThirdPartyClassificationFlags |= aClassificationFlags; 321 } else { 322 mFirstPartyClassificationFlags |= aClassificationFlags; 323 } 324 } 325 326 static bool isSecureOrTrustworthyURL(nsIURI* aURI) { 327 return aURI->SchemeIs("https") || 328 nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(aURI) || 329 (StaticPrefs::network_http_encoding_trustworthy_is_https() && 330 nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI)); 331 } 332 333 nsresult HttpBaseChannel::Init(nsIURI* aURI, uint32_t aCaps, 334 nsProxyInfo* aProxyInfo, 335 uint32_t aProxyResolveFlags, nsIURI* aProxyURI, 336 uint64_t aChannelId, nsILoadInfo* aLoadInfo) { 337 LOG1(("HttpBaseChannel::Init [this=%p]\n", this)); 338 339 MOZ_ASSERT(aURI, "null uri"); 340 341 mURI = aURI; 342 mOriginalURI = aURI; 343 mDocumentURI = nullptr; 344 mCaps = aCaps; 345 mProxyResolveFlags = aProxyResolveFlags; 346 mProxyURI = aProxyURI; 347 mChannelId = aChannelId; 348 mLoadInfo = aLoadInfo; 349 350 // Construct connection info object 351 nsAutoCString host; 352 int32_t port = -1; 353 354 nsresult rv = mURI->GetAsciiHost(host); 355 if (NS_FAILED(rv)) return rv; 356 357 // Reject the URL if it doesn't specify a host 358 if (host.IsEmpty()) return NS_ERROR_MALFORMED_URI; 359 360 rv = mURI->GetPort(&port); 361 if (NS_FAILED(rv)) return rv; 362 363 LOG1(("host=%s port=%d\n", host.get(), port)); 364 365 rv = mURI->GetAsciiSpec(mSpec); 366 if (NS_FAILED(rv)) return rv; 367 LOG1(("uri=%s\n", mSpec.get())); 368 369 // Assert default request method 370 MOZ_ASSERT(mRequestHead.EqualsMethod(nsHttpRequestHead::kMethod_Get)); 371 372 // Set request headers 373 nsAutoCString hostLine; 374 rv = nsHttpHandler::GenerateHostPort(host, port, hostLine); 375 if (NS_FAILED(rv)) return rv; 376 377 rv = mRequestHead.SetHeader(nsHttp::Host, hostLine); 378 if (NS_FAILED(rv)) return rv; 379 380 // Override the Accept header if a specific MediaDocument kind is forced. 381 ExtContentPolicy contentPolicyType = 382 mLoadInfo->GetExternalContentPolicyType(); 383 // TRRLoadInfo doesn't implement GetForceMediaDocument. 384 ForceMediaDocument forceMediaDocument; 385 if (NS_SUCCEEDED(mLoadInfo->GetForceMediaDocument(&forceMediaDocument))) { 386 switch (forceMediaDocument) { 387 case ForceMediaDocument::Image: 388 contentPolicyType = ExtContentPolicy::TYPE_IMAGE; 389 break; 390 case ForceMediaDocument::Video: 391 contentPolicyType = ExtContentPolicy::TYPE_MEDIA; 392 break; 393 case ForceMediaDocument::None: 394 break; 395 } 396 } 397 398 RefPtr<mozilla::dom::BrowsingContext> browsingContext; 399 mLoadInfo->GetBrowsingContext(getter_AddRefs(browsingContext)); 400 401 const nsCString& languageOverride = 402 browsingContext ? browsingContext->Top()->GetLanguageOverride() 403 : EmptyCString(); 404 405 rv = gHttpHandler->AddStandardRequestHeaders( 406 &mRequestHead, aURI, isSecureOrTrustworthyURL(mURI), contentPolicyType, 407 nsContentUtils::ShouldResistFingerprinting(this, 408 RFPTarget::HttpUserAgent), 409 languageOverride); 410 if (NS_FAILED(rv)) return rv; 411 412 nsAutoCString type; 413 if (aProxyInfo && NS_SUCCEEDED(aProxyInfo->GetType(type)) && 414 !type.EqualsLiteral("unknown")) { 415 mProxyInfo = aProxyInfo; 416 } 417 418 mCurrentThread = GetCurrentSerialEventTarget(); 419 return rv; 420 } 421 422 //----------------------------------------------------------------------------- 423 // HttpBaseChannel::nsISupports 424 //----------------------------------------------------------------------------- 425 426 NS_IMPL_ADDREF(HttpBaseChannel) 427 NS_IMPL_RELEASE(HttpBaseChannel) 428 429 NS_INTERFACE_MAP_BEGIN(HttpBaseChannel) 430 NS_INTERFACE_MAP_ENTRY(nsIRequest) 431 NS_INTERFACE_MAP_ENTRY(nsIChannel) 432 NS_INTERFACE_MAP_ENTRY(nsIIdentChannel) 433 NS_INTERFACE_MAP_ENTRY(nsIEncodedChannel) 434 NS_INTERFACE_MAP_ENTRY(nsIHttpChannel) 435 NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal) 436 NS_INTERFACE_MAP_ENTRY(nsIForcePendingChannel) 437 NS_INTERFACE_MAP_ENTRY(nsIUploadChannel) 438 NS_INTERFACE_MAP_ENTRY(nsIFormPOSTActionChannel) 439 NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2) 440 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) 441 NS_INTERFACE_MAP_ENTRY(nsITraceableChannel) 442 NS_INTERFACE_MAP_ENTRY(nsIPrivateBrowsingChannel) 443 NS_INTERFACE_MAP_ENTRY(nsITimedChannel) 444 NS_INTERFACE_MAP_ENTRY(nsIConsoleReportCollector) 445 NS_INTERFACE_MAP_ENTRY(nsIThrottledInputChannel) 446 NS_INTERFACE_MAP_ENTRY(nsIClassifiedChannel) 447 NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpBaseChannel) 448 NS_INTERFACE_MAP_END_INHERITING(nsHashPropertyBag) 449 450 //----------------------------------------------------------------------------- 451 // HttpBaseChannel::nsIRequest 452 //----------------------------------------------------------------------------- 453 454 NS_IMETHODIMP 455 HttpBaseChannel::GetName(nsACString& aName) { 456 aName = mSpec; 457 return NS_OK; 458 } 459 460 NS_IMETHODIMP 461 HttpBaseChannel::IsPending(bool* aIsPending) { 462 NS_ENSURE_ARG_POINTER(aIsPending); 463 *aIsPending = LoadIsPending() || LoadForcePending(); 464 return NS_OK; 465 } 466 467 NS_IMETHODIMP 468 HttpBaseChannel::GetStatus(nsresult* aStatus) { 469 NS_ENSURE_ARG_POINTER(aStatus); 470 *aStatus = mStatus; 471 return NS_OK; 472 } 473 474 NS_IMETHODIMP 475 HttpBaseChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) { 476 NS_ENSURE_ARG_POINTER(aLoadGroup); 477 *aLoadGroup = do_AddRef(mLoadGroup).take(); 478 return NS_OK; 479 } 480 481 NS_IMETHODIMP 482 HttpBaseChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) { 483 MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread."); 484 485 if (!CanSetLoadGroup(aLoadGroup)) { 486 return NS_ERROR_FAILURE; 487 } 488 489 mLoadGroup = aLoadGroup; 490 mProgressSink = nullptr; 491 UpdatePrivateBrowsing(); 492 return NS_OK; 493 } 494 495 NS_IMETHODIMP 496 HttpBaseChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) { 497 NS_ENSURE_ARG_POINTER(aLoadFlags); 498 *aLoadFlags = mLoadFlags; 499 return NS_OK; 500 } 501 502 NS_IMETHODIMP 503 HttpBaseChannel::SetLoadFlags(nsLoadFlags aLoadFlags) { 504 mLoadFlags = aLoadFlags; 505 return NS_OK; 506 } 507 508 NS_IMETHODIMP 509 HttpBaseChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) { 510 if (!LoadIsOCSP()) { 511 return GetTRRModeImpl(aTRRMode); 512 } 513 514 nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID); 515 nsIDNSService::ResolverMode trrMode = nsIDNSService::MODE_NATIVEONLY; 516 // If this is an OCSP channel, and the global TRR mode is TRR_ONLY (3) 517 // then we set the mode for this channel as TRR_DISABLED_MODE. 518 // We do this to prevent a TRR service channel's OCSP validation from 519 // blocking DNS resolution completely. 520 if (dns && NS_SUCCEEDED(dns->GetCurrentTrrMode(&trrMode)) && 521 trrMode == nsIDNSService::MODE_TRRONLY) { 522 *aTRRMode = nsIRequest::TRR_DISABLED_MODE; 523 return NS_OK; 524 } 525 526 return GetTRRModeImpl(aTRRMode); 527 } 528 529 NS_IMETHODIMP 530 HttpBaseChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) { 531 return SetTRRModeImpl(aTRRMode); 532 } 533 534 NS_IMETHODIMP 535 HttpBaseChannel::SetDocshellUserAgentOverride() { 536 RefPtr<dom::BrowsingContext> bc; 537 MOZ_ALWAYS_SUCCEEDS(mLoadInfo->GetBrowsingContext(getter_AddRefs(bc))); 538 if (!bc) { 539 return NS_OK; 540 } 541 542 nsAutoString customUserAgent; 543 bc->GetCustomUserAgent(customUserAgent); 544 if (customUserAgent.IsEmpty() || customUserAgent.IsVoid()) { 545 return NS_OK; 546 } 547 548 NS_ConvertUTF16toUTF8 utf8CustomUserAgent(customUserAgent); 549 nsresult rv = SetRequestHeaderInternal( 550 "User-Agent"_ns, utf8CustomUserAgent, false, 551 nsHttpHeaderArray::eVarietyRequestEnforceDefault); 552 if (NS_FAILED(rv)) { 553 return rv; 554 } 555 556 return NS_OK; 557 } 558 559 //----------------------------------------------------------------------------- 560 // HttpBaseChannel::nsIChannel 561 //----------------------------------------------------------------------------- 562 563 NS_IMETHODIMP 564 HttpBaseChannel::GetOriginalURI(nsIURI** aOriginalURI) { 565 NS_ENSURE_ARG_POINTER(aOriginalURI); 566 *aOriginalURI = do_AddRef(mOriginalURI).take(); 567 return NS_OK; 568 } 569 570 NS_IMETHODIMP 571 HttpBaseChannel::SetOriginalURI(nsIURI* aOriginalURI) { 572 ENSURE_CALLED_BEFORE_CONNECT(); 573 574 NS_ENSURE_ARG_POINTER(aOriginalURI); 575 mOriginalURI = aOriginalURI; 576 return NS_OK; 577 } 578 579 NS_IMETHODIMP 580 HttpBaseChannel::GetURI(nsIURI** aURI) { 581 NS_ENSURE_ARG_POINTER(aURI); 582 *aURI = do_AddRef(mURI).take(); 583 return NS_OK; 584 } 585 586 NS_IMETHODIMP 587 HttpBaseChannel::GetOwner(nsISupports** aOwner) { 588 NS_ENSURE_ARG_POINTER(aOwner); 589 *aOwner = do_AddRef(mOwner).take(); 590 return NS_OK; 591 } 592 593 NS_IMETHODIMP 594 HttpBaseChannel::SetOwner(nsISupports* aOwner) { 595 mOwner = aOwner; 596 return NS_OK; 597 } 598 599 NS_IMETHODIMP 600 HttpBaseChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) { 601 MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null"); 602 mLoadInfo = aLoadInfo; 603 return NS_OK; 604 } 605 606 NS_IMETHODIMP 607 HttpBaseChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) { 608 *aLoadInfo = do_AddRef(mLoadInfo).take(); 609 return NS_OK; 610 } 611 612 NS_IMETHODIMP 613 HttpBaseChannel::GetIsDocument(bool* aIsDocument) { 614 return NS_GetIsDocumentChannel(this, aIsDocument); 615 } 616 617 NS_IMETHODIMP 618 HttpBaseChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) { 619 *aCallbacks = do_AddRef(mCallbacks).take(); 620 return NS_OK; 621 } 622 623 NS_IMETHODIMP 624 HttpBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) { 625 MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread."); 626 627 if (!CanSetCallbacks(aCallbacks)) { 628 return NS_ERROR_FAILURE; 629 } 630 631 mCallbacks = aCallbacks; 632 mProgressSink = nullptr; 633 634 UpdatePrivateBrowsing(); 635 return NS_OK; 636 } 637 638 NS_IMETHODIMP 639 HttpBaseChannel::GetContentType(nsACString& aContentType) { 640 if (!mResponseHead) { 641 aContentType.Truncate(); 642 return NS_ERROR_NOT_AVAILABLE; 643 } 644 645 mResponseHead->ContentType(aContentType); 646 if (!aContentType.IsEmpty()) { 647 return NS_OK; 648 } 649 650 aContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); 651 return NS_OK; 652 } 653 654 NS_IMETHODIMP 655 HttpBaseChannel::SetContentType(const nsACString& aContentType) { 656 if (mListener || LoadWasOpened() || mDummyChannelForCachedResource) { 657 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 658 659 nsAutoCString contentTypeBuf, charsetBuf; 660 bool hadCharset; 661 net_ParseContentType(aContentType, contentTypeBuf, charsetBuf, &hadCharset); 662 663 mResponseHead->SetContentType(contentTypeBuf); 664 665 // take care not to stomp on an existing charset 666 if (hadCharset) mResponseHead->SetContentCharset(charsetBuf); 667 668 } else { 669 // We are being given a content-type hint. 670 bool dummy; 671 net_ParseContentType(aContentType, mContentTypeHint, mContentCharsetHint, 672 &dummy); 673 } 674 675 return NS_OK; 676 } 677 678 NS_IMETHODIMP 679 HttpBaseChannel::GetContentCharset(nsACString& aContentCharset) { 680 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 681 682 mResponseHead->ContentCharset(aContentCharset); 683 return NS_OK; 684 } 685 686 NS_IMETHODIMP 687 HttpBaseChannel::SetContentCharset(const nsACString& aContentCharset) { 688 if (mListener) { 689 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 690 691 mResponseHead->SetContentCharset(aContentCharset); 692 } else { 693 // Charset hint 694 mContentCharsetHint = aContentCharset; 695 } 696 return NS_OK; 697 } 698 699 NS_IMETHODIMP 700 HttpBaseChannel::GetContentDisposition(uint32_t* aContentDisposition) { 701 if (mLoadInfo->GetForceMediaDocument() != ForceMediaDocument::None) { 702 *aContentDisposition = nsIChannel::DISPOSITION_FORCE_INLINE; 703 return NS_OK; 704 } 705 706 // See bug 1658877. If mContentDispositionHint is already 707 // DISPOSITION_ATTACHMENT, it means this channel is created from a 708 // download attribute. In this case, we should prefer the value from the 709 // download attribute rather than the value in content disposition header. 710 // DISPOSITION_FORCE_INLINE is used to explicitly set inline, used by 711 // the pdf reader when loading a attachment pdf without having to 712 // download it. 713 if (mContentDispositionHint == nsIChannel::DISPOSITION_ATTACHMENT || 714 mContentDispositionHint == nsIChannel::DISPOSITION_FORCE_INLINE) { 715 *aContentDisposition = mContentDispositionHint; 716 return NS_OK; 717 } 718 719 nsresult rv; 720 nsCString header; 721 722 rv = GetContentDispositionHeader(header); 723 if (NS_FAILED(rv)) { 724 if (mContentDispositionHint == UINT32_MAX) return rv; 725 726 *aContentDisposition = mContentDispositionHint; 727 return NS_OK; 728 } 729 730 *aContentDisposition = NS_GetContentDispositionFromHeader(header, this); 731 return NS_OK; 732 } 733 734 NS_IMETHODIMP 735 HttpBaseChannel::SetContentDisposition(uint32_t aContentDisposition) { 736 mContentDispositionHint = aContentDisposition; 737 return NS_OK; 738 } 739 740 NS_IMETHODIMP 741 HttpBaseChannel::GetContentDispositionFilename( 742 nsAString& aContentDispositionFilename) { 743 aContentDispositionFilename.Truncate(); 744 nsresult rv; 745 nsCString header; 746 747 rv = GetContentDispositionHeader(header); 748 if (NS_SUCCEEDED(rv)) { 749 rv = NS_GetFilenameFromDisposition(aContentDispositionFilename, header); 750 } 751 752 // If we failed to get the filename from header, we should use 753 // mContentDispositionFilename, since mContentDispositionFilename is set from 754 // the download attribute. 755 if (NS_FAILED(rv)) { 756 if (!mContentDispositionFilename) { 757 return rv; 758 } 759 760 aContentDispositionFilename = *mContentDispositionFilename; 761 return NS_OK; 762 } 763 764 return rv; 765 } 766 767 NS_IMETHODIMP 768 HttpBaseChannel::SetContentDispositionFilename( 769 const nsAString& aContentDispositionFilename) { 770 mContentDispositionFilename = 771 MakeUnique<nsString>(aContentDispositionFilename); 772 773 // For safety reasons ensure the filename doesn't contain null characters and 774 // replace them with underscores. We may later pass the extension to system 775 // MIME APIs that expect null terminated strings. 776 mContentDispositionFilename->ReplaceChar(char16_t(0), '_'); 777 778 return NS_OK; 779 } 780 781 NS_IMETHODIMP 782 HttpBaseChannel::GetContentDispositionHeader( 783 nsACString& aContentDispositionHeader) { 784 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 785 786 nsresult rv = mResponseHead->GetHeader(nsHttp::Content_Disposition, 787 aContentDispositionHeader); 788 if (NS_FAILED(rv) || aContentDispositionHeader.IsEmpty()) { 789 return NS_ERROR_NOT_AVAILABLE; 790 } 791 792 return NS_OK; 793 } 794 795 NS_IMETHODIMP 796 HttpBaseChannel::GetContentLength(int64_t* aContentLength) { 797 NS_ENSURE_ARG_POINTER(aContentLength); 798 799 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 800 801 if (LoadDeliveringAltData()) { 802 MOZ_ASSERT(!mAvailableCachedAltDataType.IsEmpty()); 803 *aContentLength = mAltDataLength; 804 return NS_OK; 805 } 806 807 *aContentLength = mResponseHead->ContentLength(); 808 return NS_OK; 809 } 810 811 NS_IMETHODIMP 812 HttpBaseChannel::SetContentLength(int64_t value) { 813 if (!mDummyChannelForCachedResource) { 814 MOZ_ASSERT_UNREACHABLE("HttpBaseChannel::SetContentLength"); 815 return NS_ERROR_NOT_IMPLEMENTED; 816 } 817 MOZ_ASSERT(mResponseHead); 818 mResponseHead->SetContentLength(value); 819 return NS_OK; 820 } 821 822 NS_IMETHODIMP 823 HttpBaseChannel::Open(nsIInputStream** aStream) { 824 if (!gHttpHandler->Active()) { 825 LOG(("HttpBaseChannel::Open after HTTP shutdown...")); 826 return NS_ERROR_NOT_AVAILABLE; 827 } 828 829 nsCOMPtr<nsIStreamListener> listener; 830 nsresult rv = 831 nsContentSecurityManager::doContentSecurityCheck(this, listener); 832 NS_ENSURE_SUCCESS(rv, rv); 833 834 NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_IN_PROGRESS); 835 836 if (!gHttpHandler->Active()) { 837 LOG(("HttpBaseChannel::Open after HTTP shutdown...")); 838 return NS_ERROR_NOT_AVAILABLE; 839 } 840 841 return NS_ImplementChannelOpen(this, aStream); 842 } 843 844 //----------------------------------------------------------------------------- 845 // HttpBaseChannel::nsIUploadChannel 846 //----------------------------------------------------------------------------- 847 848 NS_IMETHODIMP 849 HttpBaseChannel::GetUploadStream(nsIInputStream** stream) { 850 NS_ENSURE_ARG_POINTER(stream); 851 *stream = do_AddRef(mUploadStream).take(); 852 return NS_OK; 853 } 854 855 NS_IMETHODIMP 856 HttpBaseChannel::SetUploadStream(nsIInputStream* stream, 857 const nsACString& contentTypeArg, 858 int64_t contentLength) { 859 // NOTE: for backwards compatibility and for compatibility with old style 860 // plugins, |stream| may include headers, specifically Content-Type and 861 // Content-Length headers. in this case, |contentType| and |contentLength| 862 // would be unspecified. this is traditionally the case of a POST request, 863 // and so we select POST as the request method if contentType and 864 // contentLength are unspecified. 865 866 if (stream) { 867 nsAutoCString method; 868 bool hasHeaders = false; 869 870 // This method and ExplicitSetUploadStream mean different things by "empty 871 // content type string". This method means "no header", but 872 // ExplicitSetUploadStream means "header with empty value". So we have to 873 // massage the contentType argument into the form ExplicitSetUploadStream 874 // expects. 875 nsCOMPtr<nsIMIMEInputStream> mimeStream; 876 nsCString contentType(contentTypeArg); 877 if (contentType.IsEmpty()) { 878 contentType.SetIsVoid(true); 879 method = "POST"_ns; 880 881 // MIME streams are a special case, and include headers which need to be 882 // copied to the channel. 883 mimeStream = do_QueryInterface(stream); 884 if (mimeStream) { 885 // Copy non-origin related headers to the channel. 886 nsCOMPtr<nsIHttpHeaderVisitor> visitor = 887 new AddHeadersToChannelVisitor(this); 888 mimeStream->VisitHeaders(visitor); 889 890 return ExplicitSetUploadStream(stream, contentType, contentLength, 891 method, hasHeaders); 892 } 893 894 hasHeaders = true; 895 } else { 896 method = "PUT"_ns; 897 898 MOZ_ASSERT( 899 NS_FAILED(CallQueryInterface(stream, getter_AddRefs(mimeStream))), 900 "nsIMIMEInputStream should not be set with an explicit content type"); 901 } 902 return ExplicitSetUploadStream(stream, contentType, contentLength, method, 903 hasHeaders); 904 } 905 906 // if stream is null, ExplicitSetUploadStream returns error. 907 // So we need special case for GET method. 908 StoreUploadStreamHasHeaders(false); 909 SetRequestMethod("GET"_ns); // revert to GET request 910 mUploadStream = nullptr; 911 return NS_OK; 912 } 913 914 namespace { 915 916 class MIMEHeaderCopyVisitor final : public nsIHttpHeaderVisitor { 917 public: 918 explicit MIMEHeaderCopyVisitor(nsIMIMEInputStream* aDest) : mDest(aDest) {} 919 920 NS_DECL_ISUPPORTS 921 NS_IMETHOD VisitHeader(const nsACString& aName, 922 const nsACString& aValue) override { 923 return mDest->AddHeader(PromiseFlatCString(aName).get(), 924 PromiseFlatCString(aValue).get()); 925 } 926 927 private: 928 ~MIMEHeaderCopyVisitor() = default; 929 930 nsCOMPtr<nsIMIMEInputStream> mDest; 931 }; 932 933 NS_IMPL_ISUPPORTS(MIMEHeaderCopyVisitor, nsIHttpHeaderVisitor) 934 935 static void NormalizeCopyComplete(void* aClosure, nsresult aStatus) { 936 #ifdef DEBUG 937 // Called on the STS thread by NS_AsyncCopy 938 nsCOMPtr<nsIEventTarget> sts = 939 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); 940 bool result = false; 941 sts->IsOnCurrentThread(&result); 942 MOZ_ASSERT(result, "Should only be called on the STS thread."); 943 #endif 944 945 RefPtr<GenericPromise::Private> ready = 946 already_AddRefed(static_cast<GenericPromise::Private*>(aClosure)); 947 if (NS_SUCCEEDED(aStatus)) { 948 ready->Resolve(true, __func__); 949 } else { 950 ready->Reject(aStatus, __func__); 951 } 952 } 953 954 // Normalize the upload stream for a HTTP channel, so that is one of the 955 // expected and compatible types. Components like WebExtensions and DevTools 956 // expect that upload streams in the parent process are cloneable, seekable, and 957 // synchronous to read, which this function helps guarantee somewhat efficiently 958 // and without loss of information. 959 // 960 // If the replacement stream outparameter is not initialized to `nullptr`, the 961 // returned stream should be used instead of `aUploadStream` as the upload 962 // stream for the HTTP channel, and the previous stream should not be touched 963 // again. 964 // 965 // If aReadyPromise is non-nullptr after the function is called, it is a promise 966 // which should be awaited before continuing to `AsyncOpen` the HTTP channel, 967 // as the replacement stream will not be ready until it is resolved. 968 static nsresult NormalizeUploadStream(nsIInputStream* aUploadStream, 969 nsIInputStream** aReplacementStream, 970 GenericPromise** aReadyPromise) { 971 MOZ_ASSERT(XRE_IsParentProcess()); 972 973 *aReplacementStream = nullptr; 974 *aReadyPromise = nullptr; 975 976 // Unwrap RemoteLazyInputStream and normalize the contents as we're in the 977 // parent process. 978 if (nsCOMPtr<mozIRemoteLazyInputStream> lazyStream = 979 do_QueryInterface(aUploadStream)) { 980 nsCOMPtr<nsIInputStream> internal; 981 if (NS_SUCCEEDED( 982 lazyStream->TakeInternalStream(getter_AddRefs(internal)))) { 983 nsCOMPtr<nsIInputStream> replacement; 984 nsresult rv = NormalizeUploadStream(internal, getter_AddRefs(replacement), 985 aReadyPromise); 986 NS_ENSURE_SUCCESS(rv, rv); 987 988 if (replacement) { 989 replacement.forget(aReplacementStream); 990 } else { 991 internal.forget(aReplacementStream); 992 } 993 return NS_OK; 994 } 995 } 996 997 // Preserve MIME information on the stream when normalizing. 998 if (nsCOMPtr<nsIMIMEInputStream> mime = do_QueryInterface(aUploadStream)) { 999 nsCOMPtr<nsIInputStream> data; 1000 nsresult rv = mime->GetData(getter_AddRefs(data)); 1001 NS_ENSURE_SUCCESS(rv, rv); 1002 1003 nsCOMPtr<nsIInputStream> replacement; 1004 rv = 1005 NormalizeUploadStream(data, getter_AddRefs(replacement), aReadyPromise); 1006 NS_ENSURE_SUCCESS(rv, rv); 1007 1008 if (replacement) { 1009 nsCOMPtr<nsIMIMEInputStream> replacementMime( 1010 do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv)); 1011 NS_ENSURE_SUCCESS(rv, rv); 1012 1013 nsCOMPtr<nsIHttpHeaderVisitor> visitor = 1014 new MIMEHeaderCopyVisitor(replacementMime); 1015 rv = mime->VisitHeaders(visitor); 1016 NS_ENSURE_SUCCESS(rv, rv); 1017 1018 rv = replacementMime->SetData(replacement); 1019 NS_ENSURE_SUCCESS(rv, rv); 1020 1021 replacementMime.forget(aReplacementStream); 1022 } 1023 return NS_OK; 1024 } 1025 1026 // Preserve "real" buffered input streams which wrap data (i.e. are backed by 1027 // nsBufferedInputStream), but normalize the wrapped stream. 1028 if (nsCOMPtr<nsIBufferedInputStream> buffered = 1029 do_QueryInterface(aUploadStream)) { 1030 nsCOMPtr<nsIInputStream> data; 1031 if (NS_SUCCEEDED(buffered->GetData(getter_AddRefs(data)))) { 1032 nsCOMPtr<nsIInputStream> replacement; 1033 nsresult rv = NormalizeUploadStream(data, getter_AddRefs(replacement), 1034 aReadyPromise); 1035 NS_ENSURE_SUCCESS(rv, rv); 1036 if (replacement) { 1037 // This buffer size should be kept in sync with HTMLFormSubmission. 1038 rv = NS_NewBufferedInputStream(aReplacementStream, replacement.forget(), 1039 8192); 1040 NS_ENSURE_SUCCESS(rv, rv); 1041 } 1042 return NS_OK; 1043 } 1044 } 1045 1046 // Preserve multiplex input streams, normalizing each individual inner stream 1047 // to avoid unnecessary copying. 1048 if (nsCOMPtr<nsIMultiplexInputStream> multiplex = 1049 do_QueryInterface(aUploadStream)) { 1050 uint32_t count = multiplex->GetCount(); 1051 nsTArray<nsCOMPtr<nsIInputStream>> streams(count); 1052 nsTArray<RefPtr<GenericPromise>> promises(count); 1053 bool replace = false; 1054 for (uint32_t i = 0; i < count; ++i) { 1055 nsCOMPtr<nsIInputStream> inner; 1056 nsresult rv = multiplex->GetStream(i, getter_AddRefs(inner)); 1057 NS_ENSURE_SUCCESS(rv, rv); 1058 1059 RefPtr<GenericPromise> promise; 1060 nsCOMPtr<nsIInputStream> replacement; 1061 rv = NormalizeUploadStream(inner, getter_AddRefs(replacement), 1062 getter_AddRefs(promise)); 1063 NS_ENSURE_SUCCESS(rv, rv); 1064 if (promise) { 1065 promises.AppendElement(promise); 1066 } 1067 if (replacement) { 1068 streams.AppendElement(replacement); 1069 replace = true; 1070 } else { 1071 streams.AppendElement(inner); 1072 } 1073 } 1074 1075 // If any of the inner streams needed to be replaced, replace the entire 1076 // nsIMultiplexInputStream. 1077 if (replace) { 1078 nsresult rv; 1079 nsCOMPtr<nsIMultiplexInputStream> replacement = 1080 do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1", &rv); 1081 NS_ENSURE_SUCCESS(rv, rv); 1082 for (auto& stream : streams) { 1083 rv = replacement->AppendStream(stream); 1084 NS_ENSURE_SUCCESS(rv, rv); 1085 } 1086 1087 MOZ_ALWAYS_SUCCEEDS(CallQueryInterface(replacement, aReplacementStream)); 1088 } 1089 1090 // Wait for all inner promises to settle before resolving the final promise. 1091 if (!promises.IsEmpty()) { 1092 RefPtr<GenericPromise> ready = 1093 GenericPromise::AllSettled(GetCurrentSerialEventTarget(), promises) 1094 ->Then(GetCurrentSerialEventTarget(), __func__, 1095 [](GenericPromise::AllSettledPromiseType:: 1096 ResolveOrRejectValue&& aResults) 1097 -> RefPtr<GenericPromise> { 1098 MOZ_ASSERT(aResults.IsResolve(), 1099 "AllSettled never rejects"); 1100 for (auto& result : aResults.ResolveValue()) { 1101 if (result.IsReject()) { 1102 return GenericPromise::CreateAndReject( 1103 result.RejectValue(), __func__); 1104 } 1105 } 1106 return GenericPromise::CreateAndResolve(true, __func__); 1107 }); 1108 ready.forget(aReadyPromise); 1109 } 1110 return NS_OK; 1111 } 1112 1113 // If the stream is cloneable, seekable and non-async, we can allow it. Async 1114 // input streams can cause issues, as various consumers of input streams 1115 // expect the payload to be synchronous and `Available()` to be the length of 1116 // the stream, which is not true for asynchronous streams. 1117 nsCOMPtr<nsIAsyncInputStream> async = do_QueryInterface(aUploadStream); 1118 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aUploadStream); 1119 if (NS_InputStreamIsCloneable(aUploadStream) && seekable && !async) { 1120 return NS_OK; 1121 } 1122 1123 // Asynchronously copy our non-normalized stream into a StorageStream so that 1124 // it is seekable, cloneable, and synchronous once the copy completes. 1125 1126 NS_WARNING("Upload Stream is being copied into StorageStream"); 1127 1128 nsCOMPtr<nsIStorageStream> storageStream; 1129 nsresult rv = 1130 NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storageStream)); 1131 NS_ENSURE_SUCCESS(rv, rv); 1132 1133 nsCOMPtr<nsIOutputStream> sink; 1134 rv = storageStream->GetOutputStream(0, getter_AddRefs(sink)); 1135 NS_ENSURE_SUCCESS(rv, rv); 1136 1137 nsCOMPtr<nsIInputStream> replacementStream; 1138 rv = storageStream->NewInputStream(0, getter_AddRefs(replacementStream)); 1139 NS_ENSURE_SUCCESS(rv, rv); 1140 1141 // Ensure the source stream is buffered before starting the copy so we can use 1142 // ReadSegments, as nsStorageStream doesn't implement WriteSegments. 1143 nsCOMPtr<nsIInputStream> source = aUploadStream; 1144 if (!NS_InputStreamIsBuffered(aUploadStream)) { 1145 nsCOMPtr<nsIInputStream> bufferedSource; 1146 rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedSource), 1147 source.forget(), 4096); 1148 NS_ENSURE_SUCCESS(rv, rv); 1149 source = bufferedSource.forget(); 1150 } 1151 1152 // Perform an AsyncCopy into the input stream on the STS. 1153 nsCOMPtr<nsIEventTarget> target = 1154 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); 1155 RefPtr<GenericPromise::Private> ready = new GenericPromise::Private(__func__); 1156 rv = NS_AsyncCopy(source, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096, 1157 NormalizeCopyComplete, do_AddRef(ready).take()); 1158 if (NS_WARN_IF(NS_FAILED(rv))) { 1159 ready.get()->Release(); 1160 return rv; 1161 } 1162 1163 replacementStream.forget(aReplacementStream); 1164 ready.forget(aReadyPromise); 1165 return NS_OK; 1166 } 1167 1168 } // anonymous namespace 1169 1170 NS_IMETHODIMP 1171 HttpBaseChannel::CloneUploadStream(int64_t* aContentLength, 1172 nsIInputStream** aClonedStream) { 1173 NS_ENSURE_ARG_POINTER(aContentLength); 1174 NS_ENSURE_ARG_POINTER(aClonedStream); 1175 *aClonedStream = nullptr; 1176 1177 if (!XRE_IsParentProcess()) { 1178 NS_WARNING("CloneUploadStream is only supported in the parent process"); 1179 return NS_ERROR_NOT_AVAILABLE; 1180 } 1181 1182 if (!mUploadStream) { 1183 return NS_OK; 1184 } 1185 1186 nsCOMPtr<nsIInputStream> clonedStream; 1187 nsresult rv = 1188 NS_CloneInputStream(mUploadStream, getter_AddRefs(clonedStream)); 1189 NS_ENSURE_SUCCESS(rv, rv); 1190 1191 clonedStream.forget(aClonedStream); 1192 1193 *aContentLength = mReqContentLength; 1194 return NS_OK; 1195 } 1196 1197 //----------------------------------------------------------------------------- 1198 // HttpBaseChannel::nsIUploadChannel2 1199 //----------------------------------------------------------------------------- 1200 1201 NS_IMETHODIMP 1202 HttpBaseChannel::ExplicitSetUploadStream(nsIInputStream* aStream, 1203 const nsACString& aContentType, 1204 int64_t aContentLength, 1205 const nsACString& aMethod, 1206 bool aStreamHasHeaders) { 1207 // Ensure stream is set and method is valid 1208 NS_ENSURE_TRUE(aStream, NS_ERROR_FAILURE); 1209 1210 { 1211 DebugOnly<nsCOMPtr<nsIMIMEInputStream>> mimeStream; 1212 MOZ_ASSERT( 1213 !aStreamHasHeaders || NS_FAILED(CallQueryInterface( 1214 aStream, getter_AddRefs(mimeStream.value))), 1215 "nsIMIMEInputStream should not include headers"); 1216 } 1217 1218 nsresult rv = SetRequestMethod(aMethod); 1219 NS_ENSURE_SUCCESS(rv, rv); 1220 1221 if (!aStreamHasHeaders && !aContentType.IsVoid()) { 1222 if (aContentType.IsEmpty()) { 1223 SetEmptyRequestHeader("Content-Type"_ns); 1224 } else { 1225 SetRequestHeader("Content-Type"_ns, aContentType, false); 1226 } 1227 } 1228 1229 StoreUploadStreamHasHeaders(aStreamHasHeaders); 1230 1231 return InternalSetUploadStream(aStream, aContentLength, !aStreamHasHeaders); 1232 } 1233 1234 nsresult HttpBaseChannel::InternalSetUploadStream( 1235 nsIInputStream* aUploadStream, int64_t aContentLength, 1236 bool aSetContentLengthHeader) { 1237 // If we're not on the main thread, such as for TRR, the content length must 1238 // be provided, as we can't normalize our upload stream. 1239 if (!NS_IsMainThread()) { 1240 if (aContentLength < 0) { 1241 MOZ_ASSERT_UNREACHABLE( 1242 "Upload content length must be explicit off-main-thread"); 1243 return NS_ERROR_INVALID_ARG; 1244 } 1245 1246 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aUploadStream); 1247 if (!NS_InputStreamIsCloneable(aUploadStream) || !seekable) { 1248 MOZ_ASSERT_UNREACHABLE( 1249 "Upload stream must be cloneable & seekable off-main-thread"); 1250 return NS_ERROR_INVALID_ARG; 1251 } 1252 1253 mUploadStream = aUploadStream; 1254 ExplicitSetUploadStreamLength(aContentLength, aSetContentLengthHeader); 1255 return NS_OK; 1256 } 1257 1258 // Normalize the upload stream we're provided to ensure that it is cloneable, 1259 // seekable, and synchronous when in the parent process. 1260 // 1261 // This might be an async operation, in which case ready will be returned and 1262 // resolved when the operation is complete. 1263 nsCOMPtr<nsIInputStream> replacement; 1264 RefPtr<GenericPromise> ready; 1265 if (XRE_IsParentProcess()) { 1266 nsresult rv = NormalizeUploadStream( 1267 aUploadStream, getter_AddRefs(replacement), getter_AddRefs(ready)); 1268 NS_ENSURE_SUCCESS(rv, rv); 1269 } 1270 1271 mUploadStream = replacement ? replacement.get() : aUploadStream; 1272 1273 // Once the upload stream is ready, fetch its length before proceeding with 1274 // AsyncOpen. 1275 auto onReady = [self = RefPtr{this}, aContentLength, aSetContentLengthHeader, 1276 stream = mUploadStream]() { 1277 auto setLengthAndResume = [self, aSetContentLengthHeader](int64_t aLength) { 1278 self->StorePendingUploadStreamNormalization(false); 1279 self->ExplicitSetUploadStreamLength(aLength >= 0 ? aLength : 0, 1280 aSetContentLengthHeader); 1281 self->MaybeResumeAsyncOpen(); 1282 }; 1283 1284 if (aContentLength >= 0) { 1285 setLengthAndResume(aContentLength); 1286 return; 1287 } 1288 1289 int64_t length; 1290 if (InputStreamLengthHelper::GetSyncLength(stream, &length)) { 1291 setLengthAndResume(length); 1292 return; 1293 } 1294 1295 InputStreamLengthHelper::GetAsyncLength(stream, setLengthAndResume); 1296 }; 1297 StorePendingUploadStreamNormalization(true); 1298 1299 // Resolve onReady synchronously unless a promise is returned. 1300 if (ready) { 1301 ready->Then(GetCurrentSerialEventTarget(), __func__, 1302 [onReady = std::move(onReady)]( 1303 GenericPromise::ResolveOrRejectValue&&) { onReady(); }); 1304 } else { 1305 onReady(); 1306 } 1307 return NS_OK; 1308 } 1309 1310 void HttpBaseChannel::ExplicitSetUploadStreamLength( 1311 uint64_t aContentLength, bool aSetContentLengthHeader) { 1312 // We already have the content length. We don't need to determinate it. 1313 mReqContentLength = aContentLength; 1314 1315 if (!aSetContentLengthHeader) { 1316 return; 1317 } 1318 1319 nsAutoCString header; 1320 header.AssignLiteral("Content-Length"); 1321 1322 // Maybe the content-length header has been already set. 1323 nsAutoCString value; 1324 nsresult rv = GetRequestHeader(header, value); 1325 if (NS_SUCCEEDED(rv) && !value.IsEmpty()) { 1326 return; 1327 } 1328 1329 nsAutoCString contentLengthStr; 1330 contentLengthStr.AppendInt(aContentLength); 1331 SetRequestHeader(header, contentLengthStr, false); 1332 } 1333 1334 NS_IMETHODIMP 1335 HttpBaseChannel::GetUploadStreamHasHeaders(bool* hasHeaders) { 1336 NS_ENSURE_ARG(hasHeaders); 1337 1338 *hasHeaders = LoadUploadStreamHasHeaders(); 1339 return NS_OK; 1340 } 1341 1342 bool HttpBaseChannel::MaybeWaitForUploadStreamNormalization( 1343 nsIStreamListener* aListener, nsISupports* aContext) { 1344 MOZ_ASSERT(NS_IsMainThread()); 1345 MOZ_ASSERT(!LoadAsyncOpenWaitingForStreamNormalization(), 1346 "AsyncOpen() called twice?"); 1347 1348 if (!LoadPendingUploadStreamNormalization()) { 1349 return false; 1350 } 1351 1352 mListener = aListener; 1353 StoreAsyncOpenWaitingForStreamNormalization(true); 1354 return true; 1355 } 1356 1357 void HttpBaseChannel::MaybeResumeAsyncOpen() { 1358 MOZ_ASSERT(NS_IsMainThread()); 1359 MOZ_ASSERT(!LoadPendingUploadStreamNormalization()); 1360 1361 if (!LoadAsyncOpenWaitingForStreamNormalization()) { 1362 return; 1363 } 1364 1365 nsCOMPtr<nsIStreamListener> listener; 1366 listener.swap(mListener); 1367 1368 StoreAsyncOpenWaitingForStreamNormalization(false); 1369 1370 nsresult rv = AsyncOpen(listener); 1371 if (NS_WARN_IF(NS_FAILED(rv))) { 1372 DoAsyncAbort(rv); 1373 } 1374 } 1375 1376 //----------------------------------------------------------------------------- 1377 // HttpBaseChannel::nsIEncodedChannel 1378 //----------------------------------------------------------------------------- 1379 1380 NS_IMETHODIMP 1381 HttpBaseChannel::GetApplyConversion(bool* value) { 1382 *value = LoadApplyConversion(); 1383 return NS_OK; 1384 } 1385 1386 NS_IMETHODIMP 1387 HttpBaseChannel::SetApplyConversion(bool value) { 1388 LOG(("HttpBaseChannel::SetApplyConversion [this=%p value=%d]\n", this, 1389 value)); 1390 StoreApplyConversion(value); 1391 return NS_OK; 1392 } 1393 1394 nsresult HttpBaseChannel::DoApplyContentConversions( 1395 nsIStreamListener* aNextListener, nsIStreamListener** aNewNextListener) { 1396 return DoApplyContentConversionsInternal(aNextListener, aNewNextListener, 1397 false, nullptr); 1398 } 1399 1400 // create a listener chain that looks like this 1401 // http-channel -> decompressor (n times) -> InterceptFailedOnSTop -> 1402 // channel-creator-listener 1403 // 1404 // we need to do this because not every decompressor has fully streamed output 1405 // so may need a call to OnStopRequest to identify its completion state.. and if 1406 // it creates an error there the channel status code needs to be updated before 1407 // calling the terminal listener. Having the decompress do it via cancel() means 1408 // channels cannot effectively be used in two contexts (specifically this one 1409 // and a peek context for sniffing) 1410 // 1411 class InterceptFailedOnStop : public nsIThreadRetargetableStreamListener { 1412 virtual ~InterceptFailedOnStop() = default; 1413 nsCOMPtr<nsIStreamListener> mNext; 1414 HttpBaseChannel* mChannel; 1415 1416 public: 1417 InterceptFailedOnStop(nsIStreamListener* arg, HttpBaseChannel* chan) 1418 : mNext(arg), mChannel(chan) {} 1419 NS_DECL_THREADSAFE_ISUPPORTS 1420 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER 1421 1422 NS_IMETHOD OnStartRequest(nsIRequest* aRequest) override { 1423 return mNext->OnStartRequest(aRequest); 1424 } 1425 1426 NS_IMETHOD OnStopRequest(nsIRequest* aRequest, 1427 nsresult aStatusCode) override { 1428 if (NS_FAILED(aStatusCode) && NS_SUCCEEDED(mChannel->mStatus)) { 1429 LOG(("HttpBaseChannel::InterceptFailedOnStop %p seting status %" PRIx32, 1430 mChannel, static_cast<uint32_t>(aStatusCode))); 1431 mChannel->mStatus = aStatusCode; 1432 } 1433 return mNext->OnStopRequest(aRequest, aStatusCode); 1434 } 1435 1436 NS_IMETHOD OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream, 1437 uint64_t aOffset, uint32_t aCount) override { 1438 return mNext->OnDataAvailable(aRequest, aInputStream, aOffset, aCount); 1439 } 1440 }; 1441 1442 NS_IMPL_ADDREF(InterceptFailedOnStop) 1443 NS_IMPL_RELEASE(InterceptFailedOnStop) 1444 1445 NS_INTERFACE_MAP_BEGIN(InterceptFailedOnStop) 1446 NS_INTERFACE_MAP_ENTRY(nsIStreamListener) 1447 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) 1448 NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener) 1449 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver) 1450 NS_INTERFACE_MAP_END 1451 1452 NS_IMETHODIMP 1453 InterceptFailedOnStop::CheckListenerChain() { 1454 nsCOMPtr<nsIThreadRetargetableStreamListener> listener = 1455 do_QueryInterface(mNext); 1456 if (!listener) { 1457 return NS_ERROR_NO_INTERFACE; 1458 } 1459 1460 return listener->CheckListenerChain(); 1461 } 1462 1463 NS_IMETHODIMP 1464 InterceptFailedOnStop::OnDataFinished(nsresult aStatus) { 1465 nsCOMPtr<nsIThreadRetargetableStreamListener> listener = 1466 do_QueryInterface(mNext); 1467 if (listener) { 1468 return listener->OnDataFinished(aStatus); 1469 } 1470 1471 return NS_OK; 1472 } 1473 1474 NS_IMETHODIMP 1475 HttpBaseChannel::DoApplyContentConversions(nsIStreamListener* aNextListener, 1476 nsIStreamListener** aNewNextListener, 1477 nsISupports* aCtxt) { 1478 return DoApplyContentConversionsInternal(aNextListener, aNewNextListener, 1479 false, aCtxt); 1480 } 1481 1482 nsresult HttpBaseChannel::DoApplyContentConversionsInternal( 1483 nsIStreamListener* aNextListener, nsIStreamListener** aNewNextListener, 1484 bool aRemoveEncodings, nsISupports* aCtxt) { 1485 *aNewNextListener = nullptr; 1486 if (!mResponseHead || !aNextListener) { 1487 return NS_OK; 1488 } 1489 1490 LOG( 1491 ("HttpBaseChannel::DoApplyContentConversions [this=%p], " 1492 "removeEncodings=%d\n", 1493 this, aRemoveEncodings)); 1494 1495 #ifdef DEBUG 1496 { 1497 nsAutoCString contentEncoding; 1498 nsresult rv = 1499 mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding); 1500 if (NS_SUCCEEDED(rv) && !contentEncoding.IsEmpty()) { 1501 nsAutoCString newEncoding; 1502 char* cePtr = contentEncoding.BeginWriting(); 1503 while (char* val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr)) { 1504 if (strcmp(val, "dcb") == 0 || strcmp(val, "dcz") == 0) { 1505 MOZ_ASSERT(LoadApplyConversion() && !LoadHasAppliedConversion()); 1506 } 1507 } 1508 } 1509 } 1510 #endif 1511 1512 if (!LoadApplyConversion()) { 1513 LOG(("not applying conversion per ApplyConversion\n")); 1514 return NS_OK; 1515 } 1516 1517 if (LoadHasAppliedConversion()) { 1518 LOG(("not applying conversion because HasAppliedConversion is true\n")); 1519 return NS_OK; 1520 } 1521 1522 if (LoadDeliveringAltData()) { 1523 MOZ_ASSERT(!mAvailableCachedAltDataType.IsEmpty()); 1524 LOG(("not applying conversion because delivering alt-data\n")); 1525 return NS_OK; 1526 } 1527 1528 nsAutoCString contentEncoding; 1529 nsresult rv = 1530 mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding); 1531 if (NS_FAILED(rv) || contentEncoding.IsEmpty()) return NS_OK; 1532 1533 nsCOMPtr<nsIStreamListener> nextListener = 1534 new InterceptFailedOnStop(aNextListener, this); 1535 1536 // The encodings are listed in the order they were applied 1537 // (see rfc 2616 section 14.11), so they need to removed in reverse 1538 // order. This is accomplished because the converter chain ends up 1539 // being a stack with the last converter created being the first one 1540 // to accept the raw network data. 1541 1542 char* cePtr = contentEncoding.BeginWriting(); 1543 uint32_t count = 0; 1544 while (char* val = nsCRT::strtok(cePtr, HTTP_LWS ",", &cePtr)) { 1545 if (++count > 16) { 1546 // For compatibility with old code, we will just carry on without 1547 // removing the encodings 1548 LOG(("Too many Content-Encodings. Ignoring remainder.\n")); 1549 break; 1550 } 1551 1552 if (gHttpHandler->IsAcceptableEncoding(val, 1553 isSecureOrTrustworthyURL(mURI))) { 1554 RefPtr<nsHTTPCompressConv> converter = new nsHTTPCompressConv(); 1555 nsAutoCString from(val); 1556 ToLowerCase(from); 1557 rv = converter->AsyncConvertData(from.get(), "uncompressed", nextListener, 1558 aCtxt); 1559 if (NS_FAILED(rv)) { 1560 LOG(("Unexpected failure of AsyncConvertData %s\n", val)); 1561 return rv; 1562 } 1563 1564 LOG(("Adding converter for content-encoding '%s'", val)); 1565 if (Telemetry::CanRecordPrereleaseData()) { 1566 int mode = 0; 1567 if (from.EqualsLiteral("gzip") || from.EqualsLiteral("x-gzip")) { 1568 mode = 1; 1569 } else if (from.EqualsLiteral("deflate") || 1570 from.EqualsLiteral("x-deflate")) { 1571 mode = 2; 1572 } else if (from.EqualsLiteral("br")) { 1573 mode = 3; 1574 } else if (from.EqualsLiteral("zstd")) { 1575 mode = 4; 1576 } else if (from.EqualsLiteral("dcb")) { 1577 mode = 5; 1578 } else if (from.EqualsLiteral("dcz")) { 1579 mode = 6; 1580 } 1581 glean::http::content_encoding.AccumulateSingleSample(mode); 1582 } 1583 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 1584 if (from.EqualsLiteral("dcb") || from.EqualsLiteral("dcz")) { 1585 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); 1586 } 1587 #endif 1588 nextListener = converter; 1589 } else { 1590 if (val) { 1591 LOG(("Unknown content encoding '%s'\n", val)); 1592 } 1593 // we *should* return NS_ERROR_UNEXPECTED here, but that will break sites 1594 // that use things like content-encoding: x-gzip, x-gzip (or any other 1595 // weird encoding) 1596 } 1597 } 1598 1599 // dcb and dcz encodings are removed when it's decompressed (always in 1600 // the parent process). However, in theory you could have 1601 // Content-Encoding: dcb,gzip 1602 // in which case we could pass it down to the content process as 1603 // Content-Encoding: gzip. We won't do that; we'll remove all compressors 1604 // if we need to remove any. 1605 // This double compression of course is silly, but supported by the spec. 1606 if (aRemoveEncodings) { 1607 // If we have dcb or dcz, all content-encodings in the header should be 1608 // removed as we're decompressing before the tee in the parent 1609 // process. Also we should remove any encodings if it's a dictionary 1610 // so we can load it quickly 1611 LOG(("Changing Content-Encoding from '%s' to ''", contentEncoding.get())); 1612 // Can't use SetHeader; we need to overwrite the current value 1613 rv = mResponseHead->SetHeaderOverride(nsHttp::Content_Encoding, ""_ns); 1614 } 1615 *aNewNextListener = do_AddRef(nextListener).take(); 1616 return NS_OK; 1617 } 1618 1619 NS_IMETHODIMP 1620 HttpBaseChannel::GetContentEncodings(nsIUTF8StringEnumerator** aEncodings) { 1621 if (!mResponseHead) { 1622 *aEncodings = nullptr; 1623 return NS_OK; 1624 } 1625 1626 nsAutoCString encoding; 1627 (void)mResponseHead->GetHeader(nsHttp::Content_Encoding, encoding); 1628 if (encoding.IsEmpty()) { 1629 *aEncodings = nullptr; 1630 return NS_OK; 1631 } 1632 RefPtr<nsContentEncodings> enumerator = 1633 new nsContentEncodings(this, encoding.get()); 1634 enumerator.forget(aEncodings); 1635 return NS_OK; 1636 } 1637 1638 //----------------------------------------------------------------------------- 1639 // HttpBaseChannel::nsContentEncodings <public> 1640 //----------------------------------------------------------------------------- 1641 1642 HttpBaseChannel::nsContentEncodings::nsContentEncodings( 1643 nsIHttpChannel* aChannel, const char* aEncodingHeader) 1644 : mEncodingHeader(aEncodingHeader), mChannel(aChannel), mReady(false) { 1645 mCurEnd = aEncodingHeader + strlen(aEncodingHeader); 1646 mCurStart = mCurEnd; 1647 } 1648 1649 //----------------------------------------------------------------------------- 1650 // HttpBaseChannel::nsContentEncodings::nsISimpleEnumerator 1651 //----------------------------------------------------------------------------- 1652 1653 NS_IMETHODIMP 1654 HttpBaseChannel::nsContentEncodings::HasMore(bool* aMoreEncodings) { 1655 if (mReady) { 1656 *aMoreEncodings = true; 1657 return NS_OK; 1658 } 1659 1660 nsresult rv = PrepareForNext(); 1661 *aMoreEncodings = NS_SUCCEEDED(rv); 1662 return NS_OK; 1663 } 1664 1665 NS_IMETHODIMP 1666 HttpBaseChannel::nsContentEncodings::GetNext(nsACString& aNextEncoding) { 1667 aNextEncoding.Truncate(); 1668 if (!mReady) { 1669 nsresult rv = PrepareForNext(); 1670 if (NS_FAILED(rv)) { 1671 return NS_ERROR_FAILURE; 1672 } 1673 } 1674 1675 const nsACString& encoding = Substring(mCurStart, mCurEnd); 1676 1677 nsACString::const_iterator start, end; 1678 encoding.BeginReading(start); 1679 encoding.EndReading(end); 1680 1681 bool haveType = false; 1682 if (CaseInsensitiveFindInReadable("gzip"_ns, start, end)) { 1683 aNextEncoding.AssignLiteral(APPLICATION_GZIP); 1684 haveType = true; 1685 } 1686 1687 if (!haveType) { 1688 encoding.BeginReading(start); 1689 if (CaseInsensitiveFindInReadable("compress"_ns, start, end)) { 1690 aNextEncoding.AssignLiteral(APPLICATION_COMPRESS); 1691 haveType = true; 1692 } 1693 } 1694 1695 if (!haveType) { 1696 encoding.BeginReading(start); 1697 if (CaseInsensitiveFindInReadable("deflate"_ns, start, end)) { 1698 aNextEncoding.AssignLiteral(APPLICATION_ZIP); 1699 haveType = true; 1700 } 1701 } 1702 1703 if (!haveType) { 1704 encoding.BeginReading(start); 1705 if (CaseInsensitiveFindInReadable("br"_ns, start, end)) { 1706 aNextEncoding.AssignLiteral(APPLICATION_BROTLI); 1707 haveType = true; 1708 } 1709 } 1710 1711 if (!haveType) { 1712 encoding.BeginReading(start); 1713 if (CaseInsensitiveFindInReadable("zstd"_ns, start, end)) { 1714 aNextEncoding.AssignLiteral(APPLICATION_ZSTD); 1715 haveType = true; 1716 } 1717 } 1718 1719 // Prepare to fetch the next encoding 1720 mCurEnd = mCurStart; 1721 mReady = false; 1722 1723 if (haveType) return NS_OK; 1724 1725 NS_WARNING("Unknown encoding type"); 1726 return NS_ERROR_FAILURE; 1727 } 1728 1729 //----------------------------------------------------------------------------- 1730 // HttpBaseChannel::nsContentEncodings::nsISupports 1731 //----------------------------------------------------------------------------- 1732 1733 NS_IMPL_ISUPPORTS(HttpBaseChannel::nsContentEncodings, nsIUTF8StringEnumerator, 1734 nsIStringEnumerator) 1735 1736 //----------------------------------------------------------------------------- 1737 // HttpBaseChannel::nsContentEncodings <private> 1738 //----------------------------------------------------------------------------- 1739 1740 nsresult HttpBaseChannel::nsContentEncodings::PrepareForNext(void) { 1741 MOZ_ASSERT(mCurStart == mCurEnd, "Indeterminate state"); 1742 1743 // At this point both mCurStart and mCurEnd point to somewhere 1744 // past the end of the next thing we want to return 1745 1746 while (mCurEnd != mEncodingHeader) { 1747 --mCurEnd; 1748 if (*mCurEnd != ',' && !nsCRT::IsAsciiSpace(*mCurEnd)) break; 1749 } 1750 if (mCurEnd == mEncodingHeader) { 1751 return NS_ERROR_NOT_AVAILABLE; // no more encodings 1752 } 1753 ++mCurEnd; 1754 1755 // At this point mCurEnd points to the first char _after_ the 1756 // header we want. Furthermore, mCurEnd - 1 != mEncodingHeader 1757 1758 mCurStart = mCurEnd - 1; 1759 while (mCurStart != mEncodingHeader && *mCurStart != ',' && 1760 !nsCRT::IsAsciiSpace(*mCurStart)) { 1761 --mCurStart; 1762 } 1763 if (*mCurStart == ',' || nsCRT::IsAsciiSpace(*mCurStart)) { 1764 ++mCurStart; // we stopped because of a weird char, so move up one 1765 } 1766 1767 // At this point mCurStart and mCurEnd bracket the encoding string 1768 // we want. Check that it's not "identity" 1769 if (Substring(mCurStart, mCurEnd) 1770 .Equals("identity", nsCaseInsensitiveCStringComparator)) { 1771 mCurEnd = mCurStart; 1772 return PrepareForNext(); 1773 } 1774 1775 mReady = true; 1776 return NS_OK; 1777 } 1778 1779 //----------------------------------------------------------------------------- 1780 // HttpBaseChannel::nsIHttpChannel 1781 //----------------------------------------------------------------------------- 1782 1783 NS_IMETHODIMP 1784 HttpBaseChannel::GetChannelId(uint64_t* aChannelId) { 1785 NS_ENSURE_ARG_POINTER(aChannelId); 1786 *aChannelId = mChannelId; 1787 return NS_OK; 1788 } 1789 1790 NS_IMETHODIMP 1791 HttpBaseChannel::SetChannelId(uint64_t aChannelId) { 1792 mChannelId = aChannelId; 1793 return NS_OK; 1794 } 1795 1796 NS_IMETHODIMP HttpBaseChannel::GetTopLevelContentWindowId(uint64_t* aWindowId) { 1797 if (!mContentWindowId) { 1798 nsCOMPtr<nsILoadContext> loadContext; 1799 GetCallback(loadContext); 1800 if (loadContext) { 1801 nsCOMPtr<mozIDOMWindowProxy> topWindow; 1802 loadContext->GetTopWindow(getter_AddRefs(topWindow)); 1803 if (topWindow) { 1804 if (nsPIDOMWindowInner* inner = 1805 nsPIDOMWindowOuter::From(topWindow)->GetCurrentInnerWindow()) { 1806 mContentWindowId = inner->WindowID(); 1807 } 1808 } 1809 } 1810 } 1811 *aWindowId = mContentWindowId; 1812 return NS_OK; 1813 } 1814 1815 NS_IMETHODIMP HttpBaseChannel::SetBrowserId(uint64_t aId) { 1816 mBrowserId = aId; 1817 return NS_OK; 1818 } 1819 1820 NS_IMETHODIMP HttpBaseChannel::GetBrowserId(uint64_t* aId) { 1821 EnsureBrowserId(); 1822 *aId = mBrowserId; 1823 return NS_OK; 1824 } 1825 1826 NS_IMETHODIMP HttpBaseChannel::SetTopLevelContentWindowId(uint64_t aWindowId) { 1827 mContentWindowId = aWindowId; 1828 return NS_OK; 1829 } 1830 1831 NS_IMETHODIMP 1832 HttpBaseChannel::IsThirdPartyTrackingResource(bool* aIsTrackingResource) { 1833 MOZ_ASSERT( 1834 !(mFirstPartyClassificationFlags && mThirdPartyClassificationFlags)); 1835 *aIsTrackingResource = UrlClassifierCommon::IsTrackingClassificationFlag( 1836 mThirdPartyClassificationFlags, 1837 mLoadInfo->GetOriginAttributes().IsPrivateBrowsing()); 1838 return NS_OK; 1839 } 1840 1841 NS_IMETHODIMP 1842 HttpBaseChannel::IsThirdPartySocialTrackingResource( 1843 bool* aIsThirdPartySocialTrackingResource) { 1844 MOZ_ASSERT(!mFirstPartyClassificationFlags || 1845 !mThirdPartyClassificationFlags); 1846 *aIsThirdPartySocialTrackingResource = 1847 UrlClassifierCommon::IsSocialTrackingClassificationFlag( 1848 mThirdPartyClassificationFlags); 1849 return NS_OK; 1850 } 1851 1852 NS_IMETHODIMP 1853 HttpBaseChannel::GetClassificationFlags(uint32_t* aFlags) { 1854 if (mThirdPartyClassificationFlags) { 1855 *aFlags = mThirdPartyClassificationFlags; 1856 } else { 1857 *aFlags = mFirstPartyClassificationFlags; 1858 } 1859 return NS_OK; 1860 } 1861 1862 NS_IMETHODIMP 1863 HttpBaseChannel::GetFirstPartyClassificationFlags(uint32_t* aFlags) { 1864 *aFlags = mFirstPartyClassificationFlags; 1865 return NS_OK; 1866 } 1867 1868 NS_IMETHODIMP 1869 HttpBaseChannel::GetThirdPartyClassificationFlags(uint32_t* aFlags) { 1870 *aFlags = mThirdPartyClassificationFlags; 1871 return NS_OK; 1872 } 1873 1874 NS_IMETHODIMP 1875 HttpBaseChannel::GetTransferSize(uint64_t* aTransferSize) { 1876 MutexAutoLock lock(mOnDataFinishedMutex); 1877 *aTransferSize = mTransferSize; 1878 return NS_OK; 1879 } 1880 1881 NS_IMETHODIMP 1882 HttpBaseChannel::GetRequestSize(uint64_t* aRequestSize) { 1883 *aRequestSize = mRequestSize; 1884 return NS_OK; 1885 } 1886 1887 NS_IMETHODIMP 1888 HttpBaseChannel::GetDecodedBodySize(uint64_t* aDecodedBodySize) { 1889 MutexAutoLock lock(mOnDataFinishedMutex); 1890 *aDecodedBodySize = mDecodedBodySize; 1891 return NS_OK; 1892 } 1893 1894 NS_IMETHODIMP 1895 HttpBaseChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) { 1896 MutexAutoLock lock(mOnDataFinishedMutex); 1897 *aEncodedBodySize = mEncodedBodySize; 1898 return NS_OK; 1899 } 1900 1901 NS_IMETHODIMP 1902 HttpBaseChannel::GetSupportsHTTP3(bool* aSupportsHTTP3) { 1903 *aSupportsHTTP3 = mSupportsHTTP3; 1904 return NS_OK; 1905 } 1906 1907 NS_IMETHODIMP 1908 HttpBaseChannel::GetHasHTTPSRR(bool* aHasHTTPSRR) { 1909 *aHasHTTPSRR = LoadHasHTTPSRR(); 1910 return NS_OK; 1911 } 1912 1913 NS_IMETHODIMP 1914 HttpBaseChannel::GetRequestMethod(nsACString& aMethod) { 1915 mRequestHead.Method(aMethod); 1916 return NS_OK; 1917 } 1918 1919 NS_IMETHODIMP 1920 HttpBaseChannel::SetRequestMethod(const nsACString& aMethod) { 1921 ENSURE_CALLED_BEFORE_CONNECT(); 1922 1923 mLoadInfo->SetIsGETRequest(aMethod.Equals("GET")); 1924 1925 const nsCString& flatMethod = PromiseFlatCString(aMethod); 1926 1927 // Method names are restricted to valid HTTP tokens. 1928 if (!nsHttp::IsValidToken(flatMethod)) return NS_ERROR_INVALID_ARG; 1929 1930 mRequestHead.SetMethod(flatMethod); 1931 return NS_OK; 1932 } 1933 1934 NS_IMETHODIMP 1935 HttpBaseChannel::GetReferrerInfo(nsIReferrerInfo** aReferrerInfo) { 1936 NS_ENSURE_ARG_POINTER(aReferrerInfo); 1937 *aReferrerInfo = do_AddRef(mReferrerInfo).take(); 1938 return NS_OK; 1939 } 1940 1941 nsresult HttpBaseChannel::SetReferrerInfoInternal( 1942 nsIReferrerInfo* aReferrerInfo, bool aClone, bool aCompute, 1943 bool aRespectBeforeConnect) { 1944 LOG( 1945 ("HttpBaseChannel::SetReferrerInfoInternal [this=%p aClone(%d) " 1946 "aCompute(%d)]\n", 1947 this, aClone, aCompute)); 1948 if (aRespectBeforeConnect) { 1949 ENSURE_CALLED_BEFORE_CONNECT(); 1950 } 1951 1952 mReferrerInfo = aReferrerInfo; 1953 1954 // clear existing referrer, if any 1955 nsresult rv = ClearReferrerHeader(); 1956 if (NS_WARN_IF(NS_FAILED(rv))) { 1957 return rv; 1958 } 1959 1960 if (!mReferrerInfo) { 1961 return NS_OK; 1962 } 1963 1964 if (aClone) { 1965 mReferrerInfo = static_cast<dom::ReferrerInfo*>(aReferrerInfo)->Clone(); 1966 } 1967 1968 dom::ReferrerInfo* referrerInfo = 1969 static_cast<dom::ReferrerInfo*>(mReferrerInfo.get()); 1970 1971 // Don't set referrerInfo if it has not been initialized. 1972 if (!referrerInfo->IsInitialized()) { 1973 mReferrerInfo = nullptr; 1974 return NS_ERROR_NOT_INITIALIZED; 1975 } 1976 1977 if (aClone) { 1978 // Record the telemetry once we set the referrer info to the channel 1979 // successfully. 1980 referrerInfo->RecordTelemetry(this); 1981 } 1982 1983 if (aCompute) { 1984 rv = referrerInfo->ComputeReferrer(this); 1985 if (NS_WARN_IF(NS_FAILED(rv))) { 1986 return rv; 1987 } 1988 } 1989 1990 nsCOMPtr<nsIURI> computedReferrer = mReferrerInfo->GetComputedReferrer(); 1991 if (!computedReferrer) { 1992 return NS_OK; 1993 } 1994 1995 nsAutoCString spec; 1996 rv = computedReferrer->GetSpec(spec); 1997 if (NS_WARN_IF(NS_FAILED(rv))) { 1998 return rv; 1999 } 2000 2001 return SetReferrerHeader(spec, aRespectBeforeConnect); 2002 } 2003 2004 NS_IMETHODIMP 2005 HttpBaseChannel::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) { 2006 return SetReferrerInfoInternal(aReferrerInfo, true, true, true); 2007 } 2008 2009 NS_IMETHODIMP 2010 HttpBaseChannel::SetReferrerInfoWithoutClone(nsIReferrerInfo* aReferrerInfo) { 2011 return SetReferrerInfoInternal(aReferrerInfo, false, true, true); 2012 } 2013 2014 // Return the channel's proxy URI, or if it doesn't exist, the 2015 // channel's main URI. 2016 NS_IMETHODIMP 2017 HttpBaseChannel::GetProxyURI(nsIURI** aOut) { 2018 NS_ENSURE_ARG_POINTER(aOut); 2019 nsCOMPtr<nsIURI> result(mProxyURI); 2020 result.forget(aOut); 2021 return NS_OK; 2022 } 2023 2024 NS_IMETHODIMP 2025 HttpBaseChannel::GetRequestHeader(const nsACString& aHeader, 2026 nsACString& aValue) { 2027 aValue.Truncate(); 2028 2029 // XXX might be better to search the header list directly instead of 2030 // hitting the http atom hash table. 2031 nsHttpAtom atom = nsHttp::ResolveAtom(aHeader); 2032 if (!atom) return NS_ERROR_NOT_AVAILABLE; 2033 2034 return mRequestHead.GetHeader(atom, aValue); 2035 } 2036 2037 NS_IMETHODIMP 2038 HttpBaseChannel::SetRequestHeader(const nsACString& aHeader, 2039 const nsACString& aValue, bool aMerge) { 2040 return SetRequestHeaderInternal(aHeader, aValue, aMerge, 2041 nsHttpHeaderArray::eVarietyRequestOverride); 2042 } 2043 2044 nsresult HttpBaseChannel::SetRequestHeaderInternal( 2045 const nsACString& aHeader, const nsACString& aValue, bool aMerge, 2046 nsHttpHeaderArray::HeaderVariety aVariety) { 2047 const nsCString& flatHeader = PromiseFlatCString(aHeader); 2048 const nsCString& flatValue = PromiseFlatCString(aValue); 2049 2050 LOG( 2051 ("HttpBaseChannel::SetRequestHeader [this=%p header=\"%s\" value=\"%s\" " 2052 "merge=%u]\n", 2053 this, flatHeader.get(), flatValue.get(), aMerge)); 2054 2055 // Verify header names are valid HTTP tokens and header values are reasonably 2056 // close to whats allowed in RFC 2616. 2057 if (!nsHttp::IsValidToken(flatHeader) || 2058 !nsHttp::IsReasonableHeaderValue(flatValue)) { 2059 return NS_ERROR_INVALID_ARG; 2060 } 2061 2062 // Mark that the User-Agent header has been modified. 2063 if (nsHttp::ResolveAtom(aHeader) == nsHttp::User_Agent) { 2064 StoreIsUserAgentHeaderModified(true); 2065 } 2066 2067 return mRequestHead.SetHeader(aHeader, flatValue, aMerge); 2068 } 2069 2070 NS_IMETHODIMP 2071 HttpBaseChannel::SetNewReferrerInfo(const nsACString& aUrl, 2072 nsIReferrerInfo::ReferrerPolicyIDL aPolicy, 2073 bool aSendReferrer) { 2074 nsresult rv; 2075 // Create URI from string 2076 nsCOMPtr<nsIURI> aURI; 2077 rv = NS_NewURI(getter_AddRefs(aURI), aUrl); 2078 NS_ENSURE_SUCCESS(rv, rv); 2079 // Create new ReferrerInfo and initialize it. 2080 nsCOMPtr<nsIReferrerInfo> referrerInfo = new mozilla::dom::ReferrerInfo(); 2081 rv = referrerInfo->Init(aPolicy, aSendReferrer, aURI); 2082 NS_ENSURE_SUCCESS(rv, rv); 2083 // Set ReferrerInfo 2084 return SetReferrerInfo(referrerInfo); 2085 } 2086 2087 NS_IMETHODIMP 2088 HttpBaseChannel::SetEmptyRequestHeader(const nsACString& aHeader) { 2089 const nsCString& flatHeader = PromiseFlatCString(aHeader); 2090 2091 LOG(("HttpBaseChannel::SetEmptyRequestHeader [this=%p header=\"%s\"]\n", this, 2092 flatHeader.get())); 2093 2094 // Verify header names are valid HTTP tokens and header values are reasonably 2095 // close to whats allowed in RFC 2616. 2096 if (!nsHttp::IsValidToken(flatHeader)) { 2097 return NS_ERROR_INVALID_ARG; 2098 } 2099 2100 // Mark that the User-Agent header has been modified. 2101 if (nsHttp::ResolveAtom(aHeader) == nsHttp::User_Agent) { 2102 StoreIsUserAgentHeaderModified(true); 2103 } 2104 2105 return mRequestHead.SetEmptyHeader(aHeader); 2106 } 2107 2108 NS_IMETHODIMP 2109 HttpBaseChannel::VisitRequestHeaders(nsIHttpHeaderVisitor* visitor) { 2110 return mRequestHead.VisitHeaders(visitor); 2111 } 2112 2113 NS_IMETHODIMP 2114 HttpBaseChannel::VisitNonDefaultRequestHeaders(nsIHttpHeaderVisitor* visitor) { 2115 return mRequestHead.VisitHeaders(visitor, 2116 nsHttpHeaderArray::eFilterSkipDefault); 2117 } 2118 2119 NS_IMETHODIMP 2120 HttpBaseChannel::GetResponseHeader(const nsACString& header, 2121 nsACString& value) { 2122 value.Truncate(); 2123 2124 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 2125 2126 nsHttpAtom atom = nsHttp::ResolveAtom(header); 2127 if (!atom) return NS_ERROR_NOT_AVAILABLE; 2128 2129 return mResponseHead->GetHeader(atom, value); 2130 } 2131 2132 NS_IMETHODIMP 2133 HttpBaseChannel::SetResponseHeader(const nsACString& header, 2134 const nsACString& value, bool merge) { 2135 LOG( 2136 ("HttpBaseChannel::SetResponseHeader [this=%p header=\"%s\" value=\"%s\" " 2137 "merge=%u]\n", 2138 this, PromiseFlatCString(header).get(), PromiseFlatCString(value).get(), 2139 merge)); 2140 2141 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 2142 2143 nsHttpAtom atom = nsHttp::ResolveAtom(header); 2144 if (!atom) return NS_ERROR_NOT_AVAILABLE; 2145 2146 // these response headers must not be changed 2147 if (atom == nsHttp::Content_Type || atom == nsHttp::Content_Length || 2148 atom == nsHttp::Content_Encoding || atom == nsHttp::Trailer || 2149 atom == nsHttp::Transfer_Encoding) { 2150 return NS_ERROR_ILLEGAL_VALUE; 2151 } 2152 2153 StoreResponseHeadersModified(true); 2154 2155 return mResponseHead->SetHeader(header, value, merge); 2156 } 2157 2158 NS_IMETHODIMP 2159 HttpBaseChannel::VisitResponseHeaders(nsIHttpHeaderVisitor* visitor) { 2160 if (!mResponseHead) { 2161 return NS_ERROR_NOT_AVAILABLE; 2162 } 2163 return mResponseHead->VisitHeaders(visitor, 2164 nsHttpHeaderArray::eFilterResponse); 2165 } 2166 2167 NS_IMETHODIMP 2168 HttpBaseChannel::GetOriginalResponseHeader(const nsACString& aHeader, 2169 nsIHttpHeaderVisitor* aVisitor) { 2170 if (!mResponseHead) { 2171 return NS_ERROR_NOT_AVAILABLE; 2172 } 2173 2174 nsHttpAtom atom = nsHttp::ResolveAtom(aHeader); 2175 if (!atom) { 2176 return NS_ERROR_NOT_AVAILABLE; 2177 } 2178 2179 return mResponseHead->GetOriginalHeader(atom, aVisitor); 2180 } 2181 2182 NS_IMETHODIMP 2183 HttpBaseChannel::VisitOriginalResponseHeaders(nsIHttpHeaderVisitor* aVisitor) { 2184 if (!mResponseHead) { 2185 return NS_ERROR_NOT_AVAILABLE; 2186 } 2187 2188 return mResponseHead->VisitHeaders( 2189 aVisitor, nsHttpHeaderArray::eFilterResponseOriginal); 2190 } 2191 2192 NS_IMETHODIMP 2193 HttpBaseChannel::GetAllowSTS(bool* value) { 2194 NS_ENSURE_ARG_POINTER(value); 2195 *value = LoadAllowSTS(); 2196 return NS_OK; 2197 } 2198 2199 NS_IMETHODIMP 2200 HttpBaseChannel::SetAllowSTS(bool value) { 2201 ENSURE_CALLED_BEFORE_CONNECT(); 2202 StoreAllowSTS(value); 2203 return NS_OK; 2204 } 2205 2206 NS_IMETHODIMP 2207 HttpBaseChannel::GetIsOCSP(bool* value) { 2208 NS_ENSURE_ARG_POINTER(value); 2209 *value = LoadIsOCSP(); 2210 return NS_OK; 2211 } 2212 2213 NS_IMETHODIMP 2214 HttpBaseChannel::SetIsOCSP(bool value) { 2215 ENSURE_CALLED_BEFORE_CONNECT(); 2216 StoreIsOCSP(value); 2217 return NS_OK; 2218 } 2219 2220 NS_IMETHODIMP 2221 HttpBaseChannel::GetIsUserAgentHeaderModified(bool* value) { 2222 NS_ENSURE_ARG_POINTER(value); 2223 *value = LoadIsUserAgentHeaderModified(); 2224 return NS_OK; 2225 } 2226 2227 NS_IMETHODIMP 2228 HttpBaseChannel::SetIsUserAgentHeaderModified(bool value) { 2229 StoreIsUserAgentHeaderModified(value); 2230 return NS_OK; 2231 } 2232 2233 NS_IMETHODIMP 2234 HttpBaseChannel::GetRedirectionLimit(uint32_t* value) { 2235 NS_ENSURE_ARG_POINTER(value); 2236 *value = mRedirectionLimit; 2237 return NS_OK; 2238 } 2239 2240 NS_IMETHODIMP 2241 HttpBaseChannel::SetRedirectionLimit(uint32_t value) { 2242 ENSURE_CALLED_BEFORE_CONNECT(); 2243 2244 mRedirectionLimit = std::min<uint32_t>(value, 0xff); 2245 return NS_OK; 2246 } 2247 2248 nsresult HttpBaseChannel::OverrideSecurityInfo( 2249 nsITransportSecurityInfo* aSecurityInfo) { 2250 MOZ_ASSERT(!mSecurityInfo, 2251 "This can only be called when we don't have a security info " 2252 "object already"); 2253 MOZ_RELEASE_ASSERT( 2254 aSecurityInfo, 2255 "This can only be called with a valid security info object"); 2256 MOZ_ASSERT(!BypassServiceWorker(), 2257 "This can only be called on channels that are not bypassing " 2258 "interception"); 2259 MOZ_ASSERT(LoadResponseCouldBeSynthesized(), 2260 "This can only be called on channels that can be intercepted"); 2261 if (mSecurityInfo) { 2262 LOG( 2263 ("HttpBaseChannel::OverrideSecurityInfo mSecurityInfo is null! " 2264 "[this=%p]\n", 2265 this)); 2266 return NS_ERROR_UNEXPECTED; 2267 } 2268 if (!LoadResponseCouldBeSynthesized()) { 2269 LOG( 2270 ("HttpBaseChannel::OverrideSecurityInfo channel cannot be intercepted! " 2271 "[this=%p]\n", 2272 this)); 2273 return NS_ERROR_UNEXPECTED; 2274 } 2275 2276 mSecurityInfo = aSecurityInfo; 2277 return NS_OK; 2278 } 2279 2280 NS_IMETHODIMP 2281 HttpBaseChannel::IsNoStoreResponse(bool* value) { 2282 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 2283 *value = mResponseHead->NoStore(); 2284 return NS_OK; 2285 } 2286 2287 NS_IMETHODIMP 2288 HttpBaseChannel::IsNoCacheResponse(bool* value) { 2289 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 2290 *value = mResponseHead->NoCache(); 2291 if (!*value) *value = mResponseHead->ExpiresInPast(); 2292 return NS_OK; 2293 } 2294 2295 NS_IMETHODIMP 2296 HttpBaseChannel::GetResponseStatus(uint32_t* aValue) { 2297 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 2298 *aValue = mResponseHead->Status(); 2299 return NS_OK; 2300 } 2301 2302 NS_IMETHODIMP 2303 HttpBaseChannel::GetResponseStatusText(nsACString& aValue) { 2304 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 2305 nsAutoCString version; 2306 // https://fetch.spec.whatwg.org : 2307 // Responses over an HTTP/2 connection will always have the empty byte 2308 // sequence as status message as HTTP/2 does not support them. 2309 if (NS_WARN_IF(NS_FAILED(GetProtocolVersion(version))) || 2310 !version.EqualsLiteral("h2")) { 2311 mResponseHead->StatusText(aValue); 2312 } 2313 return NS_OK; 2314 } 2315 2316 NS_IMETHODIMP 2317 HttpBaseChannel::GetRequestSucceeded(bool* aValue) { 2318 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 2319 uint32_t status = mResponseHead->Status(); 2320 *aValue = (status / 100 == 2); 2321 return NS_OK; 2322 } 2323 2324 NS_IMETHODIMP 2325 HttpBaseChannel::RedirectTo(nsIURI* targetURI) { 2326 NS_ENSURE_ARG(targetURI); 2327 2328 nsAutoCString spec; 2329 targetURI->GetAsciiSpec(spec); 2330 LOG(("HttpBaseChannel::RedirectTo [this=%p, uri=%s]", this, spec.get())); 2331 LogCallingScriptLocation(this); 2332 2333 // We cannot redirect after OnStartRequest of the listener 2334 // has been called, since to redirect we have to switch channels 2335 // and the dance with OnStartRequest et al has to start over. 2336 // This would break the nsIStreamListener contract. 2337 NS_ENSURE_FALSE(LoadOnStartRequestCalled(), NS_ERROR_NOT_AVAILABLE); 2338 2339 // The first parameter is the URI we would like to redirect to 2340 // The second parameter should default to false if normal redirect 2341 mAPIRedirectTo = 2342 Some(mozilla::MakeCompactPair(nsCOMPtr<nsIURI>(targetURI), false)); 2343 2344 // Only Web Extensions are allowed to redirect a channel to a data: 2345 // URI. To avoid any bypasses after the channel was flagged by 2346 // the WebRequst API, we are dropping the flag here. 2347 mLoadInfo->SetAllowInsecureRedirectToDataURI(false); 2348 2349 // We may want to rewrite origin allowance, hence we need an 2350 // artificial response head. 2351 if (!mResponseHead) { 2352 mResponseHead.reset(new nsHttpResponseHead()); 2353 } 2354 return NS_OK; 2355 } 2356 2357 NS_IMETHODIMP 2358 HttpBaseChannel::TransparentRedirectTo(nsIURI* targetURI) { 2359 LOG(("HttpBaseChannel::TransparentRedirectTo [this=%p]", this)); 2360 RedirectTo(targetURI); 2361 MOZ_ASSERT(mAPIRedirectTo, "How did this happen?"); 2362 mAPIRedirectTo->second() = true; 2363 return NS_OK; 2364 } 2365 2366 NS_IMETHODIMP 2367 HttpBaseChannel::UpgradeToSecure() { 2368 // Upgrades are handled internally between http-on-modify-request and 2369 // http-on-before-connect, which means upgrades are only possible during 2370 // on-modify, or WebRequest.onBeforeRequest in Web Extensions. Once we are 2371 // past the code path where upgrades are handled, attempting an upgrade 2372 // will throw an error. 2373 NS_ENSURE_TRUE(LoadUpgradableToSecure(), NS_ERROR_NOT_AVAILABLE); 2374 2375 StoreUpgradeToSecure(true); 2376 // todo: Currently UpgradeToSecure() is called only by web extensions, if 2377 // that ever changes, we need to update the following telemetry collection 2378 // to reflect any future changes. 2379 mLoadInfo->SetHttpsUpgradeTelemetry(nsILoadInfo::WEB_EXTENSION_UPGRADE); 2380 2381 return NS_OK; 2382 } 2383 2384 NS_IMETHODIMP 2385 HttpBaseChannel::GetRequestObserversCalled(bool* aCalled) { 2386 NS_ENSURE_ARG_POINTER(aCalled); 2387 *aCalled = LoadRequestObserversCalled(); 2388 return NS_OK; 2389 } 2390 2391 NS_IMETHODIMP 2392 HttpBaseChannel::SetRequestObserversCalled(bool aCalled) { 2393 StoreRequestObserversCalled(aCalled); 2394 return NS_OK; 2395 } 2396 2397 NS_IMETHODIMP 2398 HttpBaseChannel::GetRequestContextID(uint64_t* aRCID) { 2399 NS_ENSURE_ARG_POINTER(aRCID); 2400 *aRCID = mRequestContextID; 2401 return NS_OK; 2402 } 2403 2404 NS_IMETHODIMP 2405 HttpBaseChannel::SetRequestContextID(uint64_t aRCID) { 2406 mRequestContextID = aRCID; 2407 return NS_OK; 2408 } 2409 2410 NS_IMETHODIMP 2411 HttpBaseChannel::GetIsMainDocumentChannel(bool* aValue) { 2412 NS_ENSURE_ARG_POINTER(aValue); 2413 *aValue = IsNavigation(); 2414 return NS_OK; 2415 } 2416 2417 NS_IMETHODIMP 2418 HttpBaseChannel::SetIsMainDocumentChannel(bool aValue) { 2419 StoreForceMainDocumentChannel(aValue); 2420 return NS_OK; 2421 } 2422 2423 NS_IMETHODIMP 2424 HttpBaseChannel::GetIsUserAgentHeaderOutdated(bool* aValue) { 2425 NS_ENSURE_ARG_POINTER(aValue); 2426 *aValue = LoadIsUserAgentHeaderOutdated(); 2427 return NS_OK; 2428 } 2429 2430 NS_IMETHODIMP 2431 HttpBaseChannel::SetIsUserAgentHeaderOutdated(bool aValue) { 2432 StoreIsUserAgentHeaderOutdated(aValue); 2433 return NS_OK; 2434 } 2435 2436 NS_IMETHODIMP 2437 HttpBaseChannel::GetProtocolVersion(nsACString& aProtocolVersion) { 2438 // Try to use ALPN if available and if it is not for a proxy, i.e if an 2439 // https proxy was not used or if https proxy was used but the connection to 2440 // the origin server is also https. In the case, an https proxy was used and 2441 // the connection to the origin server was http, mSecurityInfo will be from 2442 // the proxy. 2443 if (!mConnectionInfo || !mConnectionInfo->UsingHttpsProxy() || 2444 mConnectionInfo->EndToEndSSL()) { 2445 nsAutoCString protocol; 2446 if (mSecurityInfo && 2447 NS_SUCCEEDED(mSecurityInfo->GetNegotiatedNPN(protocol)) && 2448 !protocol.IsEmpty()) { 2449 // The negotiated protocol was not empty so we can use it. 2450 aProtocolVersion = protocol; 2451 return NS_OK; 2452 } 2453 } 2454 2455 if (mResponseHead) { 2456 HttpVersion version = mResponseHead->Version(); 2457 aProtocolVersion.Assign(nsHttp::GetProtocolVersion(version)); 2458 return NS_OK; 2459 } 2460 2461 return NS_ERROR_NOT_AVAILABLE; 2462 } 2463 2464 //----------------------------------------------------------------------------- 2465 // HttpBaseChannel::nsIHttpChannelInternal 2466 //----------------------------------------------------------------------------- 2467 2468 NS_IMETHODIMP 2469 HttpBaseChannel::SetTopWindowURIIfUnknown(nsIURI* aTopWindowURI) { 2470 if (!aTopWindowURI) { 2471 return NS_ERROR_INVALID_ARG; 2472 } 2473 2474 if (mTopWindowURI) { 2475 LOG( 2476 ("HttpChannelBase::SetTopWindowURIIfUnknown [this=%p] " 2477 "mTopWindowURI is already set.\n", 2478 this)); 2479 return NS_ERROR_FAILURE; 2480 } 2481 2482 nsCOMPtr<nsIURI> topWindowURI; 2483 (void)GetTopWindowURI(getter_AddRefs(topWindowURI)); 2484 2485 // Don't modify |mTopWindowURI| if we can get one from GetTopWindowURI(). 2486 if (topWindowURI) { 2487 LOG( 2488 ("HttpChannelBase::SetTopWindowURIIfUnknown [this=%p] " 2489 "Return an error since we got a top window uri.\n", 2490 this)); 2491 return NS_ERROR_FAILURE; 2492 } 2493 2494 mTopWindowURI = aTopWindowURI; 2495 return NS_OK; 2496 } 2497 2498 NS_IMETHODIMP 2499 HttpBaseChannel::GetTopWindowURI(nsIURI** aTopWindowURI) { 2500 nsCOMPtr<nsIURI> uriBeingLoaded = 2501 AntiTrackingUtils::MaybeGetDocumentURIBeingLoaded(this); 2502 return GetTopWindowURI(uriBeingLoaded, aTopWindowURI); 2503 } 2504 2505 nsresult HttpBaseChannel::GetTopWindowURI(nsIURI* aURIBeingLoaded, 2506 nsIURI** aTopWindowURI) { 2507 nsresult rv = NS_OK; 2508 nsCOMPtr<mozIThirdPartyUtil> util; 2509 // Only compute the top window URI once. In e10s, this must be computed in the 2510 // child. The parent gets the top window URI through HttpChannelOpenArgs. 2511 if (!mTopWindowURI) { 2512 util = components::ThirdPartyUtil::Service(); 2513 if (!util) { 2514 return NS_ERROR_NOT_AVAILABLE; 2515 } 2516 nsCOMPtr<mozIDOMWindowProxy> win; 2517 rv = util->GetTopWindowForChannel(this, aURIBeingLoaded, 2518 getter_AddRefs(win)); 2519 if (NS_SUCCEEDED(rv)) { 2520 rv = util->GetURIFromWindow(win, getter_AddRefs(mTopWindowURI)); 2521 #if DEBUG 2522 if (mTopWindowURI) { 2523 nsCString spec; 2524 if (NS_SUCCEEDED(mTopWindowURI->GetSpec(spec))) { 2525 LOG(("HttpChannelBase::Setting topwindow URI spec %s [this=%p]\n", 2526 spec.get(), this)); 2527 } 2528 } 2529 #endif 2530 } 2531 } 2532 *aTopWindowURI = do_AddRef(mTopWindowURI).take(); 2533 return rv; 2534 } 2535 2536 NS_IMETHODIMP 2537 HttpBaseChannel::GetDocumentURI(nsIURI** aDocumentURI) { 2538 NS_ENSURE_ARG_POINTER(aDocumentURI); 2539 *aDocumentURI = do_AddRef(mDocumentURI).take(); 2540 return NS_OK; 2541 } 2542 2543 NS_IMETHODIMP 2544 HttpBaseChannel::SetDocumentURI(nsIURI* aDocumentURI) { 2545 ENSURE_CALLED_BEFORE_CONNECT(); 2546 mDocumentURI = aDocumentURI; 2547 return NS_OK; 2548 } 2549 2550 NS_IMETHODIMP 2551 HttpBaseChannel::GetRequestVersion(uint32_t* major, uint32_t* minor) { 2552 HttpVersion version = mRequestHead.Version(); 2553 2554 if (major) { 2555 *major = static_cast<uint32_t>(version) / 10; 2556 } 2557 if (minor) { 2558 *minor = static_cast<uint32_t>(version) % 10; 2559 } 2560 2561 return NS_OK; 2562 } 2563 2564 NS_IMETHODIMP 2565 HttpBaseChannel::GetResponseVersion(uint32_t* major, uint32_t* minor) { 2566 if (!mResponseHead) { 2567 *major = *minor = 0; // we should at least be kind about it 2568 return NS_ERROR_NOT_AVAILABLE; 2569 } 2570 2571 HttpVersion version = mResponseHead->Version(); 2572 2573 if (major) { 2574 *major = static_cast<uint32_t>(version) / 10; 2575 } 2576 if (minor) { 2577 *minor = static_cast<uint32_t>(version) % 10; 2578 } 2579 2580 return NS_OK; 2581 } 2582 2583 bool HttpBaseChannel::IsBrowsingContextDiscarded() const { 2584 // If there is no loadGroup attached to the current channel, we check the 2585 // global private browsing state for the private channel instead. For 2586 // non-private channel, we will always return false here. 2587 // 2588 // Note that we can only access the global private browsing state in the 2589 // parent process. So, we will fallback to just return false in the content 2590 // process. 2591 if (!mLoadGroup) { 2592 if (!XRE_IsParentProcess()) { 2593 return false; 2594 } 2595 2596 return mLoadInfo->GetOriginAttributes().IsPrivateBrowsing() && 2597 !dom::CanonicalBrowsingContext::IsPrivateBrowsingActive(); 2598 } 2599 2600 return mLoadGroup->GetIsBrowsingContextDiscarded(); 2601 } 2602 2603 // https://mikewest.github.io/corpp/#process-navigation-response 2604 nsresult HttpBaseChannel::ProcessCrossOriginEmbedderPolicyHeader() { 2605 nsresult rv; 2606 if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) { 2607 return NS_OK; 2608 } 2609 2610 // Only consider Cross-Origin-Embedder-Policy for document loads. 2611 if (mLoadInfo->GetExternalContentPolicyType() != 2612 ExtContentPolicy::TYPE_DOCUMENT && 2613 mLoadInfo->GetExternalContentPolicyType() != 2614 ExtContentPolicy::TYPE_SUBDOCUMENT) { 2615 return NS_OK; 2616 } 2617 2618 nsILoadInfo::CrossOriginEmbedderPolicy resultPolicy = 2619 nsILoadInfo::EMBEDDER_POLICY_NULL; 2620 bool isCoepCredentiallessEnabled; 2621 rv = mLoadInfo->GetIsOriginTrialCoepCredentiallessEnabledForTopLevel( 2622 &isCoepCredentiallessEnabled); 2623 NS_ENSURE_SUCCESS(rv, rv); 2624 rv = GetResponseEmbedderPolicy(isCoepCredentiallessEnabled, &resultPolicy); 2625 if (NS_FAILED(rv)) { 2626 return NS_OK; 2627 } 2628 2629 // https://html.spec.whatwg.org/multipage/origin.html#coep 2630 if (mLoadInfo->GetExternalContentPolicyType() == 2631 ExtContentPolicy::TYPE_SUBDOCUMENT && 2632 !nsHttpChannel::IsRedirectStatus(mResponseHead->Status()) && 2633 mLoadInfo->GetLoadingEmbedderPolicy() != 2634 nsILoadInfo::EMBEDDER_POLICY_NULL && 2635 resultPolicy != nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP && 2636 resultPolicy != nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS) { 2637 return NS_ERROR_DOM_COEP_FAILED; 2638 } 2639 2640 return NS_OK; 2641 } 2642 2643 // https://mikewest.github.io/corpp/#corp-check 2644 nsresult HttpBaseChannel::ProcessCrossOriginResourcePolicyHeader() { 2645 // Fetch 4.5.9 2646 dom::RequestMode requestMode; 2647 MOZ_ALWAYS_SUCCEEDS(GetRequestMode(&requestMode)); 2648 // XXX this seems wrong per spec? What about navigate 2649 if (requestMode != RequestMode::No_cors) { 2650 return NS_OK; 2651 } 2652 2653 // We only apply this for resources. 2654 auto extContentPolicyType = mLoadInfo->GetExternalContentPolicyType(); 2655 if (extContentPolicyType == ExtContentPolicy::TYPE_DOCUMENT || 2656 extContentPolicyType == ExtContentPolicy::TYPE_WEBSOCKET || 2657 extContentPolicyType == ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD) { 2658 return NS_OK; 2659 } 2660 2661 if (extContentPolicyType == ExtContentPolicy::TYPE_SUBDOCUMENT) { 2662 // COEP pref off, skip CORP checking for subdocument. 2663 if (!StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) { 2664 return NS_OK; 2665 } 2666 // COEP 3.2.1.2 when request targets a nested browsing context then embedder 2667 // policy value is "unsafe-none", then return allowed. 2668 if (mLoadInfo->GetLoadingEmbedderPolicy() == 2669 nsILoadInfo::EMBEDDER_POLICY_NULL) { 2670 return NS_OK; 2671 } 2672 } 2673 2674 MOZ_ASSERT(mLoadInfo->GetLoadingPrincipal(), 2675 "Resources should always have a LoadingPrincipal"); 2676 if (!mResponseHead) { 2677 return NS_OK; 2678 } 2679 2680 if (mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()) { 2681 return NS_OK; 2682 } 2683 2684 nsAutoCString content; 2685 (void)mResponseHead->GetHeader(nsHttp::Cross_Origin_Resource_Policy, content); 2686 2687 if (StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) { 2688 if (content.IsEmpty()) { 2689 if (mLoadInfo->GetLoadingEmbedderPolicy() == 2690 nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS) { 2691 bool requestIncludesCredentials = false; 2692 nsresult rv = GetCorsIncludeCredentials(&requestIncludesCredentials); 2693 if (NS_FAILED(rv)) { 2694 return NS_OK; 2695 } 2696 // COEP: Set policy to `same-origin` if: response’s 2697 // request-includes-credentials is true, or forNavigation is true. 2698 if (requestIncludesCredentials || 2699 extContentPolicyType == ExtContentPolicyType::TYPE_SUBDOCUMENT) { 2700 content = "same-origin"_ns; 2701 } 2702 } else if (mLoadInfo->GetLoadingEmbedderPolicy() == 2703 nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP) { 2704 // COEP 3.2.1.6 If policy is null, and embedder policy is 2705 // "require-corp", set policy to "same-origin". Note that we treat 2706 // invalid value as "cross-origin", which spec indicates. We might want 2707 // to make that stricter. 2708 content = "same-origin"_ns; 2709 } 2710 } 2711 } 2712 2713 if (content.IsEmpty()) { 2714 return NS_OK; 2715 } 2716 2717 nsCOMPtr<nsIPrincipal> channelOrigin; 2718 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( 2719 this, getter_AddRefs(channelOrigin)); 2720 2721 // Cross-Origin-Resource-Policy = %s"same-origin" / %s"same-site" / 2722 // %s"cross-origin" 2723 if (content.EqualsLiteral("same-origin")) { 2724 if (!channelOrigin->Equals(mLoadInfo->GetLoadingPrincipal())) { 2725 return NS_ERROR_DOM_CORP_FAILED; 2726 } 2727 return NS_OK; 2728 } 2729 if (content.EqualsLiteral("same-site")) { 2730 nsAutoCString documentBaseDomain; 2731 nsAutoCString resourceBaseDomain; 2732 mLoadInfo->GetLoadingPrincipal()->GetBaseDomain(documentBaseDomain); 2733 channelOrigin->GetBaseDomain(resourceBaseDomain); 2734 if (documentBaseDomain != resourceBaseDomain) { 2735 return NS_ERROR_DOM_CORP_FAILED; 2736 } 2737 2738 nsCOMPtr<nsIURI> resourceURI = channelOrigin->GetURI(); 2739 if (!mLoadInfo->GetLoadingPrincipal()->SchemeIs("https") && 2740 resourceURI->SchemeIs("https")) { 2741 return NS_ERROR_DOM_CORP_FAILED; 2742 } 2743 2744 return NS_OK; 2745 } 2746 2747 return NS_OK; 2748 } 2749 2750 // See https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e 2751 // This method runs steps 1-4 of the algorithm to compare 2752 // cross-origin-opener policies 2753 static bool CompareCrossOriginOpenerPolicies( 2754 nsILoadInfo::CrossOriginOpenerPolicy documentPolicy, 2755 nsIPrincipal* documentOrigin, 2756 nsILoadInfo::CrossOriginOpenerPolicy resultPolicy, 2757 nsIPrincipal* resultOrigin) { 2758 if (documentPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE && 2759 resultPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE) { 2760 return true; 2761 } 2762 2763 if (documentPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE || 2764 resultPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE) { 2765 return false; 2766 } 2767 2768 if (documentPolicy == resultPolicy && documentOrigin->Equals(resultOrigin)) { 2769 return true; 2770 } 2771 2772 return false; 2773 } 2774 2775 // This runs steps 1-5 of the algorithm when navigating a top level document. 2776 // See https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e 2777 nsresult HttpBaseChannel::ComputeCrossOriginOpenerPolicyMismatch() { 2778 MOZ_ASSERT(XRE_IsParentProcess()); 2779 2780 StoreHasCrossOriginOpenerPolicyMismatch(false); 2781 if (!StaticPrefs::browser_tabs_remote_useCrossOriginOpenerPolicy()) { 2782 return NS_OK; 2783 } 2784 2785 // Only consider Cross-Origin-Opener-Policy for toplevel document loads. 2786 if (mLoadInfo->GetExternalContentPolicyType() != 2787 ExtContentPolicy::TYPE_DOCUMENT) { 2788 return NS_OK; 2789 } 2790 2791 // Maybe the channel failed and we have no response head? 2792 if (!mResponseHead) { 2793 // Not having a response head is not a hard failure at the point where 2794 // this method is called. 2795 return NS_OK; 2796 } 2797 2798 RefPtr<mozilla::dom::BrowsingContext> ctx; 2799 mLoadInfo->GetBrowsingContext(getter_AddRefs(ctx)); 2800 2801 // In xpcshell-tests we don't always have a browsingContext 2802 if (!ctx) { 2803 return NS_OK; 2804 } 2805 2806 nsCOMPtr<nsIPrincipal> resultOrigin; 2807 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( 2808 this, getter_AddRefs(resultOrigin)); 2809 2810 // Get the policy of the active document, and the policy for the result. 2811 nsILoadInfo::CrossOriginOpenerPolicy documentPolicy = ctx->GetOpenerPolicy(); 2812 nsILoadInfo::CrossOriginOpenerPolicy resultPolicy = 2813 nsILoadInfo::OPENER_POLICY_UNSAFE_NONE; 2814 (void)ComputeCrossOriginOpenerPolicy(documentPolicy, &resultPolicy); 2815 mComputedCrossOriginOpenerPolicy = resultPolicy; 2816 2817 // Add a permission to mark this site as high-value into the permission DB. 2818 if (resultPolicy != nsILoadInfo::OPENER_POLICY_UNSAFE_NONE) { 2819 mozilla::dom::AddHighValuePermission( 2820 resultOrigin, mozilla::dom::kHighValueCOOPPermission); 2821 } 2822 2823 // If bc's popup sandboxing flag set is not empty and potentialCOOP is 2824 // non-null, then navigate bc to a network error and abort these steps. 2825 if (resultPolicy != nsILoadInfo::OPENER_POLICY_UNSAFE_NONE && 2826 mLoadInfo->GetSandboxFlags()) { 2827 LOG(( 2828 "HttpBaseChannel::ComputeCrossOriginOpenerPolicyMismatch network error " 2829 "for non empty sandboxing and non null COOP")); 2830 return NS_ERROR_DOM_COOP_FAILED; 2831 } 2832 2833 // In xpcshell-tests we don't always have a current window global 2834 RefPtr<mozilla::dom::WindowGlobalParent> currentWindowGlobal = 2835 ctx->Canonical()->GetCurrentWindowGlobal(); 2836 if (!currentWindowGlobal) { 2837 return NS_OK; 2838 } 2839 2840 // We use the top window principal as the documentOrigin 2841 nsCOMPtr<nsIPrincipal> documentOrigin = 2842 currentWindowGlobal->DocumentPrincipal(); 2843 2844 bool compareResult = CompareCrossOriginOpenerPolicies( 2845 documentPolicy, documentOrigin, resultPolicy, resultOrigin); 2846 2847 if (LOG_ENABLED()) { 2848 LOG( 2849 ("HttpBaseChannel::HasCrossOriginOpenerPolicyMismatch - " 2850 "doc:%d result:%d - compare:%d\n", 2851 documentPolicy, resultPolicy, compareResult)); 2852 nsAutoCString docOrigin("(null)"); 2853 nsCOMPtr<nsIURI> uri = documentOrigin->GetURI(); 2854 if (uri) { 2855 uri->GetSpec(docOrigin); 2856 } 2857 nsAutoCString resOrigin("(null)"); 2858 uri = resultOrigin->GetURI(); 2859 if (uri) { 2860 uri->GetSpec(resOrigin); 2861 } 2862 LOG(("doc origin:%s - res origin: %s\n", docOrigin.get(), resOrigin.get())); 2863 } 2864 2865 if (compareResult) { 2866 return NS_OK; 2867 } 2868 2869 // If one of the following is false: 2870 // - document's policy is same-origin-allow-popups 2871 // - resultPolicy is null 2872 // - doc is the initial about:blank document 2873 // then we have a mismatch. 2874 2875 if (documentPolicy != nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS) { 2876 StoreHasCrossOriginOpenerPolicyMismatch(true); 2877 return NS_OK; 2878 } 2879 2880 if (resultPolicy != nsILoadInfo::OPENER_POLICY_UNSAFE_NONE) { 2881 StoreHasCrossOriginOpenerPolicyMismatch(true); 2882 return NS_OK; 2883 } 2884 2885 if (!currentWindowGlobal->IsInitialDocument()) { 2886 StoreHasCrossOriginOpenerPolicyMismatch(true); 2887 return NS_OK; 2888 } 2889 2890 return NS_OK; 2891 } 2892 2893 nsresult HttpBaseChannel::ProcessCrossOriginSecurityHeaders() { 2894 StoreProcessCrossOriginSecurityHeadersCalled(true); 2895 nsresult rv = ProcessCrossOriginEmbedderPolicyHeader(); 2896 if (NS_FAILED(rv)) { 2897 return rv; 2898 } 2899 rv = ProcessCrossOriginResourcePolicyHeader(); 2900 if (NS_FAILED(rv)) { 2901 return rv; 2902 } 2903 return ComputeCrossOriginOpenerPolicyMismatch(); 2904 } 2905 2906 enum class Report { Error, Warning }; 2907 2908 // Helper Function to report messages to the console when the loaded 2909 // script had a wrong MIME type. 2910 void ReportMimeTypeMismatch(HttpBaseChannel* aChannel, const char* aMessageName, 2911 nsIURI* aURI, const nsACString& aContentType, 2912 Report report) { 2913 NS_ConvertUTF8toUTF16 spec(aURI->GetSpecOrDefault()); 2914 NS_ConvertUTF8toUTF16 contentType(aContentType); 2915 2916 aChannel->LogMimeTypeMismatch(nsCString(aMessageName), 2917 report == Report::Warning, spec, contentType); 2918 } 2919 2920 // Check and potentially enforce X-Content-Type-Options: nosniff 2921 nsresult ProcessXCTO(HttpBaseChannel* aChannel, nsIURI* aURI, 2922 nsHttpResponseHead* aResponseHead, 2923 nsILoadInfo* aLoadInfo) { 2924 if (!aURI || !aResponseHead || !aLoadInfo) { 2925 // if there is no uri, no response head or no loadInfo, then there is 2926 // nothing to do 2927 return NS_OK; 2928 } 2929 2930 // 1) Query the XCTO header and check if 'nosniff' is the first value. 2931 nsAutoCString contentTypeOptionsHeader; 2932 if (!aResponseHead->GetContentTypeOptionsHeader(contentTypeOptionsHeader)) { 2933 // if failed to get XCTO header, then there is nothing to do. 2934 return NS_OK; 2935 } 2936 2937 // let's compare the header (ignoring case) 2938 // e.g. "NoSniFF" -> "nosniff" 2939 // if it's not 'nosniff' then there is nothing to do here 2940 if (!contentTypeOptionsHeader.EqualsIgnoreCase("nosniff")) { 2941 // since we are getting here, the XCTO header was sent; 2942 // a non matching value most likely means a mistake happenend; 2943 // e.g. sending 'nosnif' instead of 'nosniff', let's log a warning. 2944 AutoTArray<nsString, 1> params; 2945 CopyUTF8toUTF16(contentTypeOptionsHeader, *params.AppendElement()); 2946 RefPtr<dom::Document> doc; 2947 aLoadInfo->GetLoadingDocument(getter_AddRefs(doc)); 2948 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "XCTO"_ns, doc, 2949 nsContentUtils::eSECURITY_PROPERTIES, 2950 "XCTOHeaderValueMissing", params); 2951 return NS_OK; 2952 } 2953 2954 // 2) Query the content type from the channel 2955 nsAutoCString contentType; 2956 aResponseHead->ContentType(contentType); 2957 2958 // 3) Compare the expected MIME type with the actual type 2959 if (aLoadInfo->GetExternalContentPolicyType() == 2960 ExtContentPolicy::TYPE_STYLESHEET) { 2961 if (contentType.EqualsLiteral(TEXT_CSS)) { 2962 return NS_OK; 2963 } 2964 ReportMimeTypeMismatch(aChannel, "MimeTypeMismatch2", aURI, contentType, 2965 Report::Error); 2966 return NS_ERROR_CORRUPTED_CONTENT; 2967 } 2968 2969 if (aLoadInfo->GetExternalContentPolicyType() == 2970 ExtContentPolicy::TYPE_SCRIPT) { 2971 if (nsContentUtils::IsJavascriptMIMEType( 2972 NS_ConvertUTF8toUTF16(contentType))) { 2973 return NS_OK; 2974 } 2975 ReportMimeTypeMismatch(aChannel, "MimeTypeMismatch2", aURI, contentType, 2976 Report::Error); 2977 return NS_ERROR_CORRUPTED_CONTENT; 2978 } 2979 2980 auto policyType = aLoadInfo->GetExternalContentPolicyType(); 2981 if (policyType == ExtContentPolicy::TYPE_DOCUMENT || 2982 policyType == ExtContentPolicy::TYPE_SUBDOCUMENT || 2983 policyType == ExtContentPolicy::TYPE_OBJECT) { 2984 // If the header XCTO nosniff is set for any browsing context, then 2985 // we set the skipContentSniffing flag on the Loadinfo. Within 2986 // GetMIMETypeFromContent we then bail early and do not do any sniffing. 2987 aLoadInfo->SetSkipContentSniffing(true); 2988 return NS_OK; 2989 } 2990 2991 return NS_OK; 2992 } 2993 2994 nsresult EnsureMIMEOfJSONModule(HttpBaseChannel* aChannel, nsIURI* aURI, 2995 nsHttpResponseHead* aResponseHead, 2996 nsILoadInfo* aLoadInfo) { 2997 if (!aURI || !aResponseHead || !aLoadInfo) { 2998 // if there is no uri, no response head or no loadInfo, then there is 2999 // nothing to do 3000 return NS_OK; 3001 } 3002 3003 if (aLoadInfo->GetExternalContentPolicyType() != 3004 ExtContentPolicy::TYPE_JSON) { 3005 // if this is not a JSON load, then there is nothing to do 3006 return NS_OK; 3007 } 3008 3009 nsAutoCString contentType; 3010 aResponseHead->ContentType(contentType); 3011 NS_ConvertUTF8toUTF16 typeString(contentType); 3012 3013 if (nsContentUtils::IsJsonMimeType(typeString)) { 3014 return NS_OK; 3015 } 3016 3017 ReportMimeTypeMismatch(aChannel, "BlockJsonModuleWithWrongMimeType", aURI, 3018 contentType, Report::Error); 3019 return NS_ERROR_CORRUPTED_CONTENT; 3020 } 3021 3022 // Ensure that a load of type script has correct MIME type 3023 nsresult EnsureMIMEOfScript(HttpBaseChannel* aChannel, nsIURI* aURI, 3024 nsHttpResponseHead* aResponseHead, 3025 nsILoadInfo* aLoadInfo) { 3026 if (!aURI || !aResponseHead || !aLoadInfo) { 3027 // if there is no uri, no response head or no loadInfo, then there is 3028 // nothing to do 3029 return NS_OK; 3030 } 3031 3032 if (aLoadInfo->GetExternalContentPolicyType() != 3033 ExtContentPolicy::TYPE_SCRIPT) { 3034 // if this is not a script load, then there is nothing to do 3035 return NS_OK; 3036 } 3037 3038 nsAutoCString contentType; 3039 aResponseHead->ContentType(contentType); 3040 NS_ConvertUTF8toUTF16 typeString(contentType); 3041 3042 if (nsContentUtils::IsJavascriptMIMEType(typeString)) { 3043 // script load has type script 3044 glean::http::script_block_incorrect_mime 3045 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eJavascript) 3046 .Add(); 3047 return NS_OK; 3048 } 3049 3050 const auto internalPolicyType = aLoadInfo->InternalContentPolicyType(); 3051 if (internalPolicyType == 3052 nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE && 3053 nsContentUtils::IsJsonMimeType(typeString)) { 3054 // script and json are both allowed 3055 glean::http::script_block_incorrect_mime 3056 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextJson) 3057 .Add(); 3058 return NS_OK; 3059 } 3060 3061 switch (aLoadInfo->InternalContentPolicyType()) { 3062 case nsIContentPolicy::TYPE_SCRIPT: 3063 case nsIContentPolicy::TYPE_INTERNAL_SCRIPT: 3064 case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD: 3065 case nsIContentPolicy::TYPE_INTERNAL_MODULE: 3066 case nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD: 3067 case nsIContentPolicy::TYPE_INTERNAL_CHROMEUTILS_COMPILED_SCRIPT: 3068 case nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT: 3069 glean::http::script_block_incorrect_mime 3070 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eScriptLoad) 3071 .Add(); 3072 break; 3073 case nsIContentPolicy::TYPE_INTERNAL_WORKER: 3074 case nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE: 3075 case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER: 3076 glean::http::script_block_incorrect_mime 3077 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eWorkerLoad) 3078 .Add(); 3079 break; 3080 case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER: 3081 glean::http::script_block_incorrect_mime 3082 .EnumGet( 3083 glean::http::ScriptBlockIncorrectMimeLabel::eServiceworkerLoad) 3084 .Add(); 3085 break; 3086 case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS: 3087 glean::http::script_block_incorrect_mime 3088 .EnumGet( 3089 glean::http::ScriptBlockIncorrectMimeLabel::eImportscriptLoad) 3090 .Add(); 3091 break; 3092 case nsIContentPolicy::TYPE_INTERNAL_AUDIOWORKLET: 3093 case nsIContentPolicy::TYPE_INTERNAL_PAINTWORKLET: 3094 glean::http::script_block_incorrect_mime 3095 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eWorkletLoad) 3096 .Add(); 3097 break; 3098 default: 3099 MOZ_ASSERT_UNREACHABLE("unexpected script type"); 3100 break; 3101 } 3102 3103 if (aLoadInfo->GetLoadingPrincipal()->IsSameOrigin(aURI)) { 3104 // same origin 3105 glean::http::script_block_incorrect_mime 3106 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eSameOrigin) 3107 .Add(); 3108 } else { 3109 bool cors = false; 3110 nsAutoCString corsOrigin; 3111 nsresult rv = aResponseHead->GetHeader( 3112 nsHttp::ResolveAtom("Access-Control-Allow-Origin"_ns), corsOrigin); 3113 if (NS_SUCCEEDED(rv)) { 3114 if (corsOrigin.Equals("*")) { 3115 cors = true; 3116 } else { 3117 nsCOMPtr<nsIURI> corsOriginURI; 3118 rv = NS_NewURI(getter_AddRefs(corsOriginURI), corsOrigin); 3119 if (NS_SUCCEEDED(rv)) { 3120 if (aLoadInfo->GetLoadingPrincipal()->IsSameOrigin(corsOriginURI)) { 3121 cors = true; 3122 } 3123 } 3124 } 3125 } 3126 if (cors) { 3127 // cors origin 3128 glean::http::script_block_incorrect_mime 3129 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eCorsOrigin) 3130 .Add(); 3131 } else { 3132 // cross origin 3133 glean::http::script_block_incorrect_mime 3134 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eCrossOrigin) 3135 .Add(); 3136 } 3137 } 3138 3139 bool block = false; 3140 if (StringBeginsWith(contentType, "image/"_ns)) { 3141 // script load has type image 3142 glean::http::script_block_incorrect_mime 3143 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eImage) 3144 .Add(); 3145 block = true; 3146 } else if (StringBeginsWith(contentType, "audio/"_ns)) { 3147 // script load has type audio 3148 glean::http::script_block_incorrect_mime 3149 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eAudio) 3150 .Add(); 3151 block = true; 3152 } else if (StringBeginsWith(contentType, "video/"_ns)) { 3153 // script load has type video 3154 glean::http::script_block_incorrect_mime 3155 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eVideo) 3156 .Add(); 3157 block = true; 3158 } else if (StringBeginsWith(contentType, "text/csv"_ns)) { 3159 // script load has type text/csv 3160 glean::http::script_block_incorrect_mime 3161 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextCsv) 3162 .Add(); 3163 block = true; 3164 } 3165 3166 if (block) { 3167 ReportMimeTypeMismatch(aChannel, "BlockScriptWithWrongMimeType2", aURI, 3168 contentType, Report::Error); 3169 return NS_ERROR_CORRUPTED_CONTENT; 3170 } 3171 3172 if (StringBeginsWith(contentType, "text/plain"_ns)) { 3173 // script load has type text/plain 3174 glean::http::script_block_incorrect_mime 3175 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextPlain) 3176 .Add(); 3177 } else if (StringBeginsWith(contentType, "text/xml"_ns)) { 3178 // script load has type text/xml 3179 glean::http::script_block_incorrect_mime 3180 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextXml) 3181 .Add(); 3182 } else if (StringBeginsWith(contentType, "application/octet-stream"_ns)) { 3183 // script load has type application/octet-stream 3184 glean::http::script_block_incorrect_mime 3185 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eAppOctetStream) 3186 .Add(); 3187 } else if (StringBeginsWith(contentType, "application/xml"_ns)) { 3188 // script load has type application/xml 3189 glean::http::script_block_incorrect_mime 3190 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eAppXml) 3191 .Add(); 3192 } else if (StringBeginsWith(contentType, "application/json"_ns)) { 3193 // script load has type application/json 3194 glean::http::script_block_incorrect_mime 3195 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eAppJson) 3196 .Add(); 3197 } else if (StringBeginsWith(contentType, "text/json"_ns)) { 3198 // script load has type text/json 3199 glean::http::script_block_incorrect_mime 3200 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextJson) 3201 .Add(); 3202 } else if (StringBeginsWith(contentType, "text/html"_ns)) { 3203 // script load has type text/html 3204 glean::http::script_block_incorrect_mime 3205 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eTextHtml) 3206 .Add(); 3207 } else if (contentType.IsEmpty()) { 3208 // script load has no type 3209 glean::http::script_block_incorrect_mime 3210 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eEmpty) 3211 .Add(); 3212 } else { 3213 // script load has unknown type 3214 glean::http::script_block_incorrect_mime 3215 .EnumGet(glean::http::ScriptBlockIncorrectMimeLabel::eUnknown) 3216 .Add(); 3217 } 3218 3219 nsContentPolicyType internalType = aLoadInfo->InternalContentPolicyType(); 3220 3221 // We restrict importScripts() in worker code to JavaScript MIME types. 3222 if (internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS || 3223 internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER_STATIC_MODULE) { 3224 ReportMimeTypeMismatch(aChannel, "BlockImportScriptsWithWrongMimeType", 3225 aURI, contentType, Report::Error); 3226 return NS_ERROR_CORRUPTED_CONTENT; 3227 } 3228 3229 if (internalType == nsIContentPolicy::TYPE_INTERNAL_WORKER || 3230 internalType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER) { 3231 // Do not block the load if the feature is not enabled. 3232 if (!StaticPrefs::security_block_Worker_with_wrong_mime()) { 3233 return NS_OK; 3234 } 3235 3236 #ifdef NIGHTLY_BUILD 3237 if (StaticPrefs::javascript_options_experimental_wasm_esm_integration()) { 3238 if (nsContentUtils::HasWasmMimeTypeEssence(typeString)) { 3239 return NS_OK; 3240 } 3241 } 3242 #endif 3243 3244 ReportMimeTypeMismatch(aChannel, "BlockWorkerWithWrongMimeType", aURI, 3245 contentType, Report::Error); 3246 return NS_ERROR_CORRUPTED_CONTENT; 3247 } 3248 3249 // ES6 modules require a strict MIME type check. 3250 if (internalType == nsIContentPolicy::TYPE_INTERNAL_MODULE || 3251 internalType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD) { 3252 #ifdef NIGHTLY_BUILD 3253 if (StaticPrefs::javascript_options_experimental_wasm_esm_integration()) { 3254 if (nsContentUtils::HasWasmMimeTypeEssence(typeString)) { 3255 return NS_OK; 3256 } 3257 } 3258 #endif 3259 3260 ReportMimeTypeMismatch(aChannel, "BlockModuleWithWrongMimeType", aURI, 3261 contentType, Report::Error); 3262 return NS_ERROR_CORRUPTED_CONTENT; 3263 } 3264 3265 return NS_OK; 3266 } 3267 3268 // Warn when a load of type script uses a wrong MIME type and 3269 // wasn't blocked by EnsureMIMEOfScript or ProcessXCTO. 3270 void WarnWrongMIMEOfScript(HttpBaseChannel* aChannel, nsIURI* aURI, 3271 nsHttpResponseHead* aResponseHead, 3272 nsILoadInfo* aLoadInfo) { 3273 if (!aURI || !aResponseHead || !aLoadInfo) { 3274 // If there is no uri, no response head or no loadInfo, then there is 3275 // nothing to do. 3276 return; 3277 } 3278 3279 if (aLoadInfo->GetExternalContentPolicyType() != 3280 ExtContentPolicy::TYPE_SCRIPT) { 3281 // If this is not a script load, then there is nothing to do. 3282 return; 3283 } 3284 3285 bool succeeded; 3286 MOZ_ALWAYS_SUCCEEDS(aChannel->GetRequestSucceeded(&succeeded)); 3287 if (!succeeded) { 3288 // Do not warn for failed loads: HTTP error pages are usually in HTML. 3289 return; 3290 } 3291 3292 nsAutoCString contentType; 3293 aResponseHead->ContentType(contentType); 3294 NS_ConvertUTF8toUTF16 typeString(contentType); 3295 3296 if (nsContentUtils::IsJavascriptMIMEType(typeString)) { 3297 return; 3298 } 3299 3300 #ifdef NIGHTLY_BUILD 3301 if (StaticPrefs::javascript_options_experimental_wasm_esm_integration()) { 3302 if (nsContentUtils::HasWasmMimeTypeEssence(typeString)) { 3303 return; 3304 } 3305 } 3306 #endif 3307 3308 ReportMimeTypeMismatch(aChannel, "WarnScriptWithWrongMimeType", aURI, 3309 contentType, Report::Warning); 3310 } 3311 3312 nsresult HttpBaseChannel::ValidateMIMEType() { 3313 nsresult rv = EnsureMIMEOfScript(this, mURI, mResponseHead.get(), mLoadInfo); 3314 if (NS_FAILED(rv)) { 3315 return rv; 3316 } 3317 3318 rv = EnsureMIMEOfJSONModule(this, mURI, mResponseHead.get(), mLoadInfo); 3319 if (NS_FAILED(rv)) { 3320 return rv; 3321 } 3322 3323 rv = ProcessXCTO(this, mURI, mResponseHead.get(), mLoadInfo); 3324 if (NS_FAILED(rv)) { 3325 return rv; 3326 } 3327 3328 WarnWrongMIMEOfScript(this, mURI, mResponseHead.get(), mLoadInfo); 3329 return NS_OK; 3330 } 3331 3332 bool HttpBaseChannel::ShouldFilterOpaqueResponse( 3333 OpaqueResponseFilterFetch aFilterType) const { 3334 MOZ_ASSERT(ShouldBlockOpaqueResponse()); 3335 3336 if (!mLoadInfo || ConfiguredFilterFetchResponseBehaviour() != aFilterType) { 3337 return false; 3338 } 3339 3340 // We should filter a response in the parent if it is opaque and is the result 3341 // of a fetch() function from the Fetch specification. 3342 return mLoadInfo->InternalContentPolicyType() == nsIContentPolicy::TYPE_FETCH; 3343 } 3344 3345 bool HttpBaseChannel::ShouldBlockOpaqueResponse() const { 3346 if (!mURI || !mResponseHead || !mLoadInfo) { 3347 // if there is no uri, no response head or no loadInfo, then there is 3348 // nothing to do 3349 LOGORB("No block: no mURI, mResponseHead, or mLoadInfo"); 3350 return false; 3351 } 3352 3353 nsCOMPtr<nsIPrincipal> principal = mLoadInfo->GetLoadingPrincipal(); 3354 if (!principal || principal->IsSystemPrincipal()) { 3355 // If it's a top-level load or a system principal, then there is nothing to 3356 // do. 3357 LOGORB("No block: top-level load or system principal"); 3358 return false; 3359 } 3360 3361 // Check if the response is a opaque response, which means requestMode should 3362 // be RequestMode::No_cors and responseType should be ResponseType::Opaque. 3363 nsContentPolicyType contentPolicy = mLoadInfo->InternalContentPolicyType(); 3364 3365 // Skip the RequestMode would be RequestMode::Navigate 3366 if (contentPolicy == nsIContentPolicy::TYPE_DOCUMENT || 3367 contentPolicy == nsIContentPolicy::TYPE_SUBDOCUMENT || 3368 contentPolicy == nsIContentPolicy::TYPE_INTERNAL_FRAME || 3369 contentPolicy == nsIContentPolicy::TYPE_INTERNAL_IFRAME || 3370 // Skip the RequestMode would be RequestMode::Same_origin 3371 contentPolicy == nsIContentPolicy::TYPE_INTERNAL_WORKER || 3372 contentPolicy == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER) { 3373 return false; 3374 } 3375 3376 uint32_t securityMode = mLoadInfo->GetSecurityMode(); 3377 // Skip when RequestMode would not be RequestMode::no_cors 3378 if (securityMode != 3379 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT && 3380 securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL) { 3381 LOGORB("No block: not no_cors requests"); 3382 return false; 3383 } 3384 3385 // Only continue when ResponseType would be ResponseType::Opaque 3386 if (mLoadInfo->GetTainting() != mozilla::LoadTainting::Opaque) { 3387 LOGORB("No block: not opaque response"); 3388 return false; 3389 } 3390 3391 auto extContentPolicyType = mLoadInfo->GetExternalContentPolicyType(); 3392 if (extContentPolicyType == ExtContentPolicy::TYPE_OBJECT || 3393 extContentPolicyType == ExtContentPolicy::TYPE_WEBSOCKET || 3394 extContentPolicyType == ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD) { 3395 LOGORB("No block: object || websocket request || save as download"); 3396 return false; 3397 } 3398 3399 // Ignore the request from object or embed elements 3400 if (mLoadInfo->GetIsFromObjectOrEmbed()) { 3401 LOGORB("No block: Request From <object> or <embed>"); 3402 return false; 3403 } 3404 3405 // Exclude no_cors System XHR 3406 if (extContentPolicyType == ExtContentPolicy::TYPE_XMLHTTPREQUEST) { 3407 if (securityMode == 3408 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT) { 3409 LOGORB("No block: System XHR"); 3410 return false; 3411 } 3412 } 3413 3414 // Exclude no_cors web-identity 3415 if (extContentPolicyType == ExtContentPolicy::TYPE_WEB_IDENTITY) { 3416 if (securityMode == 3417 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT) { 3418 printf("Allowing ORB for web-identity\n"); 3419 LOGORB("No block: System web-identity"); 3420 return false; 3421 } 3422 } 3423 3424 uint32_t httpsOnlyStatus = mLoadInfo->GetHttpsOnlyStatus(); 3425 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_BYPASS_ORB) { 3426 LOGORB("No block: HTTPS_ONLY_BYPASS_ORB"); 3427 return false; 3428 } 3429 3430 bool isInDevToolsContext; 3431 mLoadInfo->GetIsInDevToolsContext(&isInDevToolsContext); 3432 if (isInDevToolsContext) { 3433 LOGORB("No block: Request created by devtools"); 3434 return false; 3435 } 3436 3437 return true; 3438 } 3439 3440 OpaqueResponse HttpBaseChannel::BlockOrFilterOpaqueResponse( 3441 OpaqueResponseBlocker* aORB, const nsAString& aReason, 3442 const OpaqueResponseBlockedTelemetryReason aTelemetryReason, 3443 const char* aFormat, ...) { 3444 NimbusFeatures::RecordExposureEvent("opaqueResponseBlocking"_ns, true); 3445 3446 const bool shouldFilter = 3447 ShouldFilterOpaqueResponse(OpaqueResponseFilterFetch::BlockedByORB); 3448 3449 if (MOZ_UNLIKELY(MOZ_LOG_TEST(GetORBLog(), LogLevel::Debug))) { 3450 va_list ap; 3451 va_start(ap, aFormat); 3452 nsVprintfCString logString(aFormat, ap); 3453 va_end(ap); 3454 3455 LOGORB("%s: %s", shouldFilter ? "Filtered" : "Blocked", logString.get()); 3456 } 3457 3458 if (shouldFilter) { 3459 glean::orb::block_initiator 3460 .EnumGet(glean::orb::BlockInitiatorLabel::eFilteredFetch) 3461 .Add(); 3462 // The existence of `mORB` depends on `BlockOrFilterOpaqueResponse` being 3463 // called before or after sniffing has completed. 3464 // Another requirement is that `OpaqueResponseFilter` must come after 3465 // `OpaqueResponseBlocker`, which is why in the case of having an 3466 // `OpaqueResponseBlocker` we let it handle creating an 3467 // `OpaqueResponseFilter`. 3468 if (aORB) { 3469 MOZ_DIAGNOSTIC_ASSERT(!mORB || aORB == mORB); 3470 aORB->FilterResponse(); 3471 } else { 3472 mListener = new OpaqueResponseFilter(mListener); 3473 } 3474 return OpaqueResponse::Allow; 3475 } 3476 3477 LogORBError(aReason, aTelemetryReason); 3478 return OpaqueResponse::Block; 3479 } 3480 3481 // The specification for ORB is currently being written: 3482 // https://whatpr.org/fetch/1442.html#orb-algorithm 3483 // The `opaque-response-safelist check` is implemented in: 3484 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckBeforeSniff` 3485 // * `nsHttpChannel::DisableIsOpaqueResponseAllowedAfterSniffCheck` 3486 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckAfterSniff` 3487 // * `OpaqueResponseBlocker::ValidateJavaScript` 3488 OpaqueResponse 3489 HttpBaseChannel::PerformOpaqueResponseSafelistCheckBeforeSniff() { 3490 MOZ_ASSERT(XRE_IsParentProcess()); 3491 3492 // https://whatpr.org/fetch/1442.html#http-fetch, step 6.4 3493 if (!ShouldBlockOpaqueResponse()) { 3494 return OpaqueResponse::Allow; 3495 } 3496 3497 // Regardless of if ORB is enabled or not, we check if we should filter the 3498 // response in the parent. This way data won't reach a content process that 3499 // will create a filtered `Response` object. This is enabled when 3500 // 'browser.opaqueResponseBlocking.filterFetchResponse' is 3501 // `OpaqueResponseFilterFetch::All`. 3502 // See https://fetch.spec.whatwg.org/#concept-filtered-response-opaque 3503 if (ShouldFilterOpaqueResponse(OpaqueResponseFilterFetch::All)) { 3504 mListener = new OpaqueResponseFilter(mListener); 3505 3506 // If we're filtering a response in the parent, there will be no data to 3507 // determine if it should be blocked or not so the only option we have is to 3508 // allow it. 3509 return OpaqueResponse::Allow; 3510 } 3511 3512 if (!mCachedOpaqueResponseBlockingPref) { 3513 return OpaqueResponse::Allow; 3514 } 3515 3516 // If ORB is enabled, we check if we should filter the response in the parent. 3517 // This way data won't reach a content process that will create a filtered 3518 // `Response` object. We allow ORB to determine if the response should be 3519 // blocked or filtered, but regardless no data should reach the content 3520 // process. This is enabled when 3521 // 'browser.opaqueResponseBlocking.filterFetchResponse' is 3522 // `OpaqueResponseFilterFetch::AllowedByORB`. 3523 // See https://fetch.spec.whatwg.org/#concept-filtered-response-opaque 3524 if (ShouldFilterOpaqueResponse(OpaqueResponseFilterFetch::AllowedByORB)) { 3525 mListener = new OpaqueResponseFilter(mListener); 3526 } 3527 3528 glean::opaque_response_blocking::cross_origin_opaque_response_count.Add(1); 3529 3530 PROFILER_MARKER_TEXT("ORB safelist check", NETWORK, {}, "Before sniff"_ns); 3531 3532 // https://whatpr.org/fetch/1442.html#orb-algorithm 3533 // Step 1 3534 nsAutoCString contentType; 3535 mResponseHead->ContentType(contentType); 3536 3537 // Step 2 3538 nsAutoCString contentTypeOptionsHeader; 3539 bool nosniff = 3540 mResponseHead->GetContentTypeOptionsHeader(contentTypeOptionsHeader) && 3541 contentTypeOptionsHeader.EqualsIgnoreCase("nosniff"); 3542 3543 // Step 3 3544 switch (GetOpaqueResponseBlockedReason(contentType, mResponseHead->Status(), 3545 nosniff)) { 3546 case OpaqueResponseBlockedReason::ALLOWED_SAFE_LISTED: 3547 // Step 3.1 3548 return OpaqueResponse::Allow; 3549 case OpaqueResponseBlockedReason::ALLOWED_SAFE_LISTED_SPEC_BREAKING: 3550 LOGORB("Allowed %s in a spec breaking way", contentType.get()); 3551 return OpaqueResponse::Allow; 3552 case OpaqueResponseBlockedReason::BLOCKED_BLOCKLISTED_NEVER_SNIFFED: 3553 return BlockOrFilterOpaqueResponse( 3554 mORB, u"mimeType is an opaque-blocklisted-never-sniffed MIME type"_ns, 3555 OpaqueResponseBlockedTelemetryReason::eMimeNeverSniffed, 3556 "BLOCKED_BLOCKLISTED_NEVER_SNIFFED"); 3557 case OpaqueResponseBlockedReason::BLOCKED_206_AND_BLOCKLISTED: 3558 // Step 3.3 3559 return BlockOrFilterOpaqueResponse( 3560 mORB, 3561 u"response's status is 206 and mimeType is an opaque-blocklisted MIME type"_ns, 3562 OpaqueResponseBlockedTelemetryReason::eResp206Blclisted, 3563 "BLOCKED_206_AND_BLOCKEDLISTED"); 3564 case OpaqueResponseBlockedReason:: 3565 BLOCKED_NOSNIFF_AND_EITHER_BLOCKLISTED_OR_TEXTPLAIN: 3566 // Step 3.4 3567 return BlockOrFilterOpaqueResponse( 3568 mORB, 3569 u"nosniff is true and mimeType is an opaque-blocklisted MIME type or its essence is 'text/plain'"_ns, 3570 OpaqueResponseBlockedTelemetryReason::eNosniffBlcOrTextp, 3571 "BLOCKED_NOSNIFF_AND_EITHER_BLOCKLISTED_OR_TEXTPLAIN"); 3572 default: 3573 break; 3574 } 3575 3576 // Step 4 3577 // If it's a media subsequent request, we assume that it will only be made 3578 // after a successful initial request. 3579 bool isMediaRequest; 3580 mLoadInfo->GetIsMediaRequest(&isMediaRequest); 3581 if (isMediaRequest) { 3582 bool isMediaInitialRequest; 3583 mLoadInfo->GetIsMediaInitialRequest(&isMediaInitialRequest); 3584 if (!isMediaInitialRequest) { 3585 return OpaqueResponse::Allow; 3586 } 3587 } 3588 3589 // Step 5 3590 if (mResponseHead->Status() == 206 && 3591 !IsFirstPartialResponse(*mResponseHead)) { 3592 return BlockOrFilterOpaqueResponse( 3593 mORB, u"response status is 206 and not first partial response"_ns, 3594 OpaqueResponseBlockedTelemetryReason::eResp206Blclisted, 3595 "Is not a valid partial response given 0"); 3596 } 3597 3598 // Setup for steps 6, 7, 8 and 10. 3599 // Steps 6 and 7 are handled by the sniffer framework. 3600 // Steps 8 and 10 by are handled by 3601 // `nsHttpChannel::DisableIsOpaqueResponseAllowedAfterSniffCheck` 3602 if (mLoadFlags & nsIChannel::LOAD_CALL_CONTENT_SNIFFERS) { 3603 mSnifferCategoryType = SnifferCategoryType::All; 3604 } else { 3605 mSnifferCategoryType = SnifferCategoryType::OpaqueResponseBlocking; 3606 } 3607 3608 mLoadFlags |= (nsIChannel::LOAD_CALL_CONTENT_SNIFFERS | 3609 nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE); 3610 3611 // Install an input stream listener that performs ORB checks that depend on 3612 // inspecting the incoming data. It is crucial that `OnStartRequest` is called 3613 // on this listener either after sniffing is completed or that we skip 3614 // sniffing, otherwise `OpaqueResponseBlocker` will allow responses that it 3615 // shouldn't. 3616 mORB = new OpaqueResponseBlocker(mListener, this, contentType, nosniff); 3617 mListener = mORB; 3618 3619 nsAutoCString contentEncoding; 3620 nsresult rv = 3621 mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding); 3622 3623 if (NS_SUCCEEDED(rv) && !contentEncoding.IsEmpty()) { 3624 return OpaqueResponse::SniffCompressed; 3625 } 3626 mLoadFlags |= (nsIChannel::LOAD_CALL_CONTENT_SNIFFERS | 3627 nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE); 3628 return OpaqueResponse::Sniff; 3629 } 3630 3631 // The specification for ORB is currently being written: 3632 // https://whatpr.org/fetch/1442.html#orb-algorithm 3633 // The `opaque-response-safelist check` is implemented in: 3634 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckBeforeSniff` 3635 // * `nsHttpChannel::DisableIsOpaqueResponseAllowedAfterSniffCheck` 3636 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckAfterSniff` 3637 // * `OpaqueResponseBlocker::ValidateJavaScript` 3638 OpaqueResponse HttpBaseChannel::PerformOpaqueResponseSafelistCheckAfterSniff( 3639 const nsACString& aContentType, bool aNoSniff) { 3640 PROFILER_MARKER_TEXT("ORB safelist check", NETWORK, {}, "After sniff"_ns); 3641 3642 // https://whatpr.org/fetch/1442.html#orb-algorithm 3643 MOZ_ASSERT(XRE_IsParentProcess()); 3644 MOZ_ASSERT(mCachedOpaqueResponseBlockingPref); 3645 3646 // Step 9 3647 bool isMediaRequest; 3648 mLoadInfo->GetIsMediaRequest(&isMediaRequest); 3649 if (isMediaRequest) { 3650 return BlockOrFilterOpaqueResponse( 3651 mORB, u"after sniff: media request"_ns, 3652 OpaqueResponseBlockedTelemetryReason::eAfterSniffMedia, 3653 "media request"); 3654 } 3655 3656 // Step 11 3657 if (aNoSniff) { 3658 return BlockOrFilterOpaqueResponse( 3659 mORB, u"after sniff: nosniff is true"_ns, 3660 OpaqueResponseBlockedTelemetryReason::eAfterSniffNosniff, "nosniff"); 3661 } 3662 3663 // Step 12 3664 if (mResponseHead && 3665 (mResponseHead->Status() < 200 || mResponseHead->Status() > 299)) { 3666 return BlockOrFilterOpaqueResponse( 3667 mORB, u"after sniff: status code is not in allowed range"_ns, 3668 OpaqueResponseBlockedTelemetryReason::eAfterSniffStaCode, 3669 "status code (%d) is not allowed", mResponseHead->Status()); 3670 } 3671 3672 // Step 13 3673 if (!mResponseHead || aContentType.IsEmpty()) { 3674 LOGORB("Allowed: mimeType is failure"); 3675 return OpaqueResponse::Allow; 3676 } 3677 3678 // Step 14 3679 if (StringBeginsWith(aContentType, "image/"_ns) || 3680 StringBeginsWith(aContentType, "video/"_ns) || 3681 StringBeginsWith(aContentType, "audio/"_ns)) { 3682 return BlockOrFilterOpaqueResponse( 3683 mORB, 3684 u"after sniff: content-type declares image/video/audio, but sniffing fails"_ns, 3685 OpaqueResponseBlockedTelemetryReason::eAfterSniffCtFail, 3686 "ContentType is image/video/audio"); 3687 } 3688 3689 return OpaqueResponse::Sniff; 3690 } 3691 3692 bool HttpBaseChannel::NeedOpaqueResponseAllowedCheckAfterSniff() const { 3693 return mORB ? mORB->IsSniffing() : false; 3694 } 3695 3696 void HttpBaseChannel::BlockOpaqueResponseAfterSniff( 3697 const nsAString& aReason, 3698 const OpaqueResponseBlockedTelemetryReason aTelemetryReason) { 3699 MOZ_DIAGNOSTIC_ASSERT(mORB); 3700 LogORBError(aReason, aTelemetryReason); 3701 mORB->BlockResponse(this, NS_BINDING_ABORTED); 3702 } 3703 3704 void HttpBaseChannel::AllowOpaqueResponseAfterSniff() { 3705 MOZ_DIAGNOSTIC_ASSERT(mORB); 3706 mORB->AllowResponse(); 3707 } 3708 3709 void HttpBaseChannel::SetChannelBlockedByOpaqueResponse() { 3710 mChannelBlockedByOpaqueResponse = true; 3711 3712 RefPtr<dom::BrowsingContext> browsingContext = 3713 dom::BrowsingContext::GetCurrentTopByBrowserId(mBrowserId); 3714 if (!browsingContext) { 3715 return; 3716 } 3717 3718 dom::WindowContext* windowContext = browsingContext->GetTopWindowContext(); 3719 if (windowContext) { 3720 windowContext->Canonical()->SetShouldReportHasBlockedOpaqueResponse( 3721 mLoadInfo->InternalContentPolicyType()); 3722 } 3723 } 3724 3725 NS_IMETHODIMP 3726 HttpBaseChannel::SetCookieHeaders(const nsTArray<nsCString>& aCookieHeaders) { 3727 if (mLoadFlags & LOAD_ANONYMOUS) return NS_OK; 3728 3729 // The loadGroup of the channel in the parent process could be null in the 3730 // XPCShell content process test, see test_cookiejars_wrap.js. In this case, 3731 // we cannot explicitly set the loadGroup for the parent channel because it's 3732 // created from the content process. To workaround this, we add a testing pref 3733 // to skip this check. 3734 if (!StaticPrefs:: 3735 network_cookie_skip_browsing_context_check_in_parent_for_testing() && 3736 IsBrowsingContextDiscarded()) { 3737 return NS_OK; 3738 } 3739 3740 // empty header isn't an error 3741 if (aCookieHeaders.IsEmpty()) { 3742 return NS_OK; 3743 } 3744 3745 nsICookieService* cs = gHttpHandler->GetCookieService(); 3746 NS_ENSURE_TRUE(cs, NS_ERROR_FAILURE); 3747 3748 for (const nsCString& cookieHeader : aCookieHeaders) { 3749 nsresult rv = cs->SetCookieStringFromHttp(mURI, cookieHeader, this); 3750 NS_ENSURE_SUCCESS(rv, rv); 3751 } 3752 3753 return NS_OK; 3754 } 3755 3756 NS_IMETHODIMP 3757 HttpBaseChannel::GetThirdPartyFlags(uint32_t* aFlags) { 3758 *aFlags = LoadThirdPartyFlags(); 3759 return NS_OK; 3760 } 3761 3762 NS_IMETHODIMP 3763 HttpBaseChannel::SetThirdPartyFlags(uint32_t aFlags) { 3764 ENSURE_CALLED_BEFORE_ASYNC_OPEN(); 3765 3766 StoreThirdPartyFlags(aFlags); 3767 return NS_OK; 3768 } 3769 3770 NS_IMETHODIMP 3771 HttpBaseChannel::GetForceAllowThirdPartyCookie(bool* aForce) { 3772 *aForce = !!(LoadThirdPartyFlags() & 3773 nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW); 3774 return NS_OK; 3775 } 3776 3777 NS_IMETHODIMP 3778 HttpBaseChannel::SetForceAllowThirdPartyCookie(bool aForce) { 3779 ENSURE_CALLED_BEFORE_ASYNC_OPEN(); 3780 3781 if (aForce) { 3782 StoreThirdPartyFlags(LoadThirdPartyFlags() | 3783 nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW); 3784 } else { 3785 StoreThirdPartyFlags(LoadThirdPartyFlags() & 3786 ~nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW); 3787 } 3788 3789 return NS_OK; 3790 } 3791 3792 NS_IMETHODIMP 3793 HttpBaseChannel::GetCanceled(bool* aCanceled) { 3794 *aCanceled = mCanceled; 3795 return NS_OK; 3796 } 3797 3798 NS_IMETHODIMP 3799 HttpBaseChannel::GetChannelIsForDownload(bool* aChannelIsForDownload) { 3800 *aChannelIsForDownload = LoadChannelIsForDownload(); 3801 return NS_OK; 3802 } 3803 3804 NS_IMETHODIMP 3805 HttpBaseChannel::SetChannelIsForDownload(bool aChannelIsForDownload) { 3806 StoreChannelIsForDownload(aChannelIsForDownload); 3807 return NS_OK; 3808 } 3809 3810 NS_IMETHODIMP 3811 HttpBaseChannel::SetCacheKeysRedirectChain(nsTArray<nsCString>* cacheKeys) { 3812 auto RedirectedCachekeys = mRedirectedCachekeys.Lock(); 3813 auto& ref = RedirectedCachekeys.ref(); 3814 ref = WrapUnique(cacheKeys); 3815 return NS_OK; 3816 } 3817 3818 NS_IMETHODIMP 3819 HttpBaseChannel::GetLocalAddress(nsACString& addr) { 3820 if (mSelfAddr.raw.family == PR_AF_UNSPEC) return NS_ERROR_NOT_AVAILABLE; 3821 3822 addr.SetLength(kIPv6CStrBufSize); 3823 mSelfAddr.ToStringBuffer(addr.BeginWriting(), kIPv6CStrBufSize); 3824 addr.SetLength(strlen(addr.BeginReading())); 3825 3826 return NS_OK; 3827 } 3828 3829 NS_IMETHODIMP 3830 HttpBaseChannel::TakeAllSecurityMessages( 3831 nsCOMArray<nsISecurityConsoleMessage>& aMessages) { 3832 MOZ_ASSERT(NS_IsMainThread()); 3833 3834 aMessages.Clear(); 3835 for (const auto& pair : mSecurityConsoleMessages) { 3836 nsresult rv; 3837 nsCOMPtr<nsISecurityConsoleMessage> message = 3838 do_CreateInstance(NS_SECURITY_CONSOLE_MESSAGE_CONTRACTID, &rv); 3839 NS_ENSURE_SUCCESS(rv, rv); 3840 3841 message->SetTag(pair.first); 3842 message->SetCategory(pair.second); 3843 aMessages.AppendElement(message); 3844 } 3845 3846 MOZ_ASSERT(mSecurityConsoleMessages.Length() == aMessages.Length()); 3847 mSecurityConsoleMessages.Clear(); 3848 3849 return NS_OK; 3850 } 3851 3852 /* Please use this method with care. This can cause the message 3853 * queue to grow large and cause the channel to take up a lot 3854 * of memory. Use only static string messages and do not add 3855 * server side data to the queue, as that can be large. 3856 * Add only a limited number of messages to the queue to keep 3857 * the channel size down and do so only in rare erroneous situations. 3858 * More information can be found here: 3859 * https://bugzilla.mozilla.org/show_bug.cgi?id=846918 3860 */ 3861 nsresult HttpBaseChannel::AddSecurityMessage( 3862 const nsAString& aMessageTag, const nsAString& aMessageCategory) { 3863 MOZ_ASSERT(NS_IsMainThread()); 3864 3865 nsresult rv; 3866 3867 // nsSecurityConsoleMessage is not thread-safe refcounted. 3868 // Delay the object construction until requested. 3869 // See TakeAllSecurityMessages() 3870 std::pair<nsString, nsString> pair(aMessageTag, aMessageCategory); 3871 mSecurityConsoleMessages.AppendElement(std::move(pair)); 3872 3873 nsCOMPtr<nsIConsoleService> console( 3874 do_GetService(NS_CONSOLESERVICE_CONTRACTID)); 3875 if (!console) { 3876 return NS_ERROR_FAILURE; 3877 } 3878 3879 nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo(); 3880 3881 auto innerWindowID = loadInfo->GetInnerWindowID(); 3882 3883 nsAutoString errorText; 3884 rv = nsContentUtils::GetLocalizedString( 3885 nsContentUtils::eSECURITY_PROPERTIES, 3886 NS_ConvertUTF16toUTF8(aMessageTag).get(), errorText); 3887 NS_ENSURE_SUCCESS(rv, rv); 3888 3889 nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); 3890 error->InitWithSourceURI(errorText, mURI, 0, 0, nsIScriptError::warningFlag, 3891 NS_ConvertUTF16toUTF8(aMessageCategory), 3892 innerWindowID); 3893 3894 console->LogMessage(error); 3895 3896 return NS_OK; 3897 } 3898 3899 NS_IMETHODIMP 3900 HttpBaseChannel::GetLocalPort(int32_t* port) { 3901 NS_ENSURE_ARG_POINTER(port); 3902 3903 if (mSelfAddr.raw.family == PR_AF_INET) { 3904 *port = (int32_t)ntohs(mSelfAddr.inet.port); 3905 } else if (mSelfAddr.raw.family == PR_AF_INET6) { 3906 *port = (int32_t)ntohs(mSelfAddr.inet6.port); 3907 } else { 3908 return NS_ERROR_NOT_AVAILABLE; 3909 } 3910 3911 return NS_OK; 3912 } 3913 3914 NS_IMETHODIMP 3915 HttpBaseChannel::GetRemoteAddress(nsACString& addr) { 3916 if (mPeerAddr.raw.family == PR_AF_UNSPEC) return NS_ERROR_NOT_AVAILABLE; 3917 3918 addr.SetLength(kIPv6CStrBufSize); 3919 mPeerAddr.ToStringBuffer(addr.BeginWriting(), kIPv6CStrBufSize); 3920 addr.SetLength(strlen(addr.BeginReading())); 3921 3922 return NS_OK; 3923 } 3924 3925 NS_IMETHODIMP 3926 HttpBaseChannel::GetRemotePort(int32_t* port) { 3927 NS_ENSURE_ARG_POINTER(port); 3928 3929 if (mPeerAddr.raw.family == PR_AF_INET) { 3930 *port = (int32_t)ntohs(mPeerAddr.inet.port); 3931 } else if (mPeerAddr.raw.family == PR_AF_INET6) { 3932 *port = (int32_t)ntohs(mPeerAddr.inet6.port); 3933 } else { 3934 return NS_ERROR_NOT_AVAILABLE; 3935 } 3936 3937 return NS_OK; 3938 } 3939 3940 NS_IMETHODIMP 3941 HttpBaseChannel::HTTPUpgrade(const nsACString& aProtocolName, 3942 nsIHttpUpgradeListener* aListener) { 3943 NS_ENSURE_ARG(!aProtocolName.IsEmpty()); 3944 NS_ENSURE_ARG_POINTER(aListener); 3945 3946 mUpgradeProtocol = aProtocolName; 3947 mUpgradeProtocolCallback = aListener; 3948 return NS_OK; 3949 } 3950 3951 NS_IMETHODIMP 3952 HttpBaseChannel::GetOnlyConnect(bool* aOnlyConnect) { 3953 NS_ENSURE_ARG_POINTER(aOnlyConnect); 3954 3955 *aOnlyConnect = mCaps & NS_HTTP_CONNECT_ONLY; 3956 return NS_OK; 3957 } 3958 3959 NS_IMETHODIMP 3960 HttpBaseChannel::SetConnectOnly(bool aTlsTunnel) { 3961 ENSURE_CALLED_BEFORE_CONNECT(); 3962 3963 if (!mUpgradeProtocolCallback) { 3964 return NS_ERROR_FAILURE; 3965 } 3966 3967 mCaps |= NS_HTTP_CONNECT_ONLY; 3968 if (aTlsTunnel) { 3969 mCaps |= NS_HTTP_TLS_TUNNEL; 3970 } 3971 mProxyResolveFlags = nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY | 3972 nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL; 3973 return SetLoadFlags(nsIRequest::INHIBIT_CACHING | nsIChannel::LOAD_ANONYMOUS | 3974 nsIRequest::LOAD_BYPASS_CACHE | 3975 nsIChannel::LOAD_BYPASS_SERVICE_WORKER); 3976 } 3977 3978 NS_IMETHODIMP 3979 HttpBaseChannel::GetAllowSpdy(bool* aAllowSpdy) { 3980 NS_ENSURE_ARG_POINTER(aAllowSpdy); 3981 3982 *aAllowSpdy = LoadAllowSpdy(); 3983 return NS_OK; 3984 } 3985 3986 NS_IMETHODIMP 3987 HttpBaseChannel::SetAllowSpdy(bool aAllowSpdy) { 3988 StoreAllowSpdy(aAllowSpdy); 3989 return NS_OK; 3990 } 3991 3992 NS_IMETHODIMP 3993 HttpBaseChannel::GetAllowHttp3(bool* aAllowHttp3) { 3994 NS_ENSURE_ARG_POINTER(aAllowHttp3); 3995 3996 *aAllowHttp3 = LoadAllowHttp3(); 3997 return NS_OK; 3998 } 3999 4000 NS_IMETHODIMP 4001 HttpBaseChannel::SetAllowHttp3(bool aAllowHttp3) { 4002 StoreAllowHttp3(aAllowHttp3); 4003 return NS_OK; 4004 } 4005 4006 NS_IMETHODIMP 4007 HttpBaseChannel::GetAllowAltSvc(bool* aAllowAltSvc) { 4008 NS_ENSURE_ARG_POINTER(aAllowAltSvc); 4009 4010 *aAllowAltSvc = LoadAllowAltSvc(); 4011 return NS_OK; 4012 } 4013 4014 NS_IMETHODIMP 4015 HttpBaseChannel::SetAllowAltSvc(bool aAllowAltSvc) { 4016 StoreAllowAltSvc(aAllowAltSvc); 4017 return NS_OK; 4018 } 4019 4020 NS_IMETHODIMP 4021 HttpBaseChannel::GetBeConservative(bool* aBeConservative) { 4022 NS_ENSURE_ARG_POINTER(aBeConservative); 4023 4024 *aBeConservative = LoadBeConservative(); 4025 return NS_OK; 4026 } 4027 4028 NS_IMETHODIMP 4029 HttpBaseChannel::SetBeConservative(bool aBeConservative) { 4030 StoreBeConservative(aBeConservative); 4031 return NS_OK; 4032 } 4033 4034 bool HttpBaseChannel::BypassProxy() { 4035 return StaticPrefs::network_proxy_allow_bypass() && LoadBypassProxy(); 4036 } 4037 4038 NS_IMETHODIMP 4039 HttpBaseChannel::GetBypassProxy(bool* aBypassProxy) { 4040 NS_ENSURE_ARG_POINTER(aBypassProxy); 4041 4042 *aBypassProxy = BypassProxy(); 4043 return NS_OK; 4044 } 4045 4046 NS_IMETHODIMP 4047 HttpBaseChannel::SetBypassProxy(bool aBypassProxy) { 4048 if (StaticPrefs::network_proxy_allow_bypass()) { 4049 StoreBypassProxy(aBypassProxy); 4050 } else { 4051 NS_WARNING("bypassProxy set but network.proxy.allow_bypass is disabled"); 4052 return NS_ERROR_FAILURE; 4053 } 4054 return NS_OK; 4055 } 4056 4057 NS_IMETHODIMP 4058 HttpBaseChannel::GetIsTRRServiceChannel(bool* aIsTRRServiceChannel) { 4059 NS_ENSURE_ARG_POINTER(aIsTRRServiceChannel); 4060 4061 *aIsTRRServiceChannel = LoadIsTRRServiceChannel(); 4062 return NS_OK; 4063 } 4064 4065 NS_IMETHODIMP 4066 HttpBaseChannel::SetIsTRRServiceChannel(bool aIsTRRServiceChannel) { 4067 StoreIsTRRServiceChannel(aIsTRRServiceChannel); 4068 return NS_OK; 4069 } 4070 4071 NS_IMETHODIMP 4072 HttpBaseChannel::GetIsResolvedByTRR(bool* aResolvedByTRR) { 4073 NS_ENSURE_ARG_POINTER(aResolvedByTRR); 4074 *aResolvedByTRR = LoadResolvedByTRR(); 4075 return NS_OK; 4076 } 4077 4078 NS_IMETHODIMP 4079 HttpBaseChannel::GetEffectiveTRRMode(nsIRequest::TRRMode* aEffectiveTRRMode) { 4080 *aEffectiveTRRMode = mEffectiveTRRMode; 4081 return NS_OK; 4082 } 4083 4084 NS_IMETHODIMP 4085 HttpBaseChannel::GetTrrSkipReason(nsITRRSkipReason::value* aTrrSkipReason) { 4086 *aTrrSkipReason = mTRRSkipReason; 4087 return NS_OK; 4088 } 4089 4090 NS_IMETHODIMP 4091 HttpBaseChannel::GetIsLoadedBySocketProcess(bool* aResult) { 4092 NS_ENSURE_ARG_POINTER(aResult); 4093 *aResult = LoadLoadedBySocketProcess(); 4094 return NS_OK; 4095 } 4096 4097 NS_IMETHODIMP 4098 HttpBaseChannel::GetTlsFlags(uint32_t* aTlsFlags) { 4099 NS_ENSURE_ARG_POINTER(aTlsFlags); 4100 4101 *aTlsFlags = mTlsFlags; 4102 return NS_OK; 4103 } 4104 4105 NS_IMETHODIMP 4106 HttpBaseChannel::SetTlsFlags(uint32_t aTlsFlags) { 4107 mTlsFlags = aTlsFlags; 4108 return NS_OK; 4109 } 4110 4111 NS_IMETHODIMP 4112 HttpBaseChannel::GetApiRedirectToURI(nsIURI** aResult) { 4113 if (!mAPIRedirectTo) { 4114 return NS_ERROR_NOT_AVAILABLE; 4115 } 4116 NS_ENSURE_ARG_POINTER(aResult); 4117 *aResult = do_AddRef(mAPIRedirectTo->first()).take(); 4118 return NS_OK; 4119 } 4120 4121 NS_IMETHODIMP 4122 HttpBaseChannel::GetResponseTimeoutEnabled(bool* aEnable) { 4123 if (NS_WARN_IF(!aEnable)) { 4124 return NS_ERROR_NULL_POINTER; 4125 } 4126 *aEnable = LoadResponseTimeoutEnabled(); 4127 return NS_OK; 4128 } 4129 4130 NS_IMETHODIMP 4131 HttpBaseChannel::SetResponseTimeoutEnabled(bool aEnable) { 4132 StoreResponseTimeoutEnabled(aEnable); 4133 return NS_OK; 4134 } 4135 4136 NS_IMETHODIMP 4137 HttpBaseChannel::GetInitialRwin(uint32_t* aRwin) { 4138 if (NS_WARN_IF(!aRwin)) { 4139 return NS_ERROR_NULL_POINTER; 4140 } 4141 *aRwin = mInitialRwin; 4142 return NS_OK; 4143 } 4144 4145 NS_IMETHODIMP 4146 HttpBaseChannel::SetInitialRwin(uint32_t aRwin) { 4147 ENSURE_CALLED_BEFORE_CONNECT(); 4148 mInitialRwin = aRwin; 4149 return NS_OK; 4150 } 4151 4152 NS_IMETHODIMP 4153 HttpBaseChannel::ForcePending(bool aForcePending) { 4154 StoreForcePending(aForcePending); 4155 return NS_OK; 4156 } 4157 4158 NS_IMETHODIMP 4159 HttpBaseChannel::GetLastModifiedTime(PRTime* lastModifiedTime) { 4160 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 4161 uint32_t lastMod; 4162 nsresult rv = mResponseHead->GetLastModifiedValue(&lastMod); 4163 NS_ENSURE_SUCCESS(rv, rv); 4164 *lastModifiedTime = lastMod; 4165 return NS_OK; 4166 } 4167 4168 NS_IMETHODIMP 4169 HttpBaseChannel::GetCorsIncludeCredentials(bool* aInclude) { 4170 *aInclude = LoadCorsIncludeCredentials(); 4171 return NS_OK; 4172 } 4173 4174 NS_IMETHODIMP 4175 HttpBaseChannel::SetCorsIncludeCredentials(bool aInclude) { 4176 StoreCorsIncludeCredentials(aInclude); 4177 return NS_OK; 4178 } 4179 4180 NS_IMETHODIMP 4181 HttpBaseChannel::GetRequestMode(RequestMode* aMode) { 4182 *aMode = mRequestMode; 4183 return NS_OK; 4184 } 4185 4186 NS_IMETHODIMP 4187 HttpBaseChannel::SetRequestMode(RequestMode aMode) { 4188 mRequestMode = aMode; 4189 return NS_OK; 4190 } 4191 4192 NS_IMETHODIMP 4193 HttpBaseChannel::GetRedirectMode(uint32_t* aMode) { 4194 *aMode = mRedirectMode; 4195 return NS_OK; 4196 } 4197 4198 NS_IMETHODIMP 4199 HttpBaseChannel::SetRedirectMode(uint32_t aMode) { 4200 mRedirectMode = aMode; 4201 return NS_OK; 4202 } 4203 4204 namespace { 4205 4206 bool ContainsAllFlags(uint32_t aLoadFlags, uint32_t aMask) { 4207 return (aLoadFlags & aMask) == aMask; 4208 } 4209 4210 } // anonymous namespace 4211 4212 NS_IMETHODIMP 4213 HttpBaseChannel::GetFetchCacheMode(uint32_t* aFetchCacheMode) { 4214 NS_ENSURE_ARG_POINTER(aFetchCacheMode); 4215 4216 // Otherwise try to guess an appropriate cache mode from the load flags. 4217 if (ContainsAllFlags(mLoadFlags, INHIBIT_CACHING | LOAD_BYPASS_CACHE)) { 4218 *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE; 4219 } else if (ContainsAllFlags(mLoadFlags, LOAD_BYPASS_CACHE)) { 4220 *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD; 4221 } else if (ContainsAllFlags(mLoadFlags, VALIDATE_ALWAYS) || 4222 LoadForceValidateCacheContent()) { 4223 *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE; 4224 } else if (ContainsAllFlags( 4225 mLoadFlags, 4226 VALIDATE_NEVER | nsICachingChannel::LOAD_ONLY_FROM_CACHE)) { 4227 *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED; 4228 } else if (ContainsAllFlags(mLoadFlags, VALIDATE_NEVER)) { 4229 *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE; 4230 } else { 4231 *aFetchCacheMode = nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT; 4232 } 4233 4234 return NS_OK; 4235 } 4236 4237 namespace { 4238 4239 void SetCacheFlags(uint32_t& aLoadFlags, uint32_t aFlags) { 4240 // First, clear any possible cache related flags. 4241 uint32_t allPossibleFlags = 4242 nsIRequest::INHIBIT_CACHING | nsIRequest::LOAD_BYPASS_CACHE | 4243 nsIRequest::VALIDATE_ALWAYS | nsIRequest::LOAD_FROM_CACHE | 4244 nsICachingChannel::LOAD_ONLY_FROM_CACHE; 4245 aLoadFlags &= ~allPossibleFlags; 4246 4247 // Then set the new flags. 4248 aLoadFlags |= aFlags; 4249 } 4250 4251 } // anonymous namespace 4252 4253 NS_IMETHODIMP 4254 HttpBaseChannel::SetFetchCacheMode(uint32_t aFetchCacheMode) { 4255 ENSURE_CALLED_BEFORE_CONNECT(); 4256 4257 // Now, set the load flags that implement each cache mode. 4258 switch (aFetchCacheMode) { 4259 case nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT: 4260 // The "default" mode means to use the http cache normally and 4261 // respect any http cache-control headers. We effectively want 4262 // to clear our cache related load flags. 4263 SetCacheFlags(mLoadFlags, 0); 4264 break; 4265 case nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE: 4266 // no-store means don't consult the cache on the way to the network, and 4267 // don't store the response in the cache even if it's cacheable. 4268 SetCacheFlags(mLoadFlags, INHIBIT_CACHING | LOAD_BYPASS_CACHE); 4269 break; 4270 case nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD: 4271 // reload means don't consult the cache on the way to the network, but 4272 // do store the response in the cache if possible. 4273 SetCacheFlags(mLoadFlags, LOAD_BYPASS_CACHE); 4274 break; 4275 case nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE: 4276 // no-cache means always validate what's in the cache. 4277 SetCacheFlags(mLoadFlags, VALIDATE_ALWAYS); 4278 break; 4279 case nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE: 4280 // force-cache means don't validate unless if the response would vary. 4281 SetCacheFlags(mLoadFlags, VALIDATE_NEVER); 4282 break; 4283 case nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED: 4284 // only-if-cached means only from cache, no network, no validation, 4285 // generate a network error if the document was't in the cache. The 4286 // privacy implications of these flags (making it fast/easy to check if 4287 // the user has things in their cache without any network traffic side 4288 // effects) are addressed in the Request constructor which 4289 // enforces/requires same-origin request mode. 4290 SetCacheFlags(mLoadFlags, 4291 VALIDATE_NEVER | nsICachingChannel::LOAD_ONLY_FROM_CACHE); 4292 break; 4293 } 4294 4295 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 4296 uint32_t finalMode = 0; 4297 MOZ_ALWAYS_SUCCEEDS(GetFetchCacheMode(&finalMode)); 4298 MOZ_DIAGNOSTIC_ASSERT(finalMode == aFetchCacheMode); 4299 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED 4300 4301 return NS_OK; 4302 } 4303 4304 //----------------------------------------------------------------------------- 4305 // HttpBaseChannel::nsISupportsPriority 4306 //----------------------------------------------------------------------------- 4307 4308 NS_IMETHODIMP 4309 HttpBaseChannel::GetPriority(int32_t* value) { 4310 *value = mPriority; 4311 return NS_OK; 4312 } 4313 4314 NS_IMETHODIMP 4315 HttpBaseChannel::AdjustPriority(int32_t delta) { 4316 return SetPriority(mPriority + delta); 4317 } 4318 4319 //----------------------------------------------------------------------------- 4320 // HttpBaseChannel::nsIResumableChannel 4321 //----------------------------------------------------------------------------- 4322 4323 NS_IMETHODIMP 4324 HttpBaseChannel::GetEntityID(nsACString& aEntityID) { 4325 // Don't return an entity ID for Non-GET requests which require 4326 // additional data 4327 if (!mRequestHead.IsGet()) { 4328 return NS_ERROR_NOT_RESUMABLE; 4329 } 4330 4331 uint64_t size = UINT64_MAX; 4332 nsAutoCString etag, lastmod; 4333 if (mResponseHead) { 4334 // Don't return an entity if the server sent the following header: 4335 // Accept-Ranges: none 4336 // Not sending the Accept-Ranges header means we can still try 4337 // sending range requests. 4338 nsAutoCString acceptRanges; 4339 (void)mResponseHead->GetHeader(nsHttp::Accept_Ranges, acceptRanges); 4340 if (!acceptRanges.IsEmpty() && 4341 !nsHttp::FindToken(acceptRanges.get(), "bytes", 4342 HTTP_HEADER_VALUE_SEPS)) { 4343 return NS_ERROR_NOT_RESUMABLE; 4344 } 4345 4346 size = mResponseHead->TotalEntitySize(); 4347 (void)mResponseHead->GetHeader(nsHttp::Last_Modified, lastmod); 4348 (void)mResponseHead->GetHeader(nsHttp::ETag, etag); 4349 } 4350 nsCString entityID; 4351 NS_EscapeURL(etag.BeginReading(), etag.Length(), 4352 esc_AlwaysCopy | esc_FileBaseName | esc_Forced, entityID); 4353 entityID.Append('/'); 4354 entityID.AppendInt(int64_t(size)); 4355 entityID.Append('/'); 4356 entityID.Append(lastmod); 4357 // NOTE: Appending lastmod as the last part avoids having to escape it 4358 4359 aEntityID = entityID; 4360 4361 return NS_OK; 4362 } 4363 4364 //----------------------------------------------------------------------------- 4365 // HttpBaseChannel::nsIConsoleReportCollector 4366 //----------------------------------------------------------------------------- 4367 4368 void HttpBaseChannel::AddConsoleReport( 4369 uint32_t aErrorFlags, const nsACString& aCategory, 4370 nsContentUtils::PropertiesFile aPropertiesFile, 4371 const nsACString& aSourceFileURI, uint32_t aLineNumber, 4372 uint32_t aColumnNumber, const nsACString& aMessageName, 4373 const nsTArray<nsString>& aStringParams) { 4374 mReportCollector->AddConsoleReport(aErrorFlags, aCategory, aPropertiesFile, 4375 aSourceFileURI, aLineNumber, aColumnNumber, 4376 aMessageName, aStringParams); 4377 4378 // If this channel is already part of a loadGroup, we can flush this console 4379 // report immediately. 4380 HttpBaseChannel::MaybeFlushConsoleReports(); 4381 } 4382 4383 void HttpBaseChannel::FlushReportsToConsole(uint64_t aInnerWindowID, 4384 ReportAction aAction) { 4385 mReportCollector->FlushReportsToConsole(aInnerWindowID, aAction); 4386 } 4387 4388 void HttpBaseChannel::FlushReportsToConsoleForServiceWorkerScope( 4389 const nsACString& aScope, ReportAction aAction) { 4390 mReportCollector->FlushReportsToConsoleForServiceWorkerScope(aScope, aAction); 4391 } 4392 4393 void HttpBaseChannel::FlushConsoleReports(dom::Document* aDocument, 4394 ReportAction aAction) { 4395 mReportCollector->FlushConsoleReports(aDocument, aAction); 4396 } 4397 4398 void HttpBaseChannel::FlushConsoleReports(nsILoadGroup* aLoadGroup, 4399 ReportAction aAction) { 4400 mReportCollector->FlushConsoleReports(aLoadGroup, aAction); 4401 } 4402 4403 void HttpBaseChannel::FlushConsoleReports( 4404 nsIConsoleReportCollector* aCollector) { 4405 mReportCollector->FlushConsoleReports(aCollector); 4406 } 4407 4408 void HttpBaseChannel::StealConsoleReports( 4409 nsTArray<net::ConsoleReportCollected>& aReports) { 4410 mReportCollector->StealConsoleReports(aReports); 4411 } 4412 4413 void HttpBaseChannel::ClearConsoleReports() { 4414 mReportCollector->ClearConsoleReports(); 4415 } 4416 4417 bool HttpBaseChannel::IsNavigation() { 4418 return LoadForceMainDocumentChannel() || (mLoadFlags & LOAD_DOCUMENT_URI); 4419 } 4420 4421 bool HttpBaseChannel::BypassServiceWorker() const { 4422 return mLoadFlags & LOAD_BYPASS_SERVICE_WORKER; 4423 } 4424 4425 bool HttpBaseChannel::ShouldIntercept(nsIURI* aURI) { 4426 nsCOMPtr<nsINetworkInterceptController> controller; 4427 GetCallback(controller); 4428 bool shouldIntercept = false; 4429 4430 if (!StaticPrefs::dom_serviceWorkers_enabled()) { 4431 return false; 4432 } 4433 4434 // We should never intercept internal redirects. The ServiceWorker code 4435 // can trigger interntal redirects as the result of a FetchEvent. If 4436 // we re-intercept then an infinite loop can occur. 4437 // 4438 // Its also important that we do not set the LOAD_BYPASS_SERVICE_WORKER 4439 // flag because an internal redirect occurs. Its possible that another 4440 // interception should occur after the internal redirect. For example, 4441 // if the ServiceWorker chooses not to call respondWith() the channel 4442 // will be reset with an internal redirect. If the request is a navigation 4443 // and the network then triggers a redirect its possible the new URL 4444 // should be intercepted again. 4445 // 4446 // Note, HSTS upgrade redirects are often treated the same as internal 4447 // redirects. In this case, however, we intentionally allow interception 4448 // of HSTS upgrade redirects. This matches the expected spec behavior and 4449 // does not run the risk of infinite loops as described above. 4450 bool internalRedirect = 4451 mLastRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL; 4452 4453 if (controller && mLoadInfo && !BypassServiceWorker() && !internalRedirect) { 4454 nsresult rv = controller->ShouldPrepareForIntercept( 4455 aURI ? aURI : mURI.get(), this, &shouldIntercept); 4456 if (NS_FAILED(rv)) { 4457 return false; 4458 } 4459 } 4460 return shouldIntercept; 4461 } 4462 4463 void HttpBaseChannel::AddAsNonTailRequest() { 4464 MOZ_ASSERT(NS_IsMainThread()); 4465 4466 if (EnsureRequestContext()) { 4467 LOG(( 4468 "HttpBaseChannel::AddAsNonTailRequest this=%p, rc=%p, already added=%d", 4469 this, mRequestContext.get(), (bool)LoadAddedAsNonTailRequest())); 4470 4471 if (!LoadAddedAsNonTailRequest()) { 4472 mRequestContext->AddNonTailRequest(); 4473 StoreAddedAsNonTailRequest(true); 4474 } 4475 } 4476 } 4477 4478 void HttpBaseChannel::RemoveAsNonTailRequest() { 4479 MOZ_ASSERT(NS_IsMainThread()); 4480 4481 if (mRequestContext) { 4482 LOG( 4483 ("HttpBaseChannel::RemoveAsNonTailRequest this=%p, rc=%p, already " 4484 "added=%d", 4485 this, mRequestContext.get(), (bool)LoadAddedAsNonTailRequest())); 4486 4487 if (LoadAddedAsNonTailRequest()) { 4488 mRequestContext->RemoveNonTailRequest(); 4489 StoreAddedAsNonTailRequest(false); 4490 } 4491 } 4492 } 4493 4494 #ifdef DEBUG 4495 void HttpBaseChannel::AssertPrivateBrowsingId() { 4496 nsCOMPtr<nsILoadContext> loadContext; 4497 NS_QueryNotificationCallbacks(this, loadContext); 4498 4499 if (!loadContext) { 4500 return; 4501 } 4502 4503 // We skip testing of favicon loading here since it could be triggered by XUL 4504 // image which uses SystemPrincipal. The SystemPrincpal doesn't have 4505 // mPrivateBrowsingId. 4506 if (mLoadInfo->GetLoadingPrincipal() && 4507 mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal() && 4508 mLoadInfo->InternalContentPolicyType() == 4509 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON) { 4510 return; 4511 } 4512 4513 OriginAttributes docShellAttrs; 4514 loadContext->GetOriginAttributes(docShellAttrs); 4515 MOZ_ASSERT(mLoadInfo->GetOriginAttributes().mPrivateBrowsingId == 4516 docShellAttrs.mPrivateBrowsingId, 4517 "PrivateBrowsingId values are not the same between LoadInfo and " 4518 "LoadContext."); 4519 } 4520 #endif 4521 4522 already_AddRefed<nsILoadInfo> HttpBaseChannel::CloneLoadInfoForRedirect( 4523 nsIURI* aNewURI, uint32_t aRedirectFlags) { 4524 // make a copy of the loadinfo, append to the redirectchain 4525 // this will be set on the newly created channel for the redirect target. 4526 nsCOMPtr<nsILoadInfo> newLoadInfo = 4527 static_cast<mozilla::net::LoadInfo*>(mLoadInfo.get())->Clone(); 4528 4529 ExtContentPolicyType contentPolicyType = 4530 mLoadInfo->GetExternalContentPolicyType(); 4531 if (contentPolicyType == ExtContentPolicy::TYPE_DOCUMENT || 4532 contentPolicyType == ExtContentPolicy::TYPE_SUBDOCUMENT) { 4533 // Reset PrincipalToInherit to a null principal. We'll credit the the 4534 // redirecting resource's result principal as the new principal's precursor. 4535 // This means that a data: URI will end up loading in a process based on the 4536 // redirected-from URI. 4537 nsCOMPtr<nsIPrincipal> redirectPrincipal; 4538 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( 4539 this, getter_AddRefs(redirectPrincipal)); 4540 nsCOMPtr<nsIPrincipal> nullPrincipalToInherit = 4541 NullPrincipal::CreateWithInheritedAttributes(redirectPrincipal); 4542 newLoadInfo->SetPrincipalToInherit(nullPrincipalToInherit); 4543 } 4544 4545 bool isTopLevelDoc = newLoadInfo->GetExternalContentPolicyType() == 4546 ExtContentPolicy::TYPE_DOCUMENT; 4547 4548 if (isTopLevelDoc) { 4549 // re-compute the origin attributes of the loadInfo if it's top-level load. 4550 nsCOMPtr<nsILoadContext> loadContext; 4551 NS_QueryNotificationCallbacks(this, loadContext); 4552 OriginAttributes docShellAttrs; 4553 if (loadContext) { 4554 loadContext->GetOriginAttributes(docShellAttrs); 4555 } 4556 4557 OriginAttributes attrs = newLoadInfo->GetOriginAttributes(); 4558 4559 MOZ_ASSERT( 4560 docShellAttrs.mUserContextId == attrs.mUserContextId, 4561 "docshell and necko should have the same userContextId attribute."); 4562 MOZ_ASSERT( 4563 docShellAttrs.mPrivateBrowsingId == attrs.mPrivateBrowsingId, 4564 "docshell and necko should have the same privateBrowsingId attribute."); 4565 MOZ_ASSERT(docShellAttrs.mGeckoViewSessionContextId == 4566 attrs.mGeckoViewSessionContextId, 4567 "docshell and necko should have the same " 4568 "geckoViewSessionContextId attribute"); 4569 4570 attrs = docShellAttrs; 4571 attrs.SetFirstPartyDomain(true, aNewURI); 4572 newLoadInfo->SetOriginAttributes(attrs); 4573 4574 // re-compute the upgrade insecure requests bit for document navigations 4575 // since it should only apply to same-origin navigations (redirects). 4576 // we only do this if the CSP of the triggering element (the cspToInherit) 4577 // uses 'upgrade-insecure-requests', otherwise UIR does not apply. 4578 nsCOMPtr<nsIPolicyContainer> policyContainer = 4579 newLoadInfo->GetPolicyContainerToInherit(); 4580 nsCOMPtr<nsIContentSecurityPolicy> csp = 4581 PolicyContainer::GetCSP(policyContainer); 4582 if (csp) { 4583 bool upgradeInsecureRequests = false; 4584 csp->GetUpgradeInsecureRequests(&upgradeInsecureRequests); 4585 if (upgradeInsecureRequests) { 4586 nsCOMPtr<nsIPrincipal> resultPrincipal = 4587 BasePrincipal::CreateContentPrincipal( 4588 aNewURI, newLoadInfo->GetOriginAttributes()); 4589 bool isConsideredSameOriginforUIR = 4590 nsContentSecurityUtils::IsConsideredSameOriginForUIR( 4591 newLoadInfo->TriggeringPrincipal(), resultPrincipal); 4592 static_cast<mozilla::net::LoadInfo*>(newLoadInfo.get()) 4593 ->SetUpgradeInsecureRequests(isConsideredSameOriginforUIR); 4594 } 4595 } 4596 } 4597 4598 // Clone a new cookieJarSettings from the old one for the new channel. 4599 // Otherwise, updating the new cookieJarSettings will affect the old one. 4600 nsCOMPtr<nsICookieJarSettings> oldCookieJarSettings; 4601 mLoadInfo->GetCookieJarSettings(getter_AddRefs(oldCookieJarSettings)); 4602 4603 RefPtr<CookieJarSettings> newCookieJarSettings; 4604 newCookieJarSettings = CookieJarSettings::Cast(oldCookieJarSettings)->Clone(); 4605 4606 newLoadInfo->SetCookieJarSettings(newCookieJarSettings); 4607 4608 // Clear the isThirdPartyContextToTopWindow flag for the new channel so that 4609 // it will be computed again when the new channel is opened. 4610 static_cast<net::LoadInfo*>(newLoadInfo.get()) 4611 ->ClearIsThirdPartyContextToTopWindow(); 4612 4613 // Leave empty, we want a 'clean ground' when creating the new channel. 4614 // This will be ensured to be either set by the protocol handler or set 4615 // to the redirect target URI properly after the channel creation. 4616 newLoadInfo->SetResultPrincipalURI(nullptr); 4617 4618 bool isInternalRedirect = 4619 (aRedirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL | 4620 nsIChannelEventSink::REDIRECT_STS_UPGRADE)); 4621 4622 // Reset our sandboxed null principal ID when cloning loadInfo for an 4623 // externally visible redirect. 4624 if (!isInternalRedirect) { 4625 // If we've redirected from http to something that isn't, clear 4626 // the "external" flag, as loads that now go to other apps should be 4627 // allowed to go ahead and not trip infinite-loop protection 4628 // (see bug 1717314 for context). 4629 if (!net::SchemeIsHttpOrHttps(aNewURI)) { 4630 newLoadInfo->SetLoadTriggeredFromExternal(false); 4631 } 4632 newLoadInfo->ResetSandboxedNullPrincipalID(); 4633 4634 if (isTopLevelDoc) { 4635 // Reset HTTPS-first and -only status on http redirect. To not 4636 // unexpectedly downgrade requests that weren't upgraded via HTTPS-First 4637 // (Bug 1904238). 4638 (void)newLoadInfo->SetHttpsOnlyStatus( 4639 nsILoadInfo::HTTPS_ONLY_UNINITIALIZED); 4640 4641 // Reset schemeless status flag to prevent schemeless HTTPS-First from 4642 // repeatedly trying to upgrade loads that get downgraded again from the 4643 // server by a redirect (Bug 1937386). 4644 (void)newLoadInfo->SetSchemelessInput( 4645 nsILoadInfo::SchemelessInputTypeUnset); 4646 } 4647 } 4648 4649 newLoadInfo->AppendRedirectHistoryEntry(this, isInternalRedirect); 4650 4651 return newLoadInfo.forget(); 4652 } 4653 4654 //----------------------------------------------------------------------------- 4655 // nsHttpChannel::nsITraceableChannel 4656 //----------------------------------------------------------------------------- 4657 4658 NS_IMETHODIMP 4659 HttpBaseChannel::SetNewListener(nsIStreamListener* aListener, 4660 bool aMustApplyContentConversion, 4661 nsIStreamListener** _retval) { 4662 LOG(( 4663 "HttpBaseChannel::SetNewListener [this=%p, mListener=%p, newListener=%p]", 4664 this, mListener.get(), aListener)); 4665 4666 if (!LoadTracingEnabled()) return NS_ERROR_FAILURE; 4667 4668 NS_ENSURE_STATE(mListener); 4669 NS_ENSURE_ARG_POINTER(aListener); 4670 4671 nsCOMPtr<nsIStreamListener> wrapper = new nsStreamListenerWrapper(mListener); 4672 4673 wrapper.forget(_retval); 4674 mListener = aListener; 4675 if (aMustApplyContentConversion) { 4676 StoreListenerRequiresContentConversion(true); 4677 } 4678 return NS_OK; 4679 } 4680 4681 //----------------------------------------------------------------------------- 4682 // HttpBaseChannel helpers 4683 //----------------------------------------------------------------------------- 4684 4685 void HttpBaseChannel::ReleaseListeners() { 4686 MOZ_ASSERT(mCurrentThread->IsOnCurrentThread(), 4687 "Should only be called on the current thread"); 4688 4689 mListener = nullptr; 4690 mCallbacks = nullptr; 4691 mProgressSink = nullptr; 4692 mCompressListener = nullptr; 4693 mORB = nullptr; 4694 } 4695 4696 void HttpBaseChannel::DoNotifyListener() { 4697 LOG(("HttpBaseChannel::DoNotifyListener this=%p", this)); 4698 4699 // In case nsHttpChannel::OnStartRequest wasn't called (e.g. due to flag 4700 // LOAD_ONLY_IF_MODIFIED) we want to set AfterOnStartRequestBegun to true 4701 // before notifying listener. 4702 if (!LoadAfterOnStartRequestBegun()) { 4703 StoreAfterOnStartRequestBegun(true); 4704 } 4705 4706 if (mListener && !LoadOnStartRequestCalled()) { 4707 nsCOMPtr<nsIStreamListener> listener = mListener; 4708 StoreOnStartRequestCalled(true); 4709 listener->OnStartRequest(this); 4710 } 4711 StoreOnStartRequestCalled(true); 4712 4713 // Make sure IsPending is set to false. At this moment we are done from 4714 // the point of view of our consumer and we have to report our self 4715 // as not-pending. 4716 StoreIsPending(false); 4717 4718 // notify "http-on-before-stop-request" observers 4719 gHttpHandler->OnBeforeStopRequest(this); 4720 4721 if (mListener && !LoadOnStopRequestCalled()) { 4722 nsCOMPtr<nsIStreamListener> listener = mListener; 4723 StoreOnStopRequestCalled(true); 4724 listener->OnStopRequest(this, mStatus); 4725 } 4726 StoreOnStopRequestCalled(true); 4727 4728 // notify "http-on-stop-request" observers 4729 gHttpHandler->OnStopRequest(this); 4730 4731 // This channel has finished its job, potentially release any tail-blocked 4732 // requests with this. 4733 RemoveAsNonTailRequest(); 4734 4735 // We have to make sure to drop the references to listeners and callbacks 4736 // no longer needed. 4737 ReleaseListeners(); 4738 4739 DoNotifyListenerCleanup(); 4740 4741 // If this is a navigation, then we must let the docshell flush the reports 4742 // to the console later. The LoadDocument() is pointing at the detached 4743 // document that started the navigation. We want to show the reports on the 4744 // new document. Otherwise the console is wiped and the user never sees 4745 // the information. 4746 if (!IsNavigation()) { 4747 if (mLoadGroup) { 4748 FlushConsoleReports(mLoadGroup); 4749 } else { 4750 RefPtr<dom::Document> doc; 4751 mLoadInfo->GetLoadingDocument(getter_AddRefs(doc)); 4752 FlushConsoleReports(doc); 4753 } 4754 } 4755 } 4756 4757 void HttpBaseChannel::AddCookiesToRequest() { 4758 if (mLoadFlags & LOAD_ANONYMOUS) { 4759 return; 4760 } 4761 4762 bool useCookieService = (XRE_IsParentProcess()); 4763 nsAutoCString cookie; 4764 if (useCookieService) { 4765 nsICookieService* cs = gHttpHandler->GetCookieService(); 4766 if (cs) { 4767 cs->GetCookieStringFromHttp(mURI, this, cookie); 4768 } 4769 4770 if (cookie.IsEmpty()) { 4771 cookie = mUserSetCookieHeader; 4772 } else if (!mUserSetCookieHeader.IsEmpty()) { 4773 cookie.AppendLiteral("; "); 4774 cookie.Append(mUserSetCookieHeader); 4775 } 4776 } else { 4777 cookie = mUserSetCookieHeader; 4778 } 4779 4780 // If we are in the child process, we want the parent seeing any 4781 // cookie headers that might have been set by SetRequestHeader() 4782 SetRequestHeader(nsHttp::Cookie.val(), cookie, false); 4783 } 4784 4785 /* static */ 4786 void HttpBaseChannel::PropagateReferenceIfNeeded( 4787 nsIURI* aURI, nsCOMPtr<nsIURI>& aRedirectURI) { 4788 bool hasRef = false; 4789 nsresult rv = aRedirectURI->GetHasRef(&hasRef); 4790 if (NS_SUCCEEDED(rv) && !hasRef) { 4791 nsAutoCString ref; 4792 aURI->GetRef(ref); 4793 if (!ref.IsEmpty()) { 4794 // NOTE: SetRef will fail if mRedirectURI is immutable 4795 // (e.g. an about: URI)... Oh well. 4796 (void)NS_MutateURI(aRedirectURI).SetRef(ref).Finalize(aRedirectURI); 4797 } 4798 } 4799 } 4800 4801 bool HttpBaseChannel::ShouldRewriteRedirectToGET( 4802 uint32_t httpStatus, nsHttpRequestHead::ParsedMethodType method) { 4803 // for 301 and 302, only rewrite POST 4804 if (httpStatus == 301 || httpStatus == 302) { 4805 return method == nsHttpRequestHead::kMethod_Post; 4806 } 4807 4808 // rewrite for 303 unless it was HEAD 4809 if (httpStatus == 303) return method != nsHttpRequestHead::kMethod_Head; 4810 4811 // otherwise, such as for 307, do not rewrite 4812 return false; 4813 } 4814 4815 NS_IMETHODIMP 4816 HttpBaseChannel::ShouldStripRequestBodyHeader(const nsACString& aMethod, 4817 bool* aResult) { 4818 *aResult = false; 4819 uint32_t httpStatus = 0; 4820 if (NS_FAILED(GetResponseStatus(&httpStatus))) { 4821 return NS_OK; 4822 } 4823 4824 nsAutoCString method(aMethod); 4825 nsHttpRequestHead::ParsedMethodType parsedMethod; 4826 nsHttpRequestHead::ParseMethod(method, parsedMethod); 4827 // Fetch 4.4.11, which is slightly different than the perserved method 4828 // algrorithm: strip request-body-header for GET->GET redirection for 303. 4829 *aResult = 4830 ShouldRewriteRedirectToGET(httpStatus, parsedMethod) && 4831 !(httpStatus == 303 && parsedMethod == nsHttpRequestHead::kMethod_Get); 4832 4833 return NS_OK; 4834 } 4835 4836 HttpBaseChannel::ReplacementChannelConfig 4837 HttpBaseChannel::CloneReplacementChannelConfig(bool aPreserveMethod, 4838 uint32_t aRedirectFlags, 4839 ReplacementReason aReason) { 4840 ReplacementChannelConfig config; 4841 config.redirectFlags = aRedirectFlags; 4842 config.classOfService = mClassOfService; 4843 4844 if (mPrivateBrowsingOverriden) { 4845 config.privateBrowsing = Some(mPrivateBrowsing); 4846 } 4847 4848 if (mReferrerInfo) { 4849 // When cloning for a document channel replacement (parent process 4850 // copying values for a new content process channel), this happens after 4851 // OnStartRequest so we have the headers for the response available. 4852 // We don't want to apply them to the referrer for the channel though, 4853 // since that is the referrer for the current document, and the header 4854 // should only apply to navigations from the current document. 4855 if (aReason == ReplacementReason::DocumentChannel) { 4856 config.referrerInfo = mReferrerInfo; 4857 } else { 4858 dom::ReferrerPolicy referrerPolicy = dom::ReferrerPolicy::_empty; 4859 nsAutoCString tRPHeaderCValue; 4860 (void)GetResponseHeader("referrer-policy"_ns, tRPHeaderCValue); 4861 NS_ConvertUTF8toUTF16 tRPHeaderValue(tRPHeaderCValue); 4862 4863 if (!tRPHeaderValue.IsEmpty()) { 4864 referrerPolicy = 4865 dom::ReferrerInfo::ReferrerPolicyFromHeaderString(tRPHeaderValue); 4866 } 4867 4868 // In case we are here because an upgrade happened through mixed content 4869 // upgrading, CSP upgrade-insecure-requests, HTTPS-Only or HTTPS-First, we 4870 // have to recalculate the referrer based on the original referrer to 4871 // account for the different scheme. This does NOT apply to HSTS. 4872 // See Bug 1857894 and order of https://fetch.spec.whatwg.org/#main-fetch. 4873 // Otherwise, if we have a new referrer policy, we want to recalculate the 4874 // referrer based on the old computed referrer (Bug 1678545). 4875 bool wasNonHSTSUpgrade = 4876 (aRedirectFlags & nsIChannelEventSink::REDIRECT_STS_UPGRADE) && 4877 (!mLoadInfo->GetHstsStatus()); 4878 if (wasNonHSTSUpgrade) { 4879 nsCOMPtr<nsIURI> referrer = mReferrerInfo->GetOriginalReferrer(); 4880 config.referrerInfo = 4881 new dom::ReferrerInfo(referrer, mReferrerInfo->ReferrerPolicy(), 4882 mReferrerInfo->GetSendReferrer()); 4883 } else if (referrerPolicy != dom::ReferrerPolicy::_empty) { 4884 nsCOMPtr<nsIURI> referrer = mReferrerInfo->GetComputedReferrer(); 4885 config.referrerInfo = new dom::ReferrerInfo( 4886 referrer, referrerPolicy, mReferrerInfo->GetSendReferrer()); 4887 } else { 4888 config.referrerInfo = mReferrerInfo; 4889 } 4890 } 4891 } 4892 4893 nsCOMPtr<nsITimedChannel> oldTimedChannel( 4894 do_QueryInterface(static_cast<nsIHttpChannel*>(this))); 4895 if (oldTimedChannel) { 4896 config.timedChannelInfo = Some(dom::TimedChannelInfo()); 4897 config.timedChannelInfo->redirectCount() = mRedirectCount; 4898 config.timedChannelInfo->internalRedirectCount() = mInternalRedirectCount; 4899 config.timedChannelInfo->asyncOpen() = mAsyncOpenTime; 4900 config.timedChannelInfo->channelCreation() = mChannelCreationTimestamp; 4901 config.timedChannelInfo->redirectStart() = mRedirectStartTimeStamp; 4902 config.timedChannelInfo->redirectEnd() = mRedirectEndTimeStamp; 4903 config.timedChannelInfo->initiatorType() = mInitiatorType; 4904 config.timedChannelInfo->allRedirectsSameOrigin() = 4905 LoadAllRedirectsSameOrigin(); 4906 config.timedChannelInfo->allRedirectsPassTimingAllowCheck() = 4907 LoadAllRedirectsPassTimingAllowCheck(); 4908 // Execute the timing allow check to determine whether 4909 // to report the redirect timing info 4910 nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo(); 4911 // TYPE_DOCUMENT loads don't have a loadingPrincipal, so we can't set 4912 // AllRedirectsPassTimingAllowCheck on them. 4913 if (loadInfo->GetExternalContentPolicyType() != 4914 ExtContentPolicy::TYPE_DOCUMENT) { 4915 nsCOMPtr<nsIPrincipal> principal = loadInfo->GetLoadingPrincipal(); 4916 config.timedChannelInfo->timingAllowCheckForPrincipal() = 4917 Some(oldTimedChannel->TimingAllowCheck(principal)); 4918 } 4919 4920 config.timedChannelInfo->allRedirectsPassTimingAllowCheck() = 4921 LoadAllRedirectsPassTimingAllowCheck(); 4922 config.timedChannelInfo->launchServiceWorkerStart() = 4923 mLaunchServiceWorkerStart; 4924 config.timedChannelInfo->launchServiceWorkerEnd() = mLaunchServiceWorkerEnd; 4925 config.timedChannelInfo->dispatchFetchEventStart() = 4926 mDispatchFetchEventStart; 4927 config.timedChannelInfo->dispatchFetchEventEnd() = mDispatchFetchEventEnd; 4928 config.timedChannelInfo->handleFetchEventStart() = mHandleFetchEventStart; 4929 config.timedChannelInfo->handleFetchEventEnd() = mHandleFetchEventEnd; 4930 config.timedChannelInfo->responseStart() = 4931 mTransactionTimings.responseStart; 4932 config.timedChannelInfo->responseEnd() = mTransactionTimings.responseEnd; 4933 } 4934 4935 if (aPreserveMethod) { 4936 // since preserveMethod is true, we need to ensure that the appropriate 4937 // request method gets set on the channel, regardless of whether or not 4938 // we set the upload stream above. This means SetRequestMethod() will 4939 // be called twice if ExplicitSetUploadStream() gets called above. 4940 4941 nsAutoCString method; 4942 mRequestHead.Method(method); 4943 config.method = Some(method); 4944 4945 if (mUploadStream) { 4946 // rewind upload stream 4947 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream); 4948 if (seekable) { 4949 seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); 4950 } 4951 config.uploadStream = mUploadStream; 4952 } 4953 config.uploadStreamLength = mReqContentLength; 4954 config.uploadStreamHasHeaders = LoadUploadStreamHasHeaders(); 4955 4956 nsAutoCString contentType; 4957 nsresult rv = mRequestHead.GetHeader(nsHttp::Content_Type, contentType); 4958 if (NS_SUCCEEDED(rv)) { 4959 config.contentType = Some(contentType); 4960 } 4961 4962 nsAutoCString contentLength; 4963 rv = mRequestHead.GetHeader(nsHttp::Content_Length, contentLength); 4964 if (NS_SUCCEEDED(rv)) { 4965 config.contentLength = Some(contentLength); 4966 } 4967 } 4968 4969 return config; 4970 } 4971 4972 /* static */ void HttpBaseChannel::ConfigureReplacementChannel( 4973 nsIChannel* newChannel, const ReplacementChannelConfig& config, 4974 ReplacementReason aReason) { 4975 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(newChannel)); 4976 if (cos) { 4977 cos->SetClassOfService(config.classOfService); 4978 } 4979 4980 // Try to preserve the privacy bit if it has been overridden 4981 if (config.privateBrowsing) { 4982 nsCOMPtr<nsIPrivateBrowsingChannel> newPBChannel = 4983 do_QueryInterface(newChannel); 4984 if (newPBChannel) { 4985 newPBChannel->SetPrivate(*config.privateBrowsing); 4986 } 4987 } 4988 4989 // Transfer the timing data (if we are dealing with an nsITimedChannel). 4990 nsCOMPtr<nsITimedChannel> newTimedChannel(do_QueryInterface(newChannel)); 4991 if (config.timedChannelInfo && newTimedChannel) { 4992 // If we're an internal redirect, or a document channel replacement, 4993 // then we shouldn't record any new timing for this and just copy 4994 // over the existing values. 4995 bool shouldHideTiming = aReason != ReplacementReason::Redirect; 4996 if (shouldHideTiming) { 4997 newTimedChannel->SetRedirectCount( 4998 config.timedChannelInfo->redirectCount()); 4999 int32_t newCount = config.timedChannelInfo->internalRedirectCount() + 1; 5000 newTimedChannel->SetInternalRedirectCount(std::max( 5001 newCount, static_cast<int32_t>( 5002 config.timedChannelInfo->internalRedirectCount()))); 5003 } else { 5004 int32_t newCount = config.timedChannelInfo->redirectCount() + 1; 5005 newTimedChannel->SetRedirectCount(std::max( 5006 newCount, 5007 static_cast<int32_t>(config.timedChannelInfo->redirectCount()))); 5008 newTimedChannel->SetInternalRedirectCount( 5009 config.timedChannelInfo->internalRedirectCount()); 5010 } 5011 5012 if (shouldHideTiming) { 5013 if (!config.timedChannelInfo->channelCreation().IsNull()) { 5014 newTimedChannel->SetChannelCreation( 5015 config.timedChannelInfo->channelCreation()); 5016 } 5017 5018 if (!config.timedChannelInfo->asyncOpen().IsNull()) { 5019 newTimedChannel->SetAsyncOpen(config.timedChannelInfo->asyncOpen()); 5020 } 5021 } 5022 5023 // If the RedirectStart is null, we will use the AsyncOpen value of the 5024 // previous channel (this is the first redirect in the redirects chain). 5025 if (config.timedChannelInfo->redirectStart().IsNull()) { 5026 // Only do this for real redirects. Internal redirects should be hidden. 5027 if (!shouldHideTiming) { 5028 newTimedChannel->SetRedirectStart(config.timedChannelInfo->asyncOpen()); 5029 } 5030 } else { 5031 newTimedChannel->SetRedirectStart( 5032 config.timedChannelInfo->redirectStart()); 5033 } 5034 5035 // For internal redirects just propagate the last redirect end time 5036 // forward. Otherwise the new redirect end time is the last response 5037 // end time. 5038 TimeStamp newRedirectEnd; 5039 if (shouldHideTiming) { 5040 newRedirectEnd = config.timedChannelInfo->redirectEnd(); 5041 } else if (!config.timedChannelInfo->responseEnd().IsNull()) { 5042 newRedirectEnd = config.timedChannelInfo->responseEnd(); 5043 } else { 5044 newRedirectEnd = TimeStamp::Now(); 5045 } 5046 newTimedChannel->SetRedirectEnd(newRedirectEnd); 5047 5048 newTimedChannel->SetInitiatorType(config.timedChannelInfo->initiatorType()); 5049 5050 nsCOMPtr<nsILoadInfo> loadInfo = newChannel->LoadInfo(); 5051 MOZ_ASSERT(loadInfo); 5052 5053 newTimedChannel->SetAllRedirectsSameOrigin( 5054 config.timedChannelInfo->allRedirectsSameOrigin()); 5055 5056 if (config.timedChannelInfo->timingAllowCheckForPrincipal()) { 5057 newTimedChannel->SetAllRedirectsPassTimingAllowCheck( 5058 config.timedChannelInfo->allRedirectsPassTimingAllowCheck() && 5059 *config.timedChannelInfo->timingAllowCheckForPrincipal()); 5060 } 5061 5062 // Propagate service worker measurements across redirects. The 5063 // PeformanceResourceTiming.workerStart API expects to see the 5064 // worker start time after a redirect. 5065 newTimedChannel->SetLaunchServiceWorkerStart( 5066 config.timedChannelInfo->launchServiceWorkerStart()); 5067 newTimedChannel->SetLaunchServiceWorkerEnd( 5068 config.timedChannelInfo->launchServiceWorkerEnd()); 5069 newTimedChannel->SetDispatchFetchEventStart( 5070 config.timedChannelInfo->dispatchFetchEventStart()); 5071 newTimedChannel->SetDispatchFetchEventEnd( 5072 config.timedChannelInfo->dispatchFetchEventEnd()); 5073 newTimedChannel->SetHandleFetchEventStart( 5074 config.timedChannelInfo->handleFetchEventStart()); 5075 newTimedChannel->SetHandleFetchEventEnd( 5076 config.timedChannelInfo->handleFetchEventEnd()); 5077 } 5078 5079 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel); 5080 if (!httpChannel) { 5081 return; // no other options to set 5082 } 5083 5084 if (config.uploadStream) { 5085 nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(httpChannel); 5086 // replicate original call to SetUploadStream... 5087 if (uploadChannel2) { 5088 const nsACString& ctype = 5089 config.contentType ? *config.contentType : VoidCString(); 5090 // If header is not present mRequestHead.HasHeaderValue will truncated 5091 // it. But we want to end up with a void string, not an empty string, 5092 // because ExplicitSetUploadStream treats the former as "no header" and 5093 // the latter as "header with empty string value". 5094 const nsACString& method = config.method ? *config.method : VoidCString(); 5095 uploadChannel2->ExplicitSetUploadStream(config.uploadStream, ctype, 5096 config.uploadStreamLength, method, 5097 config.uploadStreamHasHeaders); 5098 } else if (nsCOMPtr<nsIUploadChannel> uploadChannel = 5099 do_QueryInterface(httpChannel)) { 5100 MOZ_ASSERT(false, 5101 "Should not QI to nsIUploadChannel but not nsIUploadChannel2"); 5102 } 5103 } 5104 5105 if (config.referrerInfo) { 5106 DebugOnly<nsresult> success{}; 5107 success = httpChannel->SetReferrerInfo(config.referrerInfo); 5108 MOZ_ASSERT(NS_SUCCEEDED(success)); 5109 } 5110 5111 if (config.method) { 5112 DebugOnly<nsresult> rv = httpChannel->SetRequestMethod(*config.method); 5113 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5114 } 5115 } 5116 5117 HttpBaseChannel::ReplacementChannelConfig::ReplacementChannelConfig( 5118 const dom::ReplacementChannelConfigInit& aInit) { 5119 redirectFlags = aInit.redirectFlags(); 5120 classOfService = aInit.classOfService(); 5121 privateBrowsing = aInit.privateBrowsing(); 5122 method = aInit.method(); 5123 referrerInfo = aInit.referrerInfo(); 5124 timedChannelInfo = aInit.timedChannelInfo(); 5125 uploadStream = aInit.uploadStream(); 5126 uploadStreamLength = aInit.uploadStreamLength(); 5127 uploadStreamHasHeaders = aInit.uploadStreamHasHeaders(); 5128 contentType = aInit.contentType(); 5129 contentLength = aInit.contentLength(); 5130 } 5131 5132 dom::ReplacementChannelConfigInit 5133 HttpBaseChannel::ReplacementChannelConfig::Serialize() { 5134 dom::ReplacementChannelConfigInit config; 5135 config.redirectFlags() = redirectFlags; 5136 config.classOfService() = classOfService; 5137 config.privateBrowsing() = privateBrowsing; 5138 config.method() = method; 5139 config.referrerInfo() = referrerInfo; 5140 config.timedChannelInfo() = timedChannelInfo; 5141 config.uploadStream() = 5142 uploadStream ? RemoteLazyInputStream::WrapStream(uploadStream) : nullptr; 5143 config.uploadStreamLength() = uploadStreamLength; 5144 config.uploadStreamHasHeaders() = uploadStreamHasHeaders; 5145 config.contentType() = contentType; 5146 config.contentLength() = contentLength; 5147 5148 return config; 5149 } 5150 5151 nsresult HttpBaseChannel::SetupReplacementChannel(nsIURI* newURI, 5152 nsIChannel* newChannel, 5153 bool preserveMethod, 5154 uint32_t redirectFlags) { 5155 nsresult rv; 5156 5157 LOG( 5158 ("HttpBaseChannel::SetupReplacementChannel " 5159 "[this=%p newChannel=%p preserveMethod=%d]", 5160 this, newChannel, preserveMethod)); 5161 5162 // Ensure the channel's loadInfo's result principal URI so that it's 5163 // either non-null or updated to the redirect target URI. 5164 // We must do this because in case the loadInfo's result principal URI 5165 // is null, it would be taken from OriginalURI of the channel. But we 5166 // overwrite it with the whole redirect chain first URI before opening 5167 // the target channel, hence the information would be lost. 5168 // If the protocol handler that created the channel wants to use 5169 // the originalURI of the channel as the principal URI, this fulfills 5170 // that request - newURI is the original URI of the channel. 5171 nsCOMPtr<nsILoadInfo> newLoadInfo = newChannel->LoadInfo(); 5172 nsCOMPtr<nsIURI> resultPrincipalURI; 5173 rv = newLoadInfo->GetResultPrincipalURI(getter_AddRefs(resultPrincipalURI)); 5174 NS_ENSURE_SUCCESS(rv, rv); 5175 if (!resultPrincipalURI) { 5176 rv = newLoadInfo->SetResultPrincipalURI(newURI); 5177 NS_ENSURE_SUCCESS(rv, rv); 5178 } 5179 5180 nsLoadFlags loadFlags = mLoadFlags; 5181 loadFlags |= LOAD_REPLACE; 5182 5183 // if the original channel was using SSL and this channel is not using 5184 // SSL, then no need to inhibit persistent caching. however, if the 5185 // original channel was not using SSL and has INHIBIT_PERSISTENT_CACHING 5186 // set, then allow the flag to apply to the redirected channel as well. 5187 // since we force set INHIBIT_PERSISTENT_CACHING on all HTTPS channels, 5188 // we only need to check if the original channel was using SSL. 5189 if (mURI->SchemeIs("https")) { 5190 loadFlags &= ~INHIBIT_PERSISTENT_CACHING; 5191 } 5192 5193 newChannel->SetLoadFlags(loadFlags); 5194 5195 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel); 5196 5197 ReplacementReason redirectType = 5198 redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL | 5199 nsIChannelEventSink::REDIRECT_TRANSPARENT) 5200 ? ReplacementReason::InternalRedirect 5201 : ReplacementReason::Redirect; 5202 ReplacementChannelConfig config = CloneReplacementChannelConfig( 5203 preserveMethod, redirectFlags, redirectType); 5204 ConfigureReplacementChannel(newChannel, config, redirectType); 5205 5206 // Check whether or not this was a cross-domain redirect. 5207 nsCOMPtr<nsITimedChannel> newTimedChannel(do_QueryInterface(newChannel)); 5208 bool sameOriginWithOriginalUri = SameOriginWithOriginalUri(newURI); 5209 if (config.timedChannelInfo && newTimedChannel) { 5210 newTimedChannel->SetAllRedirectsSameOrigin( 5211 config.timedChannelInfo->allRedirectsSameOrigin() && 5212 sameOriginWithOriginalUri); 5213 } 5214 5215 newChannel->SetLoadGroup(mLoadGroup); 5216 newChannel->SetNotificationCallbacks(mCallbacks); 5217 // TODO: create tests for cross-origin redirect in bug 1662896. 5218 if (sameOriginWithOriginalUri) { 5219 newChannel->SetContentDisposition(mContentDispositionHint); 5220 if (mContentDispositionFilename) { 5221 newChannel->SetContentDispositionFilename(*mContentDispositionFilename); 5222 } 5223 } 5224 5225 if (!httpChannel) return NS_OK; // no other options to set 5226 5227 // Preserve the CORS preflight information. 5228 nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel); 5229 if (httpInternal) { 5230 httpInternal->SetLastRedirectFlags(redirectFlags); 5231 5232 if (LoadRequireCORSPreflight()) { 5233 httpInternal->SetCorsPreflightParameters(mUnsafeHeaders, false, false); 5234 } 5235 } 5236 5237 // convey the LoadAllowSTS() flags 5238 rv = httpChannel->SetAllowSTS(LoadAllowSTS()); 5239 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5240 5241 // convey the Accept header value 5242 { 5243 nsAutoCString oldAcceptValue; 5244 nsresult hasHeader = mRequestHead.GetHeader(nsHttp::Accept, oldAcceptValue); 5245 if (NS_SUCCEEDED(hasHeader)) { 5246 rv = httpChannel->SetRequestHeader("Accept"_ns, oldAcceptValue, false); 5247 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5248 } 5249 } 5250 5251 // convey the User-Agent header value 5252 // since we might be setting custom user agent from DevTools. 5253 if (httpInternal && mRequestMode == RequestMode::No_cors && 5254 redirectType == ReplacementReason::Redirect) { 5255 nsAutoCString oldUserAgent; 5256 nsresult hasHeader = 5257 mRequestHead.GetHeader(nsHttp::User_Agent, oldUserAgent); 5258 if (NS_SUCCEEDED(hasHeader)) { 5259 rv = httpChannel->SetRequestHeader("User-Agent"_ns, oldUserAgent, false); 5260 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5261 } 5262 } 5263 5264 // convery the IsUserAgentHeaderModified value. 5265 if (httpInternal) { 5266 rv = httpInternal->SetIsUserAgentHeaderModified( 5267 LoadIsUserAgentHeaderModified()); 5268 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5269 } 5270 5271 // share the request context - see bug 1236650 5272 rv = httpChannel->SetRequestContextID(mRequestContextID); 5273 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5274 5275 // When on the parent process, the channel can't attempt to get it itself. 5276 // When on the child process, it would be waste to query it again. 5277 rv = httpChannel->SetBrowserId(mBrowserId); 5278 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5279 5280 // Not setting this flag would break carrying permissions down to the child 5281 // process when the channel is artificially forced to be a main document load. 5282 rv = httpChannel->SetIsMainDocumentChannel(LoadForceMainDocumentChannel()); 5283 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5284 5285 // Preserve the loading order 5286 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(newChannel); 5287 if (p) { 5288 p->SetPriority(mPriority); 5289 } 5290 5291 if (httpInternal) { 5292 // Convey third party cookie, conservative, and spdy flags. 5293 rv = httpInternal->SetThirdPartyFlags(LoadThirdPartyFlags()); 5294 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5295 rv = httpInternal->SetAllowSpdy(LoadAllowSpdy()); 5296 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5297 rv = httpInternal->SetAllowHttp3(LoadAllowHttp3()); 5298 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5299 rv = httpInternal->SetAllowAltSvc(LoadAllowAltSvc()); 5300 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5301 rv = httpInternal->SetBeConservative(LoadBeConservative()); 5302 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5303 rv = httpInternal->SetIsTRRServiceChannel(LoadIsTRRServiceChannel()); 5304 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5305 rv = httpInternal->SetTlsFlags(mTlsFlags); 5306 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5307 5308 // Ensure the type of realChannel involves all types it may redirect to. 5309 // Such as nsHttpChannel and InterceptedChannel. 5310 // Even thought InterceptedChannel itself doesn't require these information, 5311 // it may still be necessary for the following redirections. 5312 // E.g. nsHttpChannel -> InterceptedChannel -> nsHttpChannel 5313 RefPtr<HttpBaseChannel> realChannel; 5314 CallQueryInterface(newChannel, realChannel.StartAssignment()); 5315 if (realChannel) { 5316 realChannel->SetTopWindowURI(mTopWindowURI); 5317 5318 realChannel->StoreTaintedOriginFlag( 5319 ShouldTaintReplacementChannelOrigin(newChannel, redirectFlags)); 5320 } 5321 5322 // update the DocumentURI indicator since we are being redirected. 5323 // if this was a top-level document channel, then the new channel 5324 // should have its mDocumentURI point to newURI; otherwise, we 5325 // just need to pass along our mDocumentURI to the new channel. 5326 if (newURI && (mURI == mDocumentURI)) { 5327 rv = httpInternal->SetDocumentURI(newURI); 5328 } else { 5329 rv = httpInternal->SetDocumentURI(mDocumentURI); 5330 } 5331 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5332 5333 // if there is a chain of keys for redirect-responses we transfer it to 5334 // the new channel (see bug #561276) 5335 { 5336 auto redirectedCachekeys = mRedirectedCachekeys.Lock(); 5337 auto& ref = redirectedCachekeys.ref(); 5338 if (ref) { 5339 LOG( 5340 ("HttpBaseChannel::SetupReplacementChannel " 5341 "[this=%p] transferring chain of redirect cache-keys", 5342 this)); 5343 rv = httpInternal->SetCacheKeysRedirectChain(ref.release()); 5344 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5345 } 5346 } 5347 5348 // Preserve Request mode. 5349 rv = httpInternal->SetRequestMode(mRequestMode); 5350 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5351 5352 // Preserve Redirect mode flag. 5353 rv = httpInternal->SetRedirectMode(mRedirectMode); 5354 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5355 5356 httpInternal->SetAltDataForChild(LoadAltDataForChild()); 5357 if (LoadDisableAltDataCache()) { 5358 httpInternal->DisableAltDataCache(); 5359 } 5360 } 5361 5362 // transfer any properties 5363 nsCOMPtr<nsIWritablePropertyBag> bag(do_QueryInterface(newChannel)); 5364 if (bag) { 5365 for (const auto& entry : mPropertyHash) { 5366 bag->SetProperty(entry.GetKey(), entry.GetWeak()); 5367 } 5368 } 5369 5370 // Pass the preferred alt-data type on to the new channel. 5371 nsCOMPtr<nsICacheInfoChannel> cacheInfoChan(do_QueryInterface(newChannel)); 5372 if (cacheInfoChan) { 5373 for (auto& data : mPreferredCachedAltDataTypes) { 5374 cacheInfoChan->PreferAlternativeDataType(data.type(), data.contentType(), 5375 data.deliverAltData()); 5376 } 5377 5378 if (LoadForceValidateCacheContent()) { 5379 (void)cacheInfoChan->SetForceValidateCacheContent(true); 5380 } 5381 } 5382 5383 if (redirectFlags & (nsIChannelEventSink::REDIRECT_INTERNAL | 5384 nsIChannelEventSink::REDIRECT_STS_UPGRADE)) { 5385 // Copy non-origin related headers to the new channel. 5386 nsCOMPtr<nsIHttpHeaderVisitor> visitor = 5387 new AddHeadersToChannelVisitor(httpChannel); 5388 rv = mRequestHead.VisitHeaders(visitor); 5389 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5390 } 5391 5392 // we need to strip Authentication headers for cross-origin requests 5393 // Ref: https://fetch.spec.whatwg.org/#http-redirect-fetch 5394 nsAutoCString authHeader; 5395 if (NS_SUCCEEDED( 5396 httpChannel->GetRequestHeader("Authorization"_ns, authHeader)) && 5397 NS_ShouldRemoveAuthHeaderOnRedirect(static_cast<nsIChannel*>(this), 5398 newChannel, redirectFlags)) { 5399 rv = httpChannel->SetRequestHeader("Authorization"_ns, ""_ns, false); 5400 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5401 } 5402 5403 return NS_OK; 5404 } 5405 5406 // check whether the new channel is of same origin as the current channel 5407 bool HttpBaseChannel::IsNewChannelSameOrigin(nsIChannel* aNewChannel) { 5408 bool isSameOrigin = false; 5409 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); 5410 5411 if (!ssm) { 5412 return false; 5413 } 5414 5415 nsCOMPtr<nsIURI> newURI; 5416 NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI)); 5417 5418 nsresult rv = ssm->CheckSameOriginURI(newURI, mURI, false, false); 5419 if (NS_SUCCEEDED(rv)) { 5420 isSameOrigin = true; 5421 } 5422 5423 return isSameOrigin; 5424 } 5425 5426 bool HttpBaseChannel::ShouldTaintReplacementChannelOrigin( 5427 nsIChannel* aNewChannel, uint32_t aRedirectFlags) { 5428 if (LoadTaintedOriginFlag()) { 5429 return true; 5430 } 5431 5432 if (NS_IsInternalSameURIRedirect(this, aNewChannel, aRedirectFlags) || 5433 NS_IsHSTSUpgradeRedirect(this, aNewChannel, aRedirectFlags)) { 5434 return false; 5435 } 5436 5437 // If new channel is not of same origin we need to taint unless 5438 // mURI <-> mOriginalURI/LoadingPrincipal are same origin. 5439 if (IsNewChannelSameOrigin(aNewChannel)) { 5440 return false; 5441 } 5442 5443 nsresult rv; 5444 5445 if (mLoadInfo->GetLoadingPrincipal()) { 5446 bool sameOrigin = false; 5447 rv = mLoadInfo->GetLoadingPrincipal()->IsSameOrigin(mURI, &sameOrigin); 5448 if (NS_FAILED(rv)) { 5449 return true; 5450 } 5451 return !sameOrigin; 5452 } 5453 if (!mOriginalURI) { 5454 return true; 5455 } 5456 5457 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); 5458 if (!ssm) { 5459 return true; 5460 } 5461 5462 rv = ssm->CheckSameOriginURI(mOriginalURI, mURI, false, false); 5463 return NS_FAILED(rv); 5464 } 5465 5466 // Redirect Tracking 5467 bool HttpBaseChannel::SameOriginWithOriginalUri(nsIURI* aURI) { 5468 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); 5469 bool isPrivateWin = mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(); 5470 nsresult rv = 5471 ssm->CheckSameOriginURI(aURI, mOriginalURI, false, isPrivateWin); 5472 return (NS_SUCCEEDED(rv)); 5473 } 5474 5475 //----------------------------------------------------------------------------- 5476 // HttpBaseChannel::nsIClassifiedChannel 5477 5478 NS_IMETHODIMP 5479 HttpBaseChannel::GetMatchedList(nsACString& aList) { 5480 aList = mMatchedList; 5481 return NS_OK; 5482 } 5483 5484 NS_IMETHODIMP 5485 HttpBaseChannel::GetMatchedProvider(nsACString& aProvider) { 5486 aProvider = mMatchedProvider; 5487 return NS_OK; 5488 } 5489 5490 NS_IMETHODIMP 5491 HttpBaseChannel::GetMatchedFullHash(nsACString& aFullHash) { 5492 aFullHash = mMatchedFullHash; 5493 return NS_OK; 5494 } 5495 5496 NS_IMETHODIMP 5497 HttpBaseChannel::SetMatchedInfo(const nsACString& aList, 5498 const nsACString& aProvider, 5499 const nsACString& aFullHash) { 5500 NS_ENSURE_ARG(!aList.IsEmpty()); 5501 5502 mMatchedList = aList; 5503 mMatchedProvider = aProvider; 5504 mMatchedFullHash = aFullHash; 5505 return NS_OK; 5506 } 5507 5508 NS_IMETHODIMP 5509 HttpBaseChannel::GetMatchedTrackingLists(nsTArray<nsCString>& aLists) { 5510 aLists = mMatchedTrackingLists.Clone(); 5511 return NS_OK; 5512 } 5513 5514 NS_IMETHODIMP 5515 HttpBaseChannel::GetMatchedTrackingFullHashes( 5516 nsTArray<nsCString>& aFullHashes) { 5517 aFullHashes = mMatchedTrackingFullHashes.Clone(); 5518 return NS_OK; 5519 } 5520 5521 NS_IMETHODIMP 5522 HttpBaseChannel::SetMatchedTrackingInfo( 5523 const nsTArray<nsCString>& aLists, const nsTArray<nsCString>& aFullHashes) { 5524 NS_ENSURE_ARG(!aLists.IsEmpty()); 5525 // aFullHashes can be empty for non hash-matching algorithm, for example, 5526 // host based test entries in preference. 5527 5528 mMatchedTrackingLists = aLists.Clone(); 5529 mMatchedTrackingFullHashes = aFullHashes.Clone(); 5530 return NS_OK; 5531 } 5532 //----------------------------------------------------------------------------- 5533 // HttpBaseChannel::nsITimedChannel 5534 //----------------------------------------------------------------------------- 5535 5536 NS_IMETHODIMP 5537 HttpBaseChannel::GetChannelCreation(TimeStamp* _retval) { 5538 *_retval = mChannelCreationTimestamp; 5539 return NS_OK; 5540 } 5541 5542 NS_IMETHODIMP 5543 HttpBaseChannel::SetChannelCreation(TimeStamp aValue) { 5544 MOZ_DIAGNOSTIC_ASSERT(!aValue.IsNull()); 5545 TimeDuration adjust = aValue - mChannelCreationTimestamp; 5546 mChannelCreationTimestamp = aValue; 5547 mChannelCreationTime += (PRTime)adjust.ToMicroseconds(); 5548 return NS_OK; 5549 } 5550 5551 NS_IMETHODIMP 5552 HttpBaseChannel::GetAsyncOpen(TimeStamp* _retval) { 5553 *_retval = mAsyncOpenTime; 5554 return NS_OK; 5555 } 5556 5557 NS_IMETHODIMP 5558 HttpBaseChannel::SetAsyncOpen(TimeStamp aValue) { 5559 MOZ_DIAGNOSTIC_ASSERT(!aValue.IsNull()); 5560 mAsyncOpenTime = aValue; 5561 StoreAsyncOpenTimeOverriden(true); 5562 return NS_OK; 5563 } 5564 5565 /** 5566 * @return the number of redirects. There is no check for cross-domain 5567 * redirects. This check must be done by the consumers. 5568 */ 5569 NS_IMETHODIMP 5570 HttpBaseChannel::GetRedirectCount(uint8_t* aRedirectCount) { 5571 *aRedirectCount = mRedirectCount; 5572 return NS_OK; 5573 } 5574 5575 NS_IMETHODIMP 5576 HttpBaseChannel::SetRedirectCount(uint8_t aRedirectCount) { 5577 mRedirectCount = aRedirectCount; 5578 return NS_OK; 5579 } 5580 5581 NS_IMETHODIMP 5582 HttpBaseChannel::GetInternalRedirectCount(uint8_t* aRedirectCount) { 5583 *aRedirectCount = mInternalRedirectCount; 5584 return NS_OK; 5585 } 5586 5587 NS_IMETHODIMP 5588 HttpBaseChannel::SetInternalRedirectCount(uint8_t aRedirectCount) { 5589 mInternalRedirectCount = aRedirectCount; 5590 return NS_OK; 5591 } 5592 5593 NS_IMETHODIMP 5594 HttpBaseChannel::GetRedirectStart(TimeStamp* _retval) { 5595 *_retval = mRedirectStartTimeStamp; 5596 return NS_OK; 5597 } 5598 5599 NS_IMETHODIMP 5600 HttpBaseChannel::SetRedirectStart(TimeStamp aRedirectStart) { 5601 mRedirectStartTimeStamp = aRedirectStart; 5602 return NS_OK; 5603 } 5604 5605 NS_IMETHODIMP 5606 HttpBaseChannel::GetRedirectEnd(TimeStamp* _retval) { 5607 *_retval = mRedirectEndTimeStamp; 5608 return NS_OK; 5609 } 5610 5611 NS_IMETHODIMP 5612 HttpBaseChannel::SetRedirectEnd(TimeStamp aRedirectEnd) { 5613 mRedirectEndTimeStamp = aRedirectEnd; 5614 return NS_OK; 5615 } 5616 5617 NS_IMETHODIMP 5618 HttpBaseChannel::GetAllRedirectsSameOrigin(bool* aAllRedirectsSameOrigin) { 5619 *aAllRedirectsSameOrigin = LoadAllRedirectsSameOrigin(); 5620 return NS_OK; 5621 } 5622 5623 NS_IMETHODIMP 5624 HttpBaseChannel::SetAllRedirectsSameOrigin(bool aAllRedirectsSameOrigin) { 5625 StoreAllRedirectsSameOrigin(aAllRedirectsSameOrigin); 5626 return NS_OK; 5627 } 5628 5629 NS_IMETHODIMP 5630 HttpBaseChannel::GetAllRedirectsPassTimingAllowCheck(bool* aPassesCheck) { 5631 *aPassesCheck = LoadAllRedirectsPassTimingAllowCheck(); 5632 return NS_OK; 5633 } 5634 5635 NS_IMETHODIMP 5636 HttpBaseChannel::SetAllRedirectsPassTimingAllowCheck(bool aPassesCheck) { 5637 StoreAllRedirectsPassTimingAllowCheck(aPassesCheck); 5638 return NS_OK; 5639 } 5640 5641 // https://fetch.spec.whatwg.org/#cors-check 5642 bool HttpBaseChannel::PerformCORSCheck() { 5643 // Step 1 5644 // Let origin be the result of getting `Access-Control-Allow-Origin` 5645 // from response’s header list. 5646 nsAutoCString origin; 5647 nsresult rv = GetResponseHeader("Access-Control-Allow-Origin"_ns, origin); 5648 5649 // Step 2 5650 // If origin is null, then return failure. (Note: null, not 'null'). 5651 if (NS_FAILED(rv) || origin.IsVoid()) { 5652 return false; 5653 } 5654 5655 // Step 3 5656 // If request’s credentials mode is not "include" 5657 // and origin is `*`, then return success. 5658 uint32_t cookiePolicy = mLoadInfo->GetCookiePolicy(); 5659 if (cookiePolicy != nsILoadInfo::SEC_COOKIES_INCLUDE && 5660 origin.EqualsLiteral("*")) { 5661 return true; 5662 } 5663 5664 // Step 4 5665 // If the result of byte-serializing a request origin 5666 // with request is not origin, then return failure. 5667 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); 5668 nsCOMPtr<nsIPrincipal> resourcePrincipal; 5669 rv = ssm->GetChannelURIPrincipal(this, getter_AddRefs(resourcePrincipal)); 5670 if (NS_FAILED(rv) || !resourcePrincipal) { 5671 return false; 5672 } 5673 nsAutoCString serializedOrigin; 5674 nsContentSecurityManager::GetSerializedOrigin( 5675 mLoadInfo->TriggeringPrincipal(), resourcePrincipal, serializedOrigin, 5676 mLoadInfo); 5677 if (!serializedOrigin.Equals(origin)) { 5678 return false; 5679 } 5680 5681 // Step 5 5682 // If request’s credentials mode is not "include", then return success. 5683 if (cookiePolicy != nsILoadInfo::SEC_COOKIES_INCLUDE) { 5684 return true; 5685 } 5686 5687 // Step 6 5688 // Let credentials be the result of getting 5689 // `Access-Control-Allow-Credentials` from response’s header list. 5690 nsAutoCString credentials; 5691 rv = GetResponseHeader("Access-Control-Allow-Credentials"_ns, credentials); 5692 5693 // Step 7 and 8 5694 // If credentials is `true`, then return success. 5695 // (else) return failure. 5696 return NS_SUCCEEDED(rv) && credentials.EqualsLiteral("true"); 5697 } 5698 5699 NS_IMETHODIMP 5700 HttpBaseChannel::BodyInfoAccessAllowedCheck(nsIPrincipal* aOrigin, 5701 BodyInfoAccess* _retval) { 5702 // Per the Fetch spec, https://fetch.spec.whatwg.org/#response-body-info, 5703 // the bodyInfo for Resource Timing and Navigation Timing info consists of 5704 // encoded size, decoded size, and content type. It is however made opaque 5705 // whenever the response is turned into a network error, which sets its 5706 // bodyInfo to its default values (sizes=0, content-type=""). 5707 5708 // Case 1: 5709 // "no-cors" -> Upon success, fetch will return an opaque filtered response. 5710 // An opaque(-redirect) filtered response is a filtered response 5711 // whose ... body info is a new response body info. 5712 auto tainting = mLoadInfo->GetTainting(); 5713 if (tainting == mozilla::LoadTainting::Opaque) { 5714 *_retval = BodyInfoAccess::DISALLOWED; 5715 return NS_OK; 5716 } 5717 5718 // Case 2: 5719 // If request’s response tainting is "cors" and a CORS check for request 5720 // and response returns failure, then return a network error. 5721 if (tainting == mozilla::LoadTainting::CORS && !PerformCORSCheck()) { 5722 *_retval = BodyInfoAccess::DISALLOWED; 5723 return NS_OK; 5724 } 5725 5726 // Otherwise: 5727 // The fetch response handover, given a fetch params fetchParams 5728 // and a response response, run these steps: 5729 // processResponseEndOfBody: 5730 // - If fetchParams’s request’s mode is not "navigate" or response’s 5731 // has-cross-origin-redirects is false: 5732 // - Let mimeType be the result of extracting a MIME type from 5733 // response’s header list. 5734 // - If mimeType is not failure, then set bodyInfo’s content type to the 5735 // result of minimizing a supported MIME type given mimeType. 5736 dom::RequestMode requestMode; 5737 MOZ_ALWAYS_SUCCEEDS(GetRequestMode(&requestMode)); 5738 if (requestMode != RequestMode::Navigate || LoadAllRedirectsSameOrigin()) { 5739 *_retval = BodyInfoAccess::ALLOW_ALL; 5740 return NS_OK; 5741 } 5742 5743 *_retval = BodyInfoAccess::ALLOW_SIZES; 5744 return NS_OK; 5745 } 5746 5747 // https://fetch.spec.whatwg.org/#tao-check 5748 NS_IMETHODIMP 5749 HttpBaseChannel::TimingAllowCheck(nsIPrincipal* aOrigin, bool* _retval) { 5750 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); 5751 nsCOMPtr<nsIPrincipal> resourcePrincipal; 5752 nsresult rv = 5753 ssm->GetChannelURIPrincipal(this, getter_AddRefs(resourcePrincipal)); 5754 if (NS_FAILED(rv) || !resourcePrincipal || !aOrigin) { 5755 *_retval = false; 5756 return NS_OK; 5757 } 5758 5759 bool sameOrigin = false; 5760 rv = resourcePrincipal->Equals(aOrigin, &sameOrigin); 5761 5762 nsAutoCString serializedOrigin; 5763 nsContentSecurityManager::GetSerializedOrigin(aOrigin, resourcePrincipal, 5764 serializedOrigin, mLoadInfo); 5765 5766 // All redirects are same origin 5767 if (sameOrigin && (!serializedOrigin.IsEmpty() && 5768 !serializedOrigin.EqualsLiteral("null"))) { 5769 *_retval = true; 5770 return NS_OK; 5771 } 5772 5773 nsAutoCString headerValue; 5774 rv = GetResponseHeader("Timing-Allow-Origin"_ns, headerValue); 5775 if (NS_FAILED(rv)) { 5776 *_retval = false; 5777 return NS_OK; 5778 } 5779 5780 Tokenizer p(headerValue); 5781 Tokenizer::Token t; 5782 5783 p.Record(); 5784 nsAutoCString headerItem; 5785 while (p.Next(t)) { 5786 if (t.Type() == Tokenizer::TOKEN_EOF || 5787 t.Equals(Tokenizer::Token::Char(','))) { 5788 p.Claim(headerItem); 5789 nsHttp::TrimHTTPWhitespace(headerItem, headerItem); 5790 // If the list item contains a case-sensitive match for the value of the 5791 // origin, or a wildcard, return pass 5792 if (headerItem == serializedOrigin || headerItem == "*") { 5793 *_retval = true; 5794 return NS_OK; 5795 } 5796 // We start recording again for the following items in the list 5797 p.Record(); 5798 } 5799 } 5800 5801 *_retval = false; 5802 return NS_OK; 5803 } 5804 5805 NS_IMETHODIMP 5806 HttpBaseChannel::GetLaunchServiceWorkerStart(TimeStamp* _retval) { 5807 MOZ_ASSERT(_retval); 5808 *_retval = mLaunchServiceWorkerStart; 5809 return NS_OK; 5810 } 5811 5812 NS_IMETHODIMP 5813 HttpBaseChannel::SetLaunchServiceWorkerStart(TimeStamp aTimeStamp) { 5814 mLaunchServiceWorkerStart = aTimeStamp; 5815 return NS_OK; 5816 } 5817 5818 NS_IMETHODIMP 5819 HttpBaseChannel::GetLaunchServiceWorkerEnd(TimeStamp* _retval) { 5820 MOZ_ASSERT(_retval); 5821 *_retval = mLaunchServiceWorkerEnd; 5822 return NS_OK; 5823 } 5824 5825 NS_IMETHODIMP 5826 HttpBaseChannel::SetLaunchServiceWorkerEnd(TimeStamp aTimeStamp) { 5827 mLaunchServiceWorkerEnd = aTimeStamp; 5828 return NS_OK; 5829 } 5830 5831 NS_IMETHODIMP 5832 HttpBaseChannel::GetDispatchFetchEventStart(TimeStamp* _retval) { 5833 MOZ_ASSERT(_retval); 5834 *_retval = mDispatchFetchEventStart; 5835 return NS_OK; 5836 } 5837 5838 NS_IMETHODIMP 5839 HttpBaseChannel::SetDispatchFetchEventStart(TimeStamp aTimeStamp) { 5840 mDispatchFetchEventStart = aTimeStamp; 5841 return NS_OK; 5842 } 5843 5844 NS_IMETHODIMP 5845 HttpBaseChannel::GetDispatchFetchEventEnd(TimeStamp* _retval) { 5846 MOZ_ASSERT(_retval); 5847 *_retval = mDispatchFetchEventEnd; 5848 return NS_OK; 5849 } 5850 5851 NS_IMETHODIMP 5852 HttpBaseChannel::SetDispatchFetchEventEnd(TimeStamp aTimeStamp) { 5853 mDispatchFetchEventEnd = aTimeStamp; 5854 return NS_OK; 5855 } 5856 5857 NS_IMETHODIMP 5858 HttpBaseChannel::GetHandleFetchEventStart(TimeStamp* _retval) { 5859 MOZ_ASSERT(_retval); 5860 *_retval = mHandleFetchEventStart; 5861 return NS_OK; 5862 } 5863 5864 NS_IMETHODIMP 5865 HttpBaseChannel::SetHandleFetchEventStart(TimeStamp aTimeStamp) { 5866 mHandleFetchEventStart = aTimeStamp; 5867 return NS_OK; 5868 } 5869 5870 NS_IMETHODIMP 5871 HttpBaseChannel::GetHandleFetchEventEnd(TimeStamp* _retval) { 5872 MOZ_ASSERT(_retval); 5873 *_retval = mHandleFetchEventEnd; 5874 return NS_OK; 5875 } 5876 5877 NS_IMETHODIMP 5878 HttpBaseChannel::SetHandleFetchEventEnd(TimeStamp aTimeStamp) { 5879 mHandleFetchEventEnd = aTimeStamp; 5880 return NS_OK; 5881 } 5882 5883 NS_IMETHODIMP 5884 HttpBaseChannel::GetDomainLookupStart(TimeStamp* _retval) { 5885 *_retval = mTransactionTimings.domainLookupStart; 5886 return NS_OK; 5887 } 5888 5889 NS_IMETHODIMP 5890 HttpBaseChannel::GetDomainLookupEnd(TimeStamp* _retval) { 5891 *_retval = mTransactionTimings.domainLookupEnd; 5892 return NS_OK; 5893 } 5894 5895 NS_IMETHODIMP 5896 HttpBaseChannel::GetConnectStart(TimeStamp* _retval) { 5897 *_retval = mTransactionTimings.connectStart; 5898 return NS_OK; 5899 } 5900 5901 NS_IMETHODIMP 5902 HttpBaseChannel::GetTcpConnectEnd(TimeStamp* _retval) { 5903 *_retval = mTransactionTimings.tcpConnectEnd; 5904 return NS_OK; 5905 } 5906 5907 NS_IMETHODIMP 5908 HttpBaseChannel::GetSecureConnectionStart(TimeStamp* _retval) { 5909 *_retval = mTransactionTimings.secureConnectionStart; 5910 return NS_OK; 5911 } 5912 5913 NS_IMETHODIMP 5914 HttpBaseChannel::GetConnectEnd(TimeStamp* _retval) { 5915 *_retval = mTransactionTimings.connectEnd; 5916 return NS_OK; 5917 } 5918 5919 NS_IMETHODIMP 5920 HttpBaseChannel::GetRequestStart(TimeStamp* _retval) { 5921 *_retval = mTransactionTimings.requestStart; 5922 return NS_OK; 5923 } 5924 5925 NS_IMETHODIMP 5926 HttpBaseChannel::GetResponseStart(TimeStamp* _retval) { 5927 *_retval = mTransactionTimings.responseStart; 5928 return NS_OK; 5929 } 5930 5931 NS_IMETHODIMP 5932 HttpBaseChannel::GetResponseEnd(TimeStamp* _retval) { 5933 *_retval = mTransactionTimings.responseEnd; 5934 return NS_OK; 5935 } 5936 5937 NS_IMETHODIMP 5938 HttpBaseChannel::GetCacheReadStart(TimeStamp* _retval) { 5939 *_retval = mCacheReadStart; 5940 return NS_OK; 5941 } 5942 5943 NS_IMETHODIMP 5944 HttpBaseChannel::GetCacheReadEnd(TimeStamp* _retval) { 5945 *_retval = mCacheReadEnd; 5946 return NS_OK; 5947 } 5948 5949 NS_IMETHODIMP 5950 HttpBaseChannel::GetTransactionPending(TimeStamp* _retval) { 5951 *_retval = mTransactionTimings.transactionPending; 5952 return NS_OK; 5953 } 5954 5955 NS_IMETHODIMP 5956 HttpBaseChannel::GetInitiatorType(nsAString& aInitiatorType) { 5957 aInitiatorType = mInitiatorType; 5958 return NS_OK; 5959 } 5960 5961 NS_IMETHODIMP 5962 HttpBaseChannel::SetInitiatorType(const nsAString& aInitiatorType) { 5963 mInitiatorType = aInitiatorType; 5964 return NS_OK; 5965 } 5966 5967 #define IMPL_TIMING_ATTR(name) \ 5968 NS_IMETHODIMP \ 5969 HttpBaseChannel::Get##name##Time(PRTime* _retval) { \ 5970 TimeStamp stamp; \ 5971 Get##name(&stamp); \ 5972 if (stamp.IsNull()) { \ 5973 *_retval = 0; \ 5974 return NS_OK; \ 5975 } \ 5976 *_retval = \ 5977 mChannelCreationTime + \ 5978 (PRTime)((stamp - mChannelCreationTimestamp).ToSeconds() * 1e6); \ 5979 return NS_OK; \ 5980 } 5981 5982 IMPL_TIMING_ATTR(ChannelCreation) 5983 IMPL_TIMING_ATTR(AsyncOpen) 5984 IMPL_TIMING_ATTR(LaunchServiceWorkerStart) 5985 IMPL_TIMING_ATTR(LaunchServiceWorkerEnd) 5986 IMPL_TIMING_ATTR(DispatchFetchEventStart) 5987 IMPL_TIMING_ATTR(DispatchFetchEventEnd) 5988 IMPL_TIMING_ATTR(HandleFetchEventStart) 5989 IMPL_TIMING_ATTR(HandleFetchEventEnd) 5990 IMPL_TIMING_ATTR(DomainLookupStart) 5991 IMPL_TIMING_ATTR(DomainLookupEnd) 5992 IMPL_TIMING_ATTR(ConnectStart) 5993 IMPL_TIMING_ATTR(TcpConnectEnd) 5994 IMPL_TIMING_ATTR(SecureConnectionStart) 5995 IMPL_TIMING_ATTR(ConnectEnd) 5996 IMPL_TIMING_ATTR(RequestStart) 5997 IMPL_TIMING_ATTR(ResponseStart) 5998 IMPL_TIMING_ATTR(ResponseEnd) 5999 IMPL_TIMING_ATTR(CacheReadStart) 6000 IMPL_TIMING_ATTR(CacheReadEnd) 6001 IMPL_TIMING_ATTR(RedirectStart) 6002 IMPL_TIMING_ATTR(RedirectEnd) 6003 IMPL_TIMING_ATTR(TransactionPending) 6004 6005 #undef IMPL_TIMING_ATTR 6006 6007 void HttpBaseChannel::MaybeReportTimingData() { 6008 // There is no point in continuing, since the performance object in the parent 6009 // isn't the same as the one in the child which will be reporting resource 6010 // performance. 6011 if (XRE_IsE10sParentProcess()) { 6012 return; 6013 } 6014 6015 // Devtools can create fetch requests on behalf the content document. 6016 // If we don't exclude these requests, they'd also be reported 6017 // to the content document. 6018 bool isInDevToolsContext; 6019 mLoadInfo->GetIsInDevToolsContext(&isInDevToolsContext); 6020 if (isInDevToolsContext) { 6021 return; 6022 } 6023 6024 mozilla::dom::PerformanceStorage* documentPerformance = 6025 mLoadInfo->GetPerformanceStorage(); 6026 if (documentPerformance) { 6027 documentPerformance->AddEntry(this, this); 6028 return; 6029 } 6030 6031 if (!nsGlobalWindowInner::GetInnerWindowWithId( 6032 mLoadInfo->GetInnerWindowID())) { 6033 // The inner window is in a different process. 6034 dom::ContentChild* child = dom::ContentChild::GetSingleton(); 6035 6036 if (!child) { 6037 return; 6038 } 6039 nsAutoString initiatorType; 6040 nsAutoString entryName; 6041 6042 UniquePtr<dom::PerformanceTimingData> performanceTimingData( 6043 dom::PerformanceTimingData::Create(this, this, 0, initiatorType, 6044 entryName)); 6045 if (!performanceTimingData) { 6046 return; 6047 } 6048 6049 LoadInfoArgs loadInfoArgs; 6050 mozilla::ipc::LoadInfoToLoadInfoArgs(mLoadInfo, &loadInfoArgs); 6051 child->SendReportFrameTimingData(loadInfoArgs, entryName, initiatorType, 6052 std::move(performanceTimingData)); 6053 } 6054 } 6055 6056 NS_IMETHODIMP 6057 HttpBaseChannel::SetReportResourceTiming(bool enabled) { 6058 StoreReportTiming(enabled); 6059 return NS_OK; 6060 } 6061 6062 NS_IMETHODIMP 6063 HttpBaseChannel::GetReportResourceTiming(bool* _retval) { 6064 *_retval = LoadReportTiming(); 6065 return NS_OK; 6066 } 6067 6068 nsIURI* HttpBaseChannel::GetReferringPage() { 6069 nsCOMPtr<nsPIDOMWindowInner> pDomWindow = GetInnerDOMWindow(); 6070 if (!pDomWindow) { 6071 return nullptr; 6072 } 6073 return pDomWindow->GetDocumentURI(); 6074 } 6075 6076 nsPIDOMWindowInner* HttpBaseChannel::GetInnerDOMWindow() { 6077 nsCOMPtr<nsILoadContext> loadContext; 6078 NS_QueryNotificationCallbacks(this, loadContext); 6079 if (!loadContext) { 6080 return nullptr; 6081 } 6082 nsCOMPtr<mozIDOMWindowProxy> domWindow; 6083 loadContext->GetAssociatedWindow(getter_AddRefs(domWindow)); 6084 if (!domWindow) { 6085 return nullptr; 6086 } 6087 auto* pDomWindow = nsPIDOMWindowOuter::From(domWindow); 6088 if (!pDomWindow) { 6089 return nullptr; 6090 } 6091 nsCOMPtr<nsPIDOMWindowInner> innerWindow = 6092 pDomWindow->GetCurrentInnerWindow(); 6093 if (!innerWindow) { 6094 return nullptr; 6095 } 6096 6097 return innerWindow; 6098 } 6099 6100 //----------------------------------------------------------------------------- 6101 // HttpBaseChannel::nsIThrottledInputChannel 6102 //----------------------------------------------------------------------------- 6103 6104 NS_IMETHODIMP 6105 HttpBaseChannel::SetThrottleQueue(nsIInputChannelThrottleQueue* aQueue) { 6106 if (!XRE_IsParentProcess()) { 6107 return NS_ERROR_FAILURE; 6108 } 6109 6110 mThrottleQueue = aQueue; 6111 return NS_OK; 6112 } 6113 6114 NS_IMETHODIMP 6115 HttpBaseChannel::GetThrottleQueue(nsIInputChannelThrottleQueue** aQueue) { 6116 NS_ENSURE_ARG_POINTER(aQueue); 6117 nsCOMPtr<nsIInputChannelThrottleQueue> queue = mThrottleQueue; 6118 queue.forget(aQueue); 6119 return NS_OK; 6120 } 6121 6122 //------------------------------------------------------------------------------ 6123 6124 bool HttpBaseChannel::EnsureRequestContextID() { 6125 if (mRequestContextID) { 6126 // Already have a request context ID, no need to do the rest of this work 6127 LOG(("HttpBaseChannel::EnsureRequestContextID this=%p id=%" PRIx64, this, 6128 mRequestContextID)); 6129 return true; 6130 } 6131 6132 // Find the loadgroup at the end of the chain in order 6133 // to make sure all channels derived from the load group 6134 // use the same connection scope. 6135 nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(mLoadGroup); 6136 if (!childLoadGroup) { 6137 return false; 6138 } 6139 6140 nsCOMPtr<nsILoadGroup> rootLoadGroup; 6141 childLoadGroup->GetRootLoadGroup(getter_AddRefs(rootLoadGroup)); 6142 if (!rootLoadGroup) { 6143 return false; 6144 } 6145 6146 // Set the load group connection scope on this channel and its transaction 6147 rootLoadGroup->GetRequestContextID(&mRequestContextID); 6148 6149 LOG(("HttpBaseChannel::EnsureRequestContextID this=%p id=%" PRIx64, this, 6150 mRequestContextID)); 6151 6152 return true; 6153 } 6154 6155 bool HttpBaseChannel::EnsureRequestContext() { 6156 if (mRequestContext) { 6157 // Already have a request context, no need to do the rest of this work 6158 return true; 6159 } 6160 6161 if (!EnsureRequestContextID()) { 6162 return false; 6163 } 6164 6165 nsIRequestContextService* rcsvc = gHttpHandler->GetRequestContextService(); 6166 if (!rcsvc) { 6167 return false; 6168 } 6169 6170 rcsvc->GetRequestContext(mRequestContextID, getter_AddRefs(mRequestContext)); 6171 return static_cast<bool>(mRequestContext); 6172 } 6173 6174 void HttpBaseChannel::EnsureBrowserId() { 6175 if (mBrowserId) { 6176 return; 6177 } 6178 6179 RefPtr<dom::BrowsingContext> bc; 6180 MOZ_ALWAYS_SUCCEEDS(mLoadInfo->GetBrowsingContext(getter_AddRefs(bc))); 6181 6182 if (bc) { 6183 mBrowserId = bc->GetBrowserId(); 6184 } 6185 } 6186 6187 void HttpBaseChannel::SetCorsPreflightParameters( 6188 const nsTArray<nsCString>& aUnsafeHeaders, 6189 bool aShouldStripRequestBodyHeader, bool aShouldStripAuthHeader) { 6190 MOZ_RELEASE_ASSERT(!LoadRequestObserversCalled()); 6191 6192 StoreRequireCORSPreflight(true); 6193 mUnsafeHeaders = aUnsafeHeaders.Clone(); 6194 if (aShouldStripRequestBodyHeader || aShouldStripAuthHeader) { 6195 mUnsafeHeaders.RemoveElementsBy([&](const nsCString& aHeader) { 6196 return (aShouldStripRequestBodyHeader && 6197 (aHeader.LowerCaseEqualsASCII("content-type") || 6198 aHeader.LowerCaseEqualsASCII("content-encoding") || 6199 aHeader.LowerCaseEqualsASCII("content-language") || 6200 aHeader.LowerCaseEqualsASCII("content-location"))) || 6201 (aShouldStripAuthHeader && 6202 aHeader.LowerCaseEqualsASCII("authorization")); 6203 }); 6204 } 6205 } 6206 6207 void HttpBaseChannel::SetAltDataForChild(bool aIsForChild) { 6208 StoreAltDataForChild(aIsForChild); 6209 } 6210 6211 NS_IMETHODIMP 6212 HttpBaseChannel::GetBlockAuthPrompt(bool* aValue) { 6213 if (!aValue) { 6214 return NS_ERROR_FAILURE; 6215 } 6216 6217 *aValue = LoadBlockAuthPrompt(); 6218 return NS_OK; 6219 } 6220 6221 NS_IMETHODIMP 6222 HttpBaseChannel::SetBlockAuthPrompt(bool aValue) { 6223 ENSURE_CALLED_BEFORE_CONNECT(); 6224 6225 StoreBlockAuthPrompt(aValue); 6226 return NS_OK; 6227 } 6228 6229 NS_IMETHODIMP 6230 HttpBaseChannel::GetConnectionInfoHashKey(nsACString& aConnectionInfoHashKey) { 6231 if (!mConnectionInfo) { 6232 return NS_ERROR_FAILURE; 6233 } 6234 aConnectionInfoHashKey.Assign(mConnectionInfo->HashKey()); 6235 return NS_OK; 6236 } 6237 6238 NS_IMETHODIMP 6239 HttpBaseChannel::GetLastRedirectFlags(uint32_t* aValue) { 6240 NS_ENSURE_ARG(aValue); 6241 *aValue = mLastRedirectFlags; 6242 return NS_OK; 6243 } 6244 6245 NS_IMETHODIMP 6246 HttpBaseChannel::SetLastRedirectFlags(uint32_t aValue) { 6247 mLastRedirectFlags = aValue; 6248 return NS_OK; 6249 } 6250 6251 NS_IMETHODIMP 6252 HttpBaseChannel::GetNavigationStartTimeStamp(TimeStamp* aTimeStamp) { 6253 return NS_ERROR_NOT_IMPLEMENTED; 6254 } 6255 6256 NS_IMETHODIMP 6257 HttpBaseChannel::SetNavigationStartTimeStamp(TimeStamp aTimeStamp) { 6258 return NS_ERROR_NOT_IMPLEMENTED; 6259 } 6260 6261 nsresult HttpBaseChannel::CheckRedirectLimit(nsIURI* aNewURI, 6262 uint32_t aRedirectFlags) const { 6263 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_INTERNAL) { 6264 // for internal redirect due to auth retry we do not have any limit 6265 // as we might restrict the number of times a user might retry 6266 // authentication 6267 if (aRedirectFlags & nsIChannelEventSink::REDIRECT_AUTH_RETRY) { 6268 return NS_OK; 6269 } 6270 // Some platform features, like Service Workers, depend on internal 6271 // redirects. We should allow some number of internal redirects above 6272 // and beyond the normal redirect limit so these features continue 6273 // to work. 6274 static const int8_t kMinInternalRedirects = 5; 6275 6276 if (mInternalRedirectCount >= (mRedirectionLimit + kMinInternalRedirects)) { 6277 LOG(("internal redirection limit reached!\n")); 6278 return NS_ERROR_REDIRECT_LOOP; 6279 } 6280 return NS_OK; 6281 } 6282 6283 MOZ_ASSERT(aRedirectFlags & (nsIChannelEventSink::REDIRECT_TEMPORARY | 6284 nsIChannelEventSink::REDIRECT_PERMANENT | 6285 nsIChannelEventSink::REDIRECT_STS_UPGRADE)); 6286 6287 if (mRedirectCount >= mRedirectionLimit) { 6288 LOG(("redirection limit reached!\n")); 6289 return NS_ERROR_REDIRECT_LOOP; 6290 } 6291 6292 // in case https-only mode is enabled which upgrades top-level requests to 6293 // https and the page answers with a redirect (meta, 302, win.location, ...) 6294 // then this method can break the cycle which causes the https-only exception 6295 // page to appear. Note that https-first mode breaks upgrade downgrade endless 6296 // loops within ShouldUpgradeHttpsFirstRequest because https-first does not 6297 // display an exception page but needs a soft fallback/downgrade. 6298 if (nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop( 6299 mURI, aNewURI, mLoadInfo, 6300 {nsHTTPSOnlyUtils::UpgradeDowngradeEndlessLoopOptions:: 6301 EnforceForHTTPSOnlyMode})) { 6302 // Mark that we didn't upgrade to https due to loop detection in https-only 6303 // mode to show https-only error page. We know that we are in https-only 6304 // mode, because we passed `EnforceForHTTPSOnlyMode` to 6305 // `IsUpgradeDowngradeEndlessLoop`. In other words we upgrade the request 6306 // with https-only mode, but then immediately cancel the request. 6307 uint32_t httpsOnlyStatus = mLoadInfo->GetHttpsOnlyStatus(); 6308 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) { 6309 httpsOnlyStatus ^= nsILoadInfo::HTTPS_ONLY_UNINITIALIZED; 6310 httpsOnlyStatus |= 6311 nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED; 6312 mLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus); 6313 } 6314 6315 LOG(("upgrade downgrade redirect loop!\n")); 6316 return NS_ERROR_REDIRECT_LOOP; 6317 } 6318 // in case of http-first mode we want to add an exception to disable the 6319 // upgrade behavior if we have upgrade-downgrade loop to break the loop and 6320 // load the http request next 6321 if (mozilla::StaticPrefs:: 6322 dom_security_https_first_add_exception_on_failure() && 6323 nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop( 6324 mURI, aNewURI, mLoadInfo, 6325 {nsHTTPSOnlyUtils::UpgradeDowngradeEndlessLoopOptions:: 6326 EnforceForHTTPSFirstMode})) { 6327 nsHTTPSOnlyUtils::AddHTTPSFirstException(mURI, mLoadInfo); 6328 } 6329 6330 return NS_OK; 6331 } 6332 6333 // NOTE: This function duplicates code from nsBaseChannel. This will go away 6334 // once HTTP uses nsBaseChannel (part of bug 312760) 6335 /* static */ 6336 void HttpBaseChannel::CallTypeSniffers(void* aClosure, const uint8_t* aData, 6337 uint32_t aCount) { 6338 nsIChannel* chan = static_cast<nsIChannel*>(aClosure); 6339 const char* snifferType = [chan]() { 6340 if (RefPtr<nsHttpChannel> httpChannel = do_QueryObject(chan)) { 6341 switch (httpChannel->GetSnifferCategoryType()) { 6342 case SnifferCategoryType::NetContent: 6343 return NS_CONTENT_SNIFFER_CATEGORY; 6344 case SnifferCategoryType::OpaqueResponseBlocking: 6345 return NS_ORB_SNIFFER_CATEGORY; 6346 case SnifferCategoryType::All: 6347 return NS_CONTENT_AND_ORB_SNIFFER_CATEGORY; 6348 default: 6349 MOZ_ASSERT_UNREACHABLE("Unexpected SnifferCategoryType!"); 6350 } 6351 } 6352 6353 return NS_CONTENT_SNIFFER_CATEGORY; 6354 }(); 6355 6356 nsAutoCString newType; 6357 NS_SniffContent(snifferType, chan, aData, aCount, newType); 6358 if (!newType.IsEmpty()) { 6359 chan->SetContentType(newType); 6360 } 6361 } 6362 6363 template <class T> 6364 static void ParseServerTimingHeader( 6365 const UniquePtr<T>& aHeader, nsTArray<nsCOMPtr<nsIServerTiming>>& aOutput) { 6366 if (!aHeader) { 6367 return; 6368 } 6369 6370 nsAutoCString serverTimingHeader; 6371 (void)aHeader->GetHeader(nsHttp::Server_Timing, serverTimingHeader); 6372 if (serverTimingHeader.IsEmpty()) { 6373 return; 6374 } 6375 6376 ServerTimingParser parser(serverTimingHeader); 6377 parser.Parse(); 6378 6379 nsTArray<nsCOMPtr<nsIServerTiming>> array = parser.TakeServerTimingHeaders(); 6380 aOutput.AppendElements(array); 6381 } 6382 6383 NS_IMETHODIMP 6384 HttpBaseChannel::GetServerTiming(nsIArray** aServerTiming) { 6385 nsresult rv; 6386 NS_ENSURE_ARG_POINTER(aServerTiming); 6387 6388 nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); 6389 NS_ENSURE_SUCCESS(rv, rv); 6390 6391 nsTArray<nsCOMPtr<nsIServerTiming>> data; 6392 rv = GetNativeServerTiming(data); 6393 NS_ENSURE_SUCCESS(rv, rv); 6394 6395 for (const auto& entry : data) { 6396 array->AppendElement(entry); 6397 } 6398 6399 array.forget(aServerTiming); 6400 return NS_OK; 6401 } 6402 6403 NS_IMETHODIMP 6404 HttpBaseChannel::GetNativeServerTiming( 6405 nsTArray<nsCOMPtr<nsIServerTiming>>& aServerTiming) { 6406 aServerTiming.Clear(); 6407 6408 if (nsContentUtils::ComputeIsSecureContext(this)) { 6409 ParseServerTimingHeader(mResponseHead, aServerTiming); 6410 ParseServerTimingHeader(mResponseTrailers, aServerTiming); 6411 } 6412 6413 return NS_OK; 6414 } 6415 6416 NS_IMETHODIMP 6417 HttpBaseChannel::CancelByURLClassifier(nsresult aErrorCode) { 6418 MOZ_ASSERT( 6419 UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode)); 6420 return Cancel(aErrorCode); 6421 } 6422 6423 NS_IMETHODIMP HttpBaseChannel::SetIPv4Disabled() { 6424 mCaps |= NS_HTTP_DISABLE_IPV4; 6425 return NS_OK; 6426 } 6427 6428 NS_IMETHODIMP HttpBaseChannel::SetIPv6Disabled() { 6429 mCaps |= NS_HTTP_DISABLE_IPV6; 6430 return NS_OK; 6431 } 6432 6433 NS_IMETHODIMP HttpBaseChannel::GetResponseEmbedderPolicy( 6434 bool aIsOriginTrialCoepCredentiallessEnabled, 6435 nsILoadInfo::CrossOriginEmbedderPolicy* aOutPolicy) { 6436 *aOutPolicy = nsILoadInfo::EMBEDDER_POLICY_NULL; 6437 if (!mResponseHead) { 6438 return NS_ERROR_NOT_AVAILABLE; 6439 } 6440 6441 if (!nsContentUtils::ComputeIsSecureContext(this)) { 6442 // Feature is only available for secure contexts. 6443 return NS_OK; 6444 } 6445 6446 nsAutoCString content; 6447 (void)mResponseHead->GetHeader(nsHttp::Cross_Origin_Embedder_Policy, content); 6448 *aOutPolicy = NS_GetCrossOriginEmbedderPolicyFromHeader( 6449 content, aIsOriginTrialCoepCredentiallessEnabled); 6450 return NS_OK; 6451 } 6452 6453 // Obtain a cross-origin opener-policy from a response response and a 6454 // cross-origin opener policy initiator. 6455 // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e 6456 NS_IMETHODIMP HttpBaseChannel::ComputeCrossOriginOpenerPolicy( 6457 nsILoadInfo::CrossOriginOpenerPolicy aInitiatorPolicy, 6458 nsILoadInfo::CrossOriginOpenerPolicy* aOutPolicy) { 6459 MOZ_ASSERT(aOutPolicy); 6460 *aOutPolicy = nsILoadInfo::OPENER_POLICY_UNSAFE_NONE; 6461 6462 if (!mResponseHead) { 6463 return NS_ERROR_NOT_AVAILABLE; 6464 } 6465 6466 // COOP headers are ignored for insecure-context loads. 6467 if (!nsContentUtils::ComputeIsSecureContext(this)) { 6468 return NS_OK; 6469 } 6470 6471 nsAutoCString openerPolicy; 6472 (void)mResponseHead->GetHeader(nsHttp::Cross_Origin_Opener_Policy, 6473 openerPolicy); 6474 6475 // Cross-Origin-Opener-Policy = %s"same-origin" / 6476 // %s"same-origin-allow-popups" / 6477 // %s"unsafe-none"; case-sensitive 6478 6479 nsCOMPtr<nsISFVService> sfv = GetSFVService(); 6480 6481 nsCOMPtr<nsISFVItem> item; 6482 nsresult rv = sfv->ParseItem(openerPolicy, getter_AddRefs(item)); 6483 if (NS_FAILED(rv)) { 6484 return rv; 6485 } 6486 6487 nsCOMPtr<nsISFVBareItem> value; 6488 rv = item->GetValue(getter_AddRefs(value)); 6489 if (NS_FAILED(rv)) { 6490 return rv; 6491 } 6492 6493 nsCOMPtr<nsISFVToken> token = do_QueryInterface(value); 6494 if (!token) { 6495 return NS_ERROR_UNEXPECTED; 6496 } 6497 6498 rv = token->GetValue(openerPolicy); 6499 if (NS_FAILED(rv)) { 6500 return rv; 6501 } 6502 6503 nsILoadInfo::CrossOriginOpenerPolicy policy = 6504 nsILoadInfo::OPENER_POLICY_UNSAFE_NONE; 6505 6506 if (openerPolicy.EqualsLiteral("same-origin")) { 6507 policy = nsILoadInfo::OPENER_POLICY_SAME_ORIGIN; 6508 } else if (openerPolicy.EqualsLiteral("same-origin-allow-popups")) { 6509 policy = nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS; 6510 } 6511 if (policy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN) { 6512 nsILoadInfo::CrossOriginEmbedderPolicy coep = 6513 nsILoadInfo::EMBEDDER_POLICY_NULL; 6514 bool isCoepCredentiallessEnabled; 6515 rv = mLoadInfo->GetIsOriginTrialCoepCredentiallessEnabledForTopLevel( 6516 &isCoepCredentiallessEnabled); 6517 if (!isCoepCredentiallessEnabled) { 6518 nsAutoCString originTrialToken; 6519 (void)mResponseHead->GetHeader(nsHttp::OriginTrial, originTrialToken); 6520 if (!originTrialToken.IsEmpty()) { 6521 nsCOMPtr<nsIPrincipal> resultPrincipal; 6522 rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( 6523 this, getter_AddRefs(resultPrincipal)); 6524 if (!NS_WARN_IF(NS_FAILED(rv))) { 6525 OriginTrials trials; 6526 trials.UpdateFromToken(NS_ConvertASCIItoUTF16(originTrialToken), 6527 resultPrincipal); 6528 if (trials.IsEnabled(OriginTrial::CoepCredentialless)) { 6529 isCoepCredentiallessEnabled = true; 6530 } 6531 } 6532 } 6533 } 6534 6535 NS_ENSURE_SUCCESS(rv, rv); 6536 if (NS_SUCCEEDED( 6537 GetResponseEmbedderPolicy(isCoepCredentiallessEnabled, &coep)) && 6538 (coep == nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP || 6539 coep == nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS)) { 6540 policy = 6541 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP; 6542 } 6543 } 6544 6545 *aOutPolicy = policy; 6546 return NS_OK; 6547 } 6548 6549 NS_IMETHODIMP 6550 HttpBaseChannel::GetCrossOriginOpenerPolicy( 6551 nsILoadInfo::CrossOriginOpenerPolicy* aPolicy) { 6552 MOZ_ASSERT(aPolicy); 6553 if (!aPolicy) { 6554 return NS_ERROR_INVALID_ARG; 6555 } 6556 // If this method is called before OnStartRequest (ie. before we call 6557 // ComputeCrossOriginOpenerPolicy) or if we were unable to compute the 6558 // policy we'll throw an error. 6559 if (!LoadOnStartRequestCalled()) { 6560 return NS_ERROR_NOT_AVAILABLE; 6561 } 6562 *aPolicy = mComputedCrossOriginOpenerPolicy; 6563 return NS_OK; 6564 } 6565 6566 NS_IMETHODIMP 6567 HttpBaseChannel::HasCrossOriginOpenerPolicyMismatch(bool* aIsMismatch) { 6568 // This should only be called in parent process. 6569 MOZ_ASSERT(XRE_IsParentProcess()); 6570 *aIsMismatch = LoadHasCrossOriginOpenerPolicyMismatch(); 6571 return NS_OK; 6572 } 6573 6574 NS_IMETHODIMP 6575 HttpBaseChannel::GetOriginAgentClusterHeader(bool* aValue) { 6576 MOZ_ASSERT(XRE_IsParentProcess()); 6577 if (!mResponseHead) { 6578 return NS_ERROR_NOT_AVAILABLE; 6579 } 6580 6581 nsAutoCString content; 6582 nsresult rv = mResponseHead->GetHeader(nsHttp::OriginAgentCluster, content); 6583 if (NS_FAILED(rv)) { 6584 return rv; 6585 } 6586 6587 // Origin-Agent-Cluster = <boolean> 6588 nsCOMPtr<nsISFVService> sfv = GetSFVService(); 6589 nsCOMPtr<nsISFVItem> item; 6590 rv = sfv->ParseItem(content, getter_AddRefs(item)); 6591 if (NS_FAILED(rv)) { 6592 return rv; 6593 } 6594 nsCOMPtr<nsISFVBareItem> value; 6595 rv = item->GetValue(getter_AddRefs(value)); 6596 if (NS_FAILED(rv)) { 6597 return rv; 6598 } 6599 nsCOMPtr<nsISFVBool> flag = do_QueryInterface(value); 6600 if (!flag) { 6601 return NS_ERROR_NOT_AVAILABLE; 6602 } 6603 return flag->GetValue(aValue); 6604 } 6605 6606 void HttpBaseChannel::MaybeFlushConsoleReports() { 6607 // Flush if we have a known window ID. 6608 if (mLoadInfo->GetInnerWindowID() > 0) { 6609 FlushReportsToConsole(mLoadInfo->GetInnerWindowID()); 6610 return; 6611 } 6612 6613 // If this channel is part of a loadGroup, we can flush the console reports 6614 // immediately. 6615 nsCOMPtr<nsILoadGroup> loadGroup; 6616 nsresult rv = GetLoadGroup(getter_AddRefs(loadGroup)); 6617 if (NS_SUCCEEDED(rv) && loadGroup) { 6618 FlushConsoleReports(loadGroup); 6619 } 6620 } 6621 6622 void HttpBaseChannel::DoDiagnosticAssertWhenOnStopNotCalledOnDestroy() {} 6623 6624 bool HttpBaseChannel::Http3Allowed() const { 6625 bool allowedProxyInfo = 6626 mProxyInfo ? (static_cast<nsProxyInfo*>(mProxyInfo.get())->IsDirect() || 6627 static_cast<nsProxyInfo*>(mProxyInfo.get())->IsHttp3Proxy()) 6628 : true; 6629 // TODO: When mUpgradeProtocolCallback is not null, we should allow HTTP/3 for 6630 // connect-udp. 6631 return !mUpgradeProtocolCallback && allowedProxyInfo && 6632 !(mCaps & NS_HTTP_BE_CONSERVATIVE) && !LoadBeConservative() && 6633 LoadAllowHttp3(); 6634 } 6635 6636 UniquePtr<nsHttpResponseHead> 6637 HttpBaseChannel::MaybeCloneResponseHeadForCachedResource() { 6638 if (!mResponseHead) { 6639 return nullptr; 6640 } 6641 6642 return MakeUnique<nsHttpResponseHead>(*mResponseHead); 6643 } 6644 6645 void HttpBaseChannel::SetDummyChannelForCachedResource( 6646 const nsHttpResponseHead* aMaybeResponseHead /* = nullptr */) { 6647 mDummyChannelForCachedResource = true; 6648 MOZ_ASSERT(!mResponseHead, 6649 "SetDummyChannelForCachedResource should only be called once"); 6650 if (aMaybeResponseHead) { 6651 mResponseHead = MakeUnique<nsHttpResponseHead>(*aMaybeResponseHead); 6652 } else { 6653 mResponseHead = MakeUnique<nsHttpResponseHead>(); 6654 } 6655 } 6656 6657 void HttpBaseChannel::SetEarlyHints( 6658 nsTArray<EarlyHintConnectArgs>&& aEarlyHints) { 6659 mEarlyHints = std::move(aEarlyHints); 6660 } 6661 6662 nsTArray<EarlyHintConnectArgs>&& HttpBaseChannel::TakeEarlyHints() { 6663 return std::move(mEarlyHints); 6664 } 6665 6666 NS_IMETHODIMP 6667 HttpBaseChannel::SetEarlyHintPreloaderId(uint64_t aEarlyHintPreloaderId) { 6668 mEarlyHintPreloaderId = aEarlyHintPreloaderId; 6669 return NS_OK; 6670 } 6671 6672 NS_IMETHODIMP 6673 HttpBaseChannel::GetEarlyHintPreloaderId(uint64_t* aEarlyHintPreloaderId) { 6674 NS_ENSURE_ARG_POINTER(aEarlyHintPreloaderId); 6675 *aEarlyHintPreloaderId = mEarlyHintPreloaderId; 6676 return NS_OK; 6677 } 6678 6679 NS_IMETHODIMP 6680 HttpBaseChannel::SetClassicScriptHintCharset( 6681 const nsAString& aClassicScriptHintCharset) { 6682 mClassicScriptHintCharset = aClassicScriptHintCharset; 6683 return NS_OK; 6684 } 6685 6686 NS_IMETHODIMP HttpBaseChannel::GetClassicScriptHintCharset( 6687 nsAString& aClassicScriptHintCharset) { 6688 aClassicScriptHintCharset = mClassicScriptHintCharset; 6689 return NS_OK; 6690 } 6691 6692 NS_IMETHODIMP HttpBaseChannel::SetDocumentCharacterSet( 6693 const nsAString& aDocumentCharacterSet) { 6694 mDocumentCharacterSet = aDocumentCharacterSet; 6695 return NS_OK; 6696 } 6697 6698 NS_IMETHODIMP HttpBaseChannel::GetDocumentCharacterSet( 6699 nsAString& aDocumentCharacterSet) { 6700 aDocumentCharacterSet = mDocumentCharacterSet; 6701 return NS_OK; 6702 } 6703 6704 void HttpBaseChannel::SetConnectionInfo(nsHttpConnectionInfo* aCI) { 6705 mConnectionInfo = aCI ? aCI->Clone() : nullptr; 6706 } 6707 6708 NS_IMETHODIMP 6709 HttpBaseChannel::GetIsProxyUsed(bool* aIsProxyUsed) { 6710 if (mProxyInfo) { 6711 if (!static_cast<nsProxyInfo*>(mProxyInfo.get())->IsDirect()) { 6712 StoreIsProxyUsed(true); 6713 } 6714 } 6715 *aIsProxyUsed = LoadIsProxyUsed(); 6716 return NS_OK; 6717 } 6718 6719 static void CollectORBBlockTelemetry( 6720 const OpaqueResponseBlockedTelemetryReason aTelemetryReason, 6721 ExtContentPolicy aPolicy) { 6722 glean::orb::block_reason.EnumGet(aTelemetryReason).Add(); 6723 6724 switch (aPolicy) { 6725 case ExtContentPolicy::TYPE_INVALID: 6726 glean::orb::block_initiator 6727 .EnumGet(glean::orb::BlockInitiatorLabel::eInvalid) 6728 .Add(); 6729 break; 6730 case ExtContentPolicy::TYPE_OTHER: 6731 glean::orb::block_initiator 6732 .EnumGet(glean::orb::BlockInitiatorLabel::eOther) 6733 .Add(); 6734 break; 6735 case ExtContentPolicy::TYPE_FETCH: 6736 glean::orb::block_initiator 6737 .EnumGet(glean::orb::BlockInitiatorLabel::eBlockedFetch) 6738 .Add(); 6739 break; 6740 case ExtContentPolicy::TYPE_SCRIPT: 6741 glean::orb::block_initiator 6742 .EnumGet(glean::orb::BlockInitiatorLabel::eScript) 6743 .Add(); 6744 break; 6745 case ExtContentPolicy::TYPE_JSON: 6746 glean::orb::block_initiator 6747 .EnumGet(glean::orb::BlockInitiatorLabel::eJson) 6748 .Add(); 6749 break; 6750 case ExtContentPolicy::TYPE_IMAGE: 6751 glean::orb::block_initiator 6752 .EnumGet(glean::orb::BlockInitiatorLabel::eImage) 6753 .Add(); 6754 break; 6755 case ExtContentPolicy::TYPE_STYLESHEET: 6756 glean::orb::block_initiator 6757 .EnumGet(glean::orb::BlockInitiatorLabel::eStylesheet) 6758 .Add(); 6759 break; 6760 case ExtContentPolicy::TYPE_XMLHTTPREQUEST: 6761 glean::orb::block_initiator 6762 .EnumGet(glean::orb::BlockInitiatorLabel::eXmlhttprequest) 6763 .Add(); 6764 break; 6765 case ExtContentPolicy::TYPE_DTD: 6766 glean::orb::block_initiator.EnumGet(glean::orb::BlockInitiatorLabel::eDtd) 6767 .Add(); 6768 break; 6769 case ExtContentPolicy::TYPE_FONT: 6770 glean::orb::block_initiator 6771 .EnumGet(glean::orb::BlockInitiatorLabel::eFont) 6772 .Add(); 6773 break; 6774 case ExtContentPolicy::TYPE_MEDIA: 6775 glean::orb::block_initiator 6776 .EnumGet(glean::orb::BlockInitiatorLabel::eMedia) 6777 .Add(); 6778 break; 6779 case ExtContentPolicy::TYPE_CSP_REPORT: 6780 glean::orb::block_initiator 6781 .EnumGet(glean::orb::BlockInitiatorLabel::eCspReport) 6782 .Add(); 6783 break; 6784 case ExtContentPolicy::TYPE_XSLT: 6785 glean::orb::block_initiator 6786 .EnumGet(glean::orb::BlockInitiatorLabel::eXslt) 6787 .Add(); 6788 break; 6789 case ExtContentPolicy::TYPE_IMAGESET: 6790 glean::orb::block_initiator 6791 .EnumGet(glean::orb::BlockInitiatorLabel::eImageset) 6792 .Add(); 6793 break; 6794 case ExtContentPolicy::TYPE_WEB_MANIFEST: 6795 glean::orb::block_initiator 6796 .EnumGet(glean::orb::BlockInitiatorLabel::eWebManifest) 6797 .Add(); 6798 break; 6799 case ExtContentPolicy::TYPE_SPECULATIVE: 6800 glean::orb::block_initiator 6801 .EnumGet(glean::orb::BlockInitiatorLabel::eSpeculative) 6802 .Add(); 6803 break; 6804 case ExtContentPolicy::TYPE_UA_FONT: 6805 glean::orb::block_initiator 6806 .EnumGet(glean::orb::BlockInitiatorLabel::eUaFont) 6807 .Add(); 6808 break; 6809 case ExtContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA: 6810 glean::orb::block_initiator 6811 .EnumGet(glean::orb::BlockInitiatorLabel::eProxiedWebrtcMedia) 6812 .Add(); 6813 break; 6814 case ExtContentPolicy::TYPE_PING: 6815 glean::orb::block_initiator 6816 .EnumGet(glean::orb::BlockInitiatorLabel::ePing) 6817 .Add(); 6818 break; 6819 case ExtContentPolicy::TYPE_BEACON: 6820 glean::orb::block_initiator 6821 .EnumGet(glean::orb::BlockInitiatorLabel::eBeacon) 6822 .Add(); 6823 break; 6824 case ExtContentPolicy::TYPE_WEB_TRANSPORT: 6825 glean::orb::block_initiator 6826 .EnumGet(glean::orb::BlockInitiatorLabel::eWebTransport) 6827 .Add(); 6828 break; 6829 case ExtContentPolicy::TYPE_WEB_IDENTITY: 6830 // Don't bother extending the telemetry for this. 6831 glean::orb::block_initiator 6832 .EnumGet(glean::orb::BlockInitiatorLabel::eOther) 6833 .Add(); 6834 break; 6835 case ExtContentPolicy::TYPE_DOCUMENT: 6836 case ExtContentPolicy::TYPE_SUBDOCUMENT: 6837 case ExtContentPolicy::TYPE_OBJECT: 6838 case ExtContentPolicy::TYPE_WEBSOCKET: 6839 case ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD: 6840 MOZ_ASSERT_UNREACHABLE("Shouldn't block this type"); 6841 // DOCUMENT, SUBDOCUMENT, OBJECT, 6842 // WEBSOCKET and SAVEAS_DOWNLOAD are excluded from ORB 6843 glean::orb::block_initiator 6844 .EnumGet(glean::orb::BlockInitiatorLabel::eExcluded) 6845 .Add(); 6846 break; 6847 // Do not add default: so that compilers can catch the missing case. 6848 } 6849 } 6850 6851 void HttpBaseChannel::LogORBError( 6852 const nsAString& aReason, 6853 const OpaqueResponseBlockedTelemetryReason aTelemetryReason) { 6854 auto policy = mLoadInfo->GetExternalContentPolicyType(); 6855 CollectORBBlockTelemetry(aTelemetryReason, policy); 6856 6857 // Blocking `ExtContentPolicy::TYPE_BEACON` isn't web observable, so keep 6858 // quiet in the console about blocking it. 6859 if (policy == ExtContentPolicy::TYPE_BEACON) { 6860 return; 6861 } 6862 6863 RefPtr<dom::Document> doc; 6864 mLoadInfo->GetLoadingDocument(getter_AddRefs(doc)); 6865 6866 nsAutoCString uri; 6867 nsresult rv = nsContentUtils::AnonymizeURI(mURI, uri); 6868 if (NS_WARN_IF(NS_FAILED(rv))) { 6869 return; 6870 } 6871 6872 uint64_t contentWindowId; 6873 GetTopLevelContentWindowId(&contentWindowId); 6874 if (contentWindowId) { 6875 nsContentUtils::ReportToConsoleByWindowID( 6876 u"A resource is blocked by OpaqueResponseBlocking, please check browser console for details."_ns, 6877 nsIScriptError::warningFlag, "ORB"_ns, contentWindowId, 6878 SourceLocation(mURI.get())); 6879 } 6880 6881 AutoTArray<nsString, 2> params; 6882 params.AppendElement(NS_ConvertUTF8toUTF16(uri)); 6883 params.AppendElement(aReason); 6884 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "ORB"_ns, doc, 6885 nsContentUtils::eNECKO_PROPERTIES, 6886 "ResourceBlockedORB", params); 6887 } 6888 6889 NS_IMETHODIMP HttpBaseChannel::SetEarlyHintLinkType( 6890 uint32_t aEarlyHintLinkType) { 6891 mEarlyHintLinkType = aEarlyHintLinkType; 6892 return NS_OK; 6893 } 6894 6895 NS_IMETHODIMP HttpBaseChannel::GetEarlyHintLinkType( 6896 uint32_t* aEarlyHintLinkType) { 6897 *aEarlyHintLinkType = mEarlyHintLinkType; 6898 return NS_OK; 6899 } 6900 6901 NS_IMETHODIMP 6902 HttpBaseChannel::SetHasContentDecompressed(bool aValue) { 6903 LOG(("HttpBaseChannel::SetHasContentDecompressed [this=%p value=%d]\n", this, 6904 aValue)); 6905 mHasContentDecompressed = aValue; 6906 return NS_OK; 6907 } 6908 NS_IMETHODIMP 6909 HttpBaseChannel::GetHasContentDecompressed(bool* value) { 6910 *value = mHasContentDecompressed; 6911 return NS_OK; 6912 } 6913 6914 NS_IMETHODIMP 6915 HttpBaseChannel::SetRenderBlocking(bool aRenderBlocking) { 6916 mRenderBlocking = aRenderBlocking; 6917 return NS_OK; 6918 } 6919 6920 NS_IMETHODIMP 6921 HttpBaseChannel::GetRenderBlocking(bool* aRenderBlocking) { 6922 *aRenderBlocking = mRenderBlocking; 6923 return NS_OK; 6924 } 6925 6926 NS_IMETHODIMP HttpBaseChannel::GetLastTransportStatus( 6927 nsresult* aLastTransportStatus) { 6928 return NS_ERROR_NOT_IMPLEMENTED; 6929 } 6930 6931 void HttpBaseChannel::SetFetchPriorityDOM( 6932 mozilla::dom::FetchPriority aPriority) { 6933 switch (aPriority) { 6934 case mozilla::dom::FetchPriority::Auto: 6935 SetFetchPriority(nsIClassOfService::FETCHPRIORITY_AUTO); 6936 return; 6937 case mozilla::dom::FetchPriority::High: 6938 SetFetchPriority(nsIClassOfService::FETCHPRIORITY_HIGH); 6939 return; 6940 case mozilla::dom::FetchPriority::Low: 6941 SetFetchPriority(nsIClassOfService::FETCHPRIORITY_LOW); 6942 return; 6943 default: 6944 MOZ_ASSERT_UNREACHABLE(); 6945 } 6946 } 6947 6948 } // namespace net 6949 } // namespace mozilla