Skip to content

Ensure that WebClient disposes the HTTP client connection once the client response is consumed [SPR-15920] #20474

@spring-projects-issues

Description

@spring-projects-issues

Brian Clozel opened SPR-15920 and commented

Problems with the WebClient API

Given the current WebClient API, it is possible to have:

Mono<String> result = this.webClient.get()
				.uri("/greeting")
				.retrieve()
				.bodyToMono(String.class);

In that case, we're consuming the response body completely; under the covers, reactor-netty will dispose the connection (close it or return it to the connection pool) as soon as the body has been consumed.

But we can also do this:

Mono<HttpStatus> status = this.webClient.get()
				.uri("/example")
				.exchange()
				.map(response -> response.statusCode());

In that case, the body is not consumed, and the underlying client has no way of knowing that the connection should be closed. This can lead to issues with the connection pool, memory leaks, etc.

In the ClientConnector#connect method, before returning the ClientHttpResponse, we could do something like:

responseMono.doOnTerminate((response, ex) -> {
  if (response != null) {
    response.dispose();
  }
})

But unfortunately, this will close the connection too soon. The first example can be rewritten like:

Mono<ResponseEntity<String>> result = this.webClient.get()
				.uri("/greeting")
				.exchange()
				.flatMap(response -> response.toEntity(String.class));

With the flatMap operator, we wait until the Mono<ClientHttpResponse> is completed and proceed with the body. The completion triggers that doOnTerminate operator.

Reactor Netty changes

Reactor Netty is currently dealing with this in its API and considering the following changes:

public interface ResponseReceiver  {
 
  // HttpClientResponse has no reference to the body, just headers/status/etc
  Mono<HttpClientResponse> response();

  <V> Flux<V> response(BiFunction<? super HttpClientResponse, ? super ByteBufFlux, ? extends Publisher<? extends V>> receiver);

  ByteBufFlux responseContent();

  <V> Mono<V> responseSingle(BiFunction<? super HttpClientResponse, ? super ByteBufMono, ? extends Mono<? extends V>> receiver);

}

With this type of changes, the response body Publisher is tied back to the lifecycle of the connection.

We need to revisit our current client API to make sure that the connection lifecycle can be properly managed for the underlying client library.


Issue Links:

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions