mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 23:31:56 +00:00
Bug 1726528 - Add a pref to control whether to enable echConfig for http3, r=necko-reviewers,dragana
Differential Revision: https://phabricator.services.mozilla.com/D123125
This commit is contained in:
parent
2b5f1f4393
commit
21d1774ea9
@ -9886,6 +9886,12 @@
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
# Whether to enable echconfig for http3.
|
||||
- name: network.dns.http3_echconfig.enabled
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
# This pref needs to be worked together with network.dns.echconfig.enabled
|
||||
# being true and there is no record without ECHConfig.
|
||||
# When we try all records with ECHConfig in HTTPS RRs and still can't connect,
|
||||
|
@ -83,6 +83,10 @@ struct IPDLParamTraits<mozilla::net::SVCB> {
|
||||
const paramType& aParam) {
|
||||
WriteIPDLParam(aMsg, aActor, aParam.mSvcFieldPriority);
|
||||
WriteIPDLParam(aMsg, aActor, aParam.mSvcDomainName);
|
||||
WriteIPDLParam(aMsg, aActor, aParam.mEchConfig);
|
||||
WriteIPDLParam(aMsg, aActor, aParam.mODoHConfig);
|
||||
WriteIPDLParam(aMsg, aActor, aParam.mHasIPHints);
|
||||
WriteIPDLParam(aMsg, aActor, aParam.mHasEchConfig);
|
||||
WriteIPDLParam(aMsg, aActor, aParam.mSvcFieldValue);
|
||||
}
|
||||
|
||||
@ -94,6 +98,18 @@ struct IPDLParamTraits<mozilla::net::SVCB> {
|
||||
if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSvcDomainName)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mEchConfig)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mODoHConfig)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mHasIPHints)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mHasEchConfig)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSvcFieldValue)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -185,24 +185,6 @@ bool SVCB::NoDefaultAlpn() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<Tuple<nsCString, bool>> SVCB::GetAlpn(bool aNoHttp2,
|
||||
bool aNoHttp3) const {
|
||||
Maybe<Tuple<nsCString, bool>> alpn;
|
||||
for (const auto& value : mSvcFieldValue) {
|
||||
if (value.mValue.is<SvcParamAlpn>()) {
|
||||
nsTArray<nsCString> alpnList;
|
||||
alpnList.AppendElements(value.mValue.as<SvcParamAlpn>().mValue);
|
||||
if (!alpnList.IsEmpty()) {
|
||||
alpn.emplace();
|
||||
alpn = Some(SelectAlpnFromAlpnList(alpnList, aNoHttp2, aNoHttp3));
|
||||
}
|
||||
return alpn;
|
||||
}
|
||||
}
|
||||
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
void SVCB::GetIPHints(CopyableTArray<mozilla::net::NetAddr>& aAddresses) const {
|
||||
if (mSvcFieldPriority == 0) {
|
||||
return;
|
||||
@ -217,6 +199,22 @@ void SVCB::GetIPHints(CopyableTArray<mozilla::net::NetAddr>& aAddresses) const {
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<nsCString> SVCB::GetAllAlpn() const {
|
||||
nsTArray<nsCString> alpnList;
|
||||
for (const auto& value : mSvcFieldValue) {
|
||||
if (value.mValue.is<SvcParamAlpn>()) {
|
||||
alpnList.AppendElements(value.mValue.as<SvcParamAlpn>().mValue);
|
||||
}
|
||||
}
|
||||
return alpnList;
|
||||
}
|
||||
|
||||
SVCBRecord::SVCBRecord(const SVCB& data,
|
||||
Maybe<Tuple<nsCString, SupportedAlpnType>> aAlpn)
|
||||
: mData(data), mAlpn(aAlpn) {
|
||||
mPort = mData.GetPort();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP SVCBRecord::GetPriority(uint16_t* aPriority) {
|
||||
*aPriority = mData.mSvcFieldPriority;
|
||||
return NS_OK;
|
||||
@ -229,7 +227,18 @@ NS_IMETHODIMP SVCBRecord::GetName(nsACString& aName) {
|
||||
|
||||
Maybe<uint16_t> SVCBRecord::GetPort() { return mPort; }
|
||||
|
||||
Maybe<Tuple<nsCString, bool>> SVCBRecord::GetAlpn() { return mAlpn; }
|
||||
Maybe<Tuple<nsCString, SupportedAlpnType>> SVCBRecord::GetAlpn() {
|
||||
return mAlpn;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP SVCBRecord::GetSelectedAlpn(nsACString& aAlpn) {
|
||||
if (!mAlpn) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
aAlpn = Get<0>(*mAlpn);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP SVCBRecord::GetEchConfig(nsACString& aEchConfig) {
|
||||
aEchConfig = mData.mEchConfig;
|
||||
@ -254,95 +263,159 @@ NS_IMETHODIMP SVCBRecord::GetHasIPHintAddress(bool* aHasIPHintAddress) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool CheckAlpnIsUsable(const nsACString& aTargetName,
|
||||
const nsACString& aAlpn, bool aIsHttp3,
|
||||
uint32_t& aExcludedCount) {
|
||||
if (aAlpn.IsEmpty()) {
|
||||
return false;
|
||||
static bool CheckRecordIsUsable(const SVCB& aRecord, nsIDNSService* aDNSService,
|
||||
const nsACString& aHost,
|
||||
uint32_t& aExcludedCount) {
|
||||
if (!aHost.IsEmpty()) {
|
||||
bool excluded = false;
|
||||
if (NS_SUCCEEDED(aDNSService->IsSVCDomainNameFailed(
|
||||
aHost, aRecord.mSvcDomainName, &excluded)) &&
|
||||
excluded) {
|
||||
// Skip if the domain name of this record was failed to connect before.
|
||||
++aExcludedCount;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (aIsHttp3 && gHttpHandler->IsHttp3Excluded(aTargetName)) {
|
||||
aExcludedCount++;
|
||||
Maybe<uint16_t> port = aRecord.GetPort();
|
||||
if (port && *port == 0) {
|
||||
// Found an unsafe port, skip this record.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CheckAlpnIsUsable(SupportedAlpnType aAlpnType, bool aNoHttp2,
|
||||
bool aNoHttp3, bool aCheckHttp3ExcludedList,
|
||||
const nsACString& aTargetName,
|
||||
uint32_t& aExcludedCount) {
|
||||
// Skip if this alpn is not supported.
|
||||
if (aAlpnType == SupportedAlpnType::NOT_SUPPORTED) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip if we don't want to use http2.
|
||||
if (aNoHttp2 && aAlpnType == SupportedAlpnType::HTTP_2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aAlpnType == SupportedAlpnType::HTTP_3) {
|
||||
if (aCheckHttp3ExcludedList && gHttpHandler->IsHttp3Excluded(aTargetName)) {
|
||||
aExcludedCount++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aNoHttp3) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static nsTArray<SVCBWrapper> FlattenRecords(const nsTArray<SVCB>& aRecords) {
|
||||
nsTArray<SVCBWrapper> result;
|
||||
for (const auto& record : aRecords) {
|
||||
nsTArray<nsCString> alpnList = record.GetAllAlpn();
|
||||
if (alpnList.IsEmpty()) {
|
||||
result.AppendElement(SVCBWrapper(record));
|
||||
} else {
|
||||
for (const auto& alpn : alpnList) {
|
||||
SVCBWrapper wrapper(record);
|
||||
wrapper.mAlpn.emplace(MakeTuple(alpn, IsAlpnSupported(alpn)));
|
||||
result.AppendElement(wrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
already_AddRefed<nsISVCBRecord>
|
||||
DNSHTTPSSVCRecordBase::GetServiceModeRecordInternal(
|
||||
bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords,
|
||||
bool& aRecordsAllExcluded, bool aCheckHttp3ExcludedList) {
|
||||
nsCOMPtr<nsISVCBRecord> selectedRecord;
|
||||
RefPtr<SVCBRecord> selectedRecord;
|
||||
RefPtr<SVCBRecord> h3RecordWithEchConfig;
|
||||
uint32_t recordHasNoDefaultAlpnCount = 0;
|
||||
uint32_t recordExcludedCount = 0;
|
||||
aRecordsAllExcluded = false;
|
||||
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
|
||||
bool RRSetHasEchConfig = false;
|
||||
uint32_t h3ExcludedCount = 0;
|
||||
|
||||
for (const SVCB& record : aRecords) {
|
||||
if (record.mSvcFieldPriority == 0) {
|
||||
nsTArray<SVCBWrapper> records = FlattenRecords(aRecords);
|
||||
for (const auto& record : records) {
|
||||
if (record.mRecord.mSvcFieldPriority == 0) {
|
||||
// In ServiceMode, the SvcPriority should never be 0.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (record.NoDefaultAlpn()) {
|
||||
if (record.mRecord.NoDefaultAlpn()) {
|
||||
++recordHasNoDefaultAlpnCount;
|
||||
}
|
||||
|
||||
RRSetHasEchConfig |= record.mHasEchConfig;
|
||||
|
||||
bool excluded = false;
|
||||
if (NS_SUCCEEDED(dns->IsSVCDomainNameFailed(mHost, record.mSvcDomainName,
|
||||
&excluded)) &&
|
||||
excluded) {
|
||||
// Skip if the domain name of this record was failed to connect before.
|
||||
++recordExcludedCount;
|
||||
if (!CheckRecordIsUsable(record.mRecord, dns, mHost, recordExcludedCount)) {
|
||||
// Skip if this record is not usable.
|
||||
continue;
|
||||
}
|
||||
|
||||
Maybe<uint16_t> port = record.GetPort();
|
||||
if (port && *port == 0) {
|
||||
// Found an unsafe port, skip this record.
|
||||
continue;
|
||||
}
|
||||
|
||||
Maybe<Tuple<nsCString, bool>> alpn = record.GetAlpn(aNoHttp2, aNoHttp3);
|
||||
if (alpn) {
|
||||
if (!CheckAlpnIsUsable(record.mSvcDomainName, Get<0>(*alpn),
|
||||
aCheckHttp3ExcludedList && Get<1>(*alpn),
|
||||
h3ExcludedCount)) {
|
||||
if (record.mAlpn) {
|
||||
if (!CheckAlpnIsUsable(Get<1>(*(record.mAlpn)), aNoHttp2, aNoHttp3,
|
||||
aCheckHttp3ExcludedList,
|
||||
record.mRecord.mSvcDomainName, h3ExcludedCount)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Get<1>(*(record.mAlpn)) == SupportedAlpnType::HTTP_3) {
|
||||
// If the selected alpn is h3 and ech for h3 is disabled, we want
|
||||
// to find out if there is another non-h3 record that has
|
||||
// echConfig. If yes, we'll use the non-h3 record with echConfig
|
||||
// to connect. If not, we'll use h3 to connect without echConfig.
|
||||
if (record.mRecord.mHasEchConfig &&
|
||||
(gHttpHandler->EchConfigEnabled() &&
|
||||
!gHttpHandler->EchConfigEnabled(true))) {
|
||||
if (!h3RecordWithEchConfig) {
|
||||
// Save this h3 record for later use.
|
||||
h3RecordWithEchConfig =
|
||||
new SVCBRecord(record.mRecord, record.mAlpn);
|
||||
// Make sure the next record is not h3.
|
||||
aNoHttp3 = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gHttpHandler->EchConfigEnabled() && RRSetHasEchConfig &&
|
||||
!record.mHasEchConfig) {
|
||||
// Don't use this record if this record has no echConfig, but others have.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!selectedRecord) {
|
||||
selectedRecord = new SVCBRecord(record, std::move(port), std::move(alpn));
|
||||
selectedRecord = new SVCBRecord(record.mRecord, record.mAlpn);
|
||||
}
|
||||
}
|
||||
|
||||
// If all records indicate "no-default-alpn", we should not use this RRSet.
|
||||
if (recordHasNoDefaultAlpnCount == aRecords.Length()) {
|
||||
return nullptr;
|
||||
if (!selectedRecord && !h3RecordWithEchConfig) {
|
||||
// If all records indicate "no-default-alpn", we should not use this RRSet.
|
||||
if (recordHasNoDefaultAlpnCount == records.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (recordExcludedCount == records.Length()) {
|
||||
aRecordsAllExcluded = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If all records are in http3 excluded list, try again without checking the
|
||||
// excluded list. This is better than returning nothing.
|
||||
if (h3ExcludedCount == records.Length() && aCheckHttp3ExcludedList) {
|
||||
return GetServiceModeRecordInternal(aNoHttp2, aNoHttp3, aRecords,
|
||||
aRecordsAllExcluded, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (recordExcludedCount == aRecords.Length()) {
|
||||
aRecordsAllExcluded = true;
|
||||
return nullptr;
|
||||
}
|
||||
if (h3RecordWithEchConfig) {
|
||||
if (selectedRecord && selectedRecord->mData.mHasEchConfig) {
|
||||
return selectedRecord.forget();
|
||||
}
|
||||
|
||||
// If all records are in http3 excluded list, try again without checking the
|
||||
// excluded list. This is better than returning nothing.
|
||||
if (h3ExcludedCount == aRecords.Length() && aCheckHttp3ExcludedList) {
|
||||
return GetServiceModeRecordInternal(aNoHttp2, aNoHttp3, aRecords,
|
||||
aRecordsAllExcluded, false);
|
||||
return h3RecordWithEchConfig.forget();
|
||||
}
|
||||
|
||||
return selectedRecord.forget();
|
||||
@ -364,8 +437,9 @@ void DNSHTTPSSVCRecordBase::GetAllRecordsWithEchConfigInternal(
|
||||
}
|
||||
|
||||
uint32_t h3ExcludedCount = 0;
|
||||
for (const SVCB& record : aRecords) {
|
||||
if (record.mSvcFieldPriority == 0) {
|
||||
nsTArray<SVCBWrapper> records = FlattenRecords(aRecords);
|
||||
for (const auto& record : records) {
|
||||
if (record.mRecord.mSvcFieldPriority == 0) {
|
||||
// This should not happen, since GetAllRecordsWithEchConfigInternal()
|
||||
// should be called only if GetServiceModeRecordInternal() returns a
|
||||
// non-null record.
|
||||
@ -375,35 +449,34 @@ void DNSHTTPSSVCRecordBase::GetAllRecordsWithEchConfigInternal(
|
||||
|
||||
// Records with echConfig are in front of records without echConfig, so we
|
||||
// don't have to continue.
|
||||
*aAllRecordsHaveEchConfig &= record.mHasEchConfig;
|
||||
*aAllRecordsHaveEchConfig &= record.mRecord.mHasEchConfig;
|
||||
if (!(*aAllRecordsHaveEchConfig)) {
|
||||
aResult.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<uint16_t> port = record.GetPort();
|
||||
Maybe<uint16_t> port = record.mRecord.GetPort();
|
||||
if (port && *port == 0) {
|
||||
// Found an unsafe port, skip this record.
|
||||
continue;
|
||||
}
|
||||
|
||||
Maybe<Tuple<nsCString, bool>> alpn = record.GetAlpn(aNoHttp2, aNoHttp3);
|
||||
if (alpn) {
|
||||
if (!CheckAlpnIsUsable(record.mSvcDomainName, Get<0>(*alpn),
|
||||
aCheckHttp3ExcludedList && Get<1>(*alpn),
|
||||
h3ExcludedCount)) {
|
||||
if (record.mAlpn) {
|
||||
if (!CheckAlpnIsUsable(Get<1>(*(record.mAlpn)), aNoHttp2, aNoHttp3,
|
||||
aCheckHttp3ExcludedList,
|
||||
record.mRecord.mSvcDomainName, h3ExcludedCount)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<nsISVCBRecord> svcbRecord =
|
||||
new SVCBRecord(record, std::move(port), std::move(alpn));
|
||||
new SVCBRecord(record.mRecord, record.mAlpn);
|
||||
aResult.AppendElement(svcbRecord);
|
||||
}
|
||||
|
||||
// If all records are in http3 excluded list, try again without checking the
|
||||
// excluded list. This is better than returning nothing.
|
||||
if (h3ExcludedCount == aRecords.Length() && aCheckHttp3ExcludedList) {
|
||||
if (h3ExcludedCount == records.Length() && aCheckHttp3ExcludedList) {
|
||||
GetAllRecordsWithEchConfigInternal(
|
||||
aNoHttp2, aNoHttp3, aRecords, aAllRecordsHaveEchConfig,
|
||||
aAllRecordsInH3ExcludedList, aResult, false);
|
||||
|
@ -9,10 +9,13 @@
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "mozilla/Variant.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsHttp.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class DNSHTTPSSVCRecordBase;
|
||||
|
||||
enum SvcParamKey : uint16_t {
|
||||
SvcParamKeyMandatory = 0,
|
||||
SvcParamKeyAlpn = 1,
|
||||
@ -96,8 +99,8 @@ struct SVCB {
|
||||
bool operator<(const SVCB& aOther) const;
|
||||
Maybe<uint16_t> GetPort() const;
|
||||
bool NoDefaultAlpn() const;
|
||||
Maybe<Tuple<nsCString, bool>> GetAlpn(bool aNoHttp2, bool aNoHttp3) const;
|
||||
void GetIPHints(CopyableTArray<mozilla::net::NetAddr>& aAddresses) const;
|
||||
nsTArray<nsCString> GetAllAlpn() const;
|
||||
uint16_t mSvcFieldPriority = 0;
|
||||
nsCString mSvcDomainName;
|
||||
nsCString mEchConfig;
|
||||
@ -107,21 +110,29 @@ struct SVCB {
|
||||
CopyableTArray<SvcFieldValue> mSvcFieldValue;
|
||||
};
|
||||
|
||||
struct SVCBWrapper {
|
||||
explicit SVCBWrapper(const SVCB& aRecord) : mRecord(aRecord) {}
|
||||
Maybe<Tuple<nsCString, SupportedAlpnType>> mAlpn;
|
||||
const SVCB& mRecord;
|
||||
};
|
||||
|
||||
class SVCBRecord : public nsISVCBRecord {
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSISVCBRECORD
|
||||
public:
|
||||
explicit SVCBRecord(const SVCB& data)
|
||||
: mData(data), mPort(Nothing()), mAlpn(Nothing()) {}
|
||||
explicit SVCBRecord(const SVCB& data, Maybe<uint16_t>&& aPort,
|
||||
Maybe<Tuple<nsCString, bool>>&& aAlpn)
|
||||
: mData(data), mPort(std::move(aPort)), mAlpn(std::move(aAlpn)) {}
|
||||
explicit SVCBRecord(const SVCB& data,
|
||||
Maybe<Tuple<nsCString, SupportedAlpnType>> aAlpn);
|
||||
|
||||
private:
|
||||
friend class DNSHTTPSSVCRecordBase;
|
||||
|
||||
virtual ~SVCBRecord() = default;
|
||||
|
||||
SVCB mData;
|
||||
Maybe<uint16_t> mPort;
|
||||
Maybe<Tuple<nsCString, bool>> mAlpn;
|
||||
Maybe<Tuple<nsCString, SupportedAlpnType>> mAlpn;
|
||||
};
|
||||
|
||||
class DNSHTTPSSVCRecordBase {
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/Tuple.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
#include "nsHttp.h"
|
||||
#include "nsStringFwd.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -29,7 +30,7 @@ namespace net {
|
||||
native TypeResult(mozilla::net::TypeRecordResultType);
|
||||
|
||||
native MaybePort(mozilla::Maybe<uint16_t>);
|
||||
native MaybeAlpnTuple(mozilla::Maybe<mozilla::Tuple<nsCString, bool>>);
|
||||
native MaybeAlpnTuple(mozilla::Maybe<mozilla::Tuple<nsCString, mozilla::net::SupportedAlpnType>>);
|
||||
|
||||
[scriptable, uuid(5d13241b-9d46-448a-90d8-77c418491026)]
|
||||
interface nsIDNSByTypeRecord : nsIDNSRecord
|
||||
@ -98,6 +99,7 @@ interface nsISVCBRecord : nsISupports {
|
||||
readonly attribute ACString name;
|
||||
[noscript, nostdcall, notxpcom] readonly attribute MaybePort port;
|
||||
[noscript, nostdcall, notxpcom] readonly attribute MaybeAlpnTuple alpn;
|
||||
readonly attribute ACString selectedAlpn;
|
||||
readonly attribute ACString echConfig;
|
||||
readonly attribute ACString ODoHConfig;
|
||||
readonly attribute bool hasIPHintAddress;
|
||||
|
@ -1195,6 +1195,7 @@ nsresult DnsAndConnectSocket::TransportSetup::SetupStreams(
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (gHttpHandler->EchConfigEnabled()) {
|
||||
MOZ_ASSERT(!ci->IsHttp3());
|
||||
rv = socketTransport->SetEchConfig(ci->GetEchConfig());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ nsresult Http3Session::Init(const nsHttpConnectionInfo* aConnInfo,
|
||||
ZeroRttTelemetry(ZeroRttOutcome::NOT_USED);
|
||||
}
|
||||
|
||||
if (gHttpHandler->EchConfigEnabled()) {
|
||||
if (gHttpHandler->EchConfigEnabled(true)) {
|
||||
mSocketControl->SetEchConfig(mConnInfo->GetEchConfig());
|
||||
}
|
||||
|
||||
|
@ -1001,43 +1001,23 @@ nsresult HttpProxyResponseToErrorCode(uint32_t aStatusCode) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Tuple<nsCString, bool> SelectAlpnFromAlpnList(
|
||||
const nsTArray<nsCString>& aAlpnList, bool aNoHttp2, bool aNoHttp3) {
|
||||
nsCString h3Value;
|
||||
nsCString h2Value;
|
||||
nsCString h1Value;
|
||||
for (const auto& npnToken : aAlpnList) {
|
||||
bool isHttp3 = gHttpHandler->IsHttp3VersionSupported(npnToken);
|
||||
if (isHttp3 && h3Value.IsEmpty()) {
|
||||
h3Value.Assign(npnToken);
|
||||
}
|
||||
|
||||
uint32_t spdyIndex;
|
||||
SpdyInformation* spdyInfo = gHttpHandler->SpdyInfo();
|
||||
if (NS_SUCCEEDED(spdyInfo->GetNPNIndex(npnToken, &spdyIndex)) &&
|
||||
spdyInfo->ProtocolEnabled(spdyIndex) && h2Value.IsEmpty()) {
|
||||
h2Value.Assign(npnToken);
|
||||
}
|
||||
|
||||
if (npnToken.LowerCaseEqualsASCII("http/1.1") && h1Value.IsEmpty()) {
|
||||
h1Value.Assign(npnToken);
|
||||
}
|
||||
SupportedAlpnType IsAlpnSupported(const nsACString& aAlpn) {
|
||||
if (gHttpHandler->IsHttp3VersionSupported(aAlpn)) {
|
||||
return SupportedAlpnType::HTTP_3;
|
||||
}
|
||||
|
||||
if (!h3Value.IsEmpty() && gHttpHandler->IsHttp3Enabled() && !aNoHttp3) {
|
||||
return MakeTuple(h3Value, true);
|
||||
uint32_t spdyIndex;
|
||||
SpdyInformation* spdyInfo = gHttpHandler->SpdyInfo();
|
||||
if (NS_SUCCEEDED(spdyInfo->GetNPNIndex(aAlpn, &spdyIndex)) &&
|
||||
spdyInfo->ProtocolEnabled(spdyIndex)) {
|
||||
return SupportedAlpnType::HTTP_2;
|
||||
}
|
||||
|
||||
if (!h2Value.IsEmpty() && gHttpHandler->IsSpdyEnabled() && !aNoHttp2) {
|
||||
return MakeTuple(h2Value, false);
|
||||
if (aAlpn.LowerCaseEqualsASCII("http/1.1")) {
|
||||
return SupportedAlpnType::HTTP_1_1;
|
||||
}
|
||||
|
||||
if (!h1Value.IsEmpty()) {
|
||||
return MakeTuple(h1Value, false);
|
||||
}
|
||||
|
||||
// If we are here, there is no supported alpn can be used.
|
||||
return MakeTuple(EmptyCString(), false);
|
||||
return SupportedAlpnType::NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
|
@ -37,6 +37,13 @@ enum class HttpVersion {
|
||||
|
||||
enum class SpdyVersion { NONE = 0, HTTP_2 = 5 };
|
||||
|
||||
enum class SupportedAlpnType : uint8_t {
|
||||
HTTP_3 = 0,
|
||||
HTTP_2,
|
||||
HTTP_1_1,
|
||||
NOT_SUPPORTED
|
||||
};
|
||||
|
||||
extern const uint32_t kHttp3VersionCount;
|
||||
extern const nsCString kHttp3Versions[];
|
||||
|
||||
@ -383,13 +390,8 @@ void LogHeaders(const char* lineStart);
|
||||
// CONNECT method.
|
||||
nsresult HttpProxyResponseToErrorCode(uint32_t aStatusCode);
|
||||
|
||||
// Given a list of alpn-id, this function returns a supported alpn-id. If both
|
||||
// h3 and h2 are enabled, h3 alpn is preferred. This function returns a
|
||||
// Tuple<alpn-id, isHttp3>. The first element is the alpn-id and the second one
|
||||
// is a boolean to indicate if this alpn-id is for http3. If no supported
|
||||
// alpn-id is found, the first element would be a n empty string.
|
||||
Tuple<nsCString, bool> SelectAlpnFromAlpnList(
|
||||
const nsTArray<nsCString>& aAlpnList, bool aNoHttp2, bool aNoHttp3);
|
||||
// Convert an alpn string to SupportedAlpnType.
|
||||
SupportedAlpnType IsAlpnSupported(const nsACString& aAlpn);
|
||||
|
||||
static inline bool AllowedErrorForHTTPSRRFallback(nsresult aError) {
|
||||
return psm::IsNSSErrorCode(-1 * NS_ERROR_GET_CODE(aError)) ||
|
||||
|
@ -6977,8 +6977,8 @@ static void ReportHTTPSRRTelemetry(
|
||||
getter_AddRefs(svcbRecord)))) {
|
||||
MOZ_ASSERT(svcbRecord);
|
||||
|
||||
Maybe<Tuple<nsCString, bool>> alpn = svcbRecord->GetAlpn();
|
||||
bool isHttp3 = alpn ? Get<1>(*alpn) : false;
|
||||
Maybe<Tuple<nsCString, SupportedAlpnType>> alpn = svcbRecord->GetAlpn();
|
||||
bool isHttp3 = alpn ? Get<1>(*alpn) == SupportedAlpnType::HTTP_3 : false;
|
||||
Telemetry::Accumulate(Telemetry::HTTPS_RR_WITH_HTTP3_PRESENTED, isHttp3);
|
||||
}
|
||||
}
|
||||
|
@ -334,10 +334,10 @@ nsHttpConnectionInfo::CloneAndAdoptHTTPSSVCRecord(
|
||||
// Try to get the port and Alpn. If this record has SvcParamKeyPort defined,
|
||||
// the new port will be used as mRoutedPort.
|
||||
Maybe<uint16_t> port = aRecord->GetPort();
|
||||
Maybe<Tuple<nsCString, bool>> alpn = aRecord->GetAlpn();
|
||||
Maybe<Tuple<nsCString, SupportedAlpnType>> alpn = aRecord->GetAlpn();
|
||||
|
||||
// Let the new conn info learn h3 will be used.
|
||||
bool isHttp3 = alpn ? Get<1>(*alpn) : false;
|
||||
bool isHttp3 = alpn ? Get<1>(*alpn) == SupportedAlpnType::HTTP_3 : false;
|
||||
|
||||
LOG(("HTTPSSVC: use new routed host (%s) and new npnToken (%s)", name.get(),
|
||||
alpn ? Get<0>(*alpn).get() : "None"));
|
||||
|
@ -1647,7 +1647,7 @@ nsresult nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction* trans) {
|
||||
trans->Caps() & NS_HTTP_DISALLOW_HTTP3);
|
||||
MOZ_ASSERT(ent);
|
||||
|
||||
if (gHttpHandler->EchConfigEnabled()) {
|
||||
if (gHttpHandler->EchConfigEnabled(ci->IsHttp3())) {
|
||||
ent->MaybeUpdateEchConfig(ci);
|
||||
}
|
||||
|
||||
|
@ -2892,8 +2892,13 @@ bool nsHttpHandler::UseHTTPSRRAsAltSvcEnabled() const {
|
||||
return StaticPrefs::network_dns_use_https_rr_as_altsvc();
|
||||
}
|
||||
|
||||
bool nsHttpHandler::EchConfigEnabled() const {
|
||||
return StaticPrefs::network_dns_echconfig_enabled();
|
||||
bool nsHttpHandler::EchConfigEnabled(bool aIsHttp3) const {
|
||||
if (!aIsHttp3) {
|
||||
return StaticPrefs::network_dns_echconfig_enabled();
|
||||
}
|
||||
|
||||
return StaticPrefs::network_dns_echconfig_enabled() &&
|
||||
StaticPrefs::network_dns_http3_echconfig_enabled();
|
||||
}
|
||||
|
||||
bool nsHttpHandler::FallbackToOriginIfConfigsAreECHAndAllFailed() const {
|
||||
|
@ -489,7 +489,7 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
|
||||
|
||||
bool UseHTTPSRRAsAltSvcEnabled() const;
|
||||
|
||||
bool EchConfigEnabled() const;
|
||||
bool EchConfigEnabled(bool aIsHttp3 = false) const;
|
||||
// When EchConfig is enabled and all records with echConfig are failed, this
|
||||
// functon indicate whether we can fallback to the origin server.
|
||||
// In the case an HTTPS RRSet contains some RRs with echConfig and some
|
||||
|
@ -1172,8 +1172,9 @@ void nsHttpTransaction::PrepareConnInfoForRetry(nsresult aReason) {
|
||||
this, static_cast<uint32_t>(aReason)));
|
||||
RefPtr<nsHttpConnectionInfo> failedConnInfo = mConnInfo->Clone();
|
||||
mConnInfo = nullptr;
|
||||
bool echConfigUsed = gHttpHandler->EchConfigEnabled() &&
|
||||
!failedConnInfo->GetEchConfig().IsEmpty();
|
||||
bool echConfigUsed =
|
||||
gHttpHandler->EchConfigEnabled(failedConnInfo->IsHttp3()) &&
|
||||
!failedConnInfo->GetEchConfig().IsEmpty();
|
||||
|
||||
if (mFastFallbackTriggered) {
|
||||
mFastFallbackTriggered = false;
|
||||
@ -3264,8 +3265,8 @@ void nsHttpTransaction::OnFastFallbackTimer() {
|
||||
return;
|
||||
}
|
||||
|
||||
bool echConfigUsed =
|
||||
gHttpHandler->EchConfigEnabled() && !mConnInfo->GetEchConfig().IsEmpty();
|
||||
bool echConfigUsed = gHttpHandler->EchConfigEnabled(mConnInfo->IsHttp3()) &&
|
||||
!mConnInfo->GetEchConfig().IsEmpty();
|
||||
mBackupConnInfo = PrepareFastFallbackConnInfo(echConfigUsed);
|
||||
if (!mBackupConnInfo) {
|
||||
return;
|
||||
|
@ -1,61 +0,0 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsIHttpProtocolHandler.h"
|
||||
#include "nsHttp.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
TEST(TestSupportAlpn, testSvcParamKeyAlpn)
|
||||
{
|
||||
Preferences::SetBool("network.http.http3.enabled", true);
|
||||
// initialize gHttphandler.
|
||||
nsCOMPtr<nsIHttpProtocolHandler> http =
|
||||
do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http");
|
||||
|
||||
// h2 and h3 are enabled, so first appearance h3 alpn-id is returned.
|
||||
nsTArray<nsCString> alpn = {"h3-29"_ns, "h3-30"_ns, "h2"_ns, "http/1.1"_ns};
|
||||
Tuple<nsCString, bool> result = SelectAlpnFromAlpnList(alpn, false, false);
|
||||
ASSERT_EQ(Get<0>(result), "h3-29"_ns);
|
||||
ASSERT_EQ(Get<1>(result), true);
|
||||
|
||||
// We don't support h3-26, so we should choose h2.
|
||||
alpn = {"h3-26"_ns, "h2"_ns, "http/1.1"_ns};
|
||||
result = SelectAlpnFromAlpnList(alpn, false, false);
|
||||
ASSERT_EQ(Get<0>(result), "h2"_ns);
|
||||
ASSERT_EQ(Get<1>(result), false);
|
||||
|
||||
// h3 is disabled, so we will use h2.
|
||||
alpn = {"h3-29"_ns, "h3-30"_ns, "h2"_ns, "http/1.1"_ns};
|
||||
result = SelectAlpnFromAlpnList(alpn, false, true);
|
||||
ASSERT_EQ(Get<0>(result), "h2"_ns);
|
||||
ASSERT_EQ(Get<1>(result), false);
|
||||
|
||||
// h3 is disabled and h2 is not found, so we will select http/1.1.
|
||||
alpn = {"h3-29"_ns, "h3-30"_ns, "http/1.1"_ns};
|
||||
result = SelectAlpnFromAlpnList(alpn, false, true);
|
||||
ASSERT_EQ(Get<0>(result), "http/1.1"_ns);
|
||||
ASSERT_EQ(Get<1>(result), false);
|
||||
|
||||
// h3 and h2 are disabled, so we will select http/1.1.
|
||||
alpn = {"h3-29"_ns, "h3-30"_ns, "h2"_ns, "http/1.1"_ns};
|
||||
result = SelectAlpnFromAlpnList(alpn, true, true);
|
||||
ASSERT_EQ(Get<0>(result), "http/1.1"_ns);
|
||||
ASSERT_EQ(Get<1>(result), false);
|
||||
|
||||
// h3 and h2 are disabled and http1.1 is not found, we return an empty string.
|
||||
alpn = {"h3-29"_ns, "h3-30"_ns, "h2"_ns};
|
||||
result = SelectAlpnFromAlpnList(alpn, true, true);
|
||||
ASSERT_EQ(Get<0>(result), ""_ns);
|
||||
ASSERT_EQ(Get<1>(result), false);
|
||||
|
||||
// No supported alpn.
|
||||
alpn = {"ftp"_ns, "h2c"_ns};
|
||||
result = SelectAlpnFromAlpnList(alpn, true, true);
|
||||
ASSERT_EQ(Get<0>(result), ""_ns);
|
||||
ASSERT_EQ(Get<1>(result), false);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
@ -23,7 +23,6 @@ UNIFIED_SOURCES += [
|
||||
"TestServerTimingHeader.cpp",
|
||||
"TestSocketTransportService.cpp",
|
||||
"TestStandardURL.cpp",
|
||||
"TestSupportAlpn.cpp",
|
||||
"TestUDPSocket.cpp",
|
||||
]
|
||||
|
||||
|
@ -51,6 +51,7 @@ registerCleanupFunction(async () => {
|
||||
Services.prefs.clearUserPref("network.dns.upgrade_with_https_rr");
|
||||
Services.prefs.clearUserPref("network.dns.use_https_rr_as_altsvc");
|
||||
Services.prefs.clearUserPref("network.dns.echconfig.enabled");
|
||||
Services.prefs.clearUserPref("network.dns.http3_echconfig.enabled");
|
||||
Services.prefs.clearUserPref("network.dns.echconfig.fallback_to_origin");
|
||||
Services.prefs.clearUserPref("network.dns.httpssvc.reset_exclustion_list");
|
||||
Services.prefs.clearUserPref("network.http.http3.enabled");
|
||||
@ -367,6 +368,7 @@ add_task(async function testFastfallbackWithEchConfig() {
|
||||
Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
|
||||
Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
|
||||
Services.prefs.setBoolPref("network.dns.echconfig.enabled", true);
|
||||
Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", true);
|
||||
|
||||
Services.prefs.setIntPref("network.trr.mode", 3);
|
||||
Services.prefs.setCharPref(
|
||||
@ -832,3 +834,64 @@ add_task(async function testH3FastFallbackWithMultipleTransactions() {
|
||||
|
||||
await trrServer.stop();
|
||||
});
|
||||
|
||||
add_task(async function testFastfallbackToTheSameRecord() {
|
||||
trrServer = new TRRServer();
|
||||
await trrServer.start();
|
||||
Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
|
||||
Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
|
||||
Services.prefs.setBoolPref("network.dns.echconfig.enabled", true);
|
||||
Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", true);
|
||||
|
||||
Services.prefs.setIntPref("network.trr.mode", 3);
|
||||
Services.prefs.setCharPref(
|
||||
"network.trr.uri",
|
||||
`https://foo.example.com:${trrServer.port}/dns-query`
|
||||
);
|
||||
Services.prefs.setBoolPref("network.http.http3.enabled", true);
|
||||
|
||||
Services.prefs.setIntPref(
|
||||
"network.dns.httpssvc.http3_fast_fallback_timeout",
|
||||
1000
|
||||
);
|
||||
|
||||
await trrServer.registerDoHAnswers("test.ech.org", "HTTPS", {
|
||||
answers: [
|
||||
{
|
||||
name: "test.ech.org",
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 1,
|
||||
name: "test.ech1.org",
|
||||
values: [
|
||||
{ key: "alpn", value: ["h3-29", "h2"] },
|
||||
{ key: "port", value: h2Port },
|
||||
{ key: "echconfig", value: "456..." },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await trrServer.registerDoHAnswers("test.ech1.org", "A", {
|
||||
answers: [
|
||||
{
|
||||
name: "test.ech1.org",
|
||||
ttl: 55,
|
||||
type: "A",
|
||||
flush: false,
|
||||
data: "127.0.0.1",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let chan = makeChan(`https://test.ech.org/server-timing`);
|
||||
let [req] = await channelOpenPromise(chan);
|
||||
Assert.equal(req.protocolVersion, "h2");
|
||||
let internal = req.QueryInterface(Ci.nsIHttpChannelInternal);
|
||||
Assert.equal(internal.remotePort, h2Port);
|
||||
|
||||
await trrServer.stop();
|
||||
});
|
||||
|
452
netwerk/test/unit/test_https_rr_ech_prefs.js
Normal file
452
netwerk/test/unit/test_https_rr_ech_prefs.js
Normal file
@ -0,0 +1,452 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
|
||||
var { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
let trrServer;
|
||||
|
||||
const dns = Cc["@mozilla.org/network/dns-service;1"].getService(
|
||||
Ci.nsIDNSService
|
||||
);
|
||||
|
||||
function setup() {
|
||||
trr_test_setup();
|
||||
|
||||
Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
|
||||
Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
|
||||
Services.prefs.setBoolPref("network.dns.echconfig.enabled", true);
|
||||
}
|
||||
|
||||
setup();
|
||||
registerCleanupFunction(async () => {
|
||||
trr_clear_prefs();
|
||||
Services.prefs.clearUserPref("network.dns.upgrade_with_https_rr");
|
||||
Services.prefs.clearUserPref("network.dns.use_https_rr_as_altsvc");
|
||||
Services.prefs.clearUserPref("network.dns.echconfig.enabled");
|
||||
Services.prefs.clearUserPref("network.dns.http3_echconfig.enabled");
|
||||
if (trrServer) {
|
||||
await trrServer.stop();
|
||||
}
|
||||
});
|
||||
|
||||
function checkResult(inRecord, noHttp2, noHttp3, result) {
|
||||
if (!result) {
|
||||
Assert.throws(
|
||||
() => {
|
||||
inRecord
|
||||
.QueryInterface(Ci.nsIDNSHTTPSSVCRecord)
|
||||
.GetServiceModeRecord(noHttp2, noHttp3);
|
||||
},
|
||||
/NS_ERROR_NOT_AVAILABLE/,
|
||||
"Should get an error"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let record = inRecord
|
||||
.QueryInterface(Ci.nsIDNSHTTPSSVCRecord)
|
||||
.GetServiceModeRecord(noHttp2, noHttp3);
|
||||
Assert.equal(record.priority, result.expectedPriority);
|
||||
Assert.equal(record.name, result.expectedName);
|
||||
Assert.equal(record.selectedAlpn, result.expectedAlpn);
|
||||
}
|
||||
|
||||
// Test configuration:
|
||||
// There are two records: one has a echConfig and the other doesn't.
|
||||
// We want to test if the record with echConfig is preferred.
|
||||
add_task(async function testEchConfigEnabled() {
|
||||
let trrServer = new TRRServer();
|
||||
await trrServer.start();
|
||||
|
||||
Services.prefs.setIntPref("network.trr.mode", 3);
|
||||
Services.prefs.setCharPref(
|
||||
"network.trr.uri",
|
||||
`https://foo.example.com:${trrServer.port}/dns-query`
|
||||
);
|
||||
Services.prefs.setBoolPref("network.dns.echconfig.enabled", false);
|
||||
|
||||
await trrServer.registerDoHAnswers("test.bar.com", "HTTPS", {
|
||||
answers: [
|
||||
{
|
||||
name: "test.bar.com",
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 1,
|
||||
name: "test.bar_1.com",
|
||||
values: [{ key: "alpn", value: ["h3-29"] }],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "test.bar.com",
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 2,
|
||||
name: "test.bar_2.com",
|
||||
values: [
|
||||
{ key: "alpn", value: ["h2"] },
|
||||
{ key: "echconfig", value: "456..." },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let [, inRecord] = await new TRRDNSListener("test.bar.com", {
|
||||
type: dns.RESOLVE_TYPE_HTTPSSVC,
|
||||
});
|
||||
|
||||
checkResult(inRecord, false, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.bar_1.com",
|
||||
expectedAlpn: "h3-29",
|
||||
});
|
||||
checkResult(inRecord, false, true, {
|
||||
expectedPriority: 2,
|
||||
expectedName: "test.bar_2.com",
|
||||
expectedAlpn: "h2",
|
||||
});
|
||||
checkResult(inRecord, true, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.bar_1.com",
|
||||
expectedAlpn: "h3-29",
|
||||
});
|
||||
checkResult(inRecord, true, true);
|
||||
|
||||
Services.prefs.setBoolPref("network.dns.echconfig.enabled", true);
|
||||
dns.clearCache(true);
|
||||
|
||||
[, inRecord] = await new TRRDNSListener("test.bar.com", {
|
||||
type: dns.RESOLVE_TYPE_HTTPSSVC,
|
||||
});
|
||||
|
||||
checkResult(inRecord, false, false, {
|
||||
expectedPriority: 2,
|
||||
expectedName: "test.bar_2.com",
|
||||
expectedAlpn: "h2",
|
||||
});
|
||||
checkResult(inRecord, false, true, {
|
||||
expectedPriority: 2,
|
||||
expectedName: "test.bar_2.com",
|
||||
expectedAlpn: "h2",
|
||||
});
|
||||
checkResult(inRecord, true, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.bar_1.com",
|
||||
expectedAlpn: "h3-29",
|
||||
});
|
||||
checkResult(inRecord, true, true);
|
||||
|
||||
await trrServer.stop();
|
||||
trrServer = null;
|
||||
});
|
||||
|
||||
// Test configuration:
|
||||
// There are two records: both have echConfigs, and only one supports h3.
|
||||
// This test is about testing which record should we get when
|
||||
// network.dns.http3_echconfig.enabled is true and false.
|
||||
// When network.dns.http3_echconfig.enabled is false, we should try to
|
||||
// connect with h2 and echConfig.
|
||||
add_task(async function testTwoRecordsHaveEchConfig() {
|
||||
dns.clearCache(true);
|
||||
|
||||
let trrServer = new TRRServer();
|
||||
await trrServer.start();
|
||||
|
||||
Services.prefs.setBoolPref("network.dns.echconfig.enabled", true);
|
||||
Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", false);
|
||||
Services.prefs.setIntPref("network.trr.mode", 3);
|
||||
Services.prefs.setCharPref(
|
||||
"network.trr.uri",
|
||||
`https://foo.example.com:${trrServer.port}/dns-query`
|
||||
);
|
||||
|
||||
await trrServer.registerDoHAnswers("test.foo.com", "HTTPS", {
|
||||
answers: [
|
||||
{
|
||||
name: "test.foo.com",
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 1,
|
||||
name: "test.foo_h3.com",
|
||||
values: [
|
||||
{ key: "alpn", value: ["h3"] },
|
||||
{ key: "echconfig", value: "456..." },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "test.foo.com",
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 2,
|
||||
name: "test.foo_h2.com",
|
||||
values: [
|
||||
{ key: "alpn", value: ["h2"] },
|
||||
{ key: "echconfig", value: "456..." },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let [, inRecord] = await new TRRDNSListener("test.foo.com", {
|
||||
type: dns.RESOLVE_TYPE_HTTPSSVC,
|
||||
});
|
||||
|
||||
checkResult(inRecord, false, false, {
|
||||
expectedPriority: 2,
|
||||
expectedName: "test.foo_h2.com",
|
||||
expectedAlpn: "h2",
|
||||
});
|
||||
checkResult(inRecord, false, true, {
|
||||
expectedPriority: 2,
|
||||
expectedName: "test.foo_h2.com",
|
||||
expectedAlpn: "h2",
|
||||
});
|
||||
checkResult(inRecord, true, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h3",
|
||||
});
|
||||
checkResult(inRecord, true, true);
|
||||
|
||||
Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", true);
|
||||
dns.clearCache(true);
|
||||
[, inRecord] = await new TRRDNSListener("test.foo.com", {
|
||||
type: dns.RESOLVE_TYPE_HTTPSSVC,
|
||||
});
|
||||
|
||||
checkResult(inRecord, false, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h3",
|
||||
});
|
||||
checkResult(inRecord, false, true, {
|
||||
expectedPriority: 2,
|
||||
expectedName: "test.foo_h2.com",
|
||||
expectedAlpn: "h2",
|
||||
});
|
||||
checkResult(inRecord, true, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h3",
|
||||
});
|
||||
checkResult(inRecord, true, true);
|
||||
|
||||
await trrServer.stop();
|
||||
trrServer = null;
|
||||
});
|
||||
|
||||
// Test configuration:
|
||||
// There are two records: both have echConfigs, and one supports h3 and h2.
|
||||
// When network.dns.http3_echconfig.enabled is false, we should use the record
|
||||
// that supports h3 and h2 (the alpn is h2).
|
||||
add_task(async function testTwoRecordsHaveEchConfig1() {
|
||||
dns.clearCache(true);
|
||||
|
||||
let trrServer = new TRRServer();
|
||||
await trrServer.start();
|
||||
|
||||
Services.prefs.setBoolPref("network.dns.echconfig.enabled", true);
|
||||
Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", false);
|
||||
Services.prefs.setIntPref("network.trr.mode", 3);
|
||||
Services.prefs.setCharPref(
|
||||
"network.trr.uri",
|
||||
`https://foo.example.com:${trrServer.port}/dns-query`
|
||||
);
|
||||
|
||||
await trrServer.registerDoHAnswers("test.foo.com", "HTTPS", {
|
||||
answers: [
|
||||
{
|
||||
name: "test.foo.com",
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 1,
|
||||
name: "test.foo_h3.com",
|
||||
values: [
|
||||
{ key: "alpn", value: ["h3", "h2"] },
|
||||
{ key: "echconfig", value: "456..." },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "test.foo.com",
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 2,
|
||||
name: "test.foo_h2.com",
|
||||
values: [
|
||||
{ key: "alpn", value: ["h2", "http/1.1"] },
|
||||
{ key: "echconfig", value: "456..." },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let [, inRecord] = await new TRRDNSListener("test.foo.com", {
|
||||
type: dns.RESOLVE_TYPE_HTTPSSVC,
|
||||
});
|
||||
|
||||
checkResult(inRecord, false, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h2",
|
||||
});
|
||||
checkResult(inRecord, false, true, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h2",
|
||||
});
|
||||
checkResult(inRecord, true, false, {
|
||||
expectedPriority: 2,
|
||||
expectedName: "test.foo_h2.com",
|
||||
expectedAlpn: "http/1.1",
|
||||
});
|
||||
checkResult(inRecord, true, true, {
|
||||
expectedPriority: 2,
|
||||
expectedName: "test.foo_h2.com",
|
||||
expectedAlpn: "http/1.1",
|
||||
});
|
||||
|
||||
Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", true);
|
||||
dns.clearCache(true);
|
||||
[, inRecord] = await new TRRDNSListener("test.foo.com", {
|
||||
type: dns.RESOLVE_TYPE_HTTPSSVC,
|
||||
});
|
||||
|
||||
checkResult(inRecord, false, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h3",
|
||||
});
|
||||
checkResult(inRecord, false, true, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h2",
|
||||
});
|
||||
checkResult(inRecord, true, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h3",
|
||||
});
|
||||
checkResult(inRecord, true, true, {
|
||||
expectedPriority: 2,
|
||||
expectedName: "test.foo_h2.com",
|
||||
expectedAlpn: "http/1.1",
|
||||
});
|
||||
|
||||
await trrServer.stop();
|
||||
trrServer = null;
|
||||
});
|
||||
|
||||
// Test configuration:
|
||||
// There are two records: only one support h3 and only one has echConfig.
|
||||
// This test is about never usng the record without echConfig.
|
||||
add_task(async function testOneRecordsHasEchConfig() {
|
||||
dns.clearCache(true);
|
||||
|
||||
let trrServer = new TRRServer();
|
||||
await trrServer.start();
|
||||
|
||||
Services.prefs.setBoolPref("network.dns.echconfig.enabled", true);
|
||||
Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", false);
|
||||
Services.prefs.setIntPref("network.trr.mode", 3);
|
||||
Services.prefs.setCharPref(
|
||||
"network.trr.uri",
|
||||
`https://foo.example.com:${trrServer.port}/dns-query`
|
||||
);
|
||||
|
||||
await trrServer.registerDoHAnswers("test.foo.com", "HTTPS", {
|
||||
answers: [
|
||||
{
|
||||
name: "test.foo.com",
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 1,
|
||||
name: "test.foo_h3.com",
|
||||
values: [
|
||||
{ key: "alpn", value: ["h3"] },
|
||||
{ key: "echconfig", value: "456..." },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "test.foo.com",
|
||||
ttl: 55,
|
||||
type: "HTTPS",
|
||||
flush: false,
|
||||
data: {
|
||||
priority: 2,
|
||||
name: "test.foo_h2.com",
|
||||
values: [{ key: "alpn", value: ["h2"] }],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let [, inRecord] = await new TRRDNSListener("test.foo.com", {
|
||||
type: dns.RESOLVE_TYPE_HTTPSSVC,
|
||||
});
|
||||
|
||||
checkResult(inRecord, false, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h3",
|
||||
});
|
||||
checkResult(inRecord, false, true, {
|
||||
expectedPriority: 2,
|
||||
expectedName: "test.foo_h2.com",
|
||||
expectedAlpn: "h2",
|
||||
});
|
||||
checkResult(inRecord, true, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h3",
|
||||
});
|
||||
checkResult(inRecord, true, true);
|
||||
|
||||
Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", true);
|
||||
dns.clearCache(true);
|
||||
[, inRecord] = await new TRRDNSListener("test.foo.com", {
|
||||
type: dns.RESOLVE_TYPE_HTTPSSVC,
|
||||
});
|
||||
|
||||
checkResult(inRecord, false, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h3",
|
||||
});
|
||||
checkResult(inRecord, false, true, {
|
||||
expectedPriority: 2,
|
||||
expectedName: "test.foo_h2.com",
|
||||
expectedAlpn: "h2",
|
||||
});
|
||||
checkResult(inRecord, true, false, {
|
||||
expectedPriority: 1,
|
||||
expectedName: "test.foo_h3.com",
|
||||
expectedAlpn: "h3",
|
||||
});
|
||||
checkResult(inRecord, true, true);
|
||||
|
||||
await trrServer.stop();
|
||||
trrServer = null;
|
||||
});
|
@ -25,6 +25,7 @@ function setup() {
|
||||
Services.prefs.setBoolPref("network.dns.upgrade_with_https_rr", true);
|
||||
Services.prefs.setBoolPref("network.dns.use_https_rr_as_altsvc", true);
|
||||
Services.prefs.setBoolPref("network.dns.echconfig.enabled", true);
|
||||
Services.prefs.setBoolPref("network.dns.http3_echconfig.enabled", true);
|
||||
|
||||
add_tls_server_setup(
|
||||
"EncryptedClientHelloServer",
|
||||
@ -49,6 +50,7 @@ registerCleanupFunction(async () => {
|
||||
Services.prefs.clearUserPref("network.dns.upgrade_with_https_rr");
|
||||
Services.prefs.clearUserPref("network.dns.use_https_rr_as_altsvc");
|
||||
Services.prefs.clearUserPref("network.dns.echconfig.enabled");
|
||||
Services.prefs.clearUserPref("network.dns.http3_echconfig.enabled");
|
||||
Services.prefs.clearUserPref("network.dns.echconfig.fallback_to_origin");
|
||||
if (trrServer) {
|
||||
await trrServer.stop();
|
||||
|
@ -540,3 +540,6 @@ skip-if = tsan || os =='android'
|
||||
run-sequentially = node server exceptions dont replay well
|
||||
[test_bug1725766.js]
|
||||
skip-if = os == "android" # skip because of bug 1589327
|
||||
[test_https_rr_ech_prefs.js]
|
||||
skip-if = os == "android"
|
||||
run-sequentially = node server exceptions dont replay well
|
||||
|
Loading…
Reference in New Issue
Block a user