nsHttpChannel.cpp (442007B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set expandtab ts=4 sw=2 sts=2 cin: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // HttpLog.h should generally be included first 8 #include "HttpLog.h" 9 10 #include <inttypes.h> 11 12 #include "mozilla/ScopeExit.h" 13 #include "mozilla/Sprintf.h" 14 #include "mozilla/ToString.h" 15 #include "mozilla/dom/nsCSPContext.h" 16 #include "mozilla/dom/NavigatorLogin.h" 17 #include "mozilla/glean/AntitrackingMetrics.h" 18 #include "mozilla/glean/NetwerkMetrics.h" 19 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h" 20 #include "mozilla/net/CaptivePortalService.h" 21 #include "mozilla/net/CookieServiceParent.h" 22 #include "mozilla/StoragePrincipalHelper.h" 23 24 #include "nsCOMPtr.h" 25 #include "nsContentSecurityUtils.h" 26 #include "nsHttp.h" 27 #include "nsHttpChannel.h" 28 #include "nsHttpChannelAuthProvider.h" 29 #include "nsHttpHandler.h" 30 #include "nsIStreamConverter.h" 31 #include "nsString.h" 32 #include "nsICacheStorageService.h" 33 #include "nsICacheStorage.h" 34 #include "nsICacheEntry.h" 35 #include "nsICookieNotification.h" 36 #include "nsICryptoHash.h" 37 #include "nsIEffectiveTLDService.h" 38 #include "nsIHttpHeaderVisitor.h" 39 #include "nsINetworkInterceptController.h" 40 #include "nsIStringBundle.h" 41 #include "nsIStreamListenerTee.h" 42 #include "nsISeekableStream.h" 43 #include "nsIProtocolProxyService2.h" 44 #include "nsIURLQueryStringStripper.h" 45 #include "nsIWebTransport.h" 46 #include "nsCRT.h" 47 #include "nsMimeTypes.h" 48 #include "nsNetCID.h" 49 #include "nsNetUtil.h" 50 #include "nsIStreamTransportService.h" 51 #include "prnetdb.h" 52 #include "nsEscape.h" 53 #include "nsComponentManagerUtils.h" 54 #include "nsStreamUtils.h" 55 #include "nsIOService.h" 56 #include "nsDNSPrefetch.h" 57 #include "nsChannelClassifier.h" 58 #include "nsIRedirectResultListener.h" 59 #include "mozilla/TimeStamp.h" 60 #include "nsError.h" 61 #include "nsPrintfCString.h" 62 #include "nsQueryObject.h" 63 #include "nsThreadUtils.h" 64 #include "nsIConsoleService.h" 65 #include "nsINetworkErrorLogging.h" 66 #include "mozilla/AntiTrackingRedirectHeuristic.h" 67 #include "mozilla/AntiTrackingUtils.h" 68 #include "mozilla/Attributes.h" 69 #include "mozilla/BasePrincipal.h" 70 #include "mozilla/DebugOnly.h" 71 #include "mozilla/PerfStats.h" 72 #include "mozilla/ProfilerLabels.h" 73 #include "mozilla/FlowMarkers.h" 74 #include "mozilla/Components.h" 75 #include "mozilla/StaticPrefs_dom.h" 76 #include "mozilla/StaticPrefs_network.h" 77 #include "mozilla/StaticPrefs_privacy.h" 78 #include "mozilla/StaticPrefs_security.h" 79 #include "sslt.h" 80 #include "nsCharSeparatedTokenizer.h" 81 #include "nsContentUtils.h" 82 #include "nsContentSecurityManager.h" 83 #include "nsIClassOfService.h" 84 #include "CookieService.h" 85 #include "nsIPrincipal.h" 86 #include "nsIScriptError.h" 87 #include "nsIScriptSecurityManager.h" 88 #include "nsITransportSecurityInfo.h" 89 #include "nsIWebProgressListener.h" 90 #include "LoadContextInfo.h" 91 #include "netCore.h" 92 #include "nsHttpTransaction.h" 93 #include "nsICancelable.h" 94 #include "nsIHttpChannelInternal.h" 95 #include "nsIPrompt.h" 96 #include "nsInputStreamPump.h" 97 #include "nsURLHelper.h" 98 #include "nsISocketTransport.h" 99 #include "nsIStreamConverterService.h" 100 #include "nsISiteSecurityService.h" 101 #include "nsIURIMutator.h" 102 #include "nsString.h" 103 #include "nsStringStream.h" 104 #include "mozilla/dom/PerformanceStorage.h" 105 #include "mozilla/dom/ReferrerInfo.h" 106 #include "mozilla/glean/DomSecurityMetrics.h" 107 #include "mozilla/Telemetry.h" 108 #include "mozilla/Services.h" 109 #include "nsISystemInfo.h" 110 #include "mozilla/Components.h" 111 #include "AlternateServices.h" 112 #include "NetworkMarker.h" 113 #include "nsIDNSRecord.h" 114 #include "mozilla/dom/Document.h" 115 #include "nsICompressConvStats.h" 116 #include "nsCORSListenerProxy.h" 117 #include "nsISocketProvider.h" 118 #include "mozilla/extensions/StreamFilterParent.h" 119 #include "mozilla/net/SFVService.h" 120 #include "mozilla/NullPrincipal.h" 121 #include "CacheControlParser.h" 122 #include "nsMixedContentBlocker.h" 123 #include "CacheStorageService.h" 124 #include "HttpChannelParent.h" 125 #include "HttpTransactionParent.h" 126 #include "ThirdPartyUtil.h" 127 #include "InterceptedHttpChannel.h" 128 #include "../../cache2/CacheFileUtils.h" 129 #include "nsINetworkLinkService.h" 130 #include "mozilla/ContentBlockingAllowList.h" 131 #include "mozilla/dom/ServiceWorkerUtils.h" 132 #include "mozilla/dom/nsHTTPSOnlyStreamListener.h" 133 #include "mozilla/dom/nsHTTPSOnlyUtils.h" 134 #include "mozilla/net/AsyncUrlChannelClassifier.h" 135 #include "mozilla/net/CookieJarSettings.h" 136 #include "mozilla/net/NeckoChannelParams.h" 137 #include "mozilla/net/OpaqueResponseUtils.h" 138 #include "mozilla/net/UrlClassifierFeatureFactory.h" 139 #include "mozilla/net/URLPatternGlue.h" 140 #include "mozilla/net/urlpattern_glue.h" 141 #include "HttpTrafficAnalyzer.h" 142 #include "mozilla/net/SocketProcessParent.h" 143 #include "mozilla/dom/SecFetch.h" 144 #include "mozilla/dom/WindowGlobalParent.h" 145 #include "mozilla/net/TRRService.h" 146 #include "LNAPermissionRequest.h" 147 #include "nsUnknownDecoder.h" 148 #ifdef XP_WIN 149 # include "HttpWinUtils.h" 150 #endif 151 #ifdef XP_MACOSX 152 # include "MicrosoftEntraSSOUtils.h" 153 #endif 154 #ifdef FUZZING 155 # include "mozilla/StaticPrefs_fuzzing.h" 156 #endif 157 158 #include "mozilla/dom/ReportDeliver.h" 159 #include "mozilla/dom/ReportingHeader.h" 160 161 namespace mozilla { 162 163 using namespace dom; 164 165 namespace net { 166 167 namespace { 168 169 // True if the local cache should be bypassed when processing a request. 170 #define BYPASS_LOCAL_CACHE(loadFlags, isPreferCacheLoadOverBypass) \ 171 ((loadFlags) & (nsIRequest::LOAD_BYPASS_CACHE | \ 172 nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE) && \ 173 !(((loadFlags) & nsIRequest::LOAD_FROM_CACHE) && \ 174 (isPreferCacheLoadOverBypass))) 175 176 #define RECOVER_FROM_CACHE_FILE_ERROR(result) \ 177 ((result) == NS_ERROR_FILE_NOT_FOUND || \ 178 (result) == NS_ERROR_FILE_CORRUPTED || (result) == NS_ERROR_OUT_OF_MEMORY) 179 180 #define WRONG_RACING_RESPONSE_SOURCE(req) \ 181 (mRaceCacheWithNetwork && \ 182 (((mFirstResponseSource == RESPONSE_FROM_CACHE) && \ 183 ((req) != mCachePump)) || \ 184 ((mFirstResponseSource == RESPONSE_FROM_NETWORK) && \ 185 ((req) != mTransactionPump)))) 186 187 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID); 188 189 enum ChannelDisposition { 190 kHttpCanceled = 0, 191 kHttpDisk = 1, 192 kHttpNetOK = 2, 193 kHttpNetEarlyFail = 3, 194 kHttpNetLateFail = 4, 195 kHttpsCanceled = 8, 196 kHttpsDisk = 9, 197 kHttpsNetOK = 10, 198 kHttpsNetEarlyFail = 11, 199 kHttpsNetLateFail = 12 200 }; 201 202 static nsLiteralCString CacheDispositionToTelemetryLabel( 203 nsICacheInfoChannel::CacheDisposition hitOrMiss) { 204 switch (hitOrMiss) { 205 case nsICacheInfoChannel::kCacheUnresolved: 206 return "Unresolved"_ns; 207 case nsICacheInfoChannel::kCacheHit: 208 return "Hit"_ns; 209 case nsICacheInfoChannel::kCacheHitViaReval: 210 return "HitViaReval"_ns; 211 case nsICacheInfoChannel::kCacheMissedViaReval: 212 return "MissedViaReval"_ns; 213 case nsICacheInfoChannel::kCacheMissed: 214 return "Missed"_ns; 215 case nsICacheInfoChannel::kCacheUnknown: 216 return "Unknown"_ns; 217 default: 218 return "Invalid"_ns; 219 } 220 } 221 222 void AccumulateCacheHitTelemetry( 223 nsICacheInfoChannel::CacheDisposition hitOrMiss, nsIChannel* aChannel) { 224 nsCString key("UNKNOWN"); 225 226 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 227 228 nsAutoCString contentType; 229 if (NS_SUCCEEDED(aChannel->GetContentType(contentType))) { 230 if (nsContentUtils::IsJavascriptMIMEType( 231 NS_ConvertUTF8toUTF16(contentType))) { 232 key.AssignLiteral("JAVASCRIPT"); 233 } else if (StringBeginsWith(contentType, "text/css"_ns) || 234 (loadInfo && loadInfo->GetExternalContentPolicyType() == 235 ExtContentPolicy::TYPE_STYLESHEET)) { 236 key.AssignLiteral("STYLESHEET"); 237 } else if (StringBeginsWith(contentType, "application/wasm"_ns)) { 238 key.AssignLiteral("WASM"); 239 } else if (StringBeginsWith(contentType, "image/"_ns)) { 240 key.AssignLiteral("IMAGE"); 241 } else if (StringBeginsWith(contentType, "video/"_ns)) { 242 key.AssignLiteral("MEDIA"); 243 } else if (StringBeginsWith(contentType, "audio/"_ns)) { 244 key.AssignLiteral("MEDIA"); 245 } else if (!StringBeginsWith(contentType, 246 nsLiteralCString(UNKNOWN_CONTENT_TYPE))) { 247 key.AssignLiteral("OTHER"); 248 } 249 } 250 251 nsLiteralCString label = CacheDispositionToTelemetryLabel(hitOrMiss); 252 glean::http::cache_disposition.Get(key, label).Add(); 253 glean::http::cache_disposition.Get("ALL"_ns, label).Add(); 254 } 255 256 // Computes and returns a SHA1 hash of the input buffer. The input buffer 257 // must be a null-terminated string. 258 nsresult Hash(const char* buf, nsACString& hash) { 259 nsresult rv; 260 261 nsCOMPtr<nsICryptoHash> hasher = 262 do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); 263 NS_ENSURE_SUCCESS(rv, rv); 264 265 rv = hasher->Init(nsICryptoHash::SHA1); 266 NS_ENSURE_SUCCESS(rv, rv); 267 268 rv = hasher->Update(reinterpret_cast<unsigned const char*>(buf), strlen(buf)); 269 NS_ENSURE_SUCCESS(rv, rv); 270 271 rv = hasher->Finish(true, hash); 272 NS_ENSURE_SUCCESS(rv, rv); 273 274 return NS_OK; 275 } 276 277 class CookieVisitor final { 278 public: 279 explicit CookieVisitor(nsHttpResponseHead* aResponseHead) { 280 nsAutoCString cookieHeader; 281 if (NS_SUCCEEDED( 282 aResponseHead->GetHeader(nsHttp::Set_Cookie, cookieHeader))) { 283 for (const auto& cookie : cookieHeader.Split('\n')) { 284 mCookieHeaders.AppendElement(cookie); 285 } 286 } 287 } 288 289 ~CookieVisitor() = default; 290 291 const nsTArray<nsCString>& CookieHeaders() const { return mCookieHeaders; } 292 293 private: 294 nsTArray<nsCString> mCookieHeaders; 295 }; 296 297 class CookieObserver final : public nsIObserver, 298 public nsSupportsWeakReference { 299 public: 300 NS_DECL_ISUPPORTS 301 NS_DECL_NSIOBSERVER 302 303 static already_AddRefed<CookieObserver> Create(bool aPrivateBrowsing); 304 305 void StealChanges(nsTArray<CookieChange>& aChanges) { 306 aChanges.SwapElements(mChanges); 307 } 308 309 private: 310 CookieObserver() = default; 311 ~CookieObserver() = default; 312 313 nsTArray<CookieChange> mChanges; 314 }; 315 316 NS_IMPL_ISUPPORTS(CookieObserver, nsIObserver, nsISupportsWeakReference) 317 318 // static 319 already_AddRefed<CookieObserver> CookieObserver::Create(bool aPrivateBrowsing) { 320 MOZ_ASSERT(NS_IsMainThread()); 321 322 RefPtr<CookieObserver> observer = new CookieObserver(); 323 324 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 325 if (NS_WARN_IF(!os)) { 326 return nullptr; 327 } 328 329 nsresult rv = os->AddObserver( 330 observer, aPrivateBrowsing ? "private-cookie-changed" : "cookie-changed", 331 true); 332 if (NS_WARN_IF(NS_FAILED(rv))) { 333 return nullptr; 334 } 335 336 return observer.forget(); 337 } 338 339 NS_IMETHODIMP 340 CookieObserver::Observe(nsISupports* aSubject, const char* aTopic, 341 const char16_t* aData) { 342 MOZ_ASSERT(NS_IsMainThread()); 343 344 nsCOMPtr<nsICookieNotification> notification = do_QueryInterface(aSubject); 345 NS_ENSURE_TRUE(notification, NS_ERROR_FAILURE); 346 347 nsCOMPtr<nsICookie> xpcCookie; 348 nsresult rv = notification->GetCookie(getter_AddRefs(xpcCookie)); 349 if (NS_WARN_IF(NS_FAILED(rv))) { 350 return rv; 351 } 352 353 if (!xpcCookie) { 354 return NS_OK; 355 } 356 357 const Cookie& cookie = xpcCookie->AsCookie(); 358 359 nsICookieNotification::Action action = notification->GetAction(); 360 361 switch (action) { 362 case nsICookieNotification::COOKIE_DELETED: 363 mChanges.AppendElement(CookieChange{/* added */ false, cookie.ToIPC(), 364 cookie.OriginAttributesRef()}); 365 break; 366 367 case nsICookieNotification::COOKIE_ADDED: 368 [[fallthrough]]; 369 case nsICookieNotification::COOKIE_CHANGED: 370 mChanges.AppendElement(CookieChange{/* added */ true, cookie.ToIPC(), 371 cookie.OriginAttributesRef()}); 372 break; 373 374 default: 375 // We don't care about other actions because none of them can be 376 // triggered by the Set-Cookie header. 377 break; 378 } 379 380 return NS_OK; 381 } 382 383 void MaybeInitializeCookieProcessingGuard( 384 nsHttpChannel* aChannel, CookieServiceParent::CookieProcessingGuard& aGuard, 385 RefPtr<CookieObserver>& aCookieObserver, 386 RefPtr<HttpChannelParent>& aHttpChannelParent, uint32_t aHttpStatus) { 387 nsCOMPtr<nsIParentChannel> parentChannel; 388 NS_QueryNotificationCallbacks(aChannel, parentChannel); 389 aHttpChannelParent = do_QueryObject(parentChannel); 390 if (!aHttpChannelParent) { 391 return; 392 } 393 394 aCookieObserver = CookieObserver::Create(NS_UsePrivateBrowsing(aChannel)); 395 396 PNeckoParent* neckoParent = aHttpChannelParent->Manager(); 397 if (!neckoParent) { 398 return; 399 } 400 401 PCookieServiceParent* csParent = 402 LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent()); 403 CookieServiceParent* cookieServiceParent = 404 static_cast<CookieServiceParent*>(csParent); 405 if (!cookieServiceParent) { 406 return; 407 } 408 409 // on redirect we don't want to use processing guard 410 // because it will prevent cookies from being set in the 411 // content process that originated the request 412 if (nsHttpChannel::IsRedirectStatus(aHttpStatus)) { 413 return; 414 } 415 416 aGuard.Initialize(cookieServiceParent); 417 } 418 419 } // unnamed namespace 420 421 // We only treat 3xx responses as redirects if they have a Location header and 422 // the status code is in a whitelist. 423 bool nsHttpChannel::WillRedirect(const nsHttpResponseHead& response) { 424 return IsRedirectStatus(response.Status()) && 425 response.HasHeader(nsHttp::Location); 426 } 427 428 nsresult StoreAuthorizationMetaData(nsICacheEntry* entry, 429 nsHttpRequestHead* requestHead); 430 431 class MOZ_STACK_CLASS AutoRedirectVetoNotifier { 432 public: 433 explicit AutoRedirectVetoNotifier(nsHttpChannel* channel, nsresult& aRv) 434 : mChannel(channel), mRv(aRv) { 435 if (mChannel->LoadHasAutoRedirectVetoNotifier()) { 436 MOZ_CRASH("Nested AutoRedirectVetoNotifier on the stack"); 437 mChannel = nullptr; 438 return; 439 } 440 441 mChannel->StoreHasAutoRedirectVetoNotifier(true); 442 } 443 ~AutoRedirectVetoNotifier() { ReportRedirectResult(mRv); } 444 void RedirectSucceeded() { ReportRedirectResult(NS_OK); } 445 446 private: 447 nsHttpChannel* mChannel; 448 bool mCalledReport = false; 449 nsresult& mRv; 450 void ReportRedirectResult(nsresult aRv); 451 }; 452 453 void AutoRedirectVetoNotifier::ReportRedirectResult(nsresult aRv) { 454 if (!mChannel) return; 455 456 if (mCalledReport) { 457 return; 458 } 459 mCalledReport = true; 460 461 mChannel->mRedirectChannel = nullptr; 462 463 if (NS_SUCCEEDED(aRv)) { 464 mChannel->RemoveAsNonTailRequest(); 465 } 466 467 nsCOMPtr<nsIRedirectResultListener> vetoHook; 468 NS_QueryNotificationCallbacks(mChannel, NS_GET_IID(nsIRedirectResultListener), 469 getter_AddRefs(vetoHook)); 470 471 nsHttpChannel* channel = mChannel; 472 mChannel = nullptr; 473 474 if (vetoHook) vetoHook->OnRedirectResult(aRv); 475 476 // Drop after the notification 477 channel->StoreHasAutoRedirectVetoNotifier(false); 478 } 479 480 //----------------------------------------------------------------------------- 481 // nsHttpChannel <public> 482 //----------------------------------------------------------------------------- 483 484 nsHttpChannel::nsHttpChannel() : HttpAsyncAborter<nsHttpChannel>(this) { 485 LOG(("Creating nsHttpChannel [this=%p, nsIChannel=%p]\n", this, 486 static_cast<nsIChannel*>(this))); 487 mChannelCreationTime = PR_Now(); 488 mChannelCreationTimestamp = TimeStamp::Now(); 489 } 490 491 nsHttpChannel::~nsHttpChannel() { 492 PROFILER_MARKER("~nsHttpChannel", NETWORK, {}, TerminatingFlowMarker, 493 Flow::FromPointer(this)); 494 LOG(("Destroying nsHttpChannel [this=%p, nsIChannel=%p]\n", this, 495 static_cast<nsIChannel*>(this))); 496 497 if (LOG_ENABLED()) { 498 nsCString webExtension; 499 this->GetPropertyAsACString(u"cancelledByExtension"_ns, webExtension); 500 if (!webExtension.IsEmpty()) { 501 LOG(("channel [%p] cancelled by extension [id=%s]", this, 502 webExtension.get())); 503 } 504 } 505 506 if (mAuthProvider) { 507 DebugOnly<nsresult> rv = mAuthProvider->Disconnect(NS_ERROR_ABORT); 508 MOZ_ASSERT(NS_SUCCEEDED(rv)); 509 } 510 511 ReleaseMainThreadOnlyReferences(); 512 if (gHttpHandler) { 513 gHttpHandler->RemoveHttpChannel(mChannelId); 514 } 515 516 if (mDictDecompress && mUsingDictionary) { 517 mDictDecompress->UseCompleted(); 518 } 519 } 520 521 void nsHttpChannel::ReleaseMainThreadOnlyReferences() { 522 if (NS_IsMainThread()) { 523 // Already on main thread, let dtor to 524 // take care of releasing references 525 return; 526 } 527 528 nsTArray<nsCOMPtr<nsISupports>> arrayToRelease; 529 arrayToRelease.AppendElement(mAuthProvider.forget()); 530 arrayToRelease.AppendElement(mRedirectChannel.forget()); 531 arrayToRelease.AppendElement(mPreflightChannel.forget()); 532 arrayToRelease.AppendElement(mDNSPrefetch.forget()); 533 534 MOZ_DIAGNOSTIC_ASSERT( 535 !mEarlyHintObserver, 536 "Early hint observer should have been released in ReleaseListeners()"); 537 arrayToRelease.AppendElement(mEarlyHintObserver.forget()); 538 MOZ_DIAGNOSTIC_ASSERT( 539 !mChannelClassifier, 540 "Channel classifier should have been released in ReleaseListeners()"); 541 arrayToRelease.AppendElement( 542 mChannelClassifier.forget().downcast<nsIURIClassifierCallback>()); 543 MOZ_DIAGNOSTIC_ASSERT( 544 !mWarningReporter, 545 "Warning reporter should have been released in ReleaseListeners()"); 546 arrayToRelease.AppendElement(mWarningReporter.forget()); 547 548 NS_DispatchToMainThread(new ProxyReleaseRunnable(std::move(arrayToRelease))); 549 } 550 551 nsresult nsHttpChannel::Init(nsIURI* uri, uint32_t caps, nsProxyInfo* proxyInfo, 552 uint32_t proxyResolveFlags, nsIURI* proxyURI, 553 uint64_t channelId, nsILoadInfo* aLoadInfo) { 554 LOG1(("nsHttpChannel::Init [this=%p]\n", this)); 555 nsresult rv = HttpBaseChannel::Init(uri, caps, proxyInfo, proxyResolveFlags, 556 proxyURI, channelId, aLoadInfo); 557 558 return rv; 559 } 560 561 nsresult nsHttpChannel::AddSecurityMessage(const nsAString& aMessageTag, 562 const nsAString& aMessageCategory) { 563 if (mWarningReporter) { 564 return mWarningReporter->ReportSecurityMessage(aMessageTag, 565 aMessageCategory); 566 } 567 return HttpBaseChannel::AddSecurityMessage(aMessageTag, aMessageCategory); 568 } 569 570 NS_IMETHODIMP 571 nsHttpChannel::LogBlockedCORSRequest(const nsAString& aMessage, 572 const nsACString& aCategory, 573 bool aIsWarning) { 574 if (mWarningReporter) { 575 return mWarningReporter->LogBlockedCORSRequest(aMessage, aCategory, 576 aIsWarning); 577 } 578 return NS_ERROR_UNEXPECTED; 579 } 580 581 NS_IMETHODIMP 582 nsHttpChannel::LogMimeTypeMismatch(const nsACString& aMessageName, 583 bool aWarning, const nsAString& aURL, 584 const nsAString& aContentType) { 585 if (mWarningReporter) { 586 return mWarningReporter->LogMimeTypeMismatch(aMessageName, aWarning, aURL, 587 aContentType); 588 } 589 return NS_ERROR_UNEXPECTED; 590 } 591 592 //----------------------------------------------------------------------------- 593 // nsHttpChannel <private> 594 //----------------------------------------------------------------------------- 595 596 void nsHttpChannel::AddStorageAccessHeadersToRequest() { 597 if (!StaticPrefs::dom_storage_access_enabled() || 598 !StaticPrefs::dom_storage_access_headers_enabled()) { 599 return; 600 } 601 602 // check if request is eligible for storage-access 603 uint32_t cookiePolicy = 0; 604 if (mLoadInfo->GetCookiePolicy(&cookiePolicy) != NS_OK) { 605 return; 606 } 607 if (cookiePolicy != nsILoadInfo::SEC_COOKIES_INCLUDE) { 608 return; 609 } 610 611 // check whether we have storage-access permission set in channel 612 nsILoadInfo::StoragePermissionState storageAccess = 613 AntiTrackingUtils::GetStoragePermissionStateInParent(this); 614 615 switch (storageAccess) { 616 case nsILoadInfo::HasStoragePermission: 617 case nsILoadInfo::StoragePermissionAllowListed: 618 SetRequestHeader(nsHttp::Sec_Fetch_Storage_Access.val(), "active"_ns, 619 false); 620 break; 621 case nsILoadInfo::InactiveStoragePermission: 622 SetRequestHeader(nsHttp::Sec_Fetch_Storage_Access.val(), "inactive"_ns, 623 false); 624 break; 625 case nsILoadInfo::DisabledStoragePermission: 626 SetRequestHeader(nsHttp::Sec_Fetch_Storage_Access.val(), "none"_ns, 627 false); 628 break; 629 case nsILoadInfo::NoStoragePermission: 630 break; 631 } 632 } 633 634 bool nsHttpChannel::StorageAccessReloadedChannel() { 635 return LoadStorageAccessReloadChannel(); 636 } 637 638 nsresult nsHttpChannel::PrepareToConnect() { 639 LOG(("nsHttpChannel::PrepareToConnect [this=%p]\n", this)); 640 641 // This may be async; the dictionary headers may need to fetch an origin 642 // dictionary cache entry from disk before adding the headers. We can 643 // continue with channel creation, and just block on this being done later 644 AUTO_PROFILER_FLOW_MARKER("nsHttpHandler::AddAcceptAndDictionaryHeaders", 645 NETWORK, Flow::FromPointer(this)); 646 // AddAcceptAndDictionaryHeaders must call this->Suspend before kicking 647 // off the async operation that can result in calling the lambda (which 648 // will Resume), to avoid a race condition. 649 nsresult rv = gHttpHandler->AddAcceptAndDictionaryHeaders( 650 mURI, mLoadInfo->GetExternalContentPolicyType(), &mRequestHead, IsHTTPS(), 651 this, nsHttpChannel::StaticSuspend, 652 [self = RefPtr(this)](bool aNeedsResume, DictionaryCacheEntry* aDict) { 653 self->mDictDecompress = aDict; 654 if (aNeedsResume) { 655 LOG_DICTIONARIES(("Resuming after getting Dictionary headers")); 656 self->Resume(); 657 } 658 if (self->mDictDecompress) { 659 LOG_DICTIONARIES( 660 ("Added dictionary header for %p, DirectoryCacheEntry %p", 661 self.get(), aDict)); 662 AUTO_PROFILER_FLOW_MARKER( 663 "nsHttpHandler::AddAcceptAndDictionaryHeaders Add " 664 "Available-Dictionary", 665 NETWORK, Flow::FromPointer(self)); 666 // mDictDecompress is set if we added Available-Dictionary 667 self->mDictDecompress->InUse(); 668 self->mUsingDictionary = true; 669 PROFILER_MARKER("Dictionary Prefetch", NETWORK, 670 MarkerTiming::IntervalStart(), FlowMarker, 671 Flow::FromPointer(self)); 672 // XXX if this fails, retry the connection (we assume that the 673 // DictionaryCacheEntry has been removed). Failure should be only in 674 // weird cases like no storage service. 675 return NS_SUCCEEDED(self->mDictDecompress->Prefetch( 676 GetLoadContextInfo(self), self->mShouldSuspendForDictionary, 677 [self](nsresult aResult) { 678 // this is called when the prefetch is complete to 679 // un-Suspend the channel 680 PROFILER_MARKER("Dictionary Prefetch", NETWORK, 681 MarkerTiming::IntervalEnd(), FlowMarker, 682 Flow::FromPointer(self)); 683 if (NS_FAILED(aResult)) { 684 LOG( 685 ("nsHttpChannel::SetupChannelForTransaction [this=%p] " 686 "Dictionary prefetch failed: 0x%08" PRIx32, 687 self.get(), static_cast<uint32_t>(aResult))); 688 if (self->mUsingDictionary) { 689 self->mDictDecompress->UseCompleted(); 690 self->mUsingDictionary = false; 691 } 692 self->mDictDecompress = nullptr; 693 if (self->mSuspendedForDictionary) { 694 self->mSuspendedForDictionary = false; 695 self->Cancel(aResult); 696 self->Resume(); 697 } 698 return; 699 } 700 MOZ_ASSERT(self->mDictDecompress->DictionaryReady()); 701 if (self->mSuspendedForDictionary) { 702 LOG( 703 ("nsHttpChannel::SetupChannelForTransaction [this=%p] " 704 "Resuming channel " 705 "suspended for Dictionary", 706 self.get())); 707 self->mSuspendedForDictionary = false; 708 self->Resume(); 709 } 710 })); 711 } 712 return true; 713 }); 714 if (NS_FAILED(rv)) return rv; 715 716 // notify "http-on-modify-request-before-cookies" observers 717 gHttpHandler->OnModifyRequestBeforeCookies(this); 718 719 if (mStaleRevalidation) { 720 // This is a revalidating channel. 721 // The cookies (user set cookies + cookies from the cookeservice) are 722 // already copied to the request headers when opening this channel in 723 // PerformBackgroundCacheRevalidationNow(). 724 } else { 725 AddCookiesToRequest(); 726 } 727 728 #if defined(XP_WIN) || defined(XP_MACOSX) 729 730 auto prefEnabledForCurrentContainer = [&]() { 731 uint32_t containerId = mLoadInfo->GetOriginAttributes().mUserContextId; 732 // Make sure that the default container ID is 0 733 static_assert(nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID == 0); 734 735 nsAutoCString prefName; 736 # ifdef XP_WIN 737 prefName = nsPrintfCString("network.http.windows-sso.container-enabled.%u", 738 containerId); 739 # endif 740 741 # ifdef XP_MACOSX 742 prefName = nsPrintfCString( 743 "network.http.microsoft-entra-sso.container-enabled.%u", containerId); 744 # endif 745 746 bool enabled = false; 747 Preferences::GetBool(prefName.get(), &enabled); 748 749 LOG(("Pref for %s is %d\n", prefName.get(), enabled)); 750 751 return enabled; 752 }; 753 754 #endif // defined(XP_WIN) || defined(XP_MACOSX) 755 756 #ifdef XP_WIN 757 758 // If Windows 10 SSO is enabled, we potentially add auth 759 // information to secure top level loads (DOCUMENTs) and iframes 760 // (SUBDOCUMENTs) that aren't anonymous or private browsing. 761 if (StaticPrefs::network_http_windows_sso_enabled() && 762 mURI->SchemeIs("https") && !(mLoadFlags & LOAD_ANONYMOUS) && 763 !mPrivateBrowsing) { 764 ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType(); 765 if ((type == ExtContentPolicy::TYPE_DOCUMENT || 766 type == ExtContentPolicy::TYPE_SUBDOCUMENT) && 767 prefEnabledForCurrentContainer()) { 768 AddWindowsSSO(this); 769 } 770 } 771 #endif 772 773 #ifdef XP_MACOSX 774 775 auto isUriMSAuthority = [&]() { 776 nsAutoCString endPoint; 777 nsresult rv = mURI->GetHost(endPoint); 778 if (!NS_SUCCEEDED(rv)) { 779 return false; 780 } 781 LOG(("endPoint is %s\n", endPoint.get())); 782 783 return gHttpHandler->IsHostMSAuthority(endPoint); 784 }; 785 786 // If macOS SSO is enabled, we potentially add auth 787 // information to secure top level loads (DOCUMENTs) and iframes 788 // (SUBDOCUMENTs) that aren't anonymous or private browsing. 789 if (StaticPrefs::network_http_microsoft_entra_sso_enabled() && 790 mURI->SchemeIs("https") && !(mLoadFlags & LOAD_ANONYMOUS) && 791 !mPrivateBrowsing) { 792 ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType(); 793 if ((type == ExtContentPolicy::TYPE_DOCUMENT || 794 type == ExtContentPolicy::TYPE_SUBDOCUMENT) && 795 prefEnabledForCurrentContainer() && isUriMSAuthority()) { 796 nsMainThreadPtrHandle<nsHttpChannel> self( 797 new nsMainThreadPtrHolder<nsHttpChannel>( 798 "nsHttpChannel::PrepareToConnect::self", this)); 799 auto resultCallback = [self(self)]() { 800 MOZ_ASSERT(NS_IsMainThread()); 801 nsresult rv = self->ContinuePrepareToConnect(); 802 if (NS_FAILED(rv)) { 803 self->CloseCacheEntry(false); 804 (void)self->AsyncAbort(rv); 805 } 806 }; 807 808 nsresult rv = AddMicrosoftEntraSSO(this, std::move(resultCallback)); 809 810 // Returns NS_OK if performRequests is called in MicrosoftEntraSSOUtils 811 // This temporarily stops the channel setup for the delegate. 812 if (NS_SUCCEEDED(rv)) { 813 return rv; 814 } 815 } 816 } 817 818 #endif 819 820 return ContinuePrepareToConnect(); 821 } 822 823 nsresult nsHttpChannel::ContinuePrepareToConnect() { 824 // notify "http-on-modify-request" observers 825 CallOnModifyRequestObservers(); 826 827 return CallOrWaitForResume( 828 [](auto* self) { return self->OnBeforeConnect(); }); 829 } 830 831 void nsHttpChannel::HandleContinueCancellingByURLClassifier( 832 nsresult aErrorCode) { 833 MOZ_ASSERT( 834 UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode)); 835 MOZ_ASSERT(!mCallOnResume, "How did that happen?"); 836 837 if (mSuspendCount) { 838 LOG( 839 ("Waiting until resume HandleContinueCancellingByURLClassifier " 840 "[this=%p]\n", 841 this)); 842 mCallOnResume = [aErrorCode](nsHttpChannel* self) { 843 self->HandleContinueCancellingByURLClassifier(aErrorCode); 844 return NS_OK; 845 }; 846 return; 847 } 848 849 LOG(("nsHttpChannel::HandleContinueCancellingByURLClassifier [this=%p]\n", 850 this)); 851 ContinueCancellingByURLClassifier(aErrorCode); 852 } 853 854 void nsHttpChannel::SetPriorityHeader() { 855 nsAutoCString userSetPriority; 856 (void)GetRequestHeader("Priority"_ns, userSetPriority); 857 if (!userSetPriority.IsEmpty()) { 858 // If the Priority header is set by the user, do not override it. 859 return; 860 } 861 862 uint8_t urgency = 863 nsHttpHandler::UrgencyFromCoSFlags(mClassOfService.Flags(), mPriority); 864 bool incremental = mClassOfService.Incremental(); 865 866 nsPrintfCString value( 867 "%s", urgency != 3 ? nsPrintfCString("u=%d", urgency).get() : ""); 868 869 if (incremental) { 870 if (!value.IsEmpty()) { 871 value.Append(", "); 872 } 873 value.Append("i"); 874 } 875 876 if (!value.IsEmpty()) { 877 SetRequestHeader("Priority"_ns, value, false); 878 } 879 } 880 881 nsresult nsHttpChannel::OnBeforeConnect() { 882 nsresult rv = NS_OK; 883 884 // Check if request was cancelled during suspend AFTER on-modify-request 885 if (mCanceled) { 886 return mStatus; 887 } 888 889 // Check to see if we should redirect this channel elsewhere by 890 // nsIHttpChannel.redirectTo API request 891 if (mAPIRedirectTo) { 892 return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect); 893 } 894 895 // Note that we are only setting the "Upgrade-Insecure-Requests" request 896 // header for *all* navigational requests instead of all requests as 897 // defined in the spec, see: 898 // https://www.w3.org/TR/upgrade-insecure-requests/#preference 899 ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType(); 900 901 if (type == ExtContentPolicy::TYPE_DOCUMENT || 902 type == ExtContentPolicy::TYPE_SUBDOCUMENT) { 903 rv = SetRequestHeader("Upgrade-Insecure-Requests"_ns, "1"_ns, false); 904 NS_ENSURE_SUCCESS(rv, rv); 905 } 906 907 if (LoadAuthRedirectedChannel()) { 908 // This channel is a result of a redirect due to auth retry 909 // We have already checked for HSTS upgarde in the redirecting channel. 910 // We can safely skip those checks 911 return ContinueOnBeforeConnect(false, rv); 912 } 913 914 SecFetch::AddSecFetchHeader(this); 915 916 // Check to see if we should redirect this channel to the unstripped URI. To 917 // revert the query stripping if the loading channel is in the content 918 // blocking allow list. 919 if (ContentBlockingAllowList::Check(this)) { 920 nsCOMPtr<nsIURI> unstrippedURI; 921 mLoadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI)); 922 923 if (unstrippedURI) { 924 return AsyncCall(&nsHttpChannel::HandleAsyncRedirectToUnstrippedURI); 925 } 926 } 927 928 nsCOMPtr<nsIPrincipal> resultPrincipal; 929 if (!mURI->SchemeIs("https")) { 930 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( 931 this, getter_AddRefs(resultPrincipal)); 932 } 933 934 // Check if we already know about the HSTS status of the host 935 nsISiteSecurityService* sss = gHttpHandler->GetSSService(); 936 NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY); 937 bool isSecureURI; 938 OriginAttributes originAttributes; 939 if (!StoragePrincipalHelper::GetOriginAttributesForHSTS(this, 940 originAttributes)) { 941 return NS_ERROR_FAILURE; 942 } 943 rv = sss->IsSecureURI(mURI, originAttributes, &isSecureURI); 944 NS_ENSURE_SUCCESS(rv, rv); 945 // Save that on the loadInfo so it can later be consumed by 946 // SecurityInfo.sys.mjs 947 mLoadInfo->SetHstsStatus(isSecureURI); 948 949 RefPtr<mozilla::dom::BrowsingContext> bc; 950 mLoadInfo->GetBrowsingContext(getter_AddRefs(bc)); 951 // If bypassing the cache and we're forced offline 952 // we can just return the error here. 953 if (bc && bc->Top()->GetForceOffline() && 954 BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) { 955 return NS_ERROR_OFFLINE; 956 } 957 958 // At this point it is no longer possible to call 959 // HttpBaseChannel::UpgradeToSecure. 960 StoreUpgradableToSecure(false); 961 bool shouldUpgrade = LoadUpgradeToSecure(); 962 if (mURI->SchemeIs("http")) { 963 OriginAttributes originAttributes; 964 if (!StoragePrincipalHelper::GetOriginAttributesForHSTS(this, 965 originAttributes)) { 966 return NS_ERROR_FAILURE; 967 } 968 969 if (!shouldUpgrade) { 970 // Make sure http channel is released on main thread. 971 // See bug 1539148 for details. 972 nsMainThreadPtrHandle<nsHttpChannel> self( 973 new nsMainThreadPtrHolder<nsHttpChannel>( 974 "nsHttpChannel::OnBeforeConnect::self", this)); 975 auto resultCallback = [self(self)](bool aResult, nsresult aStatus) { 976 MOZ_ASSERT(NS_IsMainThread()); 977 978 nsresult rv = self->MaybeUseHTTPSRRForUpgrade(aResult, aStatus); 979 if (NS_FAILED(rv)) { 980 self->CloseCacheEntry(false); 981 (void)self->AsyncAbort(rv); 982 } 983 }; 984 985 bool willCallback = false; 986 rv = NS_ShouldSecureUpgrade( 987 mURI, mLoadInfo, resultPrincipal, LoadAllowSTS(), originAttributes, 988 shouldUpgrade, std::move(resultCallback), willCallback); 989 // If the request gets upgraded because of the HTTPS-Only mode, but no 990 // event listener has been registered so far, we want to do that here. 991 uint32_t httpOnlyStatus = mLoadInfo->GetHttpsOnlyStatus(); 992 if (httpOnlyStatus & 993 nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED) { 994 RefPtr<nsHTTPSOnlyStreamListener> httpsOnlyListener = 995 new nsHTTPSOnlyStreamListener(mListener, mLoadInfo); 996 mListener = httpsOnlyListener; 997 998 httpOnlyStatus ^= 999 nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED; 1000 httpOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED; 1001 mLoadInfo->SetHttpsOnlyStatus(httpOnlyStatus); 1002 } 1003 LOG( 1004 ("nsHttpChannel::OnBeforeConnect " 1005 "[this=%p willCallback=%d rv=%" PRIx32 "]\n", 1006 this, willCallback, static_cast<uint32_t>(rv))); 1007 1008 if (NS_FAILED(rv) || MOZ_UNLIKELY(willCallback)) { 1009 return rv; 1010 } 1011 } 1012 } 1013 1014 return MaybeUseHTTPSRRForUpgrade(shouldUpgrade, NS_OK); 1015 } 1016 1017 // Returns true if the network connectivity checker indicated 1018 // that HTTPS records can be resolved on this network - false otherwise. 1019 // When TRR is enabled, we always return true, as resolving HTTPS 1020 // records don't depend on the network. 1021 static bool canUseHTTPSRRonNetwork(bool& aTRREnabled) { 1022 // Respect the pref. 1023 if (StaticPrefs::network_dns_force_use_https_rr()) { 1024 aTRREnabled = true; 1025 return true; 1026 } 1027 1028 aTRREnabled = false; 1029 1030 if (nsCOMPtr<nsIDNSService> dns = mozilla::components::DNS::Service()) { 1031 nsIDNSService::ResolverMode mode; 1032 // If the browser is currently using TRR/DoH, then it can 1033 // definitely resolve HTTPS records. 1034 if (NS_SUCCEEDED(dns->GetCurrentTrrMode(&mode))) { 1035 if (mode == nsIDNSService::MODE_TRRFIRST) { 1036 RefPtr<TRRService> trr = TRRService::Get(); 1037 if (trr && trr->IsConfirmed()) { 1038 aTRREnabled = true; 1039 } 1040 } else if (mode == nsIDNSService::MODE_TRRONLY) { 1041 aTRREnabled = true; 1042 } 1043 if (aTRREnabled) { 1044 return true; 1045 } 1046 } 1047 } 1048 1049 if (RefPtr<NetworkConnectivityService> ncs = 1050 NetworkConnectivityService::GetSingleton()) { 1051 nsINetworkConnectivityService::ConnectivityState state; 1052 if (NS_SUCCEEDED(ncs->GetDNS_HTTPS(&state)) && 1053 state == nsINetworkConnectivityService::NOT_AVAILABLE) { 1054 return false; 1055 } 1056 } 1057 return true; 1058 } 1059 1060 nsresult nsHttpChannel::MaybeUseHTTPSRRForUpgrade(bool aShouldUpgrade, 1061 nsresult aStatus) { 1062 if (NS_FAILED(aStatus)) { 1063 return aStatus; 1064 } 1065 1066 RefPtr<mozilla::dom::BrowsingContext> bc; 1067 mLoadInfo->GetBrowsingContext(getter_AddRefs(bc)); 1068 bool forceOffline = bc && bc->Top()->GetForceOffline(); 1069 1070 if (mURI->SchemeIs("https") || aShouldUpgrade || !LoadUseHTTPSSVC() || 1071 forceOffline) { 1072 return ContinueOnBeforeConnect(aShouldUpgrade, aStatus); 1073 } 1074 1075 auto shouldSkipUpgradeWithHTTPSRR = [&]() -> bool { 1076 if (mCaps & NS_HTTP_DISALLOW_HTTPS_RR) { 1077 return true; 1078 } 1079 1080 // Skip using HTTPS RR to upgrade when this is not a top-level load and the 1081 // loading principal is http. 1082 if ((mLoadInfo->GetExternalContentPolicyType() != 1083 ExtContentPolicy::TYPE_DOCUMENT) && 1084 (mLoadInfo->GetLoadingPrincipal() && 1085 mLoadInfo->GetLoadingPrincipal()->SchemeIs("http"))) { 1086 return true; 1087 } 1088 1089 // If the network connectivity checker indicates the network is 1090 // blocking HTTPS requests, then we should skip them so we don't 1091 // needlessly wait for a timeout. 1092 bool trrEnabled = false; 1093 if (!canUseHTTPSRRonNetwork(trrEnabled)) { 1094 return true; 1095 } 1096 1097 // Don't block the channel when TRR is not used. 1098 if (!trrEnabled) { 1099 return true; 1100 } 1101 1102 auto dnsStrategy = GetProxyDNSStrategy(); 1103 if (dnsStrategy != ProxyDNSStrategy::ORIGIN) { 1104 return true; 1105 } 1106 1107 nsAutoCString uriHost; 1108 mURI->GetAsciiHost(uriHost); 1109 1110 return gHttpHandler->IsHostExcludedForHTTPSRR(uriHost); 1111 }; 1112 1113 if (shouldSkipUpgradeWithHTTPSRR()) { 1114 StoreUseHTTPSSVC(false); 1115 // If the website does not want to use HTTPS RR, we should set 1116 // NS_HTTP_DISALLOW_HTTPS_RR. This is for avoiding HTTPS RR being used by 1117 // the transaction. 1118 DisallowHTTPSRR(mCaps); 1119 return ContinueOnBeforeConnect(aShouldUpgrade, aStatus); 1120 } 1121 1122 if (mHTTPSSVCRecord.isSome()) { 1123 LOG(( 1124 "nsHttpChannel::MaybeUseHTTPSRRForUpgrade [%p] mHTTPSSVCRecord is some", 1125 this)); 1126 StoreWaitHTTPSSVCRecord(false); 1127 bool hasHTTPSRR = (mHTTPSSVCRecord.ref() != nullptr); 1128 return ContinueOnBeforeConnect(hasHTTPSRR, aStatus, hasHTTPSRR); 1129 } 1130 1131 LOG(("nsHttpChannel::MaybeUseHTTPSRRForUpgrade [%p] wait for HTTPS RR", 1132 this)); 1133 1134 OriginAttributes originAttributes; 1135 StoragePrincipalHelper::GetOriginAttributesForHTTPSRR(this, originAttributes); 1136 1137 RefPtr<nsDNSPrefetch> resolver = 1138 new nsDNSPrefetch(mURI, originAttributes, nsIRequest::GetTRRMode()); 1139 nsWeakPtr weakPtrThis( 1140 do_GetWeakReference(static_cast<nsIHttpChannel*>(this))); 1141 nsresult rv = resolver->FetchHTTPSSVC( 1142 mCaps & NS_HTTP_REFRESH_DNS, !LoadUseHTTPSSVC(), 1143 [weakPtrThis](nsIDNSHTTPSSVCRecord* aRecord) { 1144 nsCOMPtr<nsIHttpChannel> channel = do_QueryReferent(weakPtrThis); 1145 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(channel); 1146 if (httpChannelImpl) { 1147 httpChannelImpl->OnHTTPSRRAvailable(aRecord); 1148 } 1149 }); 1150 if (NS_FAILED(rv)) { 1151 LOG((" FetchHTTPSSVC failed with 0x%08" PRIx32, 1152 static_cast<uint32_t>(rv))); 1153 return ContinueOnBeforeConnect(aShouldUpgrade, aStatus); 1154 } 1155 1156 StoreWaitHTTPSSVCRecord(true); 1157 return NS_OK; 1158 } 1159 1160 nsresult nsHttpChannel::ContinueOnBeforeConnect(bool aShouldUpgrade, 1161 nsresult aStatus, 1162 bool aUpgradeWithHTTPSRR) { 1163 LOG( 1164 ("nsHttpChannel::ContinueOnBeforeConnect " 1165 "[this=%p aShouldUpgrade=%d rv=%" PRIx32 "]\n", 1166 this, aShouldUpgrade, static_cast<uint32_t>(aStatus))); 1167 1168 MOZ_ASSERT(!LoadWaitHTTPSSVCRecord()); 1169 1170 if (NS_FAILED(aStatus)) { 1171 return aStatus; 1172 } 1173 1174 if (aShouldUpgrade && !mURI->SchemeIs("https")) { 1175 // only set HTTPS_RR to be responsbile for the upgrade in the loadinfo 1176 // if it actually was responsible, otherwise the correct flag is 1177 // already present in the loadinfo. 1178 if (aUpgradeWithHTTPSRR) { 1179 mLoadInfo->SetHttpsUpgradeTelemetry(nsILoadInfo::HTTPS_RR); 1180 } 1181 return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps); 1182 } 1183 1184 // ensure that we are using a valid hostname 1185 if (!net_IsValidDNSHost(nsDependentCString(mConnectionInfo->Origin()))) { 1186 return NS_ERROR_UNKNOWN_HOST; 1187 } 1188 1189 if (mUpgradeProtocolCallback) { 1190 // Websockets can run over HTTP/2, but other upgrades can't. 1191 if (mUpgradeProtocol.EqualsLiteral("websocket") && 1192 StaticPrefs::network_http_http2_websockets()) { 1193 // Need to tell the conn manager that we're ok with http/2 even with 1194 // the allow keepalive bit not set. That bit needs to stay off, 1195 // though, in case we end up having to fallback to http/1.1 (where 1196 // we absolutely do want to disable keepalive). 1197 mCaps |= NS_HTTP_ALLOW_SPDY_WITHOUT_KEEPALIVE; 1198 } else { 1199 mCaps |= NS_HTTP_DISALLOW_SPDY; 1200 } 1201 // Upgrades cannot use HTTP/3. 1202 // TODO: When mUpgradeProtocolCallback is not null, we should allow HTTP/3 1203 // for connect-udp. 1204 mCaps |= NS_HTTP_DISALLOW_HTTP3; 1205 // Because NS_HTTP_STICKY_CONNECTION breaks HTTPS RR fallabck mecnahism, we 1206 // can not use HTTPS RR for upgrade requests. 1207 DisallowHTTPSRR(mCaps); 1208 } 1209 1210 if (LoadIsTRRServiceChannel()) { 1211 mCaps |= NS_HTTP_LARGE_KEEPALIVE; 1212 DisallowHTTPSRR(mCaps); 1213 } 1214 1215 if (mTransactionSticky) { 1216 MOZ_ASSERT(LoadAuthRedirectedChannel()); 1217 // this means this is a redirected channel channel due to auth retry and a 1218 // connection based auth scheme was used 1219 // we have a reference to the old-transaction with sticky connection which 1220 // we need to use 1221 mCaps |= NS_HTTP_STICKY_CONNECTION; 1222 } 1223 1224 mCaps |= NS_HTTP_TRR_FLAGS_FROM_MODE(nsIRequest::GetTRRMode()); 1225 1226 // Finalize ConnectionInfo flags before SpeculativeConnect 1227 mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0); 1228 mConnectionInfo->SetPrivate(mPrivateBrowsing); 1229 mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY); 1230 mConnectionInfo->SetBeConservative((mCaps & NS_HTTP_BE_CONSERVATIVE) || 1231 LoadBeConservative()); 1232 mConnectionInfo->SetTlsFlags(mTlsFlags); 1233 mConnectionInfo->SetIsTrrServiceChannel(LoadIsTRRServiceChannel()); 1234 mConnectionInfo->SetTRRMode(nsIRequest::GetTRRMode()); 1235 mConnectionInfo->SetIPv4Disabled(mCaps & NS_HTTP_DISABLE_IPV4); 1236 mConnectionInfo->SetIPv6Disabled(mCaps & NS_HTTP_DISABLE_IPV6); 1237 mConnectionInfo->SetAnonymousAllowClientCert( 1238 (mLoadFlags & LOAD_ANONYMOUS_ALLOW_CLIENT_CERT) != 0); 1239 1240 if (mWebTransportSessionEventListener) { 1241 nsTArray<RefPtr<nsIWebTransportHash>> aServerCertHashes; 1242 nsresult rv; 1243 nsCOMPtr<WebTransportConnectionSettings> wtconSettings = 1244 do_QueryInterface(mWebTransportSessionEventListener, &rv); 1245 NS_ENSURE_SUCCESS(rv, rv); 1246 1247 wtconSettings->GetServerCertificateHashes(aServerCertHashes); 1248 gHttpHandler->ConnMgr()->StoreServerCertHashes( 1249 mConnectionInfo, gHttpHandler->IsHttp2Excluded(mConnectionInfo), 1250 !Http3Allowed(), std::move(aServerCertHashes)); 1251 } 1252 1253 if (ShouldIntercept()) { 1254 return RedirectToInterceptedChannel(); 1255 } 1256 1257 // notify "http-on-before-connect" observers 1258 gHttpHandler->OnBeforeConnect(this); 1259 1260 return CallOrWaitForResume([](auto* self) { return self->Connect(); }); 1261 } 1262 1263 class MOZ_STACK_CLASS AddResponseHeadersToResponseHead final 1264 : public nsIHttpHeaderVisitor { 1265 public: 1266 explicit AddResponseHeadersToResponseHead(nsHttpResponseHead* aResponseHead) 1267 : mResponseHead(aResponseHead) {} 1268 1269 NS_IMETHOD VisitHeader(const nsACString& aHeader, 1270 const nsACString& aValue) override { 1271 nsAutoCString headerLine = aHeader + ": "_ns + aValue; 1272 DebugOnly<nsresult> rv = mResponseHead->ParseHeaderLine(headerLine); 1273 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1274 1275 return NS_OK; 1276 } 1277 1278 NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override; 1279 1280 // Stub AddRef/Release since this is a stack class. 1281 NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override { 1282 return ++mRefCnt; 1283 } 1284 1285 NS_IMETHOD_(MozExternalRefCountType) Release(void) override { 1286 return --mRefCnt; 1287 } 1288 1289 virtual ~AddResponseHeadersToResponseHead() { 1290 MOZ_DIAGNOSTIC_ASSERT(mRefCnt == 0); 1291 } 1292 1293 private: 1294 nsHttpResponseHead* mResponseHead; 1295 1296 nsrefcnt mRefCnt = 0; 1297 }; 1298 1299 NS_IMPL_QUERY_INTERFACE(AddResponseHeadersToResponseHead, nsIHttpHeaderVisitor) 1300 1301 nsresult nsHttpChannel::HandleOverrideResponse() { 1302 // Start building a response with the data from mOverrideResponse. 1303 mResponseHead = MakeUnique<nsHttpResponseHead>(); 1304 1305 // Apply override response status code and status text. 1306 uint32_t statusCode; 1307 nsresult rv = mOverrideResponse->GetResponseStatus(&statusCode); 1308 NS_ENSURE_SUCCESS(rv, rv); 1309 1310 nsAutoCString statusText; 1311 rv = mOverrideResponse->GetResponseStatusText(statusText); 1312 NS_ENSURE_SUCCESS(rv, rv); 1313 1314 // Hardcoding protocol HTTP/1.1 1315 nsPrintfCString line("HTTP/1.1 %u %s", statusCode, statusText.get()); 1316 rv = mResponseHead->ParseStatusLine(line); 1317 NS_ENSURE_SUCCESS(rv, rv); 1318 1319 // Apply override response headers. 1320 AddResponseHeadersToResponseHead visitor(mResponseHead.get()); 1321 rv = mOverrideResponse->VisitResponseHeaders(&visitor); 1322 NS_ENSURE_SUCCESS(rv, rv); 1323 1324 if (WillRedirect(*mResponseHead)) { 1325 // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here, 1326 // to avoid event dispatching latency. 1327 LOG(("Skipping read of overridden response redirect entity\n")); 1328 return AsyncCall(&nsHttpChannel::HandleAsyncRedirect); 1329 } 1330 1331 // This block parses the cookie header, collects any cookie changes, 1332 // and sends them to the parent actor. 1333 { 1334 RefPtr<HttpChannelParent> httpParent; 1335 RefPtr<CookieObserver> cookieObserver; 1336 1337 CookieServiceParent::CookieProcessingGuard cookieProcessingGuard; 1338 MaybeInitializeCookieProcessingGuard( 1339 this, cookieProcessingGuard, cookieObserver, httpParent, statusCode); 1340 1341 // Handle Set-Cookie headers as if the response was from networking. 1342 CookieVisitor cookieVisitor(mResponseHead.get()); 1343 SetCookieHeaders(cookieVisitor.CookieHeaders()); 1344 1345 if (cookieObserver) { 1346 nsTArray<CookieChange> cookieChanges; 1347 cookieObserver->StealChanges(cookieChanges); 1348 1349 if (!cookieChanges.IsEmpty()) { 1350 MOZ_ASSERT(httpParent); 1351 httpParent->SetCookieChanges(std::move(cookieChanges)); 1352 } 1353 } 1354 } 1355 1356 rv = ProcessSecurityHeaders(); 1357 if (NS_FAILED(rv)) { 1358 NS_WARNING("ProcessSecurityHeaders failed, continuing load."); 1359 } 1360 1361 if ((statusCode < 500) && (statusCode != 421)) { 1362 ProcessAltService(); 1363 } 1364 1365 nsAutoCString body; 1366 rv = mOverrideResponse->GetResponseBody(body); 1367 NS_ENSURE_SUCCESS(rv, rv); 1368 1369 nsCOMPtr<nsIInputStream> stringStream; 1370 rv = NS_NewCStringInputStream(getter_AddRefs(stringStream), body); 1371 NS_ENSURE_SUCCESS(rv, rv); 1372 rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), stringStream, 0, 0, 1373 true); 1374 if (NS_FAILED(rv)) { 1375 stringStream->Close(); 1376 return rv; 1377 } 1378 1379 rv = mCachePump->AsyncRead(this); 1380 if (NS_FAILED(rv)) return rv; 1381 1382 return NS_OK; 1383 } 1384 1385 nsresult nsHttpChannel::Connect() { 1386 LOG(("nsHttpChannel::Connect [this=%p]\n", this)); 1387 1388 if (mAPIRedirectTo) { 1389 LOG(("nsHttpChannel::Connect [transparent=%d]\n", 1390 mAPIRedirectTo->second())); 1391 1392 nsresult rv = StartRedirectChannelToURI( 1393 mAPIRedirectTo->first(), 1394 mAPIRedirectTo->second() ? nsIChannelEventSink::REDIRECT_PERMANENT | 1395 nsIChannelEventSink::REDIRECT_TRANSPARENT 1396 : nsIChannelEventSink::REDIRECT_PERMANENT); 1397 mAPIRedirectTo = Nothing(); 1398 if (NS_SUCCEEDED(rv)) { 1399 return NS_OK; 1400 } 1401 return NS_ERROR_FAILURE; 1402 } 1403 1404 // If mOverrideResponse is set, bypass the rest of the connection and reply 1405 // immediately with a response built using the data from mOverrideResponse. 1406 if (mOverrideResponse) { 1407 return HandleOverrideResponse(); 1408 } 1409 1410 // Don't allow resuming when cache must be used 1411 if (LoadResuming() && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) { 1412 LOG(("Resuming from cache is not supported yet")); 1413 return NS_ERROR_DOCUMENT_NOT_CACHED; 1414 } 1415 1416 // Step 8.18 of HTTP-network-or-cache fetch 1417 // https://fetch.spec.whatwg.org/#http-network-or-cache-fetch 1418 nsAutoCString rangeVal; 1419 if (NS_SUCCEEDED(GetRequestHeader("Range"_ns, rangeVal))) { 1420 SetRequestHeader("Accept-Encoding"_ns, "identity"_ns, false); 1421 } 1422 1423 if (mRequestHead.IsPost() || mRequestHead.IsPatch()) { 1424 // If the post id is already set then this is an attempt to replay 1425 // a post/patch transaction via the cache. Otherwise, we need a unique 1426 // post id for this transaction. 1427 if (mPostID == 0) { 1428 mPostID = gHttpHandler->GenerateUniqueID(); 1429 } 1430 1431 if (StaticPrefs::network_http_idempotencyKey_enabled() && 1432 !mRequestHead.HasHeader(nsHttp::Idempotency_Key)) { 1433 // check if we need to add 1434 // idempotency-key header 1435 // See Bug 1830022 for more details 1436 nsAutoCString key; 1437 gHttpHandler->GenerateIdempotencyKeyForPost(mPostID, mLoadInfo, key); 1438 MOZ_ALWAYS_SUCCEEDS( 1439 mRequestHead.SetHeader(nsHttp::Idempotency_Key, key, false)); 1440 } 1441 } 1442 1443 #ifdef MOZ_WIDGET_ANDROID 1444 bool val = false; 1445 if (nsIOService::ShouldAddAdditionalSearchHeaders(mURI, &val)) { 1446 SetRequestHeader("X-Search-Subdivision"_ns, val ? "1"_ns : "0"_ns, false); 1447 } 1448 #endif 1449 1450 bool isTrackingResource = IsThirdPartyTrackingResource(); 1451 LOG(("nsHttpChannel %p tracking resource=%d, cos=%lu, inc=%d", this, 1452 isTrackingResource, mClassOfService.Flags(), 1453 mClassOfService.Incremental())); 1454 1455 if (isTrackingResource) { 1456 AddClassFlags(nsIClassOfService::Tail); 1457 } 1458 1459 if (WaitingForTailUnblock()) { 1460 MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock); 1461 mOnTailUnblock = &nsHttpChannel::ConnectOnTailUnblock; 1462 return NS_OK; 1463 } 1464 1465 return ConnectOnTailUnblock(); 1466 } 1467 1468 nsresult nsHttpChannel::ConnectOnTailUnblock() { 1469 nsresult rv; 1470 1471 LOG(("nsHttpChannel::ConnectOnTailUnblock [this=%p]\n", this)); 1472 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::ConnectOnTailUnblock", NETWORK, 1473 Flow::FromPointer(this)); 1474 1475 // Consider opening a TCP connection right away. 1476 SpeculativeConnect(); 1477 1478 // open a cache entry for this channel... 1479 rv = OpenCacheEntry(mURI->SchemeIs("https")); 1480 1481 // do not continue if asyncOpenCacheEntry is in progress 1482 if (AwaitingCacheCallbacks()) { 1483 LOG(("nsHttpChannel::Connect %p AwaitingCacheCallbacks forces async\n", 1484 this)); 1485 MOZ_ASSERT(NS_SUCCEEDED(rv), "Unexpected state"); 1486 1487 if (mNetworkTriggered && mWaitingForProxy) { 1488 // Someone has called TriggerNetwork(), meaning we are racing the 1489 // network with the cache. 1490 mWaitingForProxy = false; 1491 return ContinueConnect(); 1492 } 1493 1494 return NS_OK; 1495 } 1496 1497 if (NS_FAILED(rv)) { 1498 LOG(("OpenCacheEntry failed [rv=%" PRIx32 "]\n", 1499 static_cast<uint32_t>(rv))); 1500 // if this channel is only allowed to pull from the cache, then 1501 // we must fail if we were unable to open a cache entry. 1502 if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { 1503 return NS_ERROR_DOCUMENT_NOT_CACHED; 1504 } 1505 // otherwise, let's just proceed without using the cache. 1506 } 1507 1508 if (mRaceCacheWithNetwork && ((mCacheEntry && !CachedContentIsValid() && 1509 (mDidReval || LoadCachedContentIsPartial())) || 1510 mIgnoreCacheEntry)) { 1511 // We won't send the conditional request because the unconditional 1512 // request was already sent (see bug 1377223). 1513 glean::network::race_cache_validation 1514 .EnumGet(glean::network::RaceCacheValidationLabel::eNotsent) 1515 .Add(); 1516 } 1517 1518 // When racing, if OnCacheEntryAvailable is called before AsyncOpenURI 1519 // returns, then we may not have started reading from the cache. 1520 // If the content is valid, we should attempt to do so, as technically the 1521 // cache has won the race. 1522 if (mRaceCacheWithNetwork && CachedContentIsValid()) { 1523 (void)ReadFromCache(); 1524 } 1525 1526 return TriggerNetwork(); 1527 } 1528 1529 nsresult nsHttpChannel::ContinueConnect() { 1530 // If we need to start a CORS preflight, do it now! 1531 // Note that it is important to do this before the early returns below. 1532 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::ContinueConnect", NETWORK, 1533 Flow::FromPointer(this)); 1534 if (!LoadIsCorsPreflightDone() && LoadRequireCORSPreflight()) { 1535 MOZ_ASSERT(!mPreflightChannel); 1536 nsresult rv = nsCORSListenerProxy::StartCORSPreflight( 1537 this, this, mUnsafeHeaders, getter_AddRefs(mPreflightChannel)); 1538 return rv; 1539 } 1540 1541 MOZ_RELEASE_ASSERT(!LoadRequireCORSPreflight() || LoadIsCorsPreflightDone(), 1542 "CORS preflight must have been finished by the time we " 1543 "do the rest of ContinueConnect"); 1544 1545 RefPtr<mozilla::dom::BrowsingContext> bc; 1546 mLoadInfo->GetBrowsingContext(getter_AddRefs(bc)); 1547 1548 // we may or may not have a cache entry at this point 1549 if (mCacheEntry) { 1550 // read straight from the cache if possible... 1551 if (CachedContentIsValid()) { 1552 // If we're forced offline, and set to bypass the cache, return offline. 1553 if (bc && bc->Top()->GetForceOffline() && 1554 BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) { 1555 return NS_ERROR_OFFLINE; 1556 } 1557 1558 nsRunnableMethod<nsHttpChannel>* event = nullptr; 1559 nsresult rv; 1560 if (!LoadCachedContentIsPartial()) { 1561 rv = AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse, &event); 1562 if (NS_FAILED(rv)) { 1563 LOG((" AsyncCall failed (%08x)", static_cast<uint32_t>(rv))); 1564 } 1565 } 1566 rv = ReadFromCache(); 1567 if (NS_FAILED(rv) && event) { 1568 event->Revoke(); 1569 } 1570 1571 AccumulateCacheHitTelemetry(kCacheHit, this); 1572 mCacheDisposition = kCacheHit; 1573 1574 return rv; 1575 } 1576 if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { 1577 // the cache contains the requested resource, but it must be 1578 // validated before we can reuse it. since we are not allowed 1579 // to hit the net, there's nothing more to do. the document 1580 // is effectively not in the cache. 1581 LOG((" !CachedContentIsValid() && mLoadFlags & LOAD_ONLY_FROM_CACHE")); 1582 return NS_ERROR_DOCUMENT_NOT_CACHED; 1583 } 1584 } else if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { 1585 LOG((" !mCacheEntry && mLoadFlags & LOAD_ONLY_FROM_CACHE")); 1586 return NS_ERROR_DOCUMENT_NOT_CACHED; 1587 } 1588 1589 if (mLoadFlags & LOAD_NO_NETWORK_IO) { 1590 LOG((" mLoadFlags & LOAD_NO_NETWORK_IO")); 1591 return NS_ERROR_DOCUMENT_NOT_CACHED; 1592 } 1593 1594 // We're about to hit the network. Don't if we're forced offline. 1595 if (bc && bc->Top()->GetForceOffline()) { 1596 return NS_ERROR_OFFLINE; 1597 } 1598 1599 // hit the net... 1600 nsresult rv = DoConnect(mTransactionSticky); 1601 mTransactionSticky = nullptr; 1602 return rv; 1603 } 1604 1605 nsresult nsHttpChannel::DoConnect(HttpTransactionShell* aTransWithStickyConn) { 1606 LOG(("nsHttpChannel::DoConnect [this=%p]\n", this)); 1607 1608 if (!mDNSBlockingPromise.IsEmpty()) { 1609 LOG((" waiting for DNS prefetch")); 1610 1611 // Transaction is passed only from auth retry for which we will definitely 1612 // not block on DNS to alter the origin server name for IP; it has already 1613 // been done. 1614 MOZ_ASSERT(!aTransWithStickyConn); 1615 MOZ_ASSERT(mDNSBlockingThenable); 1616 1617 nsCOMPtr<nsISerialEventTarget> target(do_GetMainThread()); 1618 RefPtr<nsHttpChannel> self(this); 1619 mDNSBlockingThenable->Then( 1620 target, __func__, 1621 [self](const nsCOMPtr<nsIDNSRecord>& aRec) { 1622 nsresult rv = self->DoConnectActual(nullptr); 1623 if (NS_FAILED(rv)) { 1624 self->CloseCacheEntry(false); 1625 (void)self->AsyncAbort(rv); 1626 } 1627 }, 1628 [self](nsresult err) { 1629 self->CloseCacheEntry(false); 1630 (void)self->AsyncAbort(err); 1631 }); 1632 1633 // The connection will continue when the promise is resolved in 1634 // OnLookupComplete. 1635 return NS_OK; 1636 } 1637 1638 return DoConnectActual(aTransWithStickyConn); 1639 } 1640 1641 nsresult nsHttpChannel::DoConnectActual( 1642 HttpTransactionShell* aTransWithStickyConn) { 1643 LOG(("nsHttpChannel::DoConnectActual [this=%p, aTransWithStickyConn=%p]\n", 1644 this, aTransWithStickyConn)); 1645 1646 nsresult rv = SetupChannelForTransaction(); 1647 if (NS_FAILED(rv)) { 1648 return rv; 1649 } 1650 1651 return CallOrWaitForResume( 1652 [trans = RefPtr(aTransWithStickyConn)](auto* self) { 1653 return self->DispatchTransaction(trans); 1654 }); 1655 } 1656 1657 nsresult nsHttpChannel::DispatchTransaction( 1658 HttpTransactionShell* aTransWithStickyConn) { 1659 LOG(("nsHttpChannel::DispatchTransaction [this=%p, aTransWithStickyConn=%p]", 1660 this, aTransWithStickyConn)); 1661 nsresult rv = InitTransaction(); 1662 if (NS_FAILED(rv)) { 1663 return rv; 1664 } 1665 if (aTransWithStickyConn) { 1666 rv = gHttpHandler->InitiateTransactionWithStickyConn( 1667 mTransaction, mPriority, aTransWithStickyConn); 1668 } else { 1669 rv = gHttpHandler->InitiateTransaction(mTransaction, mPriority); 1670 } 1671 1672 if (NS_FAILED(rv)) { 1673 return rv; 1674 } 1675 1676 rv = mTransaction->AsyncRead(this, getter_AddRefs(mTransactionPump)); 1677 if (NS_FAILED(rv)) { 1678 return rv; 1679 } 1680 1681 uint32_t suspendCount = mSuspendCount; 1682 if (LoadAsyncResumePending()) { 1683 LOG( 1684 (" Suspend()'ing transaction pump once because of async resume pending" 1685 ", sc=%u, pump=%p, this=%p", 1686 suspendCount, mTransactionPump.get(), this)); 1687 ++suspendCount; 1688 } 1689 while (suspendCount--) { 1690 mTransactionPump->Suspend(); 1691 } 1692 1693 return NS_OK; 1694 } 1695 1696 void nsHttpChannel::SpeculativeConnect() { 1697 // Before we take the latency hit of dealing with the cache, try and 1698 // get the TCP (and SSL) handshakes going so they can overlap. 1699 1700 // don't speculate if we are offline, when doing http upgrade (i.e. 1701 // websockets bootstrap), or if we can't do keep-alive (because then we 1702 // couldn't reuse the speculative connection anyhow). 1703 RefPtr<mozilla::dom::BrowsingContext> bc; 1704 mLoadInfo->GetBrowsingContext(getter_AddRefs(bc)); 1705 1706 if (gIOService->IsOffline() || mUpgradeProtocolCallback || 1707 !(mCaps & NS_HTTP_ALLOW_KEEPALIVE) || 1708 (bc && bc->Top()->GetForceOffline())) { 1709 return; 1710 } 1711 1712 // LOAD_ONLY_FROM_CACHE and LOAD_NO_NETWORK_IO must not hit network. 1713 // LOAD_FROM_CACHE is unlikely to hit network, so skip preconnects for it. 1714 if (mLoadFlags & 1715 (LOAD_ONLY_FROM_CACHE | LOAD_FROM_CACHE | LOAD_NO_NETWORK_IO)) { 1716 return; 1717 } 1718 1719 if (LoadAllowStaleCacheContent()) { 1720 return; 1721 } 1722 1723 nsCOMPtr<nsIInterfaceRequestor> callbacks; 1724 NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, 1725 getter_AddRefs(callbacks)); 1726 if (!callbacks) return; 1727 bool httpsRRAllowed = !(mCaps & NS_HTTP_DISALLOW_HTTPS_RR); 1728 (void)gHttpHandler->MaybeSpeculativeConnectWithHTTPSRR( 1729 mConnectionInfo, callbacks, 1730 mCaps & (NS_HTTP_DISALLOW_SPDY | NS_HTTP_TRR_MODE_MASK | 1731 NS_HTTP_DISABLE_IPV4 | NS_HTTP_DISABLE_IPV6 | 1732 NS_HTTP_DISALLOW_HTTP3 | NS_HTTP_REFRESH_DNS), 1733 nsHttpHandler::EchConfigEnabled() && httpsRRAllowed); 1734 } 1735 1736 void nsHttpChannel::DoNotifyListenerCleanup() { 1737 // We don't need this info anymore 1738 CleanRedirectCacheChainIfNecessary(); 1739 } 1740 1741 void nsHttpChannel::ReleaseListeners() { 1742 HttpBaseChannel::ReleaseListeners(); 1743 mChannelClassifier = nullptr; 1744 mWarningReporter = nullptr; 1745 mEarlyHintObserver = nullptr; 1746 mWebTransportSessionEventListener = nullptr; 1747 1748 for (StreamFilterRequest& request : mStreamFilterRequests) { 1749 request.mPromise->Reject(false, __func__); 1750 } 1751 mStreamFilterRequests.Clear(); 1752 } 1753 1754 void nsHttpChannel::DoAsyncAbort(nsresult aStatus) { 1755 (void)AsyncAbort(aStatus); 1756 } 1757 1758 void nsHttpChannel::HandleAsyncRedirect() { 1759 MOZ_ASSERT(!mCallOnResume, "How did that happen?"); 1760 1761 if (mSuspendCount) { 1762 LOG(("Waiting until resume to do async redirect [this=%p]\n", this)); 1763 mCallOnResume = [](nsHttpChannel* self) { 1764 self->HandleAsyncRedirect(); 1765 return NS_OK; 1766 }; 1767 return; 1768 } 1769 1770 nsresult rv = NS_OK; 1771 1772 LOG(("nsHttpChannel::HandleAsyncRedirect [this=%p]\n", this)); 1773 1774 // since this event is handled asynchronously, it is possible that this 1775 // channel could have been canceled, in which case there would be no point 1776 // in processing the redirect. 1777 if (NS_SUCCEEDED(mStatus)) { 1778 PushRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect); 1779 rv = AsyncProcessRedirection(mResponseHead->Status()); 1780 if (NS_FAILED(rv)) { 1781 PopRedirectAsyncFunc(&nsHttpChannel::ContinueHandleAsyncRedirect); 1782 // TODO: if !DoNotRender3xxBody(), render redirect body instead. 1783 // But first we need to cache 3xx bodies (bug 748510) 1784 rv = ContinueHandleAsyncRedirect(rv); 1785 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1786 } 1787 } else { 1788 rv = ContinueHandleAsyncRedirect(mStatus); 1789 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1790 } 1791 } 1792 1793 nsresult nsHttpChannel::ContinueHandleAsyncRedirect(nsresult rv) { 1794 if (NS_FAILED(rv)) { 1795 // If AsyncProcessRedirection fails, then we have to send out the 1796 // OnStart/OnStop notifications. 1797 LOG(("ContinueHandleAsyncRedirect got failure result [rv=%" PRIx32 "]\n", 1798 static_cast<uint32_t>(rv))); 1799 1800 bool redirectsEnabled = !mLoadInfo->GetDontFollowRedirects(); 1801 1802 if (redirectsEnabled) { 1803 // TODO: stop failing original channel if redirect vetoed? 1804 mStatus = rv; 1805 1806 DoNotifyListener(); 1807 1808 // Blow away cache entry if we couldn't process the redirect 1809 // for some reason (the cache entry might be corrupt). 1810 if (mCacheEntry) { 1811 mCacheEntry->AsyncDoom(nullptr); 1812 } 1813 } else { 1814 DoNotifyListener(); 1815 } 1816 } 1817 1818 CloseCacheEntry(true); 1819 1820 StoreIsPending(false); 1821 1822 if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus); 1823 1824 return NS_OK; 1825 } 1826 1827 void nsHttpChannel::HandleAsyncNotModified() { 1828 MOZ_ASSERT(!mCallOnResume, "How did that happen?"); 1829 1830 if (mSuspendCount) { 1831 LOG(("Waiting until resume to do async not-modified [this=%p]\n", this)); 1832 mCallOnResume = [](nsHttpChannel* self) { 1833 self->HandleAsyncNotModified(); 1834 return NS_OK; 1835 }; 1836 return; 1837 } 1838 1839 LOG(("nsHttpChannel::HandleAsyncNotModified [this=%p]\n", this)); 1840 1841 DoNotifyListener(); 1842 1843 CloseCacheEntry(false); 1844 1845 StoreIsPending(false); 1846 1847 if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, mStatus); 1848 } 1849 1850 nsresult nsHttpChannel::SetupChannelForTransaction() { 1851 LOG(( 1852 "nsHttpChannel::SetupChannelForTransaction [this=%p, cos=%lu, inc=%d " 1853 "prio=%d]\n", 1854 this, mClassOfService.Flags(), mClassOfService.Incremental(), mPriority)); 1855 1856 NS_ENSURE_TRUE(!mTransaction, NS_ERROR_ALREADY_INITIALIZED); 1857 1858 nsresult rv; 1859 1860 mozilla::MutexAutoLock lock(mRCWNLock); 1861 1862 if (StaticPrefs::network_http_priority_header_enabled()) { 1863 SetPriorityHeader(); 1864 } 1865 1866 // If we're racing cache with network, conditional or byte range header 1867 // could be added in OnCacheEntryCheck. We cannot send conditional request 1868 // without having the entry, so we need to remove the headers here and 1869 // ignore the cache entry in OnCacheEntryAvailable. 1870 if (mRaceCacheWithNetwork && AwaitingCacheCallbacks()) { 1871 if (mDidReval) { 1872 LOG((" Removing conditional request headers")); 1873 UntieValidationRequest(); 1874 mDidReval = false; 1875 mIgnoreCacheEntry = true; 1876 } 1877 1878 if (LoadCachedContentIsPartial()) { 1879 LOG((" Removing byte range request headers")); 1880 UntieByteRangeRequest(); 1881 StoreCachedContentIsPartial(false); 1882 mIgnoreCacheEntry = true; 1883 } 1884 1885 if (mIgnoreCacheEntry) { 1886 mAvailableCachedAltDataType.Truncate(); 1887 StoreDeliveringAltData(false); 1888 mAltDataLength = -1; 1889 mCacheInputStream.CloseAndRelease(); 1890 } 1891 } 1892 1893 StoreUsedNetwork(1); 1894 1895 if (!LoadAllowSpdy()) { 1896 mCaps |= NS_HTTP_DISALLOW_SPDY; 1897 } 1898 if (!LoadAllowHttp3()) { 1899 mCaps |= NS_HTTP_DISALLOW_HTTP3; 1900 } 1901 if (LoadBeConservative()) { 1902 mCaps |= NS_HTTP_BE_CONSERVATIVE; 1903 } 1904 1905 if (mLoadFlags & LOAD_ANONYMOUS_ALLOW_CLIENT_CERT) { 1906 mCaps |= NS_HTTP_LOAD_ANONYMOUS_CONNECT_ALLOW_CLIENT_CERT; 1907 } 1908 1909 if (nsContentUtils::ShouldResistFingerprinting(this, 1910 RFPTarget::HttpUserAgent)) { 1911 mCaps |= NS_HTTP_USE_RFP; 1912 } 1913 1914 // Use the URI path if not proxying (transparent proxying such as proxy 1915 // CONNECT does not count here). Also figure out what HTTP version to use. 1916 nsAutoCString buf, path; 1917 nsCString* requestURI; 1918 1919 // This is the normal e2e H1 path syntax "/index.html" 1920 rv = mURI->GetPathQueryRef(path); 1921 if (NS_FAILED(rv)) { 1922 return rv; 1923 } 1924 1925 // path may contain UTF-8 characters, so ensure that they're escaped. 1926 if (NS_EscapeURL(path.get(), path.Length(), esc_OnlyNonASCII | esc_Spaces, 1927 buf)) { 1928 requestURI = &buf; 1929 } else { 1930 requestURI = &path; 1931 } 1932 1933 // trim off the #ref portion if any... 1934 int32_t ref1 = requestURI->FindChar('#'); 1935 if (ref1 != kNotFound) { 1936 requestURI->SetLength(ref1); 1937 } 1938 1939 if (mConnectionInfo->UsingConnect() || !mConnectionInfo->UsingHttpProxy()) { 1940 mRequestHead.SetVersion(gHttpHandler->HttpVersion()); 1941 } else { 1942 mRequestHead.SetPath(*requestURI); 1943 1944 // RequestURI should be the absolute uri H1 proxy syntax 1945 // "http://foo/index.html" so we will overwrite the relative version in 1946 // requestURI 1947 rv = mURI->GetUserPass(buf); 1948 if (NS_FAILED(rv)) return rv; 1949 if (!buf.IsEmpty() && ((strncmp(mSpec.get(), "http:", 5) == 0) || 1950 strncmp(mSpec.get(), "https:", 6) == 0)) { 1951 nsCOMPtr<nsIURI> tempURI = nsIOService::CreateExposableURI(mURI); 1952 rv = tempURI->GetAsciiSpec(path); 1953 if (NS_FAILED(rv)) return rv; 1954 requestURI = &path; 1955 } else { 1956 requestURI = &mSpec; 1957 } 1958 1959 // trim off the #ref portion if any... 1960 int32_t ref2 = requestURI->FindChar('#'); 1961 if (ref2 != kNotFound) { 1962 requestURI->SetLength(ref2); 1963 } 1964 1965 mRequestHead.SetVersion(gHttpHandler->ProxyHttpVersion()); 1966 } 1967 1968 mRequestHead.SetRequestURI(*requestURI); 1969 1970 // set the request time for cache expiration calculations 1971 mRequestTime = NowInSeconds(); 1972 StoreRequestTimeInitialized(true); 1973 1974 // if doing a reload, force end-to-end 1975 if (mLoadFlags & LOAD_BYPASS_CACHE) { 1976 // We need to send 'Pragma:no-cache' to inhibit proxy caching even if 1977 // no proxy is configured since we might be talking with a transparent 1978 // proxy, i.e. one that operates at the network level. See bug #14772. 1979 // But we should not touch Pragma if Cache-Control is already set 1980 // (https://fetch.spec.whatwg.org/#ref-for-concept-request-cache-mode%E2%91%A3) 1981 if (!mRequestHead.HasHeader(nsHttp::Pragma)) { 1982 rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true); 1983 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1984 } 1985 // If we're configured to speak HTTP/1.1 then also send 'Cache-control: 1986 // no-cache'. But likewise don't touch Cache-Control if it's already set. 1987 if (mRequestHead.Version() >= HttpVersion::v1_1 && 1988 !mRequestHead.HasHeader(nsHttp::Cache_Control)) { 1989 rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "no-cache", true); 1990 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1991 } 1992 } else if (mLoadFlags & VALIDATE_ALWAYS) { 1993 // We need to send 'Cache-Control: max-age=0' to force each cache along 1994 // the path to the origin server to revalidate its own entry, if any, 1995 // with the next cache or server. See bug #84847. 1996 // 1997 // If we're configured to speak HTTP/1.0 then just send 'Pragma: no-cache' 1998 // 1999 // But don't send the headers if they're already set: 2000 // https://fetch.spec.whatwg.org/#ref-for-concept-request-cache-mode%E2%91%A2 2001 if (mRequestHead.Version() >= HttpVersion::v1_1) { 2002 if (!mRequestHead.HasHeader(nsHttp::Cache_Control)) { 2003 rv = mRequestHead.SetHeaderOnce(nsHttp::Cache_Control, "max-age=0", 2004 true); 2005 } 2006 } else { 2007 if (!mRequestHead.HasHeader(nsHttp::Pragma)) { 2008 rv = mRequestHead.SetHeaderOnce(nsHttp::Pragma, "no-cache", true); 2009 } 2010 } 2011 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2012 } 2013 2014 if (LoadResuming()) { 2015 char byteRange[32]; 2016 SprintfLiteral(byteRange, "bytes=%" PRIu64 "-", mStartPos); 2017 rv = mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(byteRange)); 2018 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2019 2020 if (!mEntityID.IsEmpty()) { 2021 // Also, we want an error if this resource changed in the meantime 2022 // Format of the entity id is: escaped_etag/size/lastmod 2023 nsCString::const_iterator start, end, slash; 2024 mEntityID.BeginReading(start); 2025 mEntityID.EndReading(end); 2026 mEntityID.BeginReading(slash); 2027 2028 if (FindCharInReadable('/', slash, end)) { 2029 nsAutoCString ifMatch; 2030 rv = mRequestHead.SetHeader( 2031 nsHttp::If_Match, 2032 NS_UnescapeURL(Substring(start, slash), 0, ifMatch)); 2033 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2034 2035 ++slash; // Incrementing, so that searching for '/' won't find 2036 // the same slash again 2037 } 2038 2039 if (FindCharInReadable('/', slash, end)) { 2040 rv = mRequestHead.SetHeader(nsHttp::If_Unmodified_Since, 2041 Substring(++slash, end)); 2042 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2043 } 2044 } 2045 } 2046 2047 // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer. 2048 if (mLoadFlags & LOAD_ANONYMOUS) mCaps |= NS_HTTP_LOAD_ANONYMOUS; 2049 2050 if (mUpgradeProtocolCallback) { 2051 rv = mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, false); 2052 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2053 rv = mRequestHead.SetHeaderOnce(nsHttp::Connection, nsHttp::Upgrade.get(), 2054 false); 2055 MOZ_ASSERT(NS_SUCCEEDED(rv)); 2056 mCaps |= NS_HTTP_STICKY_CONNECTION; 2057 mCaps &= ~NS_HTTP_ALLOW_KEEPALIVE; 2058 } 2059 2060 if (mWebTransportSessionEventListener) { 2061 mCaps |= NS_HTTP_STICKY_CONNECTION; 2062 } 2063 2064 return NS_OK; 2065 } 2066 2067 // Updates mLNAPermission members based on existing permission and tracking 2068 // flags in load info 2069 LNAPermission nsHttpChannel::UpdateLocalNetworkAccessPermissions( 2070 const nsACString& aPermissionType) { 2071 // We should arrive at this point after LNA has been detected at the 2072 // transaction layer and has errored 2073 2074 MOZ_ASSERT(aPermissionType == LOCAL_HOST_PERMISSION_KEY || 2075 aPermissionType == LOCAL_NETWORK_PERMISSION_KEY); 2076 LNAPermission userPerms = aPermissionType == LOCAL_HOST_PERMISSION_KEY 2077 ? mLNAPermission.mLocalHostPermission 2078 : mLNAPermission.mLocalNetworkPermission; 2079 2080 if (NS_WARN_IF(userPerms != LNAPermission::Pending)) { 2081 // Unexpected condition, we should not hit this case 2082 MOZ_ASSERT(false, 2083 "UpdateLocalNetworkAccessPermissions called with non-pending " 2084 "permission"); 2085 return userPerms; 2086 } 2087 2088 MOZ_ASSERT(mLoadInfo->TriggeringPrincipal(), "need triggering principal"); 2089 2090 // Skip LNA checks if the triggering principal and target are same origin 2091 // Note: This could be a case where there is a network change or device 2092 // migration to a private or corporate network 2093 bool isSameOrigin = false; 2094 nsresult rv = 2095 mLoadInfo->TriggeringPrincipal()->IsSameOrigin(mURI, &isSameOrigin); 2096 if (NS_SUCCEEDED(rv) && isSameOrigin) { 2097 userPerms = LNAPermission::Granted; 2098 return userPerms; 2099 } 2100 2101 // Skip LNA checks if captive portal is active 2102 nsCOMPtr<nsICaptivePortalService> cps = CaptivePortalService::GetSingleton(); 2103 if (cps) { 2104 int32_t state = cps->State(); 2105 if (state == nsICaptivePortalService::LOCKED_PORTAL && 2106 aPermissionType == LOCAL_NETWORK_PERMISSION_KEY) { 2107 userPerms = LNAPermission::Granted; 2108 return userPerms; 2109 } 2110 } 2111 2112 // Step 1. Check for Existing Allow or Deny permission 2113 if (nsContentUtils::IsExactSitePermAllow(mLoadInfo->TriggeringPrincipal(), 2114 aPermissionType)) { 2115 userPerms = LNAPermission::Granted; 2116 return userPerms; 2117 } 2118 2119 if (nsContentUtils::IsExactSitePermDeny(mLoadInfo->TriggeringPrincipal(), 2120 aPermissionType)) { 2121 userPerms = LNAPermission::Denied; 2122 return userPerms; 2123 } 2124 2125 // Step 2.If this is from third Party Tracker, just block 2126 uint32_t flags = 0; 2127 using CF = nsIClassifiedChannel::ClassificationFlags; 2128 if (StaticPrefs::network_lna_block_trackers() && 2129 NS_SUCCEEDED( 2130 mLoadInfo->GetTriggeringThirdPartyClassificationFlags(&flags)) && 2131 (flags & (CF::CLASSIFIED_ANY_BASIC_TRACKING | 2132 CF::CLASSIFIED_ANY_SOCIAL_TRACKING)) != 0) { 2133 userPerms = LNAPermission::Denied; 2134 return userPerms; 2135 } 2136 2137 // Step 3 2138 // could not determine the permission, lets prompt user 2139 // for permission if lna blocking is enabled 2140 if (StaticPrefs::network_lna_blocking()) { 2141 return userPerms; 2142 } 2143 2144 // we dont have reasons to block the request so we allow this LNA request 2145 // Ideally we should not hit this case once the feature is fully shipped 2146 userPerms = LNAPermission::Granted; 2147 return userPerms; 2148 } 2149 2150 nsresult nsHttpChannel::InitTransaction() { 2151 nsresult rv; 2152 // create wrapper for this channel's notification callbacks 2153 nsCOMPtr<nsIInterfaceRequestor> callbacks; 2154 NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, 2155 getter_AddRefs(callbacks)); 2156 2157 // create the transaction object 2158 if (nsIOService::UseSocketProcess()) { 2159 if (NS_WARN_IF(!gIOService->SocketProcessReady())) { 2160 return NS_ERROR_NOT_AVAILABLE; 2161 } 2162 RefPtr<SocketProcessParent> socketProcess = 2163 SocketProcessParent::GetSingleton(); 2164 if (!socketProcess->CanSend()) { 2165 return NS_ERROR_NOT_AVAILABLE; 2166 } 2167 2168 nsCOMPtr<nsIParentChannel> parentChannel; 2169 NS_QueryNotificationCallbacks(this, parentChannel); 2170 RefPtr<DocumentLoadListener> documentChannelParent = 2171 do_QueryObject(parentChannel); 2172 // See HttpTransactionChild::CanSendODAToContentProcessDirectly() and 2173 // nsHttpChannel::CallOnStartRequest() for the reason why we need to know if 2174 // this is a document load. We only send ODA directly to child process for 2175 // non document loads. 2176 RefPtr<HttpTransactionParent> transParent = 2177 new HttpTransactionParent(!!documentChannelParent); 2178 LOG1(("nsHttpChannel %p created HttpTransactionParent %p\n", this, 2179 transParent.get())); 2180 2181 // Since OnStopRequest could be sent to child process from socket process 2182 // directly, we need to store these two values in HttpTransactionChild and 2183 // forward to child process until HttpTransactionChild::OnStopRequest is 2184 // called. 2185 transParent->SetRedirectTimestamp(mRedirectStartTimeStamp, 2186 mRedirectEndTimeStamp); 2187 2188 if (socketProcess) { 2189 MOZ_ALWAYS_TRUE( 2190 socketProcess->SendPHttpTransactionConstructor(transParent)); 2191 } 2192 mTransaction = transParent; 2193 } else { 2194 mTransaction = new nsHttpTransaction(); 2195 LOG1(("nsHttpChannel %p created nsHttpTransaction %p\n", this, 2196 mTransaction.get())); 2197 } 2198 2199 // Save the mapping of channel id and the channel. We need this mapping for 2200 // nsIHttpActivityObserver. 2201 gHttpHandler->AddHttpChannel(mChannelId, ToSupports(this)); 2202 2203 EnsureBrowserId(); 2204 EnsureRequestContext(); 2205 2206 HttpTrafficCategory category = CreateTrafficCategory(); 2207 std::function<void(TransactionObserverResult&&)> observer; 2208 if (mTransactionObserver) { 2209 observer = [transactionObserver{std::move(mTransactionObserver)}]( 2210 TransactionObserverResult&& aResult) { 2211 transactionObserver->Complete(aResult.versionOk(), aResult.authOk(), 2212 aResult.closeReason()); 2213 }; 2214 } 2215 mTransaction->SetIsForWebTransport(!!mWebTransportSessionEventListener); 2216 2217 RefPtr<mozilla::dom::BrowsingContext> bc; 2218 mLoadInfo->GetBrowsingContext(getter_AddRefs(bc)); 2219 2220 nsILoadInfo::IPAddressSpace parentAddressSpace = 2221 nsILoadInfo::IPAddressSpace::Unknown; 2222 if (!bc) { 2223 parentAddressSpace = mLoadInfo->GetParentIpAddressSpace(); 2224 } else { 2225 parentAddressSpace = bc->GetCurrentIPAddressSpace(); 2226 } 2227 2228 // Check if this is a top-level navigation load and grant LNA permissions 2229 // to skip local network access verification for navigational loads 2230 if (mLoadInfo && StaticPrefs::network_lna_allow_top_level_navigation()) { 2231 ExtContentPolicyType contentPolicyType = 2232 mLoadInfo->GetExternalContentPolicyType(); 2233 if (contentPolicyType == ExtContentPolicy::TYPE_DOCUMENT) { 2234 // Grant permissions for top-level navigation loads 2235 mLNAPermission.mLocalHostPermission = LNAPermission::Granted; 2236 mLNAPermission.mLocalNetworkPermission = LNAPermission::Granted; 2237 } 2238 } 2239 2240 // Grant LNA permissions for captive portal tabs to allow them to access 2241 // local network resources without prompting the user 2242 if (bc && bc->GetIsCaptivePortalTab()) { 2243 mLNAPermission.mLocalNetworkPermission = LNAPermission::Granted; 2244 } 2245 2246 rv = mTransaction->Init( 2247 mCaps, mConnectionInfo, &mRequestHead, mUploadStream, mReqContentLength, 2248 LoadUploadStreamHasHeaders(), GetCurrentSerialEventTarget(), callbacks, 2249 this, mBrowserId, category, mRequestContext, mClassOfService, 2250 mInitialRwin, LoadResponseTimeoutEnabled(), mChannelId, 2251 std::move(observer), parentAddressSpace, mLNAPermission); 2252 if (NS_FAILED(rv)) { 2253 mTransaction = nullptr; 2254 return rv; 2255 } 2256 2257 return rv; 2258 } 2259 2260 HttpTrafficCategory nsHttpChannel::CreateTrafficCategory() { 2261 MOZ_ASSERT(!mFirstPartyClassificationFlags || 2262 !mThirdPartyClassificationFlags); 2263 2264 if (!StaticPrefs::network_traffic_analyzer_enabled()) { 2265 return HttpTrafficCategory::eInvalid; 2266 } 2267 2268 HttpTrafficAnalyzer::ClassOfService cos; 2269 { 2270 if ((mClassOfService.Flags() & nsIClassOfService::Leader) && 2271 mLoadInfo->GetExternalContentPolicyType() == 2272 ExtContentPolicy::TYPE_SCRIPT) { 2273 cos = HttpTrafficAnalyzer::ClassOfService::eLeader; 2274 } else if (mLoadFlags & nsIRequest::LOAD_BACKGROUND) { 2275 cos = HttpTrafficAnalyzer::ClassOfService::eBackground; 2276 } else { 2277 cos = HttpTrafficAnalyzer::ClassOfService::eOther; 2278 } 2279 } 2280 2281 bool isThirdParty = AntiTrackingUtils::IsThirdPartyChannel(this); 2282 2283 HttpTrafficAnalyzer::TrackingClassification tc; 2284 { 2285 uint32_t flags = isThirdParty ? mThirdPartyClassificationFlags 2286 : mFirstPartyClassificationFlags; 2287 2288 using CF = nsIClassifiedChannel::ClassificationFlags; 2289 using TC = HttpTrafficAnalyzer::TrackingClassification; 2290 2291 if (flags & CF::CLASSIFIED_TRACKING_CONTENT) { 2292 tc = TC::eContent; 2293 } else if (flags & CF::CLASSIFIED_FINGERPRINTING_CONTENT) { 2294 tc = TC::eFingerprinting; 2295 } else if (flags & CF::CLASSIFIED_ANY_BASIC_TRACKING) { 2296 tc = TC::eBasic; 2297 } else { 2298 tc = TC::eNone; 2299 } 2300 } 2301 2302 bool isSystemPrincipal = 2303 mLoadInfo->GetLoadingPrincipal() && 2304 mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal(); 2305 return HttpTrafficAnalyzer::CreateTrafficCategory( 2306 NS_UsePrivateBrowsing(this), isSystemPrincipal, isThirdParty, cos, tc); 2307 } 2308 2309 void nsHttpChannel::SetCachedContentType() { 2310 if (!mResponseHead) { 2311 return; 2312 } 2313 2314 nsAutoCString contentTypeStr; 2315 mResponseHead->ContentType(contentTypeStr); 2316 2317 uint8_t contentType = nsICacheEntry::CONTENT_TYPE_OTHER; 2318 if (nsContentUtils::IsJavascriptMIMEType( 2319 NS_ConvertUTF8toUTF16(contentTypeStr))) { 2320 contentType = nsICacheEntry::CONTENT_TYPE_JAVASCRIPT; 2321 } else if (StringBeginsWith(contentTypeStr, "text/css"_ns) || 2322 (mLoadInfo->GetExternalContentPolicyType() == 2323 ExtContentPolicy::TYPE_STYLESHEET)) { 2324 contentType = nsICacheEntry::CONTENT_TYPE_STYLESHEET; 2325 } else if (StringBeginsWith(contentTypeStr, "application/wasm"_ns)) { 2326 contentType = nsICacheEntry::CONTENT_TYPE_WASM; 2327 } else if (StringBeginsWith(contentTypeStr, "image/"_ns)) { 2328 contentType = nsICacheEntry::CONTENT_TYPE_IMAGE; 2329 } else if (StringBeginsWith(contentTypeStr, "video/"_ns)) { 2330 contentType = nsICacheEntry::CONTENT_TYPE_MEDIA; 2331 } else if (StringBeginsWith(contentTypeStr, "audio/"_ns)) { 2332 contentType = nsICacheEntry::CONTENT_TYPE_MEDIA; 2333 } 2334 2335 mCacheEntry->SetContentType(contentType); 2336 } 2337 2338 nsresult nsHttpChannel::CallOnStartRequest() { 2339 LOG(("nsHttpChannel::CallOnStartRequest [this=%p]", this)); 2340 2341 MOZ_RELEASE_ASSERT(!LoadRequireCORSPreflight() || LoadIsCorsPreflightDone(), 2342 "CORS preflight must have been finished by the time we " 2343 "call OnStartRequest"); 2344 2345 MOZ_RELEASE_ASSERT(mCanceled || LoadProcessCrossOriginSecurityHeadersCalled(), 2346 "Security headers need to have been processed before " 2347 "calling CallOnStartRequest"); 2348 2349 mEarlyHintObserver = nullptr; 2350 2351 if (StaticPrefs::network_http_network_error_logging_enabled() && 2352 mResponseHead && mResponseHead->HasHeader(nsHttp::NEL) && 2353 LoadUsedNetwork()) { 2354 // If the policy gets cleared, but this response is still in the cache, 2355 // we probably don't want to reinstall the policy when the entry is 2356 // reloaded. That means it's important to only call RegisterPolicy when 2357 // LoadUsedNetwork() is true. 2358 if (nsCOMPtr<nsINetworkErrorLogging> nel = 2359 components::NetworkErrorLogging::Service()) { 2360 nel->RegisterPolicy(this); 2361 } 2362 } 2363 2364 if (LoadOnStartRequestCalled()) { 2365 // This can only happen when a range request loading rest of the data 2366 // after interrupted concurrent cache read asynchronously failed, e.g. 2367 // the response range bytes are not as expected or this channel has 2368 // been externally canceled. 2369 // 2370 // It's legal to bypass CallOnStartRequest for that case since we've 2371 // already called OnStartRequest on our listener and also added all 2372 // content converters before. 2373 MOZ_ASSERT(LoadConcurrentCacheAccess()); 2374 LOG(("CallOnStartRequest already invoked before")); 2375 return mStatus; 2376 } 2377 2378 // Ensure mListener->OnStartRequest will be invoked before exiting 2379 // this function. 2380 auto onStartGuard = MakeScopeExit([&] { 2381 LOG( 2382 (" calling mListener->OnStartRequest by ScopeExit [this=%p, " 2383 "listener=%p]\n", 2384 this, mListener.get())); 2385 MOZ_ASSERT(!LoadOnStartRequestCalled()); 2386 2387 if (mListener) { 2388 nsCOMPtr<nsIStreamListener> deleteProtector(mListener); 2389 StoreOnStartRequestCalled(true); 2390 deleteProtector->OnStartRequest(this); 2391 } 2392 StoreOnStartRequestCalled(true); 2393 }); 2394 2395 nsresult rv = ValidateMIMEType(); 2396 // Since ODA and OnStopRequest could be sent from socket process directly, we 2397 // need to update the channel status before calling mListener->OnStartRequest. 2398 // This is the only way to let child process discard the already received ODA 2399 // messages. 2400 if (NS_FAILED(rv)) { 2401 mStatus = rv; 2402 return mStatus; 2403 } 2404 2405 // EnsureOpaqueResponseIsAllowed and EnsureOpauqeResponseIsAllowedAfterSniff 2406 // are the checks for Opaque Response Blocking to ensure that we block as many 2407 // cross-origin responses with CORS headers as possible that are not either 2408 // Javascript or media to avoid leaking their contents through side channels. 2409 OpaqueResponse opaqueResponse = 2410 PerformOpaqueResponseSafelistCheckBeforeSniff(); 2411 if (opaqueResponse == OpaqueResponse::Block) { 2412 SetChannelBlockedByOpaqueResponse(); 2413 CancelWithReason(NS_BINDING_ABORTED, 2414 "OpaqueResponseBlocker::BlockResponse"_ns); 2415 return NS_BINDING_ABORTED; 2416 } 2417 2418 // We must ensure that any dcb/dcz content is decompressed in the parent 2419 // process, and removed from the Content-Encoding before it's sent down 2420 // to the content process. In most (all?) cases, this should have occurred 2421 // before this. 2422 if (mResponseHead && XRE_IsParentProcess() && !LoadHasAppliedConversion()) { 2423 nsAutoCString contentEncoding; 2424 (void)mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding); 2425 // Note: doesn't handle multiple compressors: "dcb, gzip" or 2426 // "gzip, dcb" (etc) 2427 if (contentEncoding.Equals("dcb") || contentEncoding.Equals("dcz")) { 2428 LOG_DICTIONARIES( 2429 ("Still had %s encoding at CallOnStartRequest, converting", 2430 contentEncoding.get())); 2431 nsCOMPtr<nsIStreamListener> listener; 2432 MOZ_DIAGNOSTIC_ASSERT(LoadHasAppliedConversion() == false); 2433 // Insist we install a converter. This should never be the case, but 2434 // if somehow it is, we need a converter; content can't be allowed 2435 // to see dcb/dcz since it can't convert them 2436 StoreHasAppliedConversion(false); 2437 rv = DoApplyContentConversions(mListener, getter_AddRefs(listener), 2438 nullptr); 2439 if (NS_FAILED(rv)) { 2440 return rv; 2441 } 2442 if (listener) { 2443 MOZ_ASSERT(!LoadDataSentToChildProcess(), 2444 "DataSentToChildProcess being true means ODAs are sent to " 2445 "the child process directly. We MUST NOT apply content " 2446 "converter in this case."); 2447 mListener = listener; 2448 mCompressListener = listener; 2449 2450 StoreHasAppliedConversion(true); 2451 } 2452 } 2453 } 2454 2455 // Allow consumers to override our content type 2456 if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) { 2457 // NOTE: We can have both a txn pump and a cache pump when the cache 2458 // content is partial. In that case, we need to read from the cache, 2459 // because that's the one that has the initial contents. If that fails 2460 // then give the transaction pump a shot. 2461 2462 nsIChannel* thisChannel = static_cast<nsIChannel*>(this); 2463 2464 bool typeSniffersCalled = false; 2465 if (mCachePump) { 2466 typeSniffersCalled = 2467 NS_SUCCEEDED(mCachePump->PeekStream(CallTypeSniffers, thisChannel)); 2468 } 2469 2470 if (!typeSniffersCalled && mTransactionPump) { 2471 RefPtr<nsInputStreamPump> pump = do_QueryObject(mTransactionPump); 2472 if (pump) { 2473 pump->PeekStream(CallTypeSniffers, thisChannel); 2474 } else { 2475 MOZ_ASSERT(nsIOService::UseSocketProcess()); 2476 RefPtr<HttpTransactionParent> trans = do_QueryObject(mTransactionPump); 2477 MOZ_ASSERT(trans); 2478 trans->SetSniffedTypeToChannel(CallTypeSniffers, thisChannel); 2479 } 2480 } 2481 } 2482 2483 // Note that the code below should be synced with the code in 2484 // HttpTransactionChild::CanSendODAToContentProcessDirectly(). We MUST make 2485 // sure HttpTransactionChild::CanSendODAToContentProcessDirectly() returns 2486 // false when a stream converter is applied. 2487 bool unknownDecoderStarted = false; 2488 if (mResponseHead && !mResponseHead->HasContentType()) { 2489 MOZ_ASSERT(mConnectionInfo, "Should have connection info here"); 2490 if (!mContentTypeHint.IsEmpty()) { 2491 mResponseHead->SetContentType(mContentTypeHint); 2492 } else if (mResponseHead->Version() == HttpVersion::v0_9 && 2493 mConnectionInfo->OriginPort() != 2494 mConnectionInfo->DefaultPort()) { 2495 mResponseHead->SetContentType(nsLiteralCString(TEXT_PLAIN)); 2496 } else { 2497 // Uh-oh. We had better find out what type we are! 2498 mListener = new nsUnknownDecoder(mListener); 2499 unknownDecoderStarted = true; 2500 } 2501 } 2502 2503 // If unknownDecoder is not going to be launched, call 2504 // EnsureOpaqueResponseIsAllowedAfterSniff immediately. 2505 if (!unknownDecoderStarted) { 2506 if (opaqueResponse == OpaqueResponse::SniffCompressed) { 2507 mListener = new nsCompressedAudioVideoImageDetector( 2508 mListener, &HttpBaseChannel::CallTypeSniffers); 2509 } else if (opaqueResponse == OpaqueResponse::Sniff) { 2510 MOZ_DIAGNOSTIC_ASSERT(mORB); 2511 nsresult rv = mORB->EnsureOpaqueResponseIsAllowedAfterSniff(this); 2512 2513 if (NS_FAILED(rv)) { 2514 return rv; 2515 } 2516 } 2517 } 2518 2519 // If the content is multipart/x-mixed-replace, we'll insert a MIME decoder 2520 // in the pipeline to handle the content and pass it along to our 2521 // original listener. nsUnknownDecoder doesn't support detecting this type, 2522 // so we only need to insert this using the response header's mime type. 2523 // 2524 // We only do this for unwrapped document loads, since we might want to send 2525 // parts to the external protocol handler without leaving the parent process. 2526 bool mustRunStreamFilterInParent = false; 2527 nsCOMPtr<nsIParentChannel> parentChannel; 2528 NS_QueryNotificationCallbacks(this, parentChannel); 2529 RefPtr<DocumentLoadListener> docListener = do_QueryObject(parentChannel); 2530 if (mResponseHead && docListener && docListener->GetChannel() == this) { 2531 nsAutoCString contentType; 2532 mResponseHead->ContentType(contentType); 2533 2534 if (contentType.Equals("multipart/x-mixed-replace"_ns)) { 2535 nsCOMPtr<nsIStreamConverterService> convServ( 2536 mozilla::components::StreamConverter::Service(&rv)); 2537 if (NS_SUCCEEDED(rv)) { 2538 nsCOMPtr<nsIStreamListener> toListener(mListener); 2539 nsCOMPtr<nsIStreamListener> fromListener; 2540 2541 rv = convServ->AsyncConvertData("multipart/x-mixed-replace", "*/*", 2542 toListener, nullptr, 2543 getter_AddRefs(fromListener)); 2544 if (NS_SUCCEEDED(rv)) { 2545 mListener = fromListener; 2546 mustRunStreamFilterInParent = true; 2547 } 2548 } 2549 } 2550 } 2551 2552 // If we installed a multipart converter, then we need to add StreamFilter 2553 // object before it, so that extensions see the un-parsed original stream. 2554 // We may want to add an option for extensions to opt-in to proper multipart 2555 // handling. 2556 // If not, then pass the StreamFilter promise on to DocumentLoadListener, 2557 // where it'll be added in the content process. 2558 for (StreamFilterRequest& request : mStreamFilterRequests) { 2559 if (mustRunStreamFilterInParent) { 2560 mozilla::ipc::Endpoint<extensions::PStreamFilterParent> parent; 2561 mozilla::ipc::Endpoint<extensions::PStreamFilterChild> child; 2562 nsresult rv = extensions::PStreamFilter::CreateEndpoints(&parent, &child); 2563 if (NS_FAILED(rv)) { 2564 request.mPromise->Reject(false, __func__); 2565 } else { 2566 extensions::StreamFilterParent::Attach(this, std::move(parent)); 2567 request.mPromise->Resolve(std::move(child), __func__); 2568 } 2569 } else { 2570 if (docListener) { 2571 docListener->AttachStreamFilter()->ChainTo(request.mPromise.forget(), 2572 __func__); 2573 } else { 2574 request.mPromise->Reject(false, __func__); 2575 } 2576 } 2577 request.mPromise = nullptr; 2578 } 2579 mStreamFilterRequests.Clear(); 2580 StoreTracingEnabled(false); 2581 2582 if (mResponseHead && !mResponseHead->HasContentCharset()) { 2583 mResponseHead->SetContentCharset(mContentCharsetHint); 2584 } 2585 2586 if (mCacheEntry && LoadCacheEntryIsWriteOnly()) { 2587 SetCachedContentType(); 2588 } 2589 2590 LOG((" calling mListener->OnStartRequest [this=%p, listener=%p]\n", this, 2591 mListener.get())); 2592 2593 // About to call OnStartRequest, dismiss the guard object. 2594 onStartGuard.release(); 2595 2596 if (mListener) { 2597 MOZ_ASSERT(!LoadOnStartRequestCalled(), 2598 "We should not call OsStartRequest twice"); 2599 nsCOMPtr<nsIStreamListener> deleteProtector(mListener); 2600 StoreOnStartRequestCalled(true); 2601 rv = deleteProtector->OnStartRequest(this); 2602 if (NS_FAILED(rv)) return rv; 2603 } else { 2604 NS_WARNING("OnStartRequest skipped because of null listener"); 2605 StoreOnStartRequestCalled(true); 2606 } 2607 2608 // Install stream converter if required. 2609 // Normally, we expect the listener to disable content conversion during 2610 // OnStartRequest if it wants to handle it itself (which is common case with 2611 // HttpChannelParent, disabling so that it can be done in the content 2612 // process). If we've installed an nsUnknownDecoder, then we won't yet have 2613 // called OnStartRequest on the final listener (that happens after we send 2614 // OnDataAvailable to the nsUnknownDecoder), so it can't yet have disabled 2615 // content conversion. 2616 // In that case, assume that the listener will disable content conversion, 2617 // unless it's specifically told us that it won't. 2618 if (!unknownDecoderStarted || LoadListenerRequiresContentConversion()) { 2619 nsCOMPtr<nsIStreamListener> listener; 2620 rv = 2621 DoApplyContentConversions(mListener, getter_AddRefs(listener), nullptr); 2622 if (NS_FAILED(rv)) { 2623 return rv; 2624 } 2625 if (listener) { 2626 MOZ_ASSERT(!LoadDataSentToChildProcess(), 2627 "DataSentToChildProcess being true means ODAs are sent to " 2628 "the child process directly. We MUST NOT apply content " 2629 "converter in this case."); 2630 mListener = listener; 2631 mCompressListener = listener; 2632 2633 StoreHasAppliedConversion(true); 2634 } 2635 } 2636 2637 // if this channel is for a download, close off access to the cache. 2638 if (mCacheEntry && LoadChannelIsForDownload()) { 2639 mCacheEntry->AsyncDoom(nullptr); 2640 2641 // We must keep the cache entry in case of partial request. 2642 // Concurrent access is the same, we need the entry in 2643 // OnStopRequest. 2644 // We also need the cache entry when racing cache with network to find 2645 // out what is the source of the data. 2646 if (!LoadCachedContentIsPartial() && !LoadConcurrentCacheAccess() && 2647 !(mRaceCacheWithNetwork && 2648 mFirstResponseSource == RESPONSE_FROM_CACHE)) { 2649 CloseCacheEntry(false); 2650 } 2651 } 2652 2653 return NS_OK; 2654 } 2655 2656 NS_IMETHODIMP nsHttpChannel::GetHttpProxyConnectResponseCode( 2657 int32_t* aResponseCode) { 2658 NS_ENSURE_ARG_POINTER(aResponseCode); 2659 2660 if (mConnectionInfo && mConnectionInfo->UsingConnect()) { 2661 *aResponseCode = mProxyConnectResponseCode; 2662 } else { 2663 *aResponseCode = -1; 2664 } 2665 return NS_OK; 2666 } 2667 2668 nsresult nsHttpChannel::ProcessFailedProxyConnect(uint32_t httpStatus) { 2669 // Failure to set up a proxy tunnel via CONNECT means one of the following: 2670 // 1) Proxy wants authorization, or forbids. 2671 // 2) DNS at proxy couldn't resolve target URL. 2672 // 3) Proxy connection to target failed or timed out. 2673 // 4) Eve intercepted our CONNECT, and is replying with malicious HTML. 2674 // 2675 // Our current architecture would parse the proxy's response content with 2676 // the permission of the target URL. Given #4, we must avoid rendering the 2677 // body of the reply, and instead give the user a (hopefully helpful) 2678 // boilerplate error page, based on just the HTTP status of the reply. 2679 2680 MOZ_ASSERT(mConnectionInfo->UsingConnect(), 2681 "proxy connect failed but not using CONNECT?"); 2682 nsresult rv = HttpProxyResponseToErrorCode(httpStatus); 2683 LOG(("Cancelling failed proxy CONNECT [this=%p httpStatus=%u]\n", this, 2684 httpStatus)); 2685 2686 // Make sure the connection is thrown away as it can be in a bad state 2687 // and the proxy may just hang on the next request. 2688 MOZ_ASSERT(mTransaction); 2689 mTransaction->DontReuseConnection(); 2690 2691 Cancel(rv); 2692 { 2693 nsresult rv = CallOnStartRequest(); 2694 if (NS_FAILED(rv)) { 2695 LOG(("CallOnStartRequest failed [this=%p httpStatus=%u rv=%08x]\n", this, 2696 httpStatus, static_cast<uint32_t>(rv))); 2697 } 2698 } 2699 return rv; 2700 } 2701 2702 static void GetSTSConsoleErrorTag(uint32_t failureResult, 2703 nsAString& consoleErrorTag) { 2704 switch (failureResult) { 2705 case nsISiteSecurityService::ERROR_COULD_NOT_PARSE_HEADER: 2706 consoleErrorTag = u"STSCouldNotParseHeader"_ns; 2707 break; 2708 case nsISiteSecurityService::ERROR_NO_MAX_AGE: 2709 consoleErrorTag = u"STSNoMaxAge"_ns; 2710 break; 2711 case nsISiteSecurityService::ERROR_MULTIPLE_MAX_AGES: 2712 consoleErrorTag = u"STSMultipleMaxAges"_ns; 2713 break; 2714 case nsISiteSecurityService::ERROR_INVALID_MAX_AGE: 2715 consoleErrorTag = u"STSInvalidMaxAge"_ns; 2716 break; 2717 case nsISiteSecurityService::ERROR_MULTIPLE_INCLUDE_SUBDOMAINS: 2718 consoleErrorTag = u"STSMultipleIncludeSubdomains"_ns; 2719 break; 2720 case nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS: 2721 consoleErrorTag = u"STSInvalidIncludeSubdomains"_ns; 2722 break; 2723 case nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE: 2724 consoleErrorTag = u"STSCouldNotSaveState"_ns; 2725 break; 2726 default: 2727 consoleErrorTag = u"STSUnknownError"_ns; 2728 break; 2729 } 2730 } 2731 2732 /** 2733 * Process an HTTP Strict Transport Security (HSTS) header. 2734 */ 2735 nsresult nsHttpChannel::ProcessHSTSHeader(nsITransportSecurityInfo* aSecInfo) { 2736 nsHttpAtom atom(nsHttp::ResolveAtom("Strict-Transport-Security"_ns)); 2737 2738 nsAutoCString securityHeader; 2739 nsresult rv = mResponseHead->GetHeader(atom, securityHeader); 2740 if (rv == NS_ERROR_NOT_AVAILABLE) { 2741 LOG(("nsHttpChannel: No %s header, continuing load.\n", atom.get())); 2742 return NS_OK; 2743 } 2744 if (NS_WARN_IF(NS_FAILED(rv))) { 2745 return rv; 2746 } 2747 2748 if (!aSecInfo) { 2749 LOG(("nsHttpChannel::ProcessHSTSHeader: no securityInfo?")); 2750 return NS_ERROR_INVALID_ARG; 2751 } 2752 nsITransportSecurityInfo::OverridableErrorCategory overridableErrorCategory; 2753 rv = aSecInfo->GetOverridableErrorCategory(&overridableErrorCategory); 2754 if (NS_WARN_IF(NS_FAILED(rv))) { 2755 return rv; 2756 } 2757 if (overridableErrorCategory != 2758 nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET) { 2759 LOG( 2760 ("nsHttpChannel::ProcessHSTSHeader: untrustworthy connection - not " 2761 "processing header")); 2762 return NS_ERROR_FAILURE; 2763 } 2764 2765 nsISiteSecurityService* sss = gHttpHandler->GetSSService(); 2766 NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY); 2767 2768 OriginAttributes originAttributes; 2769 if (NS_WARN_IF(!StoragePrincipalHelper::GetOriginAttributesForHSTS( 2770 this, originAttributes))) { 2771 return NS_ERROR_FAILURE; 2772 } 2773 2774 uint32_t failureResult; 2775 rv = sss->ProcessHeader(mURI, securityHeader, originAttributes, nullptr, 2776 nullptr, &failureResult); 2777 if (NS_FAILED(rv)) { 2778 nsAutoString consoleErrorCategory(u"Invalid HSTS Headers"_ns); 2779 nsAutoString consoleErrorTag; 2780 GetSTSConsoleErrorTag(failureResult, consoleErrorTag); 2781 (void)AddSecurityMessage(consoleErrorTag, consoleErrorCategory); 2782 LOG(("nsHttpChannel: Failed to parse %s header, continuing load.\n", 2783 atom.get())); 2784 } 2785 return NS_OK; 2786 } 2787 2788 /** 2789 * Decide whether or not to remember Strict-Transport-Security, and whether 2790 * or not to enforce channel integrity. 2791 * 2792 * @return NS_ERROR_FAILURE if there's security information missing even though 2793 * it's an HTTPS connection. 2794 */ 2795 nsresult nsHttpChannel::ProcessSecurityHeaders() { 2796 // If this channel is not loading securely, STS or PKP doesn't do anything. 2797 // In the case of HSTS, the upgrade to HTTPS takes place earlier in the 2798 // channel load process. 2799 if (!mURI->SchemeIs("https")) { 2800 return NS_OK; 2801 } 2802 2803 if (IsBrowsingContextDiscarded()) { 2804 return NS_OK; 2805 } 2806 2807 nsAutoCString asciiHost; 2808 nsresult rv = mURI->GetAsciiHost(asciiHost); 2809 NS_ENSURE_SUCCESS(rv, NS_OK); 2810 2811 // If the channel is not a hostname, but rather an IP, do not process STS 2812 // or PKP headers 2813 if (HostIsIPLiteral(asciiHost)) { 2814 return NS_OK; 2815 } 2816 2817 // mSecurityInfo may not always be present, and if it's not then it is okay 2818 // to just disregard any security headers since we know nothing about the 2819 // security of the connection. 2820 NS_ENSURE_TRUE(mSecurityInfo, NS_OK); 2821 2822 // Only process HSTS headers for first-party loads. This prevents a 2823 // proliferation of useless HSTS state for partitioned third parties. 2824 if (!mLoadInfo->GetIsThirdPartyContextToTopWindow()) { 2825 rv = ProcessHSTSHeader(mSecurityInfo); 2826 NS_ENSURE_SUCCESS(rv, rv); 2827 } 2828 2829 return NS_OK; 2830 } 2831 2832 bool nsHttpChannel::IsHTTPS() { return mURI->SchemeIs("https"); } 2833 2834 void nsHttpChannel::ProcessSSLInformation() { 2835 // If this is HTTPS, record any use of RSA so that Key Exchange Algorithm 2836 // can be whitelisted for TLS False Start in future sessions. We could 2837 // do the same for DH but its rarity doesn't justify the lookup. 2838 2839 if (mCanceled || NS_FAILED(mStatus) || !mSecurityInfo || !IsHTTPS() || 2840 mPrivateBrowsing) { 2841 return; 2842 } 2843 2844 if (!mSecurityInfo) { 2845 return; 2846 } 2847 2848 uint32_t state; 2849 if (NS_SUCCEEDED(mSecurityInfo->GetSecurityState(&state)) && 2850 (state & nsIWebProgressListener::STATE_IS_BROKEN)) { 2851 // Send weak crypto warnings to the web console 2852 if (state & nsIWebProgressListener::STATE_USES_WEAK_CRYPTO) { 2853 nsString consoleErrorTag = u"WeakCipherSuiteWarning"_ns; 2854 nsString consoleErrorCategory = u"SSL"_ns; 2855 (void)AddSecurityMessage(consoleErrorTag, consoleErrorCategory); 2856 } 2857 } 2858 2859 uint16_t tlsVersion; 2860 nsresult rv = mSecurityInfo->GetProtocolVersion(&tlsVersion); 2861 if (NS_SUCCEEDED(rv) && 2862 tlsVersion != nsITransportSecurityInfo::TLS_VERSION_1_2 && 2863 tlsVersion != nsITransportSecurityInfo::TLS_VERSION_1_3) { 2864 nsString consoleErrorTag = u"DeprecatedTLSVersion2"_ns; 2865 nsString consoleErrorCategory = u"TLS"_ns; 2866 (void)AddSecurityMessage(consoleErrorTag, consoleErrorCategory); 2867 } 2868 } 2869 2870 void nsHttpChannel::ProcessAltService(nsHttpConnectionInfo* aTransConnInfo) { 2871 // e.g. Alt-Svc: h2=":443"; ma=60 2872 // e.g. Alt-Svc: h2="otherhost:443" 2873 // Alt-Svc = 1#( alternative *( OWS ";" OWS parameter ) ) 2874 // alternative = protocol-id "=" alt-authority 2875 // protocol-id = token ; percent-encoded ALPN protocol identifier 2876 // alt-authority = quoted-string ; containing [ uri-host ] ":" port 2877 2878 if (!LoadAllowAltSvc()) { // per channel opt out 2879 return; 2880 } 2881 2882 if (mWebTransportSessionEventListener) { 2883 return; 2884 } 2885 2886 if (!gHttpHandler->AllowAltSvc() || (mCaps & NS_HTTP_DISALLOW_SPDY)) { 2887 return; 2888 } 2889 2890 if (IsBrowsingContextDiscarded()) { 2891 return; 2892 } 2893 2894 nsAutoCString scheme; 2895 mURI->GetScheme(scheme); 2896 bool isHttp = scheme.EqualsLiteral("http"); 2897 if (!isHttp && !scheme.EqualsLiteral("https")) { 2898 return; 2899 } 2900 2901 nsAutoCString altSvc; 2902 (void)mResponseHead->GetHeader(nsHttp::Alternate_Service, altSvc); 2903 if (altSvc.IsEmpty()) { 2904 return; 2905 } 2906 2907 if (!nsHttp::IsReasonableHeaderValue(altSvc)) { 2908 LOG(("Alt-Svc Response Header seems unreasonable - skipping\n")); 2909 return; 2910 } 2911 2912 nsAutoCString originHost; 2913 int32_t originPort = 80; 2914 mURI->GetPort(&originPort); 2915 if (NS_FAILED(mURI->GetAsciiHost(originHost))) { 2916 return; 2917 } 2918 2919 nsCOMPtr<nsIInterfaceRequestor> callbacks; 2920 nsCOMPtr<nsProxyInfo> proxyInfo; 2921 NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, 2922 getter_AddRefs(callbacks)); 2923 2924 if (mProxyInfo) { 2925 proxyInfo = do_QueryInterface(mProxyInfo); 2926 } 2927 2928 OriginAttributes originAttributes; 2929 // Regular principal in case we have a proxy. 2930 if (proxyInfo && 2931 !StaticPrefs::privacy_partition_network_state_connection_with_proxy()) { 2932 StoragePrincipalHelper::GetOriginAttributes( 2933 this, originAttributes, StoragePrincipalHelper::eRegularPrincipal); 2934 } else { 2935 StoragePrincipalHelper::GetOriginAttributesForNetworkState( 2936 this, originAttributes); 2937 } 2938 2939 AltSvcMapping::ProcessHeader(altSvc, scheme, originHost, originPort, 2940 mUsername, mPrivateBrowsing, callbacks, 2941 proxyInfo, mCaps & NS_HTTP_DISALLOW_SPDY, 2942 originAttributes, aTransConnInfo); 2943 } 2944 2945 nsresult nsHttpChannel::ProcessResponse(nsHttpConnectionInfo* aConnInfo) { 2946 uint32_t httpStatus = mResponseHead->Status(); 2947 2948 LOG(("nsHttpChannel::ProcessResponse [this=%p httpStatus=%u]\n", this, 2949 httpStatus)); 2950 2951 // Gather data on whether the transaction and page (if this is 2952 // the initial page load) is being loaded with SSL. 2953 glean::http::transaction_is_ssl 2954 .EnumGet(static_cast<glean::http::TransactionIsSslLabel>( 2955 mConnectionInfo->EndToEndSSL())) 2956 .Add(); 2957 if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) { 2958 glean::http::pageload_is_ssl 2959 .EnumGet(static_cast<glean::http::PageloadIsSslLabel>( 2960 mConnectionInfo->EndToEndSSL())) 2961 .Add(); 2962 } 2963 2964 if (Telemetry::CanRecordPrereleaseData()) { 2965 // Gather data on various response status to monitor any increased frequency 2966 // of auth failures due to Bug 1896350 2967 switch (httpStatus) { 2968 case 200: 2969 mozilla::glean::networking::http_response_status_code.Get("200_ok"_ns) 2970 .Add(1); 2971 break; 2972 case 301: 2973 mozilla::glean::networking::http_response_status_code 2974 .Get("301_moved_permanently"_ns) 2975 .Add(1); 2976 break; 2977 case 302: 2978 mozilla::glean::networking::http_response_status_code 2979 .Get("302_found"_ns) 2980 .Add(1); 2981 break; 2982 case 304: 2983 mozilla::glean::networking::http_response_status_code 2984 .Get("304_not_modified"_ns) 2985 .Add(1); 2986 break; 2987 case 307: 2988 mozilla::glean::networking::http_response_status_code 2989 .Get("307_temporary_redirect"_ns) 2990 .Add(1); 2991 break; 2992 case 308: 2993 mozilla::glean::networking::http_response_status_code 2994 .Get("308_permanent_redirect"_ns) 2995 .Add(1); 2996 break; 2997 case 400: 2998 mozilla::glean::networking::http_response_status_code 2999 .Get("400_bad_request"_ns) 3000 .Add(1); 3001 break; 3002 case 401: 3003 mozilla::glean::networking::http_response_status_code 3004 .Get("401_unauthorized"_ns) 3005 .Add(1); 3006 break; 3007 case 403: 3008 mozilla::glean::networking::http_response_status_code 3009 .Get("403_forbidden"_ns) 3010 .Add(1); 3011 break; 3012 case 404: 3013 mozilla::glean::networking::http_response_status_code 3014 .Get("404_not_found"_ns) 3015 .Add(1); 3016 break; 3017 case 421: 3018 mozilla::glean::networking::http_response_status_code 3019 .Get("421_misdirected_request"_ns) 3020 .Add(1); 3021 break; 3022 case 425: 3023 mozilla::glean::networking::http_response_status_code 3024 .Get("425_too_early"_ns) 3025 .Add(1); 3026 break; 3027 case 429: 3028 mozilla::glean::networking::http_response_status_code 3029 .Get("429_too_many_requests"_ns) 3030 .Add(1); 3031 break; 3032 case 500: 3033 mozilla::glean::networking::http_response_status_code 3034 .Get("other_5xx"_ns) 3035 .Add(1); 3036 break; 3037 default: 3038 if (httpStatus >= 400 && httpStatus < 500) { 3039 mozilla::glean::networking::http_response_status_code 3040 .Get("other_4xx"_ns) 3041 .Add(1); 3042 } else if (httpStatus > 500) { 3043 mozilla::glean::networking::http_response_status_code 3044 .Get("other_5xx"_ns) 3045 .Add(1); 3046 } else { 3047 mozilla::glean::networking::http_response_status_code.Get("other"_ns) 3048 .Add(1); 3049 } 3050 break; 3051 } 3052 } 3053 3054 // We use GetReferringPage because mReferrerInfo may not be set at all(this is 3055 // especially useful in xpcshell tests, where we don't have an actual pageload 3056 // to get a referrer from). 3057 // Only allow 407 (authentication required) to continue 3058 if (mTransaction && mTransaction->ProxyConnectFailed() && httpStatus != 407) { 3059 return ProcessFailedProxyConnect(httpStatus); 3060 } 3061 3062 MOZ_ASSERT(!CachedContentIsValid() || mRaceCacheWithNetwork, 3063 "We should not be hitting the network if we have valid cached " 3064 "content unless we are racing the network and cache"); 3065 3066 ProcessSSLInformation(); 3067 3068 // notify "http-on-examine-response" observers 3069 gHttpHandler->OnExamineResponse(this); 3070 3071 return ContinueProcessResponse1(aConnInfo); 3072 } 3073 3074 void nsHttpChannel::AsyncContinueProcessResponse( 3075 nsHttpConnectionInfo* aConnInfo) { 3076 nsresult rv; 3077 rv = ContinueProcessResponse1(aConnInfo); 3078 if (NS_FAILED(rv)) { 3079 // A synchronous failure here would normally be passed as the return 3080 // value from OnStartRequest, which would in turn cancel the request. 3081 // If we're continuing asynchronously, we need to cancel the request 3082 // ourselves. 3083 (void)Cancel(rv); 3084 } 3085 } 3086 3087 nsresult nsHttpChannel::ContinueProcessResponse1( 3088 nsHttpConnectionInfo* aConnInfo) { 3089 MOZ_ASSERT(!mCallOnResume, "How did that happen?"); 3090 nsresult rv = NS_OK; 3091 3092 if (mSuspendCount) { 3093 LOG(("Waiting until resume to finish processing response [this=%p]\n", 3094 this)); 3095 mCallOnResume = [connInfo = RefPtr{aConnInfo}](nsHttpChannel* self) { 3096 self->AsyncContinueProcessResponse(connInfo); 3097 return NS_OK; 3098 }; 3099 return NS_OK; 3100 } 3101 3102 // Check if request was cancelled during http-on-examine-response. 3103 if (mCanceled) { 3104 return CallOnStartRequest(); 3105 } 3106 3107 uint32_t httpStatus = mResponseHead->Status(); 3108 3109 // STS, Cookies and Alt-Service should not be handled on proxy failure. 3110 // If proxy CONNECT response needs to complete, wait to process connection 3111 // for Strict-Transport-Security. 3112 if (!(mTransaction && mTransaction->ProxyConnectFailed()) && 3113 (httpStatus != 407)) { 3114 // This block parses the cookie header, collects any cookie changes, 3115 // and sends them to the parent actor. 3116 { 3117 RefPtr<CookieObserver> cookieObserver; 3118 RefPtr<HttpChannelParent> httpParent; 3119 CookieServiceParent::CookieProcessingGuard cookieProcessingGuard; 3120 3121 if (!LoadOnStartRequestCalled()) { 3122 // This can only happen when a range request is created again in 3123 // nsHttpChannel::ContinueOnStopRequest. If OnStartRequest is already 3124 // called, we shouldn't call SetCookieHeaders. 3125 3126 MaybeInitializeCookieProcessingGuard(this, cookieProcessingGuard, 3127 cookieObserver, httpParent, 3128 httpStatus); 3129 } 3130 3131 CookieVisitor cookieVisitor(mResponseHead.get()); 3132 SetCookieHeaders(cookieVisitor.CookieHeaders()); 3133 3134 if (cookieObserver) { 3135 nsTArray<CookieChange> cookieChanges; 3136 cookieObserver->StealChanges(cookieChanges); 3137 3138 if (!cookieChanges.IsEmpty()) { 3139 MOZ_ASSERT(httpParent); 3140 httpParent->SetCookieChanges(std::move(cookieChanges)); 3141 } 3142 } 3143 } 3144 3145 // Given a successful connection, process any STS or PKP data that's 3146 // relevant. 3147 nsresult rv = ProcessSecurityHeaders(); 3148 if (NS_FAILED(rv)) { 3149 NS_WARNING("ProcessSTSHeader failed, continuing load."); 3150 } 3151 3152 if ((httpStatus < 500) && (httpStatus != 421)) { 3153 ProcessAltService(aConnInfo); 3154 } 3155 } 3156 3157 if (LoadConcurrentCacheAccess() && LoadCachedContentIsPartial() && 3158 httpStatus != 206) { 3159 LOG( 3160 (" only expecting 206 when doing partial request during " 3161 "interrupted cache concurrent read")); 3162 return NS_ERROR_CORRUPTED_CONTENT; 3163 } 3164 3165 if (httpStatus != 401 && httpStatus != 407) { 3166 // reset the authentication's current continuation state because ourvr 3167 // last authentication attempt has been completed successfully 3168 MOZ_DIAGNOSTIC_ASSERT(mAuthProvider); 3169 rv = mAuthProvider ? mAuthProvider->Disconnect(NS_ERROR_ABORT) 3170 : NS_ERROR_UNEXPECTED; 3171 if (NS_FAILED(rv)) { 3172 LOG((" Disconnect failed (%08x)", static_cast<uint32_t>(rv))); 3173 } 3174 mAuthProvider = nullptr; 3175 LOG((" continuation state has been reset")); 3176 } 3177 3178 gHttpHandler->OnAfterExamineResponse(this); 3179 3180 // No process switch needed, continue as normal. 3181 return ContinueProcessResponse2(rv); 3182 } 3183 3184 nsresult nsHttpChannel::ContinueProcessResponse2(nsresult rv) { 3185 if (mSuspendCount) { 3186 LOG(("Waiting until resume to finish processing response [this=%p]\n", 3187 this)); 3188 mCallOnResume = [rv](nsHttpChannel* self) { 3189 (void)self->ContinueProcessResponse2(rv); 3190 return NS_OK; 3191 }; 3192 return NS_OK; 3193 } 3194 3195 if (NS_FAILED(rv) && !mCanceled) { 3196 // The process switch failed, cancel this channel. 3197 Cancel(rv); 3198 return CallOnStartRequest(); 3199 } 3200 3201 if (mAPIRedirectTo && !mCanceled) { 3202 MOZ_ASSERT(!LoadOnStartRequestCalled()); 3203 3204 PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3); 3205 rv = StartRedirectChannelToURI( 3206 mAPIRedirectTo->first(), 3207 mAPIRedirectTo->second() ? nsIChannelEventSink::REDIRECT_TEMPORARY | 3208 nsIChannelEventSink::REDIRECT_TRANSPARENT 3209 : nsIChannelEventSink::REDIRECT_TEMPORARY); 3210 mAPIRedirectTo = Nothing(); 3211 if (NS_SUCCEEDED(rv)) { 3212 return NS_OK; 3213 } 3214 PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse3); 3215 } 3216 3217 // Hack: ContinueProcessResponse3 uses NS_OK to detect successful 3218 // redirects, so we distinguish this codepath (a non-redirect that's 3219 // processing normally) by passing in a bogus error code. 3220 return ContinueProcessResponse3(NS_BINDING_FAILED); 3221 } 3222 3223 nsresult nsHttpChannel::ContinueProcessResponse3(nsresult rv) { 3224 LOG(("nsHttpChannel::ContinueProcessResponse3 [this=%p, rv=%" PRIx32 "]", 3225 this, static_cast<uint32_t>(rv))); 3226 3227 if (NS_SUCCEEDED(rv)) { 3228 // redirectTo() has passed through, we don't want to go on with 3229 // this channel. It will now be canceled by the redirect handling 3230 // code that called this function. 3231 return NS_OK; 3232 } 3233 3234 rv = NS_OK; 3235 3236 uint32_t httpStatus = mResponseHead->Status(); 3237 bool transactionRestarted = mTransaction->TakeRestartedState(); 3238 3239 // handle different server response categories. Note that we handle 3240 // caching or not caching of error pages in 3241 // nsHttpResponseHead::MustValidate; if you change this switch, update that 3242 // one 3243 switch (httpStatus) { 3244 case 200: 3245 case 203: 3246 // Per RFC 2616, 14.35.2, "A server MAY ignore the Range header". 3247 // So if a server does that and sends 200 instead of 206 that we 3248 // expect, notify our caller. 3249 // However, if we wanted to start from the beginning, let it go through 3250 if (LoadResuming() && mStartPos != 0) { 3251 LOG(("Server ignored our Range header, cancelling [this=%p]\n", this)); 3252 Cancel(NS_ERROR_NOT_RESUMABLE); 3253 rv = CallOnStartRequest(); 3254 break; 3255 } 3256 // these can normally be cached 3257 rv = ProcessNormal(); 3258 MaybeInvalidateCacheEntryForSubsequentGet(); 3259 break; 3260 case 206: 3261 if (LoadCachedContentIsPartial()) { // an internal byte range request... 3262 auto func = [](auto* self, nsresult aRv) { 3263 return self->ContinueProcessResponseAfterPartialContent(aRv); 3264 }; 3265 rv = ProcessPartialContent(func); 3266 // Directly call ContinueProcessResponseAfterPartialContent if channel 3267 // is not suspended or ProcessPartialContent throws. 3268 if (!mSuspendCount || NS_FAILED(rv)) { 3269 return ContinueProcessResponseAfterPartialContent(rv); 3270 } 3271 return NS_OK; 3272 } else { 3273 mCacheInputStream.CloseAndRelease(); 3274 rv = ProcessNormal(); 3275 } 3276 break; 3277 case 301: 3278 case 302: 3279 case 303: 3280 case 307: 3281 case 308: 3282 #if 0 3283 case 305: // disabled as a security measure (see bug 187996). 3284 #endif 3285 // RFC 9110: 303 responses are cacheable, but only with explicit 3286 // freshness information (max-age or Expires). Without these directives, 3287 // 303 should not be cached. 3288 if (httpStatus == 303) { 3289 uint32_t freshnessLifetime = 0; 3290 bool hasFreshness = 3291 (NS_SUCCEEDED( 3292 mResponseHead->ComputeFreshnessLifetime(&freshnessLifetime)) && 3293 freshnessLifetime > 0) || 3294 mResponseHead->HasHeader(nsHttp::Expires); 3295 if (mResponseHead->NoStore() || mResponseHead->NoCache() || 3296 !hasFreshness) { 3297 CloseCacheEntry(false); 3298 } 3299 } 3300 // don't store the response body for redirects 3301 MaybeInvalidateCacheEntryForSubsequentGet(); 3302 PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse4); 3303 rv = AsyncProcessRedirection(httpStatus); 3304 if (NS_FAILED(rv)) { 3305 PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessResponse4); 3306 LOG(("AsyncProcessRedirection failed [rv=%" PRIx32 "]\n", 3307 static_cast<uint32_t>(rv))); 3308 // don't cache failed redirect responses. 3309 if (mCacheEntry) mCacheEntry->AsyncDoom(nullptr); 3310 if (DoNotRender3xxBody(rv)) { 3311 mStatus = rv; 3312 DoNotifyListener(); 3313 } else { 3314 rv = ContinueProcessResponse4(rv); 3315 } 3316 } 3317 break; 3318 case 304: 3319 if (!ShouldBypassProcessNotModified()) { 3320 auto func = [](auto* self, nsresult aRv) { 3321 return self->ContinueProcessResponseAfterNotModified(aRv); 3322 }; 3323 rv = ProcessNotModified(func); 3324 // Directly call ContinueProcessResponseAfterNotModified if channel 3325 // is not suspended or ProcessNotModified throws. 3326 if (!mSuspendCount || NS_FAILED(rv)) { 3327 return ContinueProcessResponseAfterNotModified(rv); 3328 } 3329 return NS_OK; 3330 } 3331 3332 // Don't cache uninformative 304 3333 if (LoadCustomConditionalRequest()) { 3334 CloseCacheEntry(false); 3335 } 3336 3337 if (ShouldBypassProcessNotModified() || NS_FAILED(rv)) { 3338 rv = ProcessNormal(); 3339 } 3340 break; 3341 case 401: 3342 case 407: 3343 if (MOZ_UNLIKELY(httpStatus == 407 && transactionRestarted)) { 3344 // The transaction has been internally restarted. We want to 3345 // authenticate to the proxy again, so reuse either cached credentials 3346 // or use default credentials for NTLM/Negotiate. This prevents 3347 // considering the previously used credentials as invalid. 3348 MOZ_DIAGNOSTIC_ASSERT(mAuthProvider); 3349 if (!mAuthProvider) { 3350 mStatus = NS_ERROR_UNEXPECTED; 3351 return ProcessNormal(); 3352 } 3353 mAuthProvider->ClearProxyIdent(); 3354 } 3355 if (!LoadAuthRedirectedChannel() && 3356 MOZ_UNLIKELY(LoadCustomAuthHeader()) && httpStatus == 401) { 3357 // When a custom auth header fails, we don't want to try 3358 // any cached credentials, nor we want to ask the user. 3359 // It's up to the consumer to re-try w/o setting a custom 3360 // auth header if cached credentials should be attempted. 3361 rv = NS_ERROR_FAILURE; 3362 } else if (httpStatus == 401 && 3363 StaticPrefs:: 3364 network_auth_supress_auth_prompt_for_XFO_failures() && 3365 !nsContentSecurityUtils::CheckCSPFrameAncestorAndXFO(this)) { 3366 // CSP Frame Ancestor and X-Frame-Options check has failed 3367 // Do not prompt http auth - Bug 1629307 3368 rv = NS_ERROR_FAILURE; 3369 } else { 3370 MOZ_DIAGNOSTIC_ASSERT(mAuthProvider); 3371 rv = mAuthProvider 3372 ? mAuthProvider->ProcessAuthentication( 3373 httpStatus, mConnectionInfo->EndToEndSSL() && 3374 mTransaction && 3375 mTransaction->ProxyConnectFailed()) 3376 : NS_ERROR_UNEXPECTED; 3377 } 3378 if (rv == NS_ERROR_IN_PROGRESS) { 3379 // authentication prompt has been invoked and result 3380 // is expected asynchronously 3381 mIsAuthChannel = true; 3382 mAuthRetryPending = true; 3383 if (httpStatus == 407 || 3384 (mTransaction && mTransaction->ProxyConnectFailed())) { 3385 StoreProxyAuthPending(true); 3386 } 3387 3388 // suspend the transaction pump to stop receiving the 3389 // unauthenticated content data. We will throw that data 3390 // away when user provides credentials or resume the pump 3391 // when user refuses to authenticate. 3392 LOG( 3393 ("Suspending the transaction, asynchronously prompting for " 3394 "credentials")); 3395 Suspend(); 3396 3397 #ifdef DEBUG 3398 // This is for test purposes only. See bug 1683176 for details. 3399 gHttpHandler->OnTransactionSuspendedDueToAuthentication(this); 3400 #endif 3401 rv = NS_OK; 3402 } else if (NS_FAILED(rv)) { 3403 LOG(("ProcessAuthentication failed [rv=%" PRIx32 "]\n", 3404 static_cast<uint32_t>(rv))); 3405 if (mTransaction && mTransaction->ProxyConnectFailed()) { 3406 return ProcessFailedProxyConnect(httpStatus); 3407 } 3408 if (rv == NS_ERROR_BASIC_HTTP_AUTH_DISABLED) { 3409 mStatus = rv; 3410 } 3411 rv = ProcessNormal(); 3412 } else { 3413 mIsAuthChannel = true; 3414 mAuthRetryPending = true; 3415 if (StaticPrefs::network_auth_use_redirect_for_retries()) { 3416 if (NS_SUCCEEDED(RedirectToNewChannelForAuthRetry())) { 3417 return NS_OK; 3418 } 3419 mAuthRetryPending = false; 3420 rv = ProcessNormal(); 3421 } 3422 } 3423 break; 3424 3425 case 408: 3426 case 425: 3427 case 429: 3428 // Do not cache 408, 425 and 429. 3429 CloseCacheEntry(false); 3430 [[fallthrough]]; // process normally 3431 default: 3432 rv = ProcessNormal(); 3433 MaybeInvalidateCacheEntryForSubsequentGet(); 3434 break; 3435 } 3436 3437 UpdateCacheDisposition(false, false); 3438 return rv; 3439 } 3440 3441 nsresult nsHttpChannel::ContinueProcessResponseAfterPartialContent( 3442 nsresult aRv) { 3443 LOG( 3444 ("nsHttpChannel::ContinueProcessResponseAfterPartialContent " 3445 "[this=%p, rv=%" PRIx32 "]", 3446 this, static_cast<uint32_t>(aRv))); 3447 3448 UpdateCacheDisposition(false, NS_SUCCEEDED(aRv)); 3449 return aRv; 3450 } 3451 3452 nsresult nsHttpChannel::ContinueProcessResponseAfterNotModified(nsresult aRv) { 3453 LOG( 3454 ("nsHttpChannel::ContinueProcessResponseAfterNotModified " 3455 "[this=%p, rv=%" PRIx32 "]", 3456 this, static_cast<uint32_t>(aRv))); 3457 3458 if (NS_SUCCEEDED(aRv)) { 3459 StoreTransactionReplaced(true); 3460 UpdateCacheDisposition(true, false); 3461 return NS_OK; 3462 } 3463 3464 LOG(("ProcessNotModified failed [rv=%" PRIx32 "]\n", 3465 static_cast<uint32_t>(aRv))); 3466 3467 // We cannot read from the cache entry, it might be in an 3468 // incosistent state. Doom it and redirect the channel 3469 // to the same URI to reload from the network. 3470 mCacheInputStream.CloseAndRelease(); 3471 if (mCacheEntry) { 3472 mCacheEntry->AsyncDoom(nullptr); 3473 mCacheEntry = nullptr; 3474 } 3475 3476 nsresult rv = 3477 StartRedirectChannelToURI(mURI, nsIChannelEventSink::REDIRECT_INTERNAL); 3478 if (NS_SUCCEEDED(rv)) { 3479 return NS_OK; 3480 } 3481 3482 // Don't cache uninformative 304 3483 if (LoadCustomConditionalRequest()) { 3484 CloseCacheEntry(false); 3485 } 3486 3487 if (ShouldBypassProcessNotModified() || NS_FAILED(rv)) { 3488 rv = ProcessNormal(); 3489 } 3490 3491 UpdateCacheDisposition(false, false); 3492 return rv; 3493 } 3494 3495 static void ReportHttpResponseVersion(HttpVersion version) { 3496 if (Telemetry::CanRecordPrereleaseData()) { 3497 glean::http::response_version.AccumulateSingleSample( 3498 static_cast<uint32_t>(version)); 3499 } 3500 mozilla::glean::networking::http_response_version 3501 .Get(HttpVersionToTelemetryLabel(version)) 3502 .Add(1); 3503 } 3504 3505 void nsHttpChannel::UpdateCacheDisposition(bool aSuccessfulReval, 3506 bool aPartialContentUsed) { 3507 if (mRaceDelay && !mRaceCacheWithNetwork && 3508 (LoadCachedContentIsPartial() || mDidReval)) { 3509 if (aSuccessfulReval || aPartialContentUsed) { 3510 glean::network::race_cache_validation 3511 .EnumGet(glean::network::RaceCacheValidationLabel::eCachedcontentused) 3512 .Add(); 3513 } else { 3514 glean::network::race_cache_validation 3515 .EnumGet( 3516 glean::network::RaceCacheValidationLabel::eCachedcontentnotused) 3517 .Add(); 3518 } 3519 } 3520 3521 PROFILER_MARKER_TEXT( 3522 "CacheDisposition", NETWORK, {}, 3523 nsPrintfCString( 3524 !mDidReval ? "Missed" 3525 : (aSuccessfulReval ? "HitViaReval" : "MissedViaReval"))); 3526 CacheDisposition cacheDisposition; 3527 if (!mDidReval) { 3528 cacheDisposition = kCacheMissed; 3529 } else if (aSuccessfulReval) { 3530 cacheDisposition = kCacheHitViaReval; 3531 } else { 3532 cacheDisposition = kCacheMissedViaReval; 3533 } 3534 mCacheDisposition = cacheDisposition; 3535 3536 if (Telemetry::CanRecordPrereleaseData()) { 3537 AccumulateCacheHitTelemetry(cacheDisposition, this); 3538 } 3539 3540 ReportHttpResponseVersion(mResponseHead->Version()); 3541 } 3542 3543 // Only used for redirects (3XX responses) 3544 nsresult nsHttpChannel::ContinueProcessResponse4(nsresult rv) { 3545 bool doNotRender = DoNotRender3xxBody(rv); 3546 3547 if (rv == NS_ERROR_DOM_BAD_URI && mRedirectURI && 3548 !net::SchemeIsHttpOrHttps(mRedirectURI)) { 3549 // This was a blocked attempt to redirect and subvert the system by 3550 // redirecting to another protocol (perhaps javascript:) 3551 // In that case we want to throw an error instead of displaying the 3552 // non-redirected response body. 3553 LOG(("ContinueProcessResponse4 detected rejected Non-HTTP Redirection")); 3554 doNotRender = true; 3555 rv = NS_ERROR_CORRUPTED_CONTENT; 3556 } 3557 3558 if (doNotRender) { 3559 Cancel(rv); 3560 DoNotifyListener(); 3561 return rv; 3562 } 3563 3564 if (NS_SUCCEEDED(rv)) { 3565 UpdateInhibitPersistentCachingFlag(); 3566 3567 MaybeCreateCacheEntryWhenRCWN(); 3568 3569 rv = InitCacheEntry(); 3570 if (NS_FAILED(rv)) { 3571 LOG( 3572 ("ContinueProcessResponse4 " 3573 "failed to init cache entry [rv=%x]\n", 3574 static_cast<uint32_t>(rv))); 3575 } 3576 CloseCacheEntry(false); 3577 return NS_OK; 3578 } 3579 3580 LOG(("ContinueProcessResponse4 got failure result [rv=%" PRIx32 "]\n", 3581 static_cast<uint32_t>(rv))); 3582 if (mTransaction && mTransaction->ProxyConnectFailed()) { 3583 return ProcessFailedProxyConnect(mRedirectType); 3584 } 3585 return ProcessNormal(); 3586 } 3587 3588 nsresult nsHttpChannel::ProcessNormal() { 3589 LOG(("nsHttpChannel::ProcessNormal [this=%p]\n", this)); 3590 3591 return ContinueProcessNormal(NS_OK); 3592 } 3593 3594 nsresult nsHttpChannel::ContinueProcessNormal(nsresult rv) { 3595 LOG(("nsHttpChannel::ContinueProcessNormal [this=%p]", this)); 3596 3597 if (NS_FAILED(rv)) { 3598 // Fill the failure status here, we have failed to fall back, thus we 3599 // have to report our status as failed. 3600 mStatus = rv; 3601 DoNotifyListener(); 3602 return rv; 3603 } 3604 3605 rv = ProcessCrossOriginSecurityHeaders(); 3606 if (NS_FAILED(rv)) { 3607 mStatus = rv; 3608 HandleAsyncAbort(); 3609 return rv; 3610 } 3611 3612 // if we're here, then any byte-range requests failed to result in a partial 3613 // response. we must clear this flag to prevent BufferPartialContent from 3614 // being called inside our OnDataAvailable (see bug 136678). 3615 StoreCachedContentIsPartial(false); 3616 3617 UpdateInhibitPersistentCachingFlag(); 3618 3619 MaybeCreateCacheEntryWhenRCWN(); 3620 3621 // this must be called before firing OnStartRequest, since http clients, 3622 // such as imagelib, expect our cache entry to already have the correct 3623 // expiration time (bug 87710). 3624 if (mCacheEntry) { 3625 rv = InitCacheEntry(); 3626 if (NS_FAILED(rv)) CloseCacheEntry(true); 3627 } 3628 3629 // We may need to install the cache listener before CallonStartRequest, 3630 // since InstallCacheListener can modify the Content-Encoding to remove 3631 // dcb/dcz (and perhaps others), and CallOnStartRequest() copies 3632 // Content-Encoding to send to the content process. If this doesn't 3633 // install a listener (because this isn't a dictionary or 3634 // dictionary-compressed), call it after CallOnStartRequest so that we 3635 // save the compressed data in the cache, and run the decompressor in the 3636 // content process. 3637 bool isDictionaryCompressed = false; 3638 nsAutoCString contentEncoding; 3639 (void)mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding); 3640 // Note: doesn't handle dcb, gzip or gzip, dcb (etc) 3641 if (contentEncoding.Equals("dcb") || contentEncoding.Equals("dcz")) { 3642 isDictionaryCompressed = true; 3643 } 3644 3645 if (mCacheEntry && !LoadCacheEntryIsReadOnly()) { 3646 // XXX We may want to consider recompressing any dcb/dcz files to save space 3647 // and improve hitrate. Downside is CPU use, complexity and perhaps delay, 3648 // maybe. 3649 nsAutoCString dictionary; 3650 if (StaticPrefs::network_http_dictionaries_enable() && IsHTTPS()) { 3651 (void)mResponseHead->GetHeader(nsHttp::Use_As_Dictionary, dictionary); 3652 if (!dictionary.IsEmpty()) { 3653 if (!ParseDictionary(mCacheEntry, mResponseHead.get(), true)) { 3654 LOG_DICTIONARIES(("Failed to parse use-as-dictionary")); 3655 } else { 3656 MOZ_ASSERT(mDictSaving); 3657 3658 // We need to record the hash as we save it 3659 mCacheEntry->SetDictionary(mDictSaving); 3660 } 3661 } 3662 } 3663 3664 if (isDictionaryCompressed || mDictSaving) { 3665 LOG(("Decompressing before saving into cache [channel=%p]", this)); 3666 rv = DoInstallCacheListener(isDictionaryCompressed, &dictionary, 0); 3667 } 3668 } else { 3669 if (isDictionaryCompressed) { 3670 // We still need to decompress in the parent if it's dcb or dcz even if 3671 // not saving to the cache 3672 LOG_DICTIONARIES( 3673 ("Removing Content-Encoding %s for %p", contentEncoding.get(), this)); 3674 nsCOMPtr<nsIStreamListener> listener; 3675 // otherwise we won't convert in the parent process 3676 // XXX may be redundant, but safe 3677 SetApplyConversion(true); 3678 rv = DoApplyContentConversionsInternal( 3679 mListener, getter_AddRefs(listener), true, nullptr); 3680 if (NS_FAILED(rv)) { 3681 return rv; 3682 } 3683 if (listener) { 3684 LOG_DICTIONARIES(("Installed nsHTTPCompressConv %p without cache tee", 3685 listener.get())); 3686 mListener = listener; 3687 mCompressListener = listener; 3688 StoreHasAppliedConversion(true); 3689 } else { 3690 LOG_DICTIONARIES(("Didn't install decompressor without cache tee")); 3691 } 3692 } 3693 } // else we'll call InstallCacheListener after CallOnStartRequest 3694 3695 // Check that the server sent us what we were asking for 3696 if (LoadResuming()) { 3697 // Create an entity id from the response 3698 nsAutoCString id; 3699 rv = GetEntityID(id); 3700 if (NS_FAILED(rv)) { 3701 // If creating an entity id is not possible -> error 3702 Cancel(NS_ERROR_NOT_RESUMABLE); 3703 } else if (mResponseHead->Status() != 206 && 3704 mResponseHead->Status() != 200) { 3705 // Probably 404 Not Found, 412 Precondition Failed or 3706 // 416 Invalid Range -> error 3707 LOG(("Unexpected response status while resuming, aborting [this=%p]\n", 3708 this)); 3709 Cancel(NS_ERROR_ENTITY_CHANGED); 3710 } 3711 // If we were passed an entity id, verify it's equal to the server's 3712 else if (!mEntityID.IsEmpty()) { 3713 if (!mEntityID.Equals(id)) { 3714 LOG(("Entity mismatch, expected '%s', got '%s', aborting [this=%p]", 3715 mEntityID.get(), id.get(), this)); 3716 Cancel(NS_ERROR_ENTITY_CHANGED); 3717 } 3718 } 3719 } 3720 3721 // If we don't have the entire dictionary yet, Suspend() the channel 3722 // until the dictionary is in-memory. 3723 if (mDictDecompress && mUsingDictionary && mShouldSuspendForDictionary && 3724 !mDictDecompress->DictionaryReady()) { 3725 LOG( 3726 ("nsHttpChannel::ContinueProcessNormal [this=%p] Suspending the " 3727 "transaction, waiting for dictionary", 3728 this)); 3729 Suspend(); 3730 mSuspendedForDictionary = true; 3731 } 3732 3733 rv = CallOnStartRequest(); 3734 if (NS_FAILED(rv)) return rv; 3735 3736 // If we didn't install cache listeners to decompress above 3737 // install the cache listener now (so they'll get compressed data) 3738 if (!isDictionaryCompressed && !mDictSaving) { 3739 // install cache listener if we still have a cache entry open 3740 if (mCacheEntry && !LoadCacheEntryIsReadOnly()) { 3741 rv = InstallCacheListener(); 3742 if (NS_FAILED(rv)) return rv; 3743 } 3744 } 3745 return NS_OK; 3746 } 3747 3748 nsresult nsHttpChannel::PromptTempRedirect() { 3749 if (!gHttpHandler->PromptTempRedirect()) { 3750 return NS_OK; 3751 } 3752 nsresult rv; 3753 nsCOMPtr<nsIStringBundleService> bundleService; 3754 bundleService = mozilla::components::StringBundle::Service(&rv); 3755 if (NS_FAILED(rv)) return rv; 3756 3757 nsCOMPtr<nsIStringBundle> stringBundle; 3758 rv = 3759 bundleService->CreateBundle(NECKO_MSGS_URL, getter_AddRefs(stringBundle)); 3760 if (NS_FAILED(rv)) return rv; 3761 3762 nsAutoString messageString; 3763 rv = stringBundle->GetStringFromName("RepostFormData", messageString); 3764 if (NS_SUCCEEDED(rv)) { 3765 bool repost = false; 3766 3767 nsCOMPtr<nsIPrompt> prompt; 3768 GetCallback(prompt); 3769 if (!prompt) return NS_ERROR_NO_INTERFACE; 3770 3771 prompt->Confirm(nullptr, messageString.get(), &repost); 3772 if (!repost) return NS_ERROR_FAILURE; 3773 } 3774 3775 return rv; 3776 } 3777 3778 nsresult nsHttpChannel::ProxyFailover() { 3779 LOG(("nsHttpChannel::ProxyFailover [this=%p]\n", this)); 3780 3781 nsresult rv; 3782 3783 nsCOMPtr<nsIProtocolProxyService> pps; 3784 pps = mozilla::components::ProtocolProxy::Service(&rv); 3785 if (NS_FAILED(rv)) return rv; 3786 3787 nsCOMPtr<nsIProxyInfo> pi; 3788 rv = pps->GetFailoverForProxy(mConnectionInfo->ProxyInfo(), mURI, mStatus, 3789 getter_AddRefs(pi)); 3790 #ifdef MOZ_PROXY_DIRECT_FAILOVER 3791 if (NS_FAILED(rv)) { 3792 if (!StaticPrefs::network_proxy_failover_direct()) { 3793 return rv; 3794 } 3795 // If this request used a failed proxy and there is no failover available, 3796 // fallback to DIRECT connections for conservative requests. 3797 if (LoadBeConservative()) { 3798 rv = pps->NewProxyInfo("direct"_ns, ""_ns, 0, ""_ns, ""_ns, 0, UINT32_MAX, 3799 nullptr, getter_AddRefs(pi)); 3800 } 3801 #endif 3802 if (NS_FAILED(rv)) { 3803 return rv; 3804 } 3805 #ifdef MOZ_PROXY_DIRECT_FAILOVER 3806 } 3807 #endif 3808 3809 // XXXbz so where does this codepath remove us from the loadgroup, 3810 // exactly? 3811 return AsyncDoReplaceWithProxy(pi); 3812 } 3813 3814 void nsHttpChannel::SetHTTPSSVCRecord( 3815 already_AddRefed<nsIDNSHTTPSSVCRecord>&& aRecord) { 3816 LOG(("nsHttpChannel::SetHTTPSSVCRecord [this=%p]\n", this)); 3817 nsCOMPtr<nsIDNSHTTPSSVCRecord> record = aRecord; 3818 MOZ_ASSERT(!mHTTPSSVCRecord); 3819 mHTTPSSVCRecord.emplace(std::move(record)); 3820 } 3821 3822 void nsHttpChannel::HandleAsyncRedirectChannelToHttps() { 3823 MOZ_ASSERT(!mCallOnResume, "How did that happen?"); 3824 3825 if (mSuspendCount) { 3826 LOG(("Waiting until resume to do async redirect to https [this=%p]\n", 3827 this)); 3828 mCallOnResume = [](nsHttpChannel* self) { 3829 self->HandleAsyncRedirectChannelToHttps(); 3830 return NS_OK; 3831 }; 3832 return; 3833 } 3834 3835 nsresult rv = StartRedirectChannelToHttps(); 3836 if (NS_FAILED(rv)) { 3837 rv = ContinueAsyncRedirectChannelToURI(rv); 3838 if (NS_FAILED(rv)) { 3839 LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n", 3840 static_cast<uint32_t>(rv), this)); 3841 } 3842 } 3843 } 3844 3845 nsresult nsHttpChannel::StartRedirectChannelToHttps() { 3846 LOG(("nsHttpChannel::HandleAsyncRedirectChannelToHttps() [STS]\n")); 3847 3848 nsCOMPtr<nsIURI> upgradedURI; 3849 nsresult rv = NS_GetSecureUpgradedURI(mURI, getter_AddRefs(upgradedURI)); 3850 NS_ENSURE_SUCCESS(rv, rv); 3851 3852 return StartRedirectChannelToURI( 3853 upgradedURI, nsIChannelEventSink::REDIRECT_PERMANENT | 3854 nsIChannelEventSink::REDIRECT_STS_UPGRADE); 3855 } 3856 3857 void nsHttpChannel::HandleAsyncAPIRedirect() { 3858 MOZ_ASSERT(!mCallOnResume, "How did that happen?"); 3859 MOZ_ASSERT(mAPIRedirectTo, "How did that happen?"); 3860 MOZ_ASSERT(mAPIRedirectTo->first(), "How did that happen?"); 3861 3862 if (mSuspendCount) { 3863 LOG(("Waiting until resume to do async API redirect [this=%p]\n", this)); 3864 mCallOnResume = [](nsHttpChannel* self) { 3865 self->HandleAsyncAPIRedirect(); 3866 return NS_OK; 3867 }; 3868 return; 3869 } 3870 3871 nsresult rv = StartRedirectChannelToURI( 3872 mAPIRedirectTo->first(), 3873 mAPIRedirectTo->second() ? nsIChannelEventSink::REDIRECT_PERMANENT | 3874 nsIChannelEventSink::REDIRECT_TRANSPARENT 3875 : nsIChannelEventSink::REDIRECT_PERMANENT); 3876 if (NS_FAILED(rv)) { 3877 rv = ContinueAsyncRedirectChannelToURI(rv); 3878 if (NS_FAILED(rv)) { 3879 LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n", 3880 static_cast<uint32_t>(rv), this)); 3881 } 3882 } 3883 } 3884 3885 void nsHttpChannel::HandleAsyncRedirectToUnstrippedURI() { 3886 MOZ_ASSERT(!mCallOnResume, "How did that happen?"); 3887 3888 if (mSuspendCount) { 3889 LOG( 3890 ("Waiting until resume to do async redirect to unstripped URI " 3891 "[this=%p]\n", 3892 this)); 3893 mCallOnResume = [](nsHttpChannel* self) { 3894 self->HandleAsyncRedirectToUnstrippedURI(); 3895 return NS_OK; 3896 }; 3897 return; 3898 } 3899 3900 nsCOMPtr<nsIURI> unstrippedURI; 3901 mLoadInfo->GetUnstrippedURI(getter_AddRefs(unstrippedURI)); 3902 3903 // Clear the unstripped URI from the loadInfo before starting redirect in case 3904 // endless redirect. 3905 mLoadInfo->SetUnstrippedURI(nullptr); 3906 3907 nsresult rv = StartRedirectChannelToURI( 3908 unstrippedURI, nsIChannelEventSink::REDIRECT_PERMANENT); 3909 3910 if (NS_FAILED(rv)) { 3911 rv = ContinueAsyncRedirectChannelToURI(rv); 3912 if (NS_FAILED(rv)) { 3913 LOG(("ContinueAsyncRedirectChannelToURI failed (%08x) [this=%p]\n", 3914 static_cast<uint32_t>(rv), this)); 3915 } 3916 } 3917 } 3918 nsresult nsHttpChannel::RedirectToNewChannelForAuthRetry() { 3919 LOG(("nsHttpChannel::RedirectToNewChannelForAuthRetry %p", this)); 3920 nsresult rv = NS_OK; 3921 3922 nsCOMPtr<nsILoadInfo> redirectLoadInfo = CloneLoadInfoForRedirect( 3923 mURI, nsIChannelEventSink::REDIRECT_INTERNAL | 3924 nsIChannelEventSink::REDIRECT_AUTH_RETRY); 3925 3926 nsCOMPtr<nsIIOService> ioService; 3927 3928 rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); 3929 NS_ENSURE_SUCCESS(rv, rv); 3930 3931 nsCOMPtr<nsIChannel> newChannel; 3932 rv = gHttpHandler->NewProxiedChannel(mURI, mProxyInfo, mProxyResolveFlags, 3933 mProxyURI, mLoadInfo, 3934 getter_AddRefs(newChannel)); 3935 3936 NS_ENSURE_SUCCESS(rv, rv); 3937 3938 rv = SetupReplacementChannel(mURI, newChannel, true, 3939 nsIChannelEventSink::REDIRECT_INTERNAL | 3940 nsIChannelEventSink::REDIRECT_AUTH_RETRY); 3941 NS_ENSURE_SUCCESS(rv, rv); 3942 3943 // rewind the upload stream 3944 if (mUploadStream) { 3945 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream); 3946 nsresult rv = NS_ERROR_NO_INTERFACE; 3947 if (seekable) { 3948 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); 3949 } 3950 3951 // This should not normally happen, but it's possible that big memory 3952 // blobs originating in the other process can't be rewinded. 3953 // In that case we just fail the request, otherwise the content length 3954 // will not match and this load will never complete. 3955 NS_ENSURE_SUCCESS(rv, rv); 3956 } 3957 3958 RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(newChannel); 3959 3960 MOZ_ASSERT(mAuthProvider); 3961 httpChannelImpl->mAuthProvider = std::move(mAuthProvider); 3962 3963 httpChannelImpl->mProxyInfo = mProxyInfo; 3964 3965 if ((mCaps & NS_HTTP_STICKY_CONNECTION) || 3966 mTransaction->HasStickyConnection()) { 3967 mConnectionInfo = mTransaction->GetConnInfo(); 3968 3969 httpChannelImpl->mTransactionSticky = mTransaction; 3970 3971 if (mTransaction->Http2Disabled()) { 3972 httpChannelImpl->mCaps |= NS_HTTP_DISALLOW_SPDY; 3973 } 3974 if (mTransaction->Http3Disabled()) { 3975 httpChannelImpl->mCaps |= NS_HTTP_DISALLOW_HTTP3; 3976 } 3977 } 3978 // always set sticky connection flag 3979 httpChannelImpl->mCaps |= NS_HTTP_STICKY_CONNECTION; 3980 3981 if (LoadAuthConnectionRestartable()) { 3982 httpChannelImpl->mCaps |= NS_HTTP_CONNECTION_RESTARTABLE; 3983 } else { 3984 httpChannelImpl->mCaps &= ~NS_HTTP_CONNECTION_RESTARTABLE; 3985 } 3986 3987 MOZ_ASSERT(mConnectionInfo); 3988 httpChannelImpl->mConnectionInfo = mConnectionInfo->Clone(); 3989 3990 // we need to store the state to skip unnecessary checks in the new channel 3991 httpChannelImpl->StoreAuthRedirectedChannel(true); 3992 3993 // We must copy proxy and auth header to the new channel. 3994 // Although the new channel can populate auth headers from auth cache, we 3995 // would still like to use the auth headers generated in this channel. The 3996 // main reason for doing this is that certain connection-based/stateful auth 3997 // schemes like NTLM will fail when we try generate the credentials more than 3998 // the number of times the server has presented us the challenge due to the 3999 // usage of nonce in generating the credentials Copying the auth header will 4000 // bypass generation of the credentials 4001 nsAutoCString authVal; 4002 if (NS_SUCCEEDED(GetRequestHeader("Proxy-Authorization"_ns, authVal))) { 4003 httpChannelImpl->SetRequestHeader("Proxy-Authorization"_ns, authVal, false); 4004 } 4005 if (NS_SUCCEEDED(GetRequestHeader("Authorization"_ns, authVal))) { 4006 httpChannelImpl->SetRequestHeader("Authorization"_ns, authVal, false); 4007 } 4008 4009 httpChannelImpl->SetBlockAuthPrompt(LoadBlockAuthPrompt()); 4010 mRedirectChannel = newChannel; 4011 4012 rv = gHttpHandler->AsyncOnChannelRedirect( 4013 this, newChannel, 4014 nsIChannelEventSink::REDIRECT_INTERNAL | 4015 nsIChannelEventSink::REDIRECT_AUTH_RETRY); 4016 4017 if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback(); 4018 4019 // redirected channel will be opened after we receive the OnStopRequest 4020 4021 if (NS_FAILED(rv)) { 4022 AutoRedirectVetoNotifier notifier(this, rv); 4023 mRedirectChannel = nullptr; 4024 } 4025 4026 return rv; 4027 } 4028 4029 nsresult nsHttpChannel::StartRedirectChannelToURI(nsIURI* upgradedURI, 4030 uint32_t flags) { 4031 return StartRedirectChannelToURI(upgradedURI, flags, [](nsIChannel*) {}); 4032 } 4033 4034 nsresult nsHttpChannel::StartRedirectChannelToURI( 4035 nsIURI* upgradedURI, uint32_t flags, 4036 std::function<void(nsIChannel*)>&& aCallback) { 4037 nsresult rv = NS_OK; 4038 LOG(("nsHttpChannel::StartRedirectChannelToURI()\n")); 4039 4040 nsCOMPtr<nsIChannel> newChannel; 4041 nsCOMPtr<nsILoadInfo> redirectLoadInfo = 4042 CloneLoadInfoForRedirect(upgradedURI, flags); 4043 4044 nsCOMPtr<nsIIOService> ioService; 4045 rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); 4046 NS_ENSURE_SUCCESS(rv, rv); 4047 4048 rv = NS_NewChannelInternal(getter_AddRefs(newChannel), upgradedURI, 4049 redirectLoadInfo, 4050 nullptr, // PerformanceStorage 4051 nullptr, // aLoadGroup 4052 nullptr, // aCallbacks 4053 nsIRequest::LOAD_NORMAL, ioService); 4054 NS_ENSURE_SUCCESS(rv, rv); 4055 4056 rv = SetupReplacementChannel(upgradedURI, newChannel, true, flags); 4057 NS_ENSURE_SUCCESS(rv, rv); 4058 4059 if (mHTTPSSVCRecord) { 4060 RefPtr<nsHttpChannel> httpChan = do_QueryObject(newChannel); 4061 nsCOMPtr<nsIDNSHTTPSSVCRecord> rec = mHTTPSSVCRecord.ref(); 4062 if (httpChan && rec) { 4063 httpChan->SetHTTPSSVCRecord(rec.forget()); 4064 } 4065 } 4066 4067 // Inform consumers about this fake redirect 4068 mRedirectChannel = newChannel; 4069 4070 aCallback(newChannel); 4071 4072 PushRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI); 4073 rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags); 4074 4075 if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback(); 4076 4077 if (NS_FAILED(rv)) { 4078 AutoRedirectVetoNotifier notifier(this, rv); 4079 4080 /* Remove the async call to ContinueAsyncRedirectChannelToURI(). 4081 * It is called directly by our callers upon return (to clean up 4082 * the failed redirect). */ 4083 PopRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI); 4084 } 4085 4086 return rv; 4087 } 4088 4089 nsresult nsHttpChannel::ContinueAsyncRedirectChannelToURI(nsresult rv) { 4090 LOG(("nsHttpChannel::ContinueAsyncRedirectChannelToURI [this=%p]", this)); 4091 4092 // Since we handle mAPIRedirectTo uri also after on-examine-response handler 4093 // rather drop it here to avoid any redirect loops, even just hypothetical. 4094 mAPIRedirectTo = Nothing(); 4095 4096 if (NS_SUCCEEDED(rv)) { 4097 rv = OpenRedirectChannel(rv); 4098 } 4099 4100 if (NS_FAILED(rv)) { 4101 // Cancel the channel here, the update to https had been vetoed 4102 // but from the security reasons we have to discard the whole channel 4103 // load. 4104 Cancel(rv); 4105 } 4106 4107 if (mLoadGroup) { 4108 mLoadGroup->RemoveRequest(this, nullptr, mStatus); 4109 } 4110 4111 if (NS_FAILED(rv) && !mCachePump && !mTransactionPump) { 4112 // We have to manually notify the listener because there is not any pump 4113 // that would call our OnStart/StopRequest after resume from waiting for 4114 // the redirect callback. 4115 DoNotifyListener(); 4116 } 4117 4118 return rv; 4119 } 4120 4121 nsresult nsHttpChannel::OpenRedirectChannel(nsresult rv) { 4122 AutoRedirectVetoNotifier notifier(this, rv); 4123 4124 if (NS_FAILED(rv)) return rv; 4125 4126 if (!mRedirectChannel) { 4127 LOG(( 4128 "nsHttpChannel::OpenRedirectChannel unexpected null redirect channel")); 4129 return NS_ERROR_FAILURE; 4130 } 4131 4132 // Make sure to do this after we received redirect veto answer, 4133 // i.e. after all sinks had been notified 4134 mRedirectChannel->SetOriginalURI(mOriginalURI); 4135 4136 // open new channel 4137 rv = mRedirectChannel->AsyncOpen(mListener); 4138 4139 NS_ENSURE_SUCCESS(rv, rv); 4140 4141 mStatus = NS_BINDING_REDIRECTED; 4142 4143 notifier.RedirectSucceeded(); 4144 4145 ReleaseListeners(); 4146 4147 return NS_OK; 4148 } 4149 4150 nsresult nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi) { 4151 LOG(("nsHttpChannel::AsyncDoReplaceWithProxy [this=%p pi=%p]", this, pi)); 4152 nsresult rv; 4153 4154 nsCOMPtr<nsIChannel> newChannel; 4155 rv = gHttpHandler->NewProxiedChannel(mURI, pi, mProxyResolveFlags, mProxyURI, 4156 mLoadInfo, getter_AddRefs(newChannel)); 4157 if (NS_FAILED(rv)) return rv; 4158 4159 uint32_t flags = nsIChannelEventSink::REDIRECT_INTERNAL; 4160 4161 rv = SetupReplacementChannel(mURI, newChannel, true, flags); 4162 if (NS_FAILED(rv)) return rv; 4163 4164 // Inform consumers about this fake redirect 4165 mRedirectChannel = newChannel; 4166 4167 PushRedirectAsyncFunc(&nsHttpChannel::OpenRedirectChannel); 4168 rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, flags); 4169 4170 if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback(); 4171 4172 if (NS_FAILED(rv)) { 4173 AutoRedirectVetoNotifier notifier(this, rv); 4174 PopRedirectAsyncFunc(&nsHttpChannel::OpenRedirectChannel); 4175 } 4176 4177 return rv; 4178 } 4179 4180 nsresult nsHttpChannel::ResolveProxy() { 4181 LOG(("nsHttpChannel::ResolveProxy [this=%p]\n", this)); 4182 4183 nsresult rv; 4184 4185 nsCOMPtr<nsIProtocolProxyService> pps; 4186 pps = mozilla::components::ProtocolProxy::Service(&rv); 4187 if (NS_FAILED(rv)) return rv; 4188 4189 // using the nsIProtocolProxyService2 allows a minor performance 4190 // optimization, but if an add-on has only provided the original interface 4191 // then it is ok to use that version. 4192 nsCOMPtr<nsIProtocolProxyService2> pps2 = do_QueryInterface(pps); 4193 if (pps2) { 4194 rv = pps2->AsyncResolve2(this, mProxyResolveFlags, this, nullptr, 4195 getter_AddRefs(mProxyRequest)); 4196 } else { 4197 rv = pps->AsyncResolve(static_cast<nsIChannel*>(this), mProxyResolveFlags, 4198 this, nullptr, getter_AddRefs(mProxyRequest)); 4199 } 4200 4201 return rv; 4202 } 4203 4204 bool nsHttpChannel::ResponseWouldVary(nsICacheEntry* entry) { 4205 nsresult rv; 4206 nsAutoCString buf, metaKey; 4207 (void)mCachedResponseHead->GetHeader(nsHttp::Vary, buf); 4208 4209 constexpr auto prefix = "request-"_ns; 4210 4211 // enumerate the elements of the Vary header... 4212 for (const nsACString& token : 4213 nsCCharSeparatedTokenizer(buf, NS_HTTP_HEADER_SEP).ToRange()) { 4214 LOG( 4215 ("nsHttpChannel::ResponseWouldVary [channel=%p] " 4216 "processing %s\n", 4217 this, nsPromiseFlatCString(token).get())); 4218 // 4219 // if "*", then assume response would vary. technically speaking, 4220 // "Vary: header, *" is not permitted, but we allow it anyways. 4221 // 4222 // We hash values of cookie-headers for the following reasons: 4223 // 4224 // 1- cookies can be very large in size 4225 // 4226 // 2- cookies may contain sensitive information. (for parity with 4227 // out policy of not storing Set-cookie headers in the cache 4228 // meta data, we likewise do not want to store cookie headers 4229 // here.) 4230 // 4231 if (token.EqualsLiteral("*")) { 4232 return true; // if we encounter this, just get out of here 4233 } 4234 4235 // build cache meta data key... 4236 metaKey = prefix + token; 4237 4238 // check the last value of the given request header to see if it has 4239 // since changed. if so, then indeed the cached response is invalid. 4240 nsCString lastVal; 4241 entry->GetMetaDataElement(metaKey.get(), getter_Copies(lastVal)); 4242 LOG( 4243 ("nsHttpChannel::ResponseWouldVary [channel=%p] " 4244 "stored value = \"%s\"\n", 4245 this, lastVal.get())); 4246 4247 // Look for value of "Cookie" in the request headers 4248 nsHttpAtom atom = nsHttp::ResolveAtom(token); 4249 nsAutoCString newVal; 4250 bool hasHeader = NS_SUCCEEDED(mRequestHead.GetHeader(atom, newVal)); 4251 if (!lastVal.IsEmpty()) { 4252 // value for this header in cache, but no value in request 4253 if (!hasHeader) { 4254 return true; // yes - response would vary 4255 } 4256 4257 // If this is a cookie-header, stored metadata is not 4258 // the value itself but the hash. So we also hash the 4259 // outgoing value here in order to compare the hashes 4260 nsAutoCString hash; 4261 if (atom == nsHttp::Cookie) { 4262 rv = Hash(newVal.get(), hash); 4263 // If hash failed, be conservative (the cached hash 4264 // exists at this point) and claim response would vary 4265 if (NS_FAILED(rv)) return true; 4266 newVal = hash; 4267 4268 LOG( 4269 ("nsHttpChannel::ResponseWouldVary [this=%p] " 4270 "set-cookie value hashed to %s\n", 4271 this, newVal.get())); 4272 } 4273 4274 if (!newVal.Equals(lastVal)) { 4275 return true; // yes, response would vary 4276 } 4277 4278 } else if (hasHeader) { // old value is empty, but newVal is set 4279 return true; 4280 } 4281 } 4282 4283 return false; 4284 } 4285 4286 // Remove an entry from Vary header, if it exists 4287 void RemoveFromVary(nsHttpResponseHead* aResponseHead, 4288 const nsACString& aRemove) { 4289 nsAutoCString buf; 4290 (void)aResponseHead->GetHeader(nsHttp::Vary, buf); 4291 4292 bool remove = false; 4293 for (const nsACString& token : 4294 nsCCharSeparatedTokenizer(buf, NS_HTTP_HEADER_SEP).ToRange()) { 4295 if (token.Equals(aRemove)) { 4296 // Need to build a new string without aRemove 4297 remove = true; 4298 break; 4299 } 4300 } 4301 if (!remove) { 4302 return; 4303 } 4304 nsAutoCString newValue; 4305 for (const nsACString& token : 4306 nsCCharSeparatedTokenizer(buf, NS_HTTP_HEADER_SEP).ToRange()) { 4307 if (!token.Equals(aRemove)) { 4308 if (!newValue.IsEmpty()) { 4309 newValue += ","_ns; 4310 } 4311 newValue += token; 4312 } 4313 } 4314 LOG(("RemoveFromVary %s removed, new value -> %s", 4315 PromiseFlatCString(aRemove).get(), newValue.get())); 4316 (void)aResponseHead->SetHeaderOverride(nsHttp::Vary, newValue); 4317 } 4318 4319 // We need to have an implementation of this function just so that we can keep 4320 // all references to mCallOnResume of type nsHttpChannel: it's not OK in C++ 4321 // to set a member function ptr to a base class function. 4322 void nsHttpChannel::HandleAsyncAbort() { 4323 HttpAsyncAborter<nsHttpChannel>::HandleAsyncAbort(); 4324 } 4325 4326 //----------------------------------------------------------------------------- 4327 // nsHttpChannel <byte-range> 4328 //----------------------------------------------------------------------------- 4329 4330 bool nsHttpChannel::IsResumable(int64_t partialLen, int64_t contentLength, 4331 bool ignoreMissingPartialLen) const { 4332 bool hasContentEncoding = 4333 mCachedResponseHead->HasHeader(nsHttp::Content_Encoding); 4334 4335 nsAutoCString etag; 4336 (void)mCachedResponseHead->GetHeader(nsHttp::ETag, etag); 4337 bool hasWeakEtag = !etag.IsEmpty() && StringBeginsWith(etag, "W/"_ns); 4338 4339 return (partialLen < contentLength) && 4340 (partialLen > 0 || ignoreMissingPartialLen) && !hasContentEncoding && 4341 !hasWeakEtag && mCachedResponseHead->IsResumable() && 4342 !LoadCustomConditionalRequest() && !mCachedResponseHead->NoStore(); 4343 } 4344 4345 nsresult nsHttpChannel::MaybeSetupByteRangeRequest( 4346 int64_t partialLen, int64_t contentLength, bool ignoreMissingPartialLen) { 4347 // Be pesimistic 4348 StoreIsPartialRequest(false); 4349 4350 if (!IsResumable(partialLen, contentLength, ignoreMissingPartialLen)) { 4351 return NS_ERROR_NOT_RESUMABLE; 4352 } 4353 4354 // looks like a partial entry we can reuse; add If-Range 4355 // and Range headers. 4356 nsresult rv = SetupByteRangeRequest(partialLen); 4357 if (NS_FAILED(rv)) { 4358 // Make the request unconditional again. 4359 UntieByteRangeRequest(); 4360 } 4361 4362 return rv; 4363 } 4364 4365 nsresult nsHttpChannel::SetupByteRangeRequest(int64_t partialLen) { 4366 // cached content has been found to be partial, add necessary request 4367 // headers to complete cache entry. 4368 4369 // use strongest validator available... 4370 nsAutoCString val; 4371 (void)mCachedResponseHead->GetHeader(nsHttp::ETag, val); 4372 if (val.IsEmpty()) { 4373 (void)mCachedResponseHead->GetHeader(nsHttp::Last_Modified, val); 4374 } 4375 if (val.IsEmpty()) { 4376 // if we hit this code it means mCachedResponseHead->IsResumable() is 4377 // either broken or not being called. 4378 MOZ_ASSERT_UNREACHABLE("no cache validator"); 4379 StoreIsPartialRequest(false); 4380 return NS_ERROR_FAILURE; 4381 } 4382 4383 char buf[64]; 4384 SprintfLiteral(buf, "bytes=%" PRId64 "-", partialLen); 4385 4386 DebugOnly<nsresult> rv{}; 4387 rv = mRequestHead.SetHeader(nsHttp::Range, nsDependentCString(buf)); 4388 MOZ_ASSERT(NS_SUCCEEDED(rv)); 4389 rv = mRequestHead.SetHeader(nsHttp::If_Range, val); 4390 MOZ_ASSERT(NS_SUCCEEDED(rv)); 4391 StoreIsPartialRequest(true); 4392 4393 return NS_OK; 4394 } 4395 4396 void nsHttpChannel::UntieByteRangeRequest() { 4397 DebugOnly<nsresult> rv{}; 4398 rv = mRequestHead.ClearHeader(nsHttp::Range); 4399 MOZ_ASSERT(NS_SUCCEEDED(rv)); 4400 rv = mRequestHead.ClearHeader(nsHttp::If_Range); 4401 MOZ_ASSERT(NS_SUCCEEDED(rv)); 4402 } 4403 4404 nsresult nsHttpChannel::ProcessPartialContent( 4405 const std::function<nsresult(nsHttpChannel*, nsresult)>& 4406 aContinueProcessResponseFunc) { 4407 // ok, we've just received a 206 4408 // 4409 // we need to stream whatever data is in the cache out first, and then 4410 // pick up whatever data is on the wire, writing it into the cache. 4411 4412 LOG(("nsHttpChannel::ProcessPartialContent [this=%p]\n", this)); 4413 4414 NS_ENSURE_TRUE(mCachedResponseHead, NS_ERROR_NOT_INITIALIZED); 4415 NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_NOT_INITIALIZED); 4416 4417 // Check if the content-encoding we now got is different from the one we 4418 // got before 4419 nsAutoCString contentEncoding, cachedContentEncoding; 4420 // It is possible that there is not such headers 4421 (void)mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding); 4422 (void)mCachedResponseHead->GetHeader(nsHttp::Content_Encoding, 4423 cachedContentEncoding); 4424 if (nsCRT::strcasecmp(contentEncoding.get(), cachedContentEncoding.get()) != 4425 0) { 4426 Cancel(NS_ERROR_INVALID_CONTENT_ENCODING); 4427 return CallOnStartRequest(); 4428 } 4429 4430 nsresult rv; 4431 4432 int64_t cachedContentLength = mCachedResponseHead->ContentLength(); 4433 int64_t entitySize = mResponseHead->TotalEntitySize(); 4434 4435 nsAutoCString contentRange; 4436 (void)mResponseHead->GetHeader(nsHttp::Content_Range, contentRange); 4437 LOG( 4438 ("nsHttpChannel::ProcessPartialContent [this=%p trans=%p] " 4439 "original content-length %" PRId64 ", entity-size %" PRId64 4440 ", content-range %s\n", 4441 this, mTransaction.get(), cachedContentLength, entitySize, 4442 contentRange.get())); 4443 4444 if ((entitySize >= 0) && (cachedContentLength >= 0) && 4445 (entitySize != cachedContentLength)) { 4446 LOG( 4447 ("nsHttpChannel::ProcessPartialContent [this=%p] " 4448 "206 has different total entity size than the content length " 4449 "of the original partially cached entity.\n", 4450 this)); 4451 4452 mCacheEntry->AsyncDoom(nullptr); 4453 Cancel(NS_ERROR_CORRUPTED_CONTENT); 4454 return CallOnStartRequest(); 4455 } 4456 4457 if (LoadConcurrentCacheAccess()) { 4458 // We started to read cached data sooner than its write has been done. 4459 // But the concurrent write has not finished completely, so we had to 4460 // do a range request. Now let the content coming from the network 4461 // be presented to consumers and also stored to the cache entry. 4462 4463 rv = InstallCacheListener(mLogicalOffset); 4464 if (NS_FAILED(rv)) return rv; 4465 } else { 4466 // suspend the current transaction 4467 rv = mTransactionPump->Suspend(); 4468 if (NS_FAILED(rv)) return rv; 4469 } 4470 4471 // merge any new headers with the cached response headers 4472 mCachedResponseHead->UpdateHeaders(mResponseHead.get()); 4473 4474 // update the cached response head 4475 nsAutoCString head; 4476 mCachedResponseHead->Flatten(head, true); 4477 rv = mCacheEntry->SetMetaDataElement("response-head", head.get()); 4478 if (NS_FAILED(rv)) return rv; 4479 4480 // make the cached response be the current response 4481 mResponseHead = std::move(mCachedResponseHead); 4482 4483 UpdateInhibitPersistentCachingFlag(); 4484 4485 rv = UpdateExpirationTime(); 4486 if (NS_FAILED(rv)) return rv; 4487 4488 // notify observers interested in looking at a response that has been 4489 // merged with any cached headers (http-on-examine-merged-response). 4490 gHttpHandler->OnExamineMergedResponse(this); 4491 4492 if (LoadConcurrentCacheAccess()) { 4493 StoreCachedContentIsPartial(false); 4494 // Leave the ConcurrentCacheAccess flag set, we want to use it 4495 // to prevent duplicate OnStartRequest call on the target listener 4496 // in case this channel is canceled before it gets its OnStartRequest 4497 // from the http transaction. 4498 return rv; 4499 } 4500 4501 // Now we continue reading the network response. 4502 // the cached content is valid, although incomplete. 4503 StoreCachedContentIsValid(CachedContentValidity::Valid); 4504 return CallOrWaitForResume([aContinueProcessResponseFunc](auto* self) { 4505 nsresult rv = self->ReadFromCache(); 4506 return aContinueProcessResponseFunc(self, rv); 4507 }); 4508 } 4509 4510 nsresult nsHttpChannel::OnDoneReadingPartialCacheEntry(bool* streamDone) { 4511 nsresult rv; 4512 4513 LOG(("nsHttpChannel::OnDoneReadingPartialCacheEntry [this=%p]", this)); 4514 4515 // by default, assume we would have streamed all data or failed... 4516 *streamDone = true; 4517 4518 // setup cache listener to append to cache entry 4519 int64_t size; 4520 rv = mCacheEntry->GetDataSize(&size); 4521 if (NS_FAILED(rv)) return rv; 4522 4523 rv = InstallCacheListener(size); 4524 if (NS_FAILED(rv)) return rv; 4525 4526 // Entry is valid, do it now, after the output stream has been opened, 4527 // otherwise when done earlier, pending readers would consider the cache 4528 // entry still as partial (CacheEntry::GetDataSize would return the partial 4529 // data size) and consumers would do the conditional request again. 4530 rv = mCacheEntry->SetValid(); 4531 if (NS_FAILED(rv)) return rv; 4532 4533 // need to track the logical offset of the data being sent to our listener 4534 mLogicalOffset = size; 4535 4536 // we're now completing the cached content, so we can clear this flag. 4537 // this puts us in the state of a regular download. 4538 StoreCachedContentIsPartial(false); 4539 // The cache input stream pump is finished, we do not need it any more. 4540 // (see bug 1313923) 4541 mCachePump = nullptr; 4542 4543 // resume the transaction if it exists, otherwise the pipe contained the 4544 // remaining part of the document and we've now streamed all of the data. 4545 if (mTransactionPump) { 4546 rv = mTransactionPump->Resume(); 4547 if (NS_SUCCEEDED(rv)) *streamDone = false; 4548 } else { 4549 MOZ_ASSERT_UNREACHABLE("no transaction"); 4550 } 4551 return rv; 4552 } 4553 4554 //----------------------------------------------------------------------------- 4555 // nsHttpChannel <cache> 4556 //----------------------------------------------------------------------------- 4557 4558 bool nsHttpChannel::ShouldBypassProcessNotModified() { 4559 if (LoadCustomConditionalRequest()) { 4560 LOG(("Bypassing ProcessNotModified due to custom conditional headers")); 4561 return true; 4562 } 4563 4564 if (!mDidReval) { 4565 LOG( 4566 ("Server returned a 304 response even though we did not send a " 4567 "conditional request")); 4568 return true; 4569 } 4570 4571 return false; 4572 } 4573 4574 void nsHttpChannel::MaybeGenerateNELReport() { 4575 if (!StaticPrefs::network_http_network_error_logging_enabled()) { 4576 return; 4577 } 4578 4579 nsCOMPtr<nsINetworkErrorReport> report; 4580 if (nsCOMPtr<nsINetworkErrorLogging> nel = 4581 components::NetworkErrorLogging::Service()) { 4582 nel->GenerateNELReport(this, getter_AddRefs(report)); 4583 } 4584 4585 mReportedNEL = true; 4586 4587 // https://www.w3.org/TR/2023/WD-network-error-logging-20231005/#deliver-a-network-report 4588 // 4. Generate a network report given these parameters: 4589 // type: network-error 4590 // url 4591 // user_agent 4592 // body 4593 4594 if (!report) { 4595 return; 4596 } 4597 4598 nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager(); 4599 if (!ssm) { 4600 return; 4601 } 4602 4603 nsCOMPtr<nsIPrincipal> channelPrincipal; 4604 ssm->GetChannelResultPrincipal(this, getter_AddRefs(channelPrincipal)); 4605 if (!channelPrincipal) { 4606 return; 4607 } 4608 4609 nsAutoCString body; 4610 nsAutoString group; 4611 nsAutoString url; 4612 4613 report->GetBody(body); 4614 report->GetGroup(group); 4615 report->GetUrl(url); 4616 4617 nsAutoCString endpointURL; 4618 ReportingHeader::GetEndpointForReportIncludeSubdomains( 4619 group, channelPrincipal, /* includeSubdomains */ true, endpointURL); 4620 if (endpointURL.IsEmpty()) { 4621 return; 4622 } 4623 4624 ReportDeliver::ReportData data; 4625 data.mType = u"network-error"_ns; 4626 data.mGroupName = group; 4627 data.mURL = url; 4628 data.mFailures = 0; 4629 data.mCreationTime = TimeStamp::Now(); 4630 4631 data.mPrincipal = channelPrincipal; 4632 data.mEndpointURL = endpointURL; 4633 data.mReportBodyJSON = body; 4634 nsAutoCString userAgent; 4635 // XXX(valentin): Should this be the potentially user set value of the header 4636 // or the current value of user_agent from http handler? 4637 (void)mRequestHead.GetHeader(nsHttp::User_Agent, userAgent); 4638 data.mUserAgent = NS_ConvertUTF8toUTF16(userAgent); 4639 4640 // Enqueue the report to be delivered by the reporting API 4641 ReportDeliver::Fetch(data); 4642 } 4643 4644 nsresult nsHttpChannel::ProcessNotModified( 4645 const std::function<nsresult(nsHttpChannel*, nsresult)>& 4646 aContinueProcessResponseFunc) { 4647 nsresult rv; 4648 4649 LOG(("nsHttpChannel::ProcessNotModified [this=%p]\n", this)); 4650 4651 // Assert ShouldBypassProcessNotModified() has been checked before call to 4652 // ProcessNotModified(). 4653 MOZ_ASSERT(!ShouldBypassProcessNotModified()); 4654 4655 MOZ_ASSERT(mCachedResponseHead); 4656 MOZ_ASSERT(mCacheEntry); 4657 NS_ENSURE_TRUE(mCachedResponseHead && mCacheEntry, NS_ERROR_UNEXPECTED); 4658 4659 // If the 304 response contains a Last-Modified different than the 4660 // one in our cache that is pretty suspicious and is, in at least the 4661 // case of bug 716840, a sign of the server having previously corrupted 4662 // our cache with a bad response. Take the minor step here of just dooming 4663 // that cache entry so there is a fighting chance of getting things on the 4664 // right track. 4665 4666 nsAutoCString lastModifiedCached; 4667 nsAutoCString lastModified304; 4668 4669 rv = 4670 mCachedResponseHead->GetHeader(nsHttp::Last_Modified, lastModifiedCached); 4671 if (NS_SUCCEEDED(rv)) { 4672 rv = mResponseHead->GetHeader(nsHttp::Last_Modified, lastModified304); 4673 } 4674 4675 if (NS_SUCCEEDED(rv) && !lastModified304.Equals(lastModifiedCached)) { 4676 LOG( 4677 ("Cache Entry and 304 Last-Modified Headers Do Not Match " 4678 "[%s] and [%s]\n", 4679 lastModifiedCached.get(), lastModified304.get())); 4680 4681 mCacheEntry->AsyncDoom(nullptr); 4682 glean::http::cache_lm_inconsistent 4683 .EnumGet(glean::http::CacheLmInconsistentLabel::eTrue) 4684 .Add(); 4685 } 4686 4687 // merge any new headers with the cached response headers 4688 mCachedResponseHead->UpdateHeaders(mResponseHead.get()); 4689 4690 // update the cached response head 4691 nsAutoCString head; 4692 mCachedResponseHead->Flatten(head, true); 4693 rv = mCacheEntry->SetMetaDataElement("response-head", head.get()); 4694 if (NS_FAILED(rv)) return rv; 4695 4696 if (LoadUsedNetwork() && !mReportedNEL) { 4697 MaybeGenerateNELReport(); 4698 } 4699 4700 // make the cached response be the current response 4701 mResponseHead = std::move(mCachedResponseHead); 4702 4703 UpdateInhibitPersistentCachingFlag(); 4704 4705 rv = UpdateExpirationTime(); 4706 if (NS_FAILED(rv)) return rv; 4707 4708 rv = AddCacheEntryHeaders(mCacheEntry, false); 4709 if (NS_FAILED(rv)) return rv; 4710 4711 // notify observers interested in looking at a reponse that has been 4712 // merged with any cached headers 4713 gHttpHandler->OnExamineMergedResponse(this); 4714 4715 StoreCachedContentIsValid(CachedContentValidity::Valid); 4716 4717 // Tell other consumers the entry is OK to use 4718 rv = mCacheEntry->SetValid(); 4719 if (NS_FAILED(rv)) return rv; 4720 4721 return CallOrWaitForResume([aContinueProcessResponseFunc](auto* self) { 4722 nsresult rv = self->ReadFromCache(); 4723 return aContinueProcessResponseFunc(self, rv); 4724 }); 4725 } 4726 4727 // Determines if a request is a byte range request for a subrange, 4728 // i.e. is a byte range request, but not a 0- byte range request. 4729 static bool IsSubRangeRequest(nsHttpRequestHead& aRequestHead) { 4730 nsAutoCString byteRange; 4731 if (NS_FAILED(aRequestHead.GetHeader(nsHttp::Range, byteRange))) { 4732 return false; 4733 } 4734 4735 if (byteRange.EqualsLiteral("bytes=0-")) { 4736 #ifndef ANDROID 4737 glean::network::byte_range_request.Get("cacheable"_ns).Add(1); 4738 #endif 4739 return false; 4740 } 4741 #ifndef ANDROID 4742 glean::network::byte_range_request.Get("not_cacheable"_ns).Add(1); 4743 #endif 4744 return true; 4745 } 4746 4747 nsresult nsHttpChannel::OpenCacheEntry(bool isHttps) { 4748 // Drop this flag here 4749 StoreConcurrentCacheAccess(0); 4750 4751 LOG(("nsHttpChannel::OpenCacheEntry [this=%p]", this)); 4752 4753 // make sure we're not abusing this function 4754 MOZ_ASSERT(!mCacheEntry, "cache entry already open"); 4755 if (!mRequestHead.IsGet() && !mRequestHead.IsHead() && 4756 !mRequestHead.IsPost() && !mRequestHead.IsPatch()) { 4757 // don't use the cache for other types of requests 4758 return NS_OK; 4759 } 4760 4761 MOZ_ASSERT_IF(mRequestHead.IsPost() || mRequestHead.IsPatch(), mPostID > 0); 4762 4763 return OpenCacheEntryInternal(isHttps); 4764 } 4765 4766 #ifdef XP_WIN 4767 static mozilla::Maybe<bool> sHasSSD; 4768 #endif 4769 4770 static bool RCWNEnabled() { 4771 // State table for RCWN logic (race_with_non_ssd is Windows only): 4772 // network.http.rcwn.enabled | Device | race_with_non_ssd | Result 4773 // true | any | any | Enabled 4774 // false | SSD | any | Disabled 4775 // false | non-SSD | true | Enabled 4776 // false | non-SSD | false | Disabled 4777 4778 bool rcwnEnabled = StaticPrefs::network_http_rcwn_enabled(); 4779 #ifdef XP_WIN 4780 if (!rcwnEnabled) { 4781 if (sHasSSD.isNothing()) { 4782 bool hasSSD = true; 4783 nsCOMPtr<nsIPropertyBag2> sysInfo = 4784 mozilla::components::SystemInfo::Service(); 4785 if (NS_SUCCEEDED(sysInfo->GetPropertyAsBool(u"hasSSD"_ns, &hasSSD))) { 4786 sHasSSD = Some(hasSSD); 4787 } else { 4788 // Failed to detect, assume SSD (conservative default) 4789 sHasSSD = Some(true); 4790 } 4791 } 4792 if (sHasSSD.isSome() && !sHasSSD.value()) { 4793 // For non-SSD devices, check the non-SSD-specific preference 4794 rcwnEnabled = StaticPrefs::network_http_rcwn_race_with_non_ssd(); 4795 } 4796 } 4797 #endif 4798 return rcwnEnabled; 4799 } 4800 4801 nsresult nsHttpChannel::OpenCacheEntryInternal(bool isHttps) { 4802 nsresult rv; 4803 4804 if (LoadResuming()) { 4805 // We don't support caching for requests initiated 4806 // via nsIResumableChannel. 4807 return NS_OK; 4808 } 4809 4810 // Don't cache byte range requests which are subranges, only cache 0- 4811 // byte range requests. 4812 if (IsSubRangeRequest(mRequestHead)) { 4813 return NS_OK; 4814 } 4815 4816 // Handle correctly WaitForCacheEntry 4817 AutoCacheWaitFlags waitFlags(this); 4818 4819 nsAutoCString cacheKey; 4820 4821 nsCOMPtr<nsICacheStorageService> cacheStorageService( 4822 components::CacheStorage::Service()); 4823 if (!cacheStorageService) { 4824 return NS_ERROR_NOT_AVAILABLE; 4825 } 4826 4827 nsCOMPtr<nsICacheStorage> cacheStorage; 4828 mCacheEntryURI = mURI; 4829 4830 RefPtr<LoadContextInfo> info = GetLoadContextInfo(this); 4831 if (!info) { 4832 return NS_ERROR_FAILURE; 4833 } 4834 4835 uint32_t cacheEntryOpenFlags; 4836 bool offline = gIOService->IsOffline(); 4837 4838 RefPtr<mozilla::dom::BrowsingContext> bc; 4839 mLoadInfo->GetBrowsingContext(getter_AddRefs(bc)); 4840 4841 bool maybeRCWN = false; 4842 4843 nsAutoCString cacheControlRequestHeader; 4844 (void)mRequestHead.GetHeader(nsHttp::Cache_Control, 4845 cacheControlRequestHeader); 4846 CacheControlParser cacheControlRequest(cacheControlRequestHeader); 4847 if (cacheControlRequest.NoStore()) { 4848 return NS_OK; 4849 } 4850 4851 bool forceOffline = bc && bc->Top()->GetForceOffline(); 4852 if (offline || (mLoadFlags & INHIBIT_CACHING) || forceOffline) { 4853 if (BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass()) && 4854 !offline && !forceOffline) { 4855 return NS_OK; 4856 } 4857 cacheEntryOpenFlags = nsICacheStorage::OPEN_READONLY; 4858 StoreCacheEntryIsReadOnly(true); 4859 } else if (BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass())) { 4860 cacheEntryOpenFlags = nsICacheStorage::OPEN_TRUNCATE; 4861 } else { 4862 cacheEntryOpenFlags = 4863 nsICacheStorage::OPEN_NORMALLY | nsICacheStorage::CHECK_MULTITHREADED; 4864 } 4865 4866 // Remember the request is a custom conditional request so that we can 4867 // process any 304 response correctly. 4868 StoreCustomConditionalRequest( 4869 mRequestHead.HasHeader(nsHttp::If_Modified_Since) || 4870 mRequestHead.HasHeader(nsHttp::If_None_Match) || 4871 mRequestHead.HasHeader(nsHttp::If_Unmodified_Since) || 4872 mRequestHead.HasHeader(nsHttp::If_Match) || 4873 mRequestHead.HasHeader(nsHttp::If_Range)); 4874 4875 if (mLoadFlags & INHIBIT_PERSISTENT_CACHING) { 4876 rv = cacheStorageService->MemoryCacheStorage( 4877 info, // ? choose app cache as well... 4878 getter_AddRefs(cacheStorage)); 4879 } else if (LoadPinCacheContent()) { 4880 rv = cacheStorageService->PinningCacheStorage(info, 4881 getter_AddRefs(cacheStorage)); 4882 } else { 4883 // Try to race only if we use disk cache storage 4884 maybeRCWN = mRequestHead.IsSafeMethod(); 4885 rv = cacheStorageService->DiskCacheStorage(info, 4886 getter_AddRefs(cacheStorage)); 4887 } 4888 NS_ENSURE_SUCCESS(rv, rv); 4889 4890 if ((mClassOfService.Flags() & nsIClassOfService::Leader) || 4891 (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI)) { 4892 cacheEntryOpenFlags |= nsICacheStorage::OPEN_PRIORITY; 4893 } 4894 4895 // Only for backward compatibility with the old cache back end. 4896 // When removed, remove the flags and related code snippets. 4897 if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) { 4898 cacheEntryOpenFlags |= nsICacheStorage::OPEN_BYPASS_IF_BUSY; 4899 } 4900 4901 if (mPostID) { 4902 mCacheIdExtension.Append(nsPrintfCString("%d", mPostID)); 4903 } 4904 if (LoadIsTRRServiceChannel()) { 4905 mCacheIdExtension.Append("TRR"); 4906 } 4907 if (mRequestHead.IsHead()) { 4908 mCacheIdExtension.Append("HEAD"); 4909 } 4910 bool isThirdParty = false; 4911 if (StaticPrefs::network_fetch_cache_partition_cross_origin() && 4912 (NS_FAILED(mLoadInfo->TriggeringPrincipal()->IsThirdPartyChannel( 4913 this, &isThirdParty)) || 4914 isThirdParty) && 4915 (mLoadInfo->InternalContentPolicyType() == nsIContentPolicy::TYPE_FETCH || 4916 mLoadInfo->InternalContentPolicyType() == 4917 nsIContentPolicy::TYPE_XMLHTTPREQUEST || 4918 mLoadInfo->InternalContentPolicyType() == 4919 nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_ASYNC || 4920 mLoadInfo->InternalContentPolicyType() == 4921 nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST_SYNC)) { 4922 mCacheIdExtension.Append("FETCH"); 4923 } 4924 4925 mCacheOpenWithPriority = cacheEntryOpenFlags & nsICacheStorage::OPEN_PRIORITY; 4926 mCacheQueueSizeWhenOpen = 4927 CacheStorageService::CacheQueueSize(mCacheOpenWithPriority); 4928 4929 // If the browser is set to offline, or it doesn't have any active network 4930 // interfaces then don't race, as it's unlikely the network would win :) 4931 if (NS_IsOffline()) { 4932 maybeRCWN = false; 4933 } 4934 4935 if ((mNetworkTriggerDelay || RCWNEnabled()) && maybeRCWN && mAllowRCWN) { 4936 bool hasAltData = false; 4937 uint32_t sizeInKb = 0; 4938 rv = cacheStorage->GetCacheIndexEntryAttrs( 4939 mCacheEntryURI, mCacheIdExtension, &hasAltData, &sizeInKb); 4940 4941 // We will attempt to race the network vs the cache if we've found 4942 // this entry in the cache index, and it has appropriate attributes 4943 // (doesn't have alt-data, and has a small size) 4944 if (NS_SUCCEEDED(rv) && !hasAltData && 4945 sizeInKb < StaticPrefs::network_http_rcwn_small_resource_size_kb()) { 4946 MaybeRaceCacheWithNetwork(); 4947 } 4948 } 4949 4950 if (!mCacheOpenDelay) { 4951 MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread"); 4952 if (mNetworkTriggered) { 4953 mRaceCacheWithNetwork = RCWNEnabled(); 4954 } 4955 rv = cacheStorage->AsyncOpenURI(mCacheEntryURI, mCacheIdExtension, 4956 cacheEntryOpenFlags, this); 4957 } else { 4958 // We pass `this` explicitly as a parameter due to the raw pointer 4959 // to refcounted object in lambda analysis. 4960 mCacheOpenFunc = [cacheEntryOpenFlags, 4961 cacheStorage](nsHttpChannel* self) -> void { 4962 MOZ_ASSERT(NS_IsMainThread(), "Should be called on the main thread"); 4963 cacheStorage->AsyncOpenURI(self->mCacheEntryURI, self->mCacheIdExtension, 4964 cacheEntryOpenFlags, self); 4965 }; 4966 4967 // calls nsHttpChannel::Notify after `mCacheOpenDelay` milliseconds 4968 auto callback = MakeRefPtr<TimerCallback>(this); 4969 NS_NewTimerWithCallback(getter_AddRefs(mCacheOpenTimer), callback, 4970 mCacheOpenDelay, nsITimer::TYPE_ONE_SHOT); 4971 } 4972 NS_ENSURE_SUCCESS(rv, rv); 4973 4974 waitFlags.Keep(WAIT_FOR_CACHE_ENTRY); 4975 4976 return NS_OK; 4977 } 4978 4979 nsresult nsHttpChannel::CheckPartial(nsICacheEntry* aEntry, int64_t* aSize, 4980 int64_t* aContentLength) { 4981 return nsHttp::CheckPartial( 4982 aEntry, aSize, aContentLength, 4983 mCachedResponseHead ? mCachedResponseHead.get() : mResponseHead.get()); 4984 } 4985 4986 void nsHttpChannel::UntieValidationRequest() { 4987 DebugOnly<nsresult> rv{}; 4988 // Make the request unconditional again. 4989 rv = mRequestHead.ClearHeader(nsHttp::If_Modified_Since); 4990 MOZ_ASSERT(NS_SUCCEEDED(rv)); 4991 rv = mRequestHead.ClearHeader(nsHttp::If_None_Match); 4992 MOZ_ASSERT(NS_SUCCEEDED(rv)); 4993 rv = mRequestHead.ClearHeader(nsHttp::ETag); 4994 MOZ_ASSERT(NS_SUCCEEDED(rv)); 4995 } 4996 4997 NS_IMETHODIMP 4998 nsHttpChannel::OnCacheEntryCheck(nsICacheEntry* entry, uint32_t* aResult) { 4999 nsresult rv = NS_OK; 5000 5001 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnCacheEntryCheck", NETWORK, 5002 Flow::FromPointer(this)); 5003 LOG(("nsHttpChannel::OnCacheEntryCheck enter [channel=%p entry=%p]", this, 5004 entry)); 5005 5006 mozilla::MutexAutoLock lock(mRCWNLock); 5007 5008 if (mRaceCacheWithNetwork && mFirstResponseSource == RESPONSE_FROM_NETWORK) { 5009 LOG( 5010 ("Not using cached response because we've already got one from the " 5011 "network %p", 5012 this)); 5013 *aResult = ENTRY_NOT_WANTED; 5014 5015 // Net-win indicates that mOnStartRequestTimestamp is from net. 5016 TimeDuration savedTime = (TimeStamp::Now() - mOnStartRequestTimestamp); 5017 glean::network::race_cache_with_network_saved_time.AccumulateRawDuration( 5018 savedTime); 5019 PROFILER_MARKER_TEXT("RCWN", NETWORK, {}, 5020 nsPrintfCString("Network won by %" PRId64 "ms", 5021 int64_t(savedTime.ToMilliseconds()))); 5022 return NS_OK; 5023 } 5024 if (mRaceCacheWithNetwork && mFirstResponseSource == RESPONSE_PENDING) { 5025 mOnCacheEntryCheckTimestamp = TimeStamp::Now(); 5026 } 5027 5028 nsAutoCString cacheControlRequestHeader; 5029 (void)mRequestHead.GetHeader(nsHttp::Cache_Control, 5030 cacheControlRequestHeader); 5031 CacheControlParser cacheControlRequest(cacheControlRequestHeader); 5032 5033 if (cacheControlRequest.NoStore()) { 5034 LOG( 5035 ("Not using cached response based on no-store request cache " 5036 "directive\n")); 5037 *aResult = ENTRY_NOT_WANTED; 5038 return NS_OK; 5039 } 5040 5041 // Be pessimistic: assume the cache entry has no useful data. 5042 *aResult = ENTRY_WANTED; 5043 StoreCachedContentIsValid(CachedContentValidity::Invalid); 5044 5045 nsCString buf; 5046 5047 // Get the method that was used to generate the cached response 5048 rv = entry->GetMetaDataElement("request-method", getter_Copies(buf)); 5049 NS_ENSURE_SUCCESS(rv, rv); 5050 5051 bool methodWasHead = buf.EqualsLiteral("HEAD"); 5052 bool methodWasGet = buf.EqualsLiteral("GET"); 5053 5054 if (methodWasHead) { 5055 // The cached response does not contain an entity. We can only reuse 5056 // the response if the current request is also HEAD. 5057 if (!mRequestHead.IsHead()) { 5058 *aResult = ENTRY_NOT_WANTED; 5059 return NS_OK; 5060 } 5061 } 5062 buf.Adopt(nullptr); 5063 5064 // We'll need this value in later computations... 5065 uint32_t lastModifiedTime; 5066 rv = entry->GetLastModified(&lastModifiedTime); 5067 NS_ENSURE_SUCCESS(rv, rv); 5068 5069 // Determine if this is the first time that this cache entry 5070 // has been accessed during this session. 5071 bool fromPreviousSession = 5072 (gHttpHandler->SessionStartTime() > lastModifiedTime); 5073 5074 // Get the cached HTTP response headers 5075 mCachedResponseHead = MakeUnique<nsHttpResponseHead>(); 5076 5077 rv = nsHttp::GetHttpResponseHeadFromCacheEntry(entry, 5078 mCachedResponseHead.get()); 5079 NS_ENSURE_SUCCESS(rv, rv); 5080 5081 bool isCachedRedirect = WillRedirect(*mCachedResponseHead); 5082 5083 // Do not return 304 responses from the cache, and also do not return 5084 // any other non-redirect 3xx responses from the cache (see bug 759043). 5085 NS_ENSURE_TRUE((mCachedResponseHead->Status() / 100 != 3) || isCachedRedirect, 5086 NS_ERROR_ABORT); 5087 5088 if (mCachedResponseHead->NoStore() && LoadCacheEntryIsReadOnly()) { 5089 // This prevents loading no-store responses when navigating back 5090 // while the browser is set to work offline. 5091 LOG((" entry loading as read-only but is no-store, set INHIBIT_CACHING")); 5092 mLoadFlags |= nsIRequest::INHIBIT_CACHING; 5093 } 5094 5095 // Don't bother to validate items that are read-only, 5096 // unless they are read-only because of INHIBIT_CACHING 5097 if ((LoadCacheEntryIsReadOnly() && 5098 !(mLoadFlags & nsIRequest::INHIBIT_CACHING))) { 5099 int64_t size, contentLength; 5100 rv = CheckPartial(entry, &size, &contentLength); 5101 NS_ENSURE_SUCCESS(rv, rv); 5102 5103 if (contentLength != int64_t(-1) && contentLength != size) { 5104 *aResult = ENTRY_NOT_WANTED; 5105 return NS_OK; 5106 } 5107 5108 rv = OpenCacheInputStream(entry, true); 5109 if (NS_SUCCEEDED(rv)) { 5110 StoreCachedContentIsValid(CachedContentValidity::Valid); 5111 } 5112 return rv; 5113 } 5114 5115 bool wantCompleteEntry = false; 5116 5117 if (!methodWasHead && !isCachedRedirect) { 5118 // If the cached content-length is set and it does not match the data 5119 // size of the cached content, then the cached response is partial... 5120 // either we need to issue a byte range request or we need to refetch 5121 // the entire document. 5122 // 5123 // We exclude redirects from this check because we (usually) strip the 5124 // entity when we store the cache entry, and even if we didn't, we 5125 // always ignore a cached redirect's entity anyway. See bug 759043. 5126 int64_t size, contentLength; 5127 rv = CheckPartial(entry, &size, &contentLength); 5128 NS_ENSURE_SUCCESS(rv, rv); 5129 5130 if (size == int64_t(-1)) { 5131 LOG((" write is in progress")); 5132 if (mLoadFlags & LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) { 5133 LOG( 5134 (" not interested in the entry, " 5135 "LOAD_BYPASS_LOCAL_CACHE_IF_BUSY specified")); 5136 5137 *aResult = ENTRY_NOT_WANTED; 5138 return NS_OK; 5139 } 5140 5141 // Ignore !(size > 0) from the resumability condition 5142 if (!IsResumable(size, contentLength, true)) { 5143 if (IsNavigation()) { 5144 LOG( 5145 (" bypassing wait for the entry, " 5146 "this is a navigational load")); 5147 *aResult = ENTRY_NOT_WANTED; 5148 return NS_OK; 5149 } 5150 5151 LOG( 5152 (" wait for entry completion, " 5153 "response is not resumable")); 5154 5155 wantCompleteEntry = true; 5156 } else { 5157 StoreConcurrentCacheAccess(1); 5158 } 5159 } else if (contentLength != int64_t(-1) && contentLength != size) { 5160 LOG( 5161 ("Cached data size does not match the Content-Length header " 5162 "[content-length=%" PRId64 " size=%" PRId64 "]\n", 5163 contentLength, size)); 5164 5165 rv = MaybeSetupByteRangeRequest(size, contentLength); 5166 StoreCachedContentIsPartial(NS_SUCCEEDED(rv) && LoadIsPartialRequest()); 5167 if (LoadCachedContentIsPartial()) { 5168 rv = OpenCacheInputStream(entry, false); 5169 if (NS_FAILED(rv)) { 5170 UntieByteRangeRequest(); 5171 return rv; 5172 } 5173 5174 *aResult = ENTRY_NEEDS_REVALIDATION; 5175 return NS_OK; 5176 } 5177 5178 if (size == 0 && LoadCacheOnlyMetadata()) { 5179 // Don't break cache entry load when the entry's data size 5180 // is 0 and CacheOnlyMetadata flag is set. In that case we 5181 // want to proceed since the LOAD_ONLY_IF_MODIFIED flag is 5182 // also set. 5183 MOZ_ASSERT(mLoadFlags & LOAD_ONLY_IF_MODIFIED); 5184 } else { 5185 return rv; 5186 } 5187 } 5188 } 5189 5190 bool isHttps = mURI->SchemeIs("https"); 5191 5192 bool doValidation = false; 5193 bool doBackgroundValidation = false; 5194 bool canAddImsHeader = true; 5195 5196 bool isForcedValid = false; 5197 entry->GetIsForcedValid(&isForcedValid); 5198 5199 bool weaklyFramed, isImmutable; 5200 nsHttp::DetermineFramingAndImmutability(entry, mCachedResponseHead.get(), 5201 isHttps, &weaklyFramed, &isImmutable); 5202 5203 // Cached entry is not the entity we request (see bug #633743) 5204 if (ResponseWouldVary(entry)) { 5205 LOG(("Validating based on Vary headers returning TRUE\n")); 5206 canAddImsHeader = false; 5207 doValidation = true; 5208 } else { 5209 if (mCachedResponseHead->ExpiresInPast() || 5210 mCachedResponseHead->MustValidateIfExpired()) { 5211 } 5212 doValidation = nsHttp::ValidationRequired( 5213 isForcedValid, mCachedResponseHead.get(), mLoadFlags, 5214 LoadAllowStaleCacheContent(), LoadForceValidateCacheContent(), 5215 isImmutable, LoadCustomConditionalRequest(), mRequestHead, entry, 5216 cacheControlRequest, fromPreviousSession, &doBackgroundValidation); 5217 } 5218 5219 nsAutoCString requestedETag; 5220 if (!doValidation && 5221 NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::If_Match, requestedETag)) && 5222 (methodWasGet || methodWasHead)) { 5223 nsAutoCString cachedETag; 5224 (void)mCachedResponseHead->GetHeader(nsHttp::ETag, cachedETag); 5225 if (!cachedETag.IsEmpty() && (StringBeginsWith(cachedETag, "W/"_ns) || 5226 !requestedETag.Equals(cachedETag))) { 5227 // User has defined If-Match header, if the cached entry is not 5228 // matching the provided header value or the cached ETag is weak, 5229 // force validation. 5230 doValidation = true; 5231 } 5232 } 5233 5234 // Previous error should not be propagated. 5235 rv = NS_OK; 5236 5237 if (!doValidation) { 5238 // 5239 // Check the authorization headers used to generate the cache entry. 5240 // We must validate the cache entry if: 5241 // 5242 // 1) the cache entry was generated prior to this session w/ 5243 // credentials (see bug 103402). 5244 // 2) the cache entry was generated w/o credentials, but would now 5245 // require credentials (see bug 96705). 5246 // 5247 // NOTE: this does not apply to proxy authentication. 5248 // 5249 entry->GetMetaDataElement("auth", getter_Copies(buf)); 5250 doValidation = 5251 (fromPreviousSession && !buf.IsEmpty()) || 5252 (buf.IsEmpty() && mRequestHead.HasHeader(nsHttp::Authorization)); 5253 } 5254 5255 // Bug #561276: We maintain a chain of cache-keys which returns cached 5256 // 3xx-responses (redirects) in order to detect cycles. If a cycle is 5257 // found, ignore the cached response and hit the net. Otherwise, use 5258 // the cached response and add the cache-key to the chain. Note that 5259 // a limited number of redirects (cached or not) is allowed and is 5260 // enforced independently of this mechanism 5261 if (!doValidation && isCachedRedirect) { 5262 nsAutoCString cacheKey; 5263 rv = GenerateCacheKey(mPostID, cacheKey); 5264 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5265 5266 auto redirectedCachekeys = mRedirectedCachekeys.Lock(); 5267 auto& ref = redirectedCachekeys.ref(); 5268 if (!ref) { 5269 ref = MakeUnique<nsTArray<nsCString>>(); 5270 } else if (ref->Contains(cacheKey)) { 5271 doValidation = true; 5272 } 5273 5274 LOG(("Redirection-chain %s key %s\n", 5275 doValidation ? "contains" : "does not contain", cacheKey.get())); 5276 5277 // Append cacheKey if not in the chain already 5278 if (!doValidation) { 5279 ref->AppendElement(cacheKey); 5280 } 5281 } 5282 5283 StoreCachedContentIsValid(!doValidation ? CachedContentValidity::Valid 5284 : CachedContentValidity::Invalid); 5285 5286 if (isForcedValid) { 5287 // Telemetry value is only useful if this was a prefetched item 5288 if (!doValidation) { 5289 // Could have gotten to a funky state with some of the if chain above 5290 // and in nsHttp::ValidationRequired. Make sure we get it right here. 5291 entry->MarkForcedValidUse(); 5292 } 5293 } 5294 5295 if (doValidation) { 5296 // 5297 // now, we are definitely going to issue a HTTP request to the server. 5298 // make it conditional if possible. 5299 // 5300 // do not attempt to validate no-store content, since servers will not 5301 // expect it to be cached. (we only keep it in our cache for the 5302 // purposes of back/forward, etc.) 5303 // 5304 // the request method MUST be either GET or HEAD (see bug 175641) and 5305 // the cached response code must be < 400 5306 // 5307 // the cached content must not be weakly framed 5308 // 5309 // do not override conditional headers when consumer has defined its own 5310 if (!mCachedResponseHead->NoStore() && 5311 (mRequestHead.IsGet() || mRequestHead.IsHead()) && 5312 !LoadCustomConditionalRequest() && !weaklyFramed && 5313 (mCachedResponseHead->Status() < 400)) { 5314 if (LoadConcurrentCacheAccess()) { 5315 // In case of concurrent read and also validation request we 5316 // must wait for the current writer to close the output stream 5317 // first. Otherwise, when the writer's job would have been interrupted 5318 // before all the data were downloaded, we'd have to do a range request 5319 // which would be a second request in line during this channel's 5320 // life-time. nsHttpChannel is not designed to do that, so rather 5321 // turn off concurrent read and wait for entry's completion. 5322 // Then only re-validation or range-re-validation request will go out. 5323 StoreConcurrentCacheAccess(0); 5324 // This will cause that OnCacheEntryCheck is called again with the same 5325 // entry after the writer is done. 5326 wantCompleteEntry = true; 5327 } else { 5328 nsAutoCString val; 5329 // Add If-Modified-Since header if a Last-Modified was given 5330 // and we are allowed to do this (see bugs 510359 and 269303) 5331 if (canAddImsHeader) { 5332 (void)mCachedResponseHead->GetHeader(nsHttp::Last_Modified, val); 5333 if (!val.IsEmpty()) { 5334 rv = mRequestHead.SetHeader(nsHttp::If_Modified_Since, val); 5335 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5336 } 5337 } 5338 // Add If-None-Match header if an ETag was given in the response 5339 (void)mCachedResponseHead->GetHeader(nsHttp::ETag, val); 5340 if (!val.IsEmpty()) { 5341 rv = mRequestHead.SetHeader(nsHttp::If_None_Match, val); 5342 MOZ_ASSERT(NS_SUCCEEDED(rv)); 5343 } 5344 mDidReval = true; 5345 } 5346 } 5347 } 5348 5349 bool valid = CachedContentIsValid(); 5350 if (valid || mDidReval) { 5351 rv = OpenCacheInputStream(entry, valid); 5352 if (NS_FAILED(rv)) { 5353 // If we can't get the entity then we have to act as though we 5354 // don't have the cache entry. 5355 if (mDidReval) { 5356 UntieValidationRequest(); 5357 mDidReval = false; 5358 } 5359 StoreCachedContentIsValid(CachedContentValidity::Invalid); 5360 } 5361 } 5362 5363 if (mDidReval) { 5364 *aResult = ENTRY_NEEDS_REVALIDATION; 5365 } else if (wantCompleteEntry) { 5366 *aResult = RECHECK_AFTER_WRITE_FINISHED; 5367 } else { 5368 *aResult = ENTRY_WANTED; 5369 5370 if (doBackgroundValidation) { 5371 PerformBackgroundCacheRevalidation(); 5372 } 5373 } 5374 5375 LOG( 5376 ("nsHTTPChannel::OnCacheEntryCheck exit [this=%p doValidation=%d " 5377 "result=%d]\n", 5378 this, doValidation, *aResult)); 5379 return rv; 5380 } 5381 5382 NS_IMETHODIMP 5383 nsHttpChannel::OnCacheEntryAvailable(nsICacheEntry* entry, bool aNew, 5384 nsresult status) { 5385 MOZ_ASSERT(NS_IsMainThread()); 5386 5387 nsresult rv; 5388 5389 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnCacheEntryAvailable", NETWORK, 5390 Flow::FromPointer(this)); 5391 LOG( 5392 ("nsHttpChannel::OnCacheEntryAvailable [this=%p entry=%p " 5393 "new=%d status=%" PRIx32 "] for %s", 5394 this, entry, aNew, static_cast<uint32_t>(status), mSpec.get())); 5395 5396 // if the channel's already fired onStopRequest, then we should ignore 5397 // this event. 5398 if (!LoadIsPending()) { 5399 mCacheInputStream.CloseAndRelease(); 5400 return NS_OK; 5401 } 5402 5403 rv = OnCacheEntryAvailableInternal(entry, aNew, status); 5404 if (NS_FAILED(rv)) { 5405 CloseCacheEntry(false); 5406 if (mRaceCacheWithNetwork && mNetworkTriggered && 5407 mFirstResponseSource != RESPONSE_FROM_CACHE) { 5408 // Ignore the error if we're racing cache with network and the cache 5409 // didn't win, The network part will handle cancelation or any other 5410 // error. Otherwise we could end up calling the listener twice, see 5411 // bug 1397593. 5412 LOG( 5413 (" not calling AsyncAbort() because we're racing cache with " 5414 "network")); 5415 } else { 5416 (void)AsyncAbort(rv); 5417 } 5418 } 5419 5420 return NS_OK; 5421 } 5422 5423 nsresult nsHttpChannel::OnCacheEntryAvailableInternal(nsICacheEntry* entry, 5424 bool aNew, 5425 nsresult status) { 5426 nsresult rv; 5427 5428 if (mCanceled) { 5429 LOG(("channel was canceled [this=%p status=%" PRIx32 "]\n", this, 5430 static_cast<uint32_t>(static_cast<nsresult>(mStatus)))); 5431 return mStatus; 5432 } 5433 5434 if (mIgnoreCacheEntry) { 5435 if (!entry || aNew) { 5436 // We use this flag later to decide whether to report 5437 // LABELS_NETWORK_RACE_CACHE_VALIDATION::NotSent. We didn't have 5438 // an usable entry, so drop the flag. 5439 mIgnoreCacheEntry = false; 5440 } 5441 entry = nullptr; 5442 status = NS_ERROR_NOT_AVAILABLE; 5443 } 5444 5445 rv = OnNormalCacheEntryAvailable(entry, aNew, status); 5446 5447 if (NS_FAILED(rv) && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) { 5448 return NS_ERROR_DOCUMENT_NOT_CACHED; 5449 } 5450 5451 if (NS_FAILED(rv)) { 5452 return rv; 5453 } 5454 5455 // We may be waiting for more callbacks... 5456 if (AwaitingCacheCallbacks()) { 5457 return NS_OK; 5458 } 5459 5460 bool valid = CachedContentIsValid(); 5461 if (mRaceCacheWithNetwork && 5462 ((mCacheEntry && !valid && (mDidReval || LoadCachedContentIsPartial())) || 5463 mIgnoreCacheEntry)) { 5464 // We won't send the conditional request because the unconditional 5465 // request was already sent (see bug 1377223). 5466 glean::network::race_cache_validation 5467 .EnumGet(glean::network::RaceCacheValidationLabel::eNotsent) 5468 .Add(); 5469 } 5470 5471 if (mRaceCacheWithNetwork && valid) { 5472 (void)ReadFromCache(); 5473 } 5474 5475 return TriggerNetwork(); 5476 } 5477 5478 nsresult nsHttpChannel::OnNormalCacheEntryAvailable(nsICacheEntry* aEntry, 5479 bool aNew, 5480 nsresult aEntryStatus) { 5481 StoreWaitForCacheEntry(LoadWaitForCacheEntry() & ~WAIT_FOR_CACHE_ENTRY); 5482 5483 if (NS_FAILED(aEntryStatus) || aNew) { 5484 // Make sure this flag is dropped. It may happen the entry is doomed 5485 // between OnCacheEntryCheck and OnCacheEntryAvailable. 5486 StoreCachedContentIsValid(CachedContentValidity::Invalid); 5487 5488 // From the same reason remove any conditional headers added 5489 // in OnCacheEntryCheck. 5490 if (mDidReval) { 5491 LOG((" Removing conditional request headers")); 5492 UntieValidationRequest(); 5493 mDidReval = false; 5494 } 5495 5496 if (LoadCachedContentIsPartial()) { 5497 LOG((" Removing byte range request headers")); 5498 UntieByteRangeRequest(); 5499 StoreCachedContentIsPartial(false); 5500 } 5501 5502 if (mLoadFlags & LOAD_ONLY_FROM_CACHE) { 5503 // if this channel is only allowed to pull from the cache, then 5504 // we must fail if we were unable to open a cache entry for read. 5505 return NS_ERROR_DOCUMENT_NOT_CACHED; 5506 } 5507 } 5508 5509 if (NS_SUCCEEDED(aEntryStatus)) { 5510 mCacheEntry = aEntry; 5511 StoreCacheEntryIsWriteOnly(aNew); 5512 5513 if (!aNew && !mAsyncOpenTime.IsNull()) { 5514 // We use microseconds for IO operations. For consistency let's use 5515 // microseconds here too. 5516 uint32_t duration = (TimeStamp::Now() - mAsyncOpenTime).ToMicroseconds(); 5517 bool isSlow = false; 5518 if ((mCacheOpenWithPriority && 5519 mCacheQueueSizeWhenOpen >= 5520 StaticPrefs:: 5521 network_http_rcwn_cache_queue_priority_threshold()) || 5522 (!mCacheOpenWithPriority && 5523 mCacheQueueSizeWhenOpen >= 5524 StaticPrefs::network_http_rcwn_cache_queue_normal_threshold())) { 5525 isSlow = true; 5526 } 5527 CacheFileUtils::CachePerfStats::AddValue( 5528 CacheFileUtils::CachePerfStats::ENTRY_OPEN, duration, isSlow); 5529 } 5530 } 5531 5532 return NS_OK; 5533 } 5534 5535 // Generates the proper cache-key for this instance of nsHttpChannel 5536 nsresult nsHttpChannel::GenerateCacheKey(uint32_t postID, 5537 nsACString& cacheKey) { 5538 AssembleCacheKey(mSpec.get(), postID, cacheKey); 5539 return NS_OK; 5540 } 5541 5542 // Assembles a cache-key from the given pieces of information and |mLoadFlags| 5543 void nsHttpChannel::AssembleCacheKey(const char* spec, uint32_t postID, 5544 nsACString& cacheKey) { 5545 cacheKey.Truncate(); 5546 5547 if (mLoadFlags & LOAD_ANONYMOUS) { 5548 cacheKey.AssignLiteral("anon&"); 5549 } 5550 5551 if (postID) { 5552 char buf[32]; 5553 SprintfLiteral(buf, "id=%x&", postID); 5554 cacheKey.Append(buf); 5555 } 5556 5557 if (!cacheKey.IsEmpty()) { 5558 cacheKey.AppendLiteral("uri="); 5559 } 5560 5561 // Strip any trailing #ref from the URL before using it as the key 5562 const char* p = strchr(spec, '#'); 5563 if (p) { 5564 cacheKey.Append(spec, p - spec); 5565 } else { 5566 cacheKey.Append(spec); 5567 } 5568 } 5569 5570 nsresult DoUpdateExpirationTime(nsHttpChannel* aSelf, 5571 nsICacheEntry* aCacheEntry, 5572 nsHttpResponseHead* aResponseHead, 5573 uint32_t& aExpirationTime) { 5574 MOZ_ASSERT(aExpirationTime == 0); 5575 NS_ENSURE_TRUE(aResponseHead, NS_ERROR_FAILURE); 5576 5577 nsresult rv; 5578 5579 if (!aResponseHead->MustValidate()) { 5580 // For stale-while-revalidate we use expiration time as the absolute base 5581 // for calculation of the stale window absolute end time. Hence, when the 5582 // entry may be served w/o revalidation, we need a non-zero value for the 5583 // expiration time. Let's set it to |now|, which basicly means "expired", 5584 // same as when set to 0. 5585 uint32_t now = NowInSeconds(); 5586 aExpirationTime = now; 5587 5588 uint32_t freshnessLifetime = 0; 5589 5590 rv = aResponseHead->ComputeFreshnessLifetime(&freshnessLifetime); 5591 if (NS_FAILED(rv)) return rv; 5592 5593 if (freshnessLifetime > 0) { 5594 uint32_t currentAge = 0; 5595 5596 rv = aResponseHead->ComputeCurrentAge(now, aSelf->GetRequestTime(), 5597 ¤tAge); 5598 if (NS_FAILED(rv)) return rv; 5599 5600 LOG(("freshnessLifetime = %u, currentAge = %u\n", freshnessLifetime, 5601 currentAge)); 5602 5603 if (freshnessLifetime > currentAge) { 5604 uint32_t timeRemaining = freshnessLifetime - currentAge; 5605 // be careful... now + timeRemaining may overflow 5606 if (now + timeRemaining < now) { 5607 aExpirationTime = uint32_t(-1); 5608 } else { 5609 aExpirationTime = now + timeRemaining; 5610 } 5611 } 5612 } 5613 } 5614 5615 rv = aCacheEntry->SetExpirationTime(aExpirationTime); 5616 NS_ENSURE_SUCCESS(rv, rv); 5617 5618 return rv; 5619 } 5620 5621 // UpdateExpirationTime is called when a new response comes in from the server. 5622 // It updates the stored response-time and sets the expiration time on the 5623 // cache entry. 5624 // 5625 // From section 13.2.4 of RFC2616, we compute expiration time as follows: 5626 // 5627 // timeRemaining = freshnessLifetime - currentAge 5628 // expirationTime = now + timeRemaining 5629 // 5630 nsresult nsHttpChannel::UpdateExpirationTime() { 5631 uint32_t expirationTime = 0; 5632 nsresult rv = DoUpdateExpirationTime(this, mCacheEntry, mResponseHead.get(), 5633 expirationTime); 5634 NS_ENSURE_SUCCESS(rv, rv); 5635 5636 return NS_OK; 5637 } 5638 5639 nsresult nsHttpChannel::OpenCacheInputStream(nsICacheEntry* cacheEntry, 5640 bool startBuffering) { 5641 nsresult rv; 5642 5643 if (mURI->SchemeIs("https")) { 5644 rv = cacheEntry->GetSecurityInfo(getter_AddRefs(mCachedSecurityInfo)); 5645 if (NS_FAILED(rv)) { 5646 LOG(("failed to parse security-info [channel=%p, entry=%p]", this, 5647 cacheEntry)); 5648 NS_WARNING("failed to parse security-info"); 5649 cacheEntry->AsyncDoom(nullptr); 5650 return rv; 5651 } 5652 5653 MOZ_ASSERT(mCachedSecurityInfo); 5654 if (!mCachedSecurityInfo) { 5655 LOG( 5656 ("mCacheEntry->GetSecurityInfo returned success but did not " 5657 "return the security info [channel=%p, entry=%p]", 5658 this, cacheEntry)); 5659 cacheEntry->AsyncDoom(nullptr); 5660 return NS_ERROR_UNEXPECTED; // XXX error code 5661 } 5662 } 5663 5664 // Keep the conditions below in sync with the conditions in ReadFromCache. 5665 5666 rv = NS_OK; 5667 5668 if (WillRedirect(*mCachedResponseHead)) { 5669 // Do not even try to read the entity for a redirect because we do not 5670 // return an entity to the application when we process redirects. 5671 LOG(("Will skip read of cached redirect entity\n")); 5672 return NS_OK; 5673 } 5674 5675 if ((mLoadFlags & nsICachingChannel::LOAD_ONLY_IF_MODIFIED) && 5676 !LoadCachedContentIsPartial()) { 5677 // For LOAD_ONLY_IF_MODIFIED, we usually don't have to deal with the 5678 // cached entity. 5679 LOG( 5680 ("Will skip read from cache based on LOAD_ONLY_IF_MODIFIED " 5681 "load flag\n")); 5682 return NS_OK; 5683 } 5684 5685 // Open an input stream for the entity, so that the call to OpenInputStream 5686 // happens off the main thread. 5687 nsCOMPtr<nsIInputStream> stream; 5688 5689 // If an alternate representation was requested, try to open the alt 5690 // input stream. 5691 // If the entry has a "is-from-child" metadata, then only open the altdata 5692 // stream if the consumer is also from child. 5693 bool altDataFromChild = false; 5694 { 5695 nsCString value; 5696 rv = cacheEntry->GetMetaDataElement("alt-data-from-child", 5697 getter_Copies(value)); 5698 altDataFromChild = !value.IsEmpty(); 5699 } 5700 5701 nsAutoCString altDataType; 5702 (void)cacheEntry->GetAltDataType(altDataType); 5703 5704 nsAutoCString contentType; 5705 mCachedResponseHead->ContentType(contentType); 5706 5707 bool foundAltData = false; 5708 bool deliverAltData = true; 5709 if (!LoadDisableAltDataCache() && !altDataType.IsEmpty() && 5710 !mPreferredCachedAltDataTypes.IsEmpty() && 5711 altDataFromChild == LoadAltDataForChild()) { 5712 for (auto& pref : mPreferredCachedAltDataTypes) { 5713 if (pref.type() == altDataType && 5714 (pref.contentType().IsEmpty() || pref.contentType() == contentType)) { 5715 foundAltData = true; 5716 deliverAltData = 5717 pref.deliverAltData() == 5718 nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::ASYNC; 5719 break; 5720 } 5721 } 5722 } 5723 5724 nsCOMPtr<nsIInputStream> altData; 5725 int64_t altDataSize = -1; 5726 if (foundAltData) { 5727 rv = cacheEntry->OpenAlternativeInputStream(altDataType, 5728 getter_AddRefs(altData)); 5729 if (NS_SUCCEEDED(rv)) { 5730 // We have succeeded. 5731 mAvailableCachedAltDataType = altDataType; 5732 StoreDeliveringAltData(deliverAltData); 5733 5734 // Set the correct data size on the channel. 5735 (void)cacheEntry->GetAltDataSize(&altDataSize); 5736 mAltDataLength = altDataSize; 5737 5738 LOG(("Opened alt-data input stream [type=%s, size=%" PRId64 5739 ", deliverAltData=%d]", 5740 altDataType.get(), mAltDataLength, deliverAltData)); 5741 5742 if (deliverAltData) { 5743 stream = altData; 5744 } 5745 } 5746 } 5747 5748 if (!stream) { 5749 rv = cacheEntry->OpenInputStream(0, getter_AddRefs(stream)); 5750 } 5751 5752 if (NS_FAILED(rv)) { 5753 LOG( 5754 ("Failed to open cache input stream [channel=%p, " 5755 "mCacheEntry=%p]", 5756 this, cacheEntry)); 5757 return rv; 5758 } 5759 5760 if (startBuffering) { 5761 bool nonBlocking; 5762 rv = stream->IsNonBlocking(&nonBlocking); 5763 if (NS_SUCCEEDED(rv) && nonBlocking) startBuffering = false; 5764 } 5765 5766 if (!startBuffering) { 5767 // Bypass wrapping the input stream for the new cache back-end since 5768 // nsIStreamTransportService expects a blocking stream. Preloading of 5769 // the data must be done on the level of the cache backend, internally. 5770 // 5771 // We do not connect the stream to the stream transport service if we 5772 // have to validate the entry with the server. If we did, we would get 5773 // into a race condition between the stream transport service reading 5774 // the existing contents and the opening of the cache entry's output 5775 // stream to write the new contents in the case where we get a non-304 5776 // response. 5777 LOG( 5778 ("Opened cache input stream without buffering [channel=%p, " 5779 "mCacheEntry=%p, stream=%p]", 5780 this, cacheEntry, stream.get())); 5781 mCacheInputStream.takeOver(stream); 5782 return rv; 5783 } 5784 5785 // Have the stream transport service start reading the entity on one of its 5786 // background threads. 5787 5788 nsCOMPtr<nsITransport> transport; 5789 nsCOMPtr<nsIInputStream> wrapper; 5790 5791 nsCOMPtr<nsIStreamTransportService> sts( 5792 components::StreamTransport::Service()); 5793 rv = sts ? NS_OK : NS_ERROR_NOT_AVAILABLE; 5794 if (NS_SUCCEEDED(rv)) { 5795 rv = sts->CreateInputTransport(stream, true, getter_AddRefs(transport)); 5796 } 5797 if (NS_SUCCEEDED(rv)) { 5798 rv = transport->OpenInputStream(0, 0, 0, getter_AddRefs(wrapper)); 5799 } 5800 if (NS_SUCCEEDED(rv)) { 5801 LOG( 5802 ("Opened cache input stream [channel=%p, wrapper=%p, " 5803 "transport=%p, stream=%p]", 5804 this, wrapper.get(), transport.get(), stream.get())); 5805 } else { 5806 LOG( 5807 ("Failed to open cache input stream [channel=%p, " 5808 "wrapper=%p, transport=%p, stream=%p]", 5809 this, wrapper.get(), transport.get(), stream.get())); 5810 5811 stream->Close(); 5812 return rv; 5813 } 5814 5815 mCacheInputStream.takeOver(wrapper); 5816 5817 return NS_OK; 5818 } 5819 5820 // Actually process the cached response that we started to handle in CheckCache 5821 // and/or StartBufferingCachedEntity. 5822 nsresult nsHttpChannel::ReadFromCache(void) { 5823 NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_FAILURE); 5824 NS_ENSURE_TRUE(CachedContentIsValid(), NS_ERROR_FAILURE); 5825 NS_ENSURE_TRUE(!mCachePump, NS_OK); // already opened 5826 5827 LOG( 5828 ("nsHttpChannel::ReadFromCache [this=%p] " 5829 "Using cached copy of: %s\n", 5830 this, mSpec.get())); 5831 5832 // When racing the cache with the network with a timer, and we get data from 5833 // the cache, we should prevent the timer from triggering a network request. 5834 if (mNetworkTriggerTimer) { 5835 mNetworkTriggerTimer->Cancel(); 5836 mNetworkTriggerTimer = nullptr; 5837 } 5838 5839 if (mRaceCacheWithNetwork) { 5840 MOZ_ASSERT(mFirstResponseSource != RESPONSE_FROM_CACHE); 5841 if (mFirstResponseSource == RESPONSE_PENDING) { 5842 LOG(("First response from cache")); 5843 PROFILER_MARKER_TEXT( 5844 "RCWN", NETWORK, {}, 5845 nsPrintfCString("Cache won for %s (%p)", mSpec.get(), this)); 5846 mFirstResponseSource = RESPONSE_FROM_CACHE; 5847 5848 // Cancel the transaction because we will serve the request from the cache 5849 CancelNetworkRequest(NS_BINDING_ABORTED); 5850 if (mTransactionPump && mSuspendCount) { 5851 uint32_t suspendCount = mSuspendCount; 5852 while (suspendCount--) { 5853 mTransactionPump->Resume(); 5854 } 5855 } 5856 mTransaction = nullptr; 5857 mTransactionPump = nullptr; 5858 } else { 5859 MOZ_ASSERT(mFirstResponseSource == RESPONSE_FROM_NETWORK); 5860 LOG( 5861 ("Skipping read from cache because first response was from " 5862 "network\n")); 5863 5864 if (!mOnCacheEntryCheckTimestamp.IsNull()) { 5865 TimeStamp currentTime = TimeStamp::Now(); 5866 TimeDuration savedTime = currentTime - mOnStartRequestTimestamp; 5867 glean::network::race_cache_with_network_saved_time 5868 .AccumulateRawDuration(savedTime); 5869 5870 PROFILER_MARKER_TEXT( 5871 "RCWN", NETWORK, {}, 5872 nsPrintfCString("Network won by %" PRId64 "ms for %s", 5873 int64_t(savedTime.ToMilliseconds()), mSpec.get())); 5874 TimeDuration diffTime = currentTime - mOnCacheEntryCheckTimestamp; 5875 glean::network::race_cache_with_network_ocec_on_start_diff 5876 .AccumulateRawDuration(diffTime); 5877 } 5878 return NS_OK; 5879 } 5880 } 5881 5882 if (mCachedResponseHead) mResponseHead = std::move(mCachedResponseHead); 5883 5884 UpdateInhibitPersistentCachingFlag(); 5885 5886 // if we don't already have security info, try to get it from the cache 5887 // entry. there are two cases to consider here: 1) we are just reading 5888 // from the cache, or 2) this may be due to a 304 not modified response, 5889 // in which case we could have security info from a socket transport. 5890 if (!mSecurityInfo) mSecurityInfo = mCachedSecurityInfo; 5891 5892 nsresult rv; 5893 5894 // Keep the conditions below in sync with the conditions in 5895 // StartBufferingCachedEntity. 5896 5897 if (WillRedirect(*mResponseHead)) { 5898 // TODO: Bug 759040 - We should call HandleAsyncRedirect directly here, 5899 // to avoid event dispatching latency. 5900 MOZ_ASSERT(!mCacheInputStream); 5901 LOG(("Skipping skip read of cached redirect entity\n")); 5902 return AsyncCall(&nsHttpChannel::HandleAsyncRedirect); 5903 } 5904 5905 if ((mLoadFlags & LOAD_ONLY_IF_MODIFIED) && !LoadCachedContentIsPartial()) { 5906 LOG( 5907 ("Skipping read from cache based on LOAD_ONLY_IF_MODIFIED " 5908 "load flag\n")); 5909 MOZ_ASSERT(!mCacheInputStream); 5910 // TODO: Bug 759040 - We should call HandleAsyncNotModified directly 5911 // here, to avoid event dispatching latency. 5912 return AsyncCall(&nsHttpChannel::HandleAsyncNotModified); 5913 } 5914 5915 MOZ_ASSERT(mCacheInputStream); 5916 if (!mCacheInputStream) { 5917 NS_ERROR( 5918 "mCacheInputStream is null but we're expecting to " 5919 "be able to read from it."); 5920 return NS_ERROR_UNEXPECTED; 5921 } 5922 5923 nsCOMPtr<nsIInputStream> inputStream = mCacheInputStream.forget(); 5924 5925 rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump), inputStream, 0, 0, 5926 true); 5927 if (NS_FAILED(rv)) { 5928 inputStream->Close(); 5929 return rv; 5930 } 5931 5932 rv = mCachePump->AsyncRead(this); 5933 if (NS_FAILED(rv)) return rv; 5934 5935 uint32_t suspendCount = mSuspendCount; 5936 if (LoadAsyncResumePending()) { 5937 LOG( 5938 (" Suspend()'ing cache pump once because of async resume pending" 5939 ", sc=%u, pump=%p, this=%p", 5940 suspendCount, mCachePump.get(), this)); 5941 ++suspendCount; 5942 } 5943 while (suspendCount--) { 5944 mCachePump->Suspend(); 5945 } 5946 5947 return NS_OK; 5948 } 5949 5950 void nsHttpChannel::CloseCacheEntry(bool doomOnFailure) { 5951 mCacheInputStream.CloseAndRelease(); 5952 5953 if (!mCacheEntry) return; 5954 5955 LOG(("nsHttpChannel::CloseCacheEntry [this=%p] mStatus=%" PRIx32 5956 " CacheEntryIsWriteOnly=%x", 5957 this, static_cast<uint32_t>(static_cast<nsresult>(mStatus)), 5958 LoadCacheEntryIsWriteOnly())); 5959 5960 // If we have begun to create or replace a cache entry, and that cache 5961 // entry is not complete and not resumable, then it needs to be doomed. 5962 // Otherwise, CheckCache will make the mistake of thinking that the 5963 // partial cache entry is complete. 5964 5965 bool doom = false; 5966 if (LoadInitedCacheEntry()) { 5967 MOZ_ASSERT(mResponseHead, "oops"); 5968 if (NS_FAILED(mStatus) && doomOnFailure && LoadCacheEntryIsWriteOnly() && 5969 !mResponseHead->IsResumable()) { 5970 doom = true; 5971 } 5972 } else if (LoadCacheEntryIsWriteOnly()) { 5973 doom = true; 5974 } 5975 5976 if (doom) { 5977 LOG((" dooming cache entry!!")); 5978 mCacheEntry->AsyncDoom(nullptr); 5979 } else { 5980 // Store updated security info, makes cached EV status race less likely 5981 // (see bug 1040086) 5982 if (mSecurityInfo) { 5983 mCacheEntry->SetSecurityInfo(mSecurityInfo); 5984 } 5985 } 5986 5987 mCachedResponseHead = nullptr; 5988 5989 mCachePump = nullptr; 5990 // This releases the entry for other consumers to use. 5991 // We call Dismiss() in case someone still keeps a reference 5992 // to this entry handle. 5993 mCacheEntry->Dismiss(); 5994 mCacheEntry = nullptr; 5995 StoreCacheEntryIsWriteOnly(false); 5996 StoreInitedCacheEntry(false); 5997 } 5998 5999 void nsHttpChannel::MaybeCreateCacheEntryWhenRCWN() { 6000 mozilla::MutexAutoLock lock(mRCWNLock); 6001 6002 // Create cache entry for writing only when we're racing cache with network 6003 // and we don't have the entry because network won. 6004 if (mCacheEntry || !mRaceCacheWithNetwork || 6005 mFirstResponseSource != RESPONSE_FROM_NETWORK || 6006 LoadCacheEntryIsReadOnly()) { 6007 return; 6008 } 6009 6010 LOG(("nsHttpChannel::MaybeCreateCacheEntryWhenRCWN [this=%p]", this)); 6011 6012 nsCOMPtr<nsICacheStorageService> cacheStorageService( 6013 components::CacheStorage::Service()); 6014 if (!cacheStorageService) { 6015 return; 6016 } 6017 6018 nsCOMPtr<nsICacheStorage> cacheStorage; 6019 RefPtr<LoadContextInfo> info = GetLoadContextInfo(this); 6020 (void)cacheStorageService->DiskCacheStorage(info, 6021 getter_AddRefs(cacheStorage)); 6022 if (!cacheStorage) { 6023 return; 6024 } 6025 6026 (void)cacheStorage->OpenTruncate(mCacheEntryURI, mCacheIdExtension, 6027 getter_AddRefs(mCacheEntry)); 6028 6029 LOG((" created entry %p", mCacheEntry.get())); 6030 6031 if (AwaitingCacheCallbacks()) { 6032 // Setting mIgnoreCacheEntry to true ensures that we won't close this 6033 // write-only entry in OnCacheEntryAvailable() if this method was called 6034 // after OnCacheEntryCheck(). 6035 mIgnoreCacheEntry = true; 6036 } 6037 6038 mAvailableCachedAltDataType.Truncate(); 6039 StoreDeliveringAltData(false); 6040 mAltDataLength = -1; 6041 mCacheInputStream.CloseAndRelease(); 6042 StoreCachedContentIsValid(CachedContentValidity::Invalid); 6043 } 6044 6045 // Initialize the cache entry for writing. 6046 // - finalize storage policy 6047 // - store security info 6048 // - update expiration time 6049 // - store headers and other meta data 6050 nsresult nsHttpChannel::InitCacheEntry() { 6051 nsresult rv; 6052 6053 NS_ENSURE_TRUE(mCacheEntry, NS_ERROR_UNEXPECTED); 6054 // if only reading, nothing to be done here. 6055 if (LoadCacheEntryIsReadOnly()) return NS_OK; 6056 6057 // Don't cache the response again if already cached... 6058 if (CachedContentIsValid()) return NS_OK; 6059 6060 LOG(("nsHttpChannel::InitCacheEntry [this=%p entry=%p]\n", this, 6061 mCacheEntry.get())); 6062 6063 bool recreate = !LoadCacheEntryIsWriteOnly(); 6064 bool dontPersist = mLoadFlags & INHIBIT_PERSISTENT_CACHING; 6065 6066 if (!recreate && dontPersist) { 6067 // If the current entry is persistent but we inhibit peristence 6068 // then force recreation of the entry as memory/only. 6069 rv = mCacheEntry->GetPersistent(&recreate); 6070 if (NS_FAILED(rv)) return rv; 6071 } 6072 6073 if (recreate) { 6074 LOG( 6075 (" we have a ready entry, but reading it again from the server -> " 6076 "recreating cache entry\n")); 6077 // clean the altData cache and reset this to avoid wrong content length 6078 mAvailableCachedAltDataType.Truncate(); 6079 StoreDeliveringAltData(false); 6080 6081 nsCOMPtr<nsICacheEntry> currentEntry; 6082 currentEntry.swap(mCacheEntry); 6083 rv = currentEntry->Recreate(dontPersist, getter_AddRefs(mCacheEntry)); 6084 if (NS_FAILED(rv)) { 6085 LOG((" recreation failed, the response will not be cached")); 6086 return NS_OK; 6087 } 6088 6089 StoreCacheEntryIsWriteOnly(true); 6090 } 6091 6092 // Set the expiration time for this cache entry 6093 rv = UpdateExpirationTime(); 6094 if (NS_FAILED(rv)) return rv; 6095 6096 // mark this weakly framed until a response body is seen 6097 mCacheEntry->SetMetaDataElement("strongly-framed", "0"); 6098 6099 rv = AddCacheEntryHeaders(mCacheEntry, false); 6100 if (NS_FAILED(rv)) return rv; 6101 6102 StoreInitedCacheEntry(true); 6103 6104 // Don't perform the check when writing (doesn't make sense) 6105 StoreConcurrentCacheAccess(0); 6106 6107 return NS_OK; 6108 } 6109 6110 void nsHttpChannel::UpdateInhibitPersistentCachingFlag() { 6111 // The no-store directive within the 'Cache-Control:' header indicates 6112 // that we must not store the response in a persistent cache. 6113 if (mResponseHead->NoStore()) { 6114 mLoadFlags |= INHIBIT_PERSISTENT_CACHING; 6115 return; 6116 } 6117 6118 if (!StaticPrefs::network_cache_persist_permanent_redirects_http() && 6119 mURI->SchemeIs("http") && 6120 nsHttp::IsPermanentRedirect(mResponseHead->Status())) { 6121 mLoadFlags |= INHIBIT_PERSISTENT_CACHING; 6122 return; 6123 } 6124 6125 // Only cache SSL content on disk if the pref is set 6126 if (!gHttpHandler->IsPersistentHttpsCachingEnabled() && 6127 mURI->SchemeIs("https")) { 6128 mLoadFlags |= INHIBIT_PERSISTENT_CACHING; 6129 } 6130 } 6131 6132 nsresult DoAddCacheEntryHeaders(nsHttpChannel* self, nsICacheEntry* entry, 6133 nsHttpRequestHead* requestHead, 6134 nsHttpResponseHead* responseHead, 6135 nsITransportSecurityInfo* securityInfo, 6136 bool aModified) { 6137 nsresult rv; 6138 6139 LOG(("nsHttpChannel::AddCacheEntryHeaders [this=%p] begin", self)); 6140 // Store secure data in memory only 6141 if (securityInfo) { 6142 entry->SetSecurityInfo(securityInfo); 6143 } 6144 6145 // Note: if aModified == false, then we're processing a 304 Not Modified, 6146 // and we *shouldn't* have any change to the Dictionary (and won't be 6147 // replacing the DictionaryEntry, though if the Match/Match-dest/Id/Type 6148 // changed, we may need to rewrite it. XXX? 6149 6150 // Store the HTTP request method with the cache entry so we can distinguish 6151 // for example GET and HEAD responses. 6152 nsAutoCString method; 6153 requestHead->Method(method); 6154 rv = entry->SetMetaDataElement("request-method", method.get()); 6155 if (NS_FAILED(rv)) return rv; 6156 6157 // Store the HTTP authorization scheme used if any... 6158 rv = StoreAuthorizationMetaData(entry, requestHead); 6159 if (NS_FAILED(rv)) return rv; 6160 6161 rv = self->UpdateCacheEntryHeaders(entry, nullptr); 6162 return rv; 6163 } 6164 6165 nsresult nsHttpChannel::AddCacheEntryHeaders(nsICacheEntry* entry, 6166 bool aModified) { 6167 return DoAddCacheEntryHeaders(this, entry, &mRequestHead, mResponseHead.get(), 6168 mSecurityInfo, aModified); 6169 } 6170 6171 nsresult nsHttpChannel::UpdateCacheEntryHeaders(nsICacheEntry* entry, 6172 const nsHttpAtom* aAtom) { 6173 nsresult rv = NS_OK; 6174 6175 // Iterate over the headers listed in the Vary response header, and 6176 // store the value of the corresponding request header so we can verify 6177 // that it has not varied when we try to re-use the cached response at 6178 // a later time. Take care to store "Cookie" headers only as hashes 6179 // due to security considerations and the fact that they can be pretty 6180 // large (bug 468426). We take care of "Vary: cookie" in ResponseWouldVary. 6181 // 6182 // NOTE: if "Vary: accept, cookie", then we will store the "accept" header 6183 // in the cache. we could try to avoid needlessly storing the "accept" 6184 // header in this case, but it doesn't seem worth the extra code to perform 6185 // the check. 6186 { 6187 nsAutoCString buf, metaKey; 6188 (void)mResponseHead->GetHeader(nsHttp::Vary, buf); 6189 6190 constexpr auto prefix = "request-"_ns; 6191 6192 for (const nsACString& token : 6193 nsCCharSeparatedTokenizer(buf, NS_HTTP_HEADER_SEP).ToRange()) { 6194 LOG( 6195 ("nsHttpChannel::ProcessVaryCacheEntryHeaders [this=%p] " 6196 "processing %s", 6197 this, nsPromiseFlatCString(token).get())); 6198 if (!token.EqualsLiteral("*")) { 6199 nsHttpAtom atom = nsHttp::ResolveAtom(token); 6200 if (!aAtom || atom == *aAtom) { 6201 nsAutoCString val; 6202 nsAutoCString hash; 6203 if (NS_SUCCEEDED(mRequestHead.GetHeader(atom, val))) { 6204 // If cookie-header, store a hash of the value 6205 if (atom == nsHttp::Cookie) { 6206 LOG( 6207 ("nsHttpChannel::ProcessVaryCacheEntryHeaders [this=%p] " 6208 "cookie-value %s", 6209 this, val.get())); 6210 rv = Hash(val.get(), hash); 6211 // If hash failed, store a string not very likely 6212 // to be the result of subsequent hashes 6213 if (NS_FAILED(rv)) { 6214 val = "<hash failed>"_ns; 6215 } else { 6216 val = hash; 6217 } 6218 6219 LOG((" hashed to %s\n", val.get())); 6220 } 6221 6222 // build cache meta data key and set meta data element... 6223 metaKey = prefix + token; 6224 entry->SetMetaDataElement(metaKey.get(), val.get()); 6225 } else { 6226 LOG( 6227 ("nsHttpChannel::ProcessVaryCacheEntryHeaders [this=%p] " 6228 "clearing metadata for %s", 6229 this, nsPromiseFlatCString(token).get())); 6230 metaKey = prefix + token; 6231 entry->SetMetaDataElement(metaKey.get(), nullptr); 6232 } 6233 } 6234 } 6235 } 6236 } 6237 if (NS_FAILED(rv)) { 6238 return rv; 6239 } 6240 // Store the received HTTP head with the cache entry as an element of 6241 // the meta data. 6242 nsAutoCString head; 6243 mResponseHead->Flatten(head, true); 6244 rv = entry->SetMetaDataElement("response-head", head.get()); 6245 if (NS_FAILED(rv)) return rv; 6246 head.Truncate(); 6247 mResponseHead->FlattenNetworkOriginalHeaders(head); 6248 rv = entry->SetMetaDataElement("original-response-headers", head.get()); 6249 if (NS_FAILED(rv)) return rv; 6250 6251 // Indicate we have successfully finished setting metadata on the cache 6252 // entry. 6253 return entry->MetaDataReady(); 6254 } 6255 6256 bool nsHttpChannel::ParseDictionary(nsICacheEntry* aEntry, 6257 nsHttpResponseHead* aResponseHead, 6258 bool aModified) { 6259 nsAutoCString val; 6260 if (NS_SUCCEEDED(aResponseHead->GetHeader(nsHttp::Use_As_Dictionary, val))) { 6261 nsAutoCStringN<128> matchVal; 6262 nsAutoCStringN<64> matchIdVal; 6263 nsTArray<nsCString> matchDestItems; 6264 nsAutoCString typeVal; 6265 6266 if (!NS_ParseUseAsDictionary(val, matchVal, matchIdVal, matchDestItems, 6267 typeVal)) { 6268 return false; 6269 } 6270 6271 nsCString key; 6272 nsresult rv; 6273 if (NS_FAILED(rv = aEntry->GetKey(key))) { 6274 return false; 6275 } 6276 6277 // Verify if the matchVal has regexp groups. If so, reject it 6278 UrlpPattern pattern; 6279 UrlpOptions options{}; 6280 if (!urlp_parse_pattern_from_string(&matchVal, &mSpec, options, &pattern)) { 6281 LOG_DICTIONARIES( 6282 ("Failed to parse dictionary pattern %s", matchVal.get())); 6283 return false; 6284 } 6285 if (urlp_get_has_regexp_groups(pattern)) { 6286 LOG_DICTIONARIES(("Pattern %s has regexp groups", matchVal.get())); 6287 return false; 6288 } 6289 6290 nsCString hash; 6291 // Available now for use 6292 RefPtr<DictionaryCache> dicts(DictionaryCache::GetInstance()); 6293 LOG_DICTIONARIES( 6294 ("Adding DictionaryCache entry for %s: key %s, matchval %s, id=%s, " 6295 "match-dest[0]=%s, type=%s", 6296 mSpec.get(), key.get(), matchVal.get(), matchIdVal.get(), 6297 matchDestItems.Length() > 0 ? matchDestItems[0].get() : "<none>", 6298 typeVal.get())); 6299 6300 uint32_t expTime = 0; 6301 (void)GetCacheTokenExpirationTime(&expTime); 6302 6303 dicts->AddEntry(mURI, key, matchVal, matchDestItems, matchIdVal, Some(hash), 6304 aModified, expTime, getter_AddRefs(mDictSaving)); 6305 // If this was 304 Not Modified, then we don't need the dictionary data 6306 // (though we may update the dictionary entry if the match/id/etc changed). 6307 // If this is 304, mDictSaving will be cleared by AddEntry. 6308 if (mDictSaving) { 6309 if (mDictSaving->ShouldSuspendUntilCacheRead()) { 6310 LOG_DICTIONARIES(("Suspending %p to wait for cache read", this)); 6311 mTransactionPump->Suspend(); 6312 mDictSaving->CallbackOnCacheRead([self = RefPtr(this)](nsresult) { 6313 LOG_DICTIONARIES(("Resuming %p after cache read", self.get())); 6314 self->Resume(); 6315 }); 6316 } 6317 } 6318 return true; 6319 } 6320 return true; // succeeded, no use-as-dictionary 6321 } 6322 6323 inline void GetAuthType(const char* challenge, nsCString& authType) { 6324 const char* p; 6325 6326 // get the challenge type 6327 if ((p = strchr(challenge, ' ')) != nullptr) { 6328 authType.Assign(challenge, p - challenge); 6329 } else { 6330 authType.Assign(challenge); 6331 } 6332 } 6333 6334 nsresult StoreAuthorizationMetaData(nsICacheEntry* entry, 6335 nsHttpRequestHead* requestHead) { 6336 // Not applicable to proxy authorization... 6337 nsAutoCString val; 6338 if (NS_FAILED(requestHead->GetHeader(nsHttp::Authorization, val))) { 6339 return NS_OK; 6340 } 6341 6342 // eg. [Basic realm="wally world"] 6343 nsAutoCString buf; 6344 GetAuthType(val.get(), buf); 6345 return entry->SetMetaDataElement("auth", buf.get()); 6346 } 6347 6348 // Finalize the cache entry 6349 // - may need to rewrite response headers if any headers changed 6350 // - may need to recalculate the expiration time if any headers changed 6351 // - called only for freshly written cache entries 6352 nsresult nsHttpChannel::FinalizeCacheEntry() { 6353 LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p]\n", this)); 6354 6355 // Don't update this meta-data on 304 6356 if (LoadStronglyFramed() && !CachedContentIsValid() && mCacheEntry) { 6357 LOG(("nsHttpChannel::FinalizeCacheEntry [this=%p] Is Strongly Framed\n", 6358 this)); 6359 mCacheEntry->SetMetaDataElement("strongly-framed", "1"); 6360 } 6361 6362 if (mResponseHead && LoadResponseHeadersModified()) { 6363 // Set the expiration time for this cache entry 6364 nsresult rv = UpdateExpirationTime(); 6365 if (NS_FAILED(rv)) return rv; 6366 } 6367 return NS_OK; 6368 } 6369 6370 nsresult nsHttpChannel::InstallCacheListener(int64_t offset) { 6371 return DoInstallCacheListener(false, nullptr, offset); 6372 } 6373 6374 // Open an output stream to the cache entry and insert a listener tee into 6375 // the chain of response listeners, so the data will go the cache and the 6376 // normal listener chain, which often will eventually include a 6377 // decompressor. If the Content-Encoding is dcb or dcz, we'll include a 6378 // decompressor *before* the tee, so the cache will see decompressed data 6379 // (we can't decompress dcb/dcz when reading from the cache). Also, if an 6380 // entry is being used as a dictionary (Use-As-Dictionary), we want the data 6381 // to in the cache to be decompressed, so we should install a decompressor 6382 // before the tee as well. 6383 nsresult nsHttpChannel::DoInstallCacheListener(bool aIsDictionaryCompressed, 6384 nsACString* aDictionary, 6385 int64_t offset) { 6386 nsresult rv; 6387 6388 LOG(("Preparing to write data into the cache [uri=%s]\n", mSpec.get())); 6389 6390 MOZ_ASSERT(mCacheEntry); 6391 MOZ_ASSERT(LoadCacheEntryIsWriteOnly() || LoadCachedContentIsPartial() || 6392 mRaceCacheWithNetwork); 6393 MOZ_ASSERT(mListener); 6394 6395 LOG(("Trading cache input stream for output stream [channel=%p]", this)); 6396 6397 // We must close the input stream first because cache entries do not 6398 // correctly handle having an output stream and input streams open at 6399 // the same time. 6400 mCacheInputStream.CloseAndRelease(); 6401 6402 int64_t predictedSize = mResponseHead->TotalEntitySize(); 6403 if (predictedSize != -1) { 6404 predictedSize -= offset; 6405 } 6406 6407 nsCOMPtr<nsIOutputStream> out; 6408 rv = 6409 mCacheEntry->OpenOutputStream(offset, predictedSize, getter_AddRefs(out)); 6410 if (rv == NS_ERROR_NOT_AVAILABLE) { 6411 LOG((" entry doomed, not writing it [channel=%p]", this)); 6412 // Entry is already doomed. 6413 // This may happen when expiration time is set to past and the entry 6414 // has been removed by the background eviction logic. 6415 return NS_OK; 6416 } 6417 if (rv == NS_ERROR_FILE_TOO_BIG) { 6418 LOG((" entry would exceed max allowed size, not writing it [channel=%p]", 6419 this)); 6420 mCacheEntry->AsyncDoom(nullptr); 6421 return NS_OK; 6422 } 6423 if (NS_FAILED(rv)) return rv; 6424 6425 if (LoadCacheOnlyMetadata()) { 6426 LOG(("Not storing content, cacheOnlyMetadata set")); 6427 // We must open and then close the output stream of the cache entry. 6428 // This way we indicate the content has been written (despite with zero 6429 // length) and the entry is now in the ready state with "having data". 6430 6431 out->Close(); 6432 return NS_OK; 6433 } 6434 6435 // XXX disk cache does not support overlapped i/o yet 6436 #if 0 6437 // Mark entry valid inorder to allow simultaneous reading... 6438 rv = mCacheEntry->MarkValid(); 6439 if (NS_FAILED(rv)) return rv; 6440 #endif 6441 6442 nsCOMPtr<nsIStreamListenerTee> tee = 6443 do_CreateInstance(kStreamListenerTeeCID, &rv); 6444 if (NS_FAILED(rv)) return rv; 6445 6446 rv = tee->Init(mListener, out, nullptr); 6447 LOG(("nsHttpChannel::InstallCacheListener sync tee %p rv=%" PRIx32, tee.get(), 6448 static_cast<uint32_t>(rv))); 6449 if (NS_FAILED(rv)) return rv; 6450 mListener = tee; 6451 6452 mWritingToCache = true; 6453 // If this is Use-As-Dictionary we need to be able to read it quickly for 6454 // dictionary use, OR if it's encoded in dcb or dcz (using a dictionary), 6455 // we must decompress it before storing since we won't have the dictionary 6456 // when we go to read it out later. 6457 // In this case, we hook an nsHTTPCompressConv instance in before the tee 6458 // since we don't want to have to decompress it here and again in the content 6459 // process (if it's not dcb/dcz); if it is dcb/dcz we must decompress it 6460 // before the content process gets to see it 6461 // XXX We could recompress this with e.g. gzip to save space and improve 6462 // hitrate, at the cost of some CPU. 6463 6464 // Note: this doesn't handle cases like "dcb, gzip" or (worse?) "gzip, dcb". 6465 // We could in theory handle them. 6466 if (aDictionary || aIsDictionaryCompressed) { 6467 nsCOMPtr<nsIStreamListener> listener; 6468 // otherwise we won't convert in the parent process 6469 SetApplyConversion(true); 6470 rv = DoApplyContentConversionsInternal(mListener, getter_AddRefs(listener), 6471 true, nullptr); 6472 if (NS_FAILED(rv)) { 6473 return rv; 6474 } 6475 // Remove Available-Dictionary from Vary header if present. This 6476 // avoids us refusing to match on a future load, for example if this 6477 // dictionary was decoded from an earlier version using a dictionary 6478 // (i.e. the update jquery to new version using the old version as a 6479 // dictionary; no future load will use that old version). 6480 6481 // XXX It would be slightly more efficient to remove all at once 6482 // instead of sequentially by passing an array of strings 6483 RemoveFromVary(mResponseHead.get(), "available-dictionary"_ns); 6484 RemoveFromVary(mResponseHead.get(), "accept-encoding"_ns); 6485 6486 if (listener) { 6487 LOG_DICTIONARIES( 6488 ("Installed nsHTTPCompressConv %p before tee", listener.get())); 6489 mListener = listener; 6490 mCompressListener = listener; 6491 StoreHasAppliedConversion(true); 6492 6493 } else { 6494 LOG_DICTIONARIES(("Didn't install decompressor before tee")); 6495 } 6496 // We may have modified Content-Encoding; make sure cache metadata 6497 // reflects that. Pass nullptr so we pick up the Vary updates above 6498 rv = UpdateCacheEntryHeaders(mCacheEntry, nullptr); 6499 if (NS_FAILED(rv)) { 6500 mCacheEntry->AsyncDoom(nullptr); 6501 return rv; 6502 } 6503 } 6504 6505 return NS_OK; 6506 } 6507 6508 //----------------------------------------------------------------------------- 6509 // nsHttpChannel <redirect> 6510 //----------------------------------------------------------------------------- 6511 6512 nsresult nsHttpChannel::SetupReplacementChannel(nsIURI* newURI, 6513 nsIChannel* newChannel, 6514 bool preserveMethod, 6515 uint32_t redirectFlags) { 6516 LOG( 6517 ("nsHttpChannel::SetupReplacementChannel " 6518 "[this=%p newChannel=%p preserveMethod=%d]", 6519 this, newChannel, preserveMethod)); 6520 6521 if (!mEndMarkerAdded && profiler_thread_is_being_profiled_for_markers()) { 6522 mEndMarkerAdded = true; 6523 6524 nsAutoCString requestMethod; 6525 GetRequestMethod(requestMethod); 6526 6527 int32_t priority = PRIORITY_NORMAL; 6528 GetPriority(&priority); 6529 6530 TimingStruct timings; 6531 if (mTransaction) { 6532 timings = mTransaction->Timings(); 6533 } 6534 6535 uint64_t size = 0; 6536 GetEncodedBodySize(&size); 6537 6538 nsAutoCString contentType; 6539 mozilla::Maybe<mozilla::net::HttpVersion> httpVersion = Nothing(); 6540 mozilla::Maybe<uint32_t> responseStatus = Nothing(); 6541 if (mResponseHead) { 6542 mResponseHead->ContentType(contentType); 6543 httpVersion = Some(mResponseHead->Version()); 6544 responseStatus = Some(mResponseHead->Status()); 6545 } 6546 6547 RefPtr<nsIIdentChannel> newIdentChannel = do_QueryObject(newChannel); 6548 uint64_t channelId = 0; 6549 if (newIdentChannel) { 6550 channelId = newIdentChannel->ChannelId(); 6551 } 6552 profiler_add_network_marker( 6553 mURI, requestMethod, priority, mChannelId, 6554 NetworkLoadType::LOAD_REDIRECT, mLastStatusReported, TimeStamp::Now(), 6555 size, mCacheDisposition, mLoadInfo->GetInnerWindowID(), 6556 mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus, 6557 &timings, std::move(mSource), httpVersion, responseStatus, 6558 Some(nsDependentCString(contentType.get())), newURI, redirectFlags, 6559 channelId); 6560 } 6561 6562 nsresult rv = HttpBaseChannel::SetupReplacementChannel( 6563 newURI, newChannel, preserveMethod, redirectFlags); 6564 if (NS_FAILED(rv)) return rv; 6565 6566 nsAutoCString uriHost; 6567 mURI->GetAsciiHost(uriHost); 6568 // disable https-rr when encountering a downgrade from https to http. 6569 // If the host would have https-rr dns-entries, it would be misconfigured 6570 // due to giving us mixed signals: 6571 // 1. the signal to upgrade all http requests to https, 6572 // 2. but also downgrading to http on https via redirects. 6573 // Add to exclude list for that reason 6574 if (!gHttpHandler->IsHostExcludedForHTTPSRR(uriHost) && 6575 nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop( 6576 mURI, newURI, mLoadInfo, 6577 {nsHTTPSOnlyUtils::UpgradeDowngradeEndlessLoopOptions:: 6578 EnforceForHTTPSRR})) { 6579 // Add the host to a excluded list because: 6580 // 1. We don't need to do the same check again. 6581 // 2. Other subresources in the same host will be also excluded. 6582 gHttpHandler->ExcludeHTTPSRRHost(uriHost); 6583 LOG(("[%p] skip HTTPS upgrade for host [%s]", this, uriHost.get())); 6584 } 6585 6586 rv = CheckRedirectLimit(newURI, redirectFlags); 6587 NS_ENSURE_SUCCESS(rv, rv); 6588 6589 // pass on the early hint observer to be able to process `103 Early Hints` 6590 // responses after cross origin redirects 6591 if (mEarlyHintObserver) { 6592 if (RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(newChannel)) { 6593 httpChannelImpl->SetEarlyHintObserver(mEarlyHintObserver); 6594 } 6595 mEarlyHintObserver = nullptr; 6596 } 6597 6598 // We don't support redirection for WebTransport for now. 6599 mWebTransportSessionEventListener = nullptr; 6600 6601 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel); 6602 if (!httpChannel) return NS_OK; // no other options to set 6603 6604 // convey the ApplyConversion flag (bug 91862) 6605 nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel); 6606 if (encodedChannel) encodedChannel->SetApplyConversion(LoadApplyConversion()); 6607 6608 // transfer the resume information 6609 if (LoadResuming()) { 6610 nsCOMPtr<nsIResumableChannel> resumableChannel( 6611 do_QueryInterface(newChannel)); 6612 if (!resumableChannel) { 6613 NS_WARNING( 6614 "Got asked to resume, but redirected to non-resumable channel!"); 6615 return NS_ERROR_NOT_RESUMABLE; 6616 } 6617 resumableChannel->ResumeAt(mStartPos, mEntityID); 6618 } 6619 6620 nsCOMPtr<nsIHttpChannelInternal> internalChannel = 6621 do_QueryInterface(newChannel, &rv); 6622 if (NS_SUCCEEDED(rv)) { 6623 TimeStamp timestamp; 6624 rv = GetNavigationStartTimeStamp(×tamp); 6625 if (NS_WARN_IF(NS_FAILED(rv))) { 6626 return rv; 6627 } 6628 if (timestamp) { 6629 (void)internalChannel->SetNavigationStartTimeStamp(timestamp); 6630 } 6631 } 6632 6633 return NS_OK; 6634 } 6635 6636 nsresult nsHttpChannel::AsyncProcessRedirection(uint32_t redirectType) { 6637 LOG(("nsHttpChannel::AsyncProcessRedirection [this=%p type=%u]\n", this, 6638 redirectType)); 6639 6640 nsresult rv = ProcessCrossOriginSecurityHeaders(); 6641 if (NS_FAILED(rv)) { 6642 mStatus = rv; 6643 HandleAsyncAbort(); 6644 return rv; 6645 } 6646 6647 nsAutoCString location; 6648 6649 // if a location header was not given, then we can't perform the redirect, 6650 // so just carry on as though this were a normal response. 6651 if (NS_FAILED(mResponseHead->GetHeader(nsHttp::Location, location))) { 6652 return NS_ERROR_FAILURE; 6653 } 6654 6655 // If we were told to not follow redirects automatically, then again 6656 // carry on as though this were a normal response. 6657 if (mLoadInfo->GetDontFollowRedirects()) { 6658 return NS_ERROR_FAILURE; 6659 } 6660 6661 // make sure non-ASCII characters in the location header are escaped. 6662 nsAutoCString locationBuf; 6663 if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces, 6664 locationBuf)) { 6665 location = locationBuf; 6666 } 6667 6668 mRedirectType = redirectType; 6669 6670 LOG(("redirecting to: %s [redirection-limit=%u]\n", location.get(), 6671 uint32_t(mRedirectionLimit))); 6672 6673 rv = CreateNewURI(location.get(), getter_AddRefs(mRedirectURI)); 6674 6675 if (NS_FAILED(rv)) { 6676 LOG(("Invalid URI for redirect: Location: %s\n", location.get())); 6677 return NS_ERROR_CORRUPTED_CONTENT; 6678 } 6679 6680 if (!StaticPrefs::network_allow_redirect_to_data() && 6681 !mLoadInfo->GetAllowInsecureRedirectToDataURI() && 6682 mRedirectURI->SchemeIs("data")) { 6683 LOG(("Invalid data URI for redirect!")); 6684 nsContentSecurityManager::ReportBlockedDataURI(mRedirectURI, mLoadInfo, 6685 true); 6686 return NS_ERROR_DOM_BAD_URI; 6687 } 6688 6689 // Perform the URL query string stripping for redirects. We will only strip 6690 // the query string if it is redirecting to a third-party URI in the top 6691 // level. 6692 if (StaticPrefs::privacy_query_stripping_redirect()) { 6693 ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance(); 6694 bool isThirdPartyRedirectURI = true; 6695 thirdPartyUtil->IsThirdPartyURI(mURI, mRedirectURI, 6696 &isThirdPartyRedirectURI); 6697 if (isThirdPartyRedirectURI && mLoadInfo->GetExternalContentPolicyType() == 6698 ExtContentPolicy::TYPE_DOCUMENT) { 6699 glean::contentblocking::query_stripping_count 6700 .EnumGet(glean::contentblocking::QueryStrippingCountLabel::eRedirect) 6701 .Add(); 6702 6703 nsCOMPtr<nsIPrincipal> prin; 6704 ContentBlockingAllowList::RecomputePrincipal( 6705 mRedirectURI, mLoadInfo->GetOriginAttributes(), getter_AddRefs(prin)); 6706 6707 bool isRedirectURIInAllowList = false; 6708 if (prin) { 6709 ContentBlockingAllowList::Check(prin, mPrivateBrowsing, 6710 isRedirectURIInAllowList); 6711 } 6712 6713 if (!isRedirectURIInAllowList) { 6714 nsCOMPtr<nsIURI> strippedURI; 6715 6716 nsCOMPtr<nsIURLQueryStringStripper> queryStripper; 6717 queryStripper = 6718 mozilla::components::URLQueryStringStripper::Service(&rv); 6719 NS_ENSURE_SUCCESS(rv, rv); 6720 6721 uint32_t numStripped; 6722 6723 rv = queryStripper->Strip(mRedirectURI, mPrivateBrowsing, 6724 getter_AddRefs(strippedURI), &numStripped); 6725 NS_ENSURE_SUCCESS(rv, rv); 6726 6727 if (numStripped) { 6728 mUnstrippedRedirectURI = mRedirectURI; 6729 mRedirectURI = strippedURI; 6730 6731 // Record telemetry, but only if we stripped any query params. 6732 glean::contentblocking::query_stripping_count 6733 .EnumGet(glean::contentblocking::QueryStrippingCountLabel:: 6734 eStripforredirect) 6735 .Add(); 6736 glean::contentblocking::query_stripping_param_count 6737 .AccumulateSingleSample(numStripped); 6738 } 6739 } 6740 } 6741 } 6742 6743 // if we have a Set-Login header, we should try to handle it here 6744 nsAutoCString setLogin; 6745 if (NS_SUCCEEDED(mResponseHead->GetHeader(nsHttp::Set_Login, setLogin))) { 6746 bool isDocument = mLoadInfo->GetExternalContentPolicyType() == 6747 ExtContentPolicy::TYPE_DOCUMENT; 6748 if (isDocument) { 6749 auto ssm = nsContentUtils::GetSecurityManager(); 6750 if (ssm) { 6751 nsCOMPtr<nsIPrincipal> documentPrincipal; 6752 nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( 6753 this, getter_AddRefs(documentPrincipal)); 6754 dom::NavigatorLogin::SetLoginStatus(documentPrincipal, setLogin); 6755 } 6756 } else { 6757 bool inThirdPartyContext = mLoadInfo->GetIsInThirdPartyContext(); 6758 nsIPrincipal* loadingPrincipal = mLoadInfo->GetLoadingPrincipal(); 6759 if (loadingPrincipal) { 6760 bool isSameOriginToLoadingPrincipal = 6761 loadingPrincipal->IsSameOrigin(mURI); 6762 if (!inThirdPartyContext && isSameOriginToLoadingPrincipal) { 6763 dom::NavigatorLogin::SetLoginStatus(loadingPrincipal, setLogin); 6764 } 6765 } 6766 } 6767 } 6768 6769 if (NS_WARN_IF(!mRedirectURI)) { 6770 LOG(("Invalid redirect URI after performaing query string stripping")); 6771 return NS_ERROR_FAILURE; 6772 } 6773 6774 return ContinueProcessRedirectionAfterFallback(NS_OK); 6775 } 6776 6777 nsresult nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv) { 6778 // Kill the current cache entry if we are redirecting 6779 // back to ourself. 6780 bool redirectingBackToSameURI = false; 6781 if (mCacheEntry && LoadCacheEntryIsWriteOnly() && 6782 NS_SUCCEEDED(mURI->Equals(mRedirectURI, &redirectingBackToSameURI)) && 6783 redirectingBackToSameURI) { 6784 mCacheEntry->AsyncDoom(nullptr); 6785 } 6786 6787 // move the reference of the old location to the new one if the new 6788 // one has none. 6789 PropagateReferenceIfNeeded(mURI, mRedirectURI); 6790 6791 bool rewriteToGET = 6792 ShouldRewriteRedirectToGET(mRedirectType, mRequestHead.ParsedMethod()); 6793 6794 // prompt if the method is not safe (such as POST, PUT, DELETE, ...) 6795 if (!rewriteToGET && !mRequestHead.IsSafeMethod()) { 6796 rv = PromptTempRedirect(); 6797 if (NS_FAILED(rv)) return rv; 6798 } 6799 6800 uint32_t redirectFlags; 6801 if (nsHttp::IsPermanentRedirect(mRedirectType)) { 6802 redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT; 6803 } else { 6804 redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY; 6805 } 6806 6807 nsCOMPtr<nsIIOService> ioService; 6808 rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); 6809 if (NS_FAILED(rv)) return rv; 6810 6811 nsCOMPtr<nsIChannel> newChannel; 6812 nsCOMPtr<nsILoadInfo> redirectLoadInfo = 6813 CloneLoadInfoForRedirect(mRedirectURI, redirectFlags); 6814 6815 // Propagate the unstripped redirect URI. 6816 redirectLoadInfo->SetUnstrippedURI(mUnstrippedRedirectURI); 6817 6818 rv = NS_NewChannelInternal(getter_AddRefs(newChannel), mRedirectURI, 6819 redirectLoadInfo, 6820 nullptr, // PerformanceStorage 6821 nullptr, // aLoadGroup 6822 nullptr, // aCallbacks 6823 nsIRequest::LOAD_NORMAL, ioService); 6824 // If this fails, it usually means that the URI was invalid. Treat this as if 6825 // it were a CreateNewURI failure. 6826 if (NS_WARN_IF(NS_FAILED(rv))) { 6827 return NS_ERROR_CORRUPTED_CONTENT; 6828 } 6829 6830 rv = SetupReplacementChannel(mRedirectURI, newChannel, !rewriteToGET, 6831 redirectFlags); 6832 if (NS_FAILED(rv)) return rv; 6833 6834 // verify that this is a legal redirect 6835 mRedirectChannel = newChannel; 6836 6837 PushRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection); 6838 rv = gHttpHandler->AsyncOnChannelRedirect(this, newChannel, redirectFlags); 6839 6840 if (NS_SUCCEEDED(rv)) rv = WaitForRedirectCallback(); 6841 6842 if (NS_FAILED(rv)) { 6843 AutoRedirectVetoNotifier notifier(this, rv); 6844 PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection); 6845 } 6846 6847 return rv; 6848 } 6849 6850 nsresult nsHttpChannel::ContinueProcessRedirection(nsresult rv) { 6851 AutoRedirectVetoNotifier notifier(this, rv); 6852 6853 LOG(("nsHttpChannel::ContinueProcessRedirection [rv=%" PRIx32 ",this=%p]\n", 6854 static_cast<uint32_t>(rv), this)); 6855 if (NS_FAILED(rv)) return rv; 6856 6857 MOZ_ASSERT(mRedirectChannel, "No redirect channel?"); 6858 6859 // Make sure to do this after we received redirect veto answer, 6860 // i.e. after all sinks had been notified 6861 mRedirectChannel->SetOriginalURI(mOriginalURI); 6862 6863 // XXX we used to talk directly with the script security manager, but that 6864 // should really be handled by the event sink implementation. 6865 6866 // begin loading the new channel 6867 rv = mRedirectChannel->AsyncOpen(mListener); 6868 LOG((" new channel AsyncOpen returned %" PRIX32, static_cast<uint32_t>(rv))); 6869 NS_ENSURE_SUCCESS(rv, rv); 6870 6871 // close down this channel 6872 Cancel(NS_BINDING_REDIRECTED); 6873 6874 notifier.RedirectSucceeded(); 6875 6876 ReleaseListeners(); 6877 6878 return NS_OK; 6879 } 6880 6881 //----------------------------------------------------------------------------- 6882 // nsHttpChannel <auth> 6883 //----------------------------------------------------------------------------- 6884 6885 NS_IMETHODIMP nsHttpChannel::OnAuthAvailable() { 6886 LOG(("nsHttpChannel::OnAuthAvailable [this=%p]", this)); 6887 6888 // setting mAuthRetryPending flag and resuming the transaction 6889 // triggers process of throwing away the unauthenticated data already 6890 // coming from the network 6891 mIsAuthChannel = true; 6892 mAuthRetryPending = true; 6893 StoreProxyAuthPending(false); 6894 LOG(("Resuming the transaction, we got credentials from user")); 6895 if (mTransactionPump) { 6896 Resume(); 6897 } 6898 6899 if (StaticPrefs::network_auth_use_redirect_for_retries()) { 6900 return CallOrWaitForResume( 6901 [](auto* self) { return self->RedirectToNewChannelForAuthRetry(); }); 6902 } 6903 6904 return NS_OK; 6905 } 6906 6907 NS_IMETHODIMP nsHttpChannel::OnAuthCancelled(bool userCancel) { 6908 LOG(("nsHttpChannel::OnAuthCancelled [this=%p]", this)); 6909 MOZ_ASSERT(mAuthRetryPending, "OnAuthCancelled should not be called twice"); 6910 6911 if (mTransactionPump) { 6912 // If the channel is trying to authenticate to a proxy and 6913 // that was canceled we cannot show the http response body 6914 // from the 40x as that might mislead the user into thinking 6915 // it was a end host response instead of a proxy reponse. 6916 // This must check explicitly whether a proxy auth was being done 6917 // because we do want to show the content if this is an error from 6918 // the origin server. 6919 if (LoadProxyAuthPending()) Cancel(NS_ERROR_PROXY_CONNECTION_REFUSED); 6920 6921 // Make sure to process security headers before calling CallOnStartRequest. 6922 nsresult rv = ProcessCrossOriginSecurityHeaders(); 6923 if (NS_FAILED(rv)) { 6924 mStatus = rv; 6925 HandleAsyncAbort(); 6926 return rv; 6927 } 6928 6929 // ensure call of OnStartRequest of the current listener here, 6930 // it would not be called otherwise at all 6931 rv = CallOnStartRequest(); 6932 6933 // drop mAuthRetryPending flag and resume the transaction 6934 // this resumes load of the unauthenticated content data (which 6935 // may have been canceled if we don't want to show it) 6936 mAuthRetryPending = false; 6937 LOG(("Resuming the transaction, user cancelled the auth dialog")); 6938 Resume(); 6939 6940 if (NS_FAILED(rv)) mTransactionPump->Cancel(rv); 6941 } 6942 6943 StoreProxyAuthPending(false); 6944 return NS_OK; 6945 } 6946 6947 NS_IMETHODIMP nsHttpChannel::CloseStickyConnection() { 6948 LOG(("nsHttpChannel::CloseStickyConnection this=%p", this)); 6949 6950 // Require we are between OnStartRequest and OnStopRequest, because 6951 // what we do here takes effect in OnStopRequest (not reusing the 6952 // connection for next authentication round). 6953 if (!LoadIsPending()) { 6954 LOG((" channel not pending")); 6955 NS_ERROR( 6956 "CloseStickyConnection not called before OnStopRequest, won't have any " 6957 "effect"); 6958 return NS_ERROR_UNEXPECTED; 6959 } 6960 6961 MOZ_ASSERT(mTransaction); 6962 if (!mTransaction) { 6963 return NS_ERROR_UNEXPECTED; 6964 } 6965 6966 if (!(mCaps & NS_HTTP_STICKY_CONNECTION || 6967 mTransaction->HasStickyConnection())) { 6968 LOG((" not sticky")); 6969 return NS_OK; 6970 } 6971 6972 mTransaction->DontReuseConnection(); 6973 return NS_OK; 6974 } 6975 6976 NS_IMETHODIMP nsHttpChannel::ConnectionRestartable(bool aRestartable) { 6977 LOG(("nsHttpChannel::ConnectionRestartable this=%p, restartable=%d", this, 6978 aRestartable)); 6979 StoreAuthConnectionRestartable(aRestartable); 6980 return NS_OK; 6981 } 6982 6983 //----------------------------------------------------------------------------- 6984 // nsHttpChannel::nsISupports 6985 //----------------------------------------------------------------------------- 6986 6987 NS_IMPL_ADDREF_INHERITED(nsHttpChannel, HttpBaseChannel) 6988 NS_IMPL_RELEASE_INHERITED(nsHttpChannel, HttpBaseChannel) 6989 6990 NS_INTERFACE_MAP_BEGIN(nsHttpChannel) 6991 NS_INTERFACE_MAP_ENTRY(nsIRequest) 6992 NS_INTERFACE_MAP_ENTRY(nsIChannel) 6993 NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) 6994 NS_INTERFACE_MAP_ENTRY(nsIStreamListener) 6995 NS_INTERFACE_MAP_ENTRY(nsIHttpChannel) 6996 NS_INTERFACE_MAP_ENTRY(nsICacheInfoChannel) 6997 NS_INTERFACE_MAP_ENTRY(nsICachingChannel) 6998 NS_INTERFACE_MAP_ENTRY(nsIClassOfService) 6999 NS_INTERFACE_MAP_ENTRY(nsIUploadChannel) 7000 NS_INTERFACE_MAP_ENTRY(nsIFormPOSTActionChannel) 7001 NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2) 7002 NS_INTERFACE_MAP_ENTRY(nsICacheEntryOpenCallback) 7003 NS_INTERFACE_MAP_ENTRY(nsIHttpChannelInternal) 7004 NS_INTERFACE_MAP_ENTRY(nsIResumableChannel) 7005 NS_INTERFACE_MAP_ENTRY(nsITransportEventSink) 7006 NS_INTERFACE_MAP_ENTRY(nsISupportsPriority) 7007 NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback) 7008 NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel) 7009 NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel) 7010 NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback) 7011 NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest) 7012 NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener) 7013 NS_INTERFACE_MAP_ENTRY(nsIDNSListener) 7014 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 7015 NS_INTERFACE_MAP_ENTRY(nsICorsPreflightCallback) 7016 NS_INTERFACE_MAP_ENTRY(nsIRaceCacheWithNetwork) 7017 NS_INTERFACE_MAP_ENTRY(nsIRequestTailUnblockCallback) 7018 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsHttpChannel) 7019 NS_INTERFACE_MAP_ENTRY(nsIEarlyHintObserver) 7020 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel) 7021 7022 //----------------------------------------------------------------------------- 7023 // nsHttpChannel::nsIRequest 7024 //----------------------------------------------------------------------------- 7025 7026 NS_IMETHODIMP nsHttpChannel::SetCanceledReason(const nsACString& aReason) { 7027 return SetCanceledReasonImpl(aReason); 7028 } 7029 7030 NS_IMETHODIMP nsHttpChannel::GetCanceledReason(nsACString& aReason) { 7031 return GetCanceledReasonImpl(aReason); 7032 } 7033 7034 NS_IMETHODIMP 7035 nsHttpChannel::CancelWithReason(nsresult aStatus, const nsACString& aReason) { 7036 return CancelWithReasonImpl(aStatus, aReason); 7037 } 7038 7039 NS_IMETHODIMP 7040 nsHttpChannel::Cancel(nsresult status) { 7041 MOZ_ASSERT(NS_IsMainThread()); 7042 // We should never have a pump open while a CORS preflight is in progress. 7043 MOZ_ASSERT_IF(mPreflightChannel, !mCachePump); 7044 #ifdef DEBUG 7045 // We want to perform this check only when the chanel is being cancelled the 7046 // first time with a URL classifier blocking error code. If mStatus is 7047 // already set to such an error code then Cancel() may be called for some 7048 // other reason, for example because we've received notification about our 7049 // parent process side channel being canceled, in which case we cannot expect 7050 // that CancelByURLClassifier() would have handled this case. 7051 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status) && 7052 !UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(mStatus)) { 7053 MOZ_CRASH_UNSAFE_PRINTF("Blocking classifier error %" PRIx32 7054 " need to be handled by CancelByURLClassifier()", 7055 static_cast<uint32_t>(status)); 7056 } 7057 #endif 7058 7059 LOG(("nsHttpChannel::Cancel [this=%p status=%" PRIx32 ", reason=%s]\n", this, 7060 static_cast<uint32_t>(status), mCanceledReason.get())); 7061 PROFILER_MARKER("nsHttpChannel::Cancel", NETWORK, {}, FlowMarker, 7062 Flow::FromPointer(this)); 7063 7064 MOZ_ASSERT_IF(!(mConnectionInfo && mConnectionInfo->UsingConnect()) && 7065 NS_SUCCEEDED(mStatus), 7066 !AllowedErrorForTransactionRetry(status)); 7067 7068 mEarlyHintObserver = nullptr; 7069 mWebTransportSessionEventListener = nullptr; 7070 7071 if (mCanceled) { 7072 LOG((" ignoring; already canceled\n")); 7073 return NS_OK; 7074 } 7075 7076 LogCallingScriptLocation(this); 7077 7078 if (LoadWaitingForRedirectCallback()) { 7079 LOG(("channel canceled during wait for redirect callback")); 7080 } 7081 7082 return CancelInternal(status); 7083 } 7084 7085 NS_IMETHODIMP 7086 nsHttpChannel::CancelByURLClassifier(nsresult aErrorCode) { 7087 MOZ_ASSERT( 7088 UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode)); 7089 MOZ_ASSERT(NS_IsMainThread()); 7090 // We should never have a pump open while a CORS preflight is in progress. 7091 MOZ_ASSERT_IF(mPreflightChannel, !mCachePump); 7092 7093 LOG(("nsHttpChannel::CancelByURLClassifier [this=%p]\n", this)); 7094 7095 if (mCanceled) { 7096 LOG((" ignoring; already canceled\n")); 7097 return NS_OK; 7098 } 7099 7100 // We are being canceled by the channel classifier because of tracking 7101 // protection, but we haven't yet had a chance to dispatch the 7102 // "http-on-modify-request" notifications yet (this would normally be 7103 // done in PrepareToConnect()). So do that now, before proceeding to 7104 // cancel. 7105 // 7106 // Note that running these observers can itself result in the channel 7107 // being canceled. In that case, we accept that cancelation code as 7108 // the cause of the cancelation, as if the classification of the channel 7109 // would have occurred past this point! 7110 7111 // notify "http-on-modify-request" observers 7112 CallOnModifyRequestObservers(); 7113 7114 // Check if request was cancelled during on-modify-request 7115 if (mCanceled) { 7116 return mStatus; 7117 } 7118 7119 if (mSuspendCount) { 7120 LOG(("Waiting until resume in Cancel [this=%p]\n", this)); 7121 MOZ_ASSERT(!mCallOnResume); 7122 StoreChannelClassifierCancellationPending(1); 7123 mCallOnResume = [aErrorCode](nsHttpChannel* self) { 7124 self->HandleContinueCancellingByURLClassifier(aErrorCode); 7125 return NS_OK; 7126 }; 7127 return NS_OK; 7128 } 7129 7130 // Check to see if we should redirect this channel elsewhere by 7131 // nsIHttpChannel.redirectTo API request 7132 if (mAPIRedirectTo) { 7133 StoreChannelClassifierCancellationPending(1); 7134 return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect); 7135 } 7136 7137 return CancelInternal(aErrorCode); 7138 } 7139 7140 void nsHttpChannel::ContinueCancellingByURLClassifier(nsresult aErrorCode) { 7141 MOZ_ASSERT( 7142 UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(aErrorCode)); 7143 MOZ_ASSERT(NS_IsMainThread()); 7144 // We should never have a pump open while a CORS preflight is in progress. 7145 MOZ_ASSERT_IF(mPreflightChannel, !mCachePump); 7146 7147 LOG(("nsHttpChannel::ContinueCancellingByURLClassifier [this=%p]\n", this)); 7148 if (mCanceled) { 7149 LOG((" ignoring; already canceled\n")); 7150 return; 7151 } 7152 7153 // Check to see if we should redirect this channel elsewhere by 7154 // nsIHttpChannel.redirectTo API request 7155 if (mAPIRedirectTo) { 7156 (void)AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect); 7157 return; 7158 } 7159 7160 (void)CancelInternal(aErrorCode); 7161 } 7162 7163 nsresult nsHttpChannel::CancelInternal(nsresult status) { 7164 LOG(("nsHttpChannel::CancelInternal [this=%p]\n", this)); 7165 bool channelClassifierCancellationPending = 7166 !!LoadChannelClassifierCancellationPending(); 7167 if (UrlClassifierFeatureFactory::IsClassifierBlockingErrorCode(status)) { 7168 StoreChannelClassifierCancellationPending(0); 7169 } 7170 7171 mEarlyHintObserver = nullptr; 7172 mWebTransportSessionEventListener = nullptr; 7173 mCanceled = true; 7174 mStatus = NS_FAILED(status) ? status : NS_ERROR_ABORT; 7175 7176 // If we're waiting for LNA permission result and the channel is being 7177 // cancelled, we need to call OnPermissionPromptResult with permission denied 7178 // to resume the channel properly 7179 if (mWaitingForLNAPermission) { 7180 LOG( 7181 ("nsHttpChannel::CancelInternal [this=%p] cancelling while waiting for " 7182 "LNA permission, denying permission", 7183 this)); 7184 // Determine permission key dynamically from transaction 7185 const nsACString& permissionKey = 7186 (mTransaction && mTransaction->GetTargetIPAddressSpace() == 7187 nsILoadInfo::IPAddressSpace::Local) 7188 ? LOCAL_HOST_PERMISSION_KEY 7189 : LOCAL_NETWORK_PERMISSION_KEY; 7190 OnPermissionPromptResult(false, permissionKey); 7191 return NS_OK; 7192 } 7193 7194 if (LoadUsedNetwork() && !mReportedNEL) { 7195 MaybeGenerateNELReport(); 7196 } 7197 7198 // We don't want the content process to see any header values 7199 // when the request is blocked by ORB 7200 if (mChannelBlockedByOpaqueResponse && mCachedOpaqueResponseBlockingPref) { 7201 mResponseHead->ClearHeaders(); 7202 } 7203 7204 if (mLastStatusReported && !mEndMarkerAdded && 7205 profiler_thread_is_being_profiled_for_markers()) { 7206 // These do allocations/frees/etc; avoid if not active 7207 // mLastStatusReported can be null if Cancel is called before we added the 7208 // start marker. 7209 mEndMarkerAdded = true; 7210 7211 nsAutoCString requestMethod; 7212 GetRequestMethod(requestMethod); 7213 7214 int32_t priority = PRIORITY_NORMAL; 7215 GetPriority(&priority); 7216 7217 uint64_t size = 0; 7218 GetEncodedBodySize(&size); 7219 7220 profiler_add_network_marker( 7221 mURI, requestMethod, priority, mChannelId, NetworkLoadType::LOAD_CANCEL, 7222 mLastStatusReported, TimeStamp::Now(), size, mCacheDisposition, 7223 mLoadInfo->GetInnerWindowID(), 7224 mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus, 7225 &mTransactionTimings, std::move(mSource)); 7226 } 7227 7228 // If we don't have mTransactionPump and mCachePump, we need to call 7229 // AsyncAbort to make sure this channel's listener got notified. 7230 bool needAsyncAbort = !mTransactionPump && !mCachePump; 7231 7232 if (mProxyRequest) mProxyRequest->Cancel(status); 7233 CancelNetworkRequest(status); 7234 mCacheInputStream.CloseAndRelease(); 7235 if (mCachePump) mCachePump->Cancel(status); 7236 if (mAuthProvider) mAuthProvider->Cancel(status); 7237 if (mPreflightChannel) mPreflightChannel->Cancel(status); 7238 if (mRequestContext && mOnTailUnblock) { 7239 mOnTailUnblock = nullptr; 7240 mRequestContext->CancelTailedRequest(this); 7241 CloseCacheEntry(false); 7242 needAsyncAbort = false; 7243 (void)AsyncAbort(status); 7244 } else if (channelClassifierCancellationPending) { 7245 // If mCallOnResume is not null here, it's set in 7246 // nsHttpChannel::CancelByURLClassifier. We can override mCallOnResume since 7247 // mCanceled is true and nsHttpChannel::ContinueCancellingByURLClassifier 7248 // does nothing. 7249 if (mCallOnResume) { 7250 mCallOnResume = nullptr; 7251 } 7252 // If we're coming from an asynchronous path when canceling a channel due 7253 // to safe-browsing protection, we need to AsyncAbort the channel now. 7254 needAsyncAbort = false; 7255 (void)AsyncAbort(status); 7256 } 7257 7258 // If we already have mCallOnResume, AsyncAbort will be called in 7259 // ResumeInternal. 7260 if (needAsyncAbort && !mCallOnResume && !mSuspendCount) { 7261 LOG(("nsHttpChannel::CancelInternal do AsyncAbort [this=%p]\n", this)); 7262 CloseCacheEntry(false); 7263 (void)AsyncAbort(status); 7264 } 7265 return NS_OK; 7266 } 7267 7268 void nsHttpChannel::CancelNetworkRequest(nsresult aStatus) { 7269 if (mTransaction) { 7270 nsresult rv = gHttpHandler->CancelTransaction(mTransaction, aStatus); 7271 if (NS_FAILED(rv)) { 7272 LOG(("failed to cancel the transaction\n")); 7273 } 7274 } 7275 if (mTransactionPump) mTransactionPump->Cancel(aStatus); 7276 7277 mEarlyHintObserver = nullptr; 7278 mWebTransportSessionEventListener = nullptr; 7279 } 7280 7281 NS_IMETHODIMP 7282 nsHttpChannel::Suspend() { 7283 MOZ_ASSERT(NS_IsMainThread()); 7284 NS_ENSURE_TRUE(LoadIsPending(), NS_ERROR_NOT_AVAILABLE); 7285 7286 PROFILER_MARKER("nsHttpChannel::Suspend", NETWORK, {}, FlowMarker, 7287 Flow::FromPointer(this)); 7288 LOG(("nsHttpChannel::SuspendInternal [this=%p]\n", this)); 7289 LogCallingScriptLocation(this); 7290 7291 ++mSuspendCount; 7292 7293 if (mSuspendCount == 1) { 7294 mSuspendTimestamp = TimeStamp::NowLoRes(); 7295 7296 // Start timer to detect if we're suspended too long 7297 // This helps unblock waiting cache listeners when a channel is suspended 7298 // for an unreasonably long time. 7299 uint32_t delay = StaticPrefs::network_cache_suspended_writer_delay_ms(); 7300 if (!mSuspendTimer && delay) { 7301 mSuspendTimer = NS_NewTimer(); 7302 } 7303 7304 if (mSuspendTimer && delay) { 7305 RefPtr<TimerCallback> timerCallback = new TimerCallback(this); 7306 mSuspendTimer->InitWithCallback(timerCallback, delay, 7307 nsITimer::TYPE_ONE_SHOT); 7308 LOG((" started suspend timer, will fire in %dms", delay)); 7309 } 7310 } 7311 7312 nsresult rvTransaction = NS_OK; 7313 if (mTransactionPump) { 7314 rvTransaction = mTransactionPump->Suspend(); 7315 } 7316 nsresult rvCache = NS_OK; 7317 if (mCachePump) { 7318 rvCache = mCachePump->Suspend(); 7319 } 7320 7321 return NS_FAILED(rvTransaction) ? rvTransaction : rvCache; 7322 } 7323 7324 // static 7325 void nsHttpChannel::StaticSuspend(nsHttpChannel* aChan) { aChan->Suspend(); } 7326 7327 NS_IMETHODIMP 7328 nsHttpChannel::Resume() { 7329 MOZ_ASSERT(NS_IsMainThread()); 7330 NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED); 7331 7332 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::Resume", NETWORK, 7333 Flow::FromPointer(this)); 7334 LOG(("nsHttpChannel::ResumeInternal [this=%p]\n", this)); 7335 LogCallingScriptLocation(this); 7336 7337 if (--mSuspendCount == 0) { 7338 mSuspendTotalTime += TimeStamp::NowLoRes() - mSuspendTimestamp; 7339 7340 // Cancel suspend timer since we're resuming 7341 if (mSuspendTimer) { 7342 mSuspendTimer->Cancel(); 7343 LOG((" cancelled suspend timer")); 7344 } 7345 7346 // Reset bypass flag since the writer is resuming 7347 if (mBypassCacheWriterSet && mCacheEntry) { 7348 mCacheEntry->SetBypassWriterLock(false); 7349 mBypassCacheWriterSet = false; 7350 LOG((" reset bypass writer lock flag")); 7351 } 7352 7353 if (mCallOnResume) { 7354 // Resume the interrupted procedure first, then resume 7355 // the pump to continue process the input stream. 7356 // Any newly created pump MUST be suspended to prevent calling 7357 // its OnStartRequest before OnStopRequest of any pre-existing 7358 // pump. AsyncResumePending ensures that. 7359 MOZ_ASSERT(!LoadAsyncResumePending()); 7360 StoreAsyncResumePending(1); 7361 7362 std::function<nsresult(nsHttpChannel*)> callOnResume = nullptr; 7363 std::swap(callOnResume, mCallOnResume); 7364 7365 RefPtr<nsHttpChannel> self(this); 7366 nsCOMPtr<nsIRequest> transactionPump = mTransactionPump; 7367 RefPtr<nsInputStreamPump> cachePump = mCachePump; 7368 7369 nsresult rv = NS_DispatchToCurrentThread(NS_NewRunnableFunction( 7370 "nsHttpChannel::CallOnResume", 7371 [callOnResume{std::move(callOnResume)}, self{std::move(self)}, 7372 transactionPump{std::move(transactionPump)}, 7373 cachePump{std::move(cachePump)}]() { 7374 MOZ_ASSERT(self->LoadAsyncResumePending()); 7375 nsresult rv = self->CallOrWaitForResume(callOnResume); 7376 if (NS_FAILED(rv)) { 7377 self->CloseCacheEntry(false); 7378 (void)self->AsyncAbort(rv); 7379 } 7380 MOZ_ASSERT(self->LoadAsyncResumePending()); 7381 7382 self->StoreAsyncResumePending(0); 7383 7384 // And now actually resume the previously existing pumps. 7385 if (transactionPump) { 7386 LOG( 7387 ("nsHttpChannel::CallOnResume resuming previous transaction " 7388 "pump %p, this=%p", 7389 transactionPump.get(), self.get())); 7390 transactionPump->Resume(); 7391 } 7392 if (cachePump) { 7393 LOG( 7394 ("nsHttpChannel::CallOnResume resuming previous cache pump " 7395 "%p, this=%p", 7396 cachePump.get(), self.get())); 7397 cachePump->Resume(); 7398 } 7399 7400 // Any newly created pumps were suspended once because of 7401 // AsyncResumePending. Problem is that the stream listener 7402 // notification is already pending in the queue right now, because 7403 // AsyncRead doesn't (regardless if called after Suspend) respect 7404 // the suspend coutner and the right order would not be preserved. 7405 // Hence, we do another dispatch round to actually Resume after 7406 // the notification from the original pump. 7407 if (transactionPump != self->mTransactionPump && 7408 self->mTransactionPump) { 7409 LOG( 7410 ("nsHttpChannel::CallOnResume async-resuming new " 7411 "transaction " 7412 "pump %p, this=%p", 7413 self->mTransactionPump.get(), self.get())); 7414 7415 nsCOMPtr<nsIRequest> pump = self->mTransactionPump; 7416 NS_DispatchToCurrentThread(NS_NewRunnableFunction( 7417 "nsHttpChannel::CallOnResume new transaction", 7418 [pump{std::move(pump)}]() { pump->Resume(); })); 7419 } 7420 if (cachePump != self->mCachePump && self->mCachePump) { 7421 LOG( 7422 ("nsHttpChannel::CallOnResume async-resuming new cache pump " 7423 "%p, this=%p", 7424 self->mCachePump.get(), self.get())); 7425 7426 RefPtr<nsInputStreamPump> pump = self->mCachePump; 7427 NS_DispatchToCurrentThread(NS_NewRunnableFunction( 7428 "nsHttpChannel::CallOnResume new pump", 7429 [pump{std::move(pump)}]() { pump->Resume(); })); 7430 } 7431 })); 7432 NS_ENSURE_SUCCESS(rv, rv); 7433 return rv; 7434 } 7435 } 7436 7437 nsresult rvTransaction = NS_OK; 7438 if (mTransactionPump) { 7439 rvTransaction = mTransactionPump->Resume(); 7440 } 7441 7442 nsresult rvCache = NS_OK; 7443 if (mCachePump) { 7444 rvCache = mCachePump->Resume(); 7445 } 7446 7447 return NS_FAILED(rvTransaction) ? rvTransaction : rvCache; 7448 } 7449 7450 //----------------------------------------------------------------------------- 7451 // nsHttpChannel::nsIChannel 7452 //----------------------------------------------------------------------------- 7453 7454 NS_IMETHODIMP 7455 nsHttpChannel::GetSecurityInfo(nsITransportSecurityInfo** securityInfo) { 7456 NS_ENSURE_ARG_POINTER(securityInfo); 7457 *securityInfo = do_AddRef(mSecurityInfo).take(); 7458 return NS_OK; 7459 } 7460 7461 // If any of the functions that AsyncOpen calls returns immediately an error 7462 // AsyncAbort(which calls onStart/onStopRequest) does not need to be call. 7463 // To be sure that they are not call ReleaseListeners() is called. 7464 // If AsyncOpen returns NS_OK, after that point AsyncAbort must be called on 7465 // any error. 7466 NS_IMETHODIMP 7467 nsHttpChannel::AsyncOpen(nsIStreamListener* aListener) { 7468 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::AsyncOpen", NETWORK, 7469 Flow::FromPointer(this)); 7470 nsCOMPtr<nsIStreamListener> listener = aListener; 7471 nsresult rv = 7472 nsContentSecurityManager::doContentSecurityCheck(this, listener); 7473 if (NS_WARN_IF(NS_FAILED(rv))) { 7474 ReleaseListeners(); 7475 return rv; 7476 } 7477 7478 MOZ_ASSERT( 7479 mLoadInfo->GetSecurityMode() == 0 || 7480 mLoadInfo->GetInitialSecurityCheckDone() || 7481 (mLoadInfo->GetSecurityMode() == 7482 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL && 7483 mLoadInfo->GetLoadingPrincipal() && 7484 mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()), 7485 "security flags in loadInfo but doContentSecurityCheck() not called"); 7486 7487 LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this)); 7488 mOpenerCallingScriptLocation = CallingScriptLocationString(); 7489 LogCallingScriptLocation(this, mOpenerCallingScriptLocation); 7490 NS_CompareLoadInfoAndLoadContext(this); 7491 7492 #ifdef DEBUG 7493 AssertPrivateBrowsingId(); 7494 #endif 7495 7496 NS_ENSURE_ARG_POINTER(listener); 7497 NS_ENSURE_TRUE(!LoadIsPending(), NS_ERROR_IN_PROGRESS); 7498 NS_ENSURE_TRUE(!LoadWasOpened(), NS_ERROR_ALREADY_OPENED); 7499 7500 if (mCanceled) { 7501 ReleaseListeners(); 7502 return NS_FAILED(mStatus) ? mStatus : NS_ERROR_FAILURE; 7503 } 7504 7505 if (MaybeWaitForUploadStreamNormalization(listener, nullptr)) { 7506 return NS_OK; 7507 } 7508 7509 MOZ_ASSERT(NS_IsMainThread()); 7510 7511 if (!gHttpHandler->Active()) { 7512 LOG((" after HTTP shutdown...")); 7513 ReleaseListeners(); 7514 return NS_ERROR_NOT_AVAILABLE; 7515 } 7516 7517 rv = NS_CheckPortSafety(mURI); 7518 if (NS_FAILED(rv)) { 7519 ReleaseListeners(); 7520 return rv; 7521 } 7522 7523 // If no one called SetLoadGroup or SetNotificationCallbacks, the private 7524 // state has not been updated on PrivateBrowsingChannel (which we derive 7525 // from) Same if the loadinfo has changed since the creation of the channel. 7526 // Hence, we have to call UpdatePrivateBrowsing() here 7527 UpdatePrivateBrowsing(); 7528 7529 AntiTrackingUtils::UpdateAntiTrackingInfoForChannel(this); 7530 7531 // Recalculate the default userAgent header after the AntiTrackingInfo gets 7532 // updated because we can only know whether the site is exempted from 7533 // fingerprinting protection after we have the AntiTracking Info. 7534 // 7535 // Note that we don't recalculate the header if it has been modified since the 7536 // channel was created because we want to preserve the modified header. 7537 if (!LoadIsUserAgentHeaderModified()) { 7538 rv = mRequestHead.SetHeader( 7539 nsHttp::User_Agent, 7540 gHttpHandler->UserAgent(nsContentUtils::ShouldResistFingerprinting( 7541 this, RFPTarget::HttpUserAgent)), 7542 false, nsHttpHeaderArray::eVarietyRequestEnforceDefault); 7543 MOZ_ASSERT(NS_SUCCEEDED(rv)); 7544 } 7545 7546 if (WaitingForTailUnblock()) { 7547 // This channel is marked as Tail and is part of a request context 7548 // that has positive number of non-tailed requestst, hence this channel 7549 // has been put to a queue. 7550 // When tail is unblocked, OnTailUnblock on this channel will be called 7551 // to continue AsyncOpen. 7552 mListener = listener; 7553 MOZ_DIAGNOSTIC_ASSERT(!mOnTailUnblock); 7554 mOnTailUnblock = &nsHttpChannel::AsyncOpenOnTailUnblock; 7555 7556 LOG((" put on hold until tail is unblocked")); 7557 return NS_OK; 7558 } 7559 7560 // Remember the cookie header that was set, if any 7561 nsAutoCString cookieHeader; 7562 if (NS_SUCCEEDED(mRequestHead.GetHeader(nsHttp::Cookie, cookieHeader))) { 7563 // if this is a cache revalidaing channel (mIsStaleRevalidation), then this 7564 // represents both user cookies and cookies from cookieService 7565 mUserSetCookieHeader = cookieHeader; 7566 } 7567 7568 // Set user agent override, do so before OnOpeningRequest notification 7569 // since we want to allow consumers of that notification change or remove 7570 // the User-Agent request header. 7571 HttpBaseChannel::SetDocshellUserAgentOverride(); 7572 7573 // After we notify any observers (on-opening-request, loadGroup, etc) we 7574 // must return NS_OK and return any errors asynchronously via 7575 // OnStart/OnStopRequest. Observers may add a reference to the channel 7576 // and expect to get OnStopRequest so they know when to drop the reference, 7577 // etc. 7578 7579 // notify "http-on-opening-request" observers, but not if this is a redirect 7580 if (!(mLoadFlags & LOAD_REPLACE)) { 7581 gHttpHandler->OnOpeningRequest(this); 7582 } 7583 7584 StoreIsPending(true); 7585 StoreWasOpened(true); 7586 7587 mListener = listener; 7588 7589 if (nsIOService::UseSocketProcess() && 7590 !gIOService->IsSocketProcessLaunchComplete()) { 7591 RefPtr<nsHttpChannel> self = this; 7592 gIOService->CallOrWaitForSocketProcess( 7593 [self]() { self->AsyncOpenFinal(TimeStamp::Now()); }); 7594 return NS_OK; 7595 } 7596 7597 AsyncOpenFinal(TimeStamp::Now()); 7598 7599 return NS_OK; 7600 } 7601 7602 void nsHttpChannel::AsyncOpenFinal(TimeStamp aTimeStamp) { 7603 // We save this timestamp from outside of the if block in case we enable the 7604 // profiler after AsyncOpen(). 7605 mLastStatusReported = TimeStamp::Now(); 7606 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::AsyncOpenFinal", NETWORK, 7607 Flow::FromPointer(this)); 7608 if (profiler_thread_is_being_profiled_for_markers()) { 7609 nsAutoCString requestMethod; 7610 GetRequestMethod(requestMethod); 7611 7612 profiler_add_network_marker( 7613 mURI, requestMethod, mPriority, mChannelId, NetworkLoadType::LOAD_START, 7614 mChannelCreationTimestamp, mLastStatusReported, 0, mCacheDisposition, 7615 mLoadInfo->GetInnerWindowID(), 7616 mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus); 7617 } 7618 7619 // Added due to PauseTask/DelayHttpChannel 7620 if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr); 7621 7622 // record asyncopen time unconditionally and clear it if we 7623 // don't want it after OnModifyRequest() weighs in. But waiting for 7624 // that to complete would mean we don't include proxy resolution in the 7625 // timing. 7626 if (!LoadAsyncOpenTimeOverriden()) { 7627 mAsyncOpenTime = aTimeStamp; 7628 } 7629 7630 // Remember we have Authorization header set here. We need to check on it 7631 // just once and early, AsyncOpen is the best place. 7632 StoreCustomAuthHeader(mRequestHead.HasHeader(nsHttp::Authorization)); 7633 7634 bool willCallback = false; 7635 // We are about to do an async lookup to check if the URI is a tracker. If 7636 // yes, this channel will be canceled by channel classifier. Chances are the 7637 // lookup is not needed so CheckIsTrackerWithLocalTable() will return an 7638 // error and then we can MaybeResolveProxyAndBeginConnect() right away. 7639 // We skip the check in case this is an internal redirected channel 7640 if (!LoadAuthRedirectedChannel() && 7641 NS_ShouldClassifyChannel(this, ClassifyType::ETP)) { 7642 RefPtr<nsHttpChannel> self = this; 7643 willCallback = NS_SUCCEEDED( 7644 AsyncUrlChannelClassifier::CheckChannel(this, [self]() -> void { 7645 nsCOMPtr<nsIURI> uri; 7646 self->GetURI(getter_AddRefs(uri)); 7647 MOZ_ASSERT(uri); 7648 7649 // Finish the AntiTracking Heuristic before 7650 // MaybeResolveProxyAndBeginConnect(). 7651 FinishAntiTrackingRedirectHeuristic(self, uri); 7652 7653 self->MaybeResolveProxyAndBeginConnect(); 7654 })); 7655 } 7656 7657 if (!willCallback) { 7658 // We can do MaybeResolveProxyAndBeginConnect immediately if 7659 // CheckIsTrackerWithLocalTable is failed. Note that we don't need to 7660 // handle the failure because BeginConnect() will return synchronously and 7661 // the caller will be responsible for handling it. 7662 MaybeResolveProxyAndBeginConnect(); 7663 } 7664 } 7665 7666 void nsHttpChannel::MaybeResolveProxyAndBeginConnect() { 7667 nsresult rv; 7668 7669 // The common case for HTTP channels is to begin proxy resolution and return 7670 // at this point. The only time we know mProxyInfo already is if we're 7671 // proxying a non-http protocol like ftp. We don't need to discover proxy 7672 // settings if we are never going to make a network connection. 7673 if (!mProxyInfo && 7674 !(mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_NO_NETWORK_IO)) && 7675 !BypassProxy() && NS_SUCCEEDED(ResolveProxy())) { 7676 return; 7677 } 7678 7679 if (!gHttpHandler->Active()) { 7680 LOG( 7681 ("nsHttpChannel::MaybeResolveProxyAndBeginConnect [this=%p] " 7682 "Handler no longer active.\n", 7683 this)); 7684 rv = NS_ERROR_NOT_AVAILABLE; 7685 } else { 7686 rv = BeginConnect(); 7687 } 7688 if (NS_FAILED(rv)) { 7689 CloseCacheEntry(false); 7690 (void)AsyncAbort(rv); 7691 } 7692 } 7693 7694 nsresult nsHttpChannel::AsyncOpenOnTailUnblock() { 7695 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::AsyncOpenOnTailUnblock", NETWORK, 7696 Flow::FromPointer(this)); 7697 return AsyncOpen(mListener); 7698 } 7699 7700 already_AddRefed<nsChannelClassifier> 7701 nsHttpChannel::GetOrCreateChannelClassifier() { 7702 if (!mChannelClassifier) { 7703 mChannelClassifier = new nsChannelClassifier(this); 7704 LOG(("nsHttpChannel [%p] created nsChannelClassifier [%p]\n", this, 7705 mChannelClassifier.get())); 7706 } 7707 7708 RefPtr<nsChannelClassifier> classifier = mChannelClassifier; 7709 return classifier.forget(); 7710 } 7711 7712 ProxyDNSStrategy nsHttpChannel::GetProxyDNSStrategy() { 7713 // When network_dns_force_use_https_rr is true, return DNS_PREFETCH_ORIGIN. 7714 // This ensures that we always perform HTTPS RR query. 7715 nsCOMPtr<nsProxyInfo> proxyInfo(static_cast<nsProxyInfo*>(mProxyInfo.get())); 7716 if (!proxyInfo || StaticPrefs::network_dns_force_use_https_rr()) { 7717 return ProxyDNSStrategy::ORIGIN; 7718 } 7719 7720 // If the proxy is not to perform name resolution itself. 7721 return GetProxyDNSStrategyHelper(proxyInfo->Type(), proxyInfo->Flags()); 7722 } 7723 7724 // BeginConnect() SHOULD NOT call AsyncAbort(). AsyncAbort will be called by 7725 // functions that called BeginConnect if needed. Only 7726 // MaybeResolveProxyAndBeginConnect and OnProxyAvailable ever call 7727 // BeginConnect. 7728 nsresult nsHttpChannel::BeginConnect() { 7729 LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this)); 7730 7731 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::BeginConnect", NETWORK, 7732 Flow::FromPointer(this)); 7733 nsresult rv; 7734 7735 // It is the caller's responsibility to not call us late in shutdown. 7736 MOZ_ASSERT(gHttpHandler->Active()); 7737 7738 // Construct connection info object 7739 nsAutoCString host; 7740 nsAutoCString scheme; 7741 int32_t port = -1; 7742 bool isHttps = mURI->SchemeIs("https"); 7743 7744 rv = mURI->GetScheme(scheme); 7745 if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiHost(host); 7746 if (NS_SUCCEEDED(rv)) rv = mURI->GetPort(&port); 7747 if (NS_SUCCEEDED(rv)) rv = mURI->GetAsciiSpec(mSpec); 7748 if (NS_FAILED(rv)) { 7749 return rv; 7750 } 7751 7752 // Just a warning here because some nsIURIs do not implement this method. 7753 (void)NS_WARN_IF(NS_FAILED(mURI->GetUsername(mUsername))); 7754 7755 // Reject the URL if it doesn't specify a host 7756 if (host.IsEmpty()) { 7757 rv = NS_ERROR_MALFORMED_URI; 7758 return rv; 7759 } 7760 LOG(("host=%s port=%d\n", host.get(), port)); 7761 LOG(("uri=%s\n", mSpec.get())); 7762 7763 nsCOMPtr<nsProxyInfo> proxyInfo; 7764 if (mProxyInfo) proxyInfo = do_QueryInterface(mProxyInfo); 7765 7766 if (mCaps & NS_HTTP_CONNECT_ONLY) { 7767 if (!proxyInfo) { 7768 LOG(("return failure: no proxy for connect-only channel\n")); 7769 return NS_ERROR_FAILURE; 7770 } 7771 7772 if (!proxyInfo->IsHTTP() && !proxyInfo->IsHTTPS()) { 7773 LOG(("return failure: non-http proxy for connect-only channel\n")); 7774 return NS_ERROR_FAILURE; 7775 } 7776 } 7777 7778 mRequestHead.SetHTTPS(isHttps); 7779 mRequestHead.SetOrigin(scheme, host, port); 7780 7781 AddStorageAccessHeadersToRequest(); 7782 SetOriginHeader(); 7783 SetDoNotTrack(); 7784 SetGlobalPrivacyControl(); 7785 7786 OriginAttributes originAttributes; 7787 // Regular principal in case we have a proxy. 7788 if (proxyInfo && 7789 !StaticPrefs::privacy_partition_network_state_connection_with_proxy()) { 7790 StoragePrincipalHelper::GetOriginAttributes( 7791 this, originAttributes, StoragePrincipalHelper::eRegularPrincipal); 7792 } else { 7793 StoragePrincipalHelper::GetOriginAttributesForNetworkState( 7794 this, originAttributes); 7795 } 7796 7797 // Adjust mCaps according to our request headers: 7798 // - If "Connection: close" is set as a request header, then do not bother 7799 // trying to establish a keep-alive connection. 7800 if (mRequestHead.HasHeaderValue(nsHttp::Connection, "close")) { 7801 mCaps &= ~(NS_HTTP_ALLOW_KEEPALIVE); 7802 StoreAllowHttp3(false); 7803 } 7804 7805 gHttpHandler->MaybeAddAltSvcForTesting(mURI, mUsername, mPrivateBrowsing, 7806 mCallbacks, originAttributes); 7807 7808 RefPtr<nsHttpConnectionInfo> connInfo; 7809 #ifdef FUZZING 7810 if (StaticPrefs::fuzzing_necko_http3()) { 7811 connInfo = 7812 new nsHttpConnectionInfo(host, port, "h3"_ns, mUsername, proxyInfo, 7813 originAttributes, host, port, true); 7814 } else { 7815 #endif 7816 if (mWebTransportSessionEventListener) { 7817 connInfo = 7818 new nsHttpConnectionInfo(host, port, "h3"_ns, mUsername, proxyInfo, 7819 originAttributes, isHttps, true, true); 7820 bool dedicated = true; 7821 nsresult rv; 7822 nsCOMPtr<WebTransportConnectionSettings> wtconSettings = 7823 do_QueryInterface(mWebTransportSessionEventListener, &rv); 7824 NS_ENSURE_SUCCESS(rv, rv); 7825 nsIWebTransport::HTTPVersion httpVersion; 7826 (void)wtconSettings->GetHttpVersion(&httpVersion); 7827 if (httpVersion == nsIWebTransport::HTTPVersion::h2) { 7828 connInfo = 7829 new nsHttpConnectionInfo(host, port, "h2"_ns, mUsername, proxyInfo, 7830 originAttributes, isHttps, false, true); 7831 } else { 7832 connInfo = 7833 new nsHttpConnectionInfo(host, port, "h3"_ns, mUsername, proxyInfo, 7834 originAttributes, isHttps, true, true); 7835 } 7836 wtconSettings->GetDedicated(&dedicated); 7837 if (dedicated) { 7838 connInfo->SetWebTransportId( 7839 nsHttpConnectionInfo::GenerateNewWebTransportId()); 7840 } 7841 } else { 7842 connInfo = new nsHttpConnectionInfo(host, port, ""_ns, mUsername, 7843 proxyInfo, originAttributes, isHttps); 7844 } 7845 #ifdef FUZZING 7846 } 7847 #endif 7848 7849 bool http2Allowed = !gHttpHandler->IsHttp2Excluded(connInfo); 7850 7851 bool http3Allowed = Http3Allowed(); 7852 if (!http3Allowed) { 7853 mCaps |= NS_HTTP_DISALLOW_HTTP3; 7854 } 7855 7856 RefPtr<AltSvcMapping> mapping; 7857 if (!mConnectionInfo && LoadAllowAltSvc() && // per channel 7858 !mWebTransportSessionEventListener && (http2Allowed || http3Allowed) && 7859 !(mLoadFlags & LOAD_FRESH_CONNECTION) && 7860 AltSvcMapping::AcceptableProxy(proxyInfo) && 7861 (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) && 7862 (mapping = gHttpHandler->GetAltServiceMapping( 7863 scheme, host, port, mPrivateBrowsing, originAttributes, http2Allowed, 7864 http3Allowed))) { 7865 LOG(("nsHttpChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n", this, 7866 scheme.get(), mapping->AlternateHost().get(), mapping->AlternatePort(), 7867 mapping->HashKey().get())); 7868 7869 if (!(mLoadFlags & LOAD_ANONYMOUS) && !mPrivateBrowsing) { 7870 nsAutoCString altUsedLine(mapping->AlternateHost()); 7871 bool defaultPort = 7872 mapping->AlternatePort() == 7873 (isHttps ? NS_HTTPS_DEFAULT_PORT : NS_HTTP_DEFAULT_PORT); 7874 if (!defaultPort) { 7875 altUsedLine.AppendLiteral(":"); 7876 altUsedLine.AppendInt(mapping->AlternatePort()); 7877 } 7878 // Like what we did for 'Authorization' header, we need to do the same for 7879 // 'Alt-Used' for avoiding this header being shown in the ServiceWorker 7880 // FetchEvent. 7881 (void)mRequestHead.ClearHeader(nsHttp::Alternate_Service_Used); 7882 rv = mRequestHead.SetHeader(nsHttp::Alternate_Service_Used, altUsedLine, 7883 false, 7884 nsHttpHeaderArray::eVarietyRequestDefault); 7885 MOZ_ASSERT(NS_SUCCEEDED(rv)); 7886 } 7887 7888 nsCOMPtr<nsIConsoleService> consoleService; 7889 consoleService = mozilla::components::Console::Service(); 7890 if (consoleService && !host.Equals(mapping->AlternateHost())) { 7891 nsAutoString message(u"Alternate Service Mapping found: "_ns); 7892 AppendASCIItoUTF16(scheme, message); 7893 message.AppendLiteral(u"://"); 7894 AppendASCIItoUTF16(host, message); 7895 message.AppendLiteral(u":"); 7896 message.AppendInt(port); 7897 message.AppendLiteral(u" to "); 7898 AppendASCIItoUTF16(scheme, message); 7899 message.AppendLiteral(u"://"); 7900 AppendASCIItoUTF16(mapping->AlternateHost(), message); 7901 message.AppendLiteral(u":"); 7902 message.AppendInt(mapping->AlternatePort()); 7903 consoleService->LogStringMessage(message.get()); 7904 } 7905 7906 LOG(("nsHttpChannel %p Using connection info from altsvc mapping", this)); 7907 mapping->GetConnectionInfo(getter_AddRefs(mConnectionInfo), proxyInfo, 7908 originAttributes); 7909 glean::http::transaction_use_altsvc 7910 .EnumGet(glean::http::TransactionUseAltsvcLabel::eTrue) 7911 .Add(); 7912 if (mConnectionInfo->IsHttp3() && 7913 StaticPrefs:: 7914 network_http_http3_force_use_alt_svc_mapping_for_testing()) { 7915 mCaps |= NS_HTTP_DISALLOW_SPDY; 7916 } 7917 } else if (mConnectionInfo) { 7918 LOG(("nsHttpChannel %p Using channel supplied connection info", this)); 7919 glean::http::transaction_use_altsvc 7920 .EnumGet(glean::http::TransactionUseAltsvcLabel::eFalse) 7921 .Add(); 7922 } else { 7923 LOG(("nsHttpChannel %p Using default connection info", this)); 7924 7925 mConnectionInfo = connInfo; 7926 glean::http::transaction_use_altsvc 7927 .EnumGet(glean::http::TransactionUseAltsvcLabel::eFalse) 7928 .Add(); 7929 } 7930 7931 bool trrEnabled = false; 7932 auto dnsStrategy = GetProxyDNSStrategy(); 7933 bool httpsRRAllowed = 7934 !LoadBeConservative() && !(mCaps & NS_HTTP_BE_CONSERVATIVE) && 7935 !(mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal() && 7936 mLoadInfo->GetExternalContentPolicyType() != 7937 ExtContentPolicy::TYPE_DOCUMENT) && 7938 dnsStrategy == ProxyDNSStrategy::ORIGIN && 7939 !mConnectionInfo->UsingConnect() && canUseHTTPSRRonNetwork(trrEnabled) && 7940 StaticPrefs::network_dns_use_https_rr_as_altsvc(); 7941 if (!httpsRRAllowed) { 7942 DisallowHTTPSRR(mCaps); 7943 } else if (trrEnabled) { 7944 if (nsIRequest::GetTRRMode() != nsIRequest::TRR_DISABLED_MODE) { 7945 mCaps |= NS_HTTP_FORCE_WAIT_HTTP_RR; 7946 } 7947 } 7948 // No need to lookup HTTPSSVC record if mHTTPSSVCRecord already contains a 7949 // value. 7950 StoreUseHTTPSSVC(StaticPrefs::network_dns_upgrade_with_https_rr() && 7951 httpsRRAllowed && mHTTPSSVCRecord.isNothing()); 7952 7953 // Need to re-ask the handler, since mConnectionInfo may not be the connInfo 7954 // we used earlier 7955 if (!mConnectionInfo->IsHttp3() && 7956 gHttpHandler->IsHttp2Excluded(mConnectionInfo)) { 7957 StoreAllowSpdy(0); 7958 mCaps |= NS_HTTP_DISALLOW_SPDY; 7959 mConnectionInfo->SetNoSpdy(true); 7960 } 7961 7962 // We can be passed with the auth provider if this channel was 7963 // a result of redirect due to auth retry 7964 if (!mAuthProvider) { 7965 mAuthProvider = new nsHttpChannelAuthProvider(); 7966 } 7967 7968 rv = mAuthProvider->Init(this); 7969 if (NS_FAILED(rv)) { 7970 return rv; 7971 } 7972 7973 // check to see if authorization headers should be included 7974 // CustomAuthHeader is set in AsyncOpen if we find Authorization header 7975 rv = mAuthProvider->AddAuthorizationHeaders(LoadCustomAuthHeader()); 7976 if (NS_FAILED(rv)) { 7977 LOG(("nsHttpChannel %p AddAuthorizationHeaders failed (%08x)", this, 7978 static_cast<uint32_t>(rv))); 7979 } 7980 7981 // if this somehow fails we can go on without it 7982 (void)gHttpHandler->AddConnectionHeader(&mRequestHead, mCaps); 7983 7984 if (!LoadIsTRRServiceChannel() && 7985 ((mLoadFlags & LOAD_FRESH_CONNECTION) || 7986 (!StaticPrefs::network_dns_only_refresh_on_fresh_connection() && 7987 (mLoadFlags & VALIDATE_ALWAYS || 7988 BYPASS_LOCAL_CACHE(mLoadFlags, LoadPreferCacheLoadOverBypass()))))) { 7989 mCaps |= NS_HTTP_REFRESH_DNS; 7990 } 7991 7992 if (gHttpHandler->CriticalRequestPrioritization()) { 7993 if (mClassOfService.Flags() & nsIClassOfService::Leader) { 7994 mCaps |= NS_HTTP_LOAD_AS_BLOCKING; 7995 } 7996 if (mClassOfService.Flags() & nsIClassOfService::Unblocked) { 7997 mCaps |= NS_HTTP_LOAD_UNBLOCKED; 7998 } 7999 if (mClassOfService.Flags() & nsIClassOfService::UrgentStart && 8000 gHttpHandler->IsUrgentStartEnabled()) { 8001 mCaps |= NS_HTTP_URGENT_START; 8002 SetPriority(nsISupportsPriority::PRIORITY_HIGHEST); 8003 } 8004 } 8005 8006 // Force-Reload should reset the persistent connection pool for this host 8007 if (mLoadFlags & LOAD_FRESH_CONNECTION) { 8008 // just the initial document resets the whole pool 8009 if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) { 8010 gHttpHandler->AltServiceCache()->ClearAltServiceMappings(); 8011 rv = gHttpHandler->DoShiftReloadConnectionCleanupWithConnInfo( 8012 mConnectionInfo); 8013 if (NS_FAILED(rv)) { 8014 LOG(( 8015 "nsHttpChannel::BeginConnect " 8016 "DoShiftReloadConnectionCleanupWithConnInfo failed: %08x [this=%p]", 8017 static_cast<uint32_t>(rv), this)); 8018 } 8019 } 8020 } 8021 8022 // We may have been cancelled already, either by on-modify-request 8023 // listeners or load group observers; in that case, we should not send the 8024 // request to the server 8025 if (mCanceled) { 8026 return mStatus; 8027 } 8028 // skip classifier checks if this channel was the result of internal auth 8029 // redirect 8030 bool shouldBeClassifiedForTracker = 8031 !LoadAuthRedirectedChannel() && 8032 NS_ShouldClassifyChannel(this, ClassifyType::ETP); 8033 8034 if (shouldBeClassifiedForTracker) { 8035 if (LoadChannelClassifierCancellationPending()) { 8036 LOG( 8037 ("Waiting for safe-browsing protection cancellation in BeginConnect " 8038 "[this=%p]\n", 8039 this)); 8040 return NS_OK; 8041 } 8042 8043 ReEvaluateReferrerAfterTrackingStatusIsKnown(); 8044 } 8045 8046 MaybeStartDNSPrefetch(); 8047 8048 // Update whether the channel is on the third-party cookie blocking exception 8049 // list. 8050 CookieService::Update3PCBExceptionInfo(this); 8051 8052 rv = CallOrWaitForResume( 8053 [](nsHttpChannel* self) { return self->PrepareToConnect(); }); 8054 if (NS_FAILED(rv)) { 8055 return rv; 8056 } 8057 8058 bool shouldBeClassifiedForSafeBrowsing = 8059 NS_ShouldClassifyChannel(this, ClassifyType::SafeBrowsing); 8060 8061 if (shouldBeClassifiedForSafeBrowsing) { 8062 // Start nsChannelClassifier to catch phishing and malware URIs. 8063 RefPtr<nsChannelClassifier> channelClassifier = 8064 GetOrCreateChannelClassifier(); 8065 LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]", 8066 channelClassifier.get(), this)); 8067 channelClassifier->Start(); 8068 } 8069 8070 return NS_OK; 8071 } 8072 8073 void nsHttpChannel::MaybeStartDNSPrefetch() { 8074 // Start a DNS lookup very early in case the real open is queued the DNS can 8075 // happen in parallel. Do not do so in the presence of an HTTP proxy as 8076 // all lookups other than for the proxy itself are done by the proxy. 8077 // Also we don't do a lookup if the LOAD_NO_NETWORK_IO or 8078 // LOAD_ONLY_FROM_CACHE flags are set. 8079 // 8080 // We keep the DNS prefetch object around so that we can retrieve 8081 // timing information from it. There is no guarantee that we actually 8082 // use the DNS prefetch data for the real connection, but as we keep 8083 // this data around for 3 minutes by default, this should almost always 8084 // be correct, and even when it isn't, the timing still represents _a_ 8085 // valid DNS lookup timing for the site, even if it is not _the_ 8086 // timing we used. 8087 if ((mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE)) || 8088 LoadAuthRedirectedChannel()) { 8089 return; 8090 } 8091 8092 auto dnsStrategy = GetProxyDNSStrategy(); 8093 8094 LOG( 8095 ("nsHttpChannel::MaybeStartDNSPrefetch [this=%p, strategy=%u] " 8096 "prefetching%s\n", 8097 this, static_cast<uint32_t>(dnsStrategy), 8098 mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "")); 8099 8100 if (dnsStrategy == ProxyDNSStrategy::ORIGIN) { 8101 OriginAttributes originAttributes; 8102 StoragePrincipalHelper::GetOriginAttributesForNetworkState( 8103 this, originAttributes); 8104 8105 mDNSPrefetch = new nsDNSPrefetch(mURI, originAttributes, 8106 nsIRequest::GetTRRMode(), this, true); 8107 nsIDNSService::DNSFlags dnsFlags = nsIDNSService::RESOLVE_DEFAULT_FLAGS; 8108 if (mCaps & NS_HTTP_REFRESH_DNS) { 8109 dnsFlags |= nsIDNSService::RESOLVE_BYPASS_CACHE; 8110 } 8111 8112 (void)mDNSPrefetch->PrefetchHigh(dnsFlags); 8113 8114 bool unused; 8115 if (StaticPrefs::network_dns_use_https_rr_as_altsvc() && !mHTTPSSVCRecord && 8116 !(mCaps & NS_HTTP_DISALLOW_HTTPS_RR) && 8117 canUseHTTPSRRonNetwork(unused)) { 8118 MOZ_ASSERT(!mHTTPSSVCRecord); 8119 8120 OriginAttributes originAttributes; 8121 StoragePrincipalHelper::GetOriginAttributesForHTTPSRR(this, 8122 originAttributes); 8123 8124 RefPtr<nsDNSPrefetch> resolver = 8125 new nsDNSPrefetch(mURI, originAttributes, nsIRequest::GetTRRMode()); 8126 (void)resolver->FetchHTTPSSVC(mCaps & NS_HTTP_REFRESH_DNS, true, 8127 [](nsIDNSHTTPSSVCRecord*) { 8128 // Do nothing. This is a DNS prefetch. 8129 }); 8130 } 8131 } 8132 } 8133 8134 NS_IMETHODIMP 8135 nsHttpChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) { 8136 if (mCacheEntry && !LoadCacheEntryIsWriteOnly()) { 8137 int64_t dataSize = 0; 8138 mCacheEntry->GetDataSize(&dataSize); 8139 *aEncodedBodySize = dataSize; 8140 } else { 8141 *aEncodedBodySize = mLogicalOffset; 8142 } 8143 return NS_OK; 8144 } 8145 8146 NS_IMETHODIMP 8147 nsHttpChannel::GetDecompressDictionary(DictionaryCacheEntry** aDictionary) { 8148 *aDictionary = do_AddRef(mDictDecompress).take(); 8149 return NS_OK; 8150 } 8151 8152 NS_IMETHODIMP 8153 nsHttpChannel::SetDecompressDictionary(DictionaryCacheEntry* aDictionary) { 8154 if (!aDictionary) { 8155 if (mDictDecompress && mUsingDictionary) { 8156 mDictDecompress->UseCompleted(); 8157 } 8158 mUsingDictionary = false; 8159 } else { 8160 MOZ_ASSERT(!mDictDecompress); 8161 aDictionary->InUse(); 8162 mUsingDictionary = true; 8163 } 8164 mDictDecompress = aDictionary; 8165 return NS_OK; 8166 } 8167 8168 //----------------------------------------------------------------------------- 8169 // nsHttpChannel::nsIHttpChannelInternal 8170 //----------------------------------------------------------------------------- 8171 8172 NS_IMETHODIMP 8173 nsHttpChannel::GetIsAuthChannel(bool* aIsAuthChannel) { 8174 *aIsAuthChannel = mIsAuthChannel; 8175 return NS_OK; 8176 } 8177 8178 NS_IMETHODIMP 8179 nsHttpChannel::SetChannelIsForDownload(bool aChannelIsForDownload) { 8180 if (aChannelIsForDownload) { 8181 AddClassFlags(nsIClassOfService::Throttleable); 8182 } else { 8183 ClearClassFlags(nsIClassOfService::Throttleable); 8184 } 8185 8186 return HttpBaseChannel::SetChannelIsForDownload(aChannelIsForDownload); 8187 } 8188 8189 base::ProcessId nsHttpChannel::ProcessId() { 8190 nsCOMPtr<nsIParentChannel> parentChannel; 8191 NS_QueryNotificationCallbacks(this, parentChannel); 8192 if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel)) { 8193 return httpParent->OtherPid(); 8194 } 8195 if (RefPtr<DocumentLoadListener> docParent = do_QueryObject(parentChannel)) { 8196 return docParent->OtherPid(); 8197 } 8198 return base::GetCurrentProcId(); 8199 } 8200 8201 auto nsHttpChannel::AttachStreamFilter() -> RefPtr<ChildEndpointPromise> { 8202 LOG(("nsHttpChannel::AttachStreamFilter [this=%p]", this)); 8203 MOZ_ASSERT(!LoadOnStartRequestCalled()); 8204 8205 if (!ProcessId()) { 8206 return ChildEndpointPromise::CreateAndReject(false, __func__); 8207 } 8208 8209 nsCOMPtr<nsIParentChannel> parentChannel; 8210 NS_QueryNotificationCallbacks(this, parentChannel); 8211 8212 // If our listener is a DocumentLoadListener, then we might handle 8213 // multi-part responses here in the parent process. The current extension 8214 // API doesn't understand the parsed multipart format, so we defer responding 8215 // here until CallOnStartRequest, and attach the StreamFilter before the 8216 // multipart handler (in the parent process!) if applicable. 8217 if (RefPtr<DocumentLoadListener> docParent = do_QueryObject(parentChannel)) { 8218 StreamFilterRequest* request = mStreamFilterRequests.AppendElement(); 8219 request->mPromise = new ChildEndpointPromise::Private(__func__); 8220 return request->mPromise; 8221 } 8222 8223 mozilla::ipc::Endpoint<extensions::PStreamFilterParent> parent; 8224 mozilla::ipc::Endpoint<extensions::PStreamFilterChild> child; 8225 nsresult rv = extensions::PStreamFilter::CreateEndpoints(&parent, &child); 8226 if (NS_FAILED(rv)) { 8227 return ChildEndpointPromise::CreateAndReject(false, __func__); 8228 } 8229 8230 if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel)) { 8231 return httpParent->AttachStreamFilter(std::move(parent), std::move(child)); 8232 } 8233 8234 extensions::StreamFilterParent::Attach(this, std::move(parent)); 8235 return ChildEndpointPromise::CreateAndResolve(std::move(child), __func__); 8236 } 8237 8238 NS_IMETHODIMP 8239 nsHttpChannel::GetNavigationStartTimeStamp(TimeStamp* aTimeStamp) { 8240 LOG(("nsHttpChannel::GetNavigationStartTimeStamp [this=%p]", this)); 8241 MOZ_ASSERT(aTimeStamp); 8242 *aTimeStamp = mNavigationStartTimeStamp; 8243 return NS_OK; 8244 } 8245 8246 NS_IMETHODIMP 8247 nsHttpChannel::SetNavigationStartTimeStamp(TimeStamp aTimeStamp) { 8248 LOG(("nsHttpChannel::SetNavigationStartTimeStamp [this=%p]", this)); 8249 mNavigationStartTimeStamp = aTimeStamp; 8250 return NS_OK; 8251 } 8252 8253 //----------------------------------------------------------------------------- 8254 // nsHttpChannel::nsISupportsPriority 8255 //----------------------------------------------------------------------------- 8256 8257 NS_IMETHODIMP 8258 nsHttpChannel::SetPriority(int32_t value) { 8259 int16_t newValue = std::clamp<int32_t>(value, INT16_MIN, INT16_MAX); 8260 if (mPriority == newValue) return NS_OK; 8261 8262 LOG(("nsHttpChannel::SetPriority %p p=%d", this, newValue)); 8263 8264 mPriority = newValue; 8265 if (mTransaction) { 8266 nsresult rv = gHttpHandler->RescheduleTransaction(mTransaction, mPriority); 8267 if (NS_FAILED(rv)) { 8268 LOG( 8269 ("nsHttpChannel::SetPriority [this=%p] " 8270 "RescheduleTransaction failed (%08x)", 8271 this, static_cast<uint32_t>(rv))); 8272 } 8273 } 8274 8275 // If this channel is the real channel for an e10s channel, notify the 8276 // child side about the priority change as well. 8277 nsCOMPtr<nsIParentChannel> parentChannel; 8278 NS_QueryNotificationCallbacks(this, parentChannel); 8279 RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel); 8280 if (httpParent) { 8281 httpParent->DoSendSetPriority(newValue); 8282 } 8283 8284 return NS_OK; 8285 } 8286 8287 //----------------------------------------------------------------------------- 8288 // HttpChannel::nsIClassOfService 8289 //----------------------------------------------------------------------------- 8290 8291 void nsHttpChannel::OnClassOfServiceUpdated() { 8292 LOG(("nsHttpChannel::OnClassOfServiceUpdated this=%p, cos=%lu, inc=%d", this, 8293 mClassOfService.Flags(), mClassOfService.Incremental())); 8294 8295 if (mTransaction) { 8296 gHttpHandler->UpdateClassOfServiceOnTransaction(mTransaction, 8297 mClassOfService); 8298 } 8299 if (EligibleForTailing()) { 8300 RemoveAsNonTailRequest(); 8301 } else { 8302 AddAsNonTailRequest(); 8303 } 8304 } 8305 8306 NS_IMETHODIMP 8307 nsHttpChannel::SetClassFlags(uint32_t inFlags) { 8308 uint32_t previous = mClassOfService.Flags(); 8309 mClassOfService.SetFlags(inFlags); 8310 if (previous != mClassOfService.Flags()) { 8311 OnClassOfServiceUpdated(); 8312 } 8313 return NS_OK; 8314 } 8315 8316 NS_IMETHODIMP 8317 nsHttpChannel::AddClassFlags(uint32_t inFlags) { 8318 uint32_t previous = mClassOfService.Flags(); 8319 mClassOfService.SetFlags(inFlags | mClassOfService.Flags()); 8320 if (previous != mClassOfService.Flags()) { 8321 OnClassOfServiceUpdated(); 8322 } 8323 return NS_OK; 8324 } 8325 8326 NS_IMETHODIMP 8327 nsHttpChannel::ClearClassFlags(uint32_t inFlags) { 8328 uint32_t previous = mClassOfService.Flags(); 8329 mClassOfService.SetFlags(~inFlags & mClassOfService.Flags()); 8330 if (previous != mClassOfService.Flags()) { 8331 OnClassOfServiceUpdated(); 8332 } 8333 return NS_OK; 8334 } 8335 8336 NS_IMETHODIMP 8337 nsHttpChannel::SetClassOfService(ClassOfService cos) { 8338 ClassOfService previous = mClassOfService; 8339 mClassOfService = cos; 8340 if (previous != mClassOfService) { 8341 OnClassOfServiceUpdated(); 8342 } 8343 return NS_OK; 8344 } 8345 8346 NS_IMETHODIMP 8347 nsHttpChannel::SetIncremental(bool incremental) { 8348 bool previous = mClassOfService.Incremental(); 8349 mClassOfService.SetIncremental(incremental); 8350 if (previous != mClassOfService.Incremental()) { 8351 OnClassOfServiceUpdated(); 8352 } 8353 return NS_OK; 8354 } 8355 8356 //----------------------------------------------------------------------------- 8357 // nsHttpChannel::nsIProtocolProxyCallback 8358 //----------------------------------------------------------------------------- 8359 8360 NS_IMETHODIMP 8361 nsHttpChannel::OnProxyAvailable(nsICancelable* request, nsIChannel* channel, 8362 nsIProxyInfo* pi, nsresult status) { 8363 LOG(("nsHttpChannel::OnProxyAvailable [this=%p pi=%p status=%" PRIx32 8364 " mStatus=%" PRIx32 "]\n", 8365 this, pi, static_cast<uint32_t>(status), 8366 static_cast<uint32_t>(static_cast<nsresult>(mStatus)))); 8367 mProxyRequest = nullptr; 8368 8369 nsresult rv; 8370 8371 // If status is a failure code, then it means that we failed to resolve 8372 // proxy info. That is a non-fatal error assuming it wasn't because the 8373 // request was canceled. We just failover to DIRECT when proxy resolution 8374 // fails (failure can mean that the PAC URL could not be loaded). 8375 8376 if (NS_SUCCEEDED(status)) { 8377 mProxyInfo = pi; 8378 8379 if (mProxyInfo) { 8380 nsAutoCStringN<8> type; 8381 mProxyInfo->GetType(type); 8382 uint32_t flags = 0; 8383 mProxyInfo->GetFlags(&flags); 8384 8385 if (type.EqualsLiteral("socks")) { 8386 if (flags & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) { 8387 glean::networking::proxy_info_type 8388 .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks5h) 8389 .Add(1); 8390 } else { 8391 glean::networking::proxy_info_type 8392 .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks5) 8393 .Add(1); 8394 } 8395 } else if (type.EqualsLiteral("socks4")) { 8396 if (flags & nsIProxyInfo::TRANSPARENT_PROXY_RESOLVES_HOST) { 8397 glean::networking::proxy_info_type 8398 .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks4a) 8399 .Add(1); 8400 } else { 8401 glean::networking::proxy_info_type 8402 .EnumGet(glean::networking::ProxyInfoTypeLabel::eSocks4) 8403 .Add(1); 8404 } 8405 } else if (type.EqualsLiteral("http")) { 8406 glean::networking::proxy_info_type 8407 .EnumGet(glean::networking::ProxyInfoTypeLabel::eHttp) 8408 .Add(1); 8409 } else if (type.EqualsLiteral("https")) { 8410 glean::networking::proxy_info_type 8411 .EnumGet(glean::networking::ProxyInfoTypeLabel::eHttps) 8412 .Add(1); 8413 } else if (type.EqualsLiteral("direct")) { 8414 glean::networking::proxy_info_type 8415 .EnumGet(glean::networking::ProxyInfoTypeLabel::eDirect) 8416 .Add(1); 8417 } else { 8418 glean::networking::proxy_info_type 8419 .EnumGet(glean::networking::ProxyInfoTypeLabel::eUnknown) 8420 .Add(1); 8421 } 8422 } 8423 } 8424 8425 if (!gHttpHandler->Active()) { 8426 LOG( 8427 ("nsHttpChannel::OnProxyAvailable [this=%p] " 8428 "Handler no longer active.\n", 8429 this)); 8430 rv = NS_ERROR_NOT_AVAILABLE; 8431 } else { 8432 rv = BeginConnect(); 8433 } 8434 8435 if (NS_FAILED(rv)) { 8436 CloseCacheEntry(false); 8437 (void)AsyncAbort(rv); 8438 } 8439 return rv; 8440 } 8441 8442 //----------------------------------------------------------------------------- 8443 // nsHttpChannel::nsIProxiedChannel 8444 //----------------------------------------------------------------------------- 8445 8446 NS_IMETHODIMP 8447 nsHttpChannel::GetProxyInfo(nsIProxyInfo** result) { 8448 if (!mConnectionInfo) { 8449 *result = do_AddRef(mProxyInfo).take(); 8450 } else { 8451 *result = do_AddRef(mConnectionInfo->ProxyInfo()).take(); 8452 } 8453 return NS_OK; 8454 } 8455 8456 //----------------------------------------------------------------------------- 8457 // nsHttpChannel::nsITimedChannel 8458 //----------------------------------------------------------------------------- 8459 8460 NS_IMETHODIMP 8461 nsHttpChannel::GetDomainLookupStart(TimeStamp* _retval) { 8462 if (mTransaction) { 8463 *_retval = mTransaction->GetDomainLookupStart(); 8464 } else { 8465 *_retval = mTransactionTimings.domainLookupStart; 8466 } 8467 return NS_OK; 8468 } 8469 8470 NS_IMETHODIMP 8471 nsHttpChannel::GetDomainLookupEnd(TimeStamp* _retval) { 8472 if (mTransaction) { 8473 *_retval = mTransaction->GetDomainLookupEnd(); 8474 } else { 8475 *_retval = mTransactionTimings.domainLookupEnd; 8476 } 8477 return NS_OK; 8478 } 8479 8480 NS_IMETHODIMP 8481 nsHttpChannel::GetConnectStart(TimeStamp* _retval) { 8482 if (mTransaction) { 8483 *_retval = mTransaction->GetConnectStart(); 8484 } else { 8485 *_retval = mTransactionTimings.connectStart; 8486 } 8487 return NS_OK; 8488 } 8489 8490 NS_IMETHODIMP 8491 nsHttpChannel::GetTcpConnectEnd(TimeStamp* _retval) { 8492 if (mTransaction) { 8493 *_retval = mTransaction->GetTcpConnectEnd(); 8494 } else { 8495 *_retval = mTransactionTimings.tcpConnectEnd; 8496 } 8497 return NS_OK; 8498 } 8499 8500 NS_IMETHODIMP 8501 nsHttpChannel::GetSecureConnectionStart(TimeStamp* _retval) { 8502 if (mTransaction) { 8503 *_retval = mTransaction->GetSecureConnectionStart(); 8504 } else { 8505 *_retval = mTransactionTimings.secureConnectionStart; 8506 } 8507 return NS_OK; 8508 } 8509 8510 NS_IMETHODIMP 8511 nsHttpChannel::GetConnectEnd(TimeStamp* _retval) { 8512 if (mTransaction) { 8513 *_retval = mTransaction->GetConnectEnd(); 8514 } else { 8515 *_retval = mTransactionTimings.connectEnd; 8516 } 8517 return NS_OK; 8518 } 8519 8520 NS_IMETHODIMP 8521 nsHttpChannel::GetRequestStart(TimeStamp* _retval) { 8522 if (mTransaction) { 8523 *_retval = mTransaction->GetRequestStart(); 8524 } else { 8525 *_retval = mTransactionTimings.requestStart; 8526 } 8527 return NS_OK; 8528 } 8529 8530 NS_IMETHODIMP 8531 nsHttpChannel::GetResponseStart(TimeStamp* _retval) { 8532 if (mTransaction) { 8533 *_retval = mTransaction->GetResponseStart(); 8534 } else { 8535 *_retval = mTransactionTimings.responseStart; 8536 } 8537 return NS_OK; 8538 } 8539 8540 NS_IMETHODIMP 8541 nsHttpChannel::GetResponseEnd(TimeStamp* _retval) { 8542 if (mTransaction) { 8543 *_retval = mTransaction->GetResponseEnd(); 8544 } else { 8545 *_retval = mTransactionTimings.responseEnd; 8546 } 8547 return NS_OK; 8548 } 8549 8550 NS_IMETHODIMP 8551 nsHttpChannel::GetTransactionPending(TimeStamp* _retval) { 8552 if (mTransaction) { 8553 *_retval = mTransaction->GetPendingTime(); 8554 } else { 8555 *_retval = mTransactionTimings.transactionPending; 8556 } 8557 return NS_OK; 8558 } 8559 8560 //----------------------------------------------------------------------------- 8561 // nsHttpChannel::nsIHttpAuthenticableChannel 8562 //----------------------------------------------------------------------------- 8563 8564 NS_IMETHODIMP 8565 nsHttpChannel::GetIsSSL(bool* aIsSSL) { 8566 // this attribute is really misnamed - it wants to know if 8567 // https:// is being used. SSL might be used to cover http:// 8568 // in some circumstances (proxies, http/2, etc..) 8569 return mURI->SchemeIs("https", aIsSSL); 8570 } 8571 8572 NS_IMETHODIMP 8573 nsHttpChannel::GetProxyMethodIsConnect(bool* aProxyMethodIsConnect) { 8574 *aProxyMethodIsConnect = mConnectionInfo->UsingConnect(); 8575 return NS_OK; 8576 } 8577 8578 NS_IMETHODIMP 8579 nsHttpChannel::GetServerResponseHeader(nsACString& value) { 8580 if (!mResponseHead) return NS_ERROR_NOT_AVAILABLE; 8581 return mResponseHead->GetHeader(nsHttp::Server, value); 8582 } 8583 8584 NS_IMETHODIMP 8585 nsHttpChannel::GetProxyChallenges(nsACString& value) { 8586 if (!mResponseHead) return NS_ERROR_UNEXPECTED; 8587 return mResponseHead->GetHeader(nsHttp::Proxy_Authenticate, value); 8588 } 8589 8590 NS_IMETHODIMP 8591 nsHttpChannel::GetWWWChallenges(nsACString& value) { 8592 if (!mResponseHead) return NS_ERROR_UNEXPECTED; 8593 return mResponseHead->GetHeader(nsHttp::WWW_Authenticate, value); 8594 } 8595 8596 NS_IMETHODIMP 8597 nsHttpChannel::SetProxyCredentials(const nsACString& value) { 8598 return mRequestHead.SetHeader(nsHttp::Proxy_Authorization, value); 8599 } 8600 8601 NS_IMETHODIMP 8602 nsHttpChannel::SetWWWCredentials(const nsACString& value) { 8603 // This method is called when various browser initiated authorization 8604 // code sets the credentials. We need to flag this header as the 8605 // "browser default" so it does not show up in the ServiceWorker 8606 // FetchEvent. This may actually get called more than once, though, 8607 // so we clear the header first since "default" headers are not 8608 // allowed to overwrite normally. 8609 (void)mRequestHead.ClearHeader(nsHttp::Authorization); 8610 return mRequestHead.SetHeader(nsHttp::Authorization, value, false, 8611 nsHttpHeaderArray::eVarietyRequestDefault); 8612 } 8613 8614 //----------------------------------------------------------------------------- 8615 // Methods that nsIHttpAuthenticableChannel dupes from other IDLs, which we 8616 // get from HttpBaseChannel, must be explicitly forwarded, because C++ sucks. 8617 //----------------------------------------------------------------------------- 8618 8619 NS_IMETHODIMP 8620 nsHttpChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) { 8621 return HttpBaseChannel::GetLoadFlags(aLoadFlags); 8622 } 8623 8624 NS_IMETHODIMP 8625 nsHttpChannel::GetURI(nsIURI** aURI) { return HttpBaseChannel::GetURI(aURI); } 8626 8627 NS_IMETHODIMP 8628 nsHttpChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) { 8629 return HttpBaseChannel::GetNotificationCallbacks(aCallbacks); 8630 } 8631 8632 NS_IMETHODIMP 8633 nsHttpChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) { 8634 return HttpBaseChannel::GetLoadGroup(aLoadGroup); 8635 } 8636 8637 NS_IMETHODIMP 8638 nsHttpChannel::GetRequestMethod(nsACString& aMethod) { 8639 return HttpBaseChannel::GetRequestMethod(aMethod); 8640 } 8641 8642 //----------------------------------------------------------------------------- 8643 // nsHttpChannel::nsIRequestObserver 8644 //----------------------------------------------------------------------------- 8645 8646 void nsHttpChannel::RecordOnStartTelemetry(nsresult aStatus, 8647 bool aIsNavigation) { 8648 glean::http::channel_onstart_success 8649 .EnumGet(static_cast<glean::http::ChannelOnstartSuccessLabel>( 8650 NS_SUCCEEDED(aStatus))) 8651 .Add(); 8652 8653 mozilla::glean::networking::http_channel_onstart_status 8654 .Get(NS_SUCCEEDED(aStatus) ? "successful"_ns : "fail"_ns) 8655 .Add(1); 8656 8657 if (mTransaction) { 8658 glean::networking::http3_channel_onstart_success 8659 .Get((mTransaction->IsHttp3Used()) ? "http3"_ns : "no_http3"_ns, 8660 NS_SUCCEEDED(aStatus) ? "true"_ns : "false"_ns) 8661 .Add(); 8662 } 8663 8664 enum class HttpOnStartState : uint32_t { 8665 Success = 0, 8666 DNSError = 1, 8667 Others = 2, 8668 }; 8669 8670 if (TRRService::Get() && TRRService::Get()->IsConfirmed()) { 8671 // Note this telemetry probe is not working when DNS resolution is done in 8672 // the socket process. 8673 HttpOnStartState state = HttpOnStartState::Others; 8674 if (NS_SUCCEEDED(aStatus)) { 8675 state = HttpOnStartState::Success; 8676 } else if (aStatus == NS_ERROR_UNKNOWN_HOST || 8677 aStatus == NS_ERROR_UNKNOWN_PROXY_HOST) { 8678 state = HttpOnStartState::DNSError; 8679 } 8680 8681 if (aIsNavigation) { 8682 glean::http::channel_page_onstart_success_trr 8683 .Get(TRRService::ProviderKey()) 8684 .AccumulateSingleSample(static_cast<uint32_t>(state)); 8685 } else { 8686 glean::http::channel_sub_onstart_success_trr 8687 .Get(TRRService::ProviderKey()) 8688 .AccumulateSingleSample(static_cast<uint32_t>(state)); 8689 } 8690 } 8691 8692 if (nsIOService::UseSocketProcess() && mTransaction) { 8693 const TimeStamp now = TimeStamp::Now(); 8694 TimeStamp responseEnd = mTransaction->GetResponseEnd(); 8695 if (!responseEnd.IsNull()) { 8696 PerfStats::RecordMeasurement(PerfStats::Metric::ResponseEndSocketToParent, 8697 now - responseEnd); 8698 } 8699 8700 mOnStartRequestStartTime = mTransaction->GetOnStartRequestStartTime(); 8701 if (!mOnStartRequestStartTime.IsNull()) { 8702 PerfStats::RecordMeasurement( 8703 PerfStats::Metric::OnStartRequestSocketToParent, 8704 now - mOnStartRequestStartTime); 8705 } 8706 } else { 8707 mOnStartRequestStartTime = TimeStamp::Now(); 8708 } 8709 } 8710 8711 static bool hasConnectivity() { 8712 if (RefPtr<NetworkConnectivityService> ncs = 8713 NetworkConnectivityService::GetSingleton()) { 8714 nsINetworkConnectivityService::ConnectivityState state; 8715 if (NS_SUCCEEDED(ncs->GetIPv4(&state)) && 8716 state == nsINetworkConnectivityService::OK) { 8717 return true; 8718 } 8719 // We should also check for IPv6 connectivity here, but since 8720 // quite a few mozilla domains don't have IPv6 records yet, 8721 // we might incorrectly fallback to a backup domain on 8722 // IPv6 only networks. 8723 // When bug 1665605 gets fixed we can also return true when only IPv6 is OK. 8724 } 8725 8726 return false; 8727 }; 8728 8729 static already_AddRefed<nsIURI> GetFallbackURI(nsIURI* aURI) { 8730 nsresult rv; 8731 nsAutoCString host; 8732 aURI->GetHost(host); 8733 nsCOMPtr<nsIURI> backupURI; 8734 8735 nsAutoCString fallbackDomain; 8736 if (!gIOService->GetFallbackDomain(host, fallbackDomain)) { 8737 return nullptr; 8738 } 8739 8740 rv = NS_MutateURI(aURI).SetHost(fallbackDomain).Finalize(backupURI); 8741 if (NS_FAILED(rv)) { 8742 return nullptr; 8743 } 8744 8745 return backupURI.forget(); 8746 } 8747 8748 // static 8749 nsHttpChannel::EssentialDomainCategory 8750 nsHttpChannel::GetEssentialDomainCategory(nsCString& domain) { 8751 if (StringEndsWith(domain, ".addons.mozilla.org"_ns)) { 8752 return EssentialDomainCategory::SubAddonsMozillaOrg; 8753 } 8754 if (domain == "addons.mozilla.org"_ns) { 8755 return EssentialDomainCategory::AddonsMozillaOrg; 8756 } 8757 if (domain == "aus5.mozilla.org"_ns) { 8758 return EssentialDomainCategory::Aus5MozillaOrg; 8759 } 8760 if (domain == "firefox.settings.services.mozilla.com"_ns) { 8761 return EssentialDomainCategory::RemoteSettings; 8762 } 8763 if (domain == "incoming.telemetry.mozilla.com"_ns) { 8764 return EssentialDomainCategory::Telemetry; 8765 } 8766 return EssentialDomainCategory::Other; 8767 } 8768 8769 // Helper function to send LNA access info to child process for console logging 8770 // The child process has access to CallingScriptLocationString() which requires 8771 // JS context 8772 static void ReportLNAAccessToConsole(nsHttpChannel* aChannel, 8773 const char* aMessageName, 8774 const nsACString& aPromptAction = ""_ns) { 8775 // Send IPC to child process to log to console with script location 8776 nsCOMPtr<nsIParentChannel> parentChannel; 8777 NS_QueryNotificationCallbacks(aChannel, parentChannel); 8778 if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel)) { 8779 NetAddr peerAddr = aChannel->GetPeerAddr(); 8780 8781 // Fetch top-level site URI in parent process and pass via IPC. 8782 // We need to fetch this here because with Fission (site isolation), 8783 // cross-site iframes run in separate content processes and cannot 8784 // access the top-level document in a different process. 8785 nsAutoCString topLevelSite; 8786 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 8787 if (loadInfo) { 8788 RefPtr<mozilla::dom::BrowsingContext> bc; 8789 loadInfo->GetBrowsingContext(getter_AddRefs(bc)); 8790 if (bc && bc->Top() && bc->Top()->Canonical()) { 8791 RefPtr<mozilla::dom::WindowGlobalParent> topWindowGlobal = 8792 bc->Top()->Canonical()->GetCurrentWindowGlobal(); 8793 if (topWindowGlobal) { 8794 nsIPrincipal* topPrincipal = topWindowGlobal->DocumentPrincipal(); 8795 if (topPrincipal) { 8796 nsCOMPtr<nsIURI> topURI = topPrincipal->GetURI(); 8797 if (topURI) { 8798 (void)topURI->GetSpec(topLevelSite); 8799 } 8800 } 8801 } 8802 } 8803 } 8804 8805 httpParent->DoSendReportLNAToConsole(peerAddr, nsCString(aMessageName), 8806 nsCString(aPromptAction), 8807 topLevelSite); 8808 } 8809 } 8810 8811 nsresult nsHttpChannel::ProcessLNAActions() { 8812 if (!mTransaction) { 8813 // this could happen with rcwn enabled. 8814 // We have hit network and have detected LNA, meanwhile cache won and reset 8815 // the transaction in ReadFromCache 8816 return NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED; 8817 } 8818 // Suspend to block any notification to the channel. 8819 // This will get resumed in 8820 // nsHttpChannel::OnPermissionPromptResult 8821 UpdateCurrentIpAddressSpace(); 8822 mWaitingForLNAPermission = true; 8823 Suspend(); 8824 auto permissionKey = mTransaction->GetTargetIPAddressSpace() == 8825 nsILoadInfo::IPAddressSpace::Local 8826 ? LOCAL_HOST_PERMISSION_KEY 8827 : LOCAL_NETWORK_PERMISSION_KEY; 8828 LNAPermission permissionUpdateResult = 8829 UpdateLocalNetworkAccessPermissions(permissionKey); 8830 8831 if (LNAPermission::Granted == permissionUpdateResult) { 8832 // permission granted (auto-allow via permanent permission) 8833 mLNAPromptAction.AssignLiteral("auto_allow"); 8834 return OnPermissionPromptResult(true, permissionKey); 8835 } 8836 8837 if (LNAPermission::Denied == permissionUpdateResult) { 8838 // permission denied (auto-deny via permanent permission) 8839 mLNAPromptAction.AssignLiteral("auto_deny"); 8840 return OnPermissionPromptResult(false, permissionKey); 8841 } 8842 8843 // If we get here, we don't have any permission to access the local 8844 // host/network. We need to prompt the user for action 8845 auto permissionPromptCallback = 8846 [self = RefPtr{this}](bool aPermissionGranted, const nsACString& aType, 8847 bool aPromptShown) -> void { 8848 // Set mLNAPromptAction based on whether prompt was shown and user response 8849 if (aPromptShown) { 8850 if (aPermissionGranted) { 8851 self->mLNAPromptAction.AssignLiteral("prompt_allow"); 8852 } else { 8853 self->mLNAPromptAction.AssignLiteral("prompt_deny"); 8854 } 8855 } else { 8856 if (aPermissionGranted) { 8857 self->mLNAPromptAction.AssignLiteral("auto_allow"); 8858 } else { 8859 self->mLNAPromptAction.AssignLiteral("auto_deny"); 8860 } 8861 } 8862 self->OnPermissionPromptResult(aPermissionGranted, aType); 8863 }; 8864 8865 RefPtr<LNAPermissionRequest> request = new LNAPermissionRequest( 8866 std::move(permissionPromptCallback), mLoadInfo, permissionKey); 8867 8868 // Log to console before requesting permission 8869 ReportLNAAccessToConsole(this, "LocalNetworkAccessPermissionRequired"); 8870 8871 // This invokes callback nsHttpChannel::OnPermissionPromptResult 8872 // synchronously if the permission is already granted or denied 8873 // if permission is not available we prompt the user and in that case 8874 // nsHttpChannel::OnPermissionPromptResult is invoked asynchronously once 8875 // the user responds to the prompt 8876 return request->RequestPermission(); 8877 } 8878 8879 void nsHttpChannel::UpdateCurrentIpAddressSpace() { 8880 if (!mTransaction) { 8881 return; 8882 } 8883 8884 if (mPeerAddr.GetIpAddressSpace() == nsILoadInfo::IPAddressSpace::Unknown) { 8885 // fetch peer address from transaction 8886 bool isTrr; 8887 bool echConfigUsed; 8888 mTransaction->GetNetworkAddresses(mSelfAddr, mPeerAddr, isTrr, 8889 mEffectiveTRRMode, mTRRSkipReason, 8890 echConfigUsed); 8891 } 8892 8893 nsILoadInfo::IPAddressSpace docAddressSpace = mPeerAddr.GetIpAddressSpace(); 8894 mLoadInfo->SetIpAddressSpace(docAddressSpace); 8895 ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType(); 8896 if (type == ExtContentPolicy::TYPE_DOCUMENT || 8897 type == ExtContentPolicy::TYPE_SUBDOCUMENT) { 8898 RefPtr<mozilla::dom::BrowsingContext> bc; 8899 mLoadInfo->GetTargetBrowsingContext(getter_AddRefs(bc)); 8900 if (bc) { 8901 bc->SetCurrentIPAddressSpace(docAddressSpace); 8902 } 8903 8904 if (mCacheEntry) { 8905 // store the ipaddr information into the cache metadata entry 8906 if (mPeerAddr.GetIpAddressSpace() != 8907 nsILoadInfo::IPAddressSpace::Unknown) { 8908 uint16_t port; 8909 mPeerAddr.GetPort(&port); 8910 mCacheEntry->SetMetaDataElement("peer-ip-address", 8911 mPeerAddr.ToString().get()); 8912 mCacheEntry->SetMetaDataElement("peer-port", ToString(port).c_str()); 8913 } 8914 } 8915 } 8916 } 8917 8918 NS_IMETHODIMP 8919 nsHttpChannel::OnStartRequest(nsIRequest* request) { 8920 nsresult rv; 8921 8922 MOZ_ASSERT(LoadRequestObserversCalled()); 8923 8924 AUTO_PROFILER_LABEL("nsHttpChannel::OnStartRequest", NETWORK); 8925 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnStartRequest", NETWORK, 8926 Flow::FromPointer(this)); 8927 8928 if (!(mCanceled || NS_FAILED(mStatus)) && 8929 !WRONG_RACING_RESPONSE_SOURCE(request)) { 8930 // capture the request's status, so our consumers will know ASAP of any 8931 // connection failures, etc - bug 93581 8932 nsresult status; 8933 request->GetStatus(&status); 8934 mStatus = status; 8935 } 8936 8937 if (mStatus == NS_ERROR_NON_LOCAL_CONNECTION_REFUSED) { 8938 MOZ_CRASH_UNSAFE(nsPrintfCString("Attempting to connect to non-local " 8939 "address! opener is [%s], uri is " 8940 "[%s]", 8941 mOpenerCallingScriptLocation 8942 ? mOpenerCallingScriptLocation->get() 8943 : "unknown", 8944 mURI->GetSpecOrDefault().get()) 8945 .get()); 8946 } 8947 8948 if (mStatus == NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED) { 8949 return ProcessLNAActions(); 8950 } 8951 8952 LOG(("nsHttpChannel::OnStartRequest [this=%p request=%p status=%" PRIx32 8953 "]\n", 8954 this, request, static_cast<uint32_t>(static_cast<nsresult>(mStatus)))); 8955 8956 RecordOnStartTelemetry(mStatus, IsNavigation()); 8957 8958 if (mRaceCacheWithNetwork) { 8959 LOG( 8960 (" racingNetAndCache - mFirstResponseSource:%d fromCache:%d " 8961 "fromNet:%d\n", 8962 static_cast<int32_t>(mFirstResponseSource), request == mCachePump, 8963 request == mTransactionPump)); 8964 if (mFirstResponseSource == RESPONSE_PENDING) { 8965 // When the cache wins mFirstResponseSource is set to 8966 // RESPONSE_FROM_CACHE earlier in ReadFromCache, so this must be a 8967 // response from the network. 8968 MOZ_ASSERT(request == mTransactionPump); 8969 LOG((" First response from network\n")); 8970 { 8971 // Race condition with OnCacheEntryCheck, which is not limited 8972 // to main thread. 8973 mozilla::MutexAutoLock lock(mRCWNLock); 8974 mFirstResponseSource = RESPONSE_FROM_NETWORK; 8975 // If we haven't gotten any response from the cache, we've won the 8976 // race. Any response from the case means it's a cache 'win', even if 8977 // the response was "we don't have an entry" or "we have an entry but 8978 // it's expired/invalid". 8979 if (LoadCachedContentIsValid() == CachedContentValidity::Unset) { 8980 StoreNetworkWonRace(1); 8981 } 8982 mOnStartRequestTimestamp = TimeStamp::Now(); 8983 PROFILER_MARKER_TEXT( 8984 "RCWN", NETWORK, {}, 8985 nsPrintfCString("Network won on StartRequest valid=%d for %s - %p", 8986 LoadCachedContentIsValid(), mSpec.get(), this)); 8987 8988 // Conditional or byte range header could be added in 8989 // OnCacheEntryCheck. We need to remove them because the 8990 // request might be sent again due to auth retry and we must 8991 // not send these headers without having the entry. 8992 if (mDidReval) { 8993 LOG((" Removing conditional request headers")); 8994 UntieValidationRequest(); 8995 mDidReval = false; 8996 } 8997 if (LoadCachedContentIsPartial()) { 8998 LOG((" Removing byte range request headers")); 8999 UntieByteRangeRequest(); 9000 StoreCachedContentIsPartial(false); 9001 } 9002 } 9003 mAvailableCachedAltDataType.Truncate(); 9004 StoreDeliveringAltData(false); 9005 } else if (WRONG_RACING_RESPONSE_SOURCE(request)) { 9006 LOG((" Early return when racing. This response not needed.")); 9007 return NS_OK; 9008 } else { 9009 PROFILER_MARKER_TEXT( 9010 "RCWN", NETWORK, {}, 9011 nsPrintfCString("Cache won on StartRequest valid=%d for %s - %p", 9012 LoadCachedContentIsValid(), mSpec.get(), this)); 9013 } 9014 } 9015 9016 // Make sure things are what we expect them to be... 9017 MOZ_ASSERT(request == mCachePump || request == mTransactionPump, 9018 "Unexpected request"); 9019 9020 MOZ_ASSERT(mRaceCacheWithNetwork || !(mTransactionPump && mCachePump) || 9021 LoadCachedContentIsPartial() || LoadTransactionReplaced(), 9022 "If we have both pumps, we're racing cache with network, the cache" 9023 " content is partial, or the cache entry was revalidated and " 9024 "OnStopRequest was not called yet for the transaction pump."); 9025 9026 StoreAfterOnStartRequestBegun(true); 9027 if (mOnStartRequestTimestamp.IsNull()) { 9028 mOnStartRequestTimestamp = TimeStamp::Now(); 9029 } 9030 9031 mozilla::glean::networking::http_onstart_suspend_total_time 9032 .AccumulateRawDuration(mSuspendTotalTime); 9033 9034 if (mTransaction) { 9035 mProxyConnectResponseCode = mTransaction->GetProxyConnectResponseCode(); 9036 if (request == mTransactionPump) { 9037 StoreDataSentToChildProcess(mTransaction->DataSentToChildProcess()); 9038 } 9039 9040 if (!mSecurityInfo && !mCachePump) { 9041 // grab the security info from the connection object; the transaction 9042 // is guaranteed to own a reference to the connection. 9043 mSecurityInfo = mTransaction->SecurityInfo(); 9044 } 9045 9046 uint32_t stage = mTransaction->HTTPSSVCReceivedStage(); 9047 if (!LoadHTTPSSVCTelemetryReported() && stage != HTTPSSVC_NOT_USED) { 9048 glean::http::dns_httpssvc_record_receiving_stage.AccumulateSingleSample( 9049 stage); 9050 } 9051 9052 if (HTTPS_RR_IS_USED(stage)) { 9053 nsAutoCString suffix(LoadEchConfigUsed() ? "_ech_used" : ""); 9054 // Determine the result string based on the status. 9055 nsAutoCString result(NS_SUCCEEDED(mStatus) ? "success" : "failure"); 9056 result.Append(suffix); 9057 9058 mozilla::glean::networking::http_channel_onstart_success_https_rr 9059 .Get(result) 9060 .Add(1); 9061 StoreHasHTTPSRR(true); 9062 } 9063 9064 StoreLoadedBySocketProcess(mTransaction->AsHttpTransactionParent() != 9065 nullptr); 9066 9067 bool isTrr; 9068 bool echConfigUsed; 9069 mTransaction->GetNetworkAddresses(mSelfAddr, mPeerAddr, isTrr, 9070 mEffectiveTRRMode, mTRRSkipReason, 9071 echConfigUsed); 9072 // update IP AddressSpace for non-proxy connections and tests 9073 // We need to update the browsing context for tests as browser tests 9074 // uses local proxy to connect to 9075 // external domains 9076 if (!mProxyInfo || xpc::IsInAutomation()) { 9077 // If this is main document load or iframe store the IP Address space in 9078 // the browsing context 9079 UpdateCurrentIpAddressSpace(); 9080 } 9081 9082 StoreResolvedByTRR(isTrr); 9083 StoreEchConfigUsed(echConfigUsed); 9084 } else { // !mTransaction 9085 MaybeUpdateDocumentIPAddressSpaceFromCache(); 9086 } 9087 9088 if (!mCanceled && mTransaction && 9089 mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal()) { 9090 // We have to report telemetry before we actually attempt to redirect to 9091 // the fallback domain because doing so will change mStatus 9092 ReportSystemChannelTelemetry(mStatus); 9093 } 9094 9095 // don't enter this block if we're reading from the cache... 9096 if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) { 9097 // mTransactionPump doesn't hit OnInputStreamReady and call this until 9098 // all of the response headers have been acquired, so we can take 9099 // ownership of them from the transaction. 9100 RefPtr<nsHttpConnectionInfo> connInfo; 9101 mResponseHead = 9102 mTransaction->TakeResponseHeadAndConnInfo(getter_AddRefs(connInfo)); 9103 mSupportsHTTP3 = mTransaction->GetSupportsHTTP3(); 9104 // the response head may be null if the transaction was cancelled. in 9105 // which case we just need to call OnStartRequest/OnStopRequest. 9106 if (mResponseHead) { 9107 if (AntiTrackingUtils::ProcessStorageAccessHeadersShouldRetry(this)) { 9108 // force reload. Doom cache to avoid redirect loop 9109 if (mCacheEntry) { 9110 mCacheEntry->AsyncDoom(nullptr); 9111 } 9112 9113 auto storeAllowStorageAccess = [&](nsIChannel* aRedirectedChannel) { 9114 RefPtr<nsHttpChannel> httpChan = do_QueryObject(aRedirectedChannel); 9115 if (httpChan) { 9116 httpChan->StoreStorageAccessReloadChannel(true); 9117 } 9118 }; 9119 9120 rv = StartRedirectChannelToURI(mURI, 9121 nsIChannelEventSink::REDIRECT_INTERNAL, 9122 storeAllowStorageAccess); 9123 if (NS_FAILED(rv)) { 9124 Cancel(rv); 9125 return CallOnStartRequest(); 9126 } 9127 return NS_OK; 9128 } 9129 return ProcessResponse(connInfo); 9130 } 9131 9132 NS_WARNING("No response head in OnStartRequest"); 9133 } 9134 9135 // cache file could be deleted on our behalf, it could contain errors or 9136 // it failed to allocate memory, reload from network here. 9137 if (mCacheEntry && mCachePump && RECOVER_FROM_CACHE_FILE_ERROR(mStatus)) { 9138 LOG((" cache file error, reloading from server")); 9139 mCacheEntry->AsyncDoom(nullptr); 9140 rv = 9141 StartRedirectChannelToURI(mURI, nsIChannelEventSink::REDIRECT_INTERNAL); 9142 if (NS_SUCCEEDED(rv)) return NS_OK; 9143 } 9144 9145 // If this is a system principal request to an essential domain and we 9146 // currently have connectivity, then check if there's a fallback domain we 9147 // can use to retry. If so we redirect to the fallback domain. 9148 if (NS_FAILED(mStatus) && !mCanceled && 9149 mLoadInfo->TriggeringPrincipal()->IsSystemPrincipal()) { 9150 if (StaticPrefs::network_essential_domains_fallback() && 9151 hasConnectivity()) { 9152 auto passDomainCategory = [&](nsIChannel* aRedirectedChannel) { 9153 RefPtr<nsHttpChannel> httpChan = do_QueryObject(aRedirectedChannel); 9154 if (httpChan) { 9155 nsAutoCString host; 9156 mURI->GetHost(host); 9157 httpChan->mEssentialDomainCategory = 9158 Some(GetEssentialDomainCategory(host)); 9159 } 9160 }; 9161 9162 if (nsCOMPtr<nsIURI> fallbackURI = GetFallbackURI(mURI)) { 9163 rv = StartRedirectChannelToURI(fallbackURI, 9164 nsIChannelEventSink::REDIRECT_INTERNAL, 9165 passDomainCategory); 9166 if (NS_SUCCEEDED(rv)) { 9167 nsCOMPtr<nsIObserverService> obsService = 9168 services::GetObserverService(); 9169 if (obsService) 9170 obsService->NotifyObservers(static_cast<nsIHttpChannel*>(this), 9171 "httpchannel-fallback", nullptr); 9172 return NS_OK; 9173 } 9174 } 9175 } 9176 } 9177 9178 // avoid crashing if mListener happens to be null... 9179 if (!mListener) { 9180 MOZ_ASSERT_UNREACHABLE("mListener is null"); 9181 return NS_OK; 9182 } 9183 9184 rv = ProcessCrossOriginSecurityHeaders(); 9185 if (NS_FAILED(rv)) { 9186 mStatus = rv; 9187 HandleAsyncAbort(); 9188 return rv; 9189 } 9190 9191 // No process change is needed, so continue on to ContinueOnStartRequest1. 9192 return ContinueOnStartRequest1(rv); 9193 } 9194 9195 void nsHttpChannel::MaybeUpdateDocumentIPAddressSpaceFromCache() { 9196 MOZ_ASSERT(mLoadInfo); 9197 ExtContentPolicyType type = mLoadInfo->GetExternalContentPolicyType(); 9198 9199 // Update the IPAddressSpace in the BrowsingContext only for main or sub 9200 // document 9201 if (type != ExtContentPolicy::TYPE_DOCUMENT && 9202 type != ExtContentPolicy::TYPE_SUBDOCUMENT) { 9203 return; 9204 } 9205 9206 RefPtr<mozilla::dom::BrowsingContext> bc; 9207 mLoadInfo->GetTargetBrowsingContext(getter_AddRefs(bc)); 9208 9209 if (!bc || !mCacheEntry) { 9210 return; 9211 } 9212 9213 nsAutoCString ipAddrStr, portStr; 9214 mCacheEntry->GetMetaDataElement("peer-ip-address", getter_Copies(ipAddrStr)); 9215 mCacheEntry->GetMetaDataElement("peer-port", getter_Copies(portStr)); 9216 9217 nsresult rv; 9218 uint32_t port = portStr.ToInteger(&rv); 9219 9220 if (!ipAddrStr.IsEmpty() && NS_SUCCEEDED(rv)) { 9221 NetAddr ipAddr; 9222 rv = ipAddr.InitFromString(ipAddrStr, port); 9223 NS_ENSURE_SUCCESS_VOID(rv); 9224 bc->SetCurrentIPAddressSpace(ipAddr.GetIpAddressSpace()); 9225 } 9226 } 9227 9228 nsresult nsHttpChannel::OnPermissionPromptResult(bool aGranted, 9229 const nsACString& aType) { 9230 mWaitingForLNAPermission = false; 9231 9232 if (aGranted) { 9233 LOG( 9234 ("nsHttpChannel::OnPermissionPromptResult [this=%p] " 9235 "LNAPermissionRequest " 9236 "granted", 9237 this)); 9238 // we need to cache this data as permission manager is updated async and 9239 // might not be reflected immediately 9240 if (aType == LOCAL_HOST_PERMISSION_KEY) { 9241 mLNAPermission.mLocalHostPermission = LNAPermission::Granted; 9242 } 9243 9244 if (aType == LOCAL_NETWORK_PERMISSION_KEY) { 9245 mLNAPermission.mLocalNetworkPermission = LNAPermission::Granted; 9246 } 9247 // reset the transaction 9248 mTransaction = nullptr; 9249 9250 // resets streams and listener. Ensures we dont get any more callbacks to 9251 // nsHttpChannel from the pumps 9252 RefPtr<nsInputStreamPump> pump = do_QueryObject(mTransactionPump); 9253 if (pump) { 9254 pump->Reset(); 9255 } 9256 mTransactionPump = nullptr; 9257 9258 // reset the status as we are going to replay the transaction 9259 mStatus = nsresult::NS_OK; 9260 9261 // allow notifications for the channel 9262 Resume(); 9263 // replay the transaction with permisions granted 9264 return CallOrWaitForResume( 9265 [](auto* self) -> nsresult { return self->DoConnect(nullptr); }); 9266 } 9267 9268 // permission denied 9269 // resume the transaction pump, we should get the OnStopRequest Notification 9270 LOG( 9271 ("nsHttpChannel::OnPermissionPromptResult [this=%p] " 9272 "LNAPermissionRequest " 9273 "denied", 9274 this)); 9275 9276 Resume(); 9277 9278 if (aType == LOCAL_HOST_PERMISSION_KEY) { 9279 mLNAPermission.mLocalHostPermission = LNAPermission::Denied; 9280 } 9281 9282 if (aType == LOCAL_NETWORK_PERMISSION_KEY) { 9283 mLNAPermission.mLocalNetworkPermission = LNAPermission::Denied; 9284 } 9285 9286 if (!mSuspendCount) { 9287 return ContinueOnStartRequest1( 9288 nsresult::NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED); 9289 } 9290 // channel is suspended state. We will continue when the channel is 9291 // resumed 9292 return CallOrWaitForResume([](auto* self) { 9293 return self->ContinueOnStartRequest1( 9294 nsresult::NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED); 9295 }); 9296 } 9297 9298 nsresult nsHttpChannel::ContinueOnStartRequest1(nsresult result) { 9299 nsresult rv; 9300 9301 // if process selection failed, cancel this load. 9302 if (NS_FAILED(result) && !mCanceled) { 9303 Cancel(result); 9304 return CallOnStartRequest(); 9305 } 9306 9307 // before we start any content load, check for redirectTo being called 9308 // this code is executed mainly before we start load from the cache 9309 if (mAPIRedirectTo && !mCanceled) { 9310 nsAutoCString redirectToSpec; 9311 mAPIRedirectTo->first()->GetAsciiSpec(redirectToSpec); 9312 LOG((" redirectTo called with uri=%s", redirectToSpec.BeginReading())); 9313 9314 MOZ_ASSERT(!LoadOnStartRequestCalled()); 9315 PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2); 9316 rv = StartRedirectChannelToURI( 9317 mAPIRedirectTo->first(), 9318 mAPIRedirectTo->second() ? nsIChannelEventSink::REDIRECT_TEMPORARY | 9319 nsIChannelEventSink::REDIRECT_TRANSPARENT 9320 : nsIChannelEventSink::REDIRECT_TEMPORARY); 9321 mAPIRedirectTo = Nothing(); 9322 if (NS_SUCCEEDED(rv)) { 9323 return NS_OK; 9324 } 9325 PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest2); 9326 } 9327 9328 // Hack: ContinueOnStartRequest2 uses NS_OK to detect successful redirects, 9329 // so we distinguish this codepath (a non-redirect that's processing 9330 // normally) by passing in a bogus error code. 9331 return ContinueOnStartRequest2(NS_BINDING_FAILED); 9332 } 9333 9334 nsresult nsHttpChannel::ContinueOnStartRequest2(nsresult result) { 9335 if (NS_SUCCEEDED(result)) { 9336 // Redirect has passed through, we don't want to go on with this 9337 // channel. It will now be canceled by the redirect handling code 9338 // that called this function. 9339 return NS_OK; 9340 } 9341 9342 // on proxy errors, try to failover 9343 if (mConnectionInfo->ProxyInfo() && 9344 (mStatus == NS_ERROR_PROXY_CONNECTION_REFUSED || 9345 mStatus == NS_ERROR_UNKNOWN_PROXY_HOST || 9346 mStatus == NS_ERROR_NET_TIMEOUT || mStatus == NS_ERROR_NET_RESET)) { 9347 PushRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3); 9348 if (NS_SUCCEEDED(ProxyFailover())) { 9349 mProxyConnectResponseCode = 0; 9350 return NS_OK; 9351 } 9352 PopRedirectAsyncFunc(&nsHttpChannel::ContinueOnStartRequest3); 9353 } 9354 9355 // Hack: ContinueOnStartRequest3 uses NS_OK to detect successful redirects, 9356 // so we distinguish this codepath (a non-redirect that's processing 9357 // normally) by passing in a bogus error code. 9358 return ContinueOnStartRequest3(NS_BINDING_FAILED); 9359 } 9360 9361 nsresult nsHttpChannel::ContinueOnStartRequest3(nsresult result) { 9362 if (NS_SUCCEEDED(result)) { 9363 // Redirect has passed through, we don't want to go on with this 9364 // channel. It will now be canceled by the redirect handling code 9365 // that called this function. 9366 return NS_OK; 9367 } 9368 9369 return CallOnStartRequest(); 9370 } 9371 9372 static void ReportHTTPSRRTelemetry( 9373 const Maybe<nsCOMPtr<nsIDNSHTTPSSVCRecord>>& aMaybeRecord) { 9374 bool hasHTTPSRR = aMaybeRecord && (aMaybeRecord.ref() != nullptr); 9375 if (!hasHTTPSRR) { 9376 mozilla::glean::networking::https_rr_presented.Get("none"_ns).Add(1); 9377 return; 9378 } 9379 9380 const nsCOMPtr<nsIDNSHTTPSSVCRecord>& record = aMaybeRecord.ref(); 9381 nsCOMPtr<nsISVCBRecord> svcbRecord; 9382 if (NS_SUCCEEDED(record->GetServiceModeRecord(false, false, 9383 getter_AddRefs(svcbRecord)))) { 9384 MOZ_ASSERT(svcbRecord); 9385 9386 Maybe<std::tuple<nsCString, SupportedAlpnRank>> alpn = 9387 svcbRecord->GetAlpn(); 9388 bool isHttp3 = alpn ? IsHttp3(std::get<1>(*alpn)) : false; 9389 mozilla::glean::networking::https_rr_presented 9390 .Get(isHttp3 ? "presented_with_http3"_ns : "presented"_ns) 9391 .Add(1); 9392 } 9393 } 9394 9395 static void RecordHttpChanDispositionGlean(ChannelDisposition chanDisposition) { 9396 switch (chanDisposition) { 9397 case kHttpCanceled: 9398 mozilla::glean::networking::http_channel_disposition 9399 .Get("http_cancelled"_ns) 9400 .Add(1); 9401 break; 9402 case kHttpDisk: 9403 mozilla::glean::networking::http_channel_disposition.Get("http_disk"_ns) 9404 .Add(1); 9405 break; 9406 case kHttpNetOK: 9407 mozilla::glean::networking::http_channel_disposition.Get("http_net_ok"_ns) 9408 .Add(1); 9409 break; 9410 case kHttpNetEarlyFail: 9411 mozilla::glean::networking::http_channel_disposition 9412 .Get("http_net_early_fail"_ns) 9413 .Add(1); 9414 break; 9415 case kHttpNetLateFail: 9416 mozilla::glean::networking::http_channel_disposition 9417 .Get("http_net_late_fail"_ns) 9418 .Add(1); 9419 break; 9420 case kHttpsCanceled: 9421 mozilla::glean::networking::http_channel_disposition 9422 .Get("https_cancelled"_ns) 9423 .Add(1); 9424 break; 9425 case kHttpsDisk: 9426 mozilla::glean::networking::http_channel_disposition.Get("http_disk"_ns) 9427 .Add(1); 9428 break; 9429 case kHttpsNetOK: 9430 mozilla::glean::networking::http_channel_disposition 9431 .Get("https_net_ok"_ns) 9432 .Add(1); 9433 break; 9434 case kHttpsNetEarlyFail: 9435 mozilla::glean::networking::http_channel_disposition 9436 .Get("https_net_early_fail"_ns) 9437 .Add(1); 9438 break; 9439 case kHttpsNetLateFail: 9440 mozilla::glean::networking::http_channel_disposition 9441 .Get("https_net_late_fail"_ns) 9442 .Add(1); 9443 break; 9444 default: 9445 MOZ_ASSERT_UNREACHABLE("Unknown value for chanDisposition"); 9446 } 9447 } 9448 9449 static nsLiteralCString HttpChanDispositionToTelemetryLabel( 9450 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE upgradeChanDisposition) { 9451 if (upgradeChanDisposition == 9452 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::cancel) { 9453 return "cancel"_ns; 9454 } 9455 if (upgradeChanDisposition == 9456 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::disk) { 9457 return "disk"_ns; 9458 } 9459 if (upgradeChanDisposition == 9460 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netOk) { 9461 return "net_ok"_ns; 9462 } 9463 if (upgradeChanDisposition == 9464 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netEarlyFail) { 9465 return "net_early_fail"_ns; 9466 } 9467 if (upgradeChanDisposition == 9468 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netLateFail) { 9469 return "net_late_fail"_ns; 9470 } 9471 9472 MOZ_ASSERT_UNREACHABLE("Unknown value for upgradeChanDecomposition"); 9473 return "other"_ns; 9474 } 9475 9476 nsresult nsHttpChannel::LogConsoleError(const char* aTag) { 9477 nsCOMPtr<nsIConsoleService> console(mozilla::components::Console::Service()); 9478 NS_ENSURE_TRUE(console, NS_ERROR_OUT_OF_MEMORY); 9479 9480 nsCOMPtr<nsILoadInfo> loadInfo = LoadInfo(); 9481 NS_ENSURE_TRUE(console, NS_ERROR_OUT_OF_MEMORY); 9482 uint64_t innerWindowID = loadInfo->GetInnerWindowID(); 9483 9484 nsAutoString errorText; 9485 nsresult rv = nsContentUtils::GetLocalizedString( 9486 nsContentUtils::eNECKO_PROPERTIES, aTag, errorText); 9487 NS_ENSURE_SUCCESS(rv, rv); 9488 9489 nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); 9490 NS_ENSURE_TRUE(error, NS_ERROR_OUT_OF_MEMORY); 9491 9492 rv = 9493 error->InitWithSourceURI(errorText, mURI, 0, 0, nsIScriptError::errorFlag, 9494 "Invalid HTTP Status Lines"_ns, innerWindowID); 9495 NS_ENSURE_SUCCESS(rv, rv); 9496 console->LogMessage(error); 9497 return NS_OK; 9498 } 9499 9500 static void RecordHTTPSUpgradeTelemetry(nsIURI* aURI, nsILoadInfo* aLoadInfo) { 9501 // we record https telemetry only for top-level loads 9502 if (aLoadInfo->GetExternalContentPolicyType() != 9503 ExtContentPolicy::TYPE_DOCUMENT) { 9504 return; 9505 } 9506 9507 // exempt loopback addresses because we only want to record telemetry 9508 // for actual web requests 9509 if (nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(aURI)) { 9510 return; 9511 } 9512 9513 // todo: for now we don't record form submissions, only 9514 // top-level document loads. Once Bug 1720500 is fixed, we can 9515 // consider recording form submissions too. 9516 if (aLoadInfo->GetIsFormSubmission()) { 9517 return; 9518 } 9519 9520 bool isHTTPS = aURI->SchemeIs("https"); 9521 9522 nsILoadInfo::HTTPSUpgradeTelemetryType httpsTelemetry = 9523 aLoadInfo->GetHttpsUpgradeTelemetry(); 9524 switch (httpsTelemetry) { 9525 case nsILoadInfo::NOT_INITIALIZED: { 9526 if (isHTTPS) { 9527 // Bug 1912222: We should never encounter NOT_INITIALIZED values, 9528 // though we still want to know whether those loads are HTTPS 9529 // or not. Eventually we'll want to remove this if-clause 9530 // again and only report "not_initialized". 9531 mozilla::glean::networking::http_to_https_upgrade_reason 9532 .Get("not_initialized_https"_ns) 9533 .Add(1); 9534 return; 9535 } 9536 mozilla::glean::networking::http_to_https_upgrade_reason 9537 .Get("not_initialized"_ns) 9538 .Add(1); 9539 break; 9540 } 9541 case nsILoadInfo::NO_UPGRADE: { 9542 if (isHTTPS) { 9543 // Bug 1912222: We should rearely encounter NO_UPGRADE values, though 9544 // we still want to ensure those are not HTTPS. Eventually we'll want 9545 // to remove this if-clause again and only report "no_upgrade". 9546 mozilla::glean::networking::http_to_https_upgrade_reason 9547 .Get("no_upgrade_https"_ns) 9548 .Add(1); 9549 return; 9550 } 9551 mozilla::glean::networking::http_to_https_upgrade_reason 9552 .Get("no_upgrade"_ns) 9553 .Add(1); 9554 break; 9555 } 9556 case nsILoadInfo::ALREADY_HTTPS: 9557 mozilla::glean::networking::http_to_https_upgrade_reason 9558 .Get("already_https"_ns) 9559 .Add(1); 9560 break; 9561 case nsILoadInfo::HSTS: 9562 mozilla::glean::networking::http_to_https_upgrade_reason.Get("hsts"_ns) 9563 .Add(1); 9564 break; 9565 case nsILoadInfo::HTTPS_ONLY_UPGRADE: 9566 mozilla::glean::networking::http_to_https_upgrade_reason 9567 .Get("https_only_upgrade"_ns) 9568 .Add(1); 9569 break; 9570 case nsILoadInfo::HTTPS_ONLY_UPGRADE_DOWNGRADE: 9571 mozilla::glean::networking::http_to_https_upgrade_reason 9572 .Get("https_only_upgrade_downgrade"_ns) 9573 .Add(1); 9574 break; 9575 case nsILoadInfo::HTTPS_FIRST_UPGRADE: 9576 mozilla::glean::networking::http_to_https_upgrade_reason 9577 .Get("https_first_upgrade"_ns) 9578 .Add(1); 9579 break; 9580 case nsILoadInfo::HTTPS_FIRST_UPGRADE_DOWNGRADE: 9581 mozilla::glean::networking::http_to_https_upgrade_reason 9582 .Get("https_first_upgrade_downgrade"_ns) 9583 .Add(1); 9584 break; 9585 case nsILoadInfo::HTTPS_FIRST_SCHEMELESS_UPGRADE: 9586 mozilla::glean::networking::http_to_https_upgrade_reason 9587 .Get("https_first_schemeless_upgrade"_ns) 9588 .Add(1); 9589 break; 9590 case nsILoadInfo::HTTPS_FIRST_SCHEMELESS_UPGRADE_DOWNGRADE: 9591 mozilla::glean::networking::http_to_https_upgrade_reason 9592 .Get("https_first_schemeless_upgrade_downgrade"_ns) 9593 .Add(1); 9594 break; 9595 case nsILoadInfo::CSP_UIR: 9596 mozilla::glean::networking::http_to_https_upgrade_reason.Get("csp_uir"_ns) 9597 .Add(1); 9598 break; 9599 case nsILoadInfo::HTTPS_RR: 9600 mozilla::glean::networking::http_to_https_upgrade_reason 9601 .Get("https_rr"_ns) 9602 .Add(1); 9603 break; 9604 case nsILoadInfo::WEB_EXTENSION_UPGRADE: 9605 mozilla::glean::networking::http_to_https_upgrade_reason 9606 .Get("web_extension_upgrade"_ns) 9607 .Add(1); 9608 break; 9609 case nsILoadInfo::UPGRADE_EXCEPTION: 9610 mozilla::glean::networking::http_to_https_upgrade_reason 9611 .Get("upgrade_exception"_ns) 9612 .Add(1); 9613 break; 9614 case nsILoadInfo::SKIP_HTTPS_UPGRADE: 9615 mozilla::glean::networking::http_to_https_upgrade_reason 9616 .Get("skip_upgrade"_ns) 9617 .Add(1); 9618 break; 9619 default: 9620 MOZ_ASSERT(false, "what telemetry flag is set to end up here?"); 9621 } 9622 } 9623 9624 static void RecordIPAddressSpaceTelemetry(bool aLoadSuccess, nsIURI* aURI, 9625 nsILoadInfo* aLoadInfo, 9626 NetAddr& aPeerAddr) { 9627 // if the load was not successful, then there is nothing to record here 9628 if (!aLoadSuccess) { 9629 return; 9630 } 9631 9632 // we record https telemetry only for top-level loads 9633 if (aLoadInfo->GetExternalContentPolicyType() != 9634 ExtContentPolicy::TYPE_DOCUMENT) { 9635 return; 9636 } 9637 9638 if (aURI->SchemeIs("https")) { 9639 mozilla::glean::networking::https_http_or_local.Get("load_is_https"_ns) 9640 .Add(1); 9641 return; 9642 } 9643 9644 if (aURI->SchemeIs("http")) { 9645 if (aPeerAddr.IsIPAddrLocal() || aPeerAddr.IsLoopbackAddr()) { 9646 mozilla::glean::networking::https_http_or_local 9647 .Get("load_is_http_for_local_domain"_ns) 9648 .Add(1); 9649 } else { 9650 mozilla::glean::networking::https_http_or_local.Get("load_is_http"_ns) 9651 .Add(1); 9652 } 9653 return; 9654 } 9655 } 9656 9657 static void RecordLNATelemetry(nsHttpChannel* aChannel, bool aLoadSuccess) { 9658 // Extract data from channel 9659 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 9660 nsCOMPtr<nsIURI> uri; 9661 aChannel->GetURI(getter_AddRefs(uri)); 9662 NetAddr peerAddr = aChannel->GetPeerAddr(); 9663 const nsACString& promptAction = aChannel->GetLNAPromptAction(); 9664 9665 if (!loadInfo || !uri) { 9666 return; 9667 } 9668 9669 RefPtr<mozilla::dom::BrowsingContext> bc; 9670 loadInfo->GetBrowsingContext(getter_AddRefs(bc)); 9671 9672 nsILoadInfo::IPAddressSpace parentAddressSpace = 9673 nsILoadInfo::IPAddressSpace::Unknown; 9674 if (!bc) { 9675 parentAddressSpace = loadInfo->GetParentIpAddressSpace(); 9676 } else { 9677 parentAddressSpace = bc->GetCurrentIPAddressSpace(); 9678 } 9679 9680 // Early return if NOT LNA - don't record telemetry or log 9681 if (!mozilla::net::IsLocalOrPrivateNetworkAccess( 9682 parentAddressSpace, loadInfo->GetIpAddressSpace())) { 9683 return; 9684 } 9685 9686 if (aLoadSuccess) { 9687 mozilla::glean::networking::local_network_access.Get("success"_ns).Add(1); 9688 } else { 9689 mozilla::glean::networking::local_network_access.Get("failure"_ns).Add(1); 9690 } 9691 9692 uint16_t port = 0; 9693 if (NS_SUCCEEDED(peerAddr.GetPort(&port))) { 9694 mozilla::glean::networking::local_network_access_port 9695 .AccumulateSingleSample(port); 9696 } 9697 9698 // label format is <parentAddressSpace>_to_<targetAddressSpace>_<scheme> 9699 // At this point we are sure that the request is a LNA, 9700 // Hence we can safely assume few conditions to construct the label 9701 nsAutoCString glean_lna_label; 9702 if (parentAddressSpace == nsILoadInfo::IPAddressSpace::Public) { 9703 glean_lna_label.Append("public_to_"_ns); 9704 } else { 9705 glean_lna_label.Append("private_to_"_ns); 9706 } 9707 if (loadInfo->GetIpAddressSpace() == nsILoadInfo::IPAddressSpace::Private) { 9708 glean_lna_label.Append("private_"_ns); 9709 } else { 9710 glean_lna_label.Append("local_"_ns); 9711 } 9712 if (uri->SchemeIs("https")) { 9713 glean_lna_label.Append("https"_ns); 9714 } else { 9715 glean_lna_label.Append("http"_ns); 9716 } 9717 9718 mozilla::glean::networking::local_network_access.Get(glean_lna_label).Add(1); 9719 9720 // Get top-level site (main window principal) - TLD+1 9721 nsAutoCString topLevelSite; 9722 if (bc && bc->Top()) { 9723 if (bc->Top()->Canonical()) { 9724 RefPtr<mozilla::dom::WindowGlobalParent> topWindowGlobal = 9725 bc->Top()->Canonical()->GetCurrentWindowGlobal(); 9726 if (topWindowGlobal) { 9727 nsCOMPtr<nsIPrincipal> topPrincipal = 9728 topWindowGlobal->DocumentPrincipal(); 9729 if (topPrincipal) { 9730 (void)topPrincipal->GetBaseDomain(topLevelSite); 9731 } 9732 } 9733 } 9734 } 9735 9736 // Get initiator (triggering principal) - TLD+1 9737 nsAutoCString initiator; 9738 nsCOMPtr<nsIPrincipal> triggeringPrincipal; 9739 loadInfo->GetTriggeringPrincipal(getter_AddRefs(triggeringPrincipal)); 9740 if (triggeringPrincipal) { 9741 (void)triggeringPrincipal->GetBaseDomain(initiator); 9742 } 9743 9744 bool isSecureContext = 9745 triggeringPrincipal 9746 ? triggeringPrincipal->GetIsOriginPotentiallyTrustworthy() 9747 : false; 9748 9749 // Get target host - TLD+1 9750 nsCOMPtr<nsIEffectiveTLDService> eTLDService = 9751 mozilla::components::EffectiveTLD::Service(); 9752 nsAutoCString targetHost; 9753 if (eTLDService) { 9754 (void)eTLDService->GetBaseDomain(uri, 0, targetHost); 9755 } 9756 9757 // Get target IP address 9758 nsCString targetIp = peerAddr.ToString(); 9759 9760 // Determine protocol from LoadInfo 9761 nsAutoCString protocol; 9762 ExtContentPolicyType contentType = loadInfo->GetExternalContentPolicyType(); 9763 switch (contentType) { 9764 case ExtContentPolicyType::TYPE_WEBSOCKET: 9765 protocol.AssignLiteral("websocket"); 9766 break; 9767 case ExtContentPolicyType::TYPE_WEB_TRANSPORT: 9768 protocol.AssignLiteral("webtransport"); 9769 break; 9770 case ExtContentPolicyType::TYPE_FETCH: 9771 protocol.AssignLiteral("fetch"); 9772 break; 9773 case ExtContentPolicyType::TYPE_XMLHTTPREQUEST: 9774 protocol.AssignLiteral("xhr"); 9775 break; 9776 default: 9777 if (uri->SchemeIs("https")) { 9778 protocol.AssignLiteral("https"); 9779 } else { 9780 protocol.AssignLiteral("http"); 9781 } 9782 break; 9783 } 9784 9785 // Record the event 9786 glean::networking::LocalNetworkAccessConnectionExtra extra = { 9787 .initiator = initiator.IsEmpty() ? Nothing() : Some(initiator), 9788 .isSecureContext = Some(isSecureContext), 9789 .loadSuccess = Some(aLoadSuccess), 9790 .promptAction = 9791 promptAction.IsEmpty() ? Nothing() : Some(nsCString(promptAction)), 9792 .protocol = Some(protocol), 9793 .targetHost = targetHost.IsEmpty() ? Nothing() : Some(targetHost), 9794 .targetIp = Some(targetIp), 9795 .targetPort = Some(port), 9796 .topLevelSite = topLevelSite.IsEmpty() ? Nothing() : Some(topLevelSite), 9797 9798 }; 9799 glean::networking::local_network_access_connection.Record(Some(extra)); 9800 9801 ReportLNAAccessToConsole(aChannel, "LocalNetworkAccessDetected", 9802 promptAction); 9803 } 9804 9805 NS_IMETHODIMP 9806 nsHttpChannel::OnStopRequest(nsIRequest* request, nsresult status) { 9807 MOZ_ASSERT(!mAsyncOpenTime.IsNull()); 9808 AUTO_PROFILER_LABEL("nsHttpChannel::OnStopRequest", NETWORK); 9809 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnStopRequest", NETWORK, 9810 Flow::FromPointer(this)); 9811 9812 LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%" PRIx32 "]\n", 9813 this, request, static_cast<uint32_t>(status))); 9814 9815 LOG(("OnStopRequest %p requestFromCache: %d mFirstResponseSource: %d\n", this, 9816 request == mCachePump, static_cast<int32_t>(mFirstResponseSource))); 9817 9818 MOZ_ASSERT(NS_IsMainThread(), 9819 "OnStopRequest should only be called from the main thread"); 9820 9821 if (mStatus == NS_ERROR_PARSING_HTTP_STATUS_LINE) { 9822 (void)LogConsoleError("InvalidHTTPResponseStatusLine"); 9823 } 9824 9825 if (WRONG_RACING_RESPONSE_SOURCE(request)) { 9826 return NS_OK; 9827 } 9828 9829 // It's possible that LoadUseHTTPSSVC() is false, but we already have 9830 // mHTTPSSVCRecord. 9831 if (LoadUseHTTPSSVC() || mHTTPSSVCRecord) { 9832 ReportHTTPSRRTelemetry(mHTTPSSVCRecord); 9833 } 9834 9835 // If this load failed because of a security error, it may be because we 9836 // are in a captive portal - trigger an async check to make sure. 9837 int32_t nsprError = -1 * NS_ERROR_GET_CODE(status); 9838 if (mozilla::psm::IsNSSErrorCode(nsprError) && IsHTTPS()) { 9839 gIOService->RecheckCaptivePortal(); 9840 } 9841 9842 if (request == mCachePump) { 9843 mCacheReadEnd = TimeStamp::Now(); 9844 } 9845 9846 // allow content to be cached if it was loaded successfully (bug #482935) 9847 bool contentComplete = NS_SUCCEEDED(status); 9848 9849 // honor the cancelation status even if the underlying transaction 9850 // completed. 9851 if (mCanceled || NS_FAILED(mStatus)) status = mStatus; 9852 9853 if (LoadCachedContentIsPartial()) { 9854 if (NS_SUCCEEDED(status)) { 9855 // mTransactionPump should be suspended 9856 MOZ_ASSERT(request != mTransactionPump, 9857 "byte-range transaction finished prematurely"); 9858 9859 if (request == mCachePump) { 9860 bool streamDone; 9861 status = OnDoneReadingPartialCacheEntry(&streamDone); 9862 if (NS_SUCCEEDED(status) && !streamDone) return status; 9863 // otherwise, fall through and fire OnStopRequest... 9864 } else if (request == mTransactionPump) { 9865 MOZ_ASSERT(LoadConcurrentCacheAccess()); 9866 } else { 9867 MOZ_ASSERT_UNREACHABLE("unexpected request"); 9868 } 9869 } 9870 // Do not to leave the transaction in a suspended state in error cases. 9871 if (NS_FAILED(status) && mTransaction) { 9872 nsresult rv = gHttpHandler->CancelTransaction(mTransaction, status); 9873 if (NS_FAILED(rv)) { 9874 LOG((" CancelTransaction failed (%08x)", static_cast<uint32_t>(rv))); 9875 } 9876 } 9877 } 9878 9879 nsCOMPtr<nsICompressConvStats> conv = do_QueryInterface(mCompressListener); 9880 if (conv) { 9881 conv->GetDecodedDataLength(&mDecodedBodySize); 9882 } 9883 9884 bool isFromNet = request == mTransactionPump; 9885 9886 if (mTransaction) { 9887 // determine if we should call DoAuthRetry 9888 bool authRetry = (mAuthRetryPending && NS_SUCCEEDED(status) && 9889 // we should only auth retry in this channel if are not 9890 // redirecting a new channel for authentication retries 9891 !StaticPrefs::network_auth_use_redirect_for_retries()); 9892 9893 StoreStronglyFramed(mTransaction->ResponseIsComplete()); 9894 LOG(("nsHttpChannel %p has a strongly framed transaction: %d", this, 9895 LoadStronglyFramed())); 9896 9897 // Save the reference of |mTransaction| to |transactionWithStickyConn| 9898 // when it has a sticky connection. 9899 // In the case we need to retry an authentication request, we need to 9900 // reuse the connection of |transactionWithStickyConn|. 9901 RefPtr<HttpTransactionShell> transactionWithStickyConn; 9902 if (mCaps & NS_HTTP_STICKY_CONNECTION || 9903 mTransaction->HasStickyConnection()) { 9904 transactionWithStickyConn = mTransaction; 9905 // Make sure we use the updated caps and connection info from transaction. 9906 // We read these values when the transaction is already closed, so there 9907 // should be no race. 9908 if (mTransaction->Http2Disabled()) { 9909 mCaps |= NS_HTTP_DISALLOW_SPDY; 9910 } 9911 if (mTransaction->Http3Disabled()) { 9912 mCaps |= NS_HTTP_DISALLOW_HTTP3; 9913 } 9914 mConnectionInfo = mTransaction->GetConnInfo(); 9915 LOG((" transaction %p has sticky connection", 9916 transactionWithStickyConn.get())); 9917 } 9918 9919 // this code relies on the code in nsHttpTransaction::Close, which 9920 // tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to 9921 // keep the connection around after the transaction is finished. 9922 // 9923 LOG((" mAuthRetryPending=%d, status=%" PRIx32 ", sticky conn cap=%d", 9924 static_cast<bool>(mAuthRetryPending), static_cast<uint32_t>(status), 9925 mCaps & NS_HTTP_STICKY_CONNECTION)); 9926 // We must check caps for stickinness also on the transaction because it 9927 // might have been updated by the transaction itself during inspection of 9928 // the reposnse headers yet on the socket thread (found connection based 9929 // auth schema). 9930 9931 if ((NS_FAILED(status)) && transactionWithStickyConn) { 9932 // Close (don't reuse) the sticky connection if this channel has been 9933 // cancelled. There are proxy servers known to get confused when we send 9934 // a new request over such a half-stated connection. 9935 if (!LoadAuthConnectionRestartable()) { 9936 LOG((" not reusing a half-authenticated sticky connection")); 9937 transactionWithStickyConn->DontReuseConnection(); 9938 } 9939 } 9940 9941 if (mCaps & NS_HTTP_STICKY_CONNECTION) { 9942 mTransaction->SetH2WSConnRefTaken(); 9943 } 9944 9945 mTransferSize = mTransaction->GetTransferSize(); 9946 mRequestSize = mTransaction->GetRequestSize(); 9947 9948 RecordHTTPSUpgradeTelemetry(mURI, mLoadInfo); 9949 9950 RecordIPAddressSpaceTelemetry(NS_SUCCEEDED(mStatus), mURI, mLoadInfo, 9951 mPeerAddr); 9952 RecordLNATelemetry(this, NS_SUCCEEDED(mStatus)); 9953 9954 uint32_t flags; 9955 if (mStatus == NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED && 9956 StaticPrefs::network_lna_block_trackers() && 9957 NS_SUCCEEDED( 9958 mLoadInfo->GetTriggeringThirdPartyClassificationFlags(&flags)) && 9959 flags != 0) { 9960 mozilla::glean::networking::local_network_blocked_tracker.Add(1); 9961 } 9962 9963 // If we are using the transaction to serve content, we also save the 9964 // time since async open in the cache entry so we can compare telemetry 9965 // between cache and net response. 9966 // Do not store the time of conditional requests because even if we 9967 // fetch the data from the server, the time includes loading of the old 9968 // cache entry which would skew the network load time. 9969 if (request == mTransactionPump && mCacheEntry && !mDidReval && 9970 !LoadCustomConditionalRequest() && !mAsyncOpenTime.IsNull() && 9971 !mOnStartRequestTimestamp.IsNull()) { 9972 uint64_t onStartTime = 9973 (mOnStartRequestTimestamp - mAsyncOpenTime).ToMilliseconds(); 9974 uint64_t onStopTime = 9975 (TimeStamp::Now() - mAsyncOpenTime).ToMilliseconds(); 9976 (void)mCacheEntry->SetNetworkTimes(onStartTime, onStopTime); 9977 } 9978 9979 mResponseTrailers = mTransaction->TakeResponseTrailers(); 9980 9981 if (nsIOService::UseSocketProcess() && mTransaction) { 9982 mOnStopRequestStartTime = mTransaction->GetOnStopRequestStartTime(); 9983 if (!mOnStopRequestStartTime.IsNull()) { 9984 PerfStats::RecordMeasurement( 9985 PerfStats::Metric::OnStopRequestSocketToParent, 9986 TimeStamp::Now() - mOnStopRequestStartTime); 9987 } 9988 } else { 9989 mOnStopRequestStartTime = TimeStamp::Now(); 9990 } 9991 9992 // at this point, we're done with the transaction 9993 mTransactionTimings = mTransaction->Timings(); 9994 mTransaction = nullptr; 9995 mTransactionPump = nullptr; 9996 9997 // We no longer need the dns prefetch object 9998 if (mDNSPrefetch && mDNSPrefetch->TimingsValid() && 9999 !mTransactionTimings.requestStart.IsNull() && 10000 !mTransactionTimings.connectStart.IsNull() && 10001 mDNSPrefetch->EndTimestamp() <= mTransactionTimings.connectStart) { 10002 // We only need the domainLookup timestamps when not using a 10003 // persistent connection, meaning if the endTimestamp < connectStart 10004 mTransactionTimings.domainLookupStart = mDNSPrefetch->StartTimestamp(); 10005 mTransactionTimings.domainLookupEnd = mDNSPrefetch->EndTimestamp(); 10006 } 10007 mDNSPrefetch = nullptr; 10008 10009 // handle auth retry... 10010 if (authRetry) { 10011 mAuthRetryPending = false; 10012 auto continueOSR = [authRetry, isFromNet, contentComplete, 10013 transactionWithStickyConn](auto* self, 10014 nsresult aStatus) { 10015 return self->ContinueOnStopRequestAfterAuthRetry( 10016 aStatus, authRetry, isFromNet, contentComplete, 10017 transactionWithStickyConn); 10018 }; 10019 status = DoAuthRetry(transactionWithStickyConn, continueOSR); 10020 if (NS_SUCCEEDED(status)) { 10021 return NS_OK; 10022 } 10023 } 10024 return ContinueOnStopRequestAfterAuthRetry(status, authRetry, isFromNet, 10025 contentComplete, 10026 transactionWithStickyConn); 10027 } 10028 10029 return ContinueOnStopRequest(status, isFromNet, contentComplete); 10030 } 10031 10032 nsresult nsHttpChannel::ContinueOnStopRequestAfterAuthRetry( 10033 nsresult aStatus, bool aAuthRetry, bool aIsFromNet, bool aContentComplete, 10034 HttpTransactionShell* aTransWithStickyConn) { 10035 LOG( 10036 ("nsHttpChannel::ContinueOnStopRequestAfterAuthRetry " 10037 "[this=%p, aStatus=%" PRIx32 10038 " aAuthRetry=%d, aIsFromNet=%d, aTransWithStickyConn=%p]\n", 10039 this, static_cast<uint32_t>(aStatus), aAuthRetry, aIsFromNet, 10040 aTransWithStickyConn)); 10041 10042 if (aAuthRetry && NS_SUCCEEDED(aStatus)) { 10043 return NS_OK; 10044 } 10045 10046 // If DoAuthRetry failed, or if we have been cancelled since showing 10047 // the auth. dialog, then we need to send OnStartRequest now 10048 if (aAuthRetry || (mAuthRetryPending && NS_FAILED(aStatus))) { 10049 MOZ_ASSERT(NS_FAILED(aStatus), "should have a failure code here"); 10050 // NOTE: since we have a failure status, we can ignore the return 10051 // value from onStartRequest. 10052 LOG((" calling mListener->OnStartRequest [this=%p, listener=%p]\n", this, 10053 mListener.get())); 10054 if (mListener) { 10055 MOZ_ASSERT(!LoadOnStartRequestCalled(), 10056 "We should not call OnStartRequest twice."); 10057 if (!LoadOnStartRequestCalled()) { 10058 nsCOMPtr<nsIStreamListener> listener(mListener); 10059 StoreOnStartRequestCalled(true); 10060 listener->OnStartRequest(this); 10061 } 10062 } else { 10063 StoreOnStartRequestCalled(true); 10064 NS_WARNING("OnStartRequest skipped because of null listener"); 10065 } 10066 mAuthRetryPending = false; 10067 } 10068 10069 // if this transaction has been replaced, then bail. 10070 if (LoadTransactionReplaced()) { 10071 LOG(("Transaction replaced\n")); 10072 // This was just the network check for a 304 response. 10073 mFirstResponseSource = RESPONSE_PENDING; 10074 return NS_OK; 10075 } 10076 10077 bool upgradeWebsocket = mUpgradeProtocolCallback && aTransWithStickyConn && 10078 mResponseHead && 10079 ((mResponseHead->Status() == 101 && 10080 mResponseHead->Version() == HttpVersion::v1_1) || 10081 (mResponseHead->Status() == 200 && 10082 mResponseHead->Version() == HttpVersion::v2_0)); 10083 10084 bool upgradeConnect = mUpgradeProtocolCallback && aTransWithStickyConn && 10085 (mCaps & NS_HTTP_CONNECT_ONLY) && mResponseHead && 10086 mResponseHead->Status() == 200; 10087 10088 if (upgradeWebsocket || upgradeConnect) { 10089 if (nsIOService::UseSocketProcess() && upgradeConnect) { 10090 // TODO: Support connection upgrade for socket process in bug 1632809. 10091 (void)mUpgradeProtocolCallback->OnUpgradeFailed(NS_ERROR_NOT_IMPLEMENTED); 10092 return ContinueOnStopRequest(aStatus, aIsFromNet, aContentComplete); 10093 } 10094 10095 nsresult rv = gHttpHandler->CompleteUpgrade(aTransWithStickyConn, 10096 mUpgradeProtocolCallback); 10097 mUpgradeProtocolCallback = nullptr; 10098 if (NS_FAILED(rv)) { 10099 LOG((" CompleteUpgrade failed with %" PRIx32, 10100 static_cast<uint32_t>(rv))); 10101 10102 // This ensures that WebSocketChannel::OnStopRequest will be 10103 // called with an error so the session is properly aborted. 10104 aStatus = rv; 10105 } 10106 } 10107 10108 return ContinueOnStopRequest(aStatus, aIsFromNet, aContentComplete); 10109 } 10110 10111 nsresult nsHttpChannel::ContinueOnStopRequest(nsresult aStatus, bool aIsFromNet, 10112 bool aContentComplete) { 10113 LOG( 10114 ("nsHttpChannel::ContinueOnStopRequest " 10115 "[this=%p aStatus=%" PRIx32 ", aIsFromNet=%d]\n", 10116 this, static_cast<uint32_t>(aStatus), aIsFromNet)); 10117 10118 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::ContinueOnStopRequest", NETWORK, 10119 Flow::FromPointer(this)); 10120 // HTTP_CHANNEL_DISPOSITION TELEMETRY 10121 ChannelDisposition chanDisposition = kHttpCanceled; 10122 // HTTP_CHANNEL_DISPOSITION_UPGRADE TELEMETRY 10123 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE upgradeChanDisposition = 10124 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::cancel; 10125 10126 // HTTP 0.9 is more likely to be an error than really 0.9, so count it that 10127 // way 10128 if (mCanceled) { 10129 chanDisposition = kHttpCanceled; 10130 upgradeChanDisposition = 10131 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::cancel; 10132 } else if (!LoadUsedNetwork() || 10133 (mRaceCacheWithNetwork && 10134 mFirstResponseSource == RESPONSE_FROM_CACHE)) { 10135 chanDisposition = kHttpDisk; 10136 upgradeChanDisposition = 10137 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::disk; 10138 } else if (NS_SUCCEEDED(aStatus) && mResponseHead && 10139 mResponseHead->Version() != HttpVersion::v0_9) { 10140 chanDisposition = kHttpNetOK; 10141 upgradeChanDisposition = 10142 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netOk; 10143 } else if (!mTransferSize) { 10144 chanDisposition = kHttpNetEarlyFail; 10145 upgradeChanDisposition = 10146 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netEarlyFail; 10147 } else { 10148 chanDisposition = kHttpNetLateFail; 10149 upgradeChanDisposition = 10150 Telemetry::LABELS_HTTP_CHANNEL_DISPOSITION_UPGRADE::netLateFail; 10151 } 10152 // Browser upgrading only happens on HTTPS pages for mixed passive content 10153 // when upgrading is enabled. 10154 nsCString upgradeKey; 10155 nsLiteralCString upgradeChanDispositionLabel = 10156 HttpChanDispositionToTelemetryLabel(upgradeChanDisposition); 10157 if (IsHTTPS()) { 10158 // Browser upgrading is disabled and the content is already HTTPS 10159 upgradeKey = "disabledNoReason"_ns; 10160 // Checks "security.mixed_content.upgrade_display_content" is true 10161 if (StaticPrefs::security_mixed_content_upgrade_display_content()) { 10162 if (mLoadInfo->GetBrowserUpgradeInsecureRequests()) { 10163 // HTTP content the browser has upgraded to HTTPS 10164 mozilla::glean::networking::http_channel_disposition_enabled_upgrade 10165 .Get(upgradeChanDispositionLabel) 10166 .Add(1); 10167 upgradeKey = "enabledUpgrade"_ns; 10168 } else { 10169 // Content wasn't upgraded but is already HTTPS 10170 mozilla::glean::networking::http_channel_disposition_enabled_no_reason 10171 .Get(upgradeChanDispositionLabel) 10172 .Add(1); 10173 upgradeKey = "enabledNoReason"_ns; 10174 } 10175 } else { 10176 mozilla::glean::networking::http_channel_disposition_disabled_no_reason 10177 .Get(upgradeChanDispositionLabel) 10178 .Add(1); 10179 } 10180 // shift http to https disposition enums 10181 chanDisposition = 10182 static_cast<ChannelDisposition>(chanDisposition + kHttpsCanceled); 10183 } else if (mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) { 10184 // HTTP content the browser would upgrade to HTTPS if upgrading was 10185 // enabled 10186 mozilla::glean::networking::http_channel_disposition_disabled_upgrade 10187 .Get(upgradeChanDispositionLabel) 10188 .Add(1); 10189 upgradeKey = "disabledUpgrade"_ns; 10190 } else if (StaticPrefs::security_mixed_content_upgrade_display_content()) { 10191 // HTTP content that wouldn't upgrade 10192 mozilla::glean::networking::http_channel_disposition_enabled_wont 10193 .Get(upgradeChanDispositionLabel) 10194 .Add(1); 10195 upgradeKey = "enabledWont"_ns; 10196 } else { 10197 mozilla::glean::networking::http_channel_disposition_disabled_wont 10198 .Get(upgradeChanDispositionLabel) 10199 .Add(1); 10200 upgradeKey = "disabledWont"_ns; 10201 } 10202 10203 glean::networking::http_channel_disposition_upgrade 10204 .Get(upgradeKey, upgradeChanDispositionLabel) 10205 .Add(); 10206 10207 LOG((" nsHttpChannel::OnStopRequest ChannelDisposition %d\n", 10208 chanDisposition)); 10209 glean::http::channel_disposition.AccumulateSingleSample(chanDisposition); 10210 RecordHttpChanDispositionGlean(chanDisposition); 10211 10212 // Collect specific telemetry for measuring image, video, audio 10213 // success/failure rates in regular browsing mode and when auto upgrading of 10214 // subresources is enabled. Note that we only evaluate actual image types, not 10215 // favicons. 10216 nsContentPolicyType internalLoadType; 10217 mLoadInfo->GetInternalContentPolicyType(&internalLoadType); 10218 bool statusIsSuccess = NS_SUCCEEDED(aStatus); 10219 if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_IMAGE || 10220 internalLoadType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD) { 10221 if (mLoadInfo->GetBrowserDidUpgradeInsecureRequests()) { 10222 glean::mixed_content::images 10223 .EnumGet(statusIsSuccess 10224 ? glean::mixed_content::ImagesLabel::eImgupsuccess 10225 : glean::mixed_content::ImagesLabel::eImgupfailure) 10226 .Add(); 10227 } else { 10228 glean::mixed_content::images 10229 .EnumGet(statusIsSuccess 10230 ? glean::mixed_content::ImagesLabel::eImgnoupsuccess 10231 : glean::mixed_content::ImagesLabel::eImgnoupfailure) 10232 .Add(); 10233 } 10234 } 10235 if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_VIDEO) { 10236 if (mLoadInfo->GetBrowserDidUpgradeInsecureRequests()) { 10237 glean::mixed_content::video 10238 .EnumGet(statusIsSuccess 10239 ? glean::mixed_content::VideoLabel::eVideoupsuccess 10240 : glean::mixed_content::VideoLabel::eVideoupfailure) 10241 .Add(); 10242 } else { 10243 glean::mixed_content::video 10244 .EnumGet(statusIsSuccess 10245 ? glean::mixed_content::VideoLabel::eVideonoupsuccess 10246 : glean::mixed_content::VideoLabel::eVideonoupfailure) 10247 .Add(); 10248 } 10249 } 10250 if (internalLoadType == nsIContentPolicy::TYPE_INTERNAL_AUDIO) { 10251 if (mLoadInfo->GetBrowserDidUpgradeInsecureRequests()) { 10252 glean::mixed_content::audio 10253 .EnumGet(statusIsSuccess 10254 ? glean::mixed_content::AudioLabel::eAudioupsuccess 10255 : glean::mixed_content::AudioLabel::eAudioupfailure) 10256 .Add(); 10257 } else { 10258 glean::mixed_content::audio 10259 .EnumGet(statusIsSuccess 10260 ? glean::mixed_content::AudioLabel::eAudionoupsuccess 10261 : glean::mixed_content::AudioLabel::eAudionoupfailure) 10262 .Add(); 10263 } 10264 } 10265 10266 // if needed, check cache entry has all data we expect 10267 if (mCacheEntry && mCachePump && LoadConcurrentCacheAccess() && 10268 aContentComplete) { 10269 int64_t size, contentLength; 10270 nsresult rv = CheckPartial(mCacheEntry, &size, &contentLength); 10271 if (NS_SUCCEEDED(rv)) { 10272 if (size == int64_t(-1)) { 10273 // mayhemer TODO - we have to restart read from cache here at the size 10274 // offset 10275 MOZ_ASSERT(false); 10276 LOG( 10277 (" cache entry write is still in progress, but we just " 10278 "finished reading the cache entry")); 10279 } else if (contentLength != int64_t(-1) && contentLength != size) { 10280 LOG((" concurrent cache entry write has been interrupted")); 10281 mCachedResponseHead = std::move(mResponseHead); 10282 // Ignore zero partial length because we also want to resume when 10283 // no data at all has been read from the cache. 10284 rv = MaybeSetupByteRangeRequest(size, contentLength, true); 10285 if (NS_SUCCEEDED(rv) && LoadIsPartialRequest()) { 10286 // Prevent read from cache again 10287 StoreCachedContentIsValid(CachedContentValidity::Invalid); 10288 StoreCachedContentIsPartial(1); 10289 10290 // We are about to perform a different network request. 10291 // We must set mRaceCacheWithNetwork to false because otherwise 10292 // we would ignore the network response thinking we didn't need it. 10293 mRaceCacheWithNetwork = false; 10294 10295 // Perform the range request 10296 rv = ContinueConnect(); 10297 if (NS_SUCCEEDED(rv)) { 10298 LOG((" performing range request")); 10299 mCachePump = nullptr; 10300 return NS_OK; 10301 } 10302 LOG((" but range request perform failed 0x%08" PRIx32, 10303 static_cast<uint32_t>(rv))); 10304 aStatus = NS_ERROR_NET_INTERRUPT; 10305 } else { 10306 LOG((" but range request setup failed rv=0x%08" PRIx32 10307 ", failing load", 10308 static_cast<uint32_t>(rv))); 10309 } 10310 } 10311 } 10312 } 10313 10314 StoreIsPending(false); 10315 mStatus = aStatus; 10316 10317 // perform any final cache operations before we close the cache entry. 10318 if (mCacheEntry && LoadRequestTimeInitialized()) { 10319 // New implementation just returns value of the !LoadCacheEntryIsReadOnly() 10320 // flag passed in. Old implementation checks on nsICache::ACCESS_WRITE 10321 // flag. 10322 10323 // Assume that write access is granted 10324 if (!LoadCacheEntryIsReadOnly()) { 10325 nsresult rv = FinalizeCacheEntry(); 10326 if (NS_FAILED(rv)) { 10327 LOG(("FinalizeCacheEntry failed (%08x)", static_cast<uint32_t>(rv))); 10328 } 10329 } 10330 } 10331 10332 ReportRcwnStats(aIsFromNet); 10333 10334 // Register entry to the PerformanceStorage resource timing 10335 MaybeReportTimingData(); 10336 10337 MaybeFlushConsoleReports(); 10338 10339 if (!mEndMarkerAdded && profiler_thread_is_being_profiled_for_markers()) { 10340 // These do allocations/frees/etc; avoid if not active 10341 mEndMarkerAdded = true; 10342 10343 nsAutoCString requestMethod; 10344 GetRequestMethod(requestMethod); 10345 10346 int32_t priority = PRIORITY_NORMAL; 10347 GetPriority(&priority); 10348 10349 uint64_t size = 0; 10350 GetEncodedBodySize(&size); 10351 10352 nsAutoCString contentType; 10353 mozilla::Maybe<mozilla::net::HttpVersion> httpVersion = Nothing(); 10354 mozilla::Maybe<uint32_t> responseStatus = Nothing(); 10355 if (mResponseHead) { 10356 mResponseHead->ContentType(contentType); 10357 httpVersion = Some(mResponseHead->Version()); 10358 responseStatus = Some(mResponseHead->Status()); 10359 } 10360 10361 profiler_add_network_marker( 10362 mURI, requestMethod, priority, mChannelId, NetworkLoadType::LOAD_STOP, 10363 mLastStatusReported, TimeStamp::Now(), size, mCacheDisposition, 10364 mLoadInfo->GetInnerWindowID(), 10365 mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(), this, mStatus, 10366 &mTransactionTimings, std::move(mSource), httpVersion, responseStatus, 10367 Some(nsDependentCString(contentType.get()))); 10368 } 10369 10370 if (mAuthRetryPending && 10371 StaticPrefs::network_auth_use_redirect_for_retries()) { 10372 nsresult rv = OpenRedirectChannel(aStatus); 10373 LOG(("Opening redirect channel for auth retry %x", 10374 static_cast<uint32_t>(rv))); 10375 if (NS_FAILED(rv)) { 10376 if (mListener) { 10377 MOZ_ASSERT(!LoadOnStartRequestCalled(), 10378 "We should not call OnStartRequest twice."); 10379 if (!LoadOnStartRequestCalled()) { 10380 nsCOMPtr<nsIStreamListener> listener(mListener); 10381 StoreOnStartRequestCalled(true); 10382 listener->OnStartRequest(this); 10383 } 10384 } else { 10385 StoreOnStartRequestCalled(true); 10386 NS_WARNING("OnStartRequest skipped because of null listener"); 10387 } 10388 } 10389 mAuthRetryPending = false; 10390 } 10391 10392 if (LoadUsedNetwork() && !mReportedNEL) { 10393 MaybeGenerateNELReport(); 10394 } 10395 10396 // notify "http-on-before-stop-request" observers 10397 gHttpHandler->OnBeforeStopRequest(this); 10398 10399 if (mListener) { 10400 LOG(("nsHttpChannel %p calling OnStopRequest\n", this)); 10401 MOZ_ASSERT(LoadOnStartRequestCalled(), 10402 "OnStartRequest should be called before OnStopRequest"); 10403 MOZ_ASSERT(!LoadOnStopRequestCalled(), 10404 "We should not call OnStopRequest twice"); 10405 StoreOnStopRequestCalled(true); 10406 mListener->OnStopRequest(this, aStatus); 10407 } 10408 StoreOnStopRequestCalled(true); 10409 10410 // The prefetch needs to be released on the main thread 10411 mDNSPrefetch = nullptr; 10412 10413 mRedirectChannel = nullptr; 10414 10415 // notify "http-on-stop-request" observers 10416 gHttpHandler->OnStopRequest(this); 10417 10418 RemoveAsNonTailRequest(); 10419 10420 if (mChannelBlockedByOpaqueResponse && mCachedOpaqueResponseBlockingPref) { 10421 mResponseHead->ClearHeaders(); 10422 } 10423 // If a preferred alt-data type was set, this signals the consumer is 10424 // interested in reading and/or writing the alt-data representation. 10425 // We need to hold a reference to the cache entry in case the listener calls 10426 // openAlternativeOutputStream() after CloseCacheEntry() clears mCacheEntry. 10427 if (!mPreferredCachedAltDataTypes.IsEmpty()) { 10428 mAltDataCacheEntry = mCacheEntry; 10429 } 10430 10431 CloseCacheEntry(!aContentComplete); 10432 mWritingToCache = false; 10433 10434 if (mLoadGroup) { 10435 mLoadGroup->RemoveRequest(this, nullptr, aStatus); 10436 } 10437 10438 // We don't need this info anymore 10439 CleanRedirectCacheChainIfNecessary(); 10440 10441 ReleaseListeners(); 10442 10443 // Release mUploadStream to free some memory sooner. 10444 // We release this in background thread to avoid blocking I/O operations on 10445 // main thread See Bug 1940224 10446 (void)NS_DispatchBackgroundTask(NS_NewRunnableFunction( 10447 "release HttpBaseChannel::mUploadStream", 10448 [uploadStream = std::move(mUploadStream)]() { (void)uploadStream; })); 10449 10450 return NS_OK; 10451 } 10452 10453 //----------------------------------------------------------------------------- 10454 // nsHttpChannel::nsIStreamListener 10455 //----------------------------------------------------------------------------- 10456 10457 class OnTransportStatusAsyncEvent : public Runnable { 10458 public: 10459 OnTransportStatusAsyncEvent(nsITransportEventSink* aEventSink, 10460 nsresult aTransportStatus, int64_t aProgress, 10461 int64_t aProgressMax) 10462 : Runnable("net::OnTransportStatusAsyncEvent"), 10463 mEventSink(aEventSink), 10464 mTransportStatus(aTransportStatus), 10465 mProgress(aProgress), 10466 mProgressMax(aProgressMax) { 10467 MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be created on main thread"); 10468 } 10469 10470 NS_IMETHOD Run() override { 10471 MOZ_ASSERT(NS_IsMainThread(), "Should run on main thread"); 10472 if (mEventSink) { 10473 mEventSink->OnTransportStatus(nullptr, mTransportStatus, mProgress, 10474 mProgressMax); 10475 } 10476 return NS_OK; 10477 } 10478 10479 private: 10480 nsCOMPtr<nsITransportEventSink> mEventSink; 10481 nsresult mTransportStatus; 10482 int64_t mProgress; 10483 int64_t mProgressMax; 10484 }; 10485 10486 NS_IMETHODIMP 10487 nsHttpChannel::OnDataAvailable(nsIRequest* request, nsIInputStream* input, 10488 uint64_t offset, uint32_t count) { 10489 nsresult rv; 10490 AUTO_PROFILER_LABEL("nsHttpChannel::OnDataAvailable", NETWORK); 10491 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnDataAvailable", NETWORK, 10492 Flow::FromPointer(this)); 10493 10494 LOG(("nsHttpChannel::OnDataAvailable [this=%p request=%p offset=%" PRIu64 10495 " count=%" PRIu32 "]\n", 10496 this, request, offset, count)); 10497 10498 LOG((" requestFromCache: %d mFirstResponseSource: %d\n", 10499 request == mCachePump, static_cast<int32_t>(mFirstResponseSource))); 10500 10501 // don't send out OnDataAvailable notifications if we've been canceled. 10502 if (mCanceled) return mStatus; 10503 10504 if (mAuthRetryPending || WRONG_RACING_RESPONSE_SOURCE(request) || 10505 (request == mTransactionPump && LoadTransactionReplaced())) { 10506 uint32_t n; 10507 return input->ReadSegments(NS_DiscardSegment, nullptr, count, &n); 10508 } 10509 10510 MOZ_ASSERT(mResponseHead, "No response head in ODA!!"); 10511 10512 MOZ_ASSERT(!(LoadCachedContentIsPartial() && (request == mTransactionPump)), 10513 "transaction pump not suspended"); 10514 10515 mIsReadingFromCache = (request == mCachePump); 10516 10517 if (mListener) { 10518 // 10519 // synthesize transport progress event. we do this here since we want 10520 // to delay OnProgress events until we start streaming data. this is 10521 // crucially important since it impacts the lock icon (see bug 240053). 10522 // 10523 nsresult transportStatus; 10524 if (request == mCachePump) { 10525 transportStatus = NS_NET_STATUS_READING; 10526 } else { 10527 transportStatus = NS_NET_STATUS_RECEIVING_FROM; 10528 } 10529 10530 // mResponseHead may reference new or cached headers, but either way it 10531 // holds our best estimate of the total content length. Even in the case 10532 // of a byte range request, the content length stored in the cached 10533 // response headers is what we want to use here. 10534 10535 int64_t progressMax = -1; 10536 rv = GetContentLength(&progressMax); 10537 if (NS_FAILED(rv)) { 10538 NS_WARNING("GetContentLength failed"); 10539 } 10540 int64_t progress = mLogicalOffset + count; 10541 10542 if ((progress > progressMax) && (progressMax != -1)) { 10543 NS_WARNING( 10544 "unexpected progress values - " 10545 "is server exceeding content length?"); 10546 } 10547 10548 // make sure params are in range for js 10549 if (!InScriptableRange(progressMax)) { 10550 progressMax = -1; 10551 } 10552 10553 if (!InScriptableRange(progress)) { 10554 progress = -1; 10555 } 10556 10557 if (NS_IsMainThread()) { 10558 OnTransportStatus(nullptr, transportStatus, progress, progressMax); 10559 } else { 10560 rv = NS_DispatchToMainThread(new OnTransportStatusAsyncEvent( 10561 this, transportStatus, progress, progressMax)); 10562 NS_ENSURE_SUCCESS(rv, rv); 10563 } 10564 10565 // 10566 // we have to manually keep the logical offset of the stream up-to-date. 10567 // we cannot depend solely on the offset provided, since we may have 10568 // already streamed some data from another source (see, for example, 10569 // OnDoneReadingPartialCacheEntry). 10570 // 10571 int64_t offsetBefore = 0; 10572 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(input); 10573 if (seekable && NS_FAILED(seekable->Tell(&offsetBefore))) { 10574 seekable = nullptr; 10575 } 10576 10577 if (nsIOService::UseSocketProcess() && mTransaction) { 10578 mOnDataAvailableStartTime = mTransaction->GetDataAvailableStartTime(); 10579 if (!mOnDataAvailableStartTime.IsNull()) { 10580 PerfStats::RecordMeasurement( 10581 PerfStats::Metric::OnDataAvailableSocketToParent, 10582 TimeStamp::Now() - mOnDataAvailableStartTime); 10583 } 10584 } else { 10585 mOnDataAvailableStartTime = TimeStamp::Now(); 10586 } 10587 nsresult rv = 10588 mListener->OnDataAvailable(this, input, mLogicalOffset, count); 10589 if (NS_SUCCEEDED(rv)) { 10590 // by contract mListener must read all of "count" bytes, but 10591 // nsInputStreamPump is tolerant to seekable streams that violate that 10592 // and it will redeliver incompletely read data. So we need to do 10593 // the same thing when updating the progress counter to stay in sync. 10594 int64_t offsetAfter, delta; 10595 if (seekable && NS_SUCCEEDED(seekable->Tell(&offsetAfter))) { 10596 delta = offsetAfter - offsetBefore; 10597 if (delta != count) { 10598 count = delta; 10599 10600 NS_WARNING("Listener OnDataAvailable contract violation"); 10601 nsCOMPtr<nsIConsoleService> consoleService; 10602 consoleService = mozilla::components::Console::Service(); 10603 nsAutoString message(nsLiteralString( 10604 u"http channel Listener OnDataAvailable contract violation")); 10605 if (consoleService) { 10606 consoleService->LogStringMessage(message.get()); 10607 } 10608 } 10609 } 10610 mLogicalOffset += count; 10611 } 10612 10613 return rv; 10614 } 10615 10616 return NS_ERROR_ABORT; 10617 } 10618 10619 //----------------------------------------------------------------------------- 10620 // nsHttpChannel::nsIThreadRetargetableRequest 10621 //----------------------------------------------------------------------------- 10622 10623 NS_IMETHODIMP 10624 nsHttpChannel::RetargetDeliveryTo(nsISerialEventTarget* aNewTarget) { 10625 MOZ_ASSERT(NS_IsMainThread(), "Should be called on main thread only"); 10626 10627 NS_ENSURE_ARG(aNewTarget); 10628 if (aNewTarget->IsOnCurrentThread()) { 10629 NS_WARNING("Retargeting delivery to same thread"); 10630 return NS_OK; 10631 } 10632 if (!mTransactionPump && !mCachePump) { 10633 LOG(("nsHttpChannel::RetargetDeliveryTo %p %p no pump available\n", this, 10634 aNewTarget)); 10635 return NS_ERROR_NOT_AVAILABLE; 10636 } 10637 10638 nsresult rv = NS_OK; 10639 // If both cache pump and transaction pump exist, we're probably dealing 10640 // with partially cached content. So, we must be able to retarget both. 10641 nsCOMPtr<nsIThreadRetargetableRequest> retargetableCachePump; 10642 nsCOMPtr<nsIThreadRetargetableRequest> retargetableTransactionPump; 10643 if (mCachePump) { 10644 retargetableCachePump = do_QueryObject(mCachePump); 10645 // nsInputStreamPump should implement this interface. 10646 MOZ_ASSERT(retargetableCachePump); 10647 rv = retargetableCachePump->RetargetDeliveryTo(aNewTarget); 10648 } 10649 if (NS_SUCCEEDED(rv) && mTransactionPump) { 10650 retargetableTransactionPump = do_QueryObject(mTransactionPump); 10651 // nsInputStreamPump should implement this interface. 10652 MOZ_ASSERT(retargetableTransactionPump); 10653 rv = retargetableTransactionPump->RetargetDeliveryTo(aNewTarget); 10654 10655 // If retarget fails for transaction pump, we must restore mCachePump. 10656 if (NS_FAILED(rv) && retargetableCachePump) { 10657 nsCOMPtr<nsISerialEventTarget> main = GetMainThreadSerialEventTarget(); 10658 NS_ENSURE_TRUE(main, NS_ERROR_UNEXPECTED); 10659 rv = retargetableCachePump->RetargetDeliveryTo(main); 10660 } 10661 } 10662 return rv; 10663 } 10664 10665 NS_IMETHODIMP 10666 nsHttpChannel::GetDeliveryTarget(nsISerialEventTarget** aEventTarget) { 10667 if (mCachePump) { 10668 return mCachePump->GetDeliveryTarget(aEventTarget); 10669 } 10670 if (mTransactionPump) { 10671 nsCOMPtr<nsIThreadRetargetableRequest> request = 10672 do_QueryInterface(mTransactionPump); 10673 return request->GetDeliveryTarget(aEventTarget); 10674 } 10675 return NS_ERROR_NOT_AVAILABLE; 10676 } 10677 10678 //----------------------------------------------------------------------------- 10679 // nsHttpChannel::nsThreadRetargetableStreamListener 10680 //----------------------------------------------------------------------------- 10681 10682 NS_IMETHODIMP 10683 nsHttpChannel::CheckListenerChain() { 10684 NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!"); 10685 nsresult rv = NS_OK; 10686 nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener = 10687 do_QueryInterface(mListener, &rv); 10688 if (retargetableListener) { 10689 rv = retargetableListener->CheckListenerChain(); 10690 } 10691 return rv; 10692 } 10693 10694 NS_IMETHODIMP 10695 nsHttpChannel::OnDataFinished(nsresult aStatus) { 10696 nsCOMPtr<nsIThreadRetargetableStreamListener> listener = 10697 do_QueryInterface(mListener); 10698 10699 if (listener) { 10700 return listener->OnDataFinished(aStatus); 10701 } 10702 10703 return NS_OK; 10704 } 10705 10706 //----------------------------------------------------------------------------- 10707 // nsHttpChannel::nsITransportEventSink 10708 //----------------------------------------------------------------------------- 10709 10710 NS_IMETHODIMP 10711 nsHttpChannel::OnTransportStatus(nsITransport* trans, nsresult status, 10712 int64_t progress, int64_t progressMax) { 10713 MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread only"); 10714 // cache the progress sink so we don't have to query for it each time. 10715 if (!mProgressSink) GetCallback(mProgressSink); 10716 10717 mLastTransportStatus = status; 10718 if (status == NS_NET_STATUS_CONNECTED_TO || 10719 status == NS_NET_STATUS_WAITING_FOR) { 10720 bool isTrr = false; 10721 bool echConfigUsed = false; 10722 if (mTransaction) { 10723 mTransaction->GetNetworkAddresses(mSelfAddr, mPeerAddr, isTrr, 10724 mEffectiveTRRMode, mTRRSkipReason, 10725 echConfigUsed); 10726 10727 } else { 10728 nsCOMPtr<nsISocketTransport> socketTransport = do_QueryInterface(trans); 10729 if (socketTransport) { 10730 socketTransport->GetPeerAddr(&mPeerAddr); 10731 socketTransport->GetSelfAddr(&mSelfAddr); 10732 socketTransport->ResolvedByTRR(&isTrr); 10733 socketTransport->GetEffectiveTRRMode(&mEffectiveTRRMode); 10734 socketTransport->GetEchConfigUsed(&echConfigUsed); 10735 } 10736 } 10737 10738 StoreResolvedByTRR(isTrr); 10739 StoreEchConfigUsed(echConfigUsed); 10740 } 10741 10742 // block socket status event after Cancel or OnStopRequest has been called. 10743 if (mProgressSink && NS_SUCCEEDED(mStatus) && LoadIsPending()) { 10744 LOG(("sending progress%s notification [this=%p status=%" PRIx32 10745 " progress=%" PRId64 "/%" PRId64 "]\n", 10746 (mLoadFlags & LOAD_BACKGROUND) ? "" : " and status", this, 10747 static_cast<uint32_t>(status), progress, progressMax)); 10748 10749 nsAutoCString host; 10750 mURI->GetHost(host); 10751 if (!(mLoadFlags & LOAD_BACKGROUND)) { 10752 mProgressSink->OnStatus(this, status, NS_ConvertUTF8toUTF16(host).get()); 10753 } else { 10754 nsCOMPtr<nsIParentChannel> parentChannel; 10755 NS_QueryNotificationCallbacks(this, parentChannel); 10756 // If the event sink is |HttpChannelParent|, we have to send status 10757 // events to it even if LOAD_BACKGROUND is set. |HttpChannelParent| 10758 // needs to be aware of whether the status is 10759 // |NS_NET_STATUS_RECEIVING_FROM| or |NS_NET_STATUS_READING|. 10760 // LOAD_BACKGROUND is checked again in |HttpChannelChild|, so the final 10761 // consumer won't get this event. 10762 if (SameCOMIdentity(parentChannel, mProgressSink)) { 10763 mProgressSink->OnStatus(this, status, 10764 NS_ConvertUTF8toUTF16(host).get()); 10765 } 10766 } 10767 10768 if (progress > 0) { 10769 if ((progress > progressMax) && (progressMax != -1)) { 10770 NS_WARNING("unexpected progress values"); 10771 } 10772 10773 // Try to get mProgressSink if it was nulled out during OnStatus. 10774 if (!mProgressSink) { 10775 GetCallback(mProgressSink); 10776 } 10777 if (mProgressSink) { 10778 mProgressSink->OnProgress(this, progress, progressMax); 10779 } 10780 } 10781 } 10782 10783 return NS_OK; 10784 } 10785 10786 //----------------------------------------------------------------------------- 10787 // nsHttpChannel::nsICacheInfoChannel 10788 //----------------------------------------------------------------------------- 10789 10790 NS_IMETHODIMP 10791 nsHttpChannel::IsFromCache(bool* value) { 10792 if (!LoadIsPending()) return NS_ERROR_NOT_AVAILABLE; 10793 10794 if (!mRaceCacheWithNetwork) { 10795 // return false if reading a partial cache entry; the data isn't 10796 // entirely from the cache! 10797 *value = (mCachePump || (mLoadFlags & LOAD_ONLY_IF_MODIFIED)) && 10798 CachedContentIsValid() && !LoadCachedContentIsPartial(); 10799 return NS_OK; 10800 } 10801 10802 // If we are racing network and cache (or skipping the cache) 10803 // we just return the first response source. 10804 *value = mFirstResponseSource == RESPONSE_FROM_CACHE; 10805 10806 return NS_OK; 10807 } 10808 10809 NS_IMETHODIMP 10810 nsHttpChannel::HasCacheEntry(bool* value) { 10811 *value = !!mCacheEntry; 10812 return NS_OK; 10813 } 10814 10815 NS_IMETHODIMP 10816 nsHttpChannel::GetCacheEntryId(uint64_t* aCacheEntryId) { 10817 if (!mCacheEntry || NS_FAILED(mCacheEntry->GetCacheEntryId(aCacheEntryId))) { 10818 return NS_ERROR_NOT_AVAILABLE; 10819 } 10820 10821 return NS_OK; 10822 } 10823 10824 NS_IMETHODIMP 10825 nsHttpChannel::GetCacheTokenFetchCount(uint32_t* _retval) { 10826 NS_ENSURE_ARG_POINTER(_retval); 10827 nsCOMPtr<nsICacheEntry> cacheEntry = 10828 mCacheEntry ? mCacheEntry : mAltDataCacheEntry; 10829 if (!cacheEntry) { 10830 return NS_ERROR_NOT_AVAILABLE; 10831 } 10832 10833 return cacheEntry->GetFetchCount(_retval); 10834 } 10835 10836 NS_IMETHODIMP 10837 nsHttpChannel::GetCacheTokenExpirationTime(uint32_t* _retval) { 10838 NS_ENSURE_ARG_POINTER(_retval); 10839 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 10840 10841 return mCacheEntry->GetExpirationTime(_retval); 10842 } 10843 10844 NS_IMETHODIMP 10845 nsHttpChannel::SetAllowStaleCacheContent(bool aAllowStaleCacheContent) { 10846 LOG(("nsHttpChannel::SetAllowStaleCacheContent [this=%p, allow=%d]", this, 10847 aAllowStaleCacheContent)); 10848 StoreAllowStaleCacheContent(aAllowStaleCacheContent); 10849 return NS_OK; 10850 } 10851 NS_IMETHODIMP 10852 nsHttpChannel::GetAllowStaleCacheContent(bool* aAllowStaleCacheContent) { 10853 NS_ENSURE_ARG(aAllowStaleCacheContent); 10854 *aAllowStaleCacheContent = LoadAllowStaleCacheContent(); 10855 return NS_OK; 10856 } 10857 10858 NS_IMETHODIMP 10859 nsHttpChannel::SetForceValidateCacheContent(bool aForceValidateCacheContent) { 10860 LOG(("nsHttpChannel::SetForceValidateCacheContent [this=%p, allow=%d]", this, 10861 aForceValidateCacheContent)); 10862 StoreForceValidateCacheContent(aForceValidateCacheContent); 10863 return NS_OK; 10864 } 10865 NS_IMETHODIMP 10866 nsHttpChannel::GetForceValidateCacheContent(bool* aForceValidateCacheContent) { 10867 NS_ENSURE_ARG(aForceValidateCacheContent); 10868 *aForceValidateCacheContent = LoadForceValidateCacheContent(); 10869 return NS_OK; 10870 } 10871 10872 NS_IMETHODIMP 10873 nsHttpChannel::SetPreferCacheLoadOverBypass(bool aPreferCacheLoadOverBypass) { 10874 StorePreferCacheLoadOverBypass(aPreferCacheLoadOverBypass); 10875 return NS_OK; 10876 } 10877 NS_IMETHODIMP 10878 nsHttpChannel::GetPreferCacheLoadOverBypass(bool* aPreferCacheLoadOverBypass) { 10879 NS_ENSURE_ARG(aPreferCacheLoadOverBypass); 10880 *aPreferCacheLoadOverBypass = LoadPreferCacheLoadOverBypass(); 10881 return NS_OK; 10882 } 10883 10884 NS_IMETHODIMP 10885 nsHttpChannel::PreferAlternativeDataType( 10886 const nsACString& aType, const nsACString& aContentType, 10887 PreferredAlternativeDataDeliveryType aDeliverAltData) { 10888 ENSURE_CALLED_BEFORE_ASYNC_OPEN(); 10889 mPreferredCachedAltDataTypes.AppendElement(PreferredAlternativeDataTypeParams( 10890 nsCString(aType), nsCString(aContentType), aDeliverAltData)); 10891 return NS_OK; 10892 } 10893 10894 const nsTArray<PreferredAlternativeDataTypeParams>& 10895 nsHttpChannel::PreferredAlternativeDataTypes() { 10896 return mPreferredCachedAltDataTypes; 10897 } 10898 10899 NS_IMETHODIMP 10900 nsHttpChannel::GetAlternativeDataType(nsACString& aType) { 10901 // must be called during or after OnStartRequest 10902 if (!LoadAfterOnStartRequestBegun()) { 10903 return NS_ERROR_NOT_AVAILABLE; 10904 } 10905 aType = mAvailableCachedAltDataType; 10906 return NS_OK; 10907 } 10908 10909 class CacheEntryWriteHandle : public nsICacheEntryWriteHandle { 10910 virtual ~CacheEntryWriteHandle() = default; 10911 10912 public: 10913 NS_DECL_ISUPPORTS 10914 NS_DECL_NSICACHEENTRYWRITEHANDLE 10915 10916 explicit CacheEntryWriteHandle(nsICacheEntry* aCacheEntry) 10917 : mCacheEntry(aCacheEntry) { 10918 MOZ_ASSERT(mCacheEntry); 10919 } 10920 10921 private: 10922 nsCOMPtr<nsICacheEntry> mCacheEntry; 10923 }; 10924 10925 NS_IMPL_ADDREF(CacheEntryWriteHandle) 10926 NS_IMPL_RELEASE(CacheEntryWriteHandle) 10927 NS_INTERFACE_MAP_BEGIN(CacheEntryWriteHandle) 10928 NS_INTERFACE_MAP_ENTRY(nsISupports) 10929 NS_INTERFACE_MAP_ENTRY(nsICacheEntryWriteHandle) 10930 NS_INTERFACE_MAP_END 10931 10932 NS_IMETHODIMP 10933 CacheEntryWriteHandle::OpenAlternativeOutputStream( 10934 const nsACString& type, int64_t predictedSize, 10935 nsIAsyncOutputStream** _retval) { 10936 nsresult rv = 10937 mCacheEntry->OpenAlternativeOutputStream(type, predictedSize, _retval); 10938 if (NS_SUCCEEDED(rv)) { 10939 mCacheEntry->SetMetaDataElement("alt-data-from-child", nullptr); 10940 } 10941 return rv; 10942 } 10943 10944 NS_IMETHODIMP 10945 nsHttpChannel::GetCacheEntryWriteHandle(nsICacheEntryWriteHandle** _retval) { 10946 nsCOMPtr<nsICacheEntry> cacheEntry = 10947 mCacheEntry ? mCacheEntry : mAltDataCacheEntry; 10948 if (!cacheEntry) { 10949 return NS_ERROR_NOT_AVAILABLE; 10950 } 10951 10952 nsCOMPtr<nsICacheEntryWriteHandle> handle = 10953 new CacheEntryWriteHandle(cacheEntry); 10954 handle.forget(_retval); 10955 return NS_OK; 10956 } 10957 10958 NS_IMETHODIMP 10959 nsHttpChannel::OpenAlternativeOutputStream(const nsACString& type, 10960 int64_t predictedSize, 10961 nsIAsyncOutputStream** _retval) { 10962 // OnStopRequest will clear mCacheEntry, but we may use mAltDataCacheEntry 10963 // if the consumer called PreferAlternativeDataType() 10964 nsCOMPtr<nsICacheEntry> cacheEntry = 10965 mCacheEntry ? mCacheEntry : mAltDataCacheEntry; 10966 if (!cacheEntry) { 10967 return NS_ERROR_NOT_AVAILABLE; 10968 } 10969 nsresult rv = 10970 cacheEntry->OpenAlternativeOutputStream(type, predictedSize, _retval); 10971 if (NS_SUCCEEDED(rv)) { 10972 // Clear this metadata flag in case it exists. 10973 // The caller of this method may set it again. 10974 cacheEntry->SetMetaDataElement("alt-data-from-child", nullptr); 10975 } 10976 return rv; 10977 } 10978 10979 NS_IMETHODIMP 10980 nsHttpChannel::GetOriginalInputStream(nsIInputStreamReceiver* aReceiver) { 10981 if (aReceiver == nullptr) { 10982 return NS_ERROR_INVALID_ARG; 10983 } 10984 nsCOMPtr<nsIInputStream> inputStream; 10985 10986 nsCOMPtr<nsICacheEntry> cacheEntry = 10987 mCacheEntry ? mCacheEntry : mAltDataCacheEntry; 10988 if (cacheEntry) { 10989 cacheEntry->OpenInputStream(0, getter_AddRefs(inputStream)); 10990 } 10991 aReceiver->OnInputStreamReady(inputStream); 10992 return NS_OK; 10993 } 10994 10995 NS_IMETHODIMP 10996 nsHttpChannel::GetAlternativeDataInputStream(nsIInputStream** aInputStream) { 10997 NS_ENSURE_ARG_POINTER(aInputStream); 10998 10999 *aInputStream = nullptr; 11000 11001 nsCOMPtr<nsICacheEntry> cacheEntry = 11002 mCacheEntry ? mCacheEntry : mAltDataCacheEntry; 11003 if (!mAvailableCachedAltDataType.IsEmpty() && cacheEntry) { 11004 nsresult rv = cacheEntry->OpenAlternativeInputStream( 11005 mAvailableCachedAltDataType, aInputStream); 11006 NS_ENSURE_SUCCESS(rv, rv); 11007 } 11008 return NS_OK; 11009 } 11010 11011 //----------------------------------------------------------------------------- 11012 // nsHttpChannel::nsICachingChannel 11013 //----------------------------------------------------------------------------- 11014 11015 NS_IMETHODIMP 11016 nsHttpChannel::IsRacing(bool* aIsRacing) { 11017 if (!LoadAfterOnStartRequestBegun()) { 11018 return NS_ERROR_NOT_AVAILABLE; 11019 } 11020 *aIsRacing = mRaceCacheWithNetwork; 11021 return NS_OK; 11022 } 11023 11024 NS_IMETHODIMP 11025 nsHttpChannel::GetCacheToken(nsISupports** token) { 11026 NS_ENSURE_ARG_POINTER(token); 11027 if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; 11028 return CallQueryInterface(mCacheEntry, token); 11029 } 11030 11031 NS_IMETHODIMP 11032 nsHttpChannel::SetCacheToken(nsISupports* token) { 11033 return NS_ERROR_NOT_IMPLEMENTED; 11034 } 11035 11036 NS_IMETHODIMP 11037 nsHttpChannel::GetCacheKey(uint32_t* key) { 11038 NS_ENSURE_ARG_POINTER(key); 11039 11040 LOG(("nsHttpChannel::GetCacheKey [this=%p]\n", this)); 11041 11042 *key = mPostID; 11043 return NS_OK; 11044 } 11045 11046 NS_IMETHODIMP 11047 nsHttpChannel::SetCacheKey(uint32_t key) { 11048 LOG(("nsHttpChannel::SetCacheKey [this=%p key=%u]\n", this, key)); 11049 11050 ENSURE_CALLED_BEFORE_CONNECT(); 11051 11052 mPostID = key; 11053 return NS_OK; 11054 } 11055 11056 NS_IMETHODIMP 11057 nsHttpChannel::GetCacheOnlyMetadata(bool* aOnlyMetadata) { 11058 NS_ENSURE_ARG(aOnlyMetadata); 11059 *aOnlyMetadata = LoadCacheOnlyMetadata(); 11060 return NS_OK; 11061 } 11062 11063 NS_IMETHODIMP 11064 nsHttpChannel::SetCacheOnlyMetadata(bool aOnlyMetadata) { 11065 LOG(("nsHttpChannel::SetCacheOnlyMetadata [this=%p only-metadata=%d]\n", this, 11066 aOnlyMetadata)); 11067 11068 ENSURE_CALLED_BEFORE_ASYNC_OPEN(); 11069 11070 StoreCacheOnlyMetadata(aOnlyMetadata); 11071 if (aOnlyMetadata) { 11072 mLoadFlags |= LOAD_ONLY_IF_MODIFIED; 11073 } 11074 11075 return NS_OK; 11076 } 11077 11078 NS_IMETHODIMP 11079 nsHttpChannel::GetPin(bool* aPin) { 11080 NS_ENSURE_ARG(aPin); 11081 *aPin = LoadPinCacheContent(); 11082 return NS_OK; 11083 } 11084 11085 NS_IMETHODIMP 11086 nsHttpChannel::SetPin(bool aPin) { 11087 LOG(("nsHttpChannel::SetPin [this=%p pin=%d]\n", this, aPin)); 11088 11089 ENSURE_CALLED_BEFORE_CONNECT(); 11090 11091 StorePinCacheContent(aPin); 11092 return NS_OK; 11093 } 11094 11095 NS_IMETHODIMP 11096 nsHttpChannel::ForceCacheEntryValidFor(uint32_t aSecondsToTheFuture) { 11097 if (!mCacheEntry) { 11098 LOG( 11099 ("nsHttpChannel::ForceCacheEntryValidFor found no cache entry " 11100 "for this channel [this=%p].", 11101 this)); 11102 } else { 11103 mCacheEntry->ForceValidFor(aSecondsToTheFuture); 11104 11105 nsAutoCString key; 11106 mCacheEntry->GetKey(key); 11107 11108 LOG( 11109 ("nsHttpChannel::ForceCacheEntryValidFor successfully forced valid " 11110 "entry with key %s for %d seconds. [this=%p]", 11111 key.get(), aSecondsToTheFuture, this)); 11112 } 11113 11114 return NS_OK; 11115 } 11116 11117 //----------------------------------------------------------------------------- 11118 // nsHttpChannel::nsIResumableChannel 11119 //----------------------------------------------------------------------------- 11120 11121 NS_IMETHODIMP 11122 nsHttpChannel::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID) { 11123 LOG(("nsHttpChannel::ResumeAt [this=%p startPos=%" PRIu64 " id='%s']\n", this, 11124 aStartPos, PromiseFlatCString(aEntityID).get())); 11125 mEntityID = aEntityID; 11126 mStartPos = aStartPos; 11127 StoreResuming(true); 11128 return NS_OK; 11129 } 11130 11131 nsresult nsHttpChannel::DoAuthRetry( 11132 HttpTransactionShell* aTransWithStickyConn, 11133 const std::function<nsresult(nsHttpChannel*, nsresult)>& 11134 aContinueOnStopRequestFunc) { 11135 LOG(("nsHttpChannel::DoAuthRetry [this=%p, aTransWithStickyConn=%p]\n", this, 11136 aTransWithStickyConn)); 11137 11138 MOZ_ASSERT(!mTransaction, "should not have a transaction"); 11139 11140 // Note that we don't have to toggle |IsPending| anymore. See the reasons 11141 // below. 11142 // 1. We can't suspend the channel during "http-on-modify-request" 11143 // when |IsPending| is false. 11144 // 2. We don't check |IsPending| in SetRequestHeader now. 11145 11146 // Reset RequestObserversCalled because we've probably called the request 11147 // observers once already. 11148 StoreRequestObserversCalled(false); 11149 11150 // fetch cookies, and add them to the request header. 11151 // the server response could have included cookies that must be sent with 11152 // this authentication attempt (bug 84794). 11153 // TODO: save cookies from auth response and send them here (bug 572151). 11154 AddCookiesToRequest(); 11155 11156 // notify "http-on-modify-request" observers 11157 CallOnModifyRequestObservers(); 11158 11159 RefPtr<HttpTransactionShell> trans(aTransWithStickyConn); 11160 return CallOrWaitForResume( 11161 [trans{std::move(trans)}, aContinueOnStopRequestFunc](auto* self) { 11162 return self->ContinueDoAuthRetry(trans, aContinueOnStopRequestFunc); 11163 }); 11164 } 11165 11166 nsresult nsHttpChannel::ContinueDoAuthRetry( 11167 HttpTransactionShell* aTransWithStickyConn, 11168 const std::function<nsresult(nsHttpChannel*, nsresult)>& 11169 aContinueOnStopRequestFunc) { 11170 LOG(("nsHttpChannel::ContinueDoAuthRetry [this=%p]\n", this)); 11171 StoreIsPending(true); 11172 11173 // get rid of the old response headers 11174 mResponseHead = nullptr; 11175 11176 // rewind the upload stream 11177 if (mUploadStream) { 11178 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream); 11179 nsresult rv = NS_ERROR_NO_INTERFACE; 11180 if (seekable) { 11181 rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); 11182 } 11183 11184 // This should not normally happen, but it's possible that big memory 11185 // blobs originating in the other process can't be rewinded. 11186 // In that case we just fail the request, otherwise the content length 11187 // will not match and this load will never complete. 11188 NS_ENSURE_SUCCESS(rv, rv); 11189 } 11190 11191 // always set sticky connection flag 11192 mCaps |= NS_HTTP_STICKY_CONNECTION; 11193 // and when needed, allow restart regardless the sticky flag 11194 if (LoadAuthConnectionRestartable()) { 11195 LOG((" connection made restartable")); 11196 mCaps |= NS_HTTP_CONNECTION_RESTARTABLE; 11197 StoreAuthConnectionRestartable(false); 11198 } else { 11199 LOG((" connection made non-restartable")); 11200 mCaps &= ~NS_HTTP_CONNECTION_RESTARTABLE; 11201 } 11202 11203 // notify "http-on-before-connect" observers 11204 gHttpHandler->OnBeforeConnect(this); 11205 11206 RefPtr<HttpTransactionShell> trans(aTransWithStickyConn); 11207 return CallOrWaitForResume( 11208 [trans{std::move(trans)}, aContinueOnStopRequestFunc](auto* self) { 11209 nsresult rv = self->DoConnect(trans); 11210 return aContinueOnStopRequestFunc(self, rv); 11211 }); 11212 } 11213 11214 //----------------------------------------------------------------------------- 11215 // nsHttpChannel::nsIAsyncVerifyRedirectCallback 11216 //----------------------------------------------------------------------------- 11217 11218 nsresult nsHttpChannel::WaitForRedirectCallback() { 11219 nsresult rv; 11220 LOG(("nsHttpChannel::WaitForRedirectCallback [this=%p]\n", this)); 11221 11222 if (mTransactionPump) { 11223 rv = mTransactionPump->Suspend(); 11224 NS_ENSURE_SUCCESS(rv, rv); 11225 } 11226 if (mCachePump) { 11227 rv = mCachePump->Suspend(); 11228 if (NS_FAILED(rv) && mTransactionPump) { 11229 #ifdef DEBUG 11230 nsresult resume = 11231 #endif 11232 mTransactionPump->Resume(); 11233 MOZ_ASSERT(NS_SUCCEEDED(resume), "Failed to resume transaction pump"); 11234 } 11235 NS_ENSURE_SUCCESS(rv, rv); 11236 } 11237 11238 StoreWaitingForRedirectCallback(true); 11239 return NS_OK; 11240 } 11241 11242 NS_IMETHODIMP 11243 nsHttpChannel::OnRedirectVerifyCallback(nsresult result) { 11244 LOG( 11245 ("nsHttpChannel::OnRedirectVerifyCallback [this=%p] " 11246 "result=%" PRIx32 " stack=%zu WaitingForRedirectCallback=%u\n", 11247 this, static_cast<uint32_t>(result), mRedirectFuncStack.Length(), 11248 LoadWaitingForRedirectCallback())); 11249 MOZ_ASSERT(LoadWaitingForRedirectCallback(), 11250 "Someone forgot to call WaitForRedirectCallback() ?!"); 11251 StoreWaitingForRedirectCallback(false); 11252 11253 if (mCanceled && NS_SUCCEEDED(result)) result = NS_BINDING_ABORTED; 11254 11255 for (uint32_t i = mRedirectFuncStack.Length(); i > 0;) { 11256 --i; 11257 // Pop the last function pushed to the stack 11258 nsContinueRedirectionFunc func = mRedirectFuncStack.PopLastElement(); 11259 11260 // Call it with the result we got from the callback or the deeper 11261 // function call. 11262 result = (this->*func)(result); 11263 11264 // If a new function has been pushed to the stack and placed us in the 11265 // waiting state, we need to break the chain and wait for the callback 11266 // again. 11267 if (LoadWaitingForRedirectCallback()) break; 11268 } 11269 11270 if (NS_FAILED(result) && !mCanceled) { 11271 // First, cancel this channel if we are in failure state to set mStatus 11272 // and let it be propagated to pumps. 11273 Cancel(result); 11274 } 11275 11276 if (!LoadWaitingForRedirectCallback()) { 11277 // We are not waiting for the callback. At this moment we must release 11278 // reference to the redirect target channel, otherwise we may leak. 11279 // However, if we are redirecting due to auth retries, we open the 11280 // redirected channel after OnStopRequest. In that case we should not 11281 // release the reference 11282 if (!StaticPrefs::network_auth_use_redirect_for_retries() || 11283 !mAuthRetryPending) { 11284 mRedirectChannel = nullptr; 11285 } 11286 } 11287 11288 // We always resume the pumps here. If all functions on stack have been 11289 // called we need OnStopRequest to be triggered, and if we broke out of the 11290 // loop above (and are thus waiting for a new callback) the suspension 11291 // count must be balanced in the pumps. 11292 if (mTransactionPump) mTransactionPump->Resume(); 11293 if (mCachePump) mCachePump->Resume(); 11294 11295 return result; 11296 } 11297 11298 void nsHttpChannel::PushRedirectAsyncFunc(nsContinueRedirectionFunc func) { 11299 mRedirectFuncStack.AppendElement(func); 11300 } 11301 11302 void nsHttpChannel::PopRedirectAsyncFunc(nsContinueRedirectionFunc func) { 11303 MOZ_ASSERT(func == mRedirectFuncStack.LastElement(), 11304 "Trying to pop wrong method from redirect async stack!"); 11305 11306 mRedirectFuncStack.RemoveLastElement(); 11307 } 11308 11309 //----------------------------------------------------------------------------- 11310 // nsIDNSListener functions 11311 //----------------------------------------------------------------------------- 11312 11313 NS_IMETHODIMP 11314 nsHttpChannel::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec, 11315 nsresult status) { 11316 MOZ_ASSERT(NS_IsMainThread(), "Expecting DNS callback on main thread."); 11317 11318 LOG( 11319 ("nsHttpChannel::OnLookupComplete [this=%p] prefetch complete%s: " 11320 "%s status[0x%" PRIx32 "]\n", 11321 this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "", 11322 NS_SUCCEEDED(status) ? "success" : "failure", 11323 static_cast<uint32_t>(status))); 11324 11325 // Unset DNS cache refresh if it was requested, 11326 if (mCaps & NS_HTTP_REFRESH_DNS) { 11327 mCaps &= ~NS_HTTP_REFRESH_DNS; 11328 if (mTransaction) { 11329 mTransaction->SetDNSWasRefreshed(); 11330 } 11331 } 11332 11333 if (!mDNSBlockingPromise.IsEmpty()) { 11334 if (NS_SUCCEEDED(status)) { 11335 nsCOMPtr<nsIDNSRecord> record(rec); 11336 mDNSBlockingPromise.Resolve(record, __func__); 11337 } else { 11338 mDNSBlockingPromise.Reject(status, __func__); 11339 } 11340 } 11341 11342 return NS_OK; 11343 } 11344 11345 void nsHttpChannel::OnHTTPSRRAvailable(nsIDNSHTTPSSVCRecord* aRecord) { 11346 MOZ_ASSERT(NS_IsMainThread(), "Expecting DNS callback on main thread."); 11347 11348 LOG(("nsHttpChannel::OnHTTPSRRAvailable [this=%p, aRecord=%p]\n", this, 11349 aRecord)); 11350 11351 if (mHTTPSSVCRecord) { 11352 MOZ_ASSERT(false, "OnHTTPSRRAvailable called twice!"); 11353 return; 11354 } 11355 11356 nsCOMPtr<nsIDNSHTTPSSVCRecord> record = aRecord; 11357 mHTTPSSVCRecord.emplace(std::move(record)); 11358 const nsCOMPtr<nsIDNSHTTPSSVCRecord>& httprr = mHTTPSSVCRecord.ref(); 11359 11360 if (LoadWaitHTTPSSVCRecord()) { 11361 MOZ_ASSERT(mURI->SchemeIs("http")); 11362 11363 StoreWaitHTTPSSVCRecord(false); 11364 nsresult rv = ContinueOnBeforeConnect(!!httprr, mStatus, !!httprr); 11365 if (NS_FAILED(rv)) { 11366 CloseCacheEntry(false); 11367 (void)AsyncAbort(rv); 11368 } 11369 } else { 11370 // This channel is not canceled and the transaction is not created. 11371 if (httprr && NS_SUCCEEDED(mStatus) && !mTransaction && 11372 (mFirstResponseSource != RESPONSE_FROM_CACHE)) { 11373 bool hasIPAddress = false; 11374 (void)httprr->GetHasIPAddresses(&hasIPAddress); 11375 glean::http::dns_httpssvc_record_receiving_stage.AccumulateSingleSample( 11376 hasIPAddress ? HTTPSSVC_WITH_IPHINT_RECEIVED_STAGE_0 11377 : HTTPSSVC_WITHOUT_IPHINT_RECEIVED_STAGE_0); 11378 StoreHTTPSSVCTelemetryReported(true); 11379 } 11380 } 11381 } 11382 11383 //----------------------------------------------------------------------------- 11384 // nsHttpChannel internal functions 11385 //----------------------------------------------------------------------------- 11386 11387 // Creates an URI to the given location using current URI for base and charset 11388 nsresult nsHttpChannel::CreateNewURI(const char* loc, nsIURI** newURI) { 11389 nsCOMPtr<nsIIOService> ioService; 11390 nsresult rv = gHttpHandler->GetIOService(getter_AddRefs(ioService)); 11391 if (NS_FAILED(rv)) return rv; 11392 11393 return ioService->NewURI(nsDependentCString(loc), nullptr, mURI, newURI); 11394 } 11395 11396 void nsHttpChannel::MaybeInvalidateCacheEntryForSubsequentGet() { 11397 // See RFC 2616 section 5.1.1. These are considered valid 11398 // methods which DO NOT invalidate cache-entries for the 11399 // referred resource. POST, PUT and DELETE as well as any 11400 // other method not listed here will potentially invalidate 11401 // any cached copy of the resource 11402 if (mRequestHead.IsGet() || mRequestHead.IsOptions() || 11403 mRequestHead.IsHead() || mRequestHead.IsTrace() || 11404 mRequestHead.IsConnect()) { 11405 return; 11406 } 11407 11408 // Invalidate the request-uri. 11409 if (LOG_ENABLED()) { 11410 nsAutoCString key; 11411 mURI->GetAsciiSpec(key); 11412 LOG(("MaybeInvalidateCacheEntryForSubsequentGet [this=%p uri=%s]\n", this, 11413 key.get())); 11414 } 11415 11416 DoInvalidateCacheEntry(mURI); 11417 11418 // Invalidate Location-header if set 11419 nsAutoCString location; 11420 (void)mResponseHead->GetHeader(nsHttp::Location, location); 11421 if (!location.IsEmpty()) { 11422 LOG((" Location-header=%s\n", location.get())); 11423 InvalidateCacheEntryForLocation(location.get()); 11424 } 11425 11426 // Invalidate Content-Location-header if set 11427 (void)mResponseHead->GetHeader(nsHttp::Content_Location, location); 11428 if (!location.IsEmpty()) { 11429 LOG((" Content-Location-header=%s\n", location.get())); 11430 InvalidateCacheEntryForLocation(location.get()); 11431 } 11432 } 11433 11434 void nsHttpChannel::InvalidateCacheEntryForLocation(const char* location) { 11435 nsAutoCString tmpCacheKey, tmpSpec; 11436 nsCOMPtr<nsIURI> resultingURI; 11437 nsresult rv = CreateNewURI(location, getter_AddRefs(resultingURI)); 11438 if (NS_SUCCEEDED(rv) && HostPartIsTheSame(resultingURI)) { 11439 DoInvalidateCacheEntry(resultingURI); 11440 } else { 11441 LOG((" hosts not matching\n")); 11442 } 11443 } 11444 11445 void nsHttpChannel::DoInvalidateCacheEntry(nsIURI* aURI) { 11446 // NOTE: 11447 // Following comments 24,32 and 33 in bug #327765, we only care about 11448 // the cache in the protocol-handler. 11449 // The logic below deviates from the original logic in OpenCacheEntry on 11450 // one point by using only READ_ONLY access-policy. I think this is safe. 11451 11452 nsresult rv; 11453 11454 nsAutoCString key; 11455 if (LOG_ENABLED()) { 11456 aURI->GetAsciiSpec(key); 11457 } 11458 11459 LOG(("DoInvalidateCacheEntry [channel=%p key=%s]", this, key.get())); 11460 11461 nsCOMPtr<nsICacheStorageService> cacheStorageService( 11462 components::CacheStorage::Service()); 11463 rv = cacheStorageService ? NS_OK : NS_ERROR_FAILURE; 11464 11465 nsCOMPtr<nsICacheStorage> cacheStorage; 11466 if (NS_SUCCEEDED(rv)) { 11467 RefPtr<LoadContextInfo> info = GetLoadContextInfo(this); 11468 rv = cacheStorageService->DiskCacheStorage(info, 11469 getter_AddRefs(cacheStorage)); 11470 } 11471 11472 if (NS_SUCCEEDED(rv)) { 11473 rv = cacheStorage->AsyncDoomURI(aURI, ""_ns, nullptr); 11474 } 11475 11476 LOG(("DoInvalidateCacheEntry [channel=%p key=%s rv=%d]", this, key.get(), 11477 int(rv))); 11478 } 11479 11480 void nsHttpChannel::AsyncOnExamineCachedResponse() { 11481 gHttpHandler->OnExamineCachedResponse(this); 11482 } 11483 11484 void nsHttpChannel::UpdateAggregateCallbacks() { 11485 if (!mTransaction) { 11486 return; 11487 } 11488 nsCOMPtr<nsIInterfaceRequestor> callbacks; 11489 NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, 11490 GetCurrentSerialEventTarget(), 11491 getter_AddRefs(callbacks)); 11492 mTransaction->SetSecurityCallbacks(callbacks); 11493 } 11494 11495 NS_IMETHODIMP 11496 nsHttpChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) { 11497 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread."); 11498 11499 nsresult rv = HttpBaseChannel::SetLoadGroup(aLoadGroup); 11500 if (NS_SUCCEEDED(rv)) { 11501 UpdateAggregateCallbacks(); 11502 } 11503 return rv; 11504 } 11505 11506 NS_IMETHODIMP 11507 nsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) { 11508 MOZ_ASSERT(NS_IsMainThread(), "Wrong thread."); 11509 11510 nsresult rv = HttpBaseChannel::SetNotificationCallbacks(aCallbacks); 11511 if (NS_SUCCEEDED(rv)) { 11512 UpdateAggregateCallbacks(); 11513 } 11514 return rv; 11515 } 11516 11517 bool nsHttpChannel::AwaitingCacheCallbacks() { 11518 return LoadWaitForCacheEntry() != 0; 11519 } 11520 11521 // static 11522 bool nsHttpChannel::IsRedirectStatus(uint32_t status) { 11523 // 305 disabled as a security measure (see bug 187996). 11524 return status == 301 || status == 302 || status == 303 || status == 307 || 11525 status == 308; 11526 } 11527 11528 void nsHttpChannel::SetCouldBeSynthesized() { 11529 MOZ_ASSERT(!BypassServiceWorker()); 11530 StoreResponseCouldBeSynthesized(true); 11531 } 11532 11533 NS_IMETHODIMP 11534 nsHttpChannel::OnPreflightSucceeded() { 11535 MOZ_ASSERT(LoadRequireCORSPreflight(), "Why did a preflight happen?"); 11536 StoreIsCorsPreflightDone(1); 11537 mPreflightChannel = nullptr; 11538 11539 return ContinueConnect(); 11540 } 11541 11542 NS_IMETHODIMP 11543 nsHttpChannel::OnPreflightFailed(nsresult aError) { 11544 MOZ_ASSERT(LoadRequireCORSPreflight(), "Why did a preflight happen?"); 11545 StoreIsCorsPreflightDone(1); 11546 mPreflightChannel = nullptr; 11547 11548 CloseCacheEntry(false); 11549 (void)AsyncAbort(aError); 11550 return NS_OK; 11551 } 11552 11553 nsresult nsHttpChannel::CallOrWaitForResume( 11554 const std::function<nsresult(nsHttpChannel*)>& aFunc) { 11555 if (mCanceled) { 11556 MOZ_ASSERT(NS_FAILED(mStatus)); 11557 return mStatus; 11558 } 11559 11560 if (mSuspendCount) { 11561 LOG(("Waiting until resume [this=%p]\n", this)); 11562 MOZ_ASSERT(!mCallOnResume); 11563 mCallOnResume = aFunc; 11564 return NS_OK; 11565 } 11566 11567 return aFunc(this); 11568 } 11569 11570 // This is loosely based on: 11571 // https://fetch.spec.whatwg.org/#serializing-a-request-origin 11572 static bool HasNullRequestOrigin(nsHttpChannel* aChannel, nsIURI* aURI, 11573 bool isAddonRequest) { 11574 // Step 1. If request has a redirect-tainted origin, then return "null". 11575 if (aChannel->HasRedirectTaintedOrigin()) { 11576 if (StaticPrefs::network_http_origin_redirectTainted()) { 11577 return true; 11578 } 11579 } 11580 11581 // Non-standard: Only allow HTTP and HTTPS origins. 11582 if (!ReferrerInfo::IsReferrerSchemeAllowed(aURI)) { 11583 // And moz-extension: for add-on initiated requests. 11584 if (!aURI->SchemeIs("moz-extension") || !isAddonRequest) { 11585 return true; 11586 } 11587 } 11588 11589 // Non-standard: Hide onion URLs. 11590 if (StaticPrefs::network_http_referer_hideOnionSource()) { 11591 nsAutoCString host; 11592 if (NS_SUCCEEDED(aURI->GetAsciiHost(host)) && 11593 StringEndsWith(host, ".onion"_ns)) { 11594 return ReferrerInfo::IsCrossOriginRequest(aChannel); 11595 } 11596 } 11597 11598 // Step 2. Return request’s origin, serialized. 11599 return false; 11600 } 11601 11602 // Step 8.12. of HTTP-network-or-cache fetch 11603 // 11604 // https://fetch.spec.whatwg.org/#append-a-request-origin-header 11605 void nsHttpChannel::SetOriginHeader() { 11606 auto* triggeringPrincipal = 11607 BasePrincipal::Cast(mLoadInfo->TriggeringPrincipal()); 11608 11609 if (triggeringPrincipal->IsSystemPrincipal()) { 11610 // We can't infer an Origin header from the system principal, 11611 // this means system requests use whatever Origin header was specified. 11612 return; 11613 } 11614 bool isAddonRequest = triggeringPrincipal->AddonPolicy() || 11615 triggeringPrincipal->ContentScriptAddonPolicy(); 11616 11617 // Non-standard: Handle already existing Origin header. 11618 nsAutoCString existingHeader; 11619 (void)mRequestHead.GetHeader(nsHttp::Origin, existingHeader); 11620 if (!existingHeader.IsEmpty()) { 11621 LOG(("nsHttpChannel::SetOriginHeader Origin header already present")); 11622 auto const shouldNullifyOriginHeader = 11623 [&existingHeader, isAddonRequest](nsHttpChannel* aChannel) { 11624 nsCOMPtr<nsIURI> uri; 11625 nsresult rv = NS_NewURI(getter_AddRefs(uri), existingHeader); 11626 if (NS_FAILED(rv)) { 11627 return false; 11628 } 11629 11630 if (HasNullRequestOrigin(aChannel, uri, isAddonRequest)) { 11631 return true; 11632 } 11633 11634 nsCOMPtr<nsILoadInfo> info = aChannel->LoadInfo(); 11635 if (info->GetTainting() == mozilla::LoadTainting::CORS) { 11636 return false; 11637 } 11638 11639 return ReferrerInfo::ShouldSetNullOriginHeader(aChannel, uri); 11640 }; 11641 11642 if (!existingHeader.EqualsLiteral("null") && 11643 shouldNullifyOriginHeader(this)) { 11644 LOG(("nsHttpChannel::SetOriginHeader null Origin by Referrer-Policy")); 11645 MOZ_ALWAYS_SUCCEEDS( 11646 mRequestHead.SetHeader(nsHttp::Origin, "null"_ns, false /* merge */)); 11647 } 11648 return; 11649 } 11650 11651 if (StaticPrefs::network_http_sendOriginHeader() == 0) { 11652 // Custom user setting: 0 means never send Origin header. 11653 return; 11654 } 11655 11656 // Step 2. Let serializedOrigin be the result of byte-serializing a request 11657 // origin with request. 11658 nsAutoCString serializedOrigin; 11659 nsCOMPtr<nsIURI> uri; 11660 { 11661 if (NS_FAILED(triggeringPrincipal->GetURI(getter_AddRefs(uri)))) { 11662 return; 11663 } 11664 11665 if (!uri) { 11666 if (isAddonRequest) { 11667 // For add-on compatibility prefer sending no header at all 11668 // instead of `Origin: null`. 11669 return; 11670 } 11671 11672 // Otherwise use "null" when the triggeringPrincipal's URI is nullptr. 11673 serializedOrigin.AssignLiteral("null"); 11674 } else if (HasNullRequestOrigin(this, uri, isAddonRequest)) { 11675 serializedOrigin.AssignLiteral("null"); 11676 } else { 11677 nsContentUtils::GetWebExposedOriginSerialization(uri, serializedOrigin); 11678 } 11679 } 11680 11681 // Step 3. If request’s response tainting is "cors" or request’s mode is 11682 // "websocket", then append (`Origin`, serializedOrigin) to request’s header 11683 // list. 11684 // 11685 // Note: We don't handle "websocket" here (yet?). 11686 if (mLoadInfo->GetTainting() == mozilla::LoadTainting::CORS) { 11687 MOZ_ALWAYS_SUCCEEDS(mRequestHead.SetHeader(nsHttp::Origin, serializedOrigin, 11688 false /* merge */)); 11689 return; 11690 } 11691 11692 // Step 4. Otherwise, if request’s method is neither `GET` nor `HEAD`, then: 11693 if (mRequestHead.IsGet() || mRequestHead.IsHead()) { 11694 // Modified Step 4 in case storage-access-headers are enabled 11695 if (!StaticPrefs::dom_storage_access_enabled() || 11696 !StaticPrefs::dom_storage_access_headers_enabled()) { 11697 // proceed with unmodified fetch spec 11698 return; 11699 } else { 11700 // the result of getting `Sec-Fetch-Storage-Access` from request’s header 11701 // list is "inactive" 11702 nsAutoCString storageAccess; 11703 nsresult rv = 11704 GetRequestHeader("Sec-Fetch-Storage-Access"_ns, storageAccess); 11705 if (NS_FAILED(rv) || !storageAccess.EqualsLiteral("inactive")) { 11706 return; 11707 } 11708 } 11709 } 11710 11711 if (!serializedOrigin.EqualsLiteral("null")) { 11712 // Step 4.1. (Implemented by ReferrerInfo::ShouldSetNullOriginHeader) 11713 if (ReferrerInfo::ShouldSetNullOriginHeader(this, uri)) { 11714 serializedOrigin.AssignLiteral("null"); 11715 } else if (StaticPrefs::network_http_sendOriginHeader() == 1) { 11716 // Non-standard: Restrict Origin to same-origin loads if requested by user 11717 nsAutoCString currentOrigin; 11718 nsContentUtils::GetWebExposedOriginSerialization(mURI, currentOrigin); 11719 if (!serializedOrigin.EqualsIgnoreCase(currentOrigin.get())) { 11720 // Origin header suppressed by user setting. 11721 serializedOrigin.AssignLiteral("null"); 11722 } 11723 } 11724 } 11725 11726 // Step 4.2. Append (`Origin`, serializedOrigin) to request’s header list. 11727 MOZ_ALWAYS_SUCCEEDS(mRequestHead.SetHeader(nsHttp::Origin, serializedOrigin, 11728 false /* merge */)); 11729 } 11730 11731 void nsHttpChannel::SetDoNotTrack() { 11732 /** 11733 * 'DoNotTrack' header should be added if 'privacy.donottrackheader.enabled' 11734 * is true. 11735 */ 11736 if (StaticPrefs::privacy_donottrackheader_enabled()) { 11737 DebugOnly<nsresult> rv = 11738 mRequestHead.SetHeader(nsHttp::DoNotTrack, "1"_ns, false); 11739 MOZ_ASSERT(NS_SUCCEEDED(rv)); 11740 } 11741 } 11742 11743 void nsHttpChannel::SetGlobalPrivacyControl() { 11744 MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread"); 11745 11746 if (StaticPrefs::privacy_globalprivacycontrol_functionality_enabled() && 11747 (StaticPrefs::privacy_globalprivacycontrol_enabled() || 11748 (StaticPrefs::privacy_globalprivacycontrol_pbmode_enabled() && 11749 NS_UsePrivateBrowsing(this)))) { 11750 // Send the header with a value of 1 to indicate opting-out 11751 DebugOnly<nsresult> rv = 11752 mRequestHead.SetHeader(nsHttp::GlobalPrivacyControl, "1"_ns, false); 11753 } 11754 } 11755 11756 void nsHttpChannel::ReportRcwnStats(bool isFromNet) { 11757 if (!StaticPrefs::network_http_rcwn_enabled()) { 11758 return; 11759 } 11760 11761 if (isFromNet) { 11762 // Race was won by the network if: 11763 // * we hadn't gotten a response from the cache yet when we started pumping 11764 // data from the network, whether or not the cache entry would have 11765 // been valid or not. 11766 // * If we did get a response from the cache first, but it wasn't valid or 11767 // just 11768 // indicated there was no entry, then it's not a network RCWN win 11769 // * Note that we consider it a Network win even if an invalid/expired cache 11770 // response comes in after the network response. 11771 11772 if (mRaceCacheWithNetwork && LoadNetworkWonRace()) { 11773 PROFILER_MARKER_TEXT( 11774 "RCWN", NETWORK, {}, 11775 nsPrintfCString("Network won valid = %d, channel %p, URI %s", 11776 LoadCachedContentIsValid(), this, mSpec.get())); 11777 gIOService->IncrementNetWonRequestNumber(); 11778 glean::network::race_cache_bandwidth_race_network_win.Accumulate( 11779 mTransferSize); 11780 } else { 11781 PROFILER_MARKER_TEXT( 11782 "RCWN", NETWORK, {}, 11783 nsPrintfCString( 11784 "Cache won or was replaced, valid = %d, channel %p, URI %s", 11785 LoadCachedContentIsValid(), this, mSpec.get())); 11786 } 11787 } else { 11788 if (mRaceCacheWithNetwork || mRaceDelay) { 11789 PROFILER_MARKER_TEXT( 11790 "RCWN", NETWORK, {}, 11791 nsPrintfCString("Cache won valid=%d, channel %p, URI %s", 11792 LoadCachedContentIsValid(), this, mSpec.get())); 11793 gIOService->IncrementCacheWonRequestNumber(); 11794 glean::network::race_cache_bandwidth_race_cache_win.Accumulate( 11795 mTransferSize); 11796 } 11797 } 11798 11799 gIOService->IncrementRequestNumber(); 11800 } 11801 11802 void nsHttpChannel::ReportSystemChannelTelemetry(nsresult status) { 11803 // Use status and httpStatus to determine 11804 // if it was successful, and if we had connectivity / offline in this time 11805 nsAutoCString domain; 11806 mOriginalURI->GetHost(domain); 11807 11808 if (!LoadUsedNetwork()) { 11809 // We're not really interested in any cached requests. 11810 return; 11811 } 11812 11813 if (!StringEndsWith(domain, ".mozilla.org"_ns) && 11814 !StringEndsWith(domain, ".mozilla.com"_ns) && 11815 mEssentialDomainCategory.isNothing()) { 11816 return; 11817 } 11818 11819 nsAutoCString label("ok"_ns); 11820 if (NS_FAILED(status)) { 11821 if (mCanceled) { 11822 // The request was cancelled. 11823 label = "cancel"_ns; 11824 } else if (NS_IsOffline()) { 11825 // The error occured while all interfaces are offline 11826 label = "offline"_ns; 11827 } else if (!hasConnectivity()) { 11828 // The error occured while the browser didn't have connectivity 11829 label = "connectivity"_ns; 11830 } else if (status == NS_ERROR_UNKNOWN_HOST) { 11831 // The failure was a DNS error 11832 label = "dns"_ns; 11833 } else if (NS_ERROR_GET_MODULE(status) == NS_ERROR_MODULE_SECURITY) { 11834 // The error was due to TLS 11835 label = "tls_fail"_ns; 11836 } else if (status == NS_ERROR_NET_RESET) { 11837 label = "reset"_ns; 11838 } else if (status == NS_ERROR_NET_TIMEOUT) { 11839 label = "timeout"_ns; 11840 } else if (status == NS_ERROR_CONNECTION_REFUSED) { 11841 label = "refused"_ns; 11842 } else if (status == NS_ERROR_NET_PARTIAL_TRANSFER) { 11843 label = "partial"_ns; 11844 } else { 11845 // Unspecified error. If this bucket is too big we might add other labels. 11846 label = "other"_ns; 11847 } 11848 } else if (mResponseHead && mResponseHead->Status() / 100 != 2) { 11849 // There was no channel error, but the server responded with a non-2XX 11850 // status code. 11851 label = "http_status"; 11852 } 11853 11854 // This is the first time failure channel 11855 if (mEssentialDomainCategory.isNothing()) { 11856 auto category = GetEssentialDomainCategory(domain); 11857 switch (category) { 11858 case EssentialDomainCategory::SubAddonsMozillaOrg: { 11859 mozilla::glean::network::system_channel_addonversion_status.Get(label) 11860 .Add(1); 11861 return; 11862 } 11863 case EssentialDomainCategory::AddonsMozillaOrg: { 11864 mozilla::glean::network::system_channel_addon_status.Get(label).Add(1); 11865 return; 11866 } 11867 case EssentialDomainCategory::Aus5MozillaOrg: { 11868 mozilla::glean::network::system_channel_update_status.Get(label).Add(1); 11869 return; 11870 } 11871 case EssentialDomainCategory::RemoteSettings: { 11872 mozilla::glean::network::system_channel_remote_settings_status 11873 .Get(label) 11874 .Add(1); 11875 return; 11876 } 11877 case EssentialDomainCategory::Telemetry: { 11878 mozilla::glean::network::system_channel_telemetry_status.Get(label).Add( 11879 1); 11880 return; 11881 } 11882 default: { 11883 // Not one of the probes we recorded earlier. 11884 mozilla::glean::network::system_channel_other_status.Get(label).Add(1); 11885 return; 11886 } 11887 } 11888 } 11889 11890 // This is a retry. 11891 switch (mEssentialDomainCategory.ref()) { 11892 case EssentialDomainCategory::SubAddonsMozillaOrg: { 11893 mozilla::glean::network::retried_system_channel_addonversion_status 11894 .Get(label) 11895 .Add(1); 11896 return; 11897 } 11898 case EssentialDomainCategory::AddonsMozillaOrg: { 11899 mozilla::glean::network::retried_system_channel_addon_status.Get(label) 11900 .Add(1); 11901 return; 11902 } 11903 case EssentialDomainCategory::Aus5MozillaOrg: { 11904 mozilla::glean::network::retried_system_channel_update_status.Get(label) 11905 .Add(1); 11906 return; 11907 } 11908 case EssentialDomainCategory::RemoteSettings: { 11909 mozilla::glean::network::retried_system_channel_remote_settings_status 11910 .Get(label) 11911 .Add(1); 11912 return; 11913 } 11914 case EssentialDomainCategory::Telemetry: { 11915 mozilla::glean::network::retried_system_channel_telemetry_status 11916 .Get(label) 11917 .Add(1); 11918 return; 11919 } 11920 default: { 11921 // Not one of the probes we recorded earlier. 11922 mozilla::glean::network::retried_system_channel_other_status.Get(label) 11923 .Add(1); 11924 return; 11925 } 11926 } 11927 } 11928 11929 NS_IMETHODIMP 11930 nsHttpChannel::GetAllowRacing(bool* aAllowRacing) { 11931 *aAllowRacing = mAllowRCWN; 11932 return NS_OK; 11933 } 11934 11935 NS_IMETHODIMP 11936 nsHttpChannel::SetAllowRacing(bool aAllowRacing) { 11937 mAllowRCWN = aAllowRacing; 11938 return NS_OK; 11939 } 11940 11941 NS_IMETHODIMP 11942 nsHttpChannel::Test_delayCacheEntryOpeningBy(int32_t aTimeout) { 11943 LOG(("nsHttpChannel::Test_delayCacheEntryOpeningBy this=%p timeout=%d", this, 11944 aTimeout)); 11945 MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread"); 11946 mRaceCacheWithNetwork = true; 11947 mCacheOpenDelay = aTimeout; 11948 if (mCacheOpenTimer) { 11949 mCacheOpenTimer->SetDelay(aTimeout); 11950 } 11951 return NS_OK; 11952 } 11953 11954 NS_IMETHODIMP 11955 nsHttpChannel::Test_triggerDelayedOpenCacheEntry() { 11956 LOG(("nsHttpChannel::Test_triggerDelayedOpenCacheEntry this=%p", this)); 11957 MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread"); 11958 nsresult rv; 11959 if (!mCacheOpenDelay) { 11960 // No delay was set. 11961 return NS_ERROR_NOT_AVAILABLE; 11962 } 11963 if (!mCacheOpenFunc) { 11964 // There should be a runnable. 11965 return NS_ERROR_FAILURE; 11966 } 11967 if (mCacheOpenTimer) { 11968 rv = mCacheOpenTimer->Cancel(); 11969 if (NS_FAILED(rv)) { 11970 return rv; 11971 } 11972 mCacheOpenTimer = nullptr; 11973 } 11974 mCacheOpenDelay = 0; 11975 // Avoid re-entrancy issues by nulling our mCacheOpenFunc before calling it. 11976 std::function<void(nsHttpChannel*)> cacheOpenFunc = nullptr; 11977 std::swap(cacheOpenFunc, mCacheOpenFunc); 11978 cacheOpenFunc(this); 11979 11980 return NS_OK; 11981 } 11982 11983 nsresult nsHttpChannel::TriggerNetworkWithDelay(uint32_t aDelay) { 11984 MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread"); 11985 11986 LOG(("nsHttpChannel::TriggerNetworkWithDelay [this=%p, delay=%u]\n", this, 11987 aDelay)); 11988 11989 if (mCanceled) { 11990 LOG((" channel was canceled.\n")); 11991 return mStatus; 11992 } 11993 11994 // If a network request has already gone out, there is no point in 11995 // doing this again. 11996 if (mNetworkTriggered) { 11997 LOG((" network already triggered. Returning.\n")); 11998 return NS_OK; 11999 } 12000 12001 if (mNetworkTriggerDelay) { 12002 aDelay = mNetworkTriggerDelay; 12003 } 12004 12005 if (!aDelay) { 12006 // We cannot call TriggerNetwork() directly here, because it would 12007 // cause performance regression in tp6 tests, see bug 1398847. 12008 return NS_DispatchToMainThread( 12009 NewRunnableMethod("net::nsHttpChannel::TriggerNetworkWithDelay", this, 12010 &nsHttpChannel::TriggerNetwork), 12011 NS_DISPATCH_NORMAL); 12012 } 12013 12014 MOZ_ASSERT(!mNetworkTriggerTimer); 12015 mNetworkTriggerTimer = NS_NewTimer(); 12016 auto callback = MakeRefPtr<TimerCallback>(this); 12017 LOG(("Creating new networkTriggertimer for delay")); 12018 mNetworkTriggerTimer->InitWithCallback(callback, aDelay, 12019 nsITimer::TYPE_ONE_SHOT); 12020 return NS_OK; 12021 } 12022 12023 nsresult nsHttpChannel::TriggerNetwork() { 12024 MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread"); 12025 12026 LOG(("nsHttpChannel::TriggerNetwork [this=%p]\n", this)); 12027 12028 if (mCanceled) { 12029 LOG((" channel was canceled.\n")); 12030 return mStatus; 12031 } 12032 12033 // If a network request has already gone out, there is no point in 12034 // doing this again. 12035 if (mNetworkTriggered) { 12036 LOG((" network already triggered. Returning.\n")); 12037 return NS_OK; 12038 } 12039 12040 mNetworkTriggered = true; 12041 if (mNetworkTriggerTimer) { 12042 mNetworkTriggerTimer->Cancel(); 12043 mNetworkTriggerTimer = nullptr; 12044 } 12045 12046 // If we are waiting for a proxy request, that means we can't trigger 12047 // the next step just yet. We need for mConnectionInfo to be non-null 12048 // before we call ContinueConnect. OnProxyAvailable will trigger 12049 // BeginConnect, and Connect will call ContinueConnect even if it's 12050 // for the cache callbacks. 12051 if (mProxyRequest) { 12052 LOG((" proxy request in progress. Delaying network trigger.\n")); 12053 mWaitingForProxy = true; 12054 return NS_OK; 12055 } 12056 12057 // If |mCacheOpenFunc| is assigned, we're delaying opening the entry to 12058 // simulate racing. Although cache entry opening hasn't started yet, we're 12059 // actually racing, so we must set mRaceCacheWithNetwork to true now. 12060 mRaceCacheWithNetwork = 12061 AwaitingCacheCallbacks() && 12062 (mCacheOpenFunc || StaticPrefs::network_http_rcwn_enabled()); 12063 12064 LOG((" triggering network rcwn=%d\n", bool(mRaceCacheWithNetwork))); 12065 return ContinueConnect(); 12066 } 12067 12068 nsresult nsHttpChannel::OnSuspendTimeout() { 12069 MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread"); 12070 12071 LOG(("nsHttpChannel::OnSuspendTimeout [this=%p]\n", this)); 12072 12073 // If we're still suspended and have a cache entry, enable bypass mode 12074 // This allows any waiting or future listeners to continue 12075 if (mSuspendCount > 0 && mCacheEntry) { 12076 LOG((" suspend timeout: bypassing writer lock")); 12077 mCacheEntry->SetBypassWriterLock(true); 12078 mBypassCacheWriterSet = true; 12079 } 12080 12081 return NS_OK; 12082 } 12083 12084 void nsHttpChannel::MaybeRaceCacheWithNetwork() { 12085 nsresult rv; 12086 12087 nsCOMPtr<nsINetworkLinkService> netLinkSvc; 12088 netLinkSvc = do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv); 12089 if (NS_FAILED(rv)) { 12090 return; 12091 } 12092 12093 uint32_t linkType; 12094 rv = netLinkSvc->GetLinkType(&linkType); 12095 if (NS_FAILED(rv)) { 12096 return; 12097 } 12098 12099 if (!(linkType == nsINetworkLinkService::LINK_TYPE_ETHERNET || 12100 #ifndef MOZ_WIDGET_ANDROID 12101 // On Android we don't assume an unknown link type is unmetered 12102 linkType == nsINetworkLinkService::LINK_TYPE_UNKNOWN || 12103 #endif 12104 linkType == nsINetworkLinkService::LINK_TYPE_USB || 12105 linkType == nsINetworkLinkService::LINK_TYPE_WIFI)) { 12106 return; 12107 } 12108 12109 // Don't trigger the network if the load flags say so. 12110 if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_NO_NETWORK_IO)) { 12111 return; 12112 } 12113 12114 // We must not race if the channel has a failure status code. 12115 if (NS_FAILED(mStatus)) { 12116 return; 12117 } 12118 12119 // If a CORS Preflight is required we must not race. 12120 if (LoadRequireCORSPreflight() && !LoadIsCorsPreflightDone()) { 12121 return; 12122 } 12123 12124 if (CacheFileUtils::CachePerfStats::IsCacheSlow()) { 12125 // If the cache is slow, trigger the network request immediately. 12126 mRaceDelay = 0; 12127 } else { 12128 // Give cache a headstart of 3 times the average cache entry open time. 12129 mRaceDelay = CacheFileUtils::CachePerfStats::GetAverage( 12130 CacheFileUtils::CachePerfStats::ENTRY_OPEN, true) * 12131 3; 12132 // We use microseconds in CachePerfStats but we need milliseconds 12133 // for TriggerNetwork. 12134 mRaceDelay /= 1000; 12135 } 12136 12137 mRaceDelay = std::clamp<uint32_t>( 12138 mRaceDelay, StaticPrefs::network_http_rcwn_min_wait_before_racing_ms(), 12139 StaticPrefs::network_http_rcwn_max_wait_before_racing_ms()); 12140 12141 MOZ_ASSERT(StaticPrefs::network_http_rcwn_enabled() || mNetworkTriggerDelay, 12142 "The pref must be turned on."); 12143 LOG(("nsHttpChannel::MaybeRaceCacheWithNetwork [this=%p, delay=%u]\n", this, 12144 mRaceDelay)); 12145 12146 TriggerNetworkWithDelay(mRaceDelay); 12147 } 12148 12149 NS_IMETHODIMP 12150 nsHttpChannel::Test_triggerNetwork(int32_t aTimeout) { 12151 LOG(("nsHttpChannel::Test_triggerNetwork this=%p timeout=%d", this, 12152 aTimeout)); 12153 MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread"); 12154 12155 // We set the trigger delay to the specified timeout. 12156 mRaceCacheWithNetwork = true; 12157 mNetworkTriggerDelay = aTimeout; 12158 12159 // If we already have a timer, set the delay/ 12160 if (mNetworkTriggerTimer) { 12161 // If the timeout is 0 and there is a timer, we can trigger 12162 // the network immediately. 12163 MOZ_ASSERT(LoadWasOpened(), "Must have been opened before"); 12164 if (!aTimeout) { 12165 return TriggerNetwork(); 12166 } 12167 mNetworkTriggerTimer->SetDelay(aTimeout); 12168 } 12169 return NS_OK; 12170 } 12171 12172 nsHttpChannel::TimerCallback::TimerCallback(nsHttpChannel* aChannel) 12173 : mChannel(aChannel) {} 12174 12175 NS_IMPL_ISUPPORTS(nsHttpChannel::TimerCallback, nsITimerCallback, nsINamed) 12176 12177 NS_IMETHODIMP 12178 nsHttpChannel::TimerCallback::GetName(nsACString& aName) { 12179 aName.AssignLiteral("nsHttpChannel"); 12180 return NS_OK; 12181 } 12182 12183 NS_IMETHODIMP 12184 nsHttpChannel::TimerCallback::Notify(nsITimer* aTimer) { 12185 if (aTimer == mChannel->mCacheOpenTimer) { 12186 return mChannel->Test_triggerDelayedOpenCacheEntry(); 12187 } 12188 if (aTimer == mChannel->mNetworkTriggerTimer) { 12189 return mChannel->TriggerNetwork(); 12190 } 12191 if (aTimer == mChannel->mSuspendTimer) { 12192 return mChannel->OnSuspendTimeout(); 12193 } 12194 MOZ_CRASH("Unknown timer"); 12195 12196 return NS_OK; 12197 } 12198 12199 bool nsHttpChannel::EligibleForTailing() { 12200 if (!(mClassOfService.Flags() & nsIClassOfService::Tail)) { 12201 return false; 12202 } 12203 12204 if (mClassOfService.Flags() & 12205 (nsIClassOfService::UrgentStart | nsIClassOfService::Leader | 12206 nsIClassOfService::TailForbidden)) { 12207 return false; 12208 } 12209 12210 if (mClassOfService.Flags() & nsIClassOfService::Unblocked && 12211 !(mClassOfService.Flags() & nsIClassOfService::TailAllowed)) { 12212 return false; 12213 } 12214 12215 if (IsNavigation()) { 12216 return false; 12217 } 12218 12219 return true; 12220 } 12221 12222 bool nsHttpChannel::WaitingForTailUnblock() { 12223 nsresult rv; 12224 12225 if (!gHttpHandler->IsTailBlockingEnabled()) { 12226 LOG(("nsHttpChannel %p tail-blocking disabled", this)); 12227 return false; 12228 } 12229 12230 if (!EligibleForTailing()) { 12231 LOG(("nsHttpChannel %p not eligible for tail-blocking", this)); 12232 AddAsNonTailRequest(); 12233 return false; 12234 } 12235 12236 if (!EnsureRequestContext()) { 12237 LOG(("nsHttpChannel %p no request context", this)); 12238 return false; 12239 } 12240 12241 LOG(("nsHttpChannel::WaitingForTailUnblock this=%p, rc=%p", this, 12242 mRequestContext.get())); 12243 12244 bool blocked; 12245 rv = mRequestContext->IsContextTailBlocked(this, &blocked); 12246 if (NS_FAILED(rv)) { 12247 return false; 12248 } 12249 12250 LOG((" blocked=%d", blocked)); 12251 12252 return blocked; 12253 } 12254 12255 //----------------------------------------------------------------------------- 12256 // nsHttpChannel::nsIRequestTailUnblockCallback 12257 //----------------------------------------------------------------------------- 12258 12259 // Must be implemented in the leaf class because we don't have 12260 // AsyncAbort in HttpBaseChannel. 12261 NS_IMETHODIMP 12262 nsHttpChannel::OnTailUnblock(nsresult rv) { 12263 LOG(("nsHttpChannel::OnTailUnblock this=%p rv=%" PRIx32 " rc=%p", this, 12264 static_cast<uint32_t>(rv), mRequestContext.get())); 12265 AUTO_PROFILER_FLOW_MARKER("nsHttpChannel::OnTailUnblock", NETWORK, 12266 Flow::FromPointer(this)); 12267 MOZ_RELEASE_ASSERT(mOnTailUnblock); 12268 12269 if (NS_FAILED(mStatus)) { 12270 rv = mStatus; 12271 } 12272 12273 if (NS_SUCCEEDED(rv)) { 12274 auto callback = mOnTailUnblock; 12275 mOnTailUnblock = nullptr; 12276 rv = (this->*callback)(); 12277 } 12278 12279 if (NS_FAILED(rv)) { 12280 CloseCacheEntry(false); 12281 return AsyncAbort(rv); 12282 } 12283 12284 return NS_OK; 12285 } 12286 12287 void nsHttpChannel::SetWarningReporter( 12288 HttpChannelSecurityWarningReporter* aReporter) { 12289 LOG(("nsHttpChannel [this=%p] SetWarningReporter [%p]", this, aReporter)); 12290 mWarningReporter = aReporter; 12291 } 12292 12293 HttpChannelSecurityWarningReporter* nsHttpChannel::GetWarningReporter() { 12294 LOG(("nsHttpChannel [this=%p] GetWarningReporter [%p]", this, 12295 mWarningReporter.get())); 12296 return mWarningReporter.get(); 12297 } 12298 12299 // The specification for ORB is currently being written: 12300 // https://whatpr.org/fetch/1442.html#orb-algorithm 12301 // The `opaque-response-safelist check` is implemented in: 12302 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckBeforeSniff` 12303 // * `nsHttpChannel::DisableIsOpaqueResponseAllowedAfterSniffCheck` 12304 // * `HttpBaseChannel::PerformOpaqueResponseSafelistCheckAfterSniff` 12305 // * `OpaqueResponseBlocker::ValidateJavaScript` 12306 // 12307 // Should only be called by nsMediaSniffer::GetMIMETypeFromContent and 12308 // imageLoader::GetMIMETypeFromContent when the content type can be 12309 // recognized by these sniffers. 12310 void nsHttpChannel::DisableIsOpaqueResponseAllowedAfterSniffCheck( 12311 SnifferType aType) { 12312 // https://whatpr.org/fetch/1442.html#orb-algorithm 12313 // This method covers steps, 8 and 10. 12314 MOZ_ASSERT(XRE_IsParentProcess()); 12315 12316 if (NeedOpaqueResponseAllowedCheckAfterSniff()) { 12317 MOZ_ASSERT(mCachedOpaqueResponseBlockingPref); 12318 12319 // If the sniffer type is media and the request comes from a media element, 12320 // we would like to check: 12321 // - Whether the information provided by the media element shows it's an 12322 // initial request. 12323 // - Whether the response's status is either 200 or 206. 12324 // 12325 // If any of the results is false, then we set 12326 // mBlockOpaqueResponseAfterSniff to true and block the response later. 12327 if (aType == SnifferType::Media) { 12328 // Step 8 12329 MOZ_ASSERT(mLoadInfo); 12330 12331 bool isMediaRequest; 12332 mLoadInfo->GetIsMediaRequest(&isMediaRequest); 12333 if (isMediaRequest) { 12334 bool isInitialRequest; 12335 mLoadInfo->GetIsMediaInitialRequest(&isInitialRequest); 12336 MOZ_ASSERT(isInitialRequest); 12337 12338 if (!isInitialRequest) { 12339 // Step 8.1 12340 BlockOpaqueResponseAfterSniff( 12341 u"media request after sniffing, but not initial request"_ns, 12342 OpaqueResponseBlockedTelemetryReason::eMediaNotInitial); 12343 return; 12344 } 12345 12346 if (mResponseHead->Status() != 200 && mResponseHead->Status() != 206) { 12347 // Step 8.2 12348 BlockOpaqueResponseAfterSniff( 12349 u"media request's response status is neither 200 nor 206"_ns, 12350 OpaqueResponseBlockedTelemetryReason::eMediaIncorrectResp); 12351 return; 12352 } 12353 } 12354 } 12355 12356 // Step 8.3 if `aType == SnifferType::Media` 12357 // Step 9 can be skipped, only `HTMLMediaElement` ever sets isMediaRequest. 12358 // Step 10 if `aType == SnifferType::Image` 12359 AllowOpaqueResponseAfterSniff(); 12360 } 12361 } 12362 12363 namespace { 12364 12365 class CopyNonDefaultHeaderVisitor final : public nsIHttpHeaderVisitor { 12366 nsCOMPtr<nsIHttpChannel> mTarget; 12367 12368 ~CopyNonDefaultHeaderVisitor() = default; 12369 12370 NS_IMETHOD 12371 VisitHeader(const nsACString& aHeader, const nsACString& aValue) override { 12372 if (aValue.IsEmpty()) { 12373 return mTarget->SetEmptyRequestHeader(aHeader); 12374 } 12375 return mTarget->SetRequestHeader(aHeader, aValue, false /* merge */); 12376 } 12377 12378 public: 12379 explicit CopyNonDefaultHeaderVisitor(nsIHttpChannel* aTarget) 12380 : mTarget(aTarget) { 12381 MOZ_DIAGNOSTIC_ASSERT(mTarget); 12382 } 12383 12384 NS_DECL_ISUPPORTS 12385 }; 12386 12387 NS_IMPL_ISUPPORTS(CopyNonDefaultHeaderVisitor, nsIHttpHeaderVisitor) 12388 12389 } // anonymous namespace 12390 12391 nsresult nsHttpChannel::RedirectToInterceptedChannel() { 12392 nsCOMPtr<nsINetworkInterceptController> controller; 12393 GetCallback(controller); 12394 12395 RefPtr<InterceptedHttpChannel> intercepted = 12396 InterceptedHttpChannel::CreateForInterception( 12397 mChannelCreationTime, mChannelCreationTimestamp, mAsyncOpenTime); 12398 12399 nsCOMPtr<nsILoadInfo> redirectLoadInfo = 12400 CloneLoadInfoForRedirect(mURI, nsIChannelEventSink::REDIRECT_INTERNAL); 12401 12402 nsresult rv = intercepted->Init( 12403 mURI, mCaps, static_cast<nsProxyInfo*>(mProxyInfo.get()), 12404 mProxyResolveFlags, mProxyURI, mChannelId, redirectLoadInfo); 12405 12406 rv = SetupReplacementChannel(mURI, intercepted, true, 12407 nsIChannelEventSink::REDIRECT_INTERNAL); 12408 NS_ENSURE_SUCCESS(rv, rv); 12409 12410 // Some APIs, like fetch(), allow content to set non-standard headers. 12411 // Normally these APIs are responsible for copying these headers across 12412 // redirects. In the e10s parent-side intercept case, though, we currently 12413 // "hide" the internal redirect to the InterceptedHttpChannel. So the 12414 // fetch() API does not have the opportunity to move headers over. 12415 // Therefore, we do it automatically here. 12416 // 12417 // Once child-side interception is removed and the internal redirect no 12418 // longer needs to be "hidden", then this header copying code can be 12419 // removed. 12420 nsCOMPtr<nsIHttpHeaderVisitor> visitor = 12421 new CopyNonDefaultHeaderVisitor(intercepted); 12422 rv = VisitNonDefaultRequestHeaders(visitor); 12423 NS_ENSURE_SUCCESS(rv, rv); 12424 12425 mRedirectChannel = intercepted; 12426 12427 PushRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI); 12428 12429 rv = gHttpHandler->AsyncOnChannelRedirect( 12430 this, intercepted, nsIChannelEventSink::REDIRECT_INTERNAL); 12431 12432 if (NS_SUCCEEDED(rv)) { 12433 rv = WaitForRedirectCallback(); 12434 } 12435 12436 if (NS_FAILED(rv)) { 12437 AutoRedirectVetoNotifier notifier(this, rv); 12438 12439 PopRedirectAsyncFunc(&nsHttpChannel::ContinueAsyncRedirectChannelToURI); 12440 } 12441 12442 return rv; 12443 } 12444 12445 void nsHttpChannel::ReEvaluateReferrerAfterTrackingStatusIsKnown() { 12446 nsCOMPtr<nsICookieJarSettings> cjs; 12447 if (mLoadInfo) { 12448 (void)mLoadInfo->GetCookieJarSettings(getter_AddRefs(cjs)); 12449 } 12450 if (!cjs) { 12451 cjs = net::CookieJarSettings::Create(mLoadInfo->GetLoadingPrincipal()); 12452 } 12453 if (cjs->GetRejectThirdPartyContexts()) { 12454 bool isPrivate = mLoadInfo->GetOriginAttributes().IsPrivateBrowsing(); 12455 // If our referrer has been set before, and our referrer policy is unset 12456 // (default policy) if we thought the channel wasn't a third-party 12457 // tracking channel, we may need to set our referrer with referrer policy 12458 // once again to ensure our defaults properly take effect now. 12459 if (mReferrerInfo) { 12460 ReferrerInfo* referrerInfo = 12461 static_cast<ReferrerInfo*>(mReferrerInfo.get()); 12462 12463 if (referrerInfo->IsPolicyOverrided() && 12464 referrerInfo->ReferrerPolicy() == 12465 ReferrerInfo::GetDefaultReferrerPolicy(nullptr, nullptr, 12466 isPrivate)) { 12467 nsCOMPtr<nsIReferrerInfo> newReferrerInfo = 12468 referrerInfo->CloneWithNewPolicy( 12469 ReferrerInfo::GetDefaultReferrerPolicy(this, mURI, isPrivate)); 12470 // The arguments passed to SetReferrerInfoInternal here should mirror 12471 // the arguments passed in 12472 // HttpChannelChild::RecvOverrideReferrerInfoDuringBeginConnect(). 12473 SetReferrerInfoInternal(newReferrerInfo, false, true, true); 12474 12475 nsCOMPtr<nsIParentChannel> parentChannel; 12476 NS_QueryNotificationCallbacks(this, parentChannel); 12477 RefPtr<HttpChannelParent> httpParent = do_QueryObject(parentChannel); 12478 if (httpParent) { 12479 httpParent->OverrideReferrerInfoDuringBeginConnect(newReferrerInfo); 12480 } 12481 } 12482 } 12483 } 12484 } 12485 12486 namespace { 12487 12488 class BackgroundRevalidatingListener : public nsIStreamListener { 12489 NS_DECL_ISUPPORTS 12490 12491 NS_DECL_NSISTREAMLISTENER 12492 NS_DECL_NSIREQUESTOBSERVER 12493 12494 private: 12495 virtual ~BackgroundRevalidatingListener() = default; 12496 }; 12497 12498 NS_IMETHODIMP 12499 BackgroundRevalidatingListener::OnStartRequest(nsIRequest* request) { 12500 return NS_OK; 12501 } 12502 12503 NS_IMETHODIMP 12504 BackgroundRevalidatingListener::OnDataAvailable(nsIRequest* request, 12505 nsIInputStream* input, 12506 uint64_t offset, 12507 uint32_t count) { 12508 uint32_t bytesRead = 0; 12509 return input->ReadSegments(NS_DiscardSegment, nullptr, count, &bytesRead); 12510 } 12511 12512 NS_IMETHODIMP 12513 BackgroundRevalidatingListener::OnStopRequest(nsIRequest* request, 12514 nsresult status) { 12515 if (NS_FAILED(status)) { 12516 return status; 12517 } 12518 12519 nsCOMPtr<nsIHttpChannel> channel(do_QueryInterface(request)); 12520 if (gHttpHandler) { 12521 gHttpHandler->OnBackgroundRevalidation(channel); 12522 } 12523 return NS_OK; 12524 } 12525 12526 NS_IMPL_ISUPPORTS(BackgroundRevalidatingListener, nsIStreamListener, 12527 nsIRequestObserver) 12528 12529 } // namespace 12530 12531 void nsHttpChannel::PerformBackgroundCacheRevalidation() { 12532 if (!StaticPrefs::network_http_stale_while_revalidate_enabled()) { 12533 return; 12534 } 12535 12536 // This is a channel doing a revalidation. It shouldn't do it again. 12537 if (mStaleRevalidation) { 12538 return; 12539 } 12540 12541 LOG(("nsHttpChannel::PerformBackgroundCacheRevalidation %p", this)); 12542 12543 (void)NS_DispatchToMainThreadQueue( 12544 NewIdleRunnableMethod( 12545 "nsHttpChannel::PerformBackgroundCacheRevalidation", this, 12546 &nsHttpChannel::PerformBackgroundCacheRevalidationNow), 12547 EventQueuePriority::Idle); 12548 } 12549 12550 void nsHttpChannel::PerformBackgroundCacheRevalidationNow() { 12551 LOG(("nsHttpChannel::PerformBackgroundCacheRevalidationNow %p", this)); 12552 12553 MOZ_ASSERT(NS_IsMainThread()); 12554 12555 nsresult rv; 12556 12557 nsLoadFlags loadFlags = mLoadFlags | LOAD_ONLY_IF_MODIFIED | VALIDATE_ALWAYS | 12558 LOAD_BACKGROUND | LOAD_BYPASS_SERVICE_WORKER; 12559 12560 nsCOMPtr<nsIChannel> validatingChannel; 12561 rv = NS_NewChannelInternal(getter_AddRefs(validatingChannel), mURI, mLoadInfo, 12562 nullptr /* performance storage */, mLoadGroup, 12563 mCallbacks, loadFlags); 12564 if (NS_FAILED(rv)) { 12565 LOG((" failed to created the channel, rv=0x%08x", 12566 static_cast<uint32_t>(rv))); 12567 return; 12568 } 12569 12570 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(validatingChannel)); 12571 MOZ_ASSERT(httpChannel); 12572 nsCOMPtr<nsIHttpHeaderVisitor> visitor = 12573 new CopyNonDefaultHeaderVisitor(httpChannel); 12574 rv = VisitNonDefaultRequestHeaders(visitor); 12575 if (NS_FAILED(rv)) { 12576 LOG(("failed to copy headers to the validating channel, rv=0x%08x", 12577 static_cast<uint32_t>(rv))); 12578 return; 12579 } 12580 12581 nsCOMPtr<nsISupportsPriority> priority(do_QueryInterface(validatingChannel)); 12582 if (priority) { 12583 priority->SetPriority(nsISupportsPriority::PRIORITY_LOWEST); 12584 } 12585 12586 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(validatingChannel)); 12587 if (cos) { 12588 cos->AddClassFlags(nsIClassOfService::Tail); 12589 } 12590 12591 RefPtr<nsHttpChannel> httpChan = do_QueryObject(validatingChannel); 12592 if (httpChan) { 12593 httpChan->mStaleRevalidation = true; 12594 } 12595 12596 RefPtr<BackgroundRevalidatingListener> listener = 12597 new BackgroundRevalidatingListener(); 12598 rv = validatingChannel->AsyncOpen(listener); 12599 if (NS_FAILED(rv)) { 12600 LOG((" failed to open the channel, rv=0x%08x", static_cast<uint32_t>(rv))); 12601 return; 12602 } 12603 12604 LOG((" %p is re-validating with a new channel %p", this, 12605 validatingChannel.get())); 12606 } 12607 12608 NS_IMETHODIMP 12609 nsHttpChannel::SetEarlyHintObserver(nsIEarlyHintObserver* aObserver) { 12610 mEarlyHintObserver = aObserver; 12611 return NS_OK; 12612 } 12613 12614 NS_IMETHODIMP 12615 nsHttpChannel::EarlyHint(const nsACString& aLinkHeader, 12616 const nsACString& aReferrerPolicy, 12617 const nsACString& aCspHeader) { 12618 LOG(("nsHttpChannel::EarlyHint.\n")); 12619 12620 if (mEarlyHintObserver && nsContentUtils::ComputeIsSecureContext(this)) { 12621 LOG(("nsHttpChannel::EarlyHint propagated.\n")); 12622 mEarlyHintObserver->EarlyHint(aLinkHeader, aReferrerPolicy, aCspHeader); 12623 } 12624 return NS_OK; 12625 } 12626 12627 NS_IMETHODIMP nsHttpChannel::SetResponseOverride( 12628 nsIReplacedHttpResponse* aReplacedHttpResponse) { 12629 mOverrideResponse = new nsMainThreadPtrHolder<nsIReplacedHttpResponse>( 12630 "nsIReplacedHttpResponse", aReplacedHttpResponse); 12631 12632 if (LoadRequireCORSPreflight()) { 12633 // Bug 1986615, Bug 1940738, responses provided via setResponseOverride will 12634 // be handled before the preflight can be sent, flag it as done. 12635 StoreIsCorsPreflightDone(true); 12636 } 12637 12638 return NS_OK; 12639 } 12640 12641 NS_IMETHODIMP nsHttpChannel::SetResponseStatus(uint32_t aStatus, 12642 const nsACString& aStatusText) { 12643 if (!mResponseHead) { 12644 return NS_ERROR_NOT_AVAILABLE; 12645 } 12646 12647 nsAutoCString statusText(aStatusText); 12648 nsAutoCString protocolVersion( 12649 nsHttp::GetProtocolVersion(mResponseHead->Version())); 12650 ToUpperCase(protocolVersion); 12651 12652 nsPrintfCString line("%s %u %s", protocolVersion.get(), aStatus, 12653 statusText.get()); 12654 12655 return mResponseHead->ParseStatusLine(line); 12656 } 12657 12658 NS_IMETHODIMP nsHttpChannel::SetWebTransportSessionEventListener( 12659 WebTransportSessionEventListener* aListener) { 12660 mWebTransportSessionEventListener = aListener; 12661 return NS_OK; 12662 } 12663 12664 already_AddRefed<WebTransportSessionEventListener> 12665 nsHttpChannel::GetWebTransportSessionEventListener() { 12666 RefPtr<WebTransportSessionEventListener> wt = 12667 mWebTransportSessionEventListener; 12668 return wt.forget(); 12669 } 12670 12671 NS_IMETHODIMP nsHttpChannel::GetLastTransportStatus( 12672 nsresult* aLastTransportStatus) { 12673 *aLastTransportStatus = mLastTransportStatus; 12674 return NS_OK; 12675 } 12676 12677 NS_IMETHODIMP 12678 nsHttpChannel::GetCacheDisposition(CacheDisposition* aDisposition) { 12679 if (!aDisposition) { 12680 return NS_ERROR_INVALID_ARG; 12681 } 12682 *aDisposition = mCacheDisposition; 12683 return NS_OK; 12684 } 12685 12686 } // namespace net 12687 } // namespace mozilla