Skip to content

Commit d8bc54d

Browse files
cookie-meringuebclozel
authored andcommitted
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>
1 parent 6467fca commit d8bc54d

2 files changed

Lines changed: 26 additions & 3 deletions

File tree

spring-core/src/main/java/org/springframework/core/io/buffer/DataBufferUtils.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,9 +1138,11 @@ protected void hookOnSubscribe(Subscription subscription) {
11381138
protected void hookOnNext(DataBuffer dataBuffer) {
11391139
try {
11401140
try (DataBuffer.ByteBufferIterator iterator = dataBuffer.readableByteBuffers()) {
1141-
ByteBuffer byteBuffer = iterator.next();
1142-
while (byteBuffer.hasRemaining()) {
1143-
this.channel.write(byteBuffer);
1141+
while (iterator.hasNext()) {
1142+
ByteBuffer byteBuffer = iterator.next();
1143+
while (byteBuffer.hasRemaining()) {
1144+
this.channel.write(byteBuffer);
1145+
}
11441146
}
11451147
}
11461148
this.sink.next(dataBuffer);

spring-core/src/test/java/org/springframework/core/io/buffer/DataBufferUtilsTests.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,27 @@ void writeWritableByteChannel(DataBufferFactory bufferFactory) throws Exception
338338
channel.close();
339339
}
340340

341+
@ParameterizedDataBufferAllocatingTest
342+
void writeWritableByteChannelWithJoinedBuffer(DataBufferFactory bufferFactory) throws Exception {
343+
super.bufferFactory = bufferFactory;
344+
345+
DataBuffer foo = stringBuffer("foo");
346+
DataBuffer bar = stringBuffer("bar");
347+
DataBuffer joined = bufferFactory.join(List.of(foo, bar));
348+
349+
WritableByteChannel channel = Files.newByteChannel(tempFile, StandardOpenOption.WRITE);
350+
351+
Flux<DataBuffer> writeResult = DataBufferUtils.write(Flux.just(joined), channel);
352+
StepVerifier.create(writeResult)
353+
.consumeNextWith(stringConsumer("foobar"))
354+
.verifyComplete();
355+
356+
String result = String.join("", Files.readAllLines(tempFile));
357+
358+
assertThat(result).isEqualTo("foobar");
359+
channel.close();
360+
}
361+
341362
@ParameterizedDataBufferAllocatingTest
342363
void writeWritableByteChannelErrorInFlux(DataBufferFactory bufferFactory) throws Exception {
343364
super.bufferFactory = bufferFactory;

0 commit comments

Comments
 (0)