Skip to content

Commit

Permalink
Bug 1658362 - Add a pref to set alt-svc mapping artificially for test…
Browse files Browse the repository at this point in the history
…ing. r=JuniorHsu,necko-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D86589
  • Loading branch information
ddragana committed Aug 13, 2020
1 parent 123b418 commit 55f478c
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 10 deletions.
6 changes: 6 additions & 0 deletions modules/libpref/init/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -1447,6 +1447,12 @@ pref("network.http.http3.default-qpack-table-size", 65536); // 64k
// instructions.
pref("network.http.http3.default-max-stream-blocked", 20);


// This is only for testing!
// This adds alt-svc mapping and it has a form of <host-name>;<alt-svc-header>
// Example: example1.com;h3-29=":443",example2.com;h3-29=":443"
pref("network.http.http3.alt-svc-mapping-for-testing", "");

// alt-svc allows separation of transport routing from
// the origin host without using a proxy.
pref("network.http.altsvc.enabled", true);
Expand Down
28 changes: 26 additions & 2 deletions netwerk/protocol/http/AlternateServices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ void AltSvcMapping::ProcessHeader(
const nsCString& originHost, int32_t originPort, const nsACString& username,
const nsACString& topWindowOrigin, bool privateBrowsing, bool isolated,
nsIInterfaceRequestor* callbacks, nsProxyInfo* proxyInfo, uint32_t caps,
const OriginAttributes& originAttributes) {
const OriginAttributes& originAttributes,
bool aDontValidate /* = false */) { // aDontValidate is only used for
// testing
MOZ_ASSERT(NS_IsMainThread());
LOG(("AltSvcMapping::ProcessHeader: %s\n", buf.get()));

Expand Down Expand Up @@ -178,9 +180,12 @@ void AltSvcMapping::ProcessHeader(
// as that would have happened if we had accepted the parameters.
gHttpHandler->AltServiceCache()->ClearHostMapping(
originHost, originPort, originAttributes, topWindowOrigin);
} else {
} else if (!aDontValidate) {
gHttpHandler->UpdateAltServiceMapping(mapping, proxyInfo, callbacks, caps,
originAttributes);
} else {
gHttpHandler->UpdateAltServiceMappingWithoutValidation(
mapping, proxyInfo, callbacks, caps, originAttributes);
}
}

Expand Down Expand Up @@ -954,6 +959,25 @@ already_AddRefed<AltSvcMapping> AltSvcCache::LookupMapping(
return rv.forget();
}

// This is only used for testing!
void AltSvcCache::UpdateAltServiceMappingWithoutValidation(
AltSvcMapping* map, nsProxyInfo* pi, nsIInterfaceRequestor* aCallbacks,
uint32_t caps, const OriginAttributes& originAttributes) {
MOZ_ASSERT(NS_IsMainThread());
if (!mStorage) {
return;
}
RefPtr<AltSvcMapping> existing =
LookupMapping(map->HashKey(), map->Private());
LOG(
("AltSvcCache::UpdateAltServiceMappingWithoutValidation %p map %p "
"existing %p %s",
this, map, existing.get(), map->AlternateHost().get()));
if (!existing) {
map->SetValidated(true);
}
}

void AltSvcCache::UpdateAltServiceMapping(
AltSvcMapping* map, nsProxyInfo* pi, nsIInterfaceRequestor* aCallbacks,
uint32_t caps, const OriginAttributes& originAttributes) {
Expand Down
20 changes: 12 additions & 8 deletions netwerk/protocol/http/AlternateServices.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ class AltSvcMapping {
AltSvcMapping(DataStorage* storage, int32_t storageEpoch,
const nsCString& serialized);

static void ProcessHeader(const nsCString& buf, const nsCString& originScheme,
const nsCString& originHost, int32_t originPort,
const nsACString& username,
const nsACString& topWindowOrigin,
bool privateBrowsing, bool isolated,
nsIInterfaceRequestor* callbacks,
nsProxyInfo* proxyInfo, uint32_t caps,
const OriginAttributes& originAttributes);
static void ProcessHeader(
const nsCString& buf, const nsCString& originScheme,
const nsCString& originHost, int32_t originPort,
const nsACString& username, const nsACString& topWindowOrigin,
bool privateBrowsing, bool isolated, nsIInterfaceRequestor* callbacks,
nsProxyInfo* proxyInfo, uint32_t caps,
const OriginAttributes& originAttributes,
bool aDontValidate = false); // aDontValidate is only used for testing!

// AcceptableProxy() decides whether a particular proxy configuration (pi) is
// suitable for use with Alt-Svc. No proxy (including a null pi) is suitable.
Expand Down Expand Up @@ -191,6 +191,10 @@ class AltSvcCache {
AltSvcMapping* map, nsProxyInfo* pi, nsIInterfaceRequestor*,
uint32_t caps,
const OriginAttributes& originAttributes); // main thread
void UpdateAltServiceMappingWithoutValidation(
AltSvcMapping* map, nsProxyInfo* pi, nsIInterfaceRequestor*,
uint32_t caps,
const OriginAttributes& originAttributes); // main thread
already_AddRefed<AltSvcMapping> GetAltServiceMapping(
const nsACString& scheme, const nsACString& host, int32_t port, bool pb,
bool isolated, const nsACString& topWindowOrigin,
Expand Down
4 changes: 4 additions & 0 deletions netwerk/protocol/http/nsHttpChannel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6806,6 +6806,10 @@ nsresult nsHttpChannel::BeginConnect() {
this, originAttributes);
}

gHttpHandler->MaybeAddAltSvcForTesting(mURI, mUsername, GetTopWindowOrigin(),
mPrivateBrowsing, IsIsolated(),
mCallbacks, originAttributes);

RefPtr<nsHttpConnectionInfo> connInfo = new nsHttpConnectionInfo(
host, port, EmptyCString(), mUsername, GetTopWindowOrigin(), proxyInfo,
originAttributes, isHttps);
Expand Down
50 changes: 50 additions & 0 deletions netwerk/protocol/http/nsHttpHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1993,6 +1993,23 @@ void nsHttpHandler::PrefsChanged(const char* pref) {
}
}

if (PREF_CHANGED(HTTP_PREF("http3.alt-svc-mapping-for-testing"))) {
nsAutoCString altSvcMappings;
rv = Preferences::GetCString(HTTP_PREF("http3.alt-svc-mapping-for-testing"),
altSvcMappings);
if (NS_SUCCEEDED(rv)) {
nsCCharSeparatedTokenizer tokenizer(altSvcMappings, ',');
while (tokenizer.hasMoreTokens()) {
nsAutoCString token(tokenizer.nextToken());
int32_t index = token.Find(";");
if (index != kNotFound) {
auto* map = new nsCString(Substring(token, index + 1));
mAltSvcMappingTemptativeMap.Put(Substring(token, 0, index), map);
}
}
}
}

// Enable HTTP response timeout if TCP Keepalives are disabled.
mResponseTimeoutEnabled =
!mTCPKeepaliveShortLivedEnabled && !mTCPKeepaliveLongLivedEnabled;
Expand Down Expand Up @@ -2914,4 +2931,37 @@ void nsHttpHandler::SetDeviceModelId(const nsCString& aModelId) {
mDeviceModelId = aModelId;
}

void nsHttpHandler::MaybeAddAltSvcForTesting(
nsIURI* aUri, const nsACString& aUsername,
const nsACString& aTopWindowOrigin, bool aPrivateBrowsing, bool aIsolated,
nsIInterfaceRequestor* aCallbacks,
const OriginAttributes& aOriginAttributes) {
if (!IsHttp3Enabled() || mAltSvcMappingTemptativeMap.IsEmpty()) {
return;
}

bool isHttps = false;
if (NS_FAILED(aUri->SchemeIs("https", &isHttps)) || !isHttps) {
// Only set forr HTTPS.
return;
}

nsAutoCString originHost;
if (NS_FAILED(aUri->GetAsciiHost(originHost))) {
return;
}

nsCString* map = mAltSvcMappingTemptativeMap.Get(originHost);
if (map) {
int32_t originPort = 80;
aUri->GetPort(&originPort);
LOG(("nsHttpHandler::MaybeAddAltSvcForTesting for %s map: %s",
originHost.get(), PromiseFlatCString(*map).get()));
AltSvcMapping::ProcessHeader(*map, nsCString("https"), originHost,
originPort, aUsername, aTopWindowOrigin,
aPrivateBrowsing, aIsolated, aCallbacks,
nullptr, 0, aOriginAttributes, true);
}
}

} // namespace mozilla::net
19 changes: 19 additions & 0 deletions netwerk/protocol/http/nsHttpHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,14 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
originAttributes);
}

void UpdateAltServiceMappingWithoutValidation(
AltSvcMapping* map, nsProxyInfo* proxyInfo,
nsIInterfaceRequestor* callbacks, uint32_t caps,
const OriginAttributes& originAttributes) {
mAltSvcCache->UpdateAltServiceMappingWithoutValidation(
map, proxyInfo, callbacks, caps, originAttributes);
}

already_AddRefed<AltSvcMapping> GetAltServiceMapping(
const nsACString& scheme, const nsACString& host, int32_t port, bool pb,
bool isolated, const nsACString& topWindowOrigin,
Expand Down Expand Up @@ -488,6 +496,12 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,
nsresult DoShiftReloadConnectionCleanupWithConnInfo(
nsHttpConnectionInfo* aCI);

void MaybeAddAltSvcForTesting(nsIURI* aUri, const nsACString& aUsername,
const nsACString& aTopWindowOrigin,
bool aPrivateBrowsing, bool aIsolated,
nsIInterfaceRequestor* aCallbacks,
const OriginAttributes& aOriginAttributes);

private:
nsHttpHandler();

Expand Down Expand Up @@ -835,6 +849,11 @@ class nsHttpHandler final : public nsIHttpProtocolHandler,

// The mapping of channel id and the weak pointer of nsHttpChannel.
nsDataHashtable<nsUint64HashKey, nsWeakPtr> mIDToHttpChannelMap;

// This is parsed pref network.http.http3.alt-svc-mapping-for-testing.
// The pref set artificial altSvc-s for origin for testing.
// This maps an origin to an altSvc.
nsClassHashtable<nsCStringHashKey, nsCString> mAltSvcMappingTemptativeMap;
};

extern StaticRefPtr<nsHttpHandler> gHttpHandler;
Expand Down
140 changes: 140 additions & 0 deletions netwerk/test/unit/test_altsvc_pref.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
"use strict";

let h3Port;
let h3Route;
let h3AltSvc;
let prefs;
let httpsOrigin;

let tests = [
// The altSvc storage may not be up imediately, therefore run test_no_altsvc_pref
// for a couple times to wait for the storage.
test_no_altsvc_pref,
test_no_altsvc_pref,
test_no_altsvc_pref,
test_altsvc_pref,
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
);
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");
prefs.setBoolPref("network.dns.disableIPv6", true);

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

run_next_test();
}

let Http3CheckListener = function() {};

Http3CheckListener.prototype = {
expectedRoute: "",
expectedStatus: Cr.NS_OK,

onStartRequest: function testOnStartRequest(request) {
Assert.ok(request instanceof Ci.nsIHttpChannel);
Assert.equal(request.status, this.expectedStatus);
if (Components.isSuccessCode(this.expectedStatus)) {
Assert.equal(request.responseStatus, 200);
}
},

onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
read_stream(stream, cnt);
},

onStopRequest: function testOnStopRequest(request, status) {
Assert.equal(status, this.expectedStatus);
if (Components.isSuccessCode(this.expectedStatus)) {
Assert.equal(request.responseStatus, 200);
let routed = "NA";
try {
routed = request.getRequestHeader("Alt-Used");
} catch (e) {}
dump("routed is " + routed + "\n");

Assert.equal(routed, this.expectedRoute);

let httpVersion = "";
try {
httpVersion = request.protocolVersion;
} catch (e) {}
Assert.equal(httpVersion, "h3");
}

run_next_test();
do_test_finished();
},
};

function makeChan(uri) {
let chan = NetUtil.newChannel({
uri,
loadUsingSystemPrincipal: true,
}).QueryInterface(Ci.nsIHttpChannel);
chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
return chan;
}

function test_no_altsvc_pref() {
dump("test_no_altsvc_pref");
do_test_pending();

let chan = makeChan(httpsOrigin + "http3-test");
let listener = new Http3CheckListener();
listener.expectedStatus = Cr.NS_ERROR_CONNECTION_REFUSED;
chan.asyncOpen(listener);
}

function test_altsvc_pref() {
dump("test_altsvc_pref");
do_test_pending();

prefs.setCharPref(
"network.http.http3.alt-svc-mapping-for-testing",
"foo.example.com;h3-27=" + h3AltSvc
);

let chan = makeChan(httpsOrigin + "http3-test");
let listener = new Http3CheckListener();
listener.expectedRoute = h3Route;
chan.asyncOpen(listener);
}

function testsDone() {
prefs.clearUserPref("network.http.http3.enabled");
prefs.clearUserPref("network.dns.localDomains");
prefs.clearUserPref("network.dns.disableIPv6");
prefs.clearUserPref("network.http.http3.alt-svc-mapping-for-testing");
dump("testDone\n");
}
2 changes: 2 additions & 0 deletions netwerk/test/unit/xpcshell.ini
Original file line number Diff line number Diff line change
Expand Up @@ -445,3 +445,5 @@ skip-if = os == "android"
skip-if = os == "android"
[test_http_sfv.js]
[test_blob_channelname.js]
[test_altsvc_pref.js]
skip-if = asan || tsan || os == 'win' || os =='android'

0 comments on commit 55f478c

Please sign in to comment.