Skip to content

fix: AI token limiter null handling, rule isolation, and gzip streaming (#6513, #6514, #6515) - #7138

Merged
Aias00 merged 2 commits into
apache:masterfrom
HY-love-sleep:fix/ai-token-limiter
Sep 21, 2026
Merged

Aias00 merged 2 commits into
apache:masterfrom
HY-love-sleep:fix/ai-token-limiter

Conversation

@HY-love-sleep

Copy link
Copy Markdown
Contributor

修复的问题

改动说明

#6513 规则字段为 null 导致请求处理崩溃

真实崩溃点是 isAllowed()>= tokenLimit 的 null 拆箱、以及 recordTokensUsage() 里的 Duration.ofSeconds(null)
AiTokenLimiterPluginHandler.handlerRule() 中复用 AiTokenLimiterHandle.newDefaultInstance() 补默认值,与 admin 新建规则时的默认保持一致。

#6514 Redis 计数 key 未按 selector / rule 隔离

key 从 PREFIX + resolverValue 改为 PREFIX + CacheKeyUtils.INST.getKey(rule) + ":" + resolverValue,复用与 handle 缓存同源的规则 key。
注意:key 格式变更,存量计数器会保留旧 key 并在时间窗口后自然过期

#6515 gzip 响应跨 DataBuffer 时漏算 token

原实现手工定位 gzip 头,并假设整个 header 都在第一个 DataBuffer 内;当 header 跨 buffer 边界时偏移算错,负载根本没被解析。
新增包级可见的 GzipStreamDecoder:GzipHeaderState 跨 buffer 解析 gzip 头(支持 FEXTRA/FNAME/FCOMMENT/FHCRC),配合 Inflater 增量解压,内存有界,与 #7124BodyWriter 方向一致。

已知限制

  • 不支持 multi-member gzip(解完第一个 deflate 流后停止)
  • gzip 头超过 266 字节时放弃解压并打一条 WARN

测试

7 个用例:头跨 buffer、头在同一 buffer、首块远大于头缓冲上限(266)的回归用例、多块端到端解压(精确字符串比对)、规则空值填充。
本地 ./mvnw -pl shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter -am test 通过,checkstyle 0 违规。

…ng (apache#6513, apache#6514, apache#6515)

1. apache#6513 null rule fields crash request processing
   Fill defaults for tokenLimit, timeWindowSeconds, aiTokenLimitType and keyName in
   AiTokenLimiterPluginHandler.handlerRule(), reusing
   AiTokenLimiterHandle.newDefaultInstance() so the values match the defaults the
   admin applies to a new rule.
   The crash came from isAllowed() (unboxing a null tokenLimit) and from
   recordTokensUsage() (Duration.ofSeconds(null)).

2. apache#6514 Redis counter key ignores selector and rule scope
   The key becomes PREFIX + CacheKeyUtils.INST.getKey(rule) + ":" + resolverValue,
   reusing the same rule key as the handle cache, so counters are scoped per
   selector and rule.
   Note: the key format change means existing counters expire naturally via TTL.

3. apache#6515 gzip responses spanning multiple DataBuffers undercount tokens
   The previous implementation located the gzip header by hand and assumed it was
   fully contained in the first DataBuffer; when the header straddled a buffer
   boundary the computed offset was wrong and the payload was never parsed.
   Add a package-visible GzipStreamDecoder: GzipHeaderState parses the gzip header
   across buffers (FEXTRA/FNAME/FCOMMENT/FHCRC) and an incremental Inflater
   decompresses each buffer, keeping memory bounded - in the same spirit as apache#7124.
   Known limitations: multi-member gzip streams are not supported (decompression
   stops after the first deflate stream), and a header larger than 266 bytes
   abandons decompression with a WARN.

Tests: 7 cases covering a header spanning buffers, a header contained in a single
buffer, a regression case whose first chunk is far larger than the header buffer,
and multi-chunk end-to-end decompression asserted by exact string comparison.
@Aias00
Aias00 merged commit 82c9d54 into apache:master Sep 21, 2026
37 checks passed
@HY-love-sleep
HY-love-sleep deleted the fix/ai-token-limiter branch September 21, 2026 02:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants