fix: AI token limiter null handling, rule isolation, and gzip streaming (#6513, #6514, #6515) - #7138
Merged
Merged
Conversation
…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
approved these changes
Sep 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
修复的问题
改动说明
#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增量解压,内存有界,与 #7124 的BodyWriter方向一致。已知限制
测试
7 个用例:头跨 buffer、头在同一 buffer、首块远大于头缓冲上限(266)的回归用例、多块端到端解压(精确字符串比对)、规则空值填充。
本地
./mvnw -pl shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter -am test通过,checkstyle 0 违规。