mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Fix memory leak in WiretapConnector
Prior to this commit, we found in gh-35953 that using the `WebTestClient`
the following way leaks data buffers:
```
var body = client.get().uri("download")
.exchange()
.expectStatus().isOk()
.returnResult()
.getResponseBodyContent();
```
Here, the test performs expectations on the response status and headers,
but not on the response body. The WiretapConnector already supports this
case by subscribing to the Flux response body in those cases and
accumulating the entire content as a single byte[].
Here, the `DataBuffer` instances are not decoded by any `Decoder` and
are not released. This results in a memory leak.
This commit ensures that the automatic subscription in
`WiretapConnector` also releases the buffers automatically as the DSL
does not allow at that point to go back to performing body expectations.
Fixes gh-36050
This commit is contained in:
+16
-5
@@ -31,6 +31,7 @@ import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.Sinks;
|
||||
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
@@ -196,11 +197,21 @@ class WiretapConnector implements ClientHttpConnector {
|
||||
// 1. Mock server never consumed request body (for example, error before read)
|
||||
// 2. FluxExchangeResult: getResponseBodyContent called before getResponseBody
|
||||
//noinspection ConstantConditions
|
||||
(this.publisher != null ? this.publisher : this.publisherNested)
|
||||
.onErrorMap(ex -> new IllegalStateException(
|
||||
"Content has not been consumed, and " +
|
||||
"an error was raised while attempting to produce it.", ex))
|
||||
.subscribe();
|
||||
if (this.publisher != null) {
|
||||
this.publisher.doOnNext(DataBufferUtils::release)
|
||||
.onErrorMap(ex -> new IllegalStateException(
|
||||
"Content has not been consumed, and " +
|
||||
"an error was raised while attempting to produce it.", ex))
|
||||
.subscribe();
|
||||
}
|
||||
else if (this.publisherNested != null) {
|
||||
this.publisherNested
|
||||
.map(pub -> Flux.from(pub).doOnNext(DataBufferUtils::release))
|
||||
.onErrorMap(ex -> new IllegalStateException(
|
||||
"Content has not been consumed, and " +
|
||||
"an error was raised while attempting to produce it.", ex))
|
||||
.subscribe();
|
||||
}
|
||||
}
|
||||
return this.content.asMono();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user