Release queued body token buffers on multipart cancel - #37115
Open
cj848 wants to merge 1 commit into
Open
Conversation
When a multipart subscriber cancels while MultipartParser has already emitted body tokens beyond the downstream demand, those tokens are held in the Flux.create sink queue (and in downstream operator queues such as windowUntil). On cancellation, Reactor discards the queued tokens, but BodyToken is not a DataBuffer, so the buffers inside the discarded tokens are never released and Netty reports "LEAK: ByteBuf.release() was not called before it's garbage-collected". Register a doOnDiscard hook for BodyToken in MultipartParser.parse() so that a discarded body token releases its buffer, both in the sink queue and in any downstream operator queue that supports discarding. Closes spring-projectsgh-37115 Signed-off-by: Hyunsik Kang <cj848@hanmail.net>
cj848
force-pushed
the
fix-multipart-token-discard-leak
branch
from
August 5, 2026 22:39
c97b6a2 to
93beb6b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
MultipartParseremits multiple tokens per inputDataBuffer(a headers token followed by body tokens), regardless of downstream demand. Tokens emitted beyond the demand are buffered in theFlux.createsink queue, and downstream operators used byDefaultPartHttpMessageReader/PartEventHttpMessageReader(windowUntil,concatMap) keep their own token queues as well.When the subscriber cancels while such tokens are queued — for example when the client aborts the connection mid-upload — Reactor discards the queued tokens.
BodyTokenis not aDataBuffer, so no existingdoOnDiscard(DataBuffer.class, ...)hook can reach the buffer it wraps, and the buffer is dropped without being released. With Netty this surfaces in production as:with leak-detector access records ending in
MultipartParser$BodyState.onNext/DataBufferUtils$AbstractNestedMatcher.matchand no matching release. This is related to, but distinct from, gh-36262: that fix coveredPartGenerator, while this leak happens one stage earlier, in the parser token stream itself.This PR registers a
doOnDiscard(BodyToken.class, ...)hook inMultipartParser.parse(). Since the discard handler is propagated upstream through the subscriber context, a single hook at this convergence point covers the sink queue as well as any downstream operator queue that supports discarding, for bothPartandPartEventreading.The new
MultipartParserTests.cancelWithQueuedBodyTokensReleasesBufferstest reproduces the leak deterministically (no race): a whole multipart message in a single input buffer with a downstream demand of 1 leaves the body token in the sink queue at cancellation time. With the fix reverted, the test fails with1 buffer leaks detected; with the fix applied, it passes.:spring-web:test --tests "org.springframework.http.codec.multipart.*",:spring-web:checkstyleMainand:spring-web:checkstyleTestpass locally.