forked from mirrors/gecko-dev
1457 lines
44 KiB
JavaScript
1457 lines
44 KiB
JavaScript
// test HTTP/2
|
|
|
|
"use strict";
|
|
|
|
// Generate a small and a large post with known pre-calculated md5 sums
|
|
function generateContent(size) {
|
|
var content = "";
|
|
for (var i = 0; i < size; i++) {
|
|
content += "0";
|
|
}
|
|
return content;
|
|
}
|
|
|
|
var posts = [];
|
|
posts.push(generateContent(10));
|
|
posts.push(generateContent(250000));
|
|
posts.push(generateContent(128000));
|
|
|
|
// pre-calculated md5sums (in hex) of the above posts
|
|
var md5s = [
|
|
"f1b708bba17f1ce948dc979f4d7092bc",
|
|
"2ef8d3b6c8f329318eb1a119b12622b6",
|
|
];
|
|
|
|
var bigListenerData = generateContent(128 * 1024);
|
|
var bigListenerMD5 = "8f607cfdd2c87d6a7eedb657dafbd836";
|
|
|
|
function checkIsHttp2(request) {
|
|
try {
|
|
if (request.getResponseHeader("X-Firefox-Spdy") == "h2") {
|
|
if (request.getResponseHeader("X-Connection-Http2") == "yes") {
|
|
return true;
|
|
}
|
|
return false; // Weird case, but the server disagrees with us
|
|
}
|
|
} catch (e) {
|
|
// Nothing to do here
|
|
}
|
|
return false;
|
|
}
|
|
|
|
var Http2CheckListener = function() {};
|
|
|
|
Http2CheckListener.prototype = {
|
|
onStartRequestFired: false,
|
|
onDataAvailableFired: false,
|
|
isHttp2Connection: false,
|
|
shouldBeHttp2: true,
|
|
accum: 0,
|
|
expected: -1,
|
|
shouldSucceed: true,
|
|
|
|
onStartRequest: function testOnStartRequest(request) {
|
|
this.onStartRequestFired = true;
|
|
if (this.shouldSucceed && !Components.isSuccessCode(request.status)) {
|
|
do_throw("Channel should have a success code! (" + request.status + ")");
|
|
} else if (
|
|
!this.shouldSucceed &&
|
|
Components.isSuccessCode(request.status)
|
|
) {
|
|
do_throw("Channel succeeded unexpectedly!");
|
|
}
|
|
|
|
Assert.ok(request instanceof Ci.nsIHttpChannel);
|
|
Assert.equal(request.requestSucceeded, this.shouldSucceed);
|
|
if (this.shouldSucceed) {
|
|
Assert.equal(request.responseStatus, 200);
|
|
}
|
|
},
|
|
|
|
onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
|
|
this.onDataAvailableFired = true;
|
|
this.isHttp2Connection = checkIsHttp2(request);
|
|
this.accum += cnt;
|
|
read_stream(stream, cnt);
|
|
},
|
|
|
|
onStopRequest: function testOnStopRequest(request, status) {
|
|
Assert.ok(this.onStartRequestFired);
|
|
if (this.expected != -1) {
|
|
Assert.equal(this.accum, this.expected);
|
|
}
|
|
if (this.shouldSucceed) {
|
|
Assert.ok(Components.isSuccessCode(status));
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);
|
|
} else {
|
|
Assert.ok(!Components.isSuccessCode(status));
|
|
}
|
|
|
|
run_next_test();
|
|
do_test_finished();
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Support for testing valid multiplexing of streams
|
|
*/
|
|
|
|
var multiplexContent = generateContent(30 * 1024);
|
|
var completed_channels = [];
|
|
function register_completed_channel(listener) {
|
|
completed_channels.push(listener);
|
|
if (completed_channels.length == 2) {
|
|
Assert.notEqual(
|
|
completed_channels[0].streamID,
|
|
completed_channels[1].streamID
|
|
);
|
|
run_next_test();
|
|
do_test_finished();
|
|
}
|
|
}
|
|
|
|
/* Listener class to control the testing of multiplexing */
|
|
var Http2MultiplexListener = function() {};
|
|
|
|
Http2MultiplexListener.prototype = new Http2CheckListener();
|
|
|
|
Http2MultiplexListener.prototype.streamID = 0;
|
|
Http2MultiplexListener.prototype.buffer = "";
|
|
|
|
Http2MultiplexListener.prototype.onDataAvailable = function(
|
|
request,
|
|
stream,
|
|
off,
|
|
cnt
|
|
) {
|
|
this.onDataAvailableFired = true;
|
|
this.isHttp2Connection = checkIsHttp2(request);
|
|
this.streamID = parseInt(request.getResponseHeader("X-Http2-StreamID"));
|
|
var data = read_stream(stream, cnt);
|
|
this.buffer = this.buffer.concat(data);
|
|
};
|
|
|
|
Http2MultiplexListener.prototype.onStopRequest = function(request, status) {
|
|
Assert.ok(this.onStartRequestFired);
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.ok(this.isHttp2Connection);
|
|
Assert.ok(this.buffer == multiplexContent);
|
|
|
|
// This is what does most of the hard work for us
|
|
register_completed_channel(this);
|
|
};
|
|
|
|
// Does the appropriate checks for header gatewaying
|
|
var Http2HeaderListener = function(name, callback) {
|
|
this.name = name;
|
|
this.callback = callback;
|
|
};
|
|
|
|
Http2HeaderListener.prototype = new Http2CheckListener();
|
|
Http2HeaderListener.prototype.value = "";
|
|
|
|
Http2HeaderListener.prototype.onDataAvailable = function(
|
|
request,
|
|
stream,
|
|
off,
|
|
cnt
|
|
) {
|
|
this.onDataAvailableFired = true;
|
|
this.isHttp2Connection = checkIsHttp2(request);
|
|
var hvalue = request.getResponseHeader(this.name);
|
|
Assert.notEqual(hvalue, "");
|
|
this.callback(hvalue);
|
|
read_stream(stream, cnt);
|
|
};
|
|
|
|
var Http2PushListener = function(shouldBePushed) {
|
|
this.shouldBePushed = shouldBePushed;
|
|
};
|
|
|
|
Http2PushListener.prototype = new Http2CheckListener();
|
|
|
|
Http2PushListener.prototype.onDataAvailable = function(
|
|
request,
|
|
stream,
|
|
off,
|
|
cnt
|
|
) {
|
|
this.onDataAvailableFired = true;
|
|
this.isHttp2Connection = checkIsHttp2(request);
|
|
if (
|
|
request.originalURI.spec ==
|
|
"https://localhost:" + serverPort + "/push.js" ||
|
|
request.originalURI.spec ==
|
|
"https://localhost:" + serverPort + "/push2.js" ||
|
|
request.originalURI.spec == "https://localhost:" + serverPort + "/push5.js"
|
|
) {
|
|
Assert.equal(
|
|
request.getResponseHeader("pushed"),
|
|
this.shouldBePushed ? "yes" : "no"
|
|
);
|
|
}
|
|
read_stream(stream, cnt);
|
|
};
|
|
|
|
const pushHdrTxt =
|
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
const pullHdrTxt = pushHdrTxt
|
|
.split("")
|
|
.reverse()
|
|
.join("");
|
|
|
|
function checkContinuedHeaders(getHeader, headerPrefix, headerText) {
|
|
for (var i = 0; i < 265; i++) {
|
|
Assert.equal(getHeader(headerPrefix + 1), headerText);
|
|
}
|
|
}
|
|
|
|
var Http2ContinuedHeaderListener = function() {};
|
|
|
|
Http2ContinuedHeaderListener.prototype = new Http2CheckListener();
|
|
|
|
Http2ContinuedHeaderListener.prototype.onStopsLeft = 2;
|
|
|
|
Http2ContinuedHeaderListener.prototype.QueryInterface = ChromeUtils.generateQI([
|
|
"nsIHttpPushListener",
|
|
"nsIStreamListener",
|
|
]);
|
|
|
|
Http2ContinuedHeaderListener.prototype.getInterface = function(aIID) {
|
|
return this.QueryInterface(aIID);
|
|
};
|
|
|
|
Http2ContinuedHeaderListener.prototype.onDataAvailable = function(
|
|
request,
|
|
stream,
|
|
off,
|
|
cnt
|
|
) {
|
|
this.onDataAvailableFired = true;
|
|
this.isHttp2Connection = checkIsHttp2(request);
|
|
if (
|
|
request.originalURI.spec ==
|
|
"https://localhost:" + serverPort + "/continuedheaders"
|
|
) {
|
|
// This is the original request, so the only one where we'll have continued response headers
|
|
checkContinuedHeaders(
|
|
request.getResponseHeader,
|
|
"X-Pull-Test-Header-",
|
|
pullHdrTxt
|
|
);
|
|
}
|
|
read_stream(stream, cnt);
|
|
};
|
|
|
|
Http2ContinuedHeaderListener.prototype.onStopRequest = function(
|
|
request,
|
|
status
|
|
) {
|
|
Assert.ok(this.onStartRequestFired);
|
|
Assert.ok(Components.isSuccessCode(status));
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.ok(this.isHttp2Connection);
|
|
|
|
--this.onStopsLeft;
|
|
if (this.onStopsLeft === 0) {
|
|
run_next_test();
|
|
do_test_finished();
|
|
}
|
|
};
|
|
|
|
Http2ContinuedHeaderListener.prototype.onPush = function(
|
|
associatedChannel,
|
|
pushChannel
|
|
) {
|
|
Assert.equal(
|
|
associatedChannel.originalURI.spec,
|
|
"https://localhost:" + serverPort + "/continuedheaders"
|
|
);
|
|
Assert.equal(pushChannel.getRequestHeader("x-pushed-request"), "true");
|
|
checkContinuedHeaders(
|
|
pushChannel.getRequestHeader,
|
|
"X-Push-Test-Header-",
|
|
pushHdrTxt
|
|
);
|
|
|
|
pushChannel.asyncOpen(this);
|
|
};
|
|
|
|
// Does the appropriate checks for a large GET response
|
|
var Http2BigListener = function() {};
|
|
|
|
Http2BigListener.prototype = new Http2CheckListener();
|
|
Http2BigListener.prototype.buffer = "";
|
|
|
|
Http2BigListener.prototype.onDataAvailable = function(
|
|
request,
|
|
stream,
|
|
off,
|
|
cnt
|
|
) {
|
|
this.onDataAvailableFired = true;
|
|
this.isHttp2Connection = checkIsHttp2(request);
|
|
this.buffer = this.buffer.concat(read_stream(stream, cnt));
|
|
// We know the server should send us the same data as our big post will be,
|
|
// so the md5 should be the same
|
|
Assert.equal(bigListenerMD5, request.getResponseHeader("X-Expected-MD5"));
|
|
};
|
|
|
|
Http2BigListener.prototype.onStopRequest = function(request, status) {
|
|
Assert.ok(this.onStartRequestFired);
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.ok(this.isHttp2Connection);
|
|
|
|
// Don't want to flood output, so don't use do_check_eq
|
|
Assert.ok(this.buffer == bigListenerData);
|
|
|
|
run_next_test();
|
|
do_test_finished();
|
|
};
|
|
|
|
var Http2HugeSuspendedListener = function() {};
|
|
|
|
Http2HugeSuspendedListener.prototype = new Http2CheckListener();
|
|
Http2HugeSuspendedListener.prototype.count = 0;
|
|
|
|
Http2HugeSuspendedListener.prototype.onDataAvailable = function(
|
|
request,
|
|
stream,
|
|
off,
|
|
cnt
|
|
) {
|
|
this.onDataAvailableFired = true;
|
|
this.isHttp2Connection = checkIsHttp2(request);
|
|
this.count += cnt;
|
|
read_stream(stream, cnt);
|
|
};
|
|
|
|
Http2HugeSuspendedListener.prototype.onStopRequest = function(request, status) {
|
|
Assert.ok(this.onStartRequestFired);
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.ok(this.isHttp2Connection);
|
|
Assert.equal(this.count, 1024 * 1024 * 1); // 1mb of data expected
|
|
run_next_test();
|
|
do_test_finished();
|
|
};
|
|
|
|
// Does the appropriate checks for POSTs
|
|
var Http2PostListener = function(expected_md5) {
|
|
this.expected_md5 = expected_md5;
|
|
};
|
|
|
|
Http2PostListener.prototype = new Http2CheckListener();
|
|
Http2PostListener.prototype.expected_md5 = "";
|
|
|
|
Http2PostListener.prototype.onDataAvailable = function(
|
|
request,
|
|
stream,
|
|
off,
|
|
cnt
|
|
) {
|
|
this.onDataAvailableFired = true;
|
|
this.isHttp2Connection = checkIsHttp2(request);
|
|
read_stream(stream, cnt);
|
|
Assert.equal(
|
|
this.expected_md5,
|
|
request.getResponseHeader("X-Calculated-MD5")
|
|
);
|
|
};
|
|
|
|
function makeChan(url) {
|
|
return NetUtil.newChannel({
|
|
uri: url,
|
|
loadUsingSystemPrincipal: true,
|
|
}).QueryInterface(Ci.nsIHttpChannel);
|
|
}
|
|
|
|
var ResumeStalledChannelListener = function() {};
|
|
|
|
ResumeStalledChannelListener.prototype = {
|
|
onStartRequestFired: false,
|
|
onDataAvailableFired: false,
|
|
isHttp2Connection: false,
|
|
shouldBeHttp2: true,
|
|
resumable: null,
|
|
|
|
onStartRequest: function testOnStartRequest(request) {
|
|
this.onStartRequestFired = true;
|
|
if (!Components.isSuccessCode(request.status)) {
|
|
do_throw("Channel should have a success code! (" + request.status + ")");
|
|
}
|
|
|
|
Assert.ok(request instanceof Ci.nsIHttpChannel);
|
|
Assert.equal(request.responseStatus, 200);
|
|
Assert.equal(request.requestSucceeded, true);
|
|
},
|
|
|
|
onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
|
|
this.onDataAvailableFired = true;
|
|
this.isHttp2Connection = checkIsHttp2(request);
|
|
read_stream(stream, cnt);
|
|
},
|
|
|
|
onStopRequest: function testOnStopRequest(request, status) {
|
|
Assert.ok(this.onStartRequestFired);
|
|
Assert.ok(Components.isSuccessCode(status));
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);
|
|
this.resumable.resume();
|
|
},
|
|
};
|
|
|
|
// test a large download that creates stream flow control and
|
|
// confirm we can do another independent stream while the download
|
|
// stream is stuck
|
|
function test_http2_blocking_download() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/bigdownload");
|
|
var internalChannel = chan.QueryInterface(Ci.nsIHttpChannelInternal);
|
|
internalChannel.initialRwin = 500000; // make the stream.suspend push back in h2
|
|
var listener = new Http2CheckListener();
|
|
listener.expected = 3 * 1024 * 1024;
|
|
chan.asyncOpen(listener);
|
|
chan.suspend();
|
|
// wait 5 seconds so that stream flow control kicks in and then see if we
|
|
// can do a basic transaction (i.e. session not blocked). afterwards resume
|
|
// channel
|
|
do_timeout(5000, function() {
|
|
var simpleChannel = makeChan("https://localhost:" + serverPort + "/");
|
|
var sl = new ResumeStalledChannelListener();
|
|
sl.resumable = chan;
|
|
simpleChannel.asyncOpen(sl);
|
|
});
|
|
}
|
|
|
|
// Make sure we make a HTTP2 connection and both us and the server mark it as such
|
|
function test_http2_basic() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/");
|
|
var listener = new Http2CheckListener();
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_basic_unblocked_dep() {
|
|
var chan = makeChan(
|
|
"https://localhost:" + serverPort + "/basic_unblocked_dep"
|
|
);
|
|
var cos = chan.QueryInterface(Ci.nsIClassOfService);
|
|
cos.addClassFlags(Ci.nsIClassOfService.Unblocked);
|
|
var listener = new Http2CheckListener();
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
// make sure we don't use h2 when disallowed
|
|
function test_http2_nospdy() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/");
|
|
var listener = new Http2CheckListener();
|
|
var internalChannel = chan.QueryInterface(Ci.nsIHttpChannelInternal);
|
|
internalChannel.allowSpdy = false;
|
|
listener.shouldBeHttp2 = false;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
// Support for making sure XHR works over SPDY
|
|
function checkXhr(xhr) {
|
|
if (xhr.readyState != 4) {
|
|
return;
|
|
}
|
|
|
|
Assert.equal(xhr.status, 200);
|
|
Assert.equal(checkIsHttp2(xhr), true);
|
|
run_next_test();
|
|
do_test_finished();
|
|
}
|
|
|
|
// Fires off an XHR request over h2
|
|
function test_http2_xhr() {
|
|
var req = new XMLHttpRequest();
|
|
req.open("GET", "https://localhost:" + serverPort + "/", true);
|
|
req.addEventListener("readystatechange", function(evt) {
|
|
checkXhr(req);
|
|
});
|
|
req.send(null);
|
|
}
|
|
|
|
var concurrent_channels = [];
|
|
|
|
var Http2ConcurrentListener = function() {};
|
|
|
|
Http2ConcurrentListener.prototype = new Http2CheckListener();
|
|
Http2ConcurrentListener.prototype.count = 0;
|
|
Http2ConcurrentListener.prototype.target = 0;
|
|
Http2ConcurrentListener.prototype.reset = 0;
|
|
Http2ConcurrentListener.prototype.recvdHdr = 0;
|
|
|
|
Http2ConcurrentListener.prototype.onStopRequest = function(request, status) {
|
|
this.count++;
|
|
Assert.ok(this.isHttp2Connection);
|
|
if (this.recvdHdr > 0) {
|
|
Assert.equal(request.getResponseHeader("X-Recvd"), this.recvdHdr);
|
|
}
|
|
|
|
if (this.count == this.target) {
|
|
if (this.reset > 0) {
|
|
prefs.setIntPref("network.http.http2.default-concurrent", this.reset);
|
|
}
|
|
run_next_test();
|
|
do_test_finished();
|
|
}
|
|
};
|
|
|
|
function test_http2_concurrent() {
|
|
var concurrent_listener = new Http2ConcurrentListener();
|
|
concurrent_listener.target = 201;
|
|
concurrent_listener.reset = prefs.getIntPref(
|
|
"network.http.http2.default-concurrent"
|
|
);
|
|
prefs.setIntPref("network.http.http2.default-concurrent", 100);
|
|
|
|
for (var i = 0; i < concurrent_listener.target; i++) {
|
|
concurrent_channels[i] = makeChan(
|
|
"https://localhost:" + serverPort + "/750ms"
|
|
);
|
|
concurrent_channels[i].loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
|
concurrent_channels[i].asyncOpen(concurrent_listener);
|
|
}
|
|
}
|
|
|
|
function test_http2_concurrent_post() {
|
|
var concurrent_listener = new Http2ConcurrentListener();
|
|
concurrent_listener.target = 8;
|
|
concurrent_listener.recvdHdr = posts[2].length;
|
|
concurrent_listener.reset = prefs.getIntPref(
|
|
"network.http.http2.default-concurrent"
|
|
);
|
|
prefs.setIntPref("network.http.http2.default-concurrent", 3);
|
|
|
|
for (var i = 0; i < concurrent_listener.target; i++) {
|
|
concurrent_channels[i] = makeChan(
|
|
"https://localhost:" + serverPort + "/750msPost"
|
|
);
|
|
concurrent_channels[i].loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
|
var stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
|
|
Ci.nsIStringInputStream
|
|
);
|
|
stream.data = posts[2];
|
|
var uchan = concurrent_channels[i].QueryInterface(Ci.nsIUploadChannel);
|
|
uchan.setUploadStream(stream, "text/plain", stream.available());
|
|
concurrent_channels[i].requestMethod = "POST";
|
|
concurrent_channels[i].asyncOpen(concurrent_listener);
|
|
}
|
|
}
|
|
|
|
// Test to make sure we get multiplexing right
|
|
function test_http2_multiplex() {
|
|
var chan1 = makeChan("https://localhost:" + serverPort + "/multiplex1");
|
|
var chan2 = makeChan("https://localhost:" + serverPort + "/multiplex2");
|
|
var listener1 = new Http2MultiplexListener();
|
|
var listener2 = new Http2MultiplexListener();
|
|
chan1.asyncOpen(listener1);
|
|
chan2.asyncOpen(listener2);
|
|
}
|
|
|
|
// Test to make sure we gateway non-standard headers properly
|
|
function test_http2_header() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/header");
|
|
var hvalue = "Headers are fun";
|
|
chan.setRequestHeader("X-Test-Header", hvalue, false);
|
|
var listener = new Http2HeaderListener("X-Received-Test-Header", function(
|
|
received_hvalue
|
|
) {
|
|
Assert.equal(received_hvalue, hvalue);
|
|
});
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
// Test to make sure headers with invalid characters in the name are rejected
|
|
function test_http2_invalid_response_header() {
|
|
var listener = new Http2CheckListener();
|
|
listener.shouldSucceed = false;
|
|
var chan = makeChan(
|
|
"https://localhost:" + serverPort + "/invalid_response_header"
|
|
);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
// Test to make sure cookies are split into separate fields before compression
|
|
function test_http2_cookie_crumbling() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/cookie_crumbling");
|
|
var cookiesSent = ["a=b", "c=d01234567890123456789", "e=f"].sort();
|
|
chan.setRequestHeader("Cookie", cookiesSent.join("; "), false);
|
|
var listener = new Http2HeaderListener("X-Received-Header-Pairs", function(
|
|
pairsReceived
|
|
) {
|
|
var cookiesReceived = JSON.parse(pairsReceived)
|
|
.filter(function(pair) {
|
|
return pair[0] == "cookie";
|
|
})
|
|
.map(function(pair) {
|
|
return pair[1];
|
|
})
|
|
.sort();
|
|
Assert.equal(cookiesReceived.length, cookiesSent.length);
|
|
cookiesReceived.forEach(function(cookieReceived, index) {
|
|
Assert.equal(cookiesSent[index], cookieReceived);
|
|
});
|
|
});
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push1() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push");
|
|
chan.loadGroup = loadGroup;
|
|
var listener = new Http2PushListener(true);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push2() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push.js");
|
|
chan.loadGroup = loadGroup;
|
|
var listener = new Http2PushListener(true);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push3() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push2");
|
|
chan.loadGroup = loadGroup;
|
|
var listener = new Http2PushListener(true);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push4() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push2.js");
|
|
chan.loadGroup = loadGroup;
|
|
var listener = new Http2PushListener(true);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push5() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push5");
|
|
chan.loadGroup = loadGroup;
|
|
var listener = new Http2PushListener(true);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push6() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push5.js");
|
|
chan.loadGroup = loadGroup;
|
|
var listener = new Http2PushListener(true);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
// this is a basic test where the server sends a simple document with 2 header
|
|
// blocks. bug 1027364
|
|
function test_http2_doubleheader() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/doubleheader");
|
|
var listener = new Http2CheckListener();
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
// Make sure we handle GETs that cover more than 2 frames properly
|
|
function test_http2_big() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/big");
|
|
var listener = new Http2BigListener();
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_huge_suspended() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/huge");
|
|
var listener = new Http2HugeSuspendedListener();
|
|
chan.asyncOpen(listener);
|
|
chan.suspend();
|
|
do_timeout(500, chan.resume);
|
|
}
|
|
|
|
// Support for doing a POST
|
|
function do_post(content, chan, listener, method) {
|
|
var stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(
|
|
Ci.nsIStringInputStream
|
|
);
|
|
stream.data = content;
|
|
|
|
var uchan = chan.QueryInterface(Ci.nsIUploadChannel);
|
|
uchan.setUploadStream(stream, "text/plain", stream.available());
|
|
|
|
chan.requestMethod = method;
|
|
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
// Make sure we can do a simple POST
|
|
function test_http2_post() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/post");
|
|
var listener = new Http2PostListener(md5s[0]);
|
|
do_post(posts[0], chan, listener, "POST");
|
|
}
|
|
|
|
function test_http2_empty_post() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/post");
|
|
var listener = new Http2PostListener("0");
|
|
do_post("", chan, listener, "POST");
|
|
}
|
|
|
|
// Make sure we can do a simple PATCH
|
|
function test_http2_patch() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/patch");
|
|
var listener = new Http2PostListener(md5s[0]);
|
|
do_post(posts[0], chan, listener, "PATCH");
|
|
}
|
|
|
|
// Make sure we can do a POST that covers more than 2 frames
|
|
function test_http2_post_big() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/post");
|
|
var listener = new Http2PostListener(md5s[1]);
|
|
do_post(posts[1], chan, listener, "POST");
|
|
}
|
|
|
|
const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
|
|
|
|
var httpserv = null;
|
|
var httpserv2 = null;
|
|
|
|
var altsvcClientListener = {
|
|
onStartRequest: function test_onStartR(request) {
|
|
Assert.equal(request.status, Cr.NS_OK);
|
|
},
|
|
|
|
onDataAvailable: function test_ODA(request, stream, offset, cnt) {
|
|
read_stream(stream, cnt);
|
|
},
|
|
|
|
onStopRequest: function test_onStopR(request, status) {
|
|
var isHttp2Connection = checkIsHttp2(
|
|
request.QueryInterface(Ci.nsIHttpChannel)
|
|
);
|
|
if (!isHttp2Connection) {
|
|
dump("/altsvc1 not over h2 yet - retry\n");
|
|
let chan = makeChan(
|
|
"http://foo.example.com:" + httpserv.identity.primaryPort + "/altsvc1"
|
|
).QueryInterface(Ci.nsIHttpChannel);
|
|
// we use this header to tell the server to issue a altsvc frame for the
|
|
// speficied origin we will use in the next part of the test
|
|
chan.setRequestHeader(
|
|
"x-redirect-origin",
|
|
"http://foo.example.com:" + httpserv2.identity.primaryPort,
|
|
false
|
|
);
|
|
chan.loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
|
chan.asyncOpen(altsvcClientListener);
|
|
} else {
|
|
Assert.ok(isHttp2Connection);
|
|
let chan = makeChan(
|
|
"http://foo.example.com:" + httpserv2.identity.primaryPort + "/altsvc2"
|
|
).QueryInterface(Ci.nsIHttpChannel);
|
|
chan.loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
|
chan.asyncOpen(altsvcClientListener2);
|
|
}
|
|
},
|
|
};
|
|
|
|
var altsvcClientListener2 = {
|
|
onStartRequest: function test_onStartR(request) {
|
|
Assert.equal(request.status, Cr.NS_OK);
|
|
},
|
|
|
|
onDataAvailable: function test_ODA(request, stream, offset, cnt) {
|
|
read_stream(stream, cnt);
|
|
},
|
|
|
|
onStopRequest: function test_onStopR(request, status) {
|
|
var isHttp2Connection = checkIsHttp2(
|
|
request.QueryInterface(Ci.nsIHttpChannel)
|
|
);
|
|
if (!isHttp2Connection) {
|
|
dump("/altsvc2 not over h2 yet - retry\n");
|
|
var chan = makeChan(
|
|
"http://foo.example.com:" + httpserv2.identity.primaryPort + "/altsvc2"
|
|
).QueryInterface(Ci.nsIHttpChannel);
|
|
chan.loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
|
chan.asyncOpen(altsvcClientListener2);
|
|
} else {
|
|
Assert.ok(isHttp2Connection);
|
|
run_next_test();
|
|
do_test_finished();
|
|
}
|
|
},
|
|
};
|
|
|
|
function altsvcHttp1Server(metadata, response) {
|
|
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
|
response.setHeader("Content-Type", "text/plain", false);
|
|
response.setHeader("Connection", "close", false);
|
|
response.setHeader("Alt-Svc", 'h2=":' + serverPort + '"', false);
|
|
var body = "this is where a cool kid would write something neat.\n";
|
|
response.bodyOutputStream.write(body, body.length);
|
|
}
|
|
|
|
function h1ServerWK(metadata, response) {
|
|
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
|
response.setHeader("Content-Type", "application/json", false);
|
|
response.setHeader("Connection", "close", false);
|
|
response.setHeader("Cache-Control", "no-cache", false);
|
|
response.setHeader("Access-Control-Allow-Origin", "*", false);
|
|
response.setHeader("Access-Control-Allow-Method", "GET", false);
|
|
|
|
var body = '["http://foo.example.com:' + httpserv.identity.primaryPort + '"]';
|
|
response.bodyOutputStream.write(body, body.length);
|
|
}
|
|
|
|
function altsvcHttp1Server2(metadata, response) {
|
|
// this server should never be used thanks to an alt svc frame from the
|
|
// h2 server.. but in case of some async lag in setting the alt svc route
|
|
// up we have it.
|
|
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
|
response.setHeader("Content-Type", "text/plain", false);
|
|
response.setHeader("Connection", "close", false);
|
|
var body = "hanging.\n";
|
|
response.bodyOutputStream.write(body, body.length);
|
|
}
|
|
|
|
function h1ServerWK2(metadata, response) {
|
|
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
|
response.setHeader("Content-Type", "application/json", false);
|
|
response.setHeader("Connection", "close", false);
|
|
response.setHeader("Cache-Control", "no-cache", false);
|
|
response.setHeader("Access-Control-Allow-Origin", "*", false);
|
|
response.setHeader("Access-Control-Allow-Method", "GET", false);
|
|
|
|
var body =
|
|
'["http://foo.example.com:' + httpserv2.identity.primaryPort + '"]';
|
|
response.bodyOutputStream.write(body, body.length);
|
|
}
|
|
function test_http2_altsvc() {
|
|
var chan = makeChan(
|
|
"http://foo.example.com:" + httpserv.identity.primaryPort + "/altsvc1"
|
|
).QueryInterface(Ci.nsIHttpChannel);
|
|
chan.asyncOpen(altsvcClientListener);
|
|
}
|
|
|
|
var Http2PushApiListener = function() {};
|
|
|
|
Http2PushApiListener.prototype = {
|
|
checksPending: 9, // 4 onDataAvailable and 5 onStop
|
|
|
|
getInterface(aIID) {
|
|
return this.QueryInterface(aIID);
|
|
},
|
|
|
|
QueryInterface: ChromeUtils.generateQI([
|
|
"nsIHttpPushListener",
|
|
"nsIStreamListener",
|
|
]),
|
|
|
|
// nsIHttpPushListener
|
|
onPush: function onPush(associatedChannel, pushChannel) {
|
|
Assert.equal(
|
|
associatedChannel.originalURI.spec,
|
|
"https://localhost:" + serverPort + "/pushapi1"
|
|
);
|
|
Assert.equal(pushChannel.getRequestHeader("x-pushed-request"), "true");
|
|
|
|
pushChannel.asyncOpen(this);
|
|
if (
|
|
pushChannel.originalURI.spec ==
|
|
"https://localhost:" + serverPort + "/pushapi1/2"
|
|
) {
|
|
pushChannel.cancel(Cr.NS_ERROR_ABORT);
|
|
} else if (
|
|
pushChannel.originalURI.spec ==
|
|
"https://localhost:" + serverPort + "/pushapi1/3"
|
|
) {
|
|
Assert.ok(pushChannel.getRequestHeader("Accept-Encoding").includes("br"));
|
|
}
|
|
},
|
|
|
|
// normal Channel listeners
|
|
onStartRequest: function pushAPIOnStart(request) {},
|
|
|
|
onDataAvailable: function pushAPIOnDataAvailable(
|
|
request,
|
|
stream,
|
|
offset,
|
|
cnt
|
|
) {
|
|
Assert.notEqual(
|
|
request.originalURI.spec,
|
|
"https://localhost:" + serverPort + "/pushapi1/2"
|
|
);
|
|
|
|
var data = read_stream(stream, cnt);
|
|
|
|
if (
|
|
request.originalURI.spec ==
|
|
"https://localhost:" + serverPort + "/pushapi1"
|
|
) {
|
|
Assert.equal(data[0], "0");
|
|
--this.checksPending;
|
|
} else if (
|
|
request.originalURI.spec ==
|
|
"https://localhost:" + serverPort + "/pushapi1/1"
|
|
) {
|
|
Assert.equal(data[0], "1");
|
|
--this.checksPending; // twice
|
|
} else if (
|
|
request.originalURI.spec ==
|
|
"https://localhost:" + serverPort + "/pushapi1/3"
|
|
) {
|
|
Assert.equal(data[0], "3");
|
|
--this.checksPending;
|
|
} else {
|
|
Assert.equal(true, false);
|
|
}
|
|
},
|
|
|
|
onStopRequest: function test_onStopR(request, status) {
|
|
if (
|
|
request.originalURI.spec ==
|
|
"https://localhost:" + serverPort + "/pushapi1/2"
|
|
) {
|
|
Assert.equal(request.status, Cr.NS_ERROR_ABORT);
|
|
} else {
|
|
Assert.equal(request.status, Cr.NS_OK);
|
|
}
|
|
|
|
--this.checksPending; // 5 times - one for each push plus the pull
|
|
if (!this.checksPending) {
|
|
run_next_test();
|
|
do_test_finished();
|
|
}
|
|
},
|
|
};
|
|
|
|
// pushAPI testcase 1 expects
|
|
// 1 to pull /pushapi1 with 0
|
|
// 2 to see /pushapi1/1 with 1
|
|
// 3 to see /pushapi1/1 with 1 (again)
|
|
// 4 to see /pushapi1/2 that it will cancel
|
|
// 5 to see /pushapi1/3 with 3 with brotli
|
|
|
|
function test_http2_pushapi_1() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/pushapi1");
|
|
chan.loadGroup = loadGroup;
|
|
var listener = new Http2PushApiListener();
|
|
chan.notificationCallbacks = listener;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
var WrongSuiteListener = function() {};
|
|
WrongSuiteListener.prototype = new Http2CheckListener();
|
|
WrongSuiteListener.prototype.shouldBeHttp2 = false;
|
|
WrongSuiteListener.prototype.onStopRequest = function(request, status) {
|
|
prefs.setBoolPref("security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", true);
|
|
prefs.clearUserPref("security.tls.version.max");
|
|
Http2CheckListener.prototype.onStopRequest.call(this);
|
|
};
|
|
|
|
// test that we use h1 without the mandatory cipher suite available when
|
|
// offering at most tls1.2
|
|
function test_http2_wrongsuite_tls12() {
|
|
prefs.setBoolPref("security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", false);
|
|
prefs.setIntPref("security.tls.version.max", 3);
|
|
var chan = makeChan("https://localhost:" + serverPort + "/wrongsuite");
|
|
chan.loadFlags =
|
|
Ci.nsIRequest.LOAD_FRESH_CONNECTION |
|
|
Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
|
|
var listener = new WrongSuiteListener();
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
// test that we use h2 when offering tls1.3 or higher regardless of if the
|
|
// mandatory cipher suite is available
|
|
function test_http2_wrongsuite_tls13() {
|
|
prefs.setBoolPref("security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", false);
|
|
var chan = makeChan("https://localhost:" + serverPort + "/wrongsuite");
|
|
chan.loadFlags =
|
|
Ci.nsIRequest.LOAD_FRESH_CONNECTION |
|
|
Ci.nsIChannel.LOAD_INITIAL_DOCUMENT_URI;
|
|
var listener = new WrongSuiteListener();
|
|
listener.shouldBeHttp2 = true;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_h11required_stream() {
|
|
var chan = makeChan(
|
|
"https://localhost:" + serverPort + "/h11required_stream"
|
|
);
|
|
var listener = new Http2CheckListener();
|
|
listener.shouldBeHttp2 = false;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function H11RequiredSessionListener() {}
|
|
H11RequiredSessionListener.prototype = new Http2CheckListener();
|
|
|
|
H11RequiredSessionListener.prototype.onStopRequest = function(request, status) {
|
|
var streamReused = request.getResponseHeader("X-H11Required-Stream-Ok");
|
|
Assert.equal(streamReused, "yes");
|
|
|
|
Assert.ok(this.onStartRequestFired);
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);
|
|
|
|
run_next_test();
|
|
do_test_finished();
|
|
};
|
|
|
|
function test_http2_h11required_session() {
|
|
var chan = makeChan(
|
|
"https://localhost:" + serverPort + "/h11required_session"
|
|
);
|
|
var listener = new H11RequiredSessionListener();
|
|
listener.shouldBeHttp2 = false;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_retry_rst() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/rstonce");
|
|
var listener = new Http2CheckListener();
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_continuations() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/continuedheaders");
|
|
chan.loadGroup = loadGroup;
|
|
var listener = new Http2ContinuedHeaderListener();
|
|
chan.notificationCallbacks = listener;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function Http2IllegalHpackValidationListener() {}
|
|
Http2IllegalHpackValidationListener.prototype = new Http2CheckListener();
|
|
Http2IllegalHpackValidationListener.prototype.shouldGoAway = false;
|
|
|
|
Http2IllegalHpackValidationListener.prototype.onStopRequest = function(
|
|
request,
|
|
status
|
|
) {
|
|
var wentAway = request.getResponseHeader("X-Did-Goaway") === "yes";
|
|
Assert.equal(wentAway, this.shouldGoAway);
|
|
|
|
Assert.ok(this.onStartRequestFired);
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);
|
|
|
|
run_next_test();
|
|
do_test_finished();
|
|
};
|
|
|
|
function Http2IllegalHpackListener() {}
|
|
Http2IllegalHpackListener.prototype = new Http2CheckListener();
|
|
Http2IllegalHpackListener.prototype.shouldGoAway = false;
|
|
|
|
Http2IllegalHpackListener.prototype.onStopRequest = function(request, status) {
|
|
var chan = makeChan(
|
|
"https://localhost:" + serverPort + "/illegalhpack_validate"
|
|
);
|
|
var listener = new Http2IllegalHpackValidationListener();
|
|
listener.shouldGoAway = this.shouldGoAway;
|
|
chan.asyncOpen(listener);
|
|
};
|
|
|
|
function test_http2_illegalhpacksoft() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/illegalhpacksoft");
|
|
var listener = new Http2IllegalHpackListener();
|
|
listener.shouldGoAway = false;
|
|
listener.shouldSucceed = false;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_illegalhpackhard() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/illegalhpackhard");
|
|
var listener = new Http2IllegalHpackListener();
|
|
listener.shouldGoAway = true;
|
|
listener.shouldSucceed = false;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_folded_header() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/foldedheader");
|
|
chan.loadGroup = loadGroup;
|
|
var listener = new Http2CheckListener();
|
|
listener.shouldSucceed = false;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_empty_data() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/emptydata");
|
|
var listener = new Http2CheckListener();
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push_firstparty1() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push");
|
|
chan.loadGroup = loadGroup;
|
|
chan.loadInfo.originAttributes = { firstPartyDomain: "foo.com" };
|
|
var listener = new Http2PushListener(true);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push_firstparty2() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push.js");
|
|
chan.loadGroup = loadGroup;
|
|
chan.loadInfo.originAttributes = { firstPartyDomain: "bar.com" };
|
|
var listener = new Http2PushListener(false);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push_firstparty3() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push.js");
|
|
chan.loadGroup = loadGroup;
|
|
chan.loadInfo.originAttributes = { firstPartyDomain: "foo.com" };
|
|
var listener = new Http2PushListener(true);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push_userContext1() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push");
|
|
chan.loadGroup = loadGroup;
|
|
chan.loadInfo.originAttributes = { userContextId: 1 };
|
|
var listener = new Http2PushListener(true);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push_userContext2() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push.js");
|
|
chan.loadGroup = loadGroup;
|
|
chan.loadInfo.originAttributes = { userContextId: 2 };
|
|
var listener = new Http2PushListener(false);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_push_userContext3() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/push.js");
|
|
chan.loadGroup = loadGroup;
|
|
chan.loadInfo.originAttributes = { userContextId: 1 };
|
|
var listener = new Http2PushListener(true);
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_status_phrase() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/statusphrase");
|
|
var listener = new Http2CheckListener();
|
|
listener.shouldSucceed = false;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
var PulledDiskCacheListener = function() {};
|
|
PulledDiskCacheListener.prototype = new Http2CheckListener();
|
|
PulledDiskCacheListener.prototype.EXPECTED_DATA = "this was pulled via h2";
|
|
PulledDiskCacheListener.prototype.readData = "";
|
|
PulledDiskCacheListener.prototype.onDataAvailable = function testOnDataAvailable(
|
|
request,
|
|
stream,
|
|
off,
|
|
cnt
|
|
) {
|
|
this.onDataAvailableFired = true;
|
|
this.isHttp2Connection = checkIsHttp2(request);
|
|
this.accum += cnt;
|
|
this.readData += read_stream(stream, cnt);
|
|
};
|
|
PulledDiskCacheListener.prototype.onStopRequest = function testOnStopRequest(
|
|
request,
|
|
status
|
|
) {
|
|
Assert.equal(this.EXPECTED_DATA, this.readData);
|
|
Http2CheckListener.prorotype.onStopRequest.call(this, request, status);
|
|
};
|
|
|
|
const DISK_CACHE_DATA = "this is from disk cache";
|
|
|
|
var FromDiskCacheListener = function() {};
|
|
FromDiskCacheListener.prototype = {
|
|
onStartRequestFired: false,
|
|
onDataAvailableFired: false,
|
|
readData: "",
|
|
|
|
onStartRequest: function testOnStartRequest(request) {
|
|
this.onStartRequestFired = true;
|
|
if (!Components.isSuccessCode(request.status)) {
|
|
do_throw("Channel should have a success code! (" + request.status + ")");
|
|
}
|
|
|
|
Assert.ok(request instanceof Ci.nsIHttpChannel);
|
|
Assert.ok(request.requestSucceeded);
|
|
Assert.equal(request.responseStatus, 200);
|
|
},
|
|
|
|
onDataAvailable: function testOnDataAvailable(request, stream, off, cnt) {
|
|
this.onDataAvailableFired = true;
|
|
this.readData += read_stream(stream, cnt);
|
|
},
|
|
|
|
onStopRequest: function testOnStopRequest(request, status) {
|
|
Assert.ok(this.onStartRequestFired);
|
|
Assert.ok(Components.isSuccessCode(status));
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.equal(this.readData, DISK_CACHE_DATA);
|
|
|
|
evict_cache_entries("disk");
|
|
syncWithCacheIOThread(() => {
|
|
// Now that we know the entry is out of the disk cache, check to make sure
|
|
// we don't have this hiding in the push cache somewhere - if we do, it
|
|
// didn't get cancelled, and we have a bug.
|
|
var chan = makeChan("https://localhost:" + serverPort + "/diskcache");
|
|
var listener = new PulledDiskCacheListener();
|
|
chan.loadGroup = loadGroup;
|
|
chan.asyncOpen(listener);
|
|
});
|
|
},
|
|
};
|
|
|
|
var Http2DiskCachePushListener = function() {};
|
|
|
|
Http2DiskCachePushListener.prototype = new Http2CheckListener();
|
|
|
|
Http2DiskCachePushListener.onStopRequest = function(request, status) {
|
|
Assert.ok(this.onStartRequestFired);
|
|
Assert.ok(Components.isSuccessCode(status));
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);
|
|
|
|
// Now we need to open a channel to ensure we get data from the disk cache
|
|
// for the pushed item, instead of from the push cache.
|
|
var chan = makeChan("https://localhost:" + serverPort + "/diskcache");
|
|
var listener = new FromDiskCacheListener();
|
|
chan.loadGroup = loadGroup;
|
|
chan.asyncOpen(listener);
|
|
};
|
|
|
|
function continue_test_http2_disk_cache_push(status, entry) {
|
|
// TODO - store stuff in cache entry, then open an h2 channel that will push
|
|
// this, once that completes, open a channel for the cache entry we made and
|
|
// ensure it came from disk cache, not the push cache.
|
|
var outputStream = entry.openOutputStream(0, -1);
|
|
outputStream.write(DISK_CACHE_DATA, DISK_CACHE_DATA.length);
|
|
|
|
// Now we open our URL that will push data for the URL above
|
|
var chan = makeChan("https://localhost:" + serverPort + "/pushindisk");
|
|
var listener = new Http2DiskCachePushListener();
|
|
chan.loadGroup = loadGroup;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
function test_http2_disk_cache_push() {
|
|
asyncOpenCacheEntry(
|
|
"https://localhost:" + serverPort + "/diskcache",
|
|
"disk",
|
|
Ci.nsICacheStorage.OPEN_NORMALLY,
|
|
null,
|
|
continue_test_http2_disk_cache_push,
|
|
false
|
|
);
|
|
}
|
|
|
|
function test_complete() {
|
|
resetPrefs();
|
|
do_test_pending();
|
|
httpserv.stop(do_test_finished);
|
|
do_test_pending();
|
|
httpserv2.stop(do_test_finished);
|
|
|
|
do_test_finished();
|
|
do_timeout(0, run_next_test);
|
|
}
|
|
|
|
var Http2DoublepushListener = function() {};
|
|
Http2DoublepushListener.prototype = new Http2CheckListener();
|
|
Http2DoublepushListener.prototype.onStopRequest = function(request, status) {
|
|
Assert.ok(this.onStartRequestFired);
|
|
Assert.ok(Components.isSuccessCode(status));
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.ok(this.isHttp2Connection == this.shouldBeHttp2);
|
|
|
|
var chan = makeChan("https://localhost:" + serverPort + "/doublypushed");
|
|
var listener = new Http2DoublypushedListener();
|
|
chan.loadGroup = loadGroup;
|
|
chan.asyncOpen(listener);
|
|
};
|
|
|
|
var Http2DoublypushedListener = function() {};
|
|
Http2DoublypushedListener.prototype = new Http2CheckListener();
|
|
Http2DoublypushedListener.prototype.readData = "";
|
|
Http2DoublypushedListener.prototype.onDataAvailable = function(
|
|
request,
|
|
stream,
|
|
off,
|
|
cnt
|
|
) {
|
|
this.onDataAvailableFired = true;
|
|
this.accum += cnt;
|
|
this.readData += read_stream(stream, cnt);
|
|
};
|
|
Http2DoublypushedListener.prototype.onStopRequest = function(request, status) {
|
|
Assert.ok(this.onStartRequestFired);
|
|
Assert.ok(Components.isSuccessCode(status));
|
|
Assert.ok(this.onDataAvailableFired);
|
|
Assert.equal(this.readData, "pushed");
|
|
|
|
run_next_test();
|
|
do_test_finished();
|
|
};
|
|
|
|
function test_http2_doublepush() {
|
|
var chan = makeChan("https://localhost:" + serverPort + "/doublepush");
|
|
var listener = new Http2DoublepushListener();
|
|
chan.loadGroup = loadGroup;
|
|
chan.asyncOpen(listener);
|
|
}
|
|
|
|
// hack - the header test resets the multiplex object on the server,
|
|
// so make sure header is always run before the multiplex test.
|
|
//
|
|
// make sure post_big runs first to test race condition in restarting
|
|
// a stalled stream when a SETTINGS frame arrives
|
|
var tests = [
|
|
test_http2_post_big,
|
|
test_http2_basic,
|
|
test_http2_concurrent,
|
|
test_http2_concurrent_post,
|
|
test_http2_basic_unblocked_dep,
|
|
test_http2_nospdy,
|
|
test_http2_push1,
|
|
test_http2_push2,
|
|
test_http2_push3,
|
|
test_http2_push4,
|
|
test_http2_push5,
|
|
test_http2_push6,
|
|
test_http2_altsvc,
|
|
test_http2_doubleheader,
|
|
test_http2_xhr,
|
|
test_http2_header,
|
|
test_http2_invalid_response_header,
|
|
test_http2_cookie_crumbling,
|
|
test_http2_multiplex,
|
|
test_http2_big,
|
|
test_http2_huge_suspended,
|
|
test_http2_post,
|
|
test_http2_empty_post,
|
|
test_http2_patch,
|
|
test_http2_pushapi_1,
|
|
test_http2_continuations,
|
|
test_http2_blocking_download,
|
|
test_http2_illegalhpacksoft,
|
|
test_http2_illegalhpackhard,
|
|
test_http2_folded_header,
|
|
test_http2_empty_data,
|
|
test_http2_status_phrase,
|
|
test_http2_doublepush,
|
|
test_http2_disk_cache_push,
|
|
// Add new tests above here - best to add new tests before h1
|
|
// streams get too involved
|
|
// These next two must always come in this order
|
|
test_http2_h11required_stream,
|
|
test_http2_h11required_session,
|
|
test_http2_retry_rst,
|
|
test_http2_wrongsuite_tls12,
|
|
test_http2_wrongsuite_tls13,
|
|
test_http2_push_firstparty1,
|
|
test_http2_push_firstparty2,
|
|
test_http2_push_firstparty3,
|
|
test_http2_push_userContext1,
|
|
test_http2_push_userContext2,
|
|
test_http2_push_userContext3,
|
|
|
|
// cleanup
|
|
test_complete,
|
|
];
|
|
var current_test = 0;
|
|
|
|
function run_next_test() {
|
|
if (current_test < tests.length) {
|
|
dump(
|
|
`starting test number ${current_test} - ${tests[current_test].name}\n`
|
|
);
|
|
tests[current_test]();
|
|
current_test++;
|
|
do_test_pending();
|
|
}
|
|
}
|
|
|
|
var prefs;
|
|
var http2push;
|
|
var http2pref;
|
|
var altsvcpref1;
|
|
var altsvcpref2;
|
|
var loadGroup;
|
|
var serverPort;
|
|
var speculativeLimit;
|
|
|
|
function resetPrefs() {
|
|
prefs.setIntPref("network.http.speculative-parallel-limit", speculativeLimit);
|
|
prefs.setBoolPref("network.http.http2.enabled", http2pref);
|
|
prefs.setBoolPref("network.http.http2.allow-push", http2push);
|
|
prefs.setBoolPref("network.http.altsvc.enabled", altsvcpref1);
|
|
prefs.setBoolPref("network.http.altsvc.oe", altsvcpref2);
|
|
prefs.clearUserPref("network.dns.localDomains");
|
|
prefs.clearUserPref("network.cookieJarSettings.unblocked_for_testing");
|
|
}
|
|
|
|
function run_test() {
|
|
var env = Cc["@mozilla.org/process/environment;1"].getService(
|
|
Ci.nsIEnvironment
|
|
);
|
|
serverPort = env.get("MOZHTTP2_PORT");
|
|
Assert.notEqual(serverPort, null);
|
|
dump("using port " + serverPort + "\n");
|
|
|
|
// Set to allow the cert presented by our H2 server
|
|
do_get_profile();
|
|
prefs = Services.prefs;
|
|
speculativeLimit = prefs.getIntPref(
|
|
"network.http.speculative-parallel-limit"
|
|
);
|
|
prefs.setIntPref("network.http.speculative-parallel-limit", 0);
|
|
|
|
// The moz-http2 cert is for foo.example.com and is signed by http2-ca.pem
|
|
// so add that cert to the trust list as a signing cert. Some older tests in
|
|
// this suite use localhost with a TOFU exception, but new ones should use
|
|
// foo.example.com
|
|
let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService(
|
|
Ci.nsIX509CertDB
|
|
);
|
|
addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u");
|
|
|
|
// Enable all versions of spdy to see that we auto negotiate http/2
|
|
http2pref = prefs.getBoolPref("network.http.http2.enabled");
|
|
http2push = prefs.getBoolPref("network.http.http2.allow-push");
|
|
altsvcpref1 = prefs.getBoolPref("network.http.altsvc.enabled");
|
|
altsvcpref2 = prefs.getBoolPref("network.http.altsvc.oe", true);
|
|
|
|
prefs.setBoolPref("network.http.http2.enabled", true);
|
|
prefs.setBoolPref("network.http.http2.allow-push", true);
|
|
prefs.setBoolPref("network.http.altsvc.enabled", true);
|
|
prefs.setBoolPref("network.http.altsvc.oe", true);
|
|
prefs.setCharPref(
|
|
"network.dns.localDomains",
|
|
"foo.example.com, bar.example.com"
|
|
);
|
|
prefs.setBoolPref("network.cookieJarSettings.unblocked_for_testing", true);
|
|
|
|
loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(
|
|
Ci.nsILoadGroup
|
|
);
|
|
|
|
httpserv = new HttpServer();
|
|
httpserv.registerPathHandler("/altsvc1", altsvcHttp1Server);
|
|
httpserv.registerPathHandler("/.well-known/http-opportunistic", h1ServerWK);
|
|
httpserv.start(-1);
|
|
httpserv.identity.setPrimary(
|
|
"http",
|
|
"foo.example.com",
|
|
httpserv.identity.primaryPort
|
|
);
|
|
|
|
httpserv2 = new HttpServer();
|
|
httpserv2.registerPathHandler("/altsvc2", altsvcHttp1Server2);
|
|
httpserv2.registerPathHandler("/.well-known/http-opportunistic", h1ServerWK2);
|
|
httpserv2.start(-1);
|
|
httpserv2.identity.setPrimary(
|
|
"http",
|
|
"foo.example.com",
|
|
httpserv2.identity.primaryPort
|
|
);
|
|
|
|
// And make go!
|
|
run_next_test();
|
|
}
|