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 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
- proxySocket.pipe(socket);
446
- socket.pipe(proxySocket);
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
- const teardown = () => { try { proxySocket.destroy(); } catch {} try { socket.destroy(); } catch {} };
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
@@ -76,5 +76,5 @@
76
76
  "access": "public",
77
77
  "registry": "https://registry.npmjs.org/"
78
78
  },
79
- "version": "1.13.4"
79
+ "version": "1.13.6"
80
80
  }