shaw
ae9a01d852
fix(compact): 二轮审计加固——心跳字节不得污染 failover 判定与并发写安全
对首轮修复的对抗式审计发现并修复以下问题:
1. failover 判定污染(真实回归风险):handler 以 "Forward 前后
c.Writer.Size() 是否变化" 判定响应是否已写出并据此放弃换号。心跳注释
字节会使该判定恒真,compact 请求一旦在上游等待期间发过心跳,上游
429/5xx 将不再 failover。新增
OpenAICompactKeepaliveAdjustedWrittenSize(扣除心跳字节、互斥锁下
一致读取、仅心跳字节归一化为未写哨兵 -1),快照、failover 比较与
openAIForwardErrorAlreadyCommunicated 三处判定统一改用该口径;无心跳
请求完全等价于原 c.Writer.Size()。
2. 并发写竞争:心跳 goroutine 与未被显式拦截的写回路径(Forward 内部
本地拒绝等)存在 ResponseWriter 数据竞争。StartOpenAICompactSSE-
Keepalive 现将 c.Writer 替换为 openAICompactKeepaliveWriter:写侧
方法(Header/Write/WriteString/WriteHeader/WriteHeaderNow/Flush)
先在互斥锁下停拍,读侧(Status/Size/Written)仅加锁不停拍——任何
请求侧响应构造与心跳从构造上互斥,热路径状态读取不误杀心跳。
3. 语义拦截补齐:rejectIfCyberSessionBlocked(在用户槽位长等待之后
执行的直接 c.JSON)与 writeOpenAIFastPolicyBlockedResponse 在心跳
提交后降级为 response.failed 终止事件;未提交时先停拍再写 JSON,
状态码语义不变。失败事件 errType 参数化(permission_error 等)。
4. reconstruct 混合形态:done 事件存在但 compaction 只在
output_item.added 中时也要补入;done 已含 compaction 时跳过 added,
避免无 id 可去重时收集两份(Codex 要求恰好一个)。
5. 观测性:logOpenAIRemoteCompactOutcome 对心跳提交后的失败(wire 200)
以 GetOpsStreamError 纠正 outcome,不再误记 succeeded。
新增 5 个测试:failover 口径不变式、包装器停拍语义(-race)、fast
policy 提交前后两态、混合 done/added 形态(含去重)。
Refs #3887 #3777
2026-07-10 10:18:16 +08:00
..
2026-07-09 14:57:53 +00:00
2026-07-09 15:38:59 +08:00
2026-07-10 10:18:16 +08:00
2026-07-09 15:38:59 +08:00
2026-07-07 03:27:11 +08:00
2026-07-01 14:25:46 +08:00
2026-07-09 14:06:57 +08:00
2026-07-09 14:06:57 +08:00
2026-07-08 08:37:29 +08:00
2026-07-01 14:25:46 +08:00