mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 02:14:43 +00:00
Bug 1652655 - P2: Use HTTPSSVC record for the request without an AltSvc or a HSTS entry, r=dragana,necko-reviewers,ckerschb
Differential Revision: https://phabricator.services.mozilla.com/D85122
This commit is contained in:
parent
2e10bf25d7
commit
a841171067
@ -8309,6 +8309,13 @@
|
||||
value: 1048576
|
||||
mirror: always
|
||||
|
||||
# When true, a http request will be upgraded to https when HTTPS RR is
|
||||
# available.
|
||||
- name: network.dns.upgrade_with_https_rr
|
||||
type: RelaxedAtomicBool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Prefs starting with "nglayout."
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "nsIDNSListener.h"
|
||||
#include "nsIDNSService.h"
|
||||
#include "nsIDNSByTypeRecord.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIURI.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
@ -110,21 +111,38 @@ nsresult nsDNSPrefetch::PrefetchHigh(bool refreshDNS) {
|
||||
return Prefetch(refreshDNS ? nsIDNSService::RESOLVE_BYPASS_CACHE : 0);
|
||||
}
|
||||
|
||||
nsresult nsDNSPrefetch::FetchHTTPSSVC(bool aRefreshDNS) {
|
||||
if (!sDNSService) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIEventTarget> target = mozilla::GetCurrentEventTarget();
|
||||
uint32_t flags = nsIDNSService::GetFlagsFromTRRMode(mTRRMode) |
|
||||
nsIDNSService::RESOLVE_SPECULATE;
|
||||
if (aRefreshDNS) {
|
||||
flags |= nsIDNSService::RESOLVE_BYPASS_CACHE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICancelable> tmpOutstanding;
|
||||
return sDNSService->AsyncResolveNative(
|
||||
mHostname, nsIDNSService::RESOLVE_TYPE_HTTPSSVC, flags, nullptr, this,
|
||||
target, mOriginAttributes, getter_AddRefs(tmpOutstanding));
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsDNSPrefetch, nsIDNSListener)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSPrefetch::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec,
|
||||
nsresult status) {
|
||||
if (mStoreTiming) {
|
||||
nsCOMPtr<nsIDNSHTTPSSVCRecord> httpsRecord = do_QueryInterface(rec);
|
||||
|
||||
if (mStoreTiming && !httpsRecord) {
|
||||
mEndTimestamp = mozilla::TimeStamp::Now();
|
||||
}
|
||||
nsCOMPtr<nsIDNSListener> listener = do_QueryReferent(mListener);
|
||||
if (listener) {
|
||||
listener->OnLookupComplete(request, rec, status);
|
||||
}
|
||||
// OnLookupComplete should be called on the target thread, so we release
|
||||
// mListener here to make sure mListener is also released on the target
|
||||
// thread.
|
||||
mListener = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
class nsIURI;
|
||||
class nsIDNSService;
|
||||
class nsIDNSHTTPSSVCRecord;
|
||||
|
||||
class nsDNSPrefetch final : public nsIDNSListener {
|
||||
~nsDNSPrefetch() = default;
|
||||
@ -43,6 +44,8 @@ class nsDNSPrefetch final : public nsIDNSListener {
|
||||
nsresult PrefetchMedium(bool refreshDNS = false);
|
||||
nsresult PrefetchLow(bool refreshDNS = false);
|
||||
|
||||
nsresult FetchHTTPSSVC(bool aRefreshDNS);
|
||||
|
||||
static void PrefChanged(const char* aPref, void* aClosure);
|
||||
|
||||
private:
|
||||
|
@ -174,7 +174,8 @@ ChildDNSRecord::ReportUnusable(uint16_t aPort) {
|
||||
|
||||
class ChildDNSByTypeRecord : public nsIDNSByTypeRecord,
|
||||
public nsIDNSTXTRecord,
|
||||
public nsIDNSHTTPSSVCRecord {
|
||||
public nsIDNSHTTPSSVCRecord,
|
||||
public DNSHTTPSSVCRecordBase {
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIDNSRECORD
|
||||
@ -246,6 +247,24 @@ ChildDNSByTypeRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3,
|
||||
nsISVCBRecord** aRecord) {
|
||||
if (!mResults.is<TypeRecordHTTPSSVC>()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
auto& results = mResults.as<TypeRecordHTTPSSVC>();
|
||||
nsCOMPtr<nsISVCBRecord> result =
|
||||
GetServiceModeRecordInternal(aNoHttp2, aNoHttp3, results);
|
||||
if (!result) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
result.forget(aRecord);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ChildDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType* aResults) {
|
||||
*aResults = mResults;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "HTTPSSVC.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "nsHttp.h"
|
||||
#include "nsNetAddr.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -129,6 +130,48 @@ SvcParam::GetIpv6Hint(nsTArray<RefPtr<nsINetAddr>>& aIpv6Hint) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Maybe<uint16_t> SVCB::GetPort() const {
|
||||
Maybe<uint16_t> port;
|
||||
for (const auto& value : mSvcFieldValue) {
|
||||
if (value.mValue.is<SvcParamPort>()) {
|
||||
port.emplace(value.mValue.as<SvcParamPort>().mValue);
|
||||
if (NS_FAILED(NS_CheckPortSafety(*port, "https"))) {
|
||||
*port = 0;
|
||||
}
|
||||
return port;
|
||||
}
|
||||
}
|
||||
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
bool SVCB::NoDefaultAlpn() const {
|
||||
for (const auto& value : mSvcFieldValue) {
|
||||
if (value.mValue.is<SvcParamKeyNoDefaultAlpn>()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<nsCString> SVCB::GetAlpn(bool aNoHttp2, bool aNoHttp3) const {
|
||||
Maybe<nsCString> alpn;
|
||||
nsAutoCString alpnValue;
|
||||
for (const auto& value : mSvcFieldValue) {
|
||||
if (value.mValue.is<SvcParamAlpn>()) {
|
||||
alpn.emplace();
|
||||
alpnValue = value.mValue.as<SvcParamAlpn>().mValue;
|
||||
if (!alpnValue.IsEmpty()) {
|
||||
alpn->Assign(SelectAlpnFromAlpnList(alpnValue, aNoHttp2, aNoHttp3));
|
||||
}
|
||||
return alpn;
|
||||
}
|
||||
}
|
||||
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP SVCBRecord::GetPriority(uint16_t* aPriority) {
|
||||
*aPriority = mData.mSvcFieldPriority;
|
||||
return NS_OK;
|
||||
@ -139,6 +182,10 @@ NS_IMETHODIMP SVCBRecord::GetName(nsACString& aName) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Maybe<uint16_t> SVCBRecord::GetPort() { return mPort; }
|
||||
|
||||
Maybe<nsCString> SVCBRecord::GetAlpn() { return mAlpn; }
|
||||
|
||||
NS_IMETHODIMP SVCBRecord::GetValues(nsTArray<RefPtr<nsISVCParam>>& aValues) {
|
||||
for (const auto& v : mData.mSvcFieldValue) {
|
||||
RefPtr<nsISVCParam> param = new SvcParam(v.mValue);
|
||||
@ -147,5 +194,45 @@ NS_IMETHODIMP SVCBRecord::GetValues(nsTArray<RefPtr<nsISVCParam>>& aValues) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<nsISVCBRecord>
|
||||
DNSHTTPSSVCRecordBase::GetServiceModeRecordInternal(
|
||||
bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords) {
|
||||
nsCOMPtr<nsISVCBRecord> selectedRecord;
|
||||
uint32_t recordHasNoDefaultAlpnCount = 0;
|
||||
for (const SVCB& record : aRecords) {
|
||||
if (record.mSvcFieldPriority == 0) {
|
||||
// In ServiceMode, the SvcPriority should never be 0.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (record.NoDefaultAlpn()) {
|
||||
++recordHasNoDefaultAlpnCount;
|
||||
}
|
||||
|
||||
Maybe<uint16_t> port = record.GetPort();
|
||||
if (port && *port == 0) {
|
||||
// Found an unsafe port, skip this record.
|
||||
continue;
|
||||
}
|
||||
|
||||
Maybe<nsCString> alpn = record.GetAlpn(aNoHttp2, aNoHttp3);
|
||||
if (alpn && alpn->IsEmpty()) {
|
||||
// Can't find any supported protocols, skip.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!selectedRecord) {
|
||||
selectedRecord = new SVCBRecord(record, std::move(port), std::move(alpn));
|
||||
}
|
||||
}
|
||||
|
||||
// If all records indicate "no-default-alpn", we should not use this RRSet.
|
||||
if (recordHasNoDefaultAlpnCount == aRecords.Length()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return selectedRecord.forget();
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
@ -85,6 +85,9 @@ struct SVCB {
|
||||
bool operator<(const SVCB& aOther) const {
|
||||
return mSvcFieldPriority < aOther.mSvcFieldPriority;
|
||||
}
|
||||
Maybe<uint16_t> GetPort() const;
|
||||
bool NoDefaultAlpn() const;
|
||||
Maybe<nsCString> GetAlpn(bool aNoHttp2, bool aNoHttp3) const;
|
||||
uint16_t mSvcFieldPriority = 0;
|
||||
nsCString mSvcDomainName;
|
||||
CopyableTArray<SvcFieldValue> mSvcFieldValue;
|
||||
@ -94,11 +97,28 @@ class SVCBRecord : public nsISVCBRecord {
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSISVCBRECORD
|
||||
public:
|
||||
explicit SVCBRecord(const SVCB& data) : mData(data) {}
|
||||
explicit SVCBRecord(const SVCB& data)
|
||||
: mData(data), mPort(Nothing()), mAlpn(Nothing()) {}
|
||||
explicit SVCBRecord(const SVCB& data, Maybe<uint16_t>&& aPort,
|
||||
Maybe<nsCString>&& aAlpn)
|
||||
: mData(data), mPort(std::move(aPort)), mAlpn(std::move(aAlpn)) {}
|
||||
|
||||
private:
|
||||
virtual ~SVCBRecord() = default;
|
||||
SVCB mData;
|
||||
Maybe<uint16_t> mPort;
|
||||
Maybe<nsCString> mAlpn;
|
||||
};
|
||||
|
||||
class DNSHTTPSSVCRecordBase {
|
||||
public:
|
||||
DNSHTTPSSVCRecordBase() = default;
|
||||
|
||||
protected:
|
||||
virtual ~DNSHTTPSSVCRecordBase() = default;
|
||||
|
||||
already_AddRefed<nsISVCBRecord> GetServiceModeRecordInternal(
|
||||
bool aNoHttp2, bool aNoHttp3, const nsTArray<SVCB>& aRecords);
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
@ -368,6 +368,12 @@ nsDNSByTypeRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) {
|
||||
return mHostRecord->GetRecords(aRecords);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSByTypeRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3,
|
||||
nsISVCBRecord** aRecord) {
|
||||
return mHostRecord->GetServiceModeRecord(aNoHttp2, aNoHttp3, aRecord);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDNSByTypeRecord::GetResults(mozilla::net::TypeRecordResultType* aResults) {
|
||||
*aResults = mHostRecord->GetResults();
|
||||
|
@ -604,6 +604,25 @@ TypeHostRecord::GetRecords(nsTArray<RefPtr<nsISVCBRecord>>& aRecords) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TypeHostRecord::GetServiceModeRecord(bool aNoHttp2, bool aNoHttp3,
|
||||
nsISVCBRecord** aRecord) {
|
||||
MutexAutoLock lock(mResultsLock);
|
||||
if (!mResults.is<TypeRecordHTTPSSVC>()) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
auto& results = mResults.as<TypeRecordHTTPSSVC>();
|
||||
nsCOMPtr<nsISVCBRecord> result =
|
||||
GetServiceModeRecordInternal(aNoHttp2, aNoHttp3, results);
|
||||
if (!result) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
result.forget(aRecord);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
static const char kPrefGetTtl[] = "network.dns.get-ttl";
|
||||
|
@ -330,7 +330,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(AddrHostRecord, ADDRHOSTRECORD_IID)
|
||||
|
||||
class TypeHostRecord final : public nsHostRecord,
|
||||
public nsIDNSTXTRecord,
|
||||
public nsIDNSHTTPSSVCRecord {
|
||||
public nsIDNSHTTPSSVCRecord,
|
||||
public mozilla::net::DNSHTTPSSVCRecordBase {
|
||||
public:
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(TYPEHOSTRECORD_IID)
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
%{ C++
|
||||
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsTArrayForwardDeclare.h"
|
||||
#include "nsStringFwd.h"
|
||||
|
||||
@ -26,6 +27,9 @@ namespace net {
|
||||
[ref] native CStringArrayRef(CopyableTArray<nsCString>);
|
||||
native TypeResult(mozilla::net::TypeRecordResultType);
|
||||
|
||||
native MaybePort(mozilla::Maybe<uint16_t>);
|
||||
native MaybeAlpn(mozilla::Maybe<nsCString>);
|
||||
|
||||
[scriptable, uuid(5d13241b-9d46-448a-90d8-77c418491026)]
|
||||
interface nsIDNSByTypeRecord : nsIDNSRecord
|
||||
{
|
||||
@ -82,10 +86,12 @@ interface nsISVCParamIPv6Hint : nsISupports {
|
||||
readonly attribute Array<nsINetAddr> ipv6Hint;
|
||||
};
|
||||
|
||||
[scriptable, uuid(a4da5645-2160-4439-bd11-540a2d26c989)]
|
||||
[scriptable, builtinclass, uuid(a4da5645-2160-4439-bd11-540a2d26c989)]
|
||||
interface nsISVCBRecord : nsISupports {
|
||||
readonly attribute uint16_t priority;
|
||||
readonly attribute ACString name;
|
||||
[noscript, nostdcall, notxpcom] readonly attribute MaybePort port;
|
||||
[noscript, nostdcall, notxpcom] readonly attribute MaybeAlpn alpn;
|
||||
readonly attribute Array<nsISVCParam> values;
|
||||
};
|
||||
|
||||
@ -93,4 +99,5 @@ interface nsISVCBRecord : nsISupports {
|
||||
interface nsIDNSHTTPSSVCRecord : nsISupports
|
||||
{
|
||||
readonly attribute Array<nsISVCBRecord> records;
|
||||
nsISVCBRecord GetServiceModeRecord(in boolean aNoHttp2, in boolean aNoHttp3);
|
||||
};
|
||||
|
@ -1023,5 +1023,48 @@ nsresult HttpProxyResponseToErrorCode(uint32_t aStatusCode) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCString SelectAlpnFromAlpnList(const nsACString& aAlpnList, bool aNoHttp2,
|
||||
bool aNoHttp3) {
|
||||
nsCString h3Value;
|
||||
nsCString h2Value;
|
||||
nsCString h1Value;
|
||||
// aAlpnList is a list of alpn-id and use comma as a delimiter.
|
||||
nsCCharSeparatedTokenizer tokenizer(aAlpnList, ',');
|
||||
nsAutoCString npnStr;
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsACString& npnToken(tokenizer.nextToken());
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (!h3Value.IsEmpty() && gHttpHandler->IsHttp3Enabled() && !aNoHttp3) {
|
||||
return h3Value;
|
||||
}
|
||||
|
||||
if (!h2Value.IsEmpty() && gHttpHandler->IsSpdyEnabled() && !aNoHttp2) {
|
||||
return h2Value;
|
||||
}
|
||||
|
||||
if (!h1Value.IsEmpty()) {
|
||||
return h1Value;
|
||||
}
|
||||
|
||||
// If we are here, there is no supported alpn can be used.
|
||||
return EmptyCString();
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
@ -347,6 +347,12 @@ 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 an empty
|
||||
// string if no supported alpn-id is found.
|
||||
nsCString SelectAlpnFromAlpnList(const nsACString& aAlpnList, bool aNoHttp2,
|
||||
bool aNoHttp3);
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -387,6 +387,8 @@ nsHttpChannel::nsHttpChannel()
|
||||
mIsIsolated(0),
|
||||
mTopWindowOriginComputed(0),
|
||||
mDataSentToChildProcess(0),
|
||||
mUseHTTPSSVC(0),
|
||||
mWaitHTTPSSVCRecord(0),
|
||||
mPushedStreamId(0),
|
||||
mLocalBlocklist(false),
|
||||
mOnTailUnblock(nullptr),
|
||||
@ -604,6 +606,15 @@ nsresult nsHttpChannel::OnBeforeConnect() {
|
||||
auto resultCallback = [self(self)](bool aResult, nsresult aStatus) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// We need to wait for HTTPSSVC record if there is no AltSvc or HSTS
|
||||
// upgrade for this request.
|
||||
if (!aResult && NS_SUCCEEDED(aStatus) && self->mUseHTTPSSVC) {
|
||||
LOG(("nsHttpChannel Wait for HTTPSSVC record [this=%p]\n",
|
||||
self.get()));
|
||||
self->mWaitHTTPSSVCRecord = true;
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = self->ContinueOnBeforeConnect(aResult, aStatus);
|
||||
if (NS_FAILED(rv)) {
|
||||
self->CloseCacheEntry(false);
|
||||
@ -698,6 +709,28 @@ nsresult nsHttpChannel::ContinueOnBeforeConnect(bool aShouldUpgrade,
|
||||
mConnectionInfo->SetIPv4Disabled(mCaps & NS_HTTP_DISABLE_IPV4);
|
||||
mConnectionInfo->SetIPv6Disabled(mCaps & NS_HTTP_DISABLE_IPV6);
|
||||
|
||||
if (mHTTPSSVCRecord) {
|
||||
MOZ_ASSERT(mURI->SchemeIs("https"));
|
||||
|
||||
LOG((" Using connection info with HTTPSSVC record"));
|
||||
nsCOMPtr<nsIDNSHTTPSSVCRecord> rec;
|
||||
mHTTPSSVCRecord.swap(rec);
|
||||
|
||||
bool http3Allowed = !mUpgradeProtocolCallback && !mProxyInfo &&
|
||||
!(mCaps & NS_HTTP_BE_CONSERVATIVE) && !mBeConservative;
|
||||
|
||||
nsCOMPtr<nsISVCBRecord> record;
|
||||
if (NS_SUCCEEDED(rec->GetServiceModeRecord(mCaps & NS_HTTP_DISALLOW_SPDY,
|
||||
!http3Allowed,
|
||||
getter_AddRefs(record)))) {
|
||||
MOZ_ASSERT(record);
|
||||
|
||||
RefPtr<nsHttpConnectionInfo> newConnInfo =
|
||||
mConnectionInfo->CloneAndAdoptHTTPSSVCRecord(record);
|
||||
mConnectionInfo = std::move(newConnInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// notify "http-on-before-connect" observers
|
||||
gHttpHandler->OnBeforeConnect(this);
|
||||
|
||||
@ -3181,6 +3214,11 @@ nsresult nsHttpChannel::ProxyFailover() {
|
||||
return AsyncDoReplaceWithProxy(pi);
|
||||
}
|
||||
|
||||
void nsHttpChannel::SetHTTPSSVCRecord(nsIDNSHTTPSSVCRecord* aRecord) {
|
||||
LOG(("nsHttpChannel::SetHTTPSSVCRecord [this=%p]\n", this));
|
||||
mHTTPSSVCRecord = aRecord;
|
||||
}
|
||||
|
||||
void nsHttpChannel::HandleAsyncRedirectChannelToHttps() {
|
||||
MOZ_ASSERT(!mCallOnResume, "How did that happen?");
|
||||
|
||||
@ -3264,6 +3302,15 @@ nsresult nsHttpChannel::StartRedirectChannelToURI(nsIURI* upgradedURI,
|
||||
rv = SetupReplacementChannel(upgradedURI, newChannel, true, flags);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (mHTTPSSVCRecord) {
|
||||
RefPtr<nsHttpChannel> httpChan = do_QueryObject(newChannel);
|
||||
if (httpChan) {
|
||||
nsCOMPtr<nsIDNSHTTPSSVCRecord> rec;
|
||||
mHTTPSSVCRecord.swap(rec);
|
||||
httpChan->SetHTTPSSVCRecord(rec);
|
||||
}
|
||||
}
|
||||
|
||||
// Inform consumers about this fake redirect
|
||||
mRedirectChannel = newChannel;
|
||||
|
||||
@ -6817,6 +6864,10 @@ nsresult nsHttpChannel::BeginConnect() {
|
||||
bool http3Allowed = !mUpgradeProtocolCallback && !mProxyInfo &&
|
||||
!(mCaps & NS_HTTP_BE_CONSERVATIVE) && !mBeConservative;
|
||||
|
||||
// No need to lookup HTTPSSVC record if we already have one.
|
||||
mUseHTTPSSVC =
|
||||
StaticPrefs::network_dns_upgrade_with_https_rr() && !mHTTPSSVCRecord;
|
||||
|
||||
RefPtr<AltSvcMapping> mapping;
|
||||
if (!mConnectionInfo && mAllowAltSvc && // per channel
|
||||
!(mLoadFlags & LOAD_FRESH_CONNECTION) &&
|
||||
@ -6865,6 +6916,9 @@ nsresult nsHttpChannel::BeginConnect() {
|
||||
originAttributes);
|
||||
Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, true);
|
||||
Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC_OE, !isHttps);
|
||||
|
||||
// Don't use HTTPSSVC record if we found altsvc mapping.
|
||||
mUseHTTPSSVC = false;
|
||||
} else if (mConnectionInfo) {
|
||||
LOG(("nsHttpChannel %p Using channel supplied connection info", this));
|
||||
Telemetry::Accumulate(Telemetry::HTTP_TRANSACTION_USE_ALTSVC, false);
|
||||
@ -7041,6 +7095,14 @@ nsresult nsHttpChannel::MaybeStartDNSPrefetch() {
|
||||
// Resolved in OnLookupComplete.
|
||||
mDNSBlockingThenable = mDNSBlockingPromise.Ensure(__func__);
|
||||
}
|
||||
|
||||
if (mUseHTTPSSVC) {
|
||||
rv = mDNSPrefetch->FetchHTTPSSVC(mCaps & NS_HTTP_REFRESH_DNS);
|
||||
if (NS_FAILED(rv)) {
|
||||
LOG((" FetchHTTPSSVC failed with 0x%08" PRIx32,
|
||||
static_cast<uint32_t>(rv)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -9106,42 +9168,59 @@ nsHttpChannel::OnLookupComplete(nsICancelable* request, nsIDNSRecord* rec,
|
||||
nsresult status) {
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Expecting DNS callback on main thread.");
|
||||
|
||||
nsCOMPtr<nsIDNSHTTPSSVCRecord> httpSSVCRecord = do_QueryInterface(rec);
|
||||
LOG(
|
||||
("nsHttpChannel::OnLookupComplete [this=%p] prefetch complete%s: "
|
||||
"%s status[0x%" PRIx32 "]\n",
|
||||
"%s status[0x%" PRIx32 "], isHTTPSSVC=%d\n",
|
||||
this, mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : "",
|
||||
NS_SUCCEEDED(status) ? "success" : "failure",
|
||||
static_cast<uint32_t>(status)));
|
||||
static_cast<uint32_t>(status), !!httpSSVCRecord));
|
||||
|
||||
// We no longer need the dns prefetch object. Note: mDNSPrefetch could be
|
||||
// validly null if OnStopRequest has already been called.
|
||||
// We only need the domainLookup timestamps when not loading from cache
|
||||
if (mDNSPrefetch && mDNSPrefetch->TimingsValid() && mTransaction) {
|
||||
TimeStamp connectStart = mTransaction->GetConnectStart();
|
||||
TimeStamp requestStart = mTransaction->GetRequestStart();
|
||||
// We only set the domainLookup timestamps if we're not using a
|
||||
// persistent connection.
|
||||
if (requestStart.IsNull() && connectStart.IsNull()) {
|
||||
mTransaction->SetDomainLookupStart(mDNSPrefetch->StartTimestamp());
|
||||
mTransaction->SetDomainLookupEnd(mDNSPrefetch->EndTimestamp());
|
||||
if (!httpSSVCRecord) {
|
||||
// We no longer need the dns prefetch object. Note: mDNSPrefetch could be
|
||||
// validly null if OnStopRequest has already been called.
|
||||
// We only need the domainLookup timestamps when not loading from cache
|
||||
if (mDNSPrefetch && mDNSPrefetch->TimingsValid() && mTransaction) {
|
||||
TimeStamp connectStart = mTransaction->GetConnectStart();
|
||||
TimeStamp requestStart = mTransaction->GetRequestStart();
|
||||
// We only set the domainLookup timestamps if we're not using a
|
||||
// persistent connection.
|
||||
if (requestStart.IsNull() && connectStart.IsNull()) {
|
||||
mTransaction->SetDomainLookupStart(mDNSPrefetch->StartTimestamp());
|
||||
mTransaction->SetDomainLookupEnd(mDNSPrefetch->EndTimestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
mDNSPrefetch = nullptr;
|
||||
|
||||
// Unset DNS cache refresh if it was requested,
|
||||
if (mCaps & NS_HTTP_REFRESH_DNS) {
|
||||
mCaps &= ~NS_HTTP_REFRESH_DNS;
|
||||
if (mTransaction) {
|
||||
mTransaction->SetDNSWasRefreshed();
|
||||
// Unset DNS cache refresh if it was requested,
|
||||
if (mCaps & NS_HTTP_REFRESH_DNS) {
|
||||
mCaps &= ~NS_HTTP_REFRESH_DNS;
|
||||
if (mTransaction) {
|
||||
mTransaction->SetDNSWasRefreshed();
|
||||
}
|
||||
}
|
||||
|
||||
if (!mDNSBlockingPromise.IsEmpty()) {
|
||||
if (NS_SUCCEEDED(status)) {
|
||||
nsCOMPtr<nsIDNSRecord> record(rec);
|
||||
mDNSBlockingPromise.Resolve(record, __func__);
|
||||
} else {
|
||||
mDNSBlockingPromise.Reject(status, __func__);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!mDNSBlockingPromise.IsEmpty()) {
|
||||
if (NS_SUCCEEDED(status)) {
|
||||
nsCOMPtr<nsIDNSRecord> record(rec);
|
||||
mDNSBlockingPromise.Resolve(record, __func__);
|
||||
} else {
|
||||
mDNSBlockingPromise.Reject(status, __func__);
|
||||
if (mWaitHTTPSSVCRecord) {
|
||||
MOZ_ASSERT(mURI->SchemeIs("http"));
|
||||
MOZ_ASSERT(!mHTTPSSVCRecord);
|
||||
|
||||
// This record will be used in the new redirect channel.
|
||||
mHTTPSSVCRecord = httpSSVCRecord;
|
||||
nsresult rv = ContinueOnBeforeConnect(true, status);
|
||||
if (NS_FAILED(rv)) {
|
||||
CloseCacheEntry(false);
|
||||
Unused << AsyncAbort(rv);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,7 @@
|
||||
class nsDNSPrefetch;
|
||||
class nsICancelable;
|
||||
class nsIDNSRecord;
|
||||
class nsIDNSHTTPSSVCRecord;
|
||||
class nsIHttpChannelAuthProvider;
|
||||
class nsInputStreamPump;
|
||||
class nsITransportSecurityInfo;
|
||||
@ -736,6 +737,9 @@ class nsHttpChannel final : public HttpBaseChannel,
|
||||
// content process directly.
|
||||
uint32_t mDataSentToChildProcess : 1;
|
||||
|
||||
uint32_t mUseHTTPSSVC : 1;
|
||||
uint32_t mWaitHTTPSSVCRecord : 1;
|
||||
|
||||
// The origin of the top window, only valid when mTopWindowOriginComputed is
|
||||
// true.
|
||||
nsCString mTopWindowOrigin;
|
||||
@ -812,6 +816,9 @@ class nsHttpChannel final : public HttpBaseChannel,
|
||||
nsresult TriggerNetworkWithDelay(uint32_t aDelay);
|
||||
nsresult TriggerNetwork();
|
||||
void CancelNetworkRequest(nsresult aStatus);
|
||||
|
||||
void SetHTTPSSVCRecord(nsIDNSHTTPSSVCRecord* aRecord);
|
||||
|
||||
// Timer used to delay the network request, or to trigger the network
|
||||
// request if retrieving the cache entry takes too long.
|
||||
nsCOMPtr<nsITimer> mNetworkTriggerTimer;
|
||||
@ -845,6 +852,10 @@ class nsHttpChannel final : public HttpBaseChannel,
|
||||
// called and reset the value when we switch to another failover proxy.
|
||||
int32_t mProxyConnectResponseCode;
|
||||
|
||||
// If this is not null, this will be used to update the connection info in
|
||||
// nsHttpChannel::BeginConnect().
|
||||
nsCOMPtr<nsIDNSHTTPSSVCRecord> mHTTPSSVCRecord;
|
||||
|
||||
protected:
|
||||
virtual void DoNotifyListenerCleanup() override;
|
||||
|
||||
|
@ -17,8 +17,10 @@
|
||||
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "mozilla/net/NeckoChannelParams.h"
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsICryptoHash.h"
|
||||
#include "nsIDNSByTypeRecord.h"
|
||||
#include "nsIProtocolProxyService.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsProxyInfo.h"
|
||||
@ -340,6 +342,53 @@ already_AddRefed<nsHttpConnectionInfo> nsHttpConnectionInfo::Clone() const {
|
||||
return clone.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsHttpConnectionInfo>
|
||||
nsHttpConnectionInfo::CloneAndAdoptHTTPSSVCRecord(
|
||||
nsISVCBRecord* aRecord) const {
|
||||
MOZ_ASSERT(aRecord);
|
||||
|
||||
// Get the domain name of this HTTPS RR. This name will be assigned to
|
||||
// mRoutedHost in the new connection info.
|
||||
nsAutoCString name;
|
||||
aRecord->GetName(name);
|
||||
|
||||
// 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<nsCString> alpn = aRecord->GetAlpn();
|
||||
|
||||
LOG(("HTTPSSVC: use new routed host (%s) and new npnToken (%s)", name.get(),
|
||||
alpn ? alpn->get() : "None"));
|
||||
|
||||
RefPtr<nsHttpConnectionInfo> clone;
|
||||
if (name.IsEmpty()) {
|
||||
clone = new nsHttpConnectionInfo(
|
||||
mOrigin, mOriginPort, alpn ? *alpn : EmptyCString(), mUsername,
|
||||
mTopWindowOrigin, mProxyInfo, mOriginAttributes, mEndToEndSSL,
|
||||
mIsolated, mIsHttp3);
|
||||
} else {
|
||||
MOZ_ASSERT(mEndToEndSSL);
|
||||
clone = new nsHttpConnectionInfo(
|
||||
mOrigin, mOriginPort, alpn ? *alpn : EmptyCString(), mUsername,
|
||||
mTopWindowOrigin, mProxyInfo, mOriginAttributes, name,
|
||||
port ? *port : mRoutedPort, mIsolated, mIsHttp3);
|
||||
}
|
||||
|
||||
// Make sure the anonymous, insecure-scheme, and private flags are transferred
|
||||
clone->SetAnonymous(GetAnonymous());
|
||||
clone->SetPrivate(GetPrivate());
|
||||
clone->SetInsecureScheme(GetInsecureScheme());
|
||||
clone->SetNoSpdy(GetNoSpdy());
|
||||
clone->SetBeConservative(GetBeConservative());
|
||||
clone->SetTlsFlags(GetTlsFlags());
|
||||
clone->SetIsTrrServiceChannel(GetIsTrrServiceChannel());
|
||||
clone->SetTRRMode(GetTRRMode());
|
||||
clone->SetIPv4Disabled(GetIPv4Disabled());
|
||||
clone->SetIPv6Disabled(GetIPv6Disabled());
|
||||
|
||||
return clone.forget();
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsHttpConnectionInfo::SerializeHttpConnectionInfo(
|
||||
nsHttpConnectionInfo* aInfo, HttpConnectionInfoCloneArgs& aArgs) {
|
||||
|
@ -29,6 +29,8 @@
|
||||
// origin and multiplex non tunneled transactions at the same time, so they have
|
||||
// a special wildcard CI that accepts all origins through that proxy.
|
||||
|
||||
class nsISVCBRecord;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
@ -83,6 +85,12 @@ class nsHttpConnectionInfo final : public ARefBase {
|
||||
|
||||
// OK to treat these as an infalible allocation
|
||||
already_AddRefed<nsHttpConnectionInfo> Clone() const;
|
||||
// This main prupose of this function is to clone this connection info, but
|
||||
// replace mRoutedHost with SvcDomainName in the given SVCB record. Note that
|
||||
// if SvcParamKeyPort and SvcParamKeyAlpn are presented in the SVCB record,
|
||||
// mRoutedPort and mNPNToken will be replaced as well.
|
||||
already_AddRefed<nsHttpConnectionInfo> CloneAndAdoptHTTPSSVCRecord(
|
||||
nsISVCBRecord* aRecord) const;
|
||||
void CloneAsDirectRoute(nsHttpConnectionInfo** outParam);
|
||||
[[nodiscard]] nsresult CreateWildCard(nsHttpConnectionInfo** outParam);
|
||||
|
||||
|
44
netwerk/test/gtest/TestSupportAlpn.cpp
Normal file
44
netwerk/test/gtest/TestSupportAlpn.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#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.
|
||||
nsCString result =
|
||||
SelectAlpnFromAlpnList("h3-28,h3-27,h2,http/1.1"_ns, false, false);
|
||||
ASSERT_EQ(result, "h3-28"_ns);
|
||||
|
||||
// h3 is disabled, so we will use h2.
|
||||
result = SelectAlpnFromAlpnList("h3-28,h3-27,h2,http/1.1"_ns, false, true);
|
||||
ASSERT_EQ(result, "h2"_ns);
|
||||
|
||||
// h3 is disabled and h2 is not found, so we will select http/1.1.
|
||||
result = SelectAlpnFromAlpnList("h3-28,h3-27,http/1.1"_ns, false, true);
|
||||
ASSERT_EQ(result, "http/1.1"_ns);
|
||||
|
||||
// h3 and h2 are disabled, so we will select http/1.1.
|
||||
result = SelectAlpnFromAlpnList("h3-28,h3-27,h2,http/1.1"_ns, true, true);
|
||||
ASSERT_EQ(result, "http/1.1"_ns);
|
||||
|
||||
// h3 and h2 are disabled and http1.1 is not found, we return an empty string.
|
||||
result = SelectAlpnFromAlpnList("h3-28,h3-27,h2"_ns, true, true);
|
||||
ASSERT_EQ(result, ""_ns);
|
||||
|
||||
// No supported alpn.
|
||||
result = SelectAlpnFromAlpnList("ftp,h2c"_ns, true, true);
|
||||
ASSERT_EQ(result, ""_ns);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
@ -22,6 +22,7 @@ UNIFIED_SOURCES += [
|
||||
'TestServerTimingHeader.cpp',
|
||||
'TestSocketTransportService.cpp',
|
||||
'TestStandardURL.cpp',
|
||||
'TestSupportAlpn.cpp',
|
||||
'TestUDPSocket.cpp',
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user