mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
Integrated into release/v3.8.41 (#5242/#5350 fixes). Tests green per PR.
This commit is contained in:
committed by
GitHub
parent
e174e90f14
commit
b7f6d3fd04
@@ -8,6 +8,10 @@
|
||||
|
||||
_In development — bullets added per PR; finalized at release._
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
- **cli (serve):** opt-in native **HTTPS/TLS** for `omniroute serve` — so strict-CSP Electron apps and browsers can reach OmniRoute over `https://` instead of plain `http://localhost`. Provide `--tls-cert <path> --tls-key <path>` (or `OMNIROUTE_TLS_CERT`/`OMNIROUTE_TLS_KEY`) and the standalone server terminates TLS on the same listener (no extra port/proxy); WebSocket upgrade (live dashboard + `/v1` streaming) works over `wss://` unchanged since `https.Server extends http.Server`. With no TLS flags the HTTP path is byte-identical to before; only one of cert/key, or an unreadable path, logs a warning and stays HTTP (never half-enables, never crashes). Auto-generated self-signed certs for localhost are a follow-up; for now provide an explicit cert/key (or front OmniRoute with a TLS terminator). Regression guard: `tests/unit/tls-options.test.ts`. (Bug 1C of [#5242](https://github.com/diegosouzapw/OmniRoute/issues/5242) — thanks @jonlwheat2-gif)
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
- **grok-web:** forward the Cloudflare clearance cookies and stop mislabeling IP-reputation blocks as a bad cookie. "Check cookie" returned `Invalid SSO cookie` even with a valid, complete browser session — but the cookie parser was never the problem (it robustly extracts `sso`/`sso-rw` from a full DevTools header). Two real gaps fixed: **(1)** `buildGrokCookieHeader` now forwards `cf_clearance` and `__cf_bm` when pasted (it dropped them before; AIClient2API forwards them too) — strictly additive, a bare `sso` blob still yields exactly `sso=…`; **(2)** when the user supplied a `cf_clearance`, a 401 / invalid-credentials-403 from grok.com is now surfaced as an IP-reputation/anti-bot block (cf_clearance is IP+TLS+UA-pinned and can't be replayed from a different machine) instead of the misleading "Invalid SSO cookie — re-paste". A bare cookie with no clearance still gets the re-paste hint. Regression guards in `web-cookie-auth.test.ts` + `provider-validation-specialty.test.ts`. ([#5350](https://github.com/diegosouzapw/OmniRoute/issues/5350) — thanks @SeaXen)
|
||||
|
||||
@@ -13,8 +13,13 @@ import {
|
||||
buildServerNodeOptions,
|
||||
buildNodeHeapArgs,
|
||||
} from "../../../scripts/build/runtime-env.mjs";
|
||||
import { resolveTlsOptions } from "../../../scripts/dev/tls-options.mjs";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// URL scheme for the "OmniRoute is running" banner — flipped to https when
|
||||
// opt-in TLS (#5242) is active. Process-scoped: one `serve` run = one scheme.
|
||||
let urlScheme = "http";
|
||||
const ROOT = join(__dirname, "..", "..", "..");
|
||||
// The standalone bundle ships in `dist/` (since the build-output-isolation
|
||||
// refactor). Fall back to the legacy `app/` location so an upgrade over a
|
||||
@@ -41,6 +46,14 @@ export function registerServe(program) {
|
||||
.option("--max-restarts <n>", t("serve.max_restarts"), parseInt, 2)
|
||||
.option("--tray", t("serve.tray") || "Show system tray icon (desktop only)")
|
||||
.option("--no-tray", t("serve.no_tray") || "Disable system tray icon")
|
||||
.option(
|
||||
"--tls-cert <path>",
|
||||
t("serve.tls_cert") || "Path to a TLS certificate (PEM) to serve HTTPS (also OMNIROUTE_TLS_CERT)"
|
||||
)
|
||||
.option(
|
||||
"--tls-key <path>",
|
||||
t("serve.tls_key") || "Path to the TLS private key (PEM) to serve HTTPS (also OMNIROUTE_TLS_KEY)"
|
||||
)
|
||||
.action(async (opts) => {
|
||||
await runServe(opts);
|
||||
});
|
||||
@@ -140,6 +153,11 @@ export async function runServe(opts = {}) {
|
||||
calibrateHeapFallbackMb(totalmem())
|
||||
);
|
||||
|
||||
// #5242: opt-in native HTTPS. CLI flags take precedence over env; the child
|
||||
// server (server-ws.mjs) reads these and terminates TLS on the same listener.
|
||||
const tlsCert = opts.tlsCert ?? process.env.OMNIROUTE_TLS_CERT;
|
||||
const tlsKey = opts.tlsKey ?? process.env.OMNIROUTE_TLS_KEY;
|
||||
|
||||
const env = {
|
||||
...process.env,
|
||||
OMNIROUTE_PORT: String(port),
|
||||
@@ -152,8 +170,15 @@ export async function runServe(opts = {}) {
|
||||
// `--max-old-space-size=…`) instead of clobbering it with the calibrated
|
||||
// default — mirror the Electron/standalone launchers.
|
||||
NODE_OPTIONS: buildServerNodeOptions(process.env, memoryLimit),
|
||||
...(tlsCert ? { OMNIROUTE_TLS_CERT: tlsCert } : {}),
|
||||
...(tlsKey ? { OMNIROUTE_TLS_KEY: tlsKey } : {}),
|
||||
};
|
||||
|
||||
// Validate the TLS pair up front so the operator sees a clear warning in the
|
||||
// CLI (the child re-validates authoritatively). Drives the banner scheme;
|
||||
// when null we fall through to identical plain-HTTP behavior as before.
|
||||
urlScheme = resolveTlsOptions(env) ? "https" : "http";
|
||||
|
||||
const isDaemon = opts.daemon === true;
|
||||
const useTray = opts.tray === true;
|
||||
|
||||
@@ -190,8 +215,8 @@ function runDaemon(serverJs, env, memoryLimit, dashboardPort, apiPort) {
|
||||
writePidFile("server", server.pid);
|
||||
server.unref();
|
||||
console.log(`\x1b[32m✔ OmniRoute started in background (PID: ${server.pid})\x1b[0m`);
|
||||
console.log(` \x1b[1mDashboard:\x1b[0m http://localhost:${dashboardPort}`);
|
||||
console.log(` \x1b[1mAPI Base:\x1b[0m http://localhost:${apiPort}/v1`);
|
||||
console.log(` \x1b[1mDashboard:\x1b[0m ${urlScheme}://localhost:${dashboardPort}`);
|
||||
console.log(` \x1b[1mAPI Base:\x1b[0m ${urlScheme}://localhost:${apiPort}/v1`);
|
||||
}
|
||||
|
||||
function runWithoutRecovery(serverJs, env, memoryLimit, dashboardPort, apiPort, noOpen) {
|
||||
@@ -321,7 +346,7 @@ async function maybeStartTray(port, apiPort, supervisor) {
|
||||
const { initTray, isTraySupported } = await import("../tray/index.mjs");
|
||||
if (!isTraySupported()) return;
|
||||
const { default: open } = await import("open").catch(() => ({ default: null }));
|
||||
const dashboardUrl = `http://localhost:${port}`;
|
||||
const dashboardUrl = `${urlScheme}://localhost:${port}`;
|
||||
const tray = await initTray({
|
||||
port,
|
||||
onQuit: () => {
|
||||
@@ -348,8 +373,8 @@ async function maybeStartTray(port, apiPort, supervisor) {
|
||||
}
|
||||
|
||||
async function onReady(dashboardPort, apiPort, noOpen) {
|
||||
const dashboardUrl = `http://localhost:${dashboardPort}`;
|
||||
const apiUrl = `http://localhost:${apiPort}`;
|
||||
const dashboardUrl = `${urlScheme}://localhost:${dashboardPort}`;
|
||||
const apiUrl = `${urlScheme}://localhost:${apiPort}`;
|
||||
|
||||
console.log(`
|
||||
\x1b[32m✔ OmniRoute is running!\x1b[0m
|
||||
|
||||
@@ -241,7 +241,9 @@
|
||||
"no_recovery": "Disable auto-restart on crash (debugging mode)",
|
||||
"max_restarts": "Max crash restarts within 30s before giving up (default: 2)",
|
||||
"tray": "Show system tray icon (desktop only, opt-in)",
|
||||
"no_tray": "Disable system tray icon"
|
||||
"no_tray": "Disable system tray icon",
|
||||
"tls_cert": "Path to a TLS certificate (PEM) to serve HTTPS (also OMNIROUTE_TLS_CERT)",
|
||||
"tls_key": "Path to the TLS private key (PEM) to serve HTTPS (also OMNIROUTE_TLS_KEY)"
|
||||
},
|
||||
"backup": {
|
||||
"title": "Backup",
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"scripts/build/colocateOptionals.mjs",
|
||||
"scripts/build/sync-env.mjs",
|
||||
"scripts/dev/responses-ws-proxy.mjs",
|
||||
"scripts/dev/tls-options.mjs",
|
||||
"scripts/check/check-supported-node-runtime.ts",
|
||||
"scripts/dev/sync-env.mjs",
|
||||
"scripts/build/native-binary-compat.mjs",
|
||||
|
||||
@@ -149,6 +149,12 @@ const EXTRA_MODULE_ENTRIES = [
|
||||
src: ["scripts", "dev", "webdav-handler.mjs"],
|
||||
dest: ["webdav-handler.mjs"],
|
||||
},
|
||||
{
|
||||
// #5242: opt-in HTTPS/TLS resolver (server-ws.mjs dependency).
|
||||
label: "tls-options (server-ws.mjs dependency)",
|
||||
src: ["scripts", "dev", "tls-options.mjs"],
|
||||
dest: ["tls-options.mjs"],
|
||||
},
|
||||
{
|
||||
label: "runtime-env script",
|
||||
src: ["scripts", "build", "runtime-env.mjs"],
|
||||
|
||||
@@ -5,11 +5,23 @@ import { createResponsesWsProxy } from "./responses-ws-proxy.mjs";
|
||||
import { ensurePeerStampToken, wrapRequestListenerWithPeerStamp } from "./peer-stamp.mjs";
|
||||
import { maybeHandleWebdav } from "./webdav-handler.mjs";
|
||||
import methodGuard from "./http-method-guard.cjs";
|
||||
import { resolveTlsOptions, createServerListener } from "./tls-options.mjs";
|
||||
|
||||
const originalCreateServer = http.createServer.bind(http);
|
||||
const proxiesByPort = new Map();
|
||||
const { wrapRequestListenerWithMethodGuard } = methodGuard;
|
||||
|
||||
// Opt-in native HTTPS (#5242). Resolved once at boot: when both OMNIROUTE_TLS_CERT
|
||||
// and OMNIROUTE_TLS_KEY point at readable files we terminate TLS on the same
|
||||
// listener Next binds to (so WS `upgrade` / request wrappers keep working over
|
||||
// TLS). Absent or misconfigured → null → identical plain-HTTP behavior as before.
|
||||
const tlsOptions = resolveTlsOptions(process.env);
|
||||
if (tlsOptions) {
|
||||
console.log(
|
||||
`[omniroute][tls] HTTPS enabled — terminating TLS with cert=${tlsOptions.certPath}`
|
||||
);
|
||||
}
|
||||
|
||||
process.env.OMNIROUTE_WS_BRIDGE_SECRET ||= randomUUID();
|
||||
// Per-process secret proving the trusted peer-IP stamp came from this server.
|
||||
ensurePeerStampToken();
|
||||
@@ -107,7 +119,10 @@ http.createServer = function createServerWithResponsesWs(...args) {
|
||||
);
|
||||
}
|
||||
|
||||
const server = originalCreateServer(...args);
|
||||
// When TLS is configured, return an https.Server (terminating TLS on the same
|
||||
// listener); otherwise the original http.Server. The downstream .on/.addListener
|
||||
// patches below apply identically to both (https.Server extends http.Server).
|
||||
const server = createServerListener(args, tlsOptions, { createHttp: originalCreateServer });
|
||||
const originalOn = server.on.bind(server);
|
||||
const originalAddListener = server.addListener.bind(server);
|
||||
|
||||
|
||||
92
scripts/dev/tls-options.mjs
Normal file
92
scripts/dev/tls-options.mjs
Normal file
@@ -0,0 +1,92 @@
|
||||
// scripts/dev/tls-options.mjs
|
||||
//
|
||||
// Pure, dependency-light helpers for OmniRoute's opt-in native HTTPS/TLS serving
|
||||
// (#5242, Bug 1C). Kept side-effect-free and free of heavy imports so it can be
|
||||
// imported both by the CLI (bin/cli/commands/serve.mjs) and by the standalone
|
||||
// server wrapper (standalone-server-ws.mjs), and unit-tested in isolation.
|
||||
//
|
||||
// TLS is strictly opt-in: when neither cert nor key is provided the server
|
||||
// behaves EXACTLY as before (plain HTTP). A misconfiguration (only one of the
|
||||
// pair, or an unreadable path) NEVER crashes the server — it logs a warning and
|
||||
// falls back to HTTP, preserving today's behavior and loopback/security posture.
|
||||
|
||||
import fs from "node:fs";
|
||||
import http from "node:http";
|
||||
import https from "node:https";
|
||||
|
||||
/**
|
||||
* Resolve TLS options from environment variables.
|
||||
*
|
||||
* Reads `OMNIROUTE_TLS_CERT` and `OMNIROUTE_TLS_KEY` (filesystem paths). Returns
|
||||
* `{ cert, key }` (file contents) only when BOTH are provided and readable.
|
||||
* Otherwise returns `null` so the caller serves plain HTTP — never throwing.
|
||||
*
|
||||
* @param {NodeJS.ProcessEnv} [env=process.env]
|
||||
* @param {{ readFileSync?: typeof fs.readFileSync, warn?: (msg: string) => void }} [deps]
|
||||
* @returns {{ cert: Buffer|string, key: Buffer|string, certPath: string, keyPath: string } | null}
|
||||
*/
|
||||
export function resolveTlsOptions(
|
||||
env = process.env,
|
||||
{ readFileSync = fs.readFileSync, warn = (m) => console.warn(m) } = {}
|
||||
) {
|
||||
const certPath = typeof env?.OMNIROUTE_TLS_CERT === "string" ? env.OMNIROUTE_TLS_CERT.trim() : "";
|
||||
const keyPath = typeof env?.OMNIROUTE_TLS_KEY === "string" ? env.OMNIROUTE_TLS_KEY.trim() : "";
|
||||
|
||||
// Neither provided → plain HTTP, no warning (the common, default case).
|
||||
if (!certPath && !keyPath) return null;
|
||||
|
||||
// Only one of the pair → never half-enable TLS. Warn + fall back to HTTP.
|
||||
if (!certPath || !keyPath) {
|
||||
warn(
|
||||
`[omniroute][tls] HTTPS not enabled: both OMNIROUTE_TLS_CERT and OMNIROUTE_TLS_KEY ` +
|
||||
`are required (only ${certPath ? "cert" : "key"} provided). Serving HTTP.`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Both provided → read them. A bad/unreadable path falls back to HTTP rather
|
||||
// than crashing the server over a TLS misconfiguration.
|
||||
try {
|
||||
const cert = readFileSync(certPath);
|
||||
const key = readFileSync(keyPath);
|
||||
return { cert, key, certPath, keyPath };
|
||||
} catch (err) {
|
||||
warn(
|
||||
`[omniroute][tls] HTTPS not enabled: could not read TLS cert/key ` +
|
||||
`(${err?.code || err?.message || String(err)}). Serving HTTP.`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create either an `https.Server` (when `tlsOptions` is provided) or an
|
||||
* `http.Server` (when it is `null`), forwarding the same request-listener /
|
||||
* options arguments the caller would otherwise pass to `http.createServer`.
|
||||
*
|
||||
* When TLS is enabled, any leading options object is merged with `cert`/`key`
|
||||
* and the trailing request listener is preserved, so existing wiring (WebSocket
|
||||
* `upgrade` handling, request wrappers) keeps working over TLS unchanged.
|
||||
*
|
||||
* @param {any[]} args - the args originally passed to http.createServer (options?, listener?)
|
||||
* @param {{ cert: Buffer|string, key: Buffer|string } | null} tlsOptions
|
||||
* @param {{ createHttp?: Function, createHttps?: Function }} [deps]
|
||||
* @returns {import("node:http").Server | import("node:https").Server}
|
||||
*/
|
||||
export function createServerListener(
|
||||
args,
|
||||
tlsOptions,
|
||||
{ createHttp = http.createServer.bind(http), createHttps = https.createServer.bind(https) } = {}
|
||||
) {
|
||||
const argList = Array.isArray(args) ? args : args === undefined ? [] : [args];
|
||||
|
||||
if (!tlsOptions) return createHttp(...argList);
|
||||
|
||||
const { cert, key } = tlsOptions;
|
||||
const lastFnIdx = argList.map((a) => typeof a === "function").lastIndexOf(true);
|
||||
const listener = lastFnIdx >= 0 ? argList[lastFnIdx] : undefined;
|
||||
const baseOpts =
|
||||
argList[0] && typeof argList[0] === "object" && !Buffer.isBuffer(argList[0]) ? argList[0] : {};
|
||||
const merged = { ...baseOpts, cert, key };
|
||||
return listener ? createHttps(merged, listener) : createHttps(merged);
|
||||
}
|
||||
160
tests/unit/tls-options.test.ts
Normal file
160
tests/unit/tls-options.test.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* #5242 (Bug 1C) — opt-in native HTTPS/TLS serving for `omniroute serve`.
|
||||
*
|
||||
* `resolveTlsOptions` + `createServerListener` (scripts/dev/tls-options.mjs) are
|
||||
* the pure decision helpers the CLI (serve.mjs) and the standalone server
|
||||
* wrapper (standalone-server-ws.mjs) share. TLS is strictly opt-in:
|
||||
* - both cert+key present & readable → TLS options → https.Server
|
||||
* - neither present → null → http.Server (byte-identical to today)
|
||||
* - only one of the pair → null + warning (never half-enable TLS)
|
||||
* - unreadable path → null + warning (never crash over a TLS misconfig)
|
||||
*
|
||||
* Filesystem reads and the http/https factories are injected so the suite is
|
||||
* deterministic and needs no real certs.
|
||||
*/
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import http from "node:http";
|
||||
import https from "node:https";
|
||||
|
||||
const { resolveTlsOptions, createServerListener } = await import(
|
||||
"../../scripts/dev/tls-options.mjs"
|
||||
);
|
||||
|
||||
function makeReader(map: Record<string, string>) {
|
||||
return (p: string) => {
|
||||
if (p in map) return Buffer.from(map[p]);
|
||||
const err: NodeJS.ErrnoException = new Error(`ENOENT: ${p}`);
|
||||
err.code = "ENOENT";
|
||||
throw err;
|
||||
};
|
||||
}
|
||||
|
||||
test("both cert+key provided and readable → returns TLS options", () => {
|
||||
const warnings: string[] = [];
|
||||
const opts = resolveTlsOptions(
|
||||
{ OMNIROUTE_TLS_CERT: "/c/server.crt", OMNIROUTE_TLS_KEY: "/c/server.key" },
|
||||
{ readFileSync: makeReader({ "/c/server.crt": "CERT", "/c/server.key": "KEY" }), warn: (m) => warnings.push(m) }
|
||||
);
|
||||
assert.ok(opts, "expected non-null TLS options");
|
||||
assert.equal(opts.cert.toString(), "CERT");
|
||||
assert.equal(opts.key.toString(), "KEY");
|
||||
assert.equal(opts.certPath, "/c/server.crt");
|
||||
assert.deepEqual(warnings, [], "no warning when correctly configured");
|
||||
});
|
||||
|
||||
test("neither cert nor key → null, no warning (default HTTP path)", () => {
|
||||
const warnings: string[] = [];
|
||||
const opts = resolveTlsOptions({}, { warn: (m) => warnings.push(m) });
|
||||
assert.equal(opts, null);
|
||||
assert.deepEqual(warnings, []);
|
||||
});
|
||||
|
||||
test("only cert provided → null + warning (never half-enable TLS)", () => {
|
||||
const warnings: string[] = [];
|
||||
const opts = resolveTlsOptions(
|
||||
{ OMNIROUTE_TLS_CERT: "/c/server.crt" },
|
||||
{ warn: (m) => warnings.push(m) }
|
||||
);
|
||||
assert.equal(opts, null);
|
||||
assert.equal(warnings.length, 1);
|
||||
assert.match(warnings[0], /both OMNIROUTE_TLS_CERT and OMNIROUTE_TLS_KEY/);
|
||||
});
|
||||
|
||||
test("only key provided → null + warning", () => {
|
||||
const warnings: string[] = [];
|
||||
const opts = resolveTlsOptions(
|
||||
{ OMNIROUTE_TLS_KEY: "/c/server.key" },
|
||||
{ warn: (m) => warnings.push(m) }
|
||||
);
|
||||
assert.equal(opts, null);
|
||||
assert.equal(warnings.length, 1);
|
||||
assert.match(warnings[0], /both OMNIROUTE_TLS_CERT and OMNIROUTE_TLS_KEY/);
|
||||
});
|
||||
|
||||
test("unreadable path → null + warning, falls back to HTTP (never crash)", () => {
|
||||
const warnings: string[] = [];
|
||||
const opts = resolveTlsOptions(
|
||||
{ OMNIROUTE_TLS_CERT: "/missing.crt", OMNIROUTE_TLS_KEY: "/missing.key" },
|
||||
{ readFileSync: makeReader({}), warn: (m) => warnings.push(m) }
|
||||
);
|
||||
assert.equal(opts, null);
|
||||
assert.equal(warnings.length, 1);
|
||||
assert.match(warnings[0], /could not read TLS cert\/key/);
|
||||
});
|
||||
|
||||
test("whitespace-only env values are treated as absent", () => {
|
||||
const opts = resolveTlsOptions(
|
||||
{ OMNIROUTE_TLS_CERT: " ", OMNIROUTE_TLS_KEY: " " },
|
||||
{ warn: () => {} }
|
||||
);
|
||||
assert.equal(opts, null);
|
||||
});
|
||||
|
||||
test("createServerListener: null tlsOptions → http server (unchanged)", () => {
|
||||
let httpCalled = false;
|
||||
let httpsCalled = false;
|
||||
const listener = () => {};
|
||||
const result = createServerListener([listener], null, {
|
||||
createHttp: (...a: unknown[]) => {
|
||||
httpCalled = true;
|
||||
assert.equal(a[a.length - 1], listener);
|
||||
return "HTTP_SERVER";
|
||||
},
|
||||
createHttps: () => {
|
||||
httpsCalled = true;
|
||||
return "HTTPS_SERVER";
|
||||
},
|
||||
});
|
||||
assert.equal(result, "HTTP_SERVER");
|
||||
assert.ok(httpCalled && !httpsCalled);
|
||||
});
|
||||
|
||||
test("createServerListener: tlsOptions → https server with merged cert/key + listener", () => {
|
||||
let httpCalled = false;
|
||||
const listener = () => {};
|
||||
const result = createServerListener([listener], { cert: "CERT", key: "KEY" }, {
|
||||
createHttp: () => {
|
||||
httpCalled = true;
|
||||
return "HTTP_SERVER";
|
||||
},
|
||||
createHttps: (opts: { cert: string; key: string }, fn: unknown) => {
|
||||
assert.equal(opts.cert, "CERT");
|
||||
assert.equal(opts.key, "KEY");
|
||||
assert.equal(fn, listener);
|
||||
return "HTTPS_SERVER";
|
||||
},
|
||||
});
|
||||
assert.equal(result, "HTTPS_SERVER");
|
||||
assert.ok(!httpCalled);
|
||||
});
|
||||
|
||||
test("createServerListener: merges a leading options object with cert/key", () => {
|
||||
const listener = () => {};
|
||||
createServerListener([{ keepAlive: true }, listener], { cert: "C", key: "K" }, {
|
||||
createHttps: (opts: Record<string, unknown>, fn: unknown) => {
|
||||
assert.equal(opts.keepAlive, true);
|
||||
assert.equal(opts.cert, "C");
|
||||
assert.equal(opts.key, "K");
|
||||
assert.equal(fn, listener);
|
||||
return "HTTPS_SERVER";
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("createServerListener: real default (no TLS) returns an http.Server", () => {
|
||||
const server = createServerListener([], null);
|
||||
assert.ok(server instanceof http.Server);
|
||||
server.close();
|
||||
});
|
||||
|
||||
test("createServerListener: with a real cert pair returns a real https.Server", async () => {
|
||||
const { default: selfsigned } = await import("selfsigned");
|
||||
const pems = selfsigned.generate([{ name: "commonName", value: "localhost" }], {
|
||||
keySize: 2048,
|
||||
algorithm: "sha256",
|
||||
});
|
||||
const server = createServerListener([() => {}], { cert: pems.cert, key: pems.private });
|
||||
assert.ok(server instanceof https.Server, "expected a real https.Server instance");
|
||||
server.close();
|
||||
});
|
||||
Reference in New Issue
Block a user