apache / apache/shenyu

[BUG] releaseIfNotConsumed re-subscribes to a single-use response-body source, so error/cancel release leaks in-flight ByteBufs

Open
#6,724 1 comment 0 reactions 0 assignees View on GitHub
plugin: response priority: medium type: bug
Dominant language
Java
Stars
8.8k
Forks
3.1k
Avg merge
7d 1h
Merged PRs (30d)
85

Description

- Severity: Medium
- Location:
`shenyu-plugin/shenyu-plugin-response/src/main/java/org/apache/shenyu/plugin/response/strategy/WebClientMessageWriter.java:84` + `:124-126`; `shenyu-plugin/shenyu-plugin-response/src/main/java/org/apache/shenyu/plugin/response/strategy/NettyClientMessageWriter.java:81` + `:97-99`
-
Description:
Both writers, on `response.writeWith(body)` error, call `releaseIfNotConsumed(body, error)` whose body is `dataBufferDody.map(DataBufferUtils::release).then(Mono.error(ex))`. That operator **re-subscribes** to the same `body` `Flux` to drain/release remaining buffers. But both body sources are single-subscription: WebClient's `fluxResponseEntity.getBody()` and Netty's `connection.inbound().receive().retain()`. A second subscription errors or emits nothing, so the buffers already emitted-but-not-yet-released when the write failed are never reached by `DataBufferUtils::release`. They leak. On the Netty path the second-subscription `IllegalStateException` can replace the original error signal.
-
Impact:
Pooled direct-memory ByteBuf leak on the response error/cancel path (client abort during streamed response). Compounds PERF-27/28.
-
Suggested fix:
Track emitted buffers via `DataBufferUtils.track(...)` or release-on-complete/cancel inside the same single subscription (`body.doOnDiscard(DataBuffer.class, DataBufferUtils::release)` + `doFinally`), instead of re-subscribing to the single-use source.
-
Confidence: Medium
- Related existing: #6413 is the *request*-body single-use retry bug (different stream and trigger); this is the *response*-body release-on-error re-subscription.

---

## D. Plugin lifecycle / SPI (8 findings)

---
_Identified during the 2026-08-02 deep re-scan; full list in [`docs/scan2-2026-08-02/06-medium-tiers.md`](docs/scan2-2026-08-02/06-medium-tiers.md)._

Contributor guide

No contributing guide indexed for this repository

Research direction

Start in WebClientMessageWriter.java at lines 84 and 124-126, then compare NettyClientMessageWriter.java at lines 81 and 97-99. Trace the response.writeWith(body) error and cancel paths, checking the single-subscription response sources and releaseIfNotConsumed behavior. Done means emitted and remaining buffers are released without re-subscribing, while the original error signal is preserved.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api, backend, performance
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.