mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1866218 - Use the latest version of HTTP/3 from Alt-Svc headers, r=necko-reviewers,valentin
Make Firefox use the latest HTTP/3 version listed in Alt-Svc headers, rather than the first one. Differential Revision: https://phabricator.services.mozilla.com/D203235
This commit is contained in:
parent
72866aa04b
commit
8d17acbe60
@ -7,6 +7,7 @@
|
||||
#include "HttpLog.h"
|
||||
|
||||
#include "AlternateServices.h"
|
||||
#include <algorithm>
|
||||
#include "LoadInfo.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/StaticPrefs_network.h"
|
||||
@ -87,16 +88,15 @@ void AltSvcMapping::ProcessHeader(
|
||||
ParsedHeaderValueListList parsedAltSvc(buf);
|
||||
int32_t numEntriesInHeader = parsedAltSvc.mValues.Length();
|
||||
|
||||
// Only use one http3 version.
|
||||
bool http3Found = false;
|
||||
|
||||
nsTArray<RefPtr<AltSvcMapping>> h3Mappings;
|
||||
nsTArray<RefPtr<AltSvcMapping>> otherMappings;
|
||||
for (uint32_t index = 0; index < parsedAltSvc.mValues.Length(); ++index) {
|
||||
uint32_t maxage = 86400; // default
|
||||
nsAutoCString hostname;
|
||||
nsAutoCString npnToken;
|
||||
int32_t portno = originPort;
|
||||
bool clearEntry = false;
|
||||
bool isHttp3 = false;
|
||||
SupportedAlpnRank alpnRank = SupportedAlpnRank::NOT_SUPPORTED;
|
||||
|
||||
for (uint32_t pairIndex = 0;
|
||||
pairIndex < parsedAltSvc.mValues[index].mValues.Length();
|
||||
@ -116,7 +116,7 @@ void AltSvcMapping::ProcessHeader(
|
||||
|
||||
// h2=[hostname]:443 or h3-xx=[hostname]:port
|
||||
// XX is current version we support and it is define in nsHttp.h.
|
||||
isHttp3 = gHttpHandler->IsHttp3VersionSupported(currentName);
|
||||
alpnRank = IsAlpnSupported(currentName);
|
||||
npnToken = currentName;
|
||||
|
||||
int32_t colonIndex = currentValue.FindChar(':');
|
||||
@ -153,12 +153,7 @@ void AltSvcMapping::ProcessHeader(
|
||||
// update nsCString length
|
||||
nsUnescape(npnToken.BeginWriting());
|
||||
npnToken.SetLength(strlen(npnToken.BeginReading()));
|
||||
|
||||
if (http3Found && isHttp3) {
|
||||
LOG(("Alt Svc ignore multiple Http3 options (%s)", npnToken.get()));
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isHttp3 = net::IsHttp3(alpnRank);
|
||||
SpdyInformation* spdyInfo = gHttpHandler->SpdyInfo();
|
||||
if (!(npnToken.Equals(spdyInfo->VersionString) &&
|
||||
StaticPrefs::network_http_http2_enabled()) &&
|
||||
@ -169,16 +164,13 @@ void AltSvcMapping::ProcessHeader(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isHttp3) {
|
||||
http3Found = true;
|
||||
}
|
||||
|
||||
RefPtr<AltSvcMapping> mapping =
|
||||
new AltSvcMapping(gHttpHandler->AltServiceCache()->GetStoragePtr(),
|
||||
gHttpHandler->AltServiceCache()->StorageEpoch(),
|
||||
originScheme, originHost, originPort, username,
|
||||
privateBrowsing, NowInSeconds() + maxage, hostname,
|
||||
portno, npnToken, originAttributes, isHttp3);
|
||||
LOG(("AltSvcMapping created npnToken=%s", npnToken.get()));
|
||||
RefPtr<AltSvcMapping> mapping = new AltSvcMapping(
|
||||
gHttpHandler->AltServiceCache()->GetStoragePtr(),
|
||||
gHttpHandler->AltServiceCache()->StorageEpoch(), originScheme,
|
||||
originHost, originPort, username, privateBrowsing,
|
||||
NowInSeconds() + maxage, hostname, portno, npnToken, originAttributes,
|
||||
isHttp3, alpnRank);
|
||||
if (mapping->TTL() <= 0) {
|
||||
LOG(("Alt Svc invalid map"));
|
||||
mapping = nullptr;
|
||||
@ -186,15 +178,39 @@ void AltSvcMapping::ProcessHeader(
|
||||
// as that would have happened if we had accepted the parameters.
|
||||
gHttpHandler->AltServiceCache()->ClearHostMapping(originHost, originPort,
|
||||
originAttributes);
|
||||
} else if (!aDontValidate) {
|
||||
gHttpHandler->UpdateAltServiceMapping(mapping, proxyInfo, callbacks, caps,
|
||||
originAttributes);
|
||||
} else {
|
||||
gHttpHandler->UpdateAltServiceMappingWithoutValidation(
|
||||
mapping, proxyInfo, callbacks, caps, originAttributes);
|
||||
if (isHttp3) {
|
||||
h3Mappings.AppendElement(std::move(mapping));
|
||||
} else {
|
||||
otherMappings.AppendElement(std::move(mapping));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto doUpdateAltSvcMapping = [&](AltSvcMapping* aMapping) {
|
||||
if (!aDontValidate) {
|
||||
gHttpHandler->UpdateAltServiceMapping(aMapping, proxyInfo, callbacks,
|
||||
caps, originAttributes);
|
||||
} else {
|
||||
gHttpHandler->UpdateAltServiceMappingWithoutValidation(
|
||||
aMapping, proxyInfo, callbacks, caps, originAttributes);
|
||||
}
|
||||
};
|
||||
|
||||
if (!h3Mappings.IsEmpty()) {
|
||||
// Select the HTTP/3 (h3) AltSvcMapping with the highest ALPN rank from
|
||||
// h3Mappings.
|
||||
RefPtr<AltSvcMapping> latestH3Mapping = *std::max_element(
|
||||
h3Mappings.begin(), h3Mappings.end(),
|
||||
[](const RefPtr<AltSvcMapping>& a, const RefPtr<AltSvcMapping>& b) {
|
||||
return a->AlpnRank() < b->AlpnRank();
|
||||
});
|
||||
doUpdateAltSvcMapping(latestH3Mapping);
|
||||
}
|
||||
|
||||
std::for_each(otherMappings.begin(), otherMappings.end(),
|
||||
doUpdateAltSvcMapping);
|
||||
|
||||
if (numEntriesInHeader) { // Ignore headers that were just "alt-svc: clear"
|
||||
Telemetry::Accumulate(Telemetry::HTTP_ALTSVC_ENTRIES_PER_HEADER,
|
||||
numEntriesInHeader);
|
||||
@ -209,7 +225,7 @@ AltSvcMapping::AltSvcMapping(nsIDataStorage* storage, int32_t epoch,
|
||||
const nsACString& alternateHost,
|
||||
int32_t alternatePort, const nsACString& npnToken,
|
||||
const OriginAttributes& originAttributes,
|
||||
bool aIsHttp3)
|
||||
bool aIsHttp3, SupportedAlpnRank aRank)
|
||||
: mStorage(storage),
|
||||
mStorageEpoch(epoch),
|
||||
mAlternateHost(alternateHost),
|
||||
@ -221,7 +237,8 @@ AltSvcMapping::AltSvcMapping(nsIDataStorage* storage, int32_t epoch,
|
||||
mExpiresAt(expiresAt),
|
||||
mNPNToken(npnToken),
|
||||
mOriginAttributes(originAttributes),
|
||||
mIsHttp3(aIsHttp3) {
|
||||
mIsHttp3(aIsHttp3),
|
||||
mAlpnRank(aRank) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_FAILED(SchemeIsHTTPS(originScheme, mHttps))) {
|
||||
|
@ -23,6 +23,7 @@ https://tools.ietf.org/html/draft-ietf-httpbis-alt-svc-06
|
||||
#ifndef mozilla_net_AlternateServices_h
|
||||
#define mozilla_net_AlternateServices_h
|
||||
|
||||
#include "nsHttp.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIDataStorage.h"
|
||||
@ -53,7 +54,8 @@ class AltSvcMapping {
|
||||
bool privateBrowsing, uint32_t expiresAt,
|
||||
const nsACString& alternateHost, int32_t alternatePort,
|
||||
const nsACString& npnToken,
|
||||
const OriginAttributes& originAttributes, bool aIsHttp3);
|
||||
const OriginAttributes& originAttributes, bool aIsHttp3,
|
||||
SupportedAlpnRank aRank);
|
||||
|
||||
public:
|
||||
AltSvcMapping(nsIDataStorage* storage, int32_t storageEpoch,
|
||||
@ -103,6 +105,7 @@ class AltSvcMapping {
|
||||
|
||||
bool IsHttp3() { return mIsHttp3; }
|
||||
const nsCString& NPNToken() const { return mNPNToken; }
|
||||
SupportedAlpnRank AlpnRank() const { return mAlpnRank; }
|
||||
|
||||
private:
|
||||
virtual ~AltSvcMapping() = default;
|
||||
@ -138,6 +141,7 @@ class AltSvcMapping {
|
||||
|
||||
bool mSyncOnlyOnSuccess{false};
|
||||
bool mIsHttp3{false};
|
||||
SupportedAlpnRank mAlpnRank{SupportedAlpnRank::NOT_SUPPORTED};
|
||||
};
|
||||
|
||||
class AltSvcOverride : public nsIInterfaceRequestor,
|
||||
|
@ -5,7 +5,7 @@ let h3AltSvc;
|
||||
let h3Route;
|
||||
let prefs;
|
||||
|
||||
let tests = [test_https_alt_svc, testsDone];
|
||||
let tests = [test_https_alt_svc, test_https_alt_svc_1, testsDone];
|
||||
|
||||
let current_test = 0;
|
||||
|
||||
@ -68,7 +68,9 @@ function makeChan(uri) {
|
||||
return chan;
|
||||
}
|
||||
|
||||
let WaitForHttp3Listener = function () {};
|
||||
let WaitForHttp3Listener = function (expectedH3Version) {
|
||||
this._expectedH3Version = expectedH3Version;
|
||||
};
|
||||
|
||||
WaitForHttp3Listener.prototype = {
|
||||
onDataAvailableFired: false,
|
||||
@ -96,13 +98,18 @@ WaitForHttp3Listener.prototype = {
|
||||
try {
|
||||
httpVersion = request.protocolVersion;
|
||||
} catch (e) {}
|
||||
Assert.equal(httpVersion, "h3-29");
|
||||
Assert.equal(httpVersion, this._expectedH3Version);
|
||||
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);
|
||||
doTest(
|
||||
this.uri,
|
||||
this.expectedRoute,
|
||||
this.h3AltSvc,
|
||||
this._expectedH3Version
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@ -110,9 +117,9 @@ WaitForHttp3Listener.prototype = {
|
||||
},
|
||||
};
|
||||
|
||||
function doTest(uri, expectedRoute, altSvc) {
|
||||
function doTest(uri, expectedRoute, altSvc, expectedH3Version) {
|
||||
let chan = makeChan(uri);
|
||||
let listener = new WaitForHttp3Listener();
|
||||
let listener = new WaitForHttp3Listener(expectedH3Version);
|
||||
listener.uri = uri;
|
||||
listener.expectedRoute = expectedRoute;
|
||||
listener.h3AltSvc = altSvc;
|
||||
@ -121,11 +128,23 @@ function doTest(uri, expectedRoute, altSvc) {
|
||||
}
|
||||
|
||||
// Test Alt-Svc for http3.
|
||||
// H2 server returns alt-svc=h2=foo2.example.com:8000,h3-29=:h3port,h3-30=foo2.example.com:8443
|
||||
// H2 server returns alt-svc=h2=foo2.example.com:8000,h3-29=:h3port
|
||||
function test_https_alt_svc() {
|
||||
dump("test_https_alt_svc()\n");
|
||||
do_test_pending();
|
||||
doTest(httpsOrigin + "http3-test2", h3Route, h3AltSvc);
|
||||
doTest(httpsOrigin + "http3-test2", h3Route, h3AltSvc, "h3-29");
|
||||
}
|
||||
|
||||
// Test if we use the latest version of HTTP/3.
|
||||
// H2 server returns alt-svc=h3-29=:h3port,h3=:h3port
|
||||
function test_https_alt_svc_1() {
|
||||
// Close the previous connection and clear alt-svc mappings.
|
||||
Services.obs.notifyObservers(null, "last-pb-context-exited");
|
||||
Services.obs.notifyObservers(null, "net:cancel-all-connections");
|
||||
do_test_pending();
|
||||
do_timeout(1000, () => {
|
||||
doTest(httpsOrigin + "http3-test3", h3Route, h3AltSvc, "h3");
|
||||
});
|
||||
}
|
||||
|
||||
function testsDone() {
|
||||
|
@ -879,9 +879,13 @@ function handleRequest(req, res) {
|
||||
res.setHeader("Cache-Control", "no-cache");
|
||||
res.setHeader(
|
||||
"Alt-Svc",
|
||||
"h2=foo2.example.com:8000,h3-29=" +
|
||||
req.headers["x-altsvc"] +
|
||||
",h3-30=foo2.example.com:8443"
|
||||
"h2=foo2.example.com:8000,h3-29=" + req.headers["x-altsvc"]
|
||||
);
|
||||
} else if (u.pathname === "/http3-test3") {
|
||||
res.setHeader("Cache-Control", "no-cache");
|
||||
res.setHeader(
|
||||
"Alt-Svc",
|
||||
"h3-29=" + req.headers["x-altsvc"] + ",h3=" + req.headers["x-altsvc"]
|
||||
);
|
||||
}
|
||||
// for use with test_trr.js
|
||||
|
Loading…
Reference in New Issue
Block a user