feat(virtual-hosts): add TLS option for HTTPS/443 backends
CI / Build & Test (push) Failing after 2m44s
CI / Build & Test (push) Failing after 2m44s
- Add "Use TLS (secure connection)" checkbox in Add Virtual Host modal - Persist and restore useTls in state; show TLS badge in table - When enabled, HTTPS proxy connects to tunnel backend over TLS (SNI = hostname) for HTTP and WebSocket; supports services on port 443
This commit is contained in:
@@ -19,7 +19,7 @@ function debugLog(...args) {
|
||||
if (process.stderr) process.stderr.write(msg + '\n');
|
||||
}
|
||||
|
||||
const virtualHosts = new Map(); // hostname -> { hsUrl, holesail, localHost, localPort, state, createdAt, reconnectTimer, reconnectDelay }
|
||||
const virtualHosts = new Map(); // hostname -> { hsUrl, useTls, holesail, localHost, localPort, state, createdAt, reconnectTimer, reconnectDelay }
|
||||
let _saveState = null;
|
||||
let _emit = null;
|
||||
let _getReadyTimeoutMs = null;
|
||||
@@ -57,7 +57,7 @@ function _scheduleVhostReconnect(hostname) {
|
||||
const cur = virtualHosts.get(hostname);
|
||||
if (!cur || cur.state === 'ready') return;
|
||||
if (process.stderr) process.stderr.write('[holesail-manager] vhost ' + hostname + ' auto-reconnect attempt\n');
|
||||
await setVirtualHost({ hostname, hsUrl: cur.hsUrl });
|
||||
await setVirtualHost({ hostname, hsUrl: cur.hsUrl, useTls: cur.useTls });
|
||||
}, delay);
|
||||
}
|
||||
|
||||
@@ -78,13 +78,15 @@ function readyWithTimeout(hs, label) {
|
||||
* @param {object} payload
|
||||
* @param {string} payload.hostname - The virtual hostname (e.g. `myapp.hs`). Normalised to lowercase.
|
||||
* @param {string} payload.hsUrl - The hs:// key of the remote peer.
|
||||
* @param {boolean} [payload.useTls] - If true, proxy connects to backend over TLS (for HTTPS/443 backends).
|
||||
* @returns {Promise<{ok: boolean, hostname?: string, localHost?: string, localPort?: number, state?: string, error?: string}>}
|
||||
*/
|
||||
async function setVirtualHost(payload) {
|
||||
let hostname = (payload.hostname || payload.hostName || '').trim();
|
||||
hostname = hostname.replace(/^https?:\/\//i, '').replace(/[/:?#].*$/, '').toLowerCase().trim();
|
||||
const hsUrl = payload.hsUrl || payload.url;
|
||||
debugLog('setVirtualHost: hostname=', hostname, 'hsUrl=', hsUrl);
|
||||
const useTls = payload.useTls === true;
|
||||
debugLog('setVirtualHost: hostname=', hostname, 'hsUrl=', hsUrl, 'useTls=', useTls);
|
||||
if (!Holesail) return { ok: false, error: 'Holesail module not installed' };
|
||||
if (!hostname || !hsUrl) return { ok: false, error: 'hostname and hsUrl required' };
|
||||
|
||||
@@ -113,7 +115,8 @@ async function setVirtualHost(payload) {
|
||||
});
|
||||
}
|
||||
await readyWithTimeout(hs, 'vhost:' + hostname);
|
||||
virtualHosts.set(hostname, { hsUrl, holesail: hs, localHost: TUNNEL_HOST, localPort, state: 'ready', createdAt: (existing && existing.createdAt) || Date.now(), reconnectTimer: null, reconnectDelay: RECONNECT_BASE_MS });
|
||||
const entryUseTls = existing ? (payload.useTls === true || existing.useTls === true) : useTls;
|
||||
virtualHosts.set(hostname, { hsUrl, useTls: entryUseTls, holesail: hs, localHost: TUNNEL_HOST, localPort, state: 'ready', createdAt: (existing && existing.createdAt) || Date.now(), reconnectTimer: null, reconnectDelay: RECONNECT_BASE_MS });
|
||||
if (_emit) _emit('tunnelReady', { hostname, hsUrl, localHost: TUNNEL_HOST, localPort });
|
||||
if (_saveState) _saveState();
|
||||
debugLog('setVirtualHost: ok hostname=', hostname, 'localPort=', localPort);
|
||||
@@ -123,7 +126,8 @@ async function setVirtualHost(payload) {
|
||||
try { hs.removeAllListeners(); } catch (_) {}
|
||||
releaseTunnelPort(localPort);
|
||||
const existingCreatedAt = existing ? existing.createdAt : undefined;
|
||||
virtualHosts.set(hostname, { hsUrl, holesail: null, localHost: null, localPort: null, state: 'error', createdAt: existingCreatedAt || Date.now() });
|
||||
const entryUseTls = existing ? (payload.useTls === true || existing.useTls === true) : useTls;
|
||||
virtualHosts.set(hostname, { hsUrl, useTls: entryUseTls, holesail: null, localHost: null, localPort: null, state: 'error', createdAt: existingCreatedAt || Date.now() });
|
||||
if (_emit) _emit('tunnelError', { hostname, error: e.message });
|
||||
return { ok: false, error: e.message };
|
||||
}
|
||||
@@ -151,12 +155,12 @@ async function removeVirtualHost(payload) {
|
||||
|
||||
/**
|
||||
* Return a snapshot of all virtual hosts (including errored/closed ones).
|
||||
* @returns {Array<{hostname: string, hsUrl: string, localHost: string|null, localPort: number|null, state: string, createdAt: number}>}
|
||||
* @returns {Array<{hostname: string, hsUrl: string, useTls: boolean, localHost: string|null, localPort: number|null, state: string, createdAt: number}>}
|
||||
*/
|
||||
function getVirtualHosts() {
|
||||
const list = [];
|
||||
for (const [hostname, v] of virtualHosts) {
|
||||
list.push({ hostname, hsUrl: v.hsUrl, localHost: v.localHost ?? null, localPort: v.localPort ?? null, state: v.state || 'unknown', createdAt: v.createdAt });
|
||||
list.push({ hostname, hsUrl: v.hsUrl, useTls: v.useTls === true, localHost: v.localHost ?? null, localPort: v.localPort ?? null, state: v.state || 'unknown', createdAt: v.createdAt });
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -172,14 +176,20 @@ function getLocalPortForHostname(hostname) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the `{ host, port }` backend object for the HTTPS proxy SNI resolver,
|
||||
* Return the `{ host, port, tls? }` backend object for the HTTPS proxy SNI resolver,
|
||||
* or null if the virtual host does not exist or is not yet ready.
|
||||
* When useTls is true, the proxy should connect to the backend over TLS.
|
||||
* @param {string} hostname
|
||||
* @returns {{host: string, port: number}|null}
|
||||
* @returns {{host: string, port: number, tls?: boolean}|null}
|
||||
*/
|
||||
function getLocalBackend(hostname) {
|
||||
const v = virtualHosts.get(hostname);
|
||||
const out = (!v || v.localPort == null) ? null : { host: v.localHost ?? '127.0.0.1', port: v.localPort };
|
||||
if (!v || v.localPort == null) {
|
||||
debugLog('getLocalBackend: hostname=', hostname, 'result=', null, 'virtualHostsKeys=', Array.from(virtualHosts.keys()));
|
||||
return null;
|
||||
}
|
||||
const out = { host: v.localHost ?? '127.0.0.1', port: v.localPort };
|
||||
if (v.useTls === true) out.tls = true;
|
||||
debugLog('getLocalBackend: hostname=', hostname, 'result=', out, 'virtualHostsKeys=', Array.from(virtualHosts.keys()));
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user