/* 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
/* eslint-env node */
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"); const dnsPacket = require(`${node_http2_root}/../dns-packet`); const ip = require(`${node_http2_root}/../node_ip`); const { fork } = require("child_process"); const { spawn } = require("child_process"); const path = require("path"); const zlib = require("zlib");
// 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) { if (frame.type == "DATA") { // Insert our empty DATA frame const emptyFrame = {};
emptyFrame.type = "DATA";
emptyFrame.data = Buffer.alloc(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(pathName) { var content = "" + "" + "HOORAY!" + // 'You Win!' used in tests to check we reached this server "You Win! (by requesting" +
pathName + ")" + ""; 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,
send(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 { // Clear these variables so we can run the test again with --verify if (res == this.mp1res) { this.mp1res = null;
} else { this.mp2res = null;
}
res.end();
}
},
};
var runlater = function () {};
runlater.prototype = {
req: null,
resp: null,
fin: true,
onTimeout: function onTimeout() { this.resp.writeHead(200); if (this.fin) { this.resp.end("It's all good 750ms.");
}
},
};
var runConnectLater = function () {};
runConnectLater.prototype = {
req: null,
resp: null,
connect: false,
var moreData = function () {};
moreData.prototype = {
req: null,
resp: null,
iter: 3,
onTimeout: function onTimeout() { // 1mb of data const content = generateContent(1024 * 1024); this.resp.write(content); // 1mb chunk this.iter--; if (!this.iter) { this.resp.end();
} else {
setTimeout(executeRunLater, 1, this);
}
},
};
var resetLater = function () {};
resetLater.prototype = {
resp: null,
onTimeout: function onTimeout() { this.resp.stream.reset("HTTP_1_1_REQUIRED");
},
};
function executeRunLater(arg) {
arg.onTimeout();
}
function executeRunLaterCatchError(arg) {
arg.onTimeout();
}
var h11required_conn = null; var h11required_header = "yes"; var didRst = false; var rstConnection = null; var illegalheader_conn = null;
var gDoHPortsLog = []; var gDoHNewConnLog = {}; var gDoHRequestCount = 0;
// eslint-disable-next-line complexity function handleRequest(req, res) { var u = ""; if (req.url != undefined) {
u = url.parse(req.url, true);
} var content = getHttpContent(u.pathname); var push, push1, push1a, push2, push3;
// PushService tests. var pushPushServer1, pushPushServer2, pushPushServer3, pushPushServer4;
function createCNameARecord() { // test23 asks for cname-a.example.com // this responds with a CNAME to here.example.com *and* an A record // for here.example.com
let rContent;
rContent = Buffer.from( "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"
);
return rContent;
}
function responseType(packet, responseIP) { if (
!!packet.questions.length &&
packet.questions[0].name == "confirm.example.com" &&
packet.questions[0].type == "NS"
) { return"NS";
}
return ip.isV4Format(responseIP) ? "A" : "AAAA";
}
function handleAuth() { // 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"); returnfalse;
} if (req.headers.authorization != "user:password") {
res.writeHead(401);
res.end("bad boy!"); returnfalse;
}
returntrue;
}
function createDNSAnswer(response, packet, responseIP, requestPayload) { // This shuts down the connection so we can test if the client reconnects if (packet.questions.length && packet.questions[0].name == "closeme.com") {
response.stream.connection.close("INTERNAL_ERROR", response.stream.id); returnnull;
}
let answers = []; if (packet.questions.length && packet.questions[0].name.endsWith(".pd")) { // Bug 1543811: test edns padding extension. Return whether padding was // included via the first half of the ip address (1.1 vs 2.2) and the // size of the request in the second half of the ip address allowing to // verify that the correct amount of padding was added. if (
!!packet.additionals.length &&
packet.additionals[0].type == "OPT" &&
packet.additionals[0].options.some(o => o.type === "PADDING")
) { // add padding to the response, because the client must be able ignore it
answers.push({
name: ".",
type: "PADDING",
data: Buffer.from( // PADDING_PADDING_PADDING "50414444494e475f50414444494e475f50414444494e47", "hex"
),
});
responseIP = "1.1." +
((requestPayload.length >> 8) & 0xff) + "." +
(requestPayload.length & 0xff);
} else {
responseIP = "2.2." +
((requestPayload.length >> 8) & 0xff) + "." +
(requestPayload.length & 0xff);
}
}
if (u.query.corruptedAnswer) { // DNS response header is 12 bytes, we check for this minimum length // at the start of decoding so this is the simplest way to force // a decode error. return"\xFF\xFF\xFF\xFF";
}
// Because we send two TRR requests (A and AAAA), skip the first two // requests when testing retry. if (u.query.retryOnDecodeFailure && gDoHRequestCount < 2) {
gDoHRequestCount++; return"\xFF\xFF\xFF\xFF";
}
function responseData() { if (
!!packet.questions.length &&
packet.questions[0].name == "confirm.example.com" &&
packet.questions[0].type == "NS"
) { return"ns.example.com";
}
if (req.headers["accept-language"] || req.headers["user-agent"]) { // If we get this header, don't send back any response. This should // cause the tests to fail. This is easier then actually sending back // the header value into test_trr.js
answers = [];
}
function getDelayFromPacket(packet, type) {
let delay = 0; if (packet.questions[0].type == "A") {
delay = parseInt(u.query.delayIPv4);
} elseif (packet.questions[0].type == "AAAA") {
delay = parseInt(u.query.delayIPv6);
}
if (u.query.slowConfirm && type == "NS") {
delay += 1000;
}
return delay;
}
function writeDNSResponse(response, buf, delay, contentType) { function writeResponse(resp, buffer) {
resp.setHeader("Set-Cookie", "trackyou=yes; path=/; max-age=100000;");
resp.setHeader("Content-Type", contentType); if (req.headers["accept-encoding"].includes("gzip")) {
zlib.gzip(buffer, function (err, result) {
resp.setHeader("Content-Encoding", "gzip");
resp.setHeader("Content-Length", result.length); try {
resp.writeHead(200);
resp.end(result);
} catch (e) { // connection was closed by the time we started writing.
}
});
} else { const output = Buffer.from(buffer, "utf-8");
resp.setHeader("Content-Length", output.length); try {
resp.writeHead(200);
resp.write(output);
resp.end("");
} catch (e) { // connection was closed by the time we started writing.
}
}
}
pushPushServer4.end( "9eba7ba6192544a39bd9e9b58e702d0748f1776b27f6616cdc55d29ed5a015a6db8f2dd82cd5751a14315546194ff1c18458ab91eb36c9760ccb042670001fd9964557a079553c3591ee131ceb259389cfffab3ab873f873caa6a72e87d262b8684c3260e5940b992234deebf57a9ff3a8775742f3cbcb152d249725a28326717e19cce8506813a155eff5df9bdba9e3ae8801d3cc2b7e7f2f1b6896e63d1fdda6f85df704b1a34db7b2dd63eba11ede154300a318c6f83c41a3d32356a196e36bc905b99195fd91ae4ff3f545c42d17f1fdc1d5bd2bf7516d0765e3a859fffac84f46160b79cedda589f74c25357cf6988cd8ba83867ebd86e4579c9d3b00a712c77fcea3b663007076e21f9819423faa830c2176ff1001c1690f34be26229a191a938517", "hex"
); return;
} elseif (
u.pathname === "/pushNotificationsDeliver1" ||
u.pathname === "/pushNotificationsDeliver2" ||
u.pathname === "/pushNotificationsDeliver3"
) {
res.writeHead(410, "GONE");
res.end(""); return;
} elseif (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;
res.setHeader("Content-Type", "text/html");
res.setHeader("x-softillegalhpack", "true");
res.writeHead(200);
res.end(content); return;
} elseif (u.pathname === "/illegalhpackhard") { // This will cause the compressor to insert an HPACK instruction that will // cause a session failure.
res.setHeader("Content-Type", "text/html");
res.setHeader("x-hardillegalhpack", "true");
res.writeHead(200);
res.end(content); return;
} elseif (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
} elseif (u.pathname === "/foldedheader") {
res.setHeader("X-Folded-Header", "this is\n folded"); // Fall through to the default response behavior
} elseif (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 elseif (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
} elseif (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
} elseif (u.pathname === "/immutable-test-expired-with-Expires-header") {
res.setHeader("Cache-Control", "immutable");
res.setHeader("Expires", "Mon, 01 Jan 1990 00:00:00 GMT");
res.setHeader("Etag", "3");
// Set up the SSL certs for our server - this server has a cert for foo.example.com // signed by netwerk/tests/unit/http2-ca.pem var options = {
key: fs.readFileSync(__dirname + "/http2-cert.key"),
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.
});
});
server.on("connect", function (req, clientSocket) {
clientSocket.write( "HTTP/1.1 404 Not Found\r\nProxy-agent: Node.js-Proxy\r\n\r\n"
);
clientSocket.destroy();
});
function makeid(length) { var result = ""; var characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; var charactersLength = characters.length; for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
} return result;
}
const http = require("http");
let httpServer = http.createServer((req, res) => { if (req.method != "POST") {
let u = url.parse(req.url, true); if (u.pathname == "/test") { // This path is used to test that the server is working properly
res.writeHead(200);
res.end("OK"); return;
}
res.writeHead(405);
res.end("Unexpected method: " + req.method); return;
}
let code = "";
req.on("data", function receivePostData(chunk) {
code += chunk;
});
req.on("end", function finishPost() {
let u = url.parse(req.url, true); if (u.pathname == "/fork") {
let id = forkProcess();
computeAndSendBackResponse(id); return;
}
if (u.pathname.startsWith("/kill/")) {
let id = u.pathname.slice(6);
let forked = globalObjects[id]; if (!forked) {
computeAndSendBackResponse(undefined, new Error("could not find id")); return;
}
new Promise((resolve, reject) => {
forked.resolve = resolve;
forked.reject = reject;
forked.kill();
})
.then(x =>
computeAndSendBackResponse(
undefined, new Error(`incorrectly resolved ${x}`)
)
)
.catch(e => { // We indicate a proper shutdown by resolving with undefined. if (e && e.toString().match(/child process exit closing code/)) {
e = undefined;
}
computeAndSendBackResponse(undefined, e);
}); return;
}
if (u.pathname.startsWith("/execute/")) {
let id = u.pathname.slice(9);
let forked = globalObjects[id]; if (!forked) {
computeAndSendBackResponse(undefined, new Error("could not find id")); return;
}
function forkH3Server(serverPath, dbPath) { const args = [dbPath];
let process = spawn(serverPath, args);
let id = forkProcessInternal(process); // Return a promise that resolves when we receive data from stdout returnnew Promise((resolve, _) => {
process.stdout.on("data", data => {
console.log(data.toString());
resolve({ id, output: data.toString().trim() });
});
});
}
function forkProcess() {
let scriptPath = path.resolve(__dirname, "moz-http2-child.js");
let forked = fork(scriptPath); return forkProcessInternal(forked);
}
function forkProcessInternal(forked) {
let id = makeid(6);
forked.errors = "";
globalObjects[id] = forked;
forked.on("message", msg => { if (forked.resolve) {
forked.resolve(msg);
forked.resolve = null;
} else {
console.log(
`forked process without handler sent: ${JSON.stringify(msg)}`
);
forked.errors += `forked process without handler sent: ${JSON.stringify(
msg
)}\n`;
}
});
let exitFunction = (code, signal) => { if (globalObjects[id]) { delete globalObjects[id];
} else { // already called return;
}
if (!forked.reject) {
console.log(
`child process ${id} closing code: ${code} signal: ${signal}`
); return;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.