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 fixedLen = parser.fixedLength();
|
||||||
int posBefore = body.position();
|
int posBefore = body.position();
|
||||||
|
|
||||||
|
// 预检:固定长度块在剩余字节不足时,lenient 模式兜 Raw 后 break;严格模式保持旧行为抛异常。
|
||||||
if (fixedLen >= 0 && body.remaining() < fixedLen) {
|
if (fixedLen >= 0 && body.remaining() < fixedLen) {
|
||||||
throw new DecodeException(
|
if (!lenientBlockFailure) {
|
||||||
"info block 0x" + Integer.toHexString(typeCode)
|
throw new DecodeException(
|
||||||
+ " needs " + fixedLen + " bytes but got " + body.remaining());
|
"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;
|
int consumed = body.position() - posBefore;
|
||||||
if (fixedLen >= 0 && consumed != fixedLen) {
|
if (fixedLen >= 0 && consumed != fixedLen) {
|
||||||
|
// parser 契约违反(声明 fixedLen=X 但实际读了 Y),保持抛异常以暴露 parser bug,
|
||||||
|
// 不走 lenient 兜底,避免静默误解析长期误判。
|
||||||
throw new DecodeException(
|
throw new DecodeException(
|
||||||
"parser " + parser.getClass().getSimpleName()
|
"parser " + parser.getClass().getSimpleName()
|
||||||
+ " consumed " + consumed
|
+ " consumed " + consumed
|
||||||
|
|||||||
Reference in New Issue
Block a user