fix: comprehensive bug fixes, security improvements, and feature additions
CI / Build & Test (push) Successful in 3m21s
CI / Build & Test (push) Successful in 3m21s
Critical fixes:
- Fix wrong registry key (com.bridgeswarm → com.holesail.browser) in
update-native-manifest-extension-id.ps1 — script was always failing on Windows
- Create missing wrong-domain.html redirect page for .host.test URLs
- Remove options_ui pointing to non-existent options.html from manifest
High-priority bug fixes:
- ssh-manager: track and kill orphaned printf FIFO writer when key auth succeeds
- ssh-manager: fix uncancelled 2000ms fallback password timer (assign to fallbackTimer,
clear in cancelPasswordWatch); fix null-check before removeAllListeners
- ssh-manager: add 30s Promise.race timeout to holesailInst.ready()
- backup-manager: fix macOS cp -R nesting bug by removing destination before copy;
add tar -tzf integrity check after archive creation
- host.js: restoreBackup now stops running tunnels before restore and re-starts them
- holesail-manager: fix stale closure bug in virtual host and service tunnel
error/close handlers (guard with v.holesail === hs check)
- dashboard.js: remove dead setText('dashTabs', ...) call referencing non-existent element
Medium improvements:
- manifest: remove unused storage and scripting permissions; restrict
web_accessible_resources match from <all_urls> to chrome-extension://*/*
- background.js: fix self-referential browser alias (globalThis.browser ?? chrome);
add 30s per-request timeout to send(); clean up dashboardTabs on tab close
- holesail-manager: gate saveStateSync stderr log behind DEBUG flag; updateSettings
now returns requiresRestart:true when proxy port changes; add backupRetention field
- host.js: pass requiresRestart through in updateSettings response
- dashboard.js: remove dead loadSettings() function; add requiresRestart warning toast;
add chrome.runtime.lastError guards in fetchState and refreshBackups;
set dynamic version from chrome.runtime.getManifest()
- dashboard.html: remove stray </button> tag; add id="sidebarVersion" for dynamic version
- install.sh/install.ps1: fetch version from RELEASE_BASE/VERSION instead of hardcoded 1.0.0
- install.ps1: add Firefox .xpi download and Firefox registry key
- update-native-manifest-extension-id.sh: add optional Firefox manifest update
- certificate-authority.js: defer RSA key generation to setImmediate to avoid blocking
startup; expose caReady promise
- host.js: await caReady before starting HTTPS proxy
Documentation:
- REMOTE-DESKTOP.md: correct RDP WebSocket protocol field names to match rdp-manager.js
(destLeft/destTop/destRight/destBottom, mouseMove/mouseButton/keyEvent/keyUnicode)
Feature additions:
- dashboard.js: add Reconnect button for service tunnels in error/closed state
- https-proxy.js: add WebSocket upgrade handler to support ws:// over *.hole.sail
- connect-proxy.js: add 10s header-read timeout to protect against idle connections
- native-host: add bare-fs as explicit dependency
This commit is contained in:
@@ -77,6 +77,8 @@ function start(port, certsDirOrCA, callback) {
|
||||
}
|
||||
try {
|
||||
proxyServer = https.createServer(opts, onRequest);
|
||||
// Forward WebSocket upgrade requests to the backend tunnel
|
||||
proxyServer.on('upgrade', onUpgrade);
|
||||
} catch (err) {
|
||||
if (process.stderr) process.stderr.write('[https-proxy] createServer threw: ' + err.message + '\n');
|
||||
done(err);
|
||||
@@ -236,6 +238,67 @@ h1{color:#c0392b}code{background:#f4f4f4;padding:2px 6px;border-radius:3px;font-
|
||||
req.on('end', () => proxyReq.end());
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle WebSocket upgrade requests by piping the raw socket to the backend.
|
||||
* This allows ws:// / wss:// connections through *.hole.sail virtual hosts.
|
||||
*/
|
||||
function onUpgrade(req, socket, head) {
|
||||
const hostHeader = req.headers && (req.headers.host || req.headers.Host);
|
||||
const hostname = hostHeader ? hostHeader.split(':')[0].trim() : '';
|
||||
debugLog('upgrade: hostname=', hostname, 'url=', req.url);
|
||||
|
||||
if (!getBackendForHostname || !hostname) {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
const backend = getBackendForHostname(hostname);
|
||||
let targetHost = '127.0.0.1';
|
||||
let targetPort = null;
|
||||
if (backend != null && typeof backend === 'object' && typeof backend.port === 'number') {
|
||||
targetHost = backend.host ?? '127.0.0.1';
|
||||
targetPort = backend.port;
|
||||
} else if (typeof backend === 'number') {
|
||||
targetPort = backend;
|
||||
}
|
||||
|
||||
if (targetPort == null) {
|
||||
debugLog('upgrade: no backend for hostname=', hostname);
|
||||
socket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n');
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
// Open a raw TCP connection to the backend and pipe the socket
|
||||
let net = null;
|
||||
try { net = require('bare-tcp'); } catch (_) {}
|
||||
if (!net) {
|
||||
debugLog('upgrade: bare-tcp not available');
|
||||
socket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n');
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
const upstream = net.connect(targetPort, targetHost, () => {
|
||||
// Reconstruct the HTTP upgrade request and forward it
|
||||
const headers = Object.entries(req.headers)
|
||||
.map(([k, v]) => k + ': ' + v)
|
||||
.join('\r\n');
|
||||
const requestLine = (req.method || 'GET') + ' ' + (req.url || '/') + ' HTTP/1.1\r\n';
|
||||
upstream.write(requestLine + headers + '\r\n\r\n');
|
||||
if (head && head.length > 0) upstream.write(head);
|
||||
socket.pipe(upstream);
|
||||
upstream.pipe(socket);
|
||||
});
|
||||
|
||||
upstream.on('error', (err) => {
|
||||
debugLog('upgrade: upstream error hostname=', hostname, 'err=', err.message);
|
||||
try { socket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n'); socket.destroy(); } catch (_) {}
|
||||
});
|
||||
socket.on('error', () => {
|
||||
try { upstream.destroy(); } catch (_) {}
|
||||
});
|
||||
}
|
||||
|
||||
function stop(callback) {
|
||||
if (!proxyServer) {
|
||||
if (callback) callback();
|
||||
|
||||
Reference in New Issue
Block a user