mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Prior to this commit, DataBufferUtils$ReadCompletionHandler#read() invoked AsynchronousFileChannel#read(ByteBuffer, long, Attachment, CompletionHandler) without guarding against exceptions thrown directly by that call. Although that method is documented to report failures asynchronously via the supplied CompletionHandler, some platform- specific implementations can instead throw synchronously – for example, on Windows with JDK 25, when the JDK rejects a ByteBuffer backed by a closeable shared memory Arena, as produced by Netty 4.2's off-heap buffer allocation. When such an exception is thrown from a recursive read() invocation triggered from completed() – which happens once a resource requires more than a single chunk – the exception has no path back to the FluxSink: it escapes on whatever thread invoked the CompletionHandler, and the resulting Flux never signals onError or onComplete. In practice, this surfaced as an indefinite hang when serving a Resource whose HTTP response is not a ZeroCopyHttpOutputMessage, since ResourceHttpMessageWriter falls back to ResourceEncoder, which reads the resource via DataBufferUtils. To address that, this commit wraps the channel.read(...) call in a try/catch block and routes any non-fatal Throwable to the existing failed(Throwable, Attachment) handler, via Exceptions.throwIfFatal(), mirroring the equivalent fix already applied to the write side for gh-36184. This ensures the allocated DataBuffer is released and the Flux always terminates with a proper error signal instead of leaking a buffer or hanging silently. See gh-36184 Closes gh-37143