fix(huya): 结构体尾部字段容错解析(服务端新增 ZERO 字段不再打挂整个 RPC)

真机现象(2026-10-15,外部 CK 导入后查询角色):checkUserBindGameAccount 响应结构
尾部多了 tag6(ZERO 类型 0x6c),共享的 read_struct 读到非 STRUCT_END 直接抛
「期望 STRUCT_END, 实际 0x0c」,整个 RPC 返回 None —— 工作台「查询角色」永远失败。

- taf_protocol:read_struct 收尾改为按 TAF 语义跳过未建模的尾部字段(上限 32 个,
  防止解析错位时无限吞字节);新增 skip_to_struct_end()
- frame_decoder / shop_structs:抽出 decode_struct_bytes 与 ShopRawRsp.from_payload,
  WSS 与 HTTP 两条通道共用一份通用解码逻辑(wss_client 同步改用)

验证:tests/test_huya_struct_parse_resilience.py 用真机响应字节回归(含 tag6=ZERO 的
响应能解析出角色「泪雨全是谎言」);按 AGENTS.md 反向验证 —— 注掉
skip_to_struct_end() 即复现线上报错「期望 STRUCT_END, 实际 0x0c」,还原后恢复。
全量 pytest 通过。
This commit is contained in:
yml2213
2026-10-15 15:21:34 +08:00
parent 4ce30e5c26
commit a9d711a7e9
5 changed files with 133 additions and 29 deletions
+24 -1
View File
@@ -431,6 +431,28 @@ class TafInputStream:
result.append(self._read_value(it, item_reader))
return result
#: 结构体尾部允许跳过的未解析字段上限(解析错位时避免无限吞字节)
STRUCT_TAIL_SKIP_LIMIT = 32
def skip_to_struct_end(self) -> int:
"""跳过结构体尾部未建模的字段,返回跳过的字段数(STRUCT_END 不消费)。
服务端会给结构体追加新字段(常见是值为 0 的 ZERO 类型:例如
checkUserBindGameAccount 响应在 tag5 之后多出 tag6=ZERO)。读取方只消费
自己认识的字段,其余按 TAF 语义跳过,由 read_struct 统一收尾 ——
否则一个新增字段就会让整个 RPC 解析失败、上层读到 None。
"""
skipped = 0
while skipped < self.STRUCT_TAIL_SKIP_LIMIT:
pos = self.buf.tell()
_tag, dtype = self.read_head()
if dtype == TafType.STRUCT_END:
self.buf.seek(pos)
return skipped
self.skip_field(dtype)
skipped += 1
raise ValueError("结构体尾部未解析字段过多,疑似解析错位")
def read_struct(self, tag: int, struct_class, required: bool = False):
"""读取结构体,struct_class 需有无参构造 + read_from"""
found = self._find_tag(tag, required)
@@ -440,7 +462,8 @@ class TafInputStream:
raise ValueError(f"期望 struct, 实际 0x{found[1]:02x}")
obj = struct_class()
obj.read_from(self)
# 消费 STRUCT_END
# 尾部未建模字段先跳过,再消费 STRUCT_END
self.skip_to_struct_end()
_t, dt = self.read_head()
if dt != TafType.STRUCT_END:
raise ValueError(f"期望 STRUCT_END, 实际 0x{dt:02x}")