fix: resolve 35 memory leaks, resource leaks, and bugs across native host and extension
CI / Build & Test (push) Successful in 2m52s
CI / Build & Test (push) Successful in 2m52s
CRITICAL:
- certificate-authority.js: declare `regenerated` variable in installRootCA Windows path to prevent ReferenceError crash
HIGH:
- virtual-hosts.js/service-tunnels.js: call hs.removeAllListeners() in catch blocks to prevent stale listeners on failed Holesail instances
- https-proxy.js: destroy rawSocket in TLS error handler to prevent file descriptor exhaustion
- message-router.js (native): move setEventEmitter() to module-level init instead of re-calling on every message
- ssh-manager.js: add error handler to WS server to prevent unhandled error crashes
- init.js: store setInterval ID and clear on beforeunload to prevent interval accumulation
- logs.js: store and remove chrome.runtime.onMessage listener on beforeunload; add duplicate-call guard
- events.js: move pending++ before async sendMessage call to fix SSH/RDP-only import showing "Nothing to import"
- ssh.js: store resizeTimer on activeSshSession and clear in disconnectSsh; fix auto-reconnect race with _sshConnecting lock
- native-messaging.js: track retry timer IDs in array and cancel all on disconnect
- rdp.js: reuse single offscreen canvas per session instead of allocating per bitmap
MEDIUM:
- virtual-hosts.js/service-tunnels.js: clear existing.reconnectTimer before replacing tunnel entries
- message-router.js (native): destroy pingTunnel socket on error path; clear 15s fallback timer via finally()
- startup.js: wrap setImmediate body in try/finally to always resolve proxiesReadyPromise
- https-proxy.js: fix pre-connect upstream error handler to avoid writing raw HTTP into piped TLS stream; move HOP_BY_HOP to module-level constant
- connect-proxy.js: destroy upstreamSocket on clientSocket close; track and destroy active sockets in stop()
- ssh-manager.js: call cancelPasswordWatch on WS disconnect during password collection
- rdp-manager.js: remove dead remotePort variable; add error handlers to both WS servers
- backup-manager.js: log cleanupStaging errors and non-zero exit codes
- rdp.js: null out ws callbacks before closing in disconnectRdp; disconnect MutationObserver on beforeunload
- proxy-ca.js: prune stale entries from validationResults Map in renderValidatorTable
- refresh.js: deduplicate in-flight pings per port via Set
- messaging.js: read chrome.runtime.lastError in sendToNative callback
- servers.js (dashboard): add null check for $('serverEditId') element
LOW:
- port-allocator.js: add dedup check before pushing to tunnelPortFreeList
- servers.js (native): add error listener to server-mode Holesail instances
- virtual-hosts.js: remove dead prevReconnectDelay variable
- tab-lifecycle.js: change swarmRefCount fallback from || 1 to || 0 to prevent premature swarm destroy
- ssh.js/rdp.js: disconnect MutationObservers on beforeunload
This commit is contained in:
@@ -24,6 +24,11 @@ const BACKEND_TIMEOUT_MS = 30000;
|
||||
// How long to wait for the first TLS bytes before giving up (ms)
|
||||
const SNI_READ_TIMEOUT_MS = 5000;
|
||||
|
||||
const HOP_BY_HOP = new Set([
|
||||
'transfer-encoding', 'connection', 'keep-alive', 'proxy-connection',
|
||||
'proxy-authorization', 'proxy-authenticate', 'te', 'trailer', 'upgrade'
|
||||
]);
|
||||
|
||||
const DEBUG = process.env.HOLESAIL_DEBUG === '1' || process.env.HOLESAIL_DEBUG === 'true';
|
||||
function debugLog (...args) {
|
||||
if (!DEBUG) return;
|
||||
@@ -275,10 +280,6 @@ h1{color:#c0392b}code{background:#f4f4f4;padding:2px 6px;border-radius:3px;font-
|
||||
// transparently. Forwarding Transfer-Encoding: chunked would cause
|
||||
// ERR_INVALID_CHUNKED_ENCODING because the HTTP library already decodes
|
||||
// the chunked body before emitting 'data' events.
|
||||
const HOP_BY_HOP = new Set([
|
||||
'transfer-encoding', 'connection', 'keep-alive', 'proxy-connection',
|
||||
'proxy-authorization', 'proxy-authenticate', 'te', 'trailer', 'upgrade'
|
||||
]);
|
||||
|
||||
const reqHeaders = {};
|
||||
for (const k of Object.keys(req.headers)) {
|
||||
@@ -366,9 +367,15 @@ function onUpgrade (req, socket, head) {
|
||||
// Register error handler synchronously before the connect callback can fire,
|
||||
// so an immediate ECONNREFUSED is never an unhandled error event.
|
||||
const upstream = bareTcp.connect(targetPort, targetHost);
|
||||
upstream.on('error', () => { try { socket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n'); socket.destroy(); } catch (_) {} });
|
||||
function _preConnectError() {
|
||||
try { socket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n'); socket.destroy(); } catch (_) {}
|
||||
try { upstream.destroy(); } catch (_) {}
|
||||
}
|
||||
upstream.on('error', _preConnectError);
|
||||
socket.on('error', () => { try { upstream.destroy(); } catch (_) {} });
|
||||
upstream.on('connect', () => {
|
||||
upstream.removeListener('error', _preConnectError);
|
||||
upstream.on('error', () => { try { socket.destroy(); } catch (_) {} });
|
||||
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');
|
||||
@@ -451,6 +458,7 @@ function handleRawConnection (rawSocket) {
|
||||
|
||||
tlsSocket.on('error', (err) => {
|
||||
debugLog('tls socket error:', err.message);
|
||||
try { rawSocket.destroy(); } catch (_) {}
|
||||
});
|
||||
|
||||
// Hand to bare-http1 for HTTP parsing — emits 'request' on the fakeServer
|
||||
|
||||
Reference in New Issue
Block a user