"use strict";
var h2Port;
var prefs;
var http2pref;
var loadGroup;
function run_test() {
h2Port = Services.env.get(
"MOZHTTP2_PORT");
Assert.notEqual(h2Port,
null);
Assert.notEqual(h2Port,
"");
// Set to allow the cert presented by our H2 server
do_get_profile();
prefs = Services.prefs;
http2pref = prefs.getBoolPref(
"network.http.http2.enabled");
Services.prefs.setBoolPref(
"network.http.http2.allow-push",
true);
prefs.setBoolPref(
"network.http.http2.enabled",
true);
prefs.setCharPref(
"network.dns.localDomains",
"foo.example.com, alt1.example.com"
);
// The moz-http2 cert is for {foo, alt1, alt2}.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");
doTest1();
}
function resetPrefs() {
prefs.setBoolPref(
"network.http.http2.enabled", http2pref);
prefs.clearUserPref(
"network.dns.localDomains");
}
function makeChan(origin) {
return NetUtil.newChannel({
uri: origin,
loadUsingSystemPrincipal:
true,
}).QueryInterface(Ci.nsIHttpChannel);
}
let origin;
var nextTest;
var nextPortExpectedToBeSame =
false;
var currentPort = 0;
var forceReload =
false;
var forceFailListener =
false;
var Listener =
function () {};
Listener.prototype.clientPort = 0;
Listener.prototype = {
onStartRequest:
function testOnStartRequest(request) {
Assert.ok(request
instanceof Ci.nsIHttpChannel);
if (!Components.isSuccessCode(request.status)) {
do_throw(
"Channel should have a success code! (" + request.status +
")");
}
Assert.equal(request.responseStatus, 200);
this.clientPort = parseInt(request.getResponseHeader(
"x-client-port"));
},
onDataAvailable:
function testOnDataAvailable(request, stream, off, cnt) {
read_stream(stream, cnt);
},
onStopRequest:
function testOnStopRequest(request, status) {
Assert.ok(Components.isSuccessCode(status));
if (nextPortExpectedToBeSame) {
Assert.equal(currentPort,
this.clientPort);
}
else {
Assert.notEqual(currentPort,
this.clientPort);
}
currentPort =
this.clientPort;
nextTest();
do_test_finished();
},
};
var FailListener =
function () {};
FailListener.prototype = {
onStartRequest:
function testOnStartRequest(request) {
Assert.ok(request
instanceof Ci.nsIHttpChannel);
Assert.ok(!Components.isSuccessCode(request.status));
},
onDataAvailable:
function testOnDataAvailable(request, stream, off, cnt) {
read_stream(stream, cnt);
},
onStopRequest:
function testOnStopRequest(request) {
Assert.ok(!Components.isSuccessCode(request.status));
nextTest();
do_test_finished();
},
};
function testsDone() {
dump(
"testsDone\n");
resetPrefs();
}
function doTest() {
dump(
"execute doTest " + origin +
"\n");
var chan = makeChan(origin);
var listener;
if (!forceFailListener) {
listener =
new Listener();
}
else {
listener =
new FailListener();
}
forceFailListener =
false;
if (!forceReload) {
chan.loadFlags = Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
}
else {
chan.loadFlags =
Ci.nsIRequest.LOAD_FRESH_CONNECTION |
Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
}
forceReload =
false;
chan.asyncOpen(listener);
}
function doTest1() {
dump(
"doTest1()\n");
origin =
"https://foo.example.com:" + h2Port + "/origin-1";
nextTest = doTest2;
nextPortExpectedToBeSame =
false;
do_test_pending();
doTest();
}
function doTest2() {
// plain connection reuse
dump(
"doTest2()\n");
origin =
"https://foo.example.com:" + h2Port + "/origin-2";
nextTest = doTest3;
nextPortExpectedToBeSame =
true;
do_test_pending();
doTest();
}
function doTest3() {
// 7540 style coalescing
dump(
"doTest3()\n");
origin =
"https://alt1.example.com:" + h2Port + "/origin-3";
nextTest = doTest4;
nextPortExpectedToBeSame =
true;
do_test_pending();
doTest();
}
function doTest4() {
// forces an empty origin frame to be omitted
dump(
"doTest4()\n");
origin =
"https://foo.example.com:" + h2Port + "/origin-4";
nextTest = doTest5;
nextPortExpectedToBeSame =
true;
do_test_pending();
doTest();
}
function doTest5() {
// 7540 style coalescing should not work due to empty origin set
dump(
"doTest5()\n");
origin =
"https://alt1.example.com:" + h2Port + "/origin-5";
nextTest = doTest6;
nextPortExpectedToBeSame =
false;
do_test_pending();
doTest();
}
function doTest6() {
// get a fresh connection with alt1 and alt2 in origin set
// note that there is no dns for alt2
dump(
"doTest6()\n");
origin =
"https://foo.example.com:" + h2Port + "/origin-6";
nextTest = doTest7;
nextPortExpectedToBeSame =
false;
forceReload =
true;
do_test_pending();
doTest();
}
function doTest7() {
// check conn reuse to ensure sni is implicit in origin set
dump(
"doTest7()\n");
origin =
"https://foo.example.com:" + h2Port + "/origin-7";
nextTest = doTest8;
nextPortExpectedToBeSame =
true;
do_test_pending();
doTest();
}
function doTest8() {
// alt1 is in origin set (and is 7540 eligible)
dump(
"doTest8()\n");
origin =
"https://alt1.example.com:" + h2Port + "/origin-8";
nextTest = doTest9;
nextPortExpectedToBeSame =
true;
do_test_pending();
doTest();
}
function doTest9() {
// alt2 is in origin set but does not have dns
dump(
"doTest9()\n");
origin =
"https://alt2.example.com:" + h2Port + "/origin-9";
nextTest = doTest10;
nextPortExpectedToBeSame =
true;
do_test_pending();
doTest();
}
function doTest10() {
// bar is in origin set but does not have dns like alt2
// but the cert is not valid for bar. so expect a failure
dump(
"doTest10()\n");
origin =
"https://bar.example.com:" + h2Port + "/origin-10";
nextTest = doTest11;
nextPortExpectedToBeSame =
false;
forceFailListener =
true;
do_test_pending();
doTest();
}
var Http2PushApiListener =
function () {};
Http2PushApiListener.prototype = {
fooOK:
false,
alt1OK:
false,
getInterface(aIID) {
return this.QueryInterface(aIID);
},
QueryInterface: ChromeUtils.generateQI([
"nsIHttpPushListener",
"nsIStreamListener",
]),
// nsIHttpPushListener
onPush:
function onPush(associatedChannel, pushChannel) {
dump(
"push api onpush " +
pushChannel.originalURI.spec +
" associated to " +
associatedChannel.originalURI.spec +
"\n"
);
Assert.equal(
associatedChannel.originalURI.spec,
"https://foo.example.com:" + h2Port + "/origin-11-a"
);
Assert.equal(pushChannel.getRequestHeader(
"x-pushed-request"),
"true");
if (
pushChannel.originalURI.spec ===
"https://foo.example.com:" + h2Port + "/origin-11-b"
) {
this.fooOK =
true;
}
else if (
pushChannel.originalURI.spec ===
"https://alt1.example.com:" + h2Port + "/origin-11-e"
) {
this.alt1OK =
true;
}
else {
// any push of bar or madeup should not end up in onPush()
Assert.equal(
true,
false);
}
pushChannel.cancel(Cr.NS_ERROR_ABORT);
},
// normal Channel listeners
onStartRequest:
function pushAPIOnStart(request) {
dump(
"push api onstart " + request.originalURI.spec +
"\n");
},
onDataAvailable:
function pushAPIOnDataAvailable(
request,
stream,
offset,
cnt
) {
read_stream(stream, cnt);
},
onStopRequest:
function test_onStopR(request) {
dump(
"push api onstop " + request.originalURI.spec +
"\n");
Assert.ok(
this.fooOK);
Assert.ok(
this.alt1OK);
nextTest();
do_test_finished();
},
};
function doTest11() {
// we are connected with an SNI of foo from test6
// but the origin set is alt1, alt2, bar - foo is implied
// and bar is not actually covered by the cert
//
// the server will push foo (b-OK), bar (c-NOT OK), madeup (d-NOT OK), alt1 (e-OK),
dump(
"doTest11()\n");
do_test_pending();
loadGroup = Cc[
"@mozilla.org/network/load-group;1"].createInstance(
Ci.nsILoadGroup
);
var chan = makeChan(
"https://foo.example.com:" + h2Port + "/origin-11-a");
chan.loadGroup = loadGroup;
var listener =
new Http2PushApiListener();
nextTest = testsDone;
chan.notificationCallbacks = listener;
chan.asyncOpen(listener);
}