summaryrefslogtreecommitdiffstats
path: root/testing/mochitest/Http2Server/http2_server.js
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mochitest/Http2Server/http2_server.js')
-rw-r--r--testing/mochitest/Http2Server/http2_server.js166
1 files changed, 166 insertions, 0 deletions
diff --git a/testing/mochitest/Http2Server/http2_server.js b/testing/mochitest/Http2Server/http2_server.js
new file mode 100644
index 0000000000..798693e6f0
--- /dev/null
+++ b/testing/mochitest/Http2Server/http2_server.js
@@ -0,0 +1,166 @@
+/* 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/. */
+
+"use strict";
+
+/* globals require, __dirname, global, Buffer, process */
+
+const fs = require("fs");
+const options = {
+ key: fs.readFileSync(__dirname + "/mochitest-cert.key.pem"),
+ cert: fs.readFileSync(__dirname + "/mochitest-cert.pem"),
+ settings: {
+ enableConnectProtocol: true,
+ },
+};
+const http2 = require("http2");
+const http = require("http");
+const url = require("url");
+const path = require("path");
+
+// This is the path of node-ws when running mochitest locally.
+let node_ws_root = path.join(__dirname, "../../xpcshell/node-ws");
+if (!fs.existsSync(node_ws_root)) {
+ // This path is for running mochitest on try.
+ node_ws_root = path.join(__dirname, "./node_ws");
+}
+
+const WebSocket = require(`${node_ws_root}/lib/websocket`);
+
+let listeningPort = parseInt(process.argv[3].split("=")[1]);
+let log = function () {};
+
+function handle_h2_non_connect(stream, headers) {
+ const session = stream.session;
+ const uri = new URL(
+ `${headers[":scheme"]}://${headers[":authority"]}${headers[":path"]}`
+ );
+ const url = uri.toString();
+
+ log("REQUEST:", url);
+ log("REQUEST HEADER:", JSON.stringify(headers));
+
+ stream.on("close", () => {
+ log("REQUEST STREAM CLOSED:", stream.rstCode);
+ });
+ stream.on("error", error => {
+ log("RESPONSE STREAM ERROR", error, url, "on session:", session.__id);
+ });
+
+ let newHeaders = {};
+ for (let key in headers) {
+ if (!key.startsWith(":")) {
+ newHeaders[key] = headers[key];
+ }
+ }
+
+ const options = {
+ protocol: "http:",
+ hostname: "127.0.0.1",
+ port: 8888,
+ path: headers[":path"],
+ method: headers[":method"],
+ headers: newHeaders,
+ };
+
+ log("OPTION:", JSON.stringify(options));
+ const request = http.request(options);
+
+ stream.pipe(request);
+
+ request.on("response", response => {
+ const headers = Object.fromEntries(
+ Object.entries(response.headers).filter(
+ ([key]) =>
+ !["connection", "transfer-encoding", "keep-alive"].includes(key)
+ )
+ );
+ headers[":status"] = response.statusCode;
+ log("RESPONSE BEGIN", url, headers, "on session:", session.__id);
+
+ try {
+ stream.respond(headers);
+
+ response.on("data", data => {
+ log("RESPONSE DATA", data.length, url);
+ stream.write(data);
+ });
+ response.on("error", error => {
+ log("RESPONSE ERROR", error, url, "on session:", session.__id);
+ stream.close(http2.constants.NGHTTP2_REFUSED_STREAM);
+ });
+ response.on("end", () => {
+ log("RESPONSE END", url, "on session:", session.__id);
+ stream.end();
+ });
+ } catch (exception) {
+ log("RESPONSE EXCEPTION", exception, url, "on session:", session.__id);
+ stream.close();
+ }
+ });
+ request.on("error", error => {
+ console.error("REQUEST ERROR", error, url, "on session:", session.__id);
+ try {
+ stream.respond({
+ ":status": 502,
+ "content-type": "application/proxy-explanation+json",
+ });
+ stream.end(
+ JSON.stringify({
+ title: "request error",
+ description: error.toString(),
+ })
+ );
+ } catch (exception) {
+ stream.close();
+ }
+ });
+}
+
+let server = http2.createSecureServer(options);
+
+let session_count = 0;
+let session_id = 0;
+
+server.on("session", session => {
+ session.__id = ++session_id;
+ session.__tunnel_count = 0;
+
+ ++session_count;
+ if (session_count === 1) {
+ log(`\n\n>>> FIRST SESSION OPENING\n`);
+ }
+ log(`*** NEW SESSION`, session.__id, "( sessions:", session_count, ")");
+
+ session.on("error", error => {
+ console.error("SESSION ERROR", session.__id, error);
+ });
+ session.on("close", () => {
+ --session_count;
+ log(`*** CLOSED SESSION`, session.__id, "( sessions:", session_count, ")");
+ if (!session_count) {
+ log(`\n\n<<< LAST SESSION CLOSED\n`);
+ }
+ });
+});
+
+server.on("stream", (stream, headers) => {
+ if (headers[":method"] === "CONNECT") {
+ stream.respond();
+
+ const ws = new WebSocket(null);
+ stream.setNoDelay = () => {};
+ ws.setSocket(stream, Buffer.from(""), 100 * 1024 * 1024);
+
+ ws.on("message", data => {
+ ws.send(data);
+ });
+ } else {
+ handle_h2_non_connect(stream, headers);
+ }
+});
+
+server.listen(listeningPort);
+
+console.log(`Http2 server listening on ports ${server.address().port}`);