feat(gb32960): isolate per-block parse failures in body parser
主循环改造:单信息块 parser 抛 DecodeException / BufferUnderflowException / IndexOutOfBoundsException 时不再放弃整帧。 - 固定长度块(fixedLen≥0):回滚 reader,按 fixedLen 截取字节兜成 InfoBlock.Raw, position 前进 fixedLen,continue 循环继续解析后续块 - 变长块(fixedLen=-1)或剩余不足 fixedLen:剩余字节全部兜成 Raw 后 break - parser 契约违反(consumed != declared fixedLen)仍抛异常,以暴露 parser bug, 不走 lenient 兜底,避免静默误解析长期误判 - 仅捕获 DecodeException / BufferUnderflowException / IndexOutOfBoundsException, RuntimeException 继续向上抛,保留程序 bug 可见性 - 通过 lingniu.ingest.gb32960.parse.lenientBlockFailure=false 可回退严格模式 不改 InfoBlock.Raw record 结构;下游 Gb32960EventMapper 按 findBlock(Class) 类型 查找,新增 Raw 不影响业务事件映射。 Gb32960BodyParserIsolationTest 4/4 绿;全模块 39 tests 全绿,无回归。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -141,14 +141,57 @@ public final class Gb32960BodyParser {
|
||||
|
||||
int fixedLen = parser.fixedLength();
|
||||
int posBefore = body.position();
|
||||
|
||||
// 预检:固定长度块在剩余字节不足时,lenient 模式兜 Raw 后 break;严格模式保持旧行为抛异常。
|
||||
if (fixedLen >= 0 && body.remaining() < fixedLen) {
|
||||
throw new DecodeException(
|
||||
"info block 0x" + Integer.toHexString(typeCode)
|
||||
+ " needs " + fixedLen + " bytes but got " + body.remaining());
|
||||
if (!lenientBlockFailure) {
|
||||
throw new DecodeException(
|
||||
"info block 0x" + Integer.toHexString(typeCode)
|
||||
+ " needs " + fixedLen + " bytes but got " + body.remaining());
|
||||
}
|
||||
int remaining = body.remaining();
|
||||
byte[] tail = new byte[remaining];
|
||||
body.get(tail);
|
||||
log.warn("[gb32960] truncated block typeCode=0x{} declaredFixedLen={} remaining={} — wrapping tail as Raw",
|
||||
Integer.toHexString(typeCode), fixedLen, remaining);
|
||||
blocks.add(new InfoBlock.Raw(typeCode, InfoBlockType.RAW, tail));
|
||||
break;
|
||||
}
|
||||
InfoBlock block = parser.parse(body);
|
||||
|
||||
InfoBlock block;
|
||||
try {
|
||||
block = parser.parse(body);
|
||||
} catch (DecodeException | java.nio.BufferUnderflowException | IndexOutOfBoundsException e) {
|
||||
if (!lenientBlockFailure) {
|
||||
if (e instanceof DecodeException de) throw de;
|
||||
throw new DecodeException(
|
||||
"parser " + parser.getClass().getSimpleName() + " failed: " + e.getMessage(), e);
|
||||
}
|
||||
// 回滚到 parser.parse 入口,按块类型分支处理。
|
||||
body.position(posBefore);
|
||||
if (fixedLen >= 0) {
|
||||
// 固定长度块:按 fixedLen 截取 → 兜 Raw → continue 循环
|
||||
byte[] corrupt = new byte[fixedLen];
|
||||
body.get(corrupt);
|
||||
log.warn("[gb32960] block parse failed typeCode=0x{} parser={} fixedLen={} — isolated as Raw, continuing",
|
||||
Integer.toHexString(typeCode), parser.getClass().getSimpleName(), fixedLen, e);
|
||||
blocks.add(new InfoBlock.Raw(typeCode, InfoBlockType.RAW, corrupt));
|
||||
continue;
|
||||
}
|
||||
// 变长块:无法安全找下一块边界 → 剩余整段兜 Raw + break
|
||||
int remaining = body.remaining();
|
||||
byte[] tail = new byte[remaining];
|
||||
body.get(tail);
|
||||
log.warn("[gb32960] variable-length block parse failed typeCode=0x{} parser={} remaining={} — wrapping remainder as Raw, stopping loop",
|
||||
Integer.toHexString(typeCode), parser.getClass().getSimpleName(), remaining, e);
|
||||
blocks.add(new InfoBlock.Raw(typeCode, InfoBlockType.RAW, tail));
|
||||
break;
|
||||
}
|
||||
|
||||
int consumed = body.position() - posBefore;
|
||||
if (fixedLen >= 0 && consumed != fixedLen) {
|
||||
// parser 契约违反(声明 fixedLen=X 但实际读了 Y),保持抛异常以暴露 parser bug,
|
||||
// 不走 lenient 兜底,避免静默误解析长期误判。
|
||||
throw new DecodeException(
|
||||
"parser " + parser.getClass().getSimpleName()
|
||||
+ " consumed " + consumed
|
||||
|
||||
Reference in New Issue
Block a user