Files
tubestation/testing/xpcshell/moz-http2/moz-http2.js
Daniel Stenberg 6c8284d0cd bug 1460327 - make the DNS packet decoder verify the answer qname r=mcmanus
... and before chasing a cname, check if the address record for that
CNAME is actually in fact already provided in the DNS packet that passed
on the CNAME!

Some existing tests ajusted for this. Two new tests added.

MozReview-Commit-ID: CBMO7N7jMEX
2018-05-08 19:30:07 +02:00

1127 lines
38 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This module is the stateful server side of test_http2.js and is meant
// to have node be restarted in between each invocation
var node_http2_root = '../node-http2';
if (process.env.NODE_HTTP2_ROOT) {
node_http2_root = process.env.NODE_HTTP2_ROOT;
}
var http2 = require(node_http2_root);
var fs = require('fs');
var url = require('url');
var crypto = require('crypto');
// Hook into the decompression code to log the decompressed name-value pairs
var compression_module = node_http2_root + "/lib/protocol/compressor";
var http2_compression = require(compression_module);
var HeaderSetDecompressor = http2_compression.HeaderSetDecompressor;
var originalRead = HeaderSetDecompressor.prototype.read;
var lastDecompressor;
var decompressedPairs;
HeaderSetDecompressor.prototype.read = function() {
if (this != lastDecompressor) {
lastDecompressor = this;
decompressedPairs = [];
}
var pair = originalRead.apply(this, arguments);
if (pair) {
decompressedPairs.push(pair);
}
return pair;
}
var connection_module = node_http2_root + "/lib/protocol/connection";
var http2_connection = require(connection_module);
var Connection = http2_connection.Connection;
var originalClose = Connection.prototype.close;
Connection.prototype.close = function (error, lastId) {
if (lastId !== undefined) {
this._lastIncomingStream = lastId;
}
originalClose.apply(this, arguments);
}
var framer_module = node_http2_root + "/lib/protocol/framer";
var http2_framer = require(framer_module);
var Serializer = http2_framer.Serializer;
var originalTransform = Serializer.prototype._transform;
var newTransform = function (frame, encoding, done) {
if (frame.type == 'DATA') {
// Insert our empty DATA frame
emptyFrame = {};
emptyFrame.type = 'DATA';
emptyFrame.data = new Buffer(0);
emptyFrame.flags = [];
emptyFrame.stream = frame.stream;
var buffers = [];
Serializer['DATA'](emptyFrame, buffers);
Serializer.commonHeader(emptyFrame, buffers);
for (var i = 0; i < buffers.length; i++) {
this.push(buffers[i]);
}
// Reset to the original version for later uses
Serializer.prototype._transform = originalTransform;
}
originalTransform.apply(this, arguments);
};
function getHttpContent(path) {
var content = '<!doctype html>' +
'<html>' +
'<head><title>HOORAY!</title></head>' +
'<body>You Win! (by requesting' + path + ')</body>' +
'</html>';
return content;
}
function generateContent(size) {
var content = '';
for (var i = 0; i < size; i++) {
content += '0';
}
return content;
}
/* This takes care of responding to the multiplexed request for us */
var m = {
mp1res: null,
mp2res: null,
buf: null,
mp1start: 0,
mp2start: 0,
checkReady: function() {
if (this.mp1res != null && this.mp2res != null) {
this.buf = generateContent(30*1024);
this.mp1start = 0;
this.mp2start = 0;
this.send(this.mp1res, 0);
setTimeout(this.send.bind(this, this.mp2res, 0), 5);
}
},
send: function(res, start) {
var end = Math.min(start + 1024, this.buf.length);
var content = this.buf.substring(start, end);
res.write(content);
if (end < this.buf.length) {
setTimeout(this.send.bind(this, res, end), 10);
} else {
res.end();
}
}
};
var runlater = function() {};
runlater.prototype = {
req : null,
resp : null,
onTimeout : function onTimeout() {
this.resp.writeHead(200);
this.resp.end("It's all good 750ms.");
}
};
var moreData = function() {};
moreData.prototype = {
req : null,
resp : null,
iter: 3,
onTimeout : function onTimeout() {
// 1mb of data
content = generateContent(1024*1024);
this.resp.write(content); // 1mb chunk
this.iter--;
if (!this.iter) {
this.resp.end();
} else {
setTimeout(executeRunLater, 1, this);
}
}
};
function executeRunLater(arg) {
arg.onTimeout();
}
var Compressor = http2_compression.Compressor;
var HeaderSetCompressor = http2_compression.HeaderSetCompressor;
var originalCompressHeaders = Compressor.prototype.compress;
function insertSoftIllegalHpack(headers) {
var originalCompressed = originalCompressHeaders.apply(this, headers);
var illegalLiteral = new Buffer([
0x00, // Literal, no index
0x08, // Name: not huffman encoded, 8 bytes long
0x3a, 0x69, 0x6c, 0x6c, 0x65, 0x67, 0x61, 0x6c, // :illegal
0x10, // Value: not huffman encoded, 16 bytes long
// REALLY NOT LEGAL
0x52, 0x45, 0x41, 0x4c, 0x4c, 0x59, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x4c, 0x45, 0x47, 0x41, 0x4c
]);
var newBufferLength = originalCompressed.length + illegalLiteral.length;
var concatenated = new Buffer(newBufferLength);
originalCompressed.copy(concatenated, 0);
illegalLiteral.copy(concatenated, originalCompressed.length);
return concatenated;
}
function insertHardIllegalHpack(headers) {
var originalCompressed = originalCompressHeaders.apply(this, headers);
// Now we have to add an invalid header
var illegalIndexed = HeaderSetCompressor.integer(5000, 7);
// The above returns an array of buffers, but there's only one buffer, so
// get rid of the array.
illegalIndexed = illegalIndexed[0];
// Set the first bit to 1 to signal this is an indexed representation
illegalIndexed[0] |= 0x80;
var newBufferLength = originalCompressed.length + illegalIndexed.length;
var concatenated = new Buffer(newBufferLength);
originalCompressed.copy(concatenated, 0);
illegalIndexed.copy(concatenated, originalCompressed.length);
return concatenated;
}
var h11required_conn = null;
var h11required_header = "yes";
var didRst = false;
var rstConnection = null;
var illegalheader_conn = null;
var ns_confirm = 0;
var cname_confirm = 0;
function handleRequest(req, res) {
// We do this first to ensure nothing goes wonky in our tests that don't want
// the headers to have something illegal in them
Compressor.prototype.compress = originalCompressHeaders;
var u = url.parse(req.url);
var content = getHttpContent(u.pathname);
var push, push1, push1a, push2, push3;
// PushService tests.
var pushPushServer1, pushPushServer2, pushPushServer3, pushPushServer4;
if (req.httpVersionMajor === 2) {
res.setHeader('X-Connection-Http2', 'yes');
res.setHeader('X-Http2-StreamId', '' + req.stream.id);
} else {
res.setHeader('X-Connection-Http2', 'no');
}
if (u.pathname === '/exit') {
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Connection', 'close');
res.writeHead(200);
res.end('ok');
process.exit();
}
if (u.pathname === '/750ms') {
var rl = new runlater();
rl.req = req;
rl.resp = res;
setTimeout(executeRunLater, 750, rl);
return;
}
else if ((u.pathname === '/multiplex1') && (req.httpVersionMajor === 2)) {
res.setHeader('Content-Type', 'text/plain');
res.writeHead(200);
m.mp1res = res;
m.checkReady();
return;
}
else if ((u.pathname === '/multiplex2') && (req.httpVersionMajor === 2)) {
res.setHeader('Content-Type', 'text/plain');
res.writeHead(200);
m.mp2res = res;
m.checkReady();
return;
}
else if (u.pathname === "/header") {
var val = req.headers["x-test-header"];
if (val) {
res.setHeader("X-Received-Test-Header", val);
}
}
else if (u.pathname === "/doubleheader") {
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.write(content);
res.writeHead(200);
res.end();
return;
}
else if (u.pathname === "/cookie_crumbling") {
res.setHeader("X-Received-Header-Pairs", JSON.stringify(decompressedPairs));
}
else if (u.pathname === "/push") {
push = res.push('/push.js');
push.writeHead(200, {
'content-type': 'application/javascript',
'pushed' : 'yes',
'content-length' : 11,
'X-Connection-Http2': 'yes'
});
push.end('// comments');
content = '<head> <script src="push.js"/></head>body text';
}
else if (u.pathname === "/push.js") {
content = '// comments';
res.setHeader("pushed", "no");
}
else if (u.pathname === "/push2") {
push = res.push('/push2.js');
push.writeHead(200, {
'content-type': 'application/javascript',
'pushed' : 'yes',
// no content-length
'X-Connection-Http2': 'yes'
});
push.end('// comments');
content = '<head> <script src="push2.js"/></head>body text';
}
else if (u.pathname === "/push5") {
push = res.push('/push5.js');
push.writeHead(200, {
'content-type': 'application/javascript',
'pushed' : 'yes',
// no content-length
'X-Connection-Http2': 'yes'
});
content = generateContent(1024 * 150);
push.write(content);
push.end();
content = '<head> <script src="push5.js"/></head>body text';
}
else if (u.pathname === "/pushapi1") {
push1 = res.push(
{ hostname: 'localhost:' + serverPort, port: serverPort, path : '/pushapi1/1', method : 'GET',
headers: {'x-pushed-request': 'true', 'x-foo' : 'bar'}});
push1.writeHead(200, {
'pushed' : 'yes',
'content-length' : 1,
'subresource' : '1',
'X-Connection-Http2': 'yes'
});
push1.end('1');
push1a = res.push(
{ hostname: 'localhost:' + serverPort, port: serverPort, path : '/pushapi1/1', method : 'GET',
headers: {'x-foo' : 'bar', 'x-pushed-request': 'true'}});
push1a.writeHead(200, {
'pushed' : 'yes',
'content-length' : 1,
'subresource' : '1a',
'X-Connection-Http2': 'yes'
});
push1a.end('1');
push2 = res.push(
{ hostname: 'localhost:' + serverPort, port: serverPort, path : '/pushapi1/2', method : 'GET',
headers: {'x-pushed-request': 'true'}});
push2.writeHead(200, {
'pushed' : 'yes',
'subresource' : '2',
'content-length' : 1,
'X-Connection-Http2': 'yes'
});
push2.end('2');
push3 = res.push(
{ hostname: 'localhost:' + serverPort, port: serverPort, path : '/pushapi1/3', method : 'GET',
headers: {'x-pushed-request': 'true', 'Accept-Encoding' : 'br'}});
push3.writeHead(200, {
'pushed' : 'yes',
'content-length' : 6,
'subresource' : '3',
'content-encoding' : 'br',
'X-Connection-Http2': 'yes'
});
push3.end(new Buffer([0x8b, 0x00, 0x80, 0x33, 0x0a, 0x03])); // '3\n'
content = '0';
}
else if (u.pathname === "/big") {
content = generateContent(128 * 1024);
var hash = crypto.createHash('md5');
hash.update(content);
var md5 = hash.digest('hex');
res.setHeader("X-Expected-MD5", md5);
}
else if (u.pathname === "/huge") {
content = generateContent(1024);
res.setHeader('Content-Type', 'text/plain');
res.writeHead(200);
// 1mb of data
for (var i = 0; i < (1024 * 1); i++) {
res.write(content); // 1kb chunk
}
res.end();
return;
}
else if (u.pathname === "/post" || u.pathname === "/patch") {
if (req.method != "POST" && req.method != "PATCH") {
res.writeHead(405);
res.end('Unexpected method: ' + req.method);
return;
}
var post_hash = crypto.createHash('md5');
req.on('data', function receivePostData(chunk) {
post_hash.update(chunk.toString());
});
req.on('end', function finishPost() {
var md5 = post_hash.digest('hex');
res.setHeader('X-Calculated-MD5', md5);
res.writeHead(200);
res.end(content);
});
return;
}
else if (u.pathname === "/750msPost") {
if (req.method != "POST") {
res.writeHead(405);
res.end('Unexpected method: ' + req.method);
return;
}
var accum = 0;
req.on('data', function receivePostData(chunk) {
accum += chunk.length;
});
req.on('end', function finishPost() {
res.setHeader('X-Recvd', accum);
var rl = new runlater();
rl.req = req;
rl.resp = res;
setTimeout(executeRunLater, 750, rl);
return;
});
return;
}
else if (u.pathname === "/h11required_stream") {
if (req.httpVersionMajor === 2) {
h11required_conn = req.stream.connection;
res.stream.reset('HTTP_1_1_REQUIRED');
return;
}
}
else if (u.pathname === "/bigdownload") {
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
var rl = new moreData();
rl.req = req;
rl.resp = res;
setTimeout(executeRunLater, 1, rl);
return;
}
else if (u.pathname === "/h11required_session") {
if (req.httpVersionMajor === 2) {
if (h11required_conn !== req.stream.connection) {
h11required_header = "no";
}
res.stream.connection.close('HTTP_1_1_REQUIRED', res.stream.id - 2);
return;
} else {
res.setHeader('X-H11Required-Stream-Ok', h11required_header);
}
}
else if (u.pathname === "/rstonce") {
if (!didRst && req.httpVersionMajor === 2) {
didRst = true;
rstConnection = req.stream.connection;
req.stream.reset('REFUSED_STREAM');
return;
}
if (rstConnection === null ||
rstConnection !== req.stream.connection) {
res.setHeader('Connection', 'close');
res.writeHead(400);
res.end("WRONG CONNECTION, HOMIE!");
return;
}
if (req.httpVersionMajor != 2) {
res.setHeader('Connection', 'close');
}
res.writeHead(200);
res.end("It's all good.");
return;
}
else if (u.pathname === "/continuedheaders") {
var pushRequestHeaders = {'x-pushed-request': 'true'};
var pushResponseHeaders = {'content-type': 'text/plain',
'content-length': '2',
'X-Connection-Http2': 'yes'};
var pushHdrTxt = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
var pullHdrTxt = pushHdrTxt.split('').reverse().join('');
for (var i = 0; i < 265; i++) {
pushRequestHeaders['X-Push-Test-Header-' + i] = pushHdrTxt;
res.setHeader('X-Pull-Test-Header-' + i, pullHdrTxt);
}
push = res.push({
hostname: 'localhost:' + serverPort,
port: serverPort,
path: '/continuedheaders/push',
method: 'GET',
headers: pushRequestHeaders
});
push.writeHead(200, pushResponseHeaders);
push.end("ok");
}
else if (u.pathname === "/altsvc1") {
if (req.httpVersionMajor != 2 ||
req.scheme != "http" ||
req.headers['alt-used'] != ("foo.example.com:" + serverPort)) {
res.writeHead(400);
res.end("WHAT?");
return;
}
// test the alt svc frame for use with altsvc2
res.altsvc("foo.example.com", serverPort, "h2", 3600, req.headers['x-redirect-origin']);
}
else if (u.pathname === "/altsvc2") {
if (req.httpVersionMajor != 2 ||
req.scheme != "http" ||
req.headers['alt-used'] != ("foo.example.com:" + serverPort)) {
res.writeHead(400);
res.end("WHAT?");
return;
}
}
// for use with test_altsvc.js
else if (u.pathname === "/altsvc-test") {
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Alt-Svc', 'h2=' + req.headers['x-altsvc']);
}
// for use with test_trr.js
else if (u.pathname === "/dns-cname") {
// asking for cname.example.com
var content;
if(0 == cname_confirm) {
// ... this sends a CNAME back to pointing-elsewhere.example.com
content = new Buffer("00000100000100010000000005636E616D65076578616D706C6503636F6D0000050001C00C0005000100000037002012706F696E74696E672D656C73657768657265076578616D706C6503636F6D00", "hex");
cname_confirm++;
}
else {
// ... this sends an A 99.88.77.66 entry back for pointing-elsewhere.example.com
content = new Buffer("00000100000100010000000012706F696E74696E672D656C73657768657265076578616D706C6503636F6D0000010001C00C0001000100000037000463584D42", "hex");
}
res.setHeader('Content-Type', 'application/dns-udpwireformat');
res.setHeader('Content-Length', content.length);
res.writeHead(200);
res.write(content);
res.end("");
return;
}
else if (u.pathname === "/dns-cname-a") {
// test23 asks for cname-a.example.com
// this responds with a CNAME to here.example.com *and* an A record
// for here.example.com
var content;
content = new Buffer("0000" +
"0100" +
"0001" + // QDCOUNT
"0002" + // ANCOUNT
"00000000" + // NSCOUNT + ARCOUNT
"07636E616D652d61" + // cname-a
"076578616D706C6503636F6D00" + // .example.com
"00010001" + // question type (A) + question class (IN)
// answer record 1
"C00C" + // name pointer to cname-a.example.com
"0005" + // type (CNAME)
"0001" + // class
"00000037" + // TTL
"0012" + // RDLENGTH
"0468657265" + // here
"076578616D706C6503636F6D00" + // .example.com
// answer record 2, the A entry for the CNAME above
"0468657265" + // here
"076578616D706C6503636F6D00" + // .example.com
"0001" + // type (A)
"0001" + // class
"00000037" + // TTL
"0004" + // RDLENGTH
"09080706", // IPv4 address
"hex");
res.setHeader('Content-Type', 'application/dns-udpwireformat');
res.setHeader('Content-Length', content.length);
res.writeHead(200);
res.write(content);
res.end("");
return;
}
else if (u.pathname === "/dns-cname-loop") {
// asking for cname.example.com
var content;
// ... this always sends a CNAME back to pointing-elsewhere.example.com. Loop time!
content = new Buffer("00000100000100010000000005636E616D65076578616D706C6503636F6D0000050001C00C0005000100000037002012706F696E74696E672D656C73657768657265076578616D706C65C01A00", "hex");
res.setHeader('Content-Type', 'application/dns-udpwireformat');
res.setHeader('Content-Length', content.length);
res.writeHead(200);
res.write(content);
res.end("");
return;
}
// for use with test_trr.js
else if (u.path === "/dns-get?ct&dns=AAABAAABAAAAAAAAA2dldAdleGFtcGxlA2NvbQAAAQAB") {
// the query string asks for an A entry for get.example.com
// get.example.com has A entry 1.2.3.4
var content= new Buffer("00000100000100010000000003676574076578616D706C6503636F6D0000010001C00C0001000100000037000401020304", "hex");
res.setHeader('Content-Type', 'application/dns-udpwireformat');
res.setHeader('Content-Length', content.length);
res.writeHead(200);
res.write(content);
res.end("");
ns_confirm = 0; // back to first reply for dns-confirm
cname_confirm = 0; // back to first reply for dns-cname
return;
}
// for use with test_trr.js
else if (u.pathname === "/dns") {
// bar.example.com has A entry 127.0.0.1
var content= new Buffer("00000100000100010000000003626172076578616D706C6503636F6D0000010001C00C000100010000003700047F000001", "hex");
res.setHeader('Content-Type', 'application/dns-udpwireformat');
res.setHeader('Content-Length', content.length);
// pass back a cookie here, check it in /dns-auth
res.setHeader('Set-Cookie', 'trackyou=yes; path=/; max-age=100000;');
res.writeHead(200);
res.write(content);
res.end("");
return;
}
else if (u.pathname === "/dns-ns") {
// confirm.example.com has NS entry ns.example.com
var content= new Buffer("00000100000100010000000007636F6E6669726D076578616D706C6503636F6D0000020001C00C00020001000000370012026E73076578616D706C6503636F6D010A00", "hex");
res.setHeader('Content-Type', 'application/dns-udpwireformat');
res.setHeader('Content-Length', content.length);
res.writeHead(200);
res.write(content);
res.end("");
return;
}
else if (u.pathname === '/dns-750ms') {
// it's just meant to be this slow - the test doesn't care about the actual response
return;
}
// for use with test_trr.js
else if (u.pathname === "/dns-confirm") {
if (0 == ns_confirm) {
// confirm.example.com has NS entry ns.example.com
var content= new Buffer("00000100000100010000000007636F6E6669726D076578616D706C6503636F6D0000020001C00C00020001000000370012026E73076578616D706C6503636F6D010A00", "hex");
ns_confirm++;
} else if (2 >= ns_confirm) {
// next response: 10b-100.example.com has AAAA entry 1::FFFF
// we expect two requests for this name (A + AAAA), respond identically
// for both and expect the client to reject the wrong one
var content= new Buffer("000001000001000100000000" + "073130622d313030" +
"076578616D706C6503636F6D00001C0001C00C001C00010000003700100001000000000000000000000000FFFF", "hex");
ns_confirm++;
} else {
// everything else is just wrong
return;
}
res.setHeader('Content-Type', 'application/dns-udpwireformat');
res.setHeader('Content-Length', content.length);
res.writeHead(200);
res.write(content);
res.end("");
return;
}
// for use with test_trr.js
else if (u.pathname === "/dns-aaaa") {
// aaaa.example.com has AAAA entry 2020:2020::2020
var content= new Buffer("0000010000010001000000000461616161076578616D706C6503636F6D00001C0001C00C001C000100000037001020202020000000000000000000002020", "hex");
res.setHeader('Content-Type', 'application/dns-udpwireformat');
res.setHeader('Content-Length', content.length);
res.writeHead(200);
res.write(content);
res.end("");
return;
}
else if (u.pathname === "/dns-rfc1918") {
// rfc1918.example.com has A entry 192.168.0.1
var content= new Buffer("0000010000010001000000000772666331393138076578616D706C6503636F6D0000010001C00C00010001000000370004C0A80001", "hex");
res.setHeader('Content-Type', 'application/dns-udpwireformat');
res.setHeader('Content-Length', content.length);
res.writeHead(200);
res.write(content);
res.end("");
return;
}
// for use with test_trr.js
else if (u.pathname === "/dns-push") {
// first.example.com has A entry 127.0.0.1
var content= new Buffer("000001000001000100000000056669727374076578616D706C6503636F6D0000010001C00C000100010000003700047F000001", "hex");
// push.example.com has AAAA entry 2018::2018
var pcontent= new Buffer("0000010000010001000000000470757368076578616D706C6503636F6D00001C0001C00C001C000100000037001020180000000000000000000000002018", "hex");
push = res.push({
hostname: 'foo.example.com:' + serverPort,
port: serverPort,
path: '/dns-pushed-response?ct&dns=AAAAAAABAAAAAAAABHB1c2gHZXhhbXBsZQNjb20AABwAAQ',
method: 'GET',
headers: {
'accept' : 'application/dns-udpwireformat'
}
});
push.writeHead(200, {
'content-type': 'application/dns-udpwireformat',
'pushed' : 'yes',
'content-length' : pcontent.length,
'X-Connection-Http2': 'yes'
});
push.end(pcontent);
res.setHeader('Content-Type', 'application/dns-udpwireformat');
res.setHeader('Content-Length', content.length);
res.writeHead(200);
res.write(content);
res.end("");
return;
}
// for use with test_trr.js
else if (u.pathname === "/dns-auth") {
// There's a Set-Cookie: header in the response for "/dns" , which this
// request subsequently would include if the http channel wasn't
// anonymous. Thus, if there's a cookie in this request, we know Firefox
// mishaved. If there's not, we're fine.
if (req.headers['cookie']) {
res.writeHead(403);
res.end("cookie for me, not for you");
return;
}
if (req.headers['authorization'] != "user:password") {
res.writeHead(401);
res.end("bad boy!");
return;
}
// bar.example.com has A entry 127.0.0.1
var content= new Buffer("00000100000100010000000003626172076578616D706C6503636F6D0000010001C00C000100010000003700047F000001", "hex");
res.setHeader('Content-Type', 'application/dns-udpwireformat');
res.setHeader('Content-Length', content.length);
res.writeHead(200);
res.write(content);
res.end("");
return;
}
else if (u.pathname === "/.well-known/http-opportunistic") {
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Content-Type', 'application/json');
res.writeHead(200, "OK");
res.end('{"http://' + req.headers['host'] + '": { "tls-ports": [' + serverPort + '] }}');
return;
}
// for PushService tests.
else if (u.pathname === "/pushSubscriptionSuccess/subscribe") {
res.setHeader("Location",
'https://localhost:' + serverPort + '/pushSubscriptionSuccesss');
res.setHeader("Link",
'</pushEndpointSuccess>; rel="urn:ietf:params:push", ' +
'</receiptPushEndpointSuccess>; rel="urn:ietf:params:push:receipt"');
res.writeHead(201, "OK");
res.end("");
return;
}
else if (u.pathname === "/pushSubscriptionSuccesss") {
// do nothing.
return;
}
else if (u.pathname === "/pushSubscriptionMissingLocation/subscribe") {
res.setHeader("Link",
'</pushEndpointMissingLocation>; rel="urn:ietf:params:push", ' +
'</receiptPushEndpointMissingLocation>; rel="urn:ietf:params:push:receipt"');
res.writeHead(201, "OK");
res.end("");
return;
}
else if (u.pathname === "/pushSubscriptionMissingLink/subscribe") {
res.setHeader("Location",
'https://localhost:' + serverPort + '/subscriptionMissingLink');
res.writeHead(201, "OK");
res.end("");
return;
}
else if (u.pathname === "/pushSubscriptionLocationBogus/subscribe") {
res.setHeader("Location", '1234');
res.setHeader("Link",
'</pushEndpointLocationBogus; rel="urn:ietf:params:push", ' +
'</receiptPushEndpointLocationBogus>; rel="urn:ietf:params:push:receipt"');
res.writeHead(201, "OK");
res.end("");
return;
}
else if (u.pathname === "/pushSubscriptionMissingLink1/subscribe") {
res.setHeader("Location",
'https://localhost:' + serverPort + '/subscriptionMissingLink1');
res.setHeader("Link",
'</receiptPushEndpointMissingLink1>; rel="urn:ietf:params:push:receipt"');
res.writeHead(201, "OK");
res.end("");
return;
}
else if (u.pathname === "/pushSubscriptionMissingLink2/subscribe") {
res.setHeader("Location",
'https://localhost:' + serverPort + '/subscriptionMissingLink2');
res.setHeader("Link",
'</pushEndpointMissingLink2>; rel="urn:ietf:params:push"');
res.writeHead(201, "OK");
res.end("");
return;
}
else if (u.pathname === "/subscriptionMissingLink2") {
// do nothing.
return;
}
else if (u.pathname === "/pushSubscriptionNot201Code/subscribe") {
res.setHeader("Location",
'https://localhost:' + serverPort + '/subscriptionNot2xxCode');
res.setHeader("Link",
'</pushEndpointNot201Code>; rel="urn:ietf:params:push", ' +
'</receiptPushEndpointNot201Code>; rel="urn:ietf:params:push:receipt"');
res.writeHead(200, "OK");
res.end("");
return;
}
else if (u.pathname ==="/pushNotifications/subscription1") {
pushPushServer1 = res.push(
{ hostname: 'localhost:' + serverPort, port: serverPort,
path : '/pushNotificationsDeliver1', method : 'GET',
headers: { 'Encryption-Key': 'keyid="notification1"; dh="BO_tgGm-yvYAGLeRe16AvhzaUcpYRiqgsGOlXpt0DRWDRGGdzVLGlEVJMygqAUECarLnxCiAOHTP_znkedrlWoU"',
'Encryption': 'keyid="notification1";salt="uAZaiXpOSfOLJxtOCZ09dA"',
'Content-Encoding': 'aesgcm128',
}
});
pushPushServer1.writeHead(200, {
'subresource' : '1'
});
pushPushServer1.end('370aeb3963f12c4f12bf946bd0a7a9ee7d3eaff8f7aec62b530fc25cfa', 'hex');
return;
}
else if (u.pathname ==="/pushNotifications/subscription2") {
pushPushServer2 = res.push(
{ hostname: 'localhost:' + serverPort, port: serverPort,
path : '/pushNotificationsDeliver3', method : 'GET',
headers: { 'Encryption-Key': 'keyid="notification2"; dh="BKVdQcgfncpNyNWsGrbecX0zq3eHIlHu5XbCGmVcxPnRSbhjrA6GyBIeGdqsUL69j5Z2CvbZd-9z1UBH0akUnGQ"',
'Encryption': 'keyid="notification2";salt="vFn3t3M_k42zHBdpch3VRw"',
'Content-Encoding': 'aesgcm128',
}
});
pushPushServer2.writeHead(200, {
'subresource' : '1'
});
pushPushServer2.end('66df5d11daa01e5c802ff97cdf7f39684b5bf7c6418a5cf9b609c6826c04b25e403823607ac514278a7da945', 'hex');
return;
}
else if (u.pathname ==="/pushNotifications/subscription3") {
pushPushServer3 = res.push(
{ hostname: 'localhost:' + serverPort, port: serverPort,
path : '/pushNotificationsDeliver3', method : 'GET',
headers: { 'Encryption-Key': 'keyid="notification3";dh="BD3xV_ACT8r6hdIYES3BJj1qhz9wyv7MBrG9vM2UCnjPzwE_YFVpkD-SGqE-BR2--0M-Yf31wctwNsO1qjBUeMg"',
'Encryption': 'keyid="notification3"; salt="DFq188piWU7osPBgqn4Nlg"; rs=24',
'Content-Encoding': 'aesgcm128',
}
});
pushPushServer3.writeHead(200, {
'subresource' : '1'
});
pushPushServer3.end('2caaeedd9cf1059b80c58b6c6827da8ff7de864ac8bea6d5775892c27c005209cbf9c4de0c3fbcddb9711d74eaeebd33f7275374cb42dd48c07168bc2cc9df63e045ce2d2a2408c66088a40c', 'hex');
return;
}
else if (u.pathname == "/pushNotifications/subscription4") {
pushPushServer4 = res.push(
{ hostname: 'localhost:' + serverPort, port: serverPort,
path : '/pushNotificationsDeliver4', method : 'GET',
headers: { 'Crypto-Key': 'keyid="notification4";dh="BJScXUUTcs7D8jJWI1AOxSgAKkF7e56ay4Lek52TqDlWo1yGd5czaxFWfsuP4j7XNWgGYm60-LKpSUMlptxPFVQ"',
'Encryption': 'keyid="notification4"; salt="sn9p2QqF3V6KBclda8vx7w"',
'Content-Encoding': 'aesgcm',
}
});
pushPushServer4.writeHead(200, {
'subresource' : '1'
});
pushPushServer4.end('9eba7ba6192544a39bd9e9b58e702d0748f1776b27f6616cdc55d29ed5a015a6db8f2dd82cd5751a14315546194ff1c18458ab91eb36c9760ccb042670001fd9964557a079553c3591ee131ceb259389cfffab3ab873f873caa6a72e87d262b8684c3260e5940b992234deebf57a9ff3a8775742f3cbcb152d249725a28326717e19cce8506813a155eff5df9bdba9e3ae8801d3cc2b7e7f2f1b6896e63d1fdda6f85df704b1a34db7b2dd63eba11ede154300a318c6f83c41a3d32356a196e36bc905b99195fd91ae4ff3f545c42d17f1fdc1d5bd2bf7516d0765e3a859fffac84f46160b79cedda589f74c25357cf6988cd8ba83867ebd86e4579c9d3b00a712c77fcea3b663007076e21f9819423faa830c2176ff1001c1690f34be26229a191a938517', 'hex');
return;
}
else if ((u.pathname === "/pushNotificationsDeliver1") ||
(u.pathname === "/pushNotificationsDeliver2") ||
(u.pathname === "/pushNotificationsDeliver3")) {
res.writeHead(410, "GONE");
res.end("");
return;
}
else if (u.pathname === "/illegalhpacksoft") {
// This will cause the compressor to compress a header that is not legal,
// but only affects the stream, not the session.
illegalheader_conn = req.stream.connection;
Compressor.prototype.compress = insertSoftIllegalHpack;
// Fall through to the default response behavior
}
else if (u.pathname === "/illegalhpackhard") {
// This will cause the compressor to insert an HPACK instruction that will
// cause a session failure.
Compressor.prototype.compress = insertHardIllegalHpack;
// Fall through to default response behavior
}
else if (u.pathname === "/illegalhpack_validate") {
if (req.stream.connection === illegalheader_conn) {
res.setHeader('X-Did-Goaway', 'no');
} else {
res.setHeader('X-Did-Goaway', 'yes');
}
// Fall through to the default response behavior
}
else if (u.pathname === "/foldedheader") {
res.setHeader('X-Folded-Header', 'this is\n folded');
// Fall through to the default response behavior
}
else if (u.pathname === "/emptydata") {
// Overwrite the original transform with our version that will insert an
// empty DATA frame at the beginning of the stream response, then fall
// through to the default response behavior.
Serializer.prototype._transform = newTransform;
}
// for use with test_immutable.js
else if (u.pathname === "/immutable-test-without-attribute") {
res.setHeader('Cache-Control', 'max-age=100000');
res.setHeader('Etag', '1');
if (req.headers["if-none-match"]) {
res.setHeader("x-conditional", "true");
}
// default response from here
}
else if (u.pathname === "/immutable-test-with-attribute") {
res.setHeader('Cache-Control', 'max-age=100000, immutable');
res.setHeader('Etag', '2');
if (req.headers["if-none-match"]) {
res.setHeader("x-conditional", "true");
}
// default response from here
}
else if (u.pathname === "/origin-4") {
var originList = [ ];
req.stream.connection.originFrame(originList);
res.setHeader("x-client-port", req.remotePort);
}
else if (u.pathname === "/origin-6") {
var originList = [ "https://alt1.example.com:" + serverPort,
"https://alt2.example.com:" + serverPort,
"https://bar.example.com:" + serverPort ];
req.stream.connection.originFrame(originList);
res.setHeader("x-client-port", req.remotePort);
}
else if (u.pathname === "/origin-11-a") {
res.setHeader("x-client-port", req.remotePort);
pushb = res.push(
{ hostname: 'foo.example.com:' + serverPort, port: serverPort, path : '/origin-11-b', method : 'GET',
headers: {'x-pushed-request': 'true', 'x-foo' : 'bar'}});
pushb.writeHead(200, {
'pushed' : 'yes',
'content-length' : 1
});
pushb.end('1');
pushc = res.push(
{ hostname: 'bar.example.com:' + serverPort, port: serverPort, path : '/origin-11-c', method : 'GET',
headers: {'x-pushed-request': 'true', 'x-foo' : 'bar'}});
pushc.writeHead(200, {
'pushed' : 'yes',
'content-length' : 1
});
pushc.end('1');
pushd = res.push(
{ hostname: 'madeup.example.com:' + serverPort, port: serverPort, path : '/origin-11-d', method : 'GET',
headers: {'x-pushed-request': 'true', 'x-foo' : 'bar'}});
pushd.writeHead(200, {
'pushed' : 'yes',
'content-length' : 1
});
pushd.end('1');
pushe = res.push(
{ hostname: 'alt1.example.com:' + serverPort, port: serverPort, path : '/origin-11-e', method : 'GET',
headers: {'x-pushed-request': 'true', 'x-foo' : 'bar'}});
pushe.writeHead(200, {
'pushed' : 'yes',
'content-length' : 1
});
pushe.end('1');
}
else if (u.pathname.substring(0,8) === "/origin-") { // test_origin.js coalescing
res.setHeader("x-client-port", req.remotePort);
}
else if (u.pathname === "/statusphrase") {
// Fortunately, the node-http2 API is dumb enough to allow this right on
// through, so we can easily test rejecting this on gecko's end.
res.writeHead("200 OK");
res.end(content);
return;
}
else if (u.pathname === "/doublepush") {
push1 = res.push('/doublypushed');
push1.writeHead(200, {
'content-type': 'text/plain',
'pushed' : 'yes',
'content-length' : 6,
'X-Connection-Http2': 'yes'
});
push1.end('pushed');
push2 = res.push('/doublypushed');
push2.writeHead(200, {
'content-type': 'text/plain',
'pushed' : 'yes',
'content-length' : 6,
'X-Connection-Http2': 'yes'
});
push2.end('pushed');
}
else if (u.pathname === "/doublypushed") {
content = 'not pushed';
}
else if (u.pathname === "/diskcache") {
content = "this was pulled via h2";
}
else if (u.pathname === "/pushindisk") {
var pushedContent = "this was pushed via h2";
push = res.push('/diskcache');
push.writeHead(200, {
'content-type': 'text/html',
'pushed' : 'yes',
'content-length' : pushedContent.length,
'X-Connection-Http2': 'yes'
});
push.end(pushedContent);
}
// For test_header_Server_Timing.js
else if (u.pathname === "/server-timing") {
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Content-Length', '12');
res.setHeader('Trailer', 'Server-Timing');
res.setHeader('Server-Timing', 'metric; dur=123.4; desc=description, metric2; dur=456.78; desc=description1');
res.write('data reached');
res.addTrailers({'Server-Timing': 'metric3; dur=789.11; desc=description2, metric4; dur=1112.13; desc=description3'});
res.end();
return;
}
res.setHeader('Content-Type', 'text/html');
if (req.httpVersionMajor != 2) {
res.setHeader('Connection', 'close');
}
res.writeHead(200);
res.end(content);
}
// Set up the SSL certs for our server - this server has a cert for foo.example.com
// signed by netwerk/tests/unit/CA.cert.der
var options = {
key: fs.readFileSync(__dirname + '/http2-key.pem'),
cert: fs.readFileSync(__dirname + '/http2-cert.pem'),
};
if (process.env.HTTP2_LOG !== undefined) {
var log_module = node_http2_root + "/test/util";
options.log = require(log_module).createLogger('server')
}
var server = http2.createServer(options, handleRequest);
server.on('connection', function(socket) {
socket.on('error', function() {
// Ignoring SSL socket errors, since they usually represent a connection that was tore down
// by the browser because of an untrusted certificate. And this happens at least once, when
// the first test case if done.
});
});
var serverPort;
function listenok() {
serverPort = server._server.address().port;
console.log('HTTP2 server listening on port ' + serverPort);
}
var portSelection = 0;
var envport = process.env.MOZHTTP2_PORT;
if (envport !== undefined) {
try {
portSelection = parseInt(envport, 10);
} catch (e) {
portSelection = -1;
}
}
server.listen(portSelection, "0.0.0.0", 200, listenok);