Catch EOFException in IntrospectingClientHttpResponse

Prior to this commit, the `IntrospectingClientHttpResponse` would try
and read the HTTP response stream in order to check for the presence of
a non-empty message body.

Developers reported that in some cases, an `EOFException` is thrown
instead of returning -1 from the `read()` method. This commit ensures
that this case is taken into account and that we report the response as
an empty body in these cases.

Closes gh-35361
This commit is contained in:
Brian Clozel
2025-09-19 17:49:12 +02:00
parent 1e29911292
commit 0cc79ba366
2 changed files with 77 additions and 33 deletions
@@ -17,11 +17,18 @@
package org.springframework.web.client;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.InputStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.testfixture.http.client.MockClientHttpResponse;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
@@ -30,27 +37,57 @@ import static org.mockito.Mockito.mock;
/**
* Tests for {@link IntrospectingClientHttpResponse}.
*
* @since 5.3.10
* @author Yin-Jui Liao
* @author Brian Clozel
*/
class IntrospectingClientHttpResponseTests {
private final ClientHttpResponse response = mock();
private final IntrospectingClientHttpResponse wrappedResponse = new IntrospectingClientHttpResponse(response);
@ParameterizedTest
@MethodSource("noBodyHttpStatus")
void noMessageBodyWhenStatus(HttpStatus status) throws Exception {
var response = new MockClientHttpResponse(new byte[0], status);
var wrapped = new IntrospectingClientHttpResponse(response);
assertThat(wrapped.hasMessageBody()).isFalse();
}
static Stream<HttpStatusCode> noBodyHttpStatus() {
return Stream.of(HttpStatus.NO_CONTENT, HttpStatus.EARLY_HINTS, HttpStatus.NOT_MODIFIED);
}
@Test
void messageBodyDoesNotExist() throws Exception {
given(response.getBody()).willReturn(null);
assertThat(wrappedResponse.hasEmptyMessageBody()).isTrue();
void noMessageBodyWhenContentLength0() throws Exception {
var response = new MockClientHttpResponse(new byte[0], HttpStatus.OK);
response.getHeaders().setContentLength(0);
var wrapped = new IntrospectingClientHttpResponse(response);
assertThat(wrapped.hasMessageBody()).isFalse();
}
@Test
void emptyMessageWhenNullInputStream() throws Exception {
ClientHttpResponse mockResponse = mock();
given(mockResponse.getBody()).willReturn(null);
var wrappedMock = new IntrospectingClientHttpResponse(mockResponse);
assertThat(wrappedMock.hasEmptyMessageBody()).isTrue();
}
@Test
void messageBodyExists() throws Exception {
InputStream stream = new ByteArrayInputStream("content".getBytes());
given(response.getBody()).willReturn(stream);
assertThat(wrappedResponse.hasEmptyMessageBody()).isFalse();
var stream = new ByteArrayInputStream("content".getBytes());
var response = new MockClientHttpResponse(stream, HttpStatus.OK);
var wrapped = new IntrospectingClientHttpResponse(response);
assertThat(wrapped.hasEmptyMessageBody()).isFalse();
}
@Test
void emptyMessageWhenEOFException() throws Exception {
ClientHttpResponse mockResponse = mock();
InputStream stream = mock();
given(mockResponse.getBody()).willReturn(stream);
given(stream.read()).willThrow(new EOFException());
var wrappedMock = new IntrospectingClientHttpResponse(mockResponse);
assertThat(wrappedMock.hasEmptyMessageBody()).isTrue();
}
}