Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ Options:
-V, --version output the version number
--ip <ip-address> Public-facing IP of the proxy
--port <n> (defaults to 8000) Public-facing port of the proxy
--socket <path> Path to a UNIX domain socket for the proxy to listen on. Alternative to specifying IP and port.
--ssl-key <keyfile> SSL key to use, if any
--ssl-cert <certfile> SSL certificate to use, if any
--ssl-ca <ca-file> SSL certificate authority, if any
Expand All @@ -122,6 +123,7 @@ Options:
--ssl-dhparam <dhparam-file> SSL Diffie-Helman Parameters pem file, if any
--api-ip <ip> Inward-facing IP for API requests (default: "localhost")
--api-port <n> Inward-facing port for API requests (defaults to --port=value+1)
--api-socket <path> Path to a UNIX domain socket for the API to listen on. Alternative to specifying API IP and port.
--api-ssl-key <keyfile> SSL key to use, if any, for API requests
--api-ssl-cert <certfile> SSL certificate to use, if any, for API requests
--api-ssl-ca <ca-file> SSL certificate authority, if any, for API requests
Expand Down Expand Up @@ -149,6 +151,7 @@ Options:
--host-routing Use host routing (host as first level of path)
--metrics-ip <ip> IP for metrics server (default: "")
--metrics-port <n> Port of metrics server. Defaults to no metrics server
--metrics-socket <path> Path to a UNIX domain socket for the metrics server to listen on. Alternative to specifying metrics IP and port.
--log-level <loglevel> Log level (debug, info, warn, error) (default: "info")
--timeout <n> Timeout (in millis) when proxy drops connection for a request.
--proxy-timeout <n> Timeout (in millis) when proxy receives no response from target.
Expand Down
50 changes: 20 additions & 30 deletions bin/configurable-http-proxy
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
//

import fs from "node:fs";
import { Command } from "commander";
import { Command, Option } from "commander";

import ConfigurableProxy from "../lib/configproxy.js";
import { parseListenOptions } from "../lib/configproxy.js";

import { defaultLogger } from "../lib/log.js";

import { createRequire } from "node:module";
Expand All @@ -21,6 +23,7 @@ cli
.version(pkg.version)
.option("--ip <ip-address>", "Public-facing IP of the proxy")
.option("--port <n> (defaults to 8000)", "Public-facing port of the proxy", parseInt)
.addOption(new Option("--socket <path>", "Path to a UNIX domain socket for the proxy to listen on. Alternative to specifying IP and port.").conflicts(['port', 'ip']))
.option("--ssl-key <keyfile>", "SSL key to use, if any")
.option("--ssl-cert <certfile>", "SSL certificate to use, if any")
.option("--ssl-ca <ca-file>", "SSL certificate authority, if any")
Expand All @@ -39,6 +42,7 @@ cli
"Inward-facing port for API requests (defaults to --port=value+1)",
parseInt
)
.addOption(new Option("--api-socket <path>", "Path to a UNIX domain socket for the API server to listen on. Alternative to specifying API IP and port.").conflicts(['api-port', 'api-ip']))
.option("--api-ssl-key <keyfile>", "SSL key to use, if any, for API requests")
.option("--api-ssl-cert <certfile>", "SSL certificate to use, if any, for API requests")
.option("--api-ssl-ca <ca-file>", "SSL certificate authority, if any, for API requests")
Expand Down Expand Up @@ -93,6 +97,7 @@ cli
.option("--host-routing", "Use host routing (host as first level of path)")
.option("--metrics-ip <ip>", "IP for metrics server", "")
.option("--metrics-port <n>", "Port of metrics server. Defaults to no metrics server")
.addOption(new Option("--metrics-socket <path>", "Path to a UNIX domain socket for the metrics server to listen on. Alternative to specifying metrics IP and port.").conflicts(['metrics-port', 'metrics-ip']))
.option("--log-level <loglevel>", "Log level (debug, info, warn, error)", "info")
.option(
"--timeout <n>",
Expand Down Expand Up @@ -279,7 +284,7 @@ options.proxyTimeout = args.proxyTimeout;
options.keepAliveTimeout = args.keepAliveTimeout;

// metrics options
options.enableMetrics = !!args.metricsPort;
options.enableMetrics = !!args.metricsPort || !!args.metricsSocket;

// certs need to be provided for https redirection
if (!options.ssl && options.redirectPort) {
Expand Down Expand Up @@ -320,42 +325,27 @@ options.storageBackend = args.storageBackend;

var proxy = new ConfigurableProxy(options);

var listen = {};
listen.port = parseInt(args.port) || 8000;
if (args.ip === "*") {
// handle ip=* alias for all interfaces
log.warn(
"Interpreting ip='*' as all-interfaces. Preferred usage is 0.0.0.0 for all IPv4 or '' for all-interfaces."
);
args.ip = "";
}
listen.ip = args.ip;
listen.apiIp = args.apiIp;
listen.apiPort = args.apiPort || listen.port + 1;
listen.metricsIp = args.metricsIp;
listen.metricsPort = args.metricsPort;

proxy.proxyServer.listen(listen.port, listen.ip);
proxy.apiServer.listen(listen.apiPort, listen.apiIp);
if (listen.metricsPort) {
proxy.metricsServer.listen(listen.metricsPort, listen.metricsIp);
var listen = parseListenOptions(args, log);

proxy.proxyServer.listen(...listen.proxyTarget);
proxy.apiServer.listen(...listen.apiTarget);
if (listen.metricsTarget) {
proxy.metricsServer.listen(...listen.metricsTarget);
}

log.info(
"Proxying %s://%s:%s to %s",
"Proxying %s://%s to %s",
options.ssl ? "https" : "http",
listen.ip || "*",
listen.port,
listen.proxyTarget.join(":"),
options.defaultTarget || "(no default)"
);
log.info(
"Proxy API at %s://%s:%s/api/routes",
"Proxy API at %s://%s/api/routes",
options.apiSsl ? "https" : "http",
listen.apiIp || "*",
listen.apiPort
listen.apiTarget.join(":")
);
if (listen.metricsPort) {
log.info("Serve metrics at %s://%s:%s/metrics", "http", listen.metricsIp, listen.metricsPort);
if (listen.metricsTarget) {
log.info("Serve metrics at %s://%s/metrics", "http", listen.metricsTarget.join(":"));
}

if (args.pidFile) {
Expand All @@ -370,7 +360,7 @@ if (args.pidFile) {
}

// Redirect HTTP to HTTPS on the proxy's port
if (options.redirectPort && listen.port !== 80) {
if (options.redirectPort && listen.port && listen.port !== 80) {
var http = require("http");
var redirectPort = options.redirectTo ? options.redirectTo : listen.port;
var server = http
Expand Down
33 changes: 33 additions & 0 deletions lib/configproxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,39 @@ const require = createRequire(import.meta.url);

const __dirname = path.dirname(fileURLToPath(import.meta.url));

export function parseListenOptions(args, logger) {
var listen = {};

if (args.socket) {
listen.proxyTarget = [args.socket];
} else {
listen.port = parseInt(args.port) || 8000;
if (args.ip === "*") {
// handle ip=* alias for all interfaces
logger.warn(
"Interpreting ip='*' as all-interfaces. Preferred usage is 0.0.0.0 for all IPv4 or '' for all-interfaces."
);
args.ip = "";
}
listen.ip = args.ip;
listen.proxyTarget = [listen.port, listen.ip];
}

if (args.apiSocket) {
listen.apiSocket = [args.apiSocket];
} else {
listen.apiPort = args.apiPort ? parseInt(args.apiPort) : listen.port ? listen.port + 1 : 8001;
listen.apiTarget = [listen.apiPort, args.apiIp];
}

if (args.metricsSocket) {
listen.metricsSocket = [args.metricsSocket];
} else if (args.metricsPort) {
listen.metricsTarget = [parseInt(args.metricsPort), args.metricsIp];
}
return listen;
}

function bound(that, method) {
// bind a method, to ensure `this=that` when it is called
// because prototype languages are bad
Expand Down
18 changes: 10 additions & 8 deletions lib/testutil.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import http from "node:http";
import https from "node:https";
import { WebSocketServer } from "ws";
import { ConfigurableProxy } from "./configproxy.js";
import { ConfigurableProxy, parseListenOptions } from "./configproxy.js";
import { defaultLogger } from "./log.js";

var servers = [];
Expand All @@ -23,10 +23,10 @@ export function addTarget(proxy, path, port, websocket, targetPath, sslOptions,
target = proto + "://" + "127.0.0.1:" + port;
listenTarget = port;
}

if (targetPath) {
target = target + targetPath;
}

var server;
var data = {
target: target,
Expand Down Expand Up @@ -99,14 +99,16 @@ export function addTargets(proxy, paths, port) {
});
}

export function setupProxy(port, options, paths) {
export function setupProxy(listenOptions, options, paths) {
options = options || {};
options.authToken = "secret";
options.log = defaultLogger({ level: "error" });

var listen = parseListenOptions(listenOptions, options.log);
var port = listen.port || 8000;
var ip = listen.ip;
var proxy = new ConfigurableProxy(options);
proxy._setup_timestamp = new Date(new Date().getTime() - 60000);
var ip = "127.0.0.1";

var countdown = 2;
var resolvePromise;

Expand Down Expand Up @@ -146,10 +148,10 @@ export function setupProxy(port, options, paths) {
proxy.proxyServer.on("listening", onlisten);

addTargets(proxy, paths || ["/"], port + 2).then(function () {
proxy.proxyServer.listen(port, ip);
proxy.apiServer.listen(port + 1, ip);
proxy.proxyServer.listen(...listen.proxyTarget);
proxy.apiServer.listen(...listen.apiTarget);
if (options.enableMetrics) {
proxy.metricsServer.listen(port + 3, ip);
proxy.metricsServer.listen(...listen.metricsTarget);
}
});
return p;
Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"c8": "^10.1.3",
"jasmine": "^5.6.0",
"node-fetch": "^2.7.0",
"ws": "^8.4.0"
"ws": "^8.4.0",
"tmp": "^0.2"
},
"engines": {
"node": ">= 18"
Expand Down
10 changes: 7 additions & 3 deletions test/api_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ log.remove(log.transports.Console);

describe("API Tests", function () {
var port = 8902;
var apiPort = port + 1;
var listenOptions = {
port: port,
apiPort: 8903,
ip: "127.0.0.1",
};
var proxy;
var apiUrl = "http://127.0.0.1:" + apiPort + "/api/routes";
var apiUrl = "http://" + listenOptions.ip + ":" + listenOptions.apiPort + "/api/routes";

var r;

beforeEach(function (callback) {
util
.setupProxy(port)
.setupProxy(listenOptions)
.then(function (newProxy) {
proxy = newProxy;
})
Expand Down
Loading