fix: resolve 14 memory leaks and bugs across native host and extension
CI / Build & Test (push) Successful in 2m47s
CI / Build & Test (push) Successful in 2m47s
Critical: - ssh-manager: release ports/wsServer/holesail on ptySpawn failure - rdp-manager: add 30s timeout to holesailInst.ready() to prevent infinite hang High: - ssh-manager: cancel password-watch timers on PTY exit/error - ssh-manager: cap outputSoFar to 4096 chars to prevent unbounded growth - connect-proxy: add 64KB header buffer cap to prevent OOM Medium: - startup: assign tunnelsRestoredPromise before resolve() to fix race - https-proxy: register upstream error handler before connect callback - connect-proxy: register upstreamSocket error handler before connect callback - https-proxy: destroy socket on backend stream error (was sending truncated 200) - logs: debounce broadcastLogs to prevent IPC storm on every log call Low: - rdp-manager: cap VNC outputBuffer entry count (not just bytes) - https-proxy: track connections in FakeHttpServer.connections Set instead of private _connections API - tab-lifecycle: clean up subscribedTabs/dashboardTabs on tab navigation - dashboard/events: guard setupEvents() against duplicate listener registration
This commit is contained in:
+20
-11
@@ -315,7 +315,9 @@ h1{color:#c0392b}code{background:#f4f4f4;padding:2px 6px;border-radius:3px;font-
|
||||
}
|
||||
proxyRes.on('data', (chunk) => !timedOut && res.write(chunk));
|
||||
proxyRes.on('end', () => !timedOut && !res.writableEnded && res.end());
|
||||
proxyRes.on('error', () => !res.writableEnded && res.end());
|
||||
// Destroy the client socket on a mid-stream backend error so the browser
|
||||
// receives a connection reset rather than a silently truncated 200 body.
|
||||
proxyRes.on('error', () => { try { if (res.socket) res.socket.destroy(); } catch (_) {} });
|
||||
});
|
||||
proxyReq.on('error', (err) => {
|
||||
clearTimeout(timeoutId);
|
||||
@@ -355,7 +357,12 @@ function onUpgrade (req, socket, head) {
|
||||
try { bareTcp = require('bare-tcp'); } catch (_) {}
|
||||
if (!bareTcp) { socket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n'); socket.destroy(); return; }
|
||||
|
||||
const upstream = bareTcp.connect(targetPort, targetHost, () => {
|
||||
// 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 (_) {} });
|
||||
socket.on('error', () => { try { upstream.destroy(); } catch (_) {} });
|
||||
upstream.on('connect', () => {
|
||||
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');
|
||||
@@ -363,8 +370,6 @@ function onUpgrade (req, socket, head) {
|
||||
socket.pipe(upstream);
|
||||
upstream.pipe(socket);
|
||||
});
|
||||
upstream.on('error', () => { try { socket.write('HTTP/1.1 502 Bad Gateway\r\n\r\n'); socket.destroy(); } catch (_) {} });
|
||||
socket.on('error', () => { try { upstream.destroy(); } catch (_) {} });
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -515,7 +520,11 @@ function start (port, certsDirOrCA, callback, _baseDomains) {
|
||||
// Attach the TCP server to the fake server so stop() can close it
|
||||
fakeServer._tcpServer = tcpServer;
|
||||
|
||||
tcpServer.on('connection', handleRawConnection);
|
||||
tcpServer.on('connection', (sock) => {
|
||||
fakeServer.connections.add(sock);
|
||||
sock.once('close', () => fakeServer.connections.delete(sock));
|
||||
handleRawConnection(sock);
|
||||
});
|
||||
|
||||
tcpServer.on('error', (err) => {
|
||||
if (err.code === 'EADDRINUSE') {
|
||||
@@ -552,13 +561,13 @@ function stop (callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Destroy all raw TCP connections so close() fires immediately
|
||||
const rawConns = tcpServer._connections;
|
||||
if (rawConns && typeof rawConns[Symbol.iterator] === 'function') {
|
||||
for (const socket of rawConns) {
|
||||
try { socket.destroy(); } catch (_) {}
|
||||
}
|
||||
// Destroy all tracked raw TCP connections so close() fires immediately.
|
||||
// We use fakeServer.connections (populated in the 'connection' handler above)
|
||||
// instead of the undocumented tcpServer._connections internal.
|
||||
for (const socket of fakeServer.connections) {
|
||||
try { socket.destroy(); } catch (_) {}
|
||||
}
|
||||
fakeServer.connections.clear();
|
||||
|
||||
tcpServer.close(() => {
|
||||
if (callback) callback();
|
||||
|
||||
Reference in New Issue
Block a user