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.
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:
Steps to Reproduce
- Create a
DefaultStreamingDecoder instance
- Call
onStreamClosed() to mark the decoder as closed
- Call
decode(inputStream) with any input stream
- 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 ByteBufs 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.
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 inByteBufInputStream(content, true)(i.e.,releaseOnClose = true), meaning the underlying pooledByteBufis released only whenInputStream.close()is called. If a late DATA frame arrives asynchronously (viaexecutor.execute()inAbstractServerTransportListener.onData()) after the decoder has started closing, theByteBufis 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 addedinputStream.close()in thedecode()method when the decoder is closing or closed.DefaultStreamingDecoderhas the same pattern but was not included in that fix:Steps to Reproduce
DefaultStreamingDecoderinstanceonStreamClosed()to mark the decoder as closeddecode(inputStream)with any input streamByteBufis leakedImpact
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
ByteBufs accumulate and can eventually trigger Netty'sResourceLeakDetectorwarnings or causeOutOfMemoryError.Suggested Fix
Mirror the #16391 fix: close the input stream in
decode()whenclosed == true, propagating close failures asDecodeException.