Bug 1618849 - Add separate AltSvc for http2 and http3 and prefer http3 if allowed. r=necko-reviewers,valentin

Differential Revision: https://phabricator.services.mozilla.com/D86934
This commit is contained in:
Dragana Damjanovic 2020-09-09 10:32:22 +00:00
parent 735a53ee64
commit 7433c1b34d
15 changed files with 284 additions and 62 deletions

View File

@ -165,7 +165,9 @@ void AltSvcMapping::ProcessHeader(
continue; continue;
} }
http3Found = true; if (isHttp3) {
http3Found = true;
}
RefPtr<AltSvcMapping> mapping = new AltSvcMapping( RefPtr<AltSvcMapping> mapping = new AltSvcMapping(
gHttpHandler->AltServiceCache()->GetStoragePtr(), gHttpHandler->AltServiceCache()->GetStoragePtr(),
@ -255,7 +257,7 @@ AltSvcMapping::AltSvcMapping(
if (mExpiresAt) { if (mExpiresAt) {
MakeHashKey(mHashKey, originScheme, mOriginHost, mOriginPort, mPrivate, MakeHashKey(mHashKey, originScheme, mOriginHost, mOriginPort, mPrivate,
mIsolated, mTopWindowOrigin, mOriginAttributes); mIsolated, mTopWindowOrigin, mOriginAttributes, mIsHttp3);
} }
} }
@ -265,7 +267,8 @@ void AltSvcMapping::MakeHashKey(nsCString& outKey,
int32_t originPort, bool privateBrowsing, int32_t originPort, bool privateBrowsing,
bool isolated, bool isolated,
const nsACString& topWindowOrigin, const nsACString& topWindowOrigin,
const OriginAttributes& originAttributes) { const OriginAttributes& originAttributes,
bool aHttp3) {
outKey.Truncate(); outKey.Truncate();
if (originPort == -1) { if (originPort == -1) {
@ -284,15 +287,16 @@ void AltSvcMapping::MakeHashKey(nsCString& outKey,
nsAutoCString suffix; nsAutoCString suffix;
originAttributes.CreateSuffix(suffix); originAttributes.CreateSuffix(suffix);
outKey.Append(suffix); outKey.Append(suffix);
outKey.Append(':');
if (isolated) { if (isolated) {
outKey.Append(':');
outKey.Append('I'); outKey.Append('I');
outKey.Append(':'); outKey.Append(':');
outKey.Append(topWindowOrigin); outKey.Append(topWindowOrigin);
outKey.Append( outKey.Append(
'|'); // Be careful, the top window origin may contain colons! '|'); // Be careful, the top window origin may contain colons!
} }
outKey.Append(aHttp3 ? '3' : '.');
} }
int32_t AltSvcMapping::TTL() { return mExpiresAt - NowInSeconds(); } int32_t AltSvcMapping::TTL() { return mExpiresAt - NowInSeconds(); }
@ -479,7 +483,7 @@ AltSvcMapping::AltSvcMapping(DataStorage* storage, int32_t epoch,
MakeHashKey(mHashKey, mHttps ? "https"_ns : "http"_ns, mOriginHost, MakeHashKey(mHashKey, mHttps ? "https"_ns : "http"_ns, mOriginHost,
mOriginPort, mPrivate, mIsolated, mTopWindowOrigin, mOriginPort, mPrivate, mIsolated, mTopWindowOrigin,
mOriginAttributes); mOriginAttributes, mIsHttp3);
} while (false); } while (false);
} }
@ -1132,7 +1136,8 @@ void AltSvcCache::UpdateAltServiceMapping(
already_AddRefed<AltSvcMapping> AltSvcCache::GetAltServiceMapping( already_AddRefed<AltSvcMapping> AltSvcCache::GetAltServiceMapping(
const nsACString& scheme, const nsACString& host, int32_t port, const nsACString& scheme, const nsACString& host, int32_t port,
bool privateBrowsing, bool isolated, const nsACString& topWindowOrigin, bool privateBrowsing, bool isolated, const nsACString& topWindowOrigin,
const OriginAttributes& originAttributes, bool aHttp3Allowed) { const OriginAttributes& originAttributes, bool aHttp2Allowed,
bool aHttp3Allowed) {
EnsureStorageInited(); EnsureStorageInited();
bool isHTTPS; bool isHTTPS;
@ -1146,24 +1151,39 @@ already_AddRefed<AltSvcMapping> AltSvcCache::GetAltServiceMapping(
return nullptr; return nullptr;
} }
nsAutoCString key; // First look for HTTP3
AltSvcMapping::MakeHashKey(key, scheme, host, port, privateBrowsing, isolated, if (aHttp3Allowed) {
topWindowOrigin, originAttributes); nsAutoCString key;
RefPtr<AltSvcMapping> existing = LookupMapping(key, privateBrowsing); AltSvcMapping::MakeHashKey(key, scheme, host, port, privateBrowsing, isolated,
LOG( topWindowOrigin, originAttributes, true);
("AltSvcCache::GetAltServiceMapping %p key=%s " RefPtr<AltSvcMapping> existing = LookupMapping(key, privateBrowsing);
"existing=%p validated=%d ttl=%d", LOG(
this, key.get(), existing.get(), existing ? existing->Validated() : 0, ("AltSvcCache::GetAltServiceMapping %p key=%s "
existing ? existing->TTL() : 0)); "existing=%p validated=%d ttl=%d",
if (existing && !existing->Validated()) { this, key.get(), existing.get(), existing ? existing->Validated() : 0,
existing = nullptr; existing ? existing->TTL() : 0));
if (existing && existing->Validated()) {
return existing.forget();
}
} }
if (existing && existing->IsHttp3() && !aHttp3Allowed) { // Now look for HTTP2.
existing = nullptr; if (aHttp2Allowed) {
nsAutoCString key;
AltSvcMapping::MakeHashKey(key, scheme, host, port, privateBrowsing, isolated,
topWindowOrigin, originAttributes, false);
RefPtr<AltSvcMapping> existing = LookupMapping(key, privateBrowsing);
LOG(
("AltSvcCache::GetAltServiceMapping %p key=%s "
"existing=%p validated=%d ttl=%d",
this, key.get(), existing.get(), existing ? existing->Validated() : 0,
existing ? existing->TTL() : 0));
if (existing && existing->Validated()) {
return existing.forget();
}
} }
return existing.forget(); return nullptr;
} }
class ProxyClearHostMapping : public Runnable { class ProxyClearHostMapping : public Runnable {
@ -1213,11 +1233,18 @@ void AltSvcCache::ClearHostMapping(const nsACString& host, int32_t port,
for (int isolate = 0; isolate < 2; ++isolate) { for (int isolate = 0; isolate < 2; ++isolate) {
AltSvcMapping::MakeHashKey(key, scheme, host, port, bool(pb), AltSvcMapping::MakeHashKey(key, scheme, host, port, bool(pb),
bool(isolate), topWindowOrigin, bool(isolate), topWindowOrigin,
originAttributes); originAttributes, false);
RefPtr<AltSvcMapping> existing = LookupMapping(key, bool(pb)); RefPtr<AltSvcMapping> existing = LookupMapping(key, bool(pb));
if (existing) { if (existing) {
existing->SetExpired(); existing->SetExpired();
} }
AltSvcMapping::MakeHashKey(key, scheme, host, port, bool(pb),
bool(isolate), topWindowOrigin,
originAttributes, true);
existing = LookupMapping(key, bool(pb));
if (existing) {
existing->SetExpired();
}
} }
} }
} }

View File

@ -102,7 +102,7 @@ class AltSvcMapping {
const nsACString& originHost, int32_t originPort, const nsACString& originHost, int32_t originPort,
bool privateBrowsing, bool isolated, bool privateBrowsing, bool isolated,
const nsACString& topWindowOrigin, const nsACString& topWindowOrigin,
const OriginAttributes& originAttributes); const OriginAttributes& originAttributes, bool aIsHttp3);
bool IsHttp3() { return mIsHttp3; } bool IsHttp3() { return mIsHttp3; }
const nsCString& NPNToken() const { return mNPNToken; } const nsCString& NPNToken() const { return mNPNToken; }
@ -198,7 +198,8 @@ class AltSvcCache {
already_AddRefed<AltSvcMapping> GetAltServiceMapping( already_AddRefed<AltSvcMapping> GetAltServiceMapping(
const nsACString& scheme, const nsACString& host, int32_t port, bool pb, const nsACString& scheme, const nsACString& host, int32_t port, bool pb,
bool isolated, const nsACString& topWindowOrigin, bool isolated, const nsACString& topWindowOrigin,
const OriginAttributes& originAttributes, bool aHttp3Allowed); const OriginAttributes& originAttributes, bool aHttp2Allowed,
bool aHttp3Allowed);
void ClearAltServiceMappings(); void ClearAltServiceMappings();
void ClearHostMapping(const nsACString& host, int32_t port, void ClearHostMapping(const nsACString& host, int32_t port,
const OriginAttributes& originAttributes, const OriginAttributes& originAttributes,

View File

@ -3118,7 +3118,7 @@ nsresult Http2Session::WriteSegmentsAgain(nsAHttpSegmentWriter* writer,
// Don't allow any more h2 connections to this host // Don't allow any more h2 connections to this host
RefPtr<nsHttpConnectionInfo> ci = ConnectionInfo(); RefPtr<nsHttpConnectionInfo> ci = ConnectionInfo();
if (ci) { if (ci) {
gHttpHandler->BlacklistSpdy(ci); gHttpHandler->ExcludeHttp2(ci);
} }
// Go through and re-start all of our transactions with h2 disabled. // Go through and re-start all of our transactions with h2 disabled.

View File

@ -241,8 +241,12 @@ nsresult HttpConnectionMgrParent::VerifyTraffic() {
return NS_OK; return NS_OK;
} }
void HttpConnectionMgrParent::BlacklistSpdy(const nsHttpConnectionInfo* ci) { void HttpConnectionMgrParent::ExcludeHttp2(const nsHttpConnectionInfo* ci) {
MOZ_ASSERT_UNREACHABLE("BlacklistSpdy should not be called"); MOZ_ASSERT_UNREACHABLE("ExcludeHttp2 should not be called");
}
void HttpConnectionMgrParent::ExcludeHttp3(const nsHttpConnectionInfo* ci) {
MOZ_ASSERT_UNREACHABLE("ExcludeHttp3 should not be called");
} }
nsresult HttpConnectionMgrParent::ClearConnectionHistory() { nsresult HttpConnectionMgrParent::ClearConnectionHistory() {

View File

@ -153,7 +153,8 @@ class HttpConnectionMgrShell : public nsISupports {
// that connection. // that connection.
[[nodiscard]] virtual nsresult VerifyTraffic() = 0; [[nodiscard]] virtual nsresult VerifyTraffic() = 0;
virtual void BlacklistSpdy(const nsHttpConnectionInfo* ci) = 0; virtual void ExcludeHttp2(const nsHttpConnectionInfo* ci) = 0;
virtual void ExcludeHttp3(const nsHttpConnectionInfo* ci) = 0;
// clears the connection history mCT // clears the connection history mCT
[[nodiscard]] virtual nsresult ClearConnectionHistory() = 0; [[nodiscard]] virtual nsresult ClearConnectionHistory() = 0;
@ -216,7 +217,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(HttpConnectionMgrShell,
nsHttpConnectionInfo*, nsIInterfaceRequestor*, uint32_t caps = 0, \ nsHttpConnectionInfo*, nsIInterfaceRequestor*, uint32_t caps = 0, \
NullHttpTransaction* = nullptr) override; \ NullHttpTransaction* = nullptr) override; \
virtual nsresult VerifyTraffic() override; \ virtual nsresult VerifyTraffic() override; \
virtual void BlacklistSpdy(const nsHttpConnectionInfo* ci) override; \ virtual void ExcludeHttp2(const nsHttpConnectionInfo* ci) override; \
virtual void ExcludeHttp3(const nsHttpConnectionInfo* ci) override; \
virtual nsresult ClearConnectionHistory() override; \ virtual nsresult ClearConnectionHistory() override; \
virtual nsresult CompleteUpgrade(HttpTransactionShell* aTrans, \ virtual nsresult CompleteUpgrade(HttpTransactionShell* aTrans, \
nsIHttpUpgradeListener* aUpgradeListener) \ nsIHttpUpgradeListener* aUpgradeListener) \

View File

@ -392,17 +392,21 @@ nsresult TRRServiceChannel::BeginConnect() {
host, port, EmptyCString(), mUsername, GetTopWindowOrigin(), proxyInfo, host, port, EmptyCString(), mUsername, GetTopWindowOrigin(), proxyInfo,
OriginAttributes(), isHttps); OriginAttributes(), isHttps);
// TODO: Bug 1622778 for using AltService in socket process. // TODO: Bug 1622778 for using AltService in socket process.
mAllowAltSvc = XRE_IsParentProcess() && mAllowAltSvc = XRE_IsParentProcess() && mAllowAltSvc;
(mAllowAltSvc && !gHttpHandler->IsSpdyBlacklisted(connInfo)); bool http2Allowed = !gHttpHandler->IsHttp2Excluded(connInfo);
bool http3Allowed = !mUpgradeProtocolCallback && !mProxyInfo &&
!(mCaps & NS_HTTP_BE_CONSERVATIVE) && !mBeConservative &&
!gHttpHandler->IsHttp3Excluded(connInfo);
RefPtr<AltSvcMapping> mapping; RefPtr<AltSvcMapping> mapping;
if (!mConnectionInfo && mAllowAltSvc && // per channel if (!mConnectionInfo && mAllowAltSvc && // per channel
(http2Allowed || http3Allowed) &&
!(mLoadFlags & LOAD_FRESH_CONNECTION) && !(mLoadFlags & LOAD_FRESH_CONNECTION) &&
AltSvcMapping::AcceptableProxy(proxyInfo) && AltSvcMapping::AcceptableProxy(proxyInfo) &&
(scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) && (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) &&
(mapping = gHttpHandler->GetAltServiceMapping( (mapping = gHttpHandler->GetAltServiceMapping(
scheme, host, port, mPrivateBrowsing, IsIsolated(), scheme, host, port, mPrivateBrowsing, IsIsolated(),
GetTopWindowOrigin(), OriginAttributes(), false))) { GetTopWindowOrigin(), OriginAttributes(), http2Allowed, http3Allowed))) {
LOG(("TRRServiceChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n", LOG(("TRRServiceChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n",
this, scheme.get(), mapping->AlternateHost().get(), this, scheme.get(), mapping->AlternateHost().get(),
mapping->AlternatePort(), mapping->HashKey().get())); mapping->AlternatePort(), mapping->HashKey().get()));
@ -438,7 +442,7 @@ nsresult TRRServiceChannel::BeginConnect() {
// Need to re-ask the handler, since mConnectionInfo may not be the connInfo // Need to re-ask the handler, since mConnectionInfo may not be the connInfo
// we used earlier // we used earlier
if (gHttpHandler->IsSpdyBlacklisted(mConnectionInfo)) { if (gHttpHandler->IsHttp2Excluded(mConnectionInfo)) {
mAllowSpdy = 0; mAllowSpdy = 0;
mCaps |= NS_HTTP_DISALLOW_SPDY; mCaps |= NS_HTTP_DISALLOW_SPDY;
mConnectionInfo->SetNoSpdy(true); mConnectionInfo->SetNoSpdy(true);

View File

@ -6895,9 +6895,10 @@ nsresult nsHttpChannel::BeginConnect() {
RefPtr<nsHttpConnectionInfo> connInfo = new nsHttpConnectionInfo( RefPtr<nsHttpConnectionInfo> connInfo = new nsHttpConnectionInfo(
host, port, EmptyCString(), mUsername, GetTopWindowOrigin(), proxyInfo, host, port, EmptyCString(), mUsername, GetTopWindowOrigin(), proxyInfo,
originAttributes, isHttps); originAttributes, isHttps);
mAllowAltSvc = (mAllowAltSvc && !gHttpHandler->IsSpdyBlacklisted(connInfo)); bool http2Allowed = !gHttpHandler->IsHttp2Excluded(connInfo);
bool http3Allowed = !mUpgradeProtocolCallback && !mProxyInfo && bool http3Allowed = !mUpgradeProtocolCallback && !mProxyInfo &&
!(mCaps & NS_HTTP_BE_CONSERVATIVE) && !mBeConservative; !(mCaps & NS_HTTP_BE_CONSERVATIVE) && !mBeConservative &&
!gHttpHandler->IsHttp3Excluded(connInfo);
// No need to lookup HTTPSSVC record if we already have one. // No need to lookup HTTPSSVC record if we already have one.
mUseHTTPSSVC = mUseHTTPSSVC =
@ -6905,12 +6906,13 @@ nsresult nsHttpChannel::BeginConnect() {
RefPtr<AltSvcMapping> mapping; RefPtr<AltSvcMapping> mapping;
if (!mConnectionInfo && mAllowAltSvc && // per channel if (!mConnectionInfo && mAllowAltSvc && // per channel
(http2Allowed || http3Allowed) &&
!(mLoadFlags & LOAD_FRESH_CONNECTION) && !(mLoadFlags & LOAD_FRESH_CONNECTION) &&
AltSvcMapping::AcceptableProxy(proxyInfo) && AltSvcMapping::AcceptableProxy(proxyInfo) &&
(scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) && (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("https")) &&
(mapping = gHttpHandler->GetAltServiceMapping( (mapping = gHttpHandler->GetAltServiceMapping(
scheme, host, port, mPrivateBrowsing, IsIsolated(), scheme, host, port, mPrivateBrowsing, IsIsolated(),
GetTopWindowOrigin(), originAttributes, http3Allowed))) { GetTopWindowOrigin(), originAttributes, http2Allowed, http3Allowed))) {
LOG(("nsHttpChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n", this, LOG(("nsHttpChannel %p Alt Service Mapping Found %s://%s:%d [%s]\n", this,
scheme.get(), mapping->AlternateHost().get(), mapping->AlternatePort(), scheme.get(), mapping->AlternateHost().get(), mapping->AlternatePort(),
mapping->HashKey().get())); mapping->HashKey().get()));
@ -6966,7 +6968,7 @@ nsresult nsHttpChannel::BeginConnect() {
// Need to re-ask the handler, since mConnectionInfo may not be the connInfo // Need to re-ask the handler, since mConnectionInfo may not be the connInfo
// we used earlier // we used earlier
if (gHttpHandler->IsSpdyBlacklisted(mConnectionInfo)) { if (gHttpHandler->IsHttp2Excluded(mConnectionInfo)) {
mAllowSpdy = 0; mAllowSpdy = 0;
mCaps |= NS_HTTP_DISALLOW_SPDY; mCaps |= NS_HTTP_DISALLOW_SPDY;
mConnectionInfo->SetNoSpdy(true); mConnectionInfo->SetNoSpdy(true);

View File

@ -2060,7 +2060,7 @@ nsresult nsHttpConnectionMgr::ProcessNewTransaction(nsHttpTransaction* trans) {
trans->SetConnection(nullptr); trans->SetConnection(nullptr);
rv = DispatchTransaction(ent, trans, conn); rv = DispatchTransaction(ent, trans, conn);
} else { } else {
if (!ent->AllowSpdy()) { if (!ent->AllowHttp2()) {
trans->DisableSpdy(); trans->DisableSpdy();
} }
pendingTransInfo = new PendingTransactionInfo(trans); pendingTransInfo = new PendingTransactionInfo(trans);
@ -5263,7 +5263,7 @@ nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport* trans,
if (status == NS_NET_STATUS_CONNECTING_TO && gHttpHandler->IsSpdyEnabled() && if (status == NS_NET_STATUS_CONNECTING_TO && gHttpHandler->IsSpdyEnabled() &&
gHttpHandler->CoalesceSpdy() && mEnt && mEnt->mConnInfo && gHttpHandler->CoalesceSpdy() && mEnt && mEnt->mConnInfo &&
mEnt->mConnInfo->EndToEndSSL() && mEnt->AllowSpdy() && mEnt->mConnInfo->EndToEndSSL() && mEnt->AllowHttp2() &&
!mEnt->mConnInfo->UsingProxy() && mEnt->mCoalescingKeys.IsEmpty()) { !mEnt->mConnInfo->UsingProxy() && mEnt->mCoalescingKeys.IsEmpty()) {
nsCOMPtr<nsIDNSAddrRecord> dnsRecord(do_GetInterface(mSocketTransport)); nsCOMPtr<nsIDNSAddrRecord> dnsRecord(do_GetInterface(mSocketTransport));
nsTArray<NetAddr> addressSet; nsTArray<NetAddr> addressSet;
@ -5544,19 +5544,31 @@ void nsHttpConnectionMgr::nsConnectionEntry::RemoveHalfOpen(
} }
} }
void nsHttpConnectionMgr::BlacklistSpdy(const nsHttpConnectionInfo* ci) { void nsHttpConnectionMgr::ExcludeHttp2(const nsHttpConnectionInfo* ci) {
LOG(("nsHttpConnectionMgr::BlacklistSpdy blacklisting ci %s", LOG(("nsHttpConnectionMgr::ExcludeHttp2 excluding ci %s",
ci->HashKey().BeginReading())); ci->HashKey().BeginReading()));
nsConnectionEntry* ent = mCT.GetWeak(ci->HashKey()); nsConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
if (!ent) { if (!ent) {
LOG(("nsHttpConnectionMgr::BlacklistSpdy no entry found?!")); LOG(("nsHttpConnectionMgr::ExcludeHttp2 no entry found?!"));
return; return;
} }
ent->DisallowSpdy(); ent->DisallowHttp2();
} }
void nsHttpConnectionMgr::nsConnectionEntry::DisallowSpdy() { void nsHttpConnectionMgr::ExcludeHttp3(const nsHttpConnectionInfo* ci) {
LOG(("nsHttpConnectionMgr::ExcludeHttp3 exclude ci %s",
ci->HashKey().BeginReading()));
nsConnectionEntry* ent = mCT.GetWeak(ci->HashKey());
if (!ent) {
LOG(("nsHttpConnectionMgr::ExcludeHttp3 no entry found?!"));
return;
}
ent->DontReuseHttp3Conn();
}
void nsHttpConnectionMgr::nsConnectionEntry::DisallowHttp2() {
mCanUseSpdy = false; mCanUseSpdy = false;
// If we have any spdy connections, we want to go ahead and close them when // If we have any spdy connections, we want to go ahead and close them when
@ -5576,6 +5588,19 @@ void nsHttpConnectionMgr::nsConnectionEntry::DisallowSpdy() {
mCoalescingKeys.Clear(); mCoalescingKeys.Clear();
} }
void nsHttpConnectionMgr::nsConnectionEntry::DontReuseHttp3Conn() {
MOZ_ASSERT(mConnInfo->IsHttp3());
// If we have any spdy connections, we want to go ahead and close them when
// they're done so we can free up some connections.
for (uint32_t i = 0; i < mActiveConns.Length(); ++i) {
mActiveConns[i]->DontReuse();
}
// Can't coalesce if we're not using http3
mCoalescingKeys.Clear();
}
void nsHttpConnectionMgr::nsConnectionEntry::RecordIPFamilyPreference( void nsHttpConnectionMgr::nsConnectionEntry::RecordIPFamilyPreference(
uint16_t family) { uint16_t family) {
LOG(("nsConnectionEntry::RecordIPFamilyPreference %p, af=%u", this, family)); LOG(("nsConnectionEntry::RecordIPFamilyPreference %p, af=%u", this, family));

View File

@ -235,8 +235,9 @@ class nsHttpConnectionMgr final : public HttpConnectionMgrShell,
bool mDoNotDestroy : 1; bool mDoNotDestroy : 1;
bool AllowSpdy() const { return mCanUseSpdy; } bool AllowHttp2() const { return mCanUseSpdy; }
void DisallowSpdy(); void DisallowHttp2();
void DontReuseHttp3Conn();
// Set the IP family preference flags according the connected family // Set the IP family preference flags according the connected family
void RecordIPFamilyPreference(uint16_t family); void RecordIPFamilyPreference(uint16_t family);

View File

@ -316,7 +316,7 @@ nsHttpHandler::nsHttpHandler()
mNextChannelId(1), mNextChannelId(1),
mLastActiveTabLoadOptimizationLock( mLastActiveTabLoadOptimizationLock(
"nsHttpConnectionMgr::LastActiveTabLoadOptimization"), "nsHttpConnectionMgr::LastActiveTabLoadOptimization"),
mSpdyBlacklistLock("nsHttpHandler::SpdyBlacklist"), mHttpExclusionLock("nsHttpHandler::HttpExclusion"),
mThroughCaptivePortal(false) { mThroughCaptivePortal(false) {
LOG(("Creating nsHttpHandler [this=%p].\n", this)); LOG(("Creating nsHttpHandler [this=%p].\n", this));
@ -2796,19 +2796,34 @@ bool nsHttpHandler::IsBeforeLastActiveTabLoadOptimization(
when <= mLastActiveTabLoadOptimizationHit; when <= mLastActiveTabLoadOptimizationHit;
} }
void nsHttpHandler::BlacklistSpdy(const nsHttpConnectionInfo* ci) { void nsHttpHandler::ExcludeHttp2(const nsHttpConnectionInfo* ci) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread"); MOZ_ASSERT(OnSocketThread(), "not on socket thread");
mConnMgr->BlacklistSpdy(ci); mConnMgr->ExcludeHttp2(ci);
if (!mBlacklistedSpdyOrigins.Contains(ci->GetOrigin())) { if (!mExcludedHttp2Origins.Contains(ci->GetOrigin())) {
MutexAutoLock lock(mSpdyBlacklistLock); MutexAutoLock lock(mHttpExclusionLock);
mBlacklistedSpdyOrigins.PutEntry(ci->GetOrigin()); mExcludedHttp2Origins.PutEntry(ci->GetOrigin());
} }
} }
bool nsHttpHandler::IsSpdyBlacklisted(const nsHttpConnectionInfo* ci) { bool nsHttpHandler::IsHttp2Excluded(const nsHttpConnectionInfo* ci) {
MutexAutoLock lock(mSpdyBlacklistLock); MutexAutoLock lock(mHttpExclusionLock);
return mBlacklistedSpdyOrigins.Contains(ci->GetOrigin()); return mExcludedHttp2Origins.Contains(ci->GetOrigin());
}
void nsHttpHandler::ExcludeHttp3(const nsHttpConnectionInfo* ci) {
MOZ_ASSERT(OnSocketThread(), "not on socket thread");
mConnMgr->ExcludeHttp3(ci);
if (!mExcludedHttp3Origins.Contains(ci->GetOrigin())) {
MutexAutoLock lock(mHttpExclusionLock);
mExcludedHttp3Origins.PutEntry(ci->GetOrigin());
}
}
bool nsHttpHandler::IsHttp3Excluded(const nsHttpConnectionInfo* ci) {
MutexAutoLock lock(mHttpExclusionLock);
return mExcludedHttp3Origins.Contains(ci->GetOrigin());
} }
HttpTrafficAnalyzer* nsHttpHandler::GetHttpTrafficAnalyzer() { HttpTrafficAnalyzer* nsHttpHandler::GetHttpTrafficAnalyzer() {

View File

@ -359,10 +359,11 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
already_AddRefed<AltSvcMapping> GetAltServiceMapping( already_AddRefed<AltSvcMapping> GetAltServiceMapping(
const nsACString& scheme, const nsACString& host, int32_t port, bool pb, const nsACString& scheme, const nsACString& host, int32_t port, bool pb,
bool isolated, const nsACString& topWindowOrigin, bool isolated, const nsACString& topWindowOrigin,
const OriginAttributes& originAttributes, bool aHttp3Allowed) { const OriginAttributes& originAttributes, bool aHttp2Allowed,
bool aHttp3Allowed) {
return mAltSvcCache->GetAltServiceMapping(scheme, host, port, pb, isolated, return mAltSvcCache->GetAltServiceMapping(scheme, host, port, pb, isolated,
topWindowOrigin, originAttributes, topWindowOrigin, originAttributes,
aHttp3Allowed); aHttp2Allowed, aHttp3Allowed);
} }
// //
@ -833,7 +834,7 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
Mutex mLastActiveTabLoadOptimizationLock; Mutex mLastActiveTabLoadOptimizationLock;
TimeStamp mLastActiveTabLoadOptimizationHit; TimeStamp mLastActiveTabLoadOptimizationHit;
Mutex mSpdyBlacklistLock; Mutex mHttpExclusionLock;
public: public:
[[nodiscard]] nsresult NewChannelId(uint64_t& channelId); [[nodiscard]] nsresult NewChannelId(uint64_t& channelId);
@ -841,11 +842,14 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
void RemoveHttpChannel(uint64_t aId); void RemoveHttpChannel(uint64_t aId);
nsWeakPtr GetWeakHttpChannel(uint64_t aId); nsWeakPtr GetWeakHttpChannel(uint64_t aId);
void BlacklistSpdy(const nsHttpConnectionInfo* ci); void ExcludeHttp2(const nsHttpConnectionInfo* ci);
[[nodiscard]] bool IsSpdyBlacklisted(const nsHttpConnectionInfo* ci); [[nodiscard]] bool IsHttp2Excluded(const nsHttpConnectionInfo* ci);
void ExcludeHttp3(const nsHttpConnectionInfo* ci);
[[nodiscard]] bool IsHttp3Excluded(const nsHttpConnectionInfo* ci);
private: private:
nsTHashtable<nsCStringHashKey> mBlacklistedSpdyOrigins; nsTHashtable<nsCStringHashKey> mExcludedHttp2Origins;
nsTHashtable<nsCStringHashKey> mExcludedHttp3Origins;
bool mThroughCaptivePortal; bool mThroughCaptivePortal;

View File

@ -0,0 +1,125 @@
"use strict";
let httpsOrigin;
let h3AltSvc;
let h3Port;
let h3Route;
let prefs;
let tests = [test_https_alt_svc, testsDone];
let current_test = 0;
function run_next_test() {
if (current_test < tests.length) {
dump("starting test number " + current_test + "\n");
tests[current_test]();
current_test++;
}
}
function run_test() {
let env = Cc["@mozilla.org/process/environment;1"].getService(
Ci.nsIEnvironment
);
let h2Port = env.get("MOZHTTP2_PORT");
Assert.notEqual(h2Port, null);
Assert.notEqual(h2Port, "");
let h3Port = env.get("MOZHTTP3_PORT");
Assert.notEqual(h3Port, null);
Assert.notEqual(h3Port, "");
h3AltSvc = ":" + h3Port;
h3Route = "foo.example.com:" + h3Port;
do_get_profile();
prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
prefs.setBoolPref("network.http.http3.enabled", true);
prefs.setCharPref("network.dns.localDomains", "foo.example.com");
// The certificate for the http3server server is for foo.example.com and
// is signed by http2-ca.pem so add that cert to the trust list as a
// signing cert.
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
Ci.nsIX509CertDB
);
addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
httpsOrigin = "https://foo.example.com:" + h2Port + "/";
run_next_test();
}
function makeChan(uri) {
let chan = NetUtil.newChannel({
uri,
loadUsingSystemPrincipal: true,
}).QueryInterface(Ci.nsIHttpChannel);
chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
return chan;
}
let WaitForHttp3Listener = function() {};
WaitForHttp3Listener.prototype = {
onDataAvailableFired: false,
expectedRoute: "",
onStartRequest: function testOnStartRequest(request) {
Assert.ok(request instanceof Ci.nsIHttpChannel);
Assert.equal(request.responseStatus, 200);
},
onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
this.onDataAvailableFired = true;
read_stream(stream, cnt);
},
onStopRequest: function testOnStopRequest(request, status) {
let routed = "NA";
try {
routed = request.getRequestHeader("Alt-Used");
} catch (e) {}
dump("routed is " + routed + "\n");
if (routed == this.expectedRoute) {
let httpVersion = "";
try {
httpVersion = request.protocolVersion;
} catch (e) {}
Assert.equal(httpVersion, "h3");
run_next_test();
} else {
dump("poll later for alt svc mapping\n");
do_test_pending();
do_timeout(500, () => {
doTest(this.uri, this.expectedRoute, this.h3AltSvc);
});
}
do_test_finished();
},
};
function doTest(uri, expectedRoute, altSvc) {
let chan = makeChan(uri);
let listener = new WaitForHttp3Listener();
listener.uri = uri;
listener.expectedRoute = expectedRoute;
listener.h3AltSvc = altSvc;
chan.setRequestHeader("x-altsvc", altSvc, false);
chan.asyncOpen(listener);
}
// Test Alt-Svc for http3.
// H2 server returns alt-svc=h2=foo2.example.com:8000,h3-27=:h3port,h3-29=foo2.example.com:8443
function test_https_alt_svc() {
dump("test_https_alt_svc()\n");
do_test_pending();
doTest(httpsOrigin + "http3-test2", h3Route, h3AltSvc);
}
function testsDone() {
prefs.clearUserPref("network.http.http3.enabled");
prefs.clearUserPref("network.dns.localDomains");
dump("testDone\n");
}

View File

@ -447,5 +447,7 @@ skip-if = os == "android"
[test_blob_channelname.js] [test_blob_channelname.js]
[test_altsvc_pref.js] [test_altsvc_pref.js]
skip-if = asan || tsan || os == 'win' || os =='android' skip-if = asan || tsan || os == 'win' || os =='android'
[test_http3_alt_svc.js]
skip-if = asan || tsan || os == 'win' || os =='android'
[test_use_httpssvc.js] [test_use_httpssvc.js]
skip-if = os == "android" skip-if = os == "android"

View File

@ -553,6 +553,16 @@ function handleRequest(req, res) {
res.setHeader("Cache-Control", "no-cache"); res.setHeader("Cache-Control", "no-cache");
res.setHeader("Alt-Svc", "h3-27=" + req.headers["x-altsvc"]); res.setHeader("Alt-Svc", "h3-27=" + req.headers["x-altsvc"]);
} }
// for use with test_http3.js
else if (u.pathname === "/http3-test2") {
res.setHeader("Cache-Control", "no-cache");
res.setHeader(
"Alt-Svc",
"h2=foo2.example.com:8000,h3-27=" +
req.headers["x-altsvc"] +
",h3-29=foo2.example.com:8443"
);
}
// for use with test_trr.js // for use with test_trr.js
else if (u.pathname === "/dns-cname") { else if (u.pathname === "/dns-cname") {
// asking for cname.example.com // asking for cname.example.com

View File

@ -4,7 +4,7 @@ function altSvcCacheKeyIsolated(parsed) {
function altSvcPartitionKey(key) { function altSvcPartitionKey(key) {
let parts = key.split(":"); let parts = key.split(":");
return parts[parts.length - 1]; return parts[parts.length - 2];
} }
const gHttpHandler = Cc["@mozilla.org/network/protocol;1?name=http"].getService( const gHttpHandler = Cc["@mozilla.org/network/protocol;1?name=http"].getService(
@ -91,7 +91,7 @@ add_task(async function() {
await p; await p;
}); });
checkAltSvcCache([partitionKey2, partitionKey1]); checkAltSvcCache([partitionKey1, partitionKey2]);
info("Removing the tabs"); info("Removing the tabs");
BrowserTestUtils.removeTab(tab); BrowserTestUtils.removeTab(tab);