apache / apache/dubbo

DefaultStreamingDecoder ByteBuf leak when decode() is called after close

Open Beginner friendly
#16,399 1 comment 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.