[BUG] releaseIfNotConsumed re-subscribes to a single-use response-body source, so error/cancel release leaks in-flight ByteBufs
- 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