dsh-pocket 1.13.4 → 1.13.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.js +3 -0
- package/lib/proxy.mjs +20 -4
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -168,6 +168,9 @@ function performUpdate(profile, { timeoutMs = 180_000 } = {}) {
|
|
|
168
168
|
return new Promise((resolve) => {
|
|
169
169
|
const child = spawn('dsh', ['plugin', '--profile', profile, 'update', 'dsh-pocket', '--latest', '-w'], {
|
|
170
170
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
171
|
+
// Windows 上裸 spawn('dsh') 解析到无扩展名 POSIX shim → ENOENT;
|
|
172
|
+
// Node 22+ 直接 spawn .cmd 会 EINVAL(CVE-2024-27980),必须走 shell(PR #54)
|
|
173
|
+
shell: process.platform === 'win32',
|
|
171
174
|
});
|
|
172
175
|
let out = '';
|
|
173
176
|
const onData = (c) => { out += String(c); if (out.length > 4000) out = out.slice(-4000); };
|
package/lib/proxy.mjs
CHANGED
|
@@ -442,17 +442,33 @@ export function createPocketProxy({ port = 3081, host = '0.0.0.0', upstream = DE
|
|
|
442
442
|
}
|
|
443
443
|
socket.write(`${raw.join('\r\n')}\r\n\r\n`);
|
|
444
444
|
if (proxyHead?.length) socket.write(proxyHead);
|
|
445
|
-
|
|
446
|
-
|
|
445
|
+
// pipe 必须 end:false:默认 end:true 会在对端 FIN 时抢先 end() 对端 socket
|
|
446
|
+
// (优雅 FIN),此时 teardown 的 destroy() 已无法强制关闭对方——上游只收
|
|
447
|
+
// 到 FIN 进入 half-open 永不关闭(PR #56)。半关闭统一交给下面的 'end'
|
|
448
|
+
// 监听 → teardown destroy(RST 强制关闭双方)。
|
|
449
|
+
socket.pipe(proxySocket, { end: false });
|
|
450
|
+
proxySocket.pipe(socket, { end: false });
|
|
447
451
|
// 心跳注入(PR #41):保活 + 静默断链检测(见 attachWebSocketHeartbeat)
|
|
448
452
|
if (heartbeat !== false) attachWebSocketHeartbeat(socket, heartbeat ?? {});
|
|
449
|
-
// 任一端断开都要清理另一端(避免上游残留僵尸连接占用 dsh
|
|
450
|
-
|
|
453
|
+
// 任一端断开都要清理另一端(避免上游残留僵尸连接占用 dsh 连接槽)。
|
|
454
|
+
// 上游侧必须 resetAndDestroy(发 RST):destroy() 只发干净 FIN,而上游
|
|
455
|
+
// http server 默认 allowHalfOpen=true,收到 FIN 不自动关闭 → 上游仍悬挂
|
|
456
|
+
// (PR #56 实测)。RST 强制对端立即关闭。
|
|
457
|
+
const teardown = () => {
|
|
458
|
+
try { proxySocket.resetAndDestroy?.() ?? proxySocket.destroy(); } catch { try { proxySocket.destroy(); } catch {} }
|
|
459
|
+
try { socket.destroy(); } catch {}
|
|
460
|
+
};
|
|
451
461
|
// 上游侧透传 socket 的读错误(如 dsh web 重启/断开时的 ECONNRESET)必须
|
|
452
462
|
// 吞掉并清理对端,否则未处理的 'error' 事件会让整个 dsh web 进程崩溃退出。
|
|
453
463
|
proxySocket.on('error', () => { try { socket.destroy(); } catch {} });
|
|
454
464
|
proxySocket.on('close', teardown);
|
|
455
465
|
socket.on('close', teardown);
|
|
466
|
+
// 半关闭(收到对端 FIN 的 'end')对双向转发同样意味着这一端要走了:http server
|
|
467
|
+
// 默认 allowHalfOpen=true,收到 FIN 只触发 'end' 不自动关——若不在 'end' 时销毁,
|
|
468
|
+
// 浏览器/App 直接关页(不发 WS close 帧就 FIN)留下的连接会永久挂在 half-open
|
|
469
|
+
// 状态,上游连接槽被占(且 server.close() 永远等不完)。双向流里半关闭无意义。
|
|
470
|
+
socket.on('end', teardown);
|
|
471
|
+
proxySocket.on('end', teardown);
|
|
456
472
|
});
|
|
457
473
|
// 上游返回普通 HTTP 响应(非 101):把状态码/头回写后断开,别让客户端永久挂起
|
|
458
474
|
proxyReq.on('response', (proxyRes) => {
|
package/package.json
CHANGED