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:
Kershaw Chang 2020-08-21 18:59:07 +00:00
parent 2e10bf25d7
commit a841171067
18 changed files with 463 additions and 35 deletions

View File

@ -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."
#---------------------------------------------------------------------------

View File

@ -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;
}

View File

@ -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:

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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";

View File

@ -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

View File

@ -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);
};

View File

@ -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

View File

@ -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

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View 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

View File

@ -22,6 +22,7 @@ UNIFIED_SOURCES += [
'TestServerTimingHeader.cpp',
'TestSocketTransportService.cpp',
'TestStandardURL.cpp',
'TestSupportAlpn.cpp',
'TestUDPSocket.cpp',
]