fune/dom/security/nsHTTPSOnlyUtils.cpp
Manuel Bucher 0da5cdc8ad Bug 1747230 - Fix IsUpgradeDowngradeEndlessLoop blocking legitimate redirects when redirecting to different query parameters a=dmeehan
This changes where the IsUpgradeDowngradeEndlessLoop check triggers.
Before this patch, it triggered during the redirect caused by the https
upgrade. With this patch, it triggers during the downgrade for http
redirects. META and JS redirect are still detected during upgrade.
This should be fixed as a follow up (See Bug 1896691).
Downgrade in this context means same url, except with the scheme http
instead of https.

Different query parameters normally lead to different responses by web servers.
Don't consider the '#ref' part of the uri, because it doesn't get send to
the server and therefore can't change the server response.

We can't use the redirect chain anymore, because the query parameters
are trimmed since Bug 1715785.

This also removes the config option dom.security.https_only_check_path_upgrade_downgrade_endless_loop,
because it adds unnecessary complexity. Removing it for this patch is
easier.

https-only, https-first and httpssvc_https_upgrade tests had to be
modified, because they depended on the incorrect handling of query
strings in loop detection.

Original Revision: https://phabricator.services.mozilla.com/D193672

Differential Revision: https://phabricator.services.mozilla.com/D214977
2024-06-27 13:01:12 +00:00

1196 lines
44 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/Components.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/glean/GleanMetrics.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/OriginAttributes.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/net/DNS.h"
#include "nsContentUtils.h"
#include "nsHTTPSOnlyUtils.h"
#include "nsIConsoleService.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIHttpsOnlyModePermission.h"
#include "nsILoadInfo.h"
#include "nsIPermissionManager.h"
#include "nsIPrincipal.h"
#include "nsIRedirectHistoryEntry.h"
#include "nsIScriptError.h"
#include "nsIURIMutator.h"
#include "nsNetUtil.h"
#include "prnetdb.h"
/* static */
bool nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(bool aFromPrivateWindow) {
// if the general pref is set to true, then we always return
if (mozilla::StaticPrefs::dom_security_https_only_mode()) {
return true;
}
// otherwise we check if executing in private browsing mode and return true
// if the PBM pref for HTTPS-Only is set.
if (aFromPrivateWindow &&
mozilla::StaticPrefs::dom_security_https_only_mode_pbm()) {
return true;
}
return false;
}
/* static */
bool nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(bool aFromPrivateWindow) {
// HTTPS-Only takes priority over HTTPS-First
if (IsHttpsOnlyModeEnabled(aFromPrivateWindow)) {
return false;
}
// if the general pref is set to true, then we always return
if (mozilla::StaticPrefs::dom_security_https_first()) {
return true;
}
// otherwise we check if executing in private browsing mode and return true
// if the PBM pref for HTTPS-First is set.
if (aFromPrivateWindow &&
mozilla::StaticPrefs::dom_security_https_first_pbm()) {
return true;
}
return false;
}
/* static */
void nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout(
mozilla::net::DocumentLoadListener* aDocumentLoadListener) {
// only send http background request to counter timeouts if the
// pref allows us to do that.
if (!mozilla::StaticPrefs::
dom_security_https_only_mode_send_http_background_request()) {
return;
}
nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel();
if (!channel) {
return;
}
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
bool isPrivateWin = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
// if neither HTTPS-Only nor HTTPS-First mode is enabled, then there is
// nothing to do here.
if ((!IsHttpsOnlyModeEnabled(isPrivateWin) &&
!IsHttpsFirstModeEnabled(isPrivateWin)) &&
!(loadInfo->GetWasSchemelessInput() &&
mozilla::StaticPrefs::dom_security_https_first_schemeless())) {
return;
}
// if we are not dealing with a top-level load, then there is nothing to do
// here.
if (loadInfo->GetExternalContentPolicyType() !=
ExtContentPolicy::TYPE_DOCUMENT) {
return;
}
// if the load is exempt, then there is nothing to do here.
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
if (httpsOnlyStatus & nsILoadInfo::nsILoadInfo::HTTPS_ONLY_EXEMPT) {
return;
}
// if it's not an http channel, then there is nothing to do here.
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
if (!httpChannel) {
return;
}
// if it's not a GET method, then there is nothing to do here either.
nsAutoCString method;
mozilla::Unused << httpChannel->GetRequestMethod(method);
if (!method.EqualsLiteral("GET")) {
return;
}
// if it's already an https channel, then there is nothing to do here.
nsCOMPtr<nsIURI> channelURI;
channel->GetURI(getter_AddRefs(channelURI));
if (!channelURI->SchemeIs("http")) {
return;
}
// HTTPS-First only applies to standard ports but HTTPS-Only brute forces
// all http connections to be https and overrules HTTPS-First. In case
// HTTPS-First is enabled, but HTTPS-Only is not enabled, we might return
// early if attempting to send a background request to a non standard port.
if ((IsHttpsFirstModeEnabled(isPrivateWin) ||
(loadInfo->GetWasSchemelessInput() &&
mozilla::StaticPrefs::dom_security_https_first_schemeless()))) {
int32_t port = 0;
nsresult rv = channelURI->GetPort(&port);
int defaultPortforScheme = NS_GetDefaultPort("http");
if (NS_SUCCEEDED(rv) && port != defaultPortforScheme && port != -1) {
return;
}
}
// Check for general exceptions
if (OnionException(channelURI) || LoopbackOrLocalException(channelURI)) {
return;
}
RefPtr<nsIRunnable> task =
new TestHTTPAnswerRunnable(channelURI, aDocumentLoadListener);
NS_DispatchToMainThread(task.forget());
}
/* static */
bool nsHTTPSOnlyUtils::ShouldUpgradeRequest(nsIURI* aURI,
nsILoadInfo* aLoadInfo) {
// 1. Check if the HTTPS-Only Mode is even enabled, before we do anything else
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
if (!IsHttpsOnlyModeEnabled(isPrivateWin)) {
return false;
}
// 2. Check for general exceptions
if (OnionException(aURI) || LoopbackOrLocalException(aURI)) {
return false;
}
// 3. Check if NoUpgrade-flag is set in LoadInfo
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
AutoTArray<nsString, 1> params = {
NS_ConvertUTF8toUTF16(aURI->GetSpecOrDefault())};
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyNoUpgradeException", params,
nsIScriptError::infoFlag, aLoadInfo,
aURI);
return false;
}
// All subresources of an exempt triggering principal are also exempt
ExtContentPolicyType contentType = aLoadInfo->GetExternalContentPolicyType();
if (contentType != ExtContentPolicy::TYPE_DOCUMENT) {
if (!aLoadInfo->TriggeringPrincipal()->IsSystemPrincipal() &&
TestIfPrincipalIsExempt(aLoadInfo->TriggeringPrincipal())) {
return false;
}
}
// We can not upgrade "Save-As" downloads, since we have no way of detecting
// if the upgrade failed (Bug 1674859). For now we will just allow the
// download, since there will still be a visual warning about the download
// being insecure.
if (contentType == ExtContentPolicyType::TYPE_SAVEAS_DOWNLOAD) {
return false;
}
// We can upgrade the request - let's log it to the console
// Appending an 's' to the scheme for the logging. (http -> https)
nsAutoCString scheme;
aURI->GetScheme(scheme);
scheme.AppendLiteral("s");
NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault());
NS_ConvertUTF8toUTF16 reportScheme(scheme);
bool isSpeculative = aLoadInfo->GetExternalContentPolicyType() ==
ExtContentPolicy::TYPE_SPECULATIVE;
AutoTArray<nsString, 2> params = {reportSpec, reportScheme};
nsHTTPSOnlyUtils::LogLocalizedString(
isSpeculative ? "HTTPSOnlyUpgradeSpeculativeConnection"
: "HTTPSOnlyUpgradeRequest",
params, nsIScriptError::warningFlag, aLoadInfo, aURI);
// If the status was not determined before, we now indicate that the request
// will get upgraded, but no event-listener has been registered yet.
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) {
httpsOnlyStatus ^= nsILoadInfo::HTTPS_ONLY_UNINITIALIZED;
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED;
aLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
}
return true;
}
/* static */
bool nsHTTPSOnlyUtils::ShouldUpgradeWebSocket(nsIURI* aURI,
nsILoadInfo* aLoadInfo) {
// 1. Check if the HTTPS-Only Mode is even enabled, before we do anything else
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
if (!IsHttpsOnlyModeEnabled(isPrivateWin)) {
return false;
}
// 2. Check for general exceptions
if (OnionException(aURI) || LoopbackOrLocalException(aURI)) {
return false;
}
// 3. Check if NoUpgrade-flag is set in LoadInfo
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
// Let's log to the console, that we didn't upgrade this request
AutoTArray<nsString, 1> params = {
NS_ConvertUTF8toUTF16(aURI->GetSpecOrDefault())};
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyNoUpgradeException", params,
nsIScriptError::infoFlag, aLoadInfo,
aURI);
return false;
}
// All subresources of an exempt triggering principal are also exempt.
if (!aLoadInfo->TriggeringPrincipal()->IsSystemPrincipal() &&
TestIfPrincipalIsExempt(aLoadInfo->TriggeringPrincipal())) {
return false;
}
// We can upgrade the request - let's log it to the console
// Appending an 's' to the scheme for the logging. (ws -> wss)
nsAutoCString scheme;
aURI->GetScheme(scheme);
scheme.AppendLiteral("s");
NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault());
NS_ConvertUTF8toUTF16 reportScheme(scheme);
AutoTArray<nsString, 2> params = {reportSpec, reportScheme};
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyUpgradeRequest", params,
nsIScriptError::warningFlag, aLoadInfo,
aURI);
return true;
}
/* static */
bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop(
nsIURI* aOldURI, nsIURI* aNewURI, nsILoadInfo* aLoadInfo,
const mozilla::EnumSet<UpgradeDowngradeEndlessLoopOptions>& aOptions) {
// 1. Check if the HTTPS-Only/HTTPS-First is even enabled, before doing
// anything else
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
bool enforceForHTTPSOnlyMode =
IsHttpsOnlyModeEnabled(isPrivateWin) &&
aOptions.contains(
UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSOnlyMode);
bool enforceForHTTPSFirstMode =
IsHttpsFirstModeEnabled(isPrivateWin) &&
aOptions.contains(
UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSFirstMode);
bool enforceForHTTPSRR =
aOptions.contains(UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSRR);
if (!enforceForHTTPSOnlyMode && !enforceForHTTPSFirstMode &&
!enforceForHTTPSRR) {
return false;
}
// 2. Check if the upgrade downgrade pref even wants us to try to break the
// cycle. In the case that HTTPS RR is presented, we ignore this pref.
if (!mozilla::StaticPrefs::
dom_security_https_only_mode_break_upgrade_downgrade_endless_loop() &&
!enforceForHTTPSRR) {
return false;
}
// 3. If it's not a top-level load, then there is nothing to do here either.
if (aLoadInfo->GetExternalContentPolicyType() !=
ExtContentPolicy::TYPE_DOCUMENT) {
return false;
}
// 4. If the load is exempt, then it's defintely not related to https-only
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
if ((httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) &&
!enforceForHTTPSRR) {
return false;
}
// 5. Check HTTP 3xx redirects. If the Principal that kicked off the
// load/redirect is not https, then it's definitely not a redirect cause by
// https-only. If the scheme of the principal however is https and the
// asciiHost of the URI to be loaded and the asciiHost of the Principal are
// identical, then we are dealing with an upgrade downgrade scenario and we
// have to break the cycle.
if (IsHttpDowngrade(aOldURI, aNewURI)) {
return true;
}
// TODO(Bug 1896691): Don't depend on triggeringPrincipal for JS/Meta
// redirects. Call this function at the correct places instead
// 6. Bug 1725026: Disable JS/Meta loop detection when the load was triggered
// by a user gesture. This information is only when the redirect chain is
// empty. When the redirect chain is not empty, this load is definitely
// triggered by redirection, not a user gesture.
// TODO(1896685): Verify whether check is still necessary.
if (aLoadInfo->RedirectChain().IsEmpty()) {
if (aLoadInfo->GetHasValidUserGestureActivation()) {
return false;
}
}
// 7. Meta redirects and JS based redirects (win.location). We detect them
// during the https upgrade internal redirect.
nsCOMPtr<nsIPrincipal> triggeringPrincipal = aLoadInfo->TriggeringPrincipal();
if (!triggeringPrincipal->SchemeIs("https")) {
return false;
}
// We detect Meta and JS based redirects during the upgrade. Check whether
// we are currently in an upgrade situation here.
if (!IsHttpDowngrade(aNewURI, aOldURI)) {
return false;
}
// If we upgrade to the same URI that the load is origining from we are
// creating a redirect loop.
bool isLoop = false;
nsresult rv = triggeringPrincipal->EqualsURI(aNewURI, &isLoop);
NS_ENSURE_SUCCESS(rv, false);
return isLoop;
}
/* static */
bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI,
nsILoadInfo* aLoadInfo) {
// 1. Check if HTTPS-First Mode is enabled
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
if (!IsHttpsFirstModeEnabled(isPrivateWin) &&
!(aLoadInfo->GetWasSchemelessInput() &&
mozilla::StaticPrefs::dom_security_https_first_schemeless())) {
return false;
}
// 2. HTTPS-First only upgrades top-level loads (and speculative connections)
ExtContentPolicyType contentType = aLoadInfo->GetExternalContentPolicyType();
if (contentType != ExtContentPolicy::TYPE_DOCUMENT &&
contentType != ExtContentPolicy::TYPE_SPECULATIVE) {
return false;
}
// 3. Check for general exceptions
if (OnionException(aURI) || LoopbackOrLocalException(aURI)) {
return false;
}
// 4. Don't upgrade if upgraded previously or exempt from upgrades
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
return false;
}
// 5. HTTPS-First Mode only upgrades default ports - do not upgrade the
// request to https if port is specified and not the default port of 80.
MOZ_ASSERT(aURI->SchemeIs("http"), "how come the request is not 'http'?");
int defaultPortforScheme = NS_GetDefaultPort("http");
// If no port is specified, then the API returns -1 to indicate the default
// port.
int32_t port = 0;
nsresult rv = aURI->GetPort(&port);
NS_ENSURE_SUCCESS(rv, false);
if (port != defaultPortforScheme && port != -1) {
return false;
}
// 6. Do not upgrade form submissions (for now), revisit within
// Bug 1720500: Revisit upgrading form submissions.
if (aLoadInfo->GetIsFormSubmission()) {
return false;
}
// https-first needs to account for breaking upgrade-downgrade endless
// loops at this point for meta and js redirects because this function
// is called before we
// check the redirect limit in HttpBaseChannel. If we encounter
// a same-origin server side downgrade from e.g https://example.com
// to http://example.com then we simply not annotating the loadinfo
// and returning false from within this function. Please note that
// the handling for https-only mode is different from https-first mode,
// because https-only mode results in an exception page in case
// we encounter and endless upgrade downgrade loop.
/*
bool isUpgradeDowngradeEndlessLoop = IsUpgradeDowngradeEndlessLoop(
aURI, aURI, aLoadInfo,
{UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSFirstMode});
if (isUpgradeDowngradeEndlessLoop) {
if (mozilla::StaticPrefs::
dom_security_https_first_add_exception_on_failiure()) {
nsHTTPSOnlyUtils::AddHTTPSFirstExceptionForSession(aURI, aLoadInfo);
}
return false;
}
*/
// We can upgrade the request - let's log to the console and set the status
// so we know that we upgraded the request.
if (aLoadInfo->GetWasSchemelessInput() &&
!IsHttpsFirstModeEnabled(isPrivateWin)) {
nsAutoCString urlCString;
aURI->GetSpec(urlCString);
NS_ConvertUTF8toUTF16 urlString(urlCString);
AutoTArray<nsString, 1> params = {urlString};
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSFirstSchemeless", params,
nsIScriptError::warningFlag, aLoadInfo,
aURI, true);
} else {
nsAutoCString scheme;
aURI->GetScheme(scheme);
scheme.AppendLiteral("s");
NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault());
NS_ConvertUTF8toUTF16 reportScheme(scheme);
bool isSpeculative = contentType == ExtContentPolicy::TYPE_SPECULATIVE;
AutoTArray<nsString, 2> params = {reportSpec, reportScheme};
nsHTTPSOnlyUtils::LogLocalizedString(
isSpeculative ? "HTTPSOnlyUpgradeSpeculativeConnection"
: "HTTPSOnlyUpgradeRequest",
params, nsIScriptError::warningFlag, aLoadInfo, aURI, true);
}
// Set flag so we know that we upgraded the request
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST;
aLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
return true;
}
/* static */
already_AddRefed<nsIURI>
nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest(
mozilla::net::DocumentLoadListener* aDocumentLoadListener,
nsresult aStatus) {
nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel();
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
// Only downgrade if we this request was upgraded using HTTPS-First Mode
if (!(httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) {
return nullptr;
}
// Once loading is in progress we set that flag so that timeout counter
// measures do not kick in.
loadInfo->SetHttpsOnlyStatus(
httpsOnlyStatus | nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS);
nsresult status = aStatus;
// Since 4xx and 5xx errors return NS_OK instead of NS_ERROR_*, we need
// to check each NS_OK for those errors.
// Only downgrade an NS_OK status if it is an 4xx or 5xx error.
if (NS_SUCCEEDED(aStatus)) {
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
// If no httpChannel exists we have nothing to do here.
if (!httpChannel) {
return nullptr;
}
uint32_t responseStatus = 0;
if (NS_FAILED(httpChannel->GetResponseStatus(&responseStatus))) {
return nullptr;
}
// In case we found one 4xx or 5xx error we need to log it later on,
// for that reason we flip the nsresult 'status' from 'NS_OK' to the
// corresponding NS_ERROR_*.
// To do so we convert the response status to an nsresult error
// Every NS_OK that is NOT an 4xx or 5xx error code won't get downgraded.
if (responseStatus >= 400 && responseStatus < 600) {
// HttpProxyResponseToErrorCode() maps 400 and 404 on
// the same error as a 500 status which would lead to no downgrade
// later on. For that reason we explicit filter for 400 and 404 status
// codes to log them correctly and to downgrade them if possible.
switch (responseStatus) {
case 400:
status = NS_ERROR_PROXY_BAD_REQUEST;
break;
case 404:
status = NS_ERROR_PROXY_NOT_FOUND;
break;
default:
status = mozilla::net::HttpProxyResponseToErrorCode(responseStatus);
break;
}
}
if (NS_SUCCEEDED(status)) {
return nullptr;
}
}
// We're only downgrading if it's possible that the error was
// caused by the upgrade.
if (HttpsUpgradeUnrelatedErrorCode(status)) {
return nullptr;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = channel->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, nullptr);
nsAutoCString spec;
nsCOMPtr<nsIURI> newURI;
// Only downgrade if the current scheme is (a) https or (b) view-source:https
if (uri->SchemeIs("https")) {
rv = uri->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, nullptr);
rv = NS_NewURI(getter_AddRefs(newURI), spec);
NS_ENSURE_SUCCESS(rv, nullptr);
rv = NS_MutateURI(newURI).SetScheme("http"_ns).Finalize(
getter_AddRefs(newURI));
NS_ENSURE_SUCCESS(rv, nullptr);
} else if (uri->SchemeIs("view-source")) {
nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri);
if (!nestedURI) {
return nullptr;
}
nsCOMPtr<nsIURI> innerURI;
rv = nestedURI->GetInnerURI(getter_AddRefs(innerURI));
NS_ENSURE_SUCCESS(rv, nullptr);
if (!innerURI || !innerURI->SchemeIs("https")) {
return nullptr;
}
rv = NS_MutateURI(innerURI).SetScheme("http"_ns).Finalize(
getter_AddRefs(innerURI));
NS_ENSURE_SUCCESS(rv, nullptr);
nsAutoCString innerSpec;
rv = innerURI->GetSpec(innerSpec);
NS_ENSURE_SUCCESS(rv, nullptr);
spec.Append("view-source:");
spec.Append(innerSpec);
rv = NS_NewURI(getter_AddRefs(newURI), spec);
NS_ENSURE_SUCCESS(rv, nullptr);
} else {
return nullptr;
}
// Log downgrade to console
NS_ConvertUTF8toUTF16 reportSpec(uri->GetSpecOrDefault());
AutoTArray<nsString, 1> params = {reportSpec};
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyFailedDowngradeAgain", params,
nsIScriptError::warningFlag, loadInfo,
uri, true);
if (mozilla::StaticPrefs::
dom_security_https_first_add_exception_on_failiure()) {
AddHTTPSFirstExceptionForSession(uri, loadInfo);
}
return newURI.forget();
}
void nsHTTPSOnlyUtils::UpdateLoadStateAfterHTTPSFirstDowngrade(
mozilla::net::DocumentLoadListener* aDocumentLoadListener,
nsDocShellLoadState* aLoadState) {
// We have to exempt the load from HTTPS-First to prevent a upgrade-downgrade
// loop
aLoadState->SetIsExemptFromHTTPSFirstMode(true);
// Add downgrade data for later telemetry usage to load state
nsDOMNavigationTiming* timing = aDocumentLoadListener->GetTiming();
if (timing) {
mozilla::TimeStamp navigationStart = timing->GetNavigationStartTimeStamp();
if (navigationStart) {
mozilla::TimeDuration duration =
mozilla::TimeStamp::Now() - navigationStart;
nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel();
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
bool isPrivateWin =
loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
bool isSchemeless =
loadInfo->GetWasSchemelessInput() &&
!nsHTTPSOnlyUtils::IsHttpsFirstModeEnabled(isPrivateWin);
nsresult channelStatus;
channel->GetStatus(&channelStatus);
RefPtr downgradeData = mozilla::MakeRefPtr<HTTPSFirstDowngradeData>();
downgradeData->downgradeTime = duration;
downgradeData->isOnTimer = channelStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL;
downgradeData->isSchemeless = isSchemeless;
aLoadState->SetHttpsFirstDowngradeData(downgradeData);
}
}
}
void nsHTTPSOnlyUtils::SubmitHTTPSFirstTelemetry(
nsCOMPtr<nsILoadInfo> const& aLoadInfo,
RefPtr<HTTPSFirstDowngradeData> const& aHttpsFirstDowngradeData) {
using namespace mozilla::glean::httpsfirst;
if (aHttpsFirstDowngradeData) {
// Successfully downgraded load
auto downgradedMetric = aHttpsFirstDowngradeData->isSchemeless
? downgraded_schemeless
: downgraded;
auto downgradedOnTimerMetric = aHttpsFirstDowngradeData->isSchemeless
? downgraded_on_timer_schemeless
: downgraded_on_timer;
auto downgradeTimeMetric = aHttpsFirstDowngradeData->isSchemeless
? downgrade_time_schemeless
: downgrade_time;
if (aHttpsFirstDowngradeData->isOnTimer) {
downgradedOnTimerMetric.AddToNumerator();
}
downgradedMetric.Add();
downgradeTimeMetric.AccumulateRawDuration(
aHttpsFirstDowngradeData->downgradeTime);
} else if (aLoadInfo->GetHttpsOnlyStatus() &
nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST) {
// Successfully upgraded load
if (aLoadInfo->GetWasSchemelessInput()) {
upgraded_schemeless.Add();
} else {
upgraded.Add();
}
}
}
/* static */
bool nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(nsIChannel* aChannel,
nsresult aError) {
// If there is no failed channel, then there is nothing to do here.
if (!aChannel) {
return false;
}
// If HTTPS-Only Mode is not enabled, then there is nothing to do here.
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
bool isPrivateWin = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
if (!IsHttpsOnlyModeEnabled(isPrivateWin)) {
return false;
}
// If the load is exempt or did not get upgraded,
// then there is nothing to do here.
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT ||
httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) {
return false;
}
// If it's one of those errors, then most likely it's not a HTTPS-Only error
// (This list of errors is largely drawn from nsDocShell::DisplayLoadError())
return !HttpsUpgradeUnrelatedErrorCode(aError);
}
/* static */
bool nsHTTPSOnlyUtils::TestIfPrincipalIsExempt(nsIPrincipal* aPrincipal,
bool aCheckForHTTPSFirst) {
static nsCOMPtr<nsIPermissionManager> sPermMgr;
if (!sPermMgr) {
sPermMgr = mozilla::components::PermissionManager::Service();
mozilla::ClearOnShutdown(&sPermMgr);
}
NS_ENSURE_TRUE(sPermMgr, false);
uint32_t perm;
nsresult rv = sPermMgr->TestExactPermissionFromPrincipal(
aPrincipal, "https-only-load-insecure"_ns, &perm);
NS_ENSURE_SUCCESS(rv, false);
return perm == nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW ||
perm == nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW_SESSION ||
(aCheckForHTTPSFirst &&
(perm == nsIHttpsOnlyModePermission::HTTPSFIRST_LOAD_INSECURE_ALLOW ||
perm == nsIHttpsOnlyModePermission::
HTTPSFIRST_LOAD_INSECURE_ALLOW_SESSION));
}
/* static */
void nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(
nsIChannel* aChannel) {
NS_ENSURE_TRUE_VOID(aChannel);
// If HTTPS-Only or HTTPS-First Mode is not enabled, then there is nothing to
// do here.
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
bool isPrivateWin = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
bool isHttpsOnly = IsHttpsOnlyModeEnabled(isPrivateWin);
bool isHttpsFirst = IsHttpsFirstModeEnabled(isPrivateWin);
bool isSchemelessHttpsFirst =
(loadInfo->GetWasSchemelessInput() &&
mozilla::StaticPrefs::dom_security_https_first_schemeless());
if (!isHttpsOnly && !isHttpsFirst && !isSchemelessHttpsFirst) {
return;
}
// if it's not a top-level load then there is nothing to here.
ExtContentPolicyType type = loadInfo->GetExternalContentPolicyType();
if (type != ExtContentPolicy::TYPE_DOCUMENT) {
return;
}
// it it's not an http channel, then there is nothing to do here.
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
if (!httpChannel) {
return;
}
nsCOMPtr<nsIPrincipal> principal;
nsresult rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
aChannel, getter_AddRefs(principal));
NS_ENSURE_SUCCESS_VOID(rv);
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
bool isPrincipalExempt = TestIfPrincipalIsExempt(
principal, isHttpsFirst || isSchemelessHttpsFirst);
if (isPrincipalExempt) {
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT;
} else {
// We explicitly remove the exemption flag, because this
// function is also consulted after redirects.
httpsOnlyStatus &= ~nsILoadInfo::HTTPS_ONLY_EXEMPT;
}
if (httpsOnlyStatus & nsILoadInfo::HTTPS_FIRST_EXEMPT_NEXT_LOAD &&
isHttpsFirst) {
httpsOnlyStatus &= ~nsILoadInfo::HTTPS_FIRST_EXEMPT_NEXT_LOAD;
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT;
}
loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
}
/* static */
bool nsHTTPSOnlyUtils::IsSafeToAcceptCORSOrMixedContent(
nsILoadInfo* aLoadInfo) {
// Check if the request is exempt from upgrades
if ((aLoadInfo->GetHttpsOnlyStatus() & nsILoadInfo::HTTPS_ONLY_EXEMPT)) {
return false;
}
// Check if HTTPS-Only Mode is enabled for this request
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
return nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(isPrivateWin);
}
/* static */
bool nsHTTPSOnlyUtils::HttpsUpgradeUnrelatedErrorCode(nsresult aError) {
return NS_ERROR_UNKNOWN_PROTOCOL == aError ||
NS_ERROR_FILE_NOT_FOUND == aError ||
NS_ERROR_FILE_ACCESS_DENIED == aError ||
NS_ERROR_UNKNOWN_HOST == aError || NS_ERROR_PHISHING_URI == aError ||
NS_ERROR_MALWARE_URI == aError || NS_ERROR_UNWANTED_URI == aError ||
NS_ERROR_HARMFUL_URI == aError || NS_ERROR_CONTENT_CRASHED == aError ||
NS_ERROR_FRAME_CRASHED == aError || NS_ERROR_SUPERFLUOS_AUTH == aError;
}
/* ------ Logging ------ */
/* static */
void nsHTTPSOnlyUtils::LogLocalizedString(const char* aName,
const nsTArray<nsString>& aParams,
uint32_t aFlags,
nsILoadInfo* aLoadInfo, nsIURI* aURI,
bool aUseHttpsFirst) {
nsAutoString logMsg;
nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES,
aName, aParams, logMsg);
LogMessage(logMsg, aFlags, aLoadInfo, aURI, aUseHttpsFirst);
}
/* static */
void nsHTTPSOnlyUtils::LogMessage(const nsAString& aMessage, uint32_t aFlags,
nsILoadInfo* aLoadInfo, nsIURI* aURI,
bool aUseHttpsFirst) {
// do not log to the console if the loadinfo says we should not!
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE) {
return;
}
// Prepending HTTPS-Only to the outgoing console message
nsString message;
message.Append(aUseHttpsFirst ? u"HTTPS-First Mode: "_ns
: u"HTTPS-Only Mode: "_ns);
message.Append(aMessage);
// Allow for easy distinction in devtools code.
auto category = aUseHttpsFirst ? "HTTPSFirst"_ns : "HTTPSOnly"_ns;
uint64_t windowId = aLoadInfo->GetInnerWindowID();
if (!windowId) {
windowId = aLoadInfo->GetTriggeringWindowId();
}
if (windowId) {
// Send to content console
nsContentUtils::ReportToConsoleByWindowID(message, aFlags, category,
windowId, aURI);
} else {
// Send to browser console
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
nsContentUtils::LogSimpleConsoleError(message, category, isPrivateWin,
true /* from chrome context */,
aFlags);
}
}
/* ------ Exceptions ------ */
/* static */
bool nsHTTPSOnlyUtils::OnionException(nsIURI* aURI) {
// Onion-host exception can get disabled with a pref
if (mozilla::StaticPrefs::dom_security_https_only_mode_upgrade_onion()) {
return false;
}
nsAutoCString host;
aURI->GetHost(host);
return StringEndsWith(host, ".onion"_ns);
}
/* static */
bool nsHTTPSOnlyUtils::LoopbackOrLocalException(nsIURI* aURI) {
nsAutoCString asciiHost;
nsresult rv = aURI->GetAsciiHost(asciiHost);
NS_ENSURE_SUCCESS(rv, false);
// Let's make a quick check if the host matches these loopback strings
// before we do anything else
if (asciiHost.EqualsLiteral("localhost") || asciiHost.EqualsLiteral("::1")) {
return true;
}
mozilla::net::NetAddr addr;
if (NS_FAILED(addr.InitFromString(asciiHost))) {
return false;
}
// Loopback IPs are always exempt
if (addr.IsLoopbackAddr()) {
return true;
}
// Local IP exception can get disabled with a pref
bool upgradeLocal =
mozilla::StaticPrefs::dom_security_https_only_mode_upgrade_local();
return (!upgradeLocal && addr.IsIPAddrLocal());
}
/* static */
bool nsHTTPSOnlyUtils::ShouldUpgradeConnection(nsILoadInfo* aLoadInfo) {
// Check if one of parameters is null then webpage can't be loaded yet
// and no further inspections are needed
if (!aLoadInfo) {
return false;
}
// Check if the HTTPS-Only Mode is even enabled, before we do anything else
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
if (!IsHttpsOnlyModeEnabled(isPrivateWin) &&
!IsHttpsFirstModeEnabled(isPrivateWin)) {
return false;
}
// If the load is exempt, then don't upgrade
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
return false;
}
return true;
}
/* static */
bool nsHTTPSOnlyUtils::IsHttpDowngrade(nsIURI* aFromURI, nsIURI* aToURI) {
MOZ_ASSERT(aFromURI);
MOZ_ASSERT(aToURI);
if (!aFromURI || !aToURI) {
return false;
}
// 2. If the target URI is not http, then it's not a http downgrade
if (!mozilla::net::SchemeIsHTTP(aToURI)) {
return false;
}
// 3. If the origin URI isn't https, then it's not a http downgrade either.
if (!mozilla::net::SchemeIsHTTPS(aFromURI)) {
return false;
}
// 4. Create a new target URI with 'https' instead of 'http' and compare it
// to the origin URI
int32_t port = 0;
nsresult rv = aToURI->GetPort(&port);
NS_ENSURE_SUCCESS(rv, false);
// a port of -1 indicates the default port, hence we upgrade from port 80 to
// port 443
// otherwise we keep the port.
if (port == -1) {
port = NS_GetDefaultPort("https");
}
nsCOMPtr<nsIURI> newHTTPSchemeURI;
rv = NS_MutateURI(aToURI)
.SetScheme("https"_ns)
.SetPort(port)
.Finalize(newHTTPSchemeURI);
NS_ENSURE_SUCCESS(rv, false);
bool uriEquals = false;
if (NS_FAILED(aFromURI->EqualsExceptRef(newHTTPSchemeURI, &uriEquals))) {
return false;
}
return uriEquals;
}
/* static */
nsresult nsHTTPSOnlyUtils::AddHTTPSFirstExceptionForSession(
nsCOMPtr<nsIURI> aURI, nsILoadInfo* const aLoadInfo) {
// We need to reconstruct a principal instead of taking one from the loadinfo,
// as the permission needs a http scheme, while the passed URL or principals
// on the loadinfo may have a https scheme.
nsresult rv =
NS_MutateURI(aURI).SetScheme("http"_ns).Finalize(getter_AddRefs(aURI));
NS_ENSURE_SUCCESS(rv, rv);
mozilla::OriginAttributes oa = aLoadInfo->GetOriginAttributes();
oa.SetFirstPartyDomain(true, aURI);
nsCOMPtr<nsIPermissionManager> permMgr =
mozilla::components::PermissionManager::Service();
NS_ENSURE_TRUE(permMgr, nsresult::NS_ERROR_SERVICE_NOT_AVAILABLE);
nsCOMPtr<nsIPrincipal> principal =
mozilla::BasePrincipal::CreateContentPrincipal(aURI, oa);
nsCString host;
aURI->GetHost(host);
LogLocalizedString("HTTPSFirstAddingSessionException",
{NS_ConvertUTF8toUTF16(host)}, nsIScriptError::warningFlag,
aLoadInfo, aURI, true);
rv = permMgr->AddFromPrincipal(
principal, "https-only-load-insecure"_ns,
nsIHttpsOnlyModePermission::HTTPSFIRST_LOAD_INSECURE_ALLOW_SESSION,
nsIPermissionManager::EXPIRE_SESSION, 0);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
/* static */
uint32_t nsHTTPSOnlyUtils::GetStatusForSubresourceLoad(
uint32_t aHttpsOnlyStatus) {
return aHttpsOnlyStatus & ~nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST;
}
/////////////////////////////////////////////////////////////////////
// Implementation of TestHTTPAnswerRunnable
NS_IMPL_ISUPPORTS_INHERITED(TestHTTPAnswerRunnable, mozilla::Runnable,
nsIStreamListener, nsIInterfaceRequestor,
nsITimerCallback)
TestHTTPAnswerRunnable::TestHTTPAnswerRunnable(
nsIURI* aURI, mozilla::net::DocumentLoadListener* aDocumentLoadListener)
: mozilla::Runnable("TestHTTPAnswerRunnable"),
mURI(aURI),
mDocumentLoadListener(aDocumentLoadListener) {}
/* static */
bool TestHTTPAnswerRunnable::IsBackgroundRequestRedirected(
nsIHttpChannel* aChannel) {
// If there is no background request (aChannel), then there is nothing
// to do here.
if (!aChannel) {
return false;
}
// If the request was not redirected, then there is nothing to do here.
nsCOMPtr<nsILoadInfo> loadinfo = aChannel->LoadInfo();
if (loadinfo->RedirectChain().IsEmpty()) {
return false;
}
// If the final URI is not targeting an https scheme, then we definitely not
// dealing with a 'same-origin' redirect.
nsCOMPtr<nsIURI> finalURI;
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI));
NS_ENSURE_SUCCESS(rv, false);
if (!finalURI->SchemeIs("https")) {
return false;
}
// If the background request was not http, then there is nothing to do here.
nsCOMPtr<nsIPrincipal> firstURIPrincipal;
loadinfo->RedirectChain()[0]->GetPrincipal(getter_AddRefs(firstURIPrincipal));
if (!firstURIPrincipal || !firstURIPrincipal->SchemeIs("http")) {
return false;
}
// By now we have verified that the inital background request was http and
// that the redirected scheme is https. We want to find the following case
// where the background channel redirects to the https version of the
// top-level request.
// --> background channel: http://example.com
// |--> redirects to: https://example.com
// Now we have to check that the hosts are 'same-origin'.
nsAutoCString redirectHost;
nsAutoCString finalHost;
firstURIPrincipal->GetAsciiHost(redirectHost);
finalURI->GetAsciiHost(finalHost);
return finalHost.Equals(redirectHost);
}
NS_IMETHODIMP
TestHTTPAnswerRunnable::OnStartRequest(nsIRequest* aRequest) {
// If the request status is not OK, it means it encountered some
// kind of error in which case we do not want to do anything.
nsresult requestStatus;
aRequest->GetStatus(&requestStatus);
if (requestStatus != NS_OK) {
return NS_OK;
}
// Check if the original top-level channel which https-only is trying
// to upgrade is already in progress or if the channel is an auth channel.
// If it is in progress or Auth is in progress, then all good, if not
// then let's cancel that channel so we can dispaly the exception page.
nsCOMPtr<nsIChannel> docChannel = mDocumentLoadListener->GetChannel();
nsCOMPtr<nsIHttpChannel> httpsOnlyChannel = do_QueryInterface(docChannel);
if (httpsOnlyChannel) {
nsCOMPtr<nsILoadInfo> loadInfo = httpsOnlyChannel->LoadInfo();
uint32_t topLevelLoadInProgress =
loadInfo->GetHttpsOnlyStatus() &
nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS;
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
do_QueryInterface(httpsOnlyChannel);
bool isAuthChannel = false;
mozilla::Unused << httpChannelInternal->GetIsAuthChannel(&isAuthChannel);
// some server configurations need a long time to respond to an https
// connection, but also redirect any http connection to the https version of
// it. If the top-level load has not started yet, but the http background
// request redirects to https, then do not show the error page, but keep
// waiting for the https response of the upgraded top-level request.
if (!topLevelLoadInProgress) {
nsCOMPtr<nsIHttpChannel> backgroundHttpChannel =
do_QueryInterface(aRequest);
topLevelLoadInProgress =
IsBackgroundRequestRedirected(backgroundHttpChannel);
}
if (!topLevelLoadInProgress && !isAuthChannel) {
// Only really cancel the original top-level channel if it's
// status is still NS_OK, otherwise it might have already
// encountered some other error and was cancelled.
nsresult httpsOnlyChannelStatus;
httpsOnlyChannel->GetStatus(&httpsOnlyChannelStatus);
if (httpsOnlyChannelStatus == NS_OK) {
httpsOnlyChannel->Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL);
}
}
}
// Cancel this http request because it has reached the end of it's
// lifetime at this point.
aRequest->Cancel(NS_ERROR_ABORT);
return NS_ERROR_ABORT;
}
NS_IMETHODIMP
TestHTTPAnswerRunnable::OnDataAvailable(nsIRequest* aRequest,
nsIInputStream* aStream,
uint64_t aOffset, uint32_t aCount) {
// TestHTTPAnswerRunnable only cares about ::OnStartRequest which
// will also cancel the request, so we should in fact never even
// get here.
MOZ_ASSERT(false, "how come we get to ::OnDataAvailable");
return NS_OK;
}
NS_IMETHODIMP
TestHTTPAnswerRunnable::OnStopRequest(nsIRequest* aRequest,
nsresult aStatusCode) {
// TestHTTPAnswerRunnable only cares about ::OnStartRequest
return NS_OK;
}
NS_IMETHODIMP
TestHTTPAnswerRunnable::GetInterface(const nsIID& aIID, void** aResult) {
return QueryInterface(aIID, aResult);
}
NS_IMETHODIMP
TestHTTPAnswerRunnable::Run() {
// Wait N milliseconds to give the original https request a heads start
// before firing up this http request in the background. By default the
// timer is set to 3 seconds. If the https request has not received
// any signal from the server during that time, than it's almost
// certain the upgraded request will result in time out.
uint32_t background_timer_ms = mozilla::StaticPrefs::
dom_security_https_only_fire_http_request_background_timer_ms();
return NS_NewTimerWithCallback(getter_AddRefs(mTimer), this,
background_timer_ms, nsITimer::TYPE_ONE_SHOT);
}
NS_IMETHODIMP
TestHTTPAnswerRunnable::Notify(nsITimer* aTimer) {
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
// If the original channel has already started loading at this point
// then there is no need to do the dance.
nsCOMPtr<nsIChannel> origChannel = mDocumentLoadListener->GetChannel();
nsCOMPtr<nsILoadInfo> origLoadInfo = origChannel->LoadInfo();
uint32_t origHttpsOnlyStatus = origLoadInfo->GetHttpsOnlyStatus();
uint32_t topLevelLoadInProgress =
origHttpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS;
uint32_t downloadInProgress =
origHttpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DOWNLOAD_IN_PROGRESS;
if (topLevelLoadInProgress || downloadInProgress) {
return NS_OK;
}
mozilla::OriginAttributes attrs = origLoadInfo->GetOriginAttributes();
RefPtr<nsIPrincipal> nullPrincipal = mozilla::NullPrincipal::Create(attrs);
uint32_t loadFlags =
nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
nsIRequest::INHIBIT_PERSISTENT_CACHING | nsIRequest::LOAD_BYPASS_CACHE |
nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
// No need to connect to the URI including the path because we only care about
// the round trip time if a server responds to an http request.
nsCOMPtr<nsIURI> backgroundChannelURI;
nsAutoCString prePathStr;
nsresult rv = mURI->GetPrePath(prePathStr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = NS_NewURI(getter_AddRefs(backgroundChannelURI), prePathStr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// we are using TYPE_OTHER because TYPE_DOCUMENT might have side effects
nsCOMPtr<nsIChannel> testHTTPChannel;
rv = NS_NewChannel(getter_AddRefs(testHTTPChannel), backgroundChannelURI,
nullPrincipal,
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
nsIContentPolicy::TYPE_OTHER, nullptr, nullptr, nullptr,
nullptr, loadFlags);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// We have exempt that load from HTTPS-Only to avoid getting upgraded
// to https as well. Additonally let's not log that request to the console
// because it might confuse end users.
nsCOMPtr<nsILoadInfo> loadInfo = testHTTPChannel->LoadInfo();
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT |
nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE |
nsILoadInfo::HTTPS_ONLY_BYPASS_ORB;
loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
testHTTPChannel->SetNotificationCallbacks(this);
testHTTPChannel->AsyncOpen(this);
return NS_OK;
}