diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java
index ce589bf726..428b14e098 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientResponse.java
@@ -41,14 +41,26 @@ import org.springframework.web.reactive.function.BodyExtractor;
/**
* Represents an HTTP response, as returned by {@link WebClient} and also
- * {@link ExchangeFunction}. Provides access to the response status and headers,
- * and also methods to consume the response body.
+ * {@link ExchangeFunction}. Provides access to the response status and
+ * headers, and also methods to consume the response body.
*
- *
NOTE: When given access to a {@link ClientResponse},
+ *
NOTE: When using a {@link ClientResponse}
* through the {@code WebClient}
* {@link WebClient.RequestHeadersSpec#exchange() exchange()} method,
- * you must always use one of the body or toEntity methods to ensure resources
- * are released and avoid potential issues with HTTP connection pooling.
+ * you have to make sure that the body is consumed or released by using
+ * one of the following methods:
+ *
+ * - {@link #body(BodyExtractor)}
+ * - {@link #bodyToMono(Class)} or
+ * {@link #bodyToMono(ParameterizedTypeReference)}
+ * - {@link #bodyToFlux(Class)} or
+ * {@link #bodyToFlux(ParameterizedTypeReference)}
+ * - {@link #toEntity(Class)} or
+ * {@link #toEntity(ParameterizedTypeReference)}
+ * - {@link #toEntityList(Class)} or
+ * {@link #toEntityList(ParameterizedTypeReference)}
+ * - {@link #releaseBody()}
+ *
* You can use {@code bodyToMono(Void.class)} if no response content is
* expected. However keep in mind that if the response does have content, the
* connection will be closed and will not be placed back in the pool.
@@ -132,6 +144,14 @@ public interface ClientResponse {
*/
Flux bodyToFlux(ParameterizedTypeReference elementTypeRef);
+ /**
+ * Releases the body of this response.
+ * @return a completion signal
+ * @since 5.2
+ * @see org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer)
+ */
+ Mono releaseBody();
+
/**
* Return this response as a delayed {@code ResponseEntity}.
* @param bodyClass the expected response body type
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java
index 395f785be8..18eff19e79 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientResponse.java
@@ -155,6 +155,13 @@ class DefaultClientResponse implements ClientResponse {
return body(BodyExtractors.toFlux(elementTypeRef));
}
+ @Override
+ public Mono releaseBody() {
+ return body(BodyExtractors.toDataBuffers())
+ .map(DataBufferUtils::release)
+ .then();
+ }
+
@Override
public Mono> toEntity(Class bodyType) {
return WebClientUtils.toEntity(this, bodyToMono(bodyType));
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapper.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapper.java
index d1d3c6a59c..045e0bd6b0 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapper.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/client/support/ClientResponseWrapper.java
@@ -118,6 +118,11 @@ public class ClientResponseWrapper implements ClientResponse {
return this.delegate.bodyToFlux(elementTypeRef);
}
+ @Override
+ public Mono releaseBody() {
+ return this.delegate.releaseBody();
+ }
+
@Override
public Mono> toEntity(Class bodyType) {
return this.delegate.toEntity(bodyType);
diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientDataBufferAllocatingTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientDataBufferAllocatingTests.java
index cc3213ab30..397ae88102 100644
--- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientDataBufferAllocatingTests.java
+++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/client/WebClientDataBufferAllocatingTests.java
@@ -166,6 +166,25 @@ public class WebClientDataBufferAllocatingTests extends AbstractDataBufferAlloca
});
}
+ @ParameterizedDataBufferAllocatingTest
+ public void releaseBody(String displayName, DataBufferFactory bufferFactory) {
+ super.bufferFactory = bufferFactory;
+
+ this.server.enqueue(new MockResponse()
+ .setResponseCode(200)
+ .setHeader("Content-Type", "text/plain")
+ .setBody("foo bar"));
+
+ Mono result = this.webClient.get()
+ .exchange()
+ .flatMap(ClientResponse::releaseBody);
+
+
+ StepVerifier.create(result)
+ .expectComplete()
+ .verify(Duration.ofSeconds(3));
+ }
+
private void testOnStatus(Throwable expected,
Function> exceptionFunction) {