Client WINDOW_UPDATE 泄漏修复实施计划
Client WINDOW_UPDATE 泄漏修复实施计划
状态:补记计划(原始实现已完成)
补记说明:用户在本机 Client 更新后仍能复现大体积 JavaScript 资源 ERR_CONTENT_LENGTH_MISMATCH。按 AGENTS.md 紧急修复条款先止损、后补记;红灯证据为实现过程中的真实观察,绿灯与门禁在补记时重新执行,见第 7 节。
1. 目标
Client 发送给 Server 的 WINDOW_UPDATE 只影响 Server→Client 发送窗口,不得泄漏到 Agent→Server 链路,避免 Agent 被重复授予额度后冲破 Server 接收缓冲。
2. 非目标
- 不新增协议 frame、不修改窗口大小、阈值或队列容量。
- 不改变 Agent 与 Server 之间已有的带外
WINDOW_UPDATE语义。 - 不调整授权、策略、API、Schema 或配置。
3. 根因与证据
本地 TCP 转发链路存在两跳独立流控:
- Agent→Server:Server 在
OPEN_STREAM.Window通告额度,并在agentRelayStream.Read消费字节后向 Agent 回补。 - Server→Client:Client 在
OPEN_STREAM.Window通告额度,并在应用消费字节后向 Server 回补。
serveClientSessionWithService 处理 Client WINDOW_UPDATE 时,先把额度加入 clientRelayStream.sendState,随后又把同一帧通过 agentRelayStream.WriteControl 发给 Agent。Agent 将其误认为 Agent→Server 方向的新额度,于是可在 Server 尚未真正消费对应字节时继续发送。Server 的 agentRelayStream.enqueue 最终命中 relay.ErrBackpressure,删除流并发送 RESET;Client 只收到短于 Content-Length 的响应。
红灯证据:
TestServeClientSessionRelayHonorsClientWindow修复前失败:Client WINDOW_UPDATE leaked to Agent relay: {Type:5 StreamID:16 Window:8}。TestSOCKS5WebPageLatency/local_tcp_forward_delivers_large_fixed-length_response修复前失败:响应体不完整,并出现protocol: stream is reset。- 临时诊断日志定位到 Server 侧先出现
relay: backpressure,随后 Client 收到RESET。
4. 架构决策
两级流控额度都在 Server 终止:
- Client
WINDOW_UPDATE只更新clientRelayStream.sendState,供relayToClient继续向 Client 发送。 - Agent
WINDOW_UPDATE仍由agentRelayStream.Read消费字节后产生,供 Agent 继续向 Server 发送。 - Server 不再把 Client 额度镜像给 Agent,也不把 Agent 额度误用于 Client 方向。
5. 技术与规格引用
internal/protocol/window.go的queued <= initialWindow不变量。docs/protocol/proxy-modules.md的 per-stream 队列与带外WINDOW_UPDATE说明。- 相关 Client 修复:
docs/superpowers/plans/2026-09-24-client-receive-window-credit.md。
6. 文件清单
internal/server/ws_client.go:删除 ClientWINDOW_UPDATE到 Agent relay 的镜像转发。internal/server/ws_client_test.go:改写契约,断言 Client 更新不会泄漏到 Agent,同时后续 DATA 仍能发给 Client。internal/e2e/socks5_web_page_latency_test.go:新增本地 TCP 转发大体积固定长度响应的端到端回归。internal/client/forward.go:记录 TCP bridge 的非 EOF 错误,避免短包再次静默。docs/protocol/proxy-modules.md:记录两级流控额度终止于 Server 的不变量。- 本文件与
docs/pull-requests/2026-09-24-client-window-update-leak.md。
7. TDD 与验证
红灯见第 3 节。最小实现为删除 serveClientSessionWithService 中的 controlWriter.WriteControl(frame) 分支。
验证命令:
go test ./internal/server -run '^TestServeClientSessionRelayHonorsClientWindow$' -count=1
go test ./internal/e2e -run '^TestSOCKS5WebPageLatency$/^local_tcp_forward_delivers_large_fixed-length_response$' -count=10
go test ./... -count=1
go test -race ./...
go vet ./...
git diff --check
补记时前两项已通过,其中端到端用例连续执行 10 次均通过;全量门禁结果记录在 PR 记录中。
8. 回滚注意事项
回退 Server 提交并重新部署旧 Server 即可恢复原行为;数据库、配置和协议格式无需回滚。回滚后本地 Client 大响应会重新出现偶发 ERR_CONTENT_LENGTH_MISMATCH。