зеркало из https://github.com/mozilla/gecko-dev.git
895 строки
28 KiB
JavaScript
895 строки
28 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;
|
|
|
|
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']);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|