Aias00 opened a new issue, #7708: URL: https://github.com/apache/shenyu/issues/7708
### Current behavior `AiStatisticServerHttpResponse.writeAndFlushWith()` maps each inner publisher to `appendResponse()`. Each call allocates a new `BodyWriter` and, for gzip, a new decoder. The parser and fallback tail therefore have inner-publisher scope instead of response scope. The normal Netty streaming writer creates an inner publisher per received buffer (`body.map(Flux::just)`). Network buffers do not have to align with SSE lines or JSON fields. A valid usage event split between buffers can thus never be reassembled by the fallback. ### Locations / production path - [AiTokenLimiterPlugin.java:225–242](https://github.com/apache/shenyu/blob/66d7d954ca37d651a5ad916f988a8081157a7ab4/shenyu-plugin/shenyu-plugin-ai/shenyu-plugin-ai-token-limiter/src/main/java/org/apache/shenyu/plugin/ai/token/limiter/AiTokenLimiterPlugin.java#L225) - [NettyClientMessageWriter.java:75–76](https://github.com/apache/shenyu/blob/66d7d954ca37d651a5ad916f988a8081157a7ab4/shenyu-plugin/shenyu-plugin-response/src/main/java/org/apache/shenyu/plugin/response/strategy/NettyClientMessageWriter.java#L75) ### Reproduction / validation Use an uncompressed `text/event-stream` response containing this single valid SSE event, split across two buffers at the indicated boundary: ```text data: {"usage":{"completion_ | tokens":12}} ``` (The ` | ` is a boundary marker, not payload.) Deliver the same two buffers first via `writeWith(Flux.just(first, second))`, then via the production streaming shape `writeAndFlushWith(Flux.just(Mono.just(first), Mono.just(second)))`. A standalone Java probe compiled the current token-limiter plugin and decoder sources and used a real mock WebFlux response with a recording token consumer: ```text WRITE recorded=[12] SUM=12 FLUSH recorded=[0, 0] SUM=0 ``` This proves a response-accounting regression dependent only on buffer/flush boundaries; Redis and a live AI provider were not needed. The bytes forwarded to the client still form a valid SSE event. ### Impact / priority **P2 / Medium; confirmed, high confidence.** Valid streaming responses can consume upstream tokens without recording the reported usage, making window counters inaccurate. This differs from the designed delay between request admission and later accounting. ### Suggested fix Own SSE framing, fallback tail and decoder state for the entire response. Preserve streaming flush behavior while carrying incomplete lines/events across inner publishers. Finalize accounting/decoder cleanup once per response rather than independently for each transport buffer; keep retained memory bounded. ### Related existing work #6515 concerns a gzip header split across buffers and its old header-skipping decoder. This reproduction is uncompressed and the defect is state reset between flush publishers. #6758 concerns bounded tail retention. #6801 concerns per-chunk regex overhead. #7702 concerns admission/reservation semantics, not missing known response usage. The ordinary `writeWith` path has a cross-buffer tail fallback and is explicitly the passing control here. ### Reviewed revision Apache ShenYu 2.7.2-SNAPSHOT, `master@66d7d954ca37d651a5ad916f988a8081157a7ab4`. Unit-test and CI issue categories excluded. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
