Fix data loss in DataBufferUtils synchronous write

Prior to this commit, WritableByteChannelSubscriber.hookOnNext() called
iterator.next() exactly once. If a DataBuffer consisted of multiple NIO
ByteBuffers (e.g., NettyDataBuffer wrapping a CompositeByteBuf), only
the first buffer was written to the channel, and the remaining buffers
were silently ignored and lost.

This commit adds the missing while (iterator.hasNext()) outer loop to
ensure all fragmented buffers exposed by the iterator are completely
and safely written to the synchronous channel.

See gh-36714

Signed-off-by: KimDaehyeon <daehyeon3351@gmail.com>
This commit is contained in:
KimDaehyeon
2026-04-28 03:55:21 +09:00
committed by Brian Clozel
parent 6467fca05b
commit d8bc54d2e7
2 changed files with 26 additions and 3 deletions
@@ -1138,9 +1138,11 @@ public abstract class DataBufferUtils {
protected void hookOnNext(DataBuffer dataBuffer) {
try {
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
ByteBuffer byteBuffer = iterator.next();
while (byteBuffer.hasRemaining()) {
this.channel.write(byteBuffer);
while (iterator.hasNext()) {
ByteBuffer byteBuffer = iterator.next();
while (byteBuffer.hasRemaining()) {
this.channel.write(byteBuffer);
}
}
}
this.sink.next(dataBuffer);
@@ -338,6 +338,27 @@ class DataBufferUtilsTests extends AbstractDataBufferAllocatingTests {
channel.close();
}
@ParameterizedDataBufferAllocatingTest
void writeWritableByteChannelWithJoinedBuffer(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;
DataBuffer foo = stringBuffer("foo");
DataBuffer bar = stringBuffer("bar");
DataBuffer joined = bufferFactory.join(List.of(foo, bar));
WritableByteChannel channel = Files.newByteChannel(tempFile, StandardOpenOption.WRITE);
Flux<DataBuffer> writeResult = DataBufferUtils.write(Flux.just(joined), channel);
StepVerifier.create(writeResult)
.consumeNextWith(stringConsumer("foobar"))
.verifyComplete();
String result = String.join("", Files.readAllLines(tempFile));
assertThat(result).isEqualTo("foobar");
channel.close();
}
@ParameterizedDataBufferAllocatingTest
void writeWritableByteChannelErrorInFlux(DataBufferFactory bufferFactory) throws Exception {
super.bufferFactory = bufferFactory;