DefaultStreamingDecoder ByteBuf leak when decode() is called after close
- Dominant language
- Java
- Stars
- 41.6k
- Forks
- 26.4k
- Avg merge
- 15h 13m
- Merged PRs (30d)
- 4
Description
## Bug Description
`DefaultStreamingDecoder.decode()` does not close the input stream when the decoder has already been closed (`closed == true`). In the Triple HTTP/2 path, DATA frames are wrapped in `ByteBufInputStream(content, true)` (i.e., `releaseOnClose = true`), meaning the underlying pooled `ByteBuf` is released only when `InputStream.close()` is called. If a late DATA frame arrives asynchronously (via `executor.execute()` in `AbstractServerTransportListener.onData()`) after the decoder has started closing, the `ByteBuf` is never released, causing a leak.
## Symmetry with #16389 / #16391
This is the exact same bug pattern as #16389, which was fixed by #16391 for `LengthFieldStreamingDecoder`. The fix in #16391 added `inputStream.close()` in the `decode()` method when the decoder is closing or closed. `DefaultStreamingDecoder` has the same pattern but was not included in that fix:
- **LengthFieldStreamingDecoder** (gRPC path): fixed in #16391 ✅
- **DefaultStreamingDecoder** (non-gRPC path): still leaks ❌
## Steps to Reproduce
1. Create a `DefaultStreamingDecoder` instance
2. Call `onStreamClosed()` to mark the decoder as closed
3. Call `decode(inputStream)` with any input stream
4. The input stream is never closed, and any wrapped `ByteBuf` is leaked
## Impact
ByteBuf memory leak in the non-gRPC Triple HTTP/2 streaming path when late DATA frames arrive after the decoder has been closed. Under sustained traffic with connection churn, leaked `ByteBuf`s accumulate and can eventually trigger Netty's `ResourceLeakDetector` warnings or cause `OutOfMemoryError`.
## Suggested Fix
Mirror the #16391 fix: close the input stream in `decode()` when `closed == true`, propagating close failures as `DecodeException`.
Contributor guide
Research direction
Start at DefaultStreamingDecoder.decode() and compare its closed-state handling with LengthFieldStreamingDecoder and fix #16391. Exercise the onStreamClosed() then decode(inputStream) sequence, including close failures, and verify that the input stream is closed and failures become DecodeException without leaking the wrapped ByteBuf.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- networking
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 75/100