Closed
Description
Denys Ivano opened SPR-17054 and commented
When remote service responses with Content-Type
that can't be read by HttpMessageReader
, an instance of UnsupportedMediaTypeException
is being thrown (see BodyExtractors.readWithMessageReaders()
). But ClientResponse
's body is being ignored in this case.
From ClientResponse
's Javadoc:
* <p><strong>NOTE:</strong> When given access to 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 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.
So in order to release resources and avoid potential issues with HTTP connection pool, the response body must be consumed.
I've created a test that reproduces this issue:
@Test
public void shouldConsumeBodyOnUnsupportedMediaTypeException() {
AtomicBoolean bodyConsumed = new AtomicBoolean();
ExchangeFunction exchangeFunction = mock(ExchangeFunction.class);
ClientResponse response = ClientResponse.create(HttpStatus.OK)
.header(HttpHeaders.CONTENT_TYPE, "application/unknown")
// .header(HttpHeaders.CONTENT_TYPE, "application/json")
.body(Flux.defer(() -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
return Flux.just("{\"name\": \"Hello World!\"}").
map(s -> {
bodyConsumed.set(true);
byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
return dataBufferFactory.wrap(bytes);
});
}))
.build();
when(exchangeFunction.exchange(any())).thenReturn(Mono.just(response));
WebClient webClient = WebClient.builder()
.exchangeFunction(exchangeFunction)
.build();
Mono<String> result = webClient.get()
.retrieve()
.bodyToMono(TestResponse.class)
.map(TestResponse::getName);
StepVerifier.create(result)
.expectError(UnsupportedMediaTypeException.class)
// .expectNext("Hello World!")
// .expectComplete()
.verify(Duration.ZERO);
assertTrue("Response body wasn't consumed", bodyConsumed.get());
}
private static class TestResponse {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Issue Links:
- DataBufferUtils#join could leak buffers in case of error from the source [SPR-17025] #21563 DataBufferUtils#join could leak buffers in case of error from the source
- WebClient throws "Only one connection receive subscriber allowed" when response has content but no Content-Type header [SPR-17482] #22014 WebClient throws "Only one connection receive subscriber allowed" when response has content but no Content-Type header