Skip to content

Commit e737980

Browse files
committed
Ensure chunks released on cancel in StringDecoder
The current test were not catching the issue because they request 1 via StepVerifier, wait for it, and then cancel. In the case of StringDecoder it means all chunks are used up to produce that first String and so the cancel doesn't catch any cached chunks. Closes gh-30299
1 parent 93fa010 commit e737980

File tree

2 files changed

+21
-8
lines changed

2 files changed

+21
-8
lines changed

spring-core/src/main/java/org/springframework/core/codec/StringDecoder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public Flux<String> decode(Publisher<DataBuffer> input, ResolvableType elementTy
124124
chunks.clear();
125125
return Mono.just(lastBuffer);
126126
}))
127-
.doOnTerminate(chunks::releaseAndClear)
127+
.doFinally(signalType -> chunks.releaseAndClear())
128128
.doOnDiscard(DataBuffer.class, DataBufferUtils::release)
129129
.map(buffer -> decode(buffer, elementType, mimeType, hints));
130130
}

spring-core/src/test/java/org/springframework/core/codec/StringDecoderTests.java

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2023 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,12 +18,15 @@
1818

1919
import java.nio.charset.Charset;
2020
import java.nio.charset.StandardCharsets;
21+
import java.time.Duration;
2122
import java.util.ArrayList;
2223
import java.util.Arrays;
2324
import java.util.Collections;
2425
import java.util.List;
2526

2627
import org.junit.jupiter.api.Test;
28+
import org.reactivestreams.Subscription;
29+
import reactor.core.publisher.BaseSubscriber;
2730
import reactor.core.publisher.Flux;
2831
import reactor.test.StepVerifier;
2932

@@ -75,14 +78,15 @@ public void decode() {
7578
String s = String.format("%s\n%s\n%s", u, e, o);
7679
Flux<DataBuffer> input = toDataBuffers(s, 1, UTF_8);
7780

78-
// TODO: temporarily replace testDecodeAll with explicit decode/cancel/empty
79-
// see https://github.com/reactor/reactor-core/issues/2041
81+
testDecodeAll(input, TYPE, step -> step.expectNext(u, e, o).verifyComplete(), null, null);
82+
}
8083

81-
// testDecode(input, TYPE, step -> step.expectNext(u, e, o).verifyComplete(), null, null);
82-
// testDecodeCancel(input, TYPE, null, null);
83-
// testDecodeEmpty(TYPE, null, null);
84+
@Test // gh-30299
85+
public void decodeAndCancelWithPendingChunks() {
86+
Flux<DataBuffer> input = toDataBuffers("abc", 1, UTF_8).concatWith(Flux.never());
87+
Flux<String> result = this.decoder.decode(input, TYPE, null, null);
8488

85-
testDecodeAll(input, TYPE, step -> step.expectNext(u, e, o).verifyComplete(), null, null);
89+
StepVerifier.create(result).thenAwait(Duration.ofMillis(100)).thenCancel().verify();
8690
}
8791

8892
@Test
@@ -264,4 +268,13 @@ private DataBuffer stringBuffer(String value) {
264268
return buffer;
265269
}
266270

271+
272+
private static class SingleRequestSubscriber extends BaseSubscriber<String> {
273+
274+
@Override
275+
protected void hookOnSubscribe(Subscription subscription) {
276+
subscription.request(1);
277+
}
278+
}
279+
267280
}

0 commit comments

Comments
 (0)