Bug 1731981 - Add a http2 server that accepts websocket, r=necko-reviewers,valentin

Differential Revision: https://phabricator.services.mozilla.com/D157807
This commit is contained in:
Kershaw Chang 2022-09-29 19:18:40 +00:00
parent aa9d8ed88b
commit 24a02945b2
2 changed files with 90 additions and 0 deletions

View file

@ -648,6 +648,79 @@ class NodeWebSocketServer extends BaseNodeServer {
} }
} }
// websocket http2 server
// This code is inspired by
// https://github.com/szmarczak/http2-wrapper/blob/master/examples/ws/server.js
class NodeWebSocketHttp2ServerCode extends BaseNodeHTTPServerCode {
static async startServer(port) {
const fs = require("fs");
const options = {
key: fs.readFileSync(__dirname + "/http2-cert.key"),
cert: fs.readFileSync(__dirname + "/http2-cert.pem"),
settings: {
enableConnectProtocol: true,
},
};
const http2 = require("http2");
global.h2Server = http2.createSecureServer(options);
let node_ws_root = `${__dirname}/../node-ws`;
const WebSocket = require(`${node_ws_root}/lib/websocket`);
global.h2Server.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);
global.ws = ws;
ws.on("message", data => {
if (global.wsInputHandler) {
global.wsInputHandler(data);
return;
}
ws.send("test");
});
} else {
stream.respond();
stream.end("ok");
}
});
await global.h2Server.listen(port);
let serverPort = global.h2Server.address().port;
await ADB.forwardPort(serverPort);
return serverPort;
}
}
class NodeWebSocketHttp2Server extends BaseNodeServer {
_protocol = "h2ws";
/// Starts the server
/// @port - default 0
/// when provided, will attempt to listen on that port.
async start(port = 0) {
this.processId = await NodeServer.fork();
await this.execute(BaseNodeHTTPServerCode);
await this.execute(NodeWebSocketHttp2ServerCode);
await this.execute(ADB);
this._port = await this.execute(
`NodeWebSocketHttp2ServerCode.startServer(${port})`
);
await this.execute(`global.path_handlers = {};`);
await this.execute(`global.wsInputHandler = null;`);
}
async registerMessageHandler(handler) {
return this.execute(`global.wsInputHandler = ${handler.toString()}`);
}
}
// Helper functions // Helper functions
async function with_node_servers(arrayOfClasses, asyncClosure) { async function with_node_servers(arrayOfClasses, asyncClosure) {

View file

@ -132,3 +132,20 @@ add_task(async function test_ws_through_https_proxy() {
await proxy.stop(); await proxy.stop();
await wss.stop(); await wss.stop();
}); });
add_task(async function test_websocket_over_h2() {
Services.prefs.setBoolPref("network.http.http2.websockets", true);
let wss = new NodeWebSocketHttp2Server();
await wss.start();
Assert.notEqual(wss.port(), null);
await wss.registerMessageHandler(data => {
global.ws.send(data);
});
let chan = makeWebSocketChan();
let url = `wss://localhost:${wss.port()}`;
const msg = "test websocket";
let [status, res] = await channelOpenPromise(chan, url, msg);
Assert.equal(status, Cr.NS_OK);
Assert.equal(res, msg);
await wss.stop();
});