The documentation states:

When you use exchange(), you must always use any of the body or toEntity methods of ClientResponse to ensure resources are released and to avoid potential issues with HTTP connection pooling. You can use bodyToMono(Void.class) if no response content is expected. However, if the response does have content, the connection is closed and is not placed back in the pool.

This leaves some confusion to the reader. If I don't want the connection removed from the pool, what should I do? Are there other choices with different tradeoffs?

It would be nice if something like this https://stackoverflow.com/a/51321602/5861829 was added to the Javadoc and reference that explained the choices developers have with the tradeoffs. I think the samples should probably use exchange()rather than retrieve to illustrate how to consume the body. Something like this would be valuable:

HttpStatus status = HttpStatus.resolve(response.rawStatusCode());
if (status == null || && !status.is2xxSuccessful()) {
    // extract the contents of this into a method named oauth2AccessTokenResponse but has an argument for the response
    return response.bodyToFlux(DataBuffer.class)
        .map(DataBufferUtils::release)
        .then(Mono.error(new RuntimeException("oops")));
}
return response.bodyToMono(String.class)

I think it is also valuable to point out it is important to avoid having an exception occur in the code to avoid a leak. Something that has surprised us is that ClientResponse.statusCode() can throw an IllegalArgumentException if the status is unknown. Calling this out explicitly and demonstrating how to use HttpStatus.resolve(response.rawStatusCode()) might help others.

Comment From: poutsma

As discussed during our meeting, adding explicit methods can hopefully improve the understanding about how to consume the body. I suggested to add

  • ClientResponse::releaseBody that ~~effectively does the same as bodyToMono(Void.class)~~ consumes and release the body.
  • WebClient.ResponseSpec::toBodilessEntity() that ~~effectively does the same as toEntity(Void.class)~~ calls releaseBody and returns the status and headers.

Comment From: rstoyanchev

bodyToMono(Void.class) actually tries to read and closes if any data arrives while discardBody would actually consume and discard, right? So effectively those new methods would provide something more than is available today.

Comment From: poutsma

Updated my comment accordingly.

Comment From: nakamorichi

@rwinch @poutsma @rstoyanchev So which one should I use for fire-and-forget request? Body is not expected in successful case, but in error case, called API returns body. Basically we just want to return Mono<Void>, so with toBodilessEntity() we would have to chain it with then() to get same behavior..?