diff --git a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960BodyParser.java b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960BodyParser.java index 8a9dc857..ab637d37 100644 --- a/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960BodyParser.java +++ b/protocol-gb32960/src/main/java/com/lingniu/ingest/protocol/gb32960/codec/Gb32960BodyParser.java @@ -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