Skip to content

Commit c129e8a

Browse files
committed
Merge branch '6.2.x'
2 parents d038269 + 0cc79ba commit c129e8a

File tree

2 files changed

+77
-33
lines changed

2 files changed

+77
-33
lines changed

spring-web/src/main/java/org/springframework/web/client/IntrospectingClientHttpResponse.java

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.web.client;
1818

19+
import java.io.EOFException;
1920
import java.io.IOException;
2021
import java.io.InputStream;
2122
import java.io.PushbackInputStream;
@@ -34,7 +35,6 @@
3435
* @author Brian Clozel
3536
* @author Rossen Stoyanchev
3637
* @since 4.1.5
37-
* @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3.3">RFC 7230 Section 3.3.3</a>
3838
*/
3939
class IntrospectingClientHttpResponse extends ClientHttpResponseDecorator {
4040

@@ -47,32 +47,34 @@ public IntrospectingClientHttpResponse(ClientHttpResponse response) {
4747

4848

4949
/**
50-
* Indicates whether the response has a message body.
50+
* Indicates whether the response might have a message body.
5151
* <p>Implementation returns {@code false} for:
5252
* <ul>
5353
* <li>a response status of {@code 1XX}, {@code 204} or {@code 304}</li>
5454
* <li>a {@code Content-Length} header of {@code 0}</li>
5555
* </ul>
56-
* @return {@code true} if the response has a message body, {@code false} otherwise
56+
* <p>In other cases, the server could use a {@code Transfer-Encoding} header or just
57+
* write the body and close the response. Reading the message body is then the only way
58+
* to check for the presence of a body.
59+
* @return {@code true} if the response might have a message body, {@code false} otherwise
5760
* @throws IOException in case of I/O errors
61+
* @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3.3">RFC 7230 Section 3.3.3</a>
5862
*/
5963
public boolean hasMessageBody() throws IOException {
6064
HttpStatusCode statusCode = getStatusCode();
6165
if (statusCode.is1xxInformational() || statusCode == HttpStatus.NO_CONTENT ||
6266
statusCode == HttpStatus.NOT_MODIFIED) {
6367
return false;
6468
}
65-
if (getHeaders().getContentLength() == 0) {
66-
return false;
67-
}
68-
return true;
69+
return getHeaders().getContentLength() != 0;
6970
}
7071

7172
/**
7273
* Indicates whether the response has an empty message body.
7374
* <p>Implementation tries to read the first bytes of the response stream:
7475
* <ul>
7576
* <li>if no bytes are available, the message body is empty</li>
77+
* <li>if an {@link EOFException} is thrown, the body is considered empty</li>
7678
* <li>otherwise it is not empty and the stream is reset to its start for further reading</li>
7779
* </ul>
7880
* @return {@code true} if the response has a zero-length message body, {@code false} otherwise
@@ -85,26 +87,31 @@ public boolean hasEmptyMessageBody() throws IOException {
8587
if (body == null) {
8688
return true;
8789
}
88-
if (body.markSupported()) {
89-
body.mark(1);
90-
if (body.read() == -1) {
91-
return true;
90+
try {
91+
if (body.markSupported()) {
92+
body.mark(1);
93+
if (body.read() == -1) {
94+
return true;
95+
}
96+
else {
97+
body.reset();
98+
return false;
99+
}
92100
}
93101
else {
94-
body.reset();
95-
return false;
102+
this.pushbackInputStream = new PushbackInputStream(body);
103+
int b = this.pushbackInputStream.read();
104+
if (b == -1) {
105+
return true;
106+
}
107+
else {
108+
this.pushbackInputStream.unread(b);
109+
return false;
110+
}
96111
}
97112
}
98-
else {
99-
this.pushbackInputStream = new PushbackInputStream(body);
100-
int b = this.pushbackInputStream.read();
101-
if (b == -1) {
102-
return true;
103-
}
104-
else {
105-
this.pushbackInputStream.unread(b);
106-
return false;
107-
}
113+
catch (EOFException exc) {
114+
return true;
108115
}
109116
}
110117

spring-web/src/test/java/org/springframework/web/client/IntrospectingClientHttpResponseTests.java

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,18 @@
1717
package org.springframework.web.client;
1818

1919
import java.io.ByteArrayInputStream;
20+
import java.io.EOFException;
2021
import java.io.InputStream;
22+
import java.util.stream.Stream;
2123

2224
import org.junit.jupiter.api.Test;
25+
import org.junit.jupiter.params.ParameterizedTest;
26+
import org.junit.jupiter.params.provider.MethodSource;
2327

28+
import org.springframework.http.HttpStatus;
29+
import org.springframework.http.HttpStatusCode;
2430
import org.springframework.http.client.ClientHttpResponse;
31+
import org.springframework.web.testfixture.http.client.MockClientHttpResponse;
2532

2633
import static org.assertj.core.api.Assertions.assertThat;
2734
import static org.mockito.BDDMockito.given;
@@ -30,27 +37,57 @@
3037
/**
3138
* Tests for {@link IntrospectingClientHttpResponse}.
3239
*
33-
* @since 5.3.10
34-
* @author Yin-Jui Liao
40+
* @author Brian Clozel
3541
*/
3642
class IntrospectingClientHttpResponseTests {
3743

38-
private final ClientHttpResponse response = mock();
3944

40-
private final IntrospectingClientHttpResponse wrappedResponse = new IntrospectingClientHttpResponse(response);
45+
@ParameterizedTest
46+
@MethodSource("noBodyHttpStatus")
47+
void noMessageBodyWhenStatus(HttpStatus status) throws Exception {
48+
var response = new MockClientHttpResponse(new byte[0], status);
49+
var wrapped = new IntrospectingClientHttpResponse(response);
4150

51+
assertThat(wrapped.hasMessageBody()).isFalse();
52+
}
53+
54+
static Stream<HttpStatusCode> noBodyHttpStatus() {
55+
return Stream.of(HttpStatus.NO_CONTENT, HttpStatus.EARLY_HINTS, HttpStatus.NOT_MODIFIED);
56+
}
57+
58+
@Test
59+
void noMessageBodyWhenContentLength0() throws Exception {
60+
var response = new MockClientHttpResponse(new byte[0], HttpStatus.OK);
61+
response.getHeaders().setContentLength(0);
62+
var wrapped = new IntrospectingClientHttpResponse(response);
63+
64+
assertThat(wrapped.hasMessageBody()).isFalse();
65+
}
4266

4367
@Test
44-
void messageBodyDoesNotExist() throws Exception {
45-
given(response.getBody()).willReturn(null);
46-
assertThat(wrappedResponse.hasEmptyMessageBody()).isTrue();
68+
void emptyMessageWhenNullInputStream() throws Exception {
69+
ClientHttpResponse mockResponse = mock();
70+
given(mockResponse.getBody()).willReturn(null);
71+
var wrappedMock = new IntrospectingClientHttpResponse(mockResponse);
72+
assertThat(wrappedMock.hasEmptyMessageBody()).isTrue();
4773
}
4874

4975
@Test
5076
void messageBodyExists() throws Exception {
51-
InputStream stream = new ByteArrayInputStream("content".getBytes());
52-
given(response.getBody()).willReturn(stream);
53-
assertThat(wrappedResponse.hasEmptyMessageBody()).isFalse();
77+
var stream = new ByteArrayInputStream("content".getBytes());
78+
var response = new MockClientHttpResponse(stream, HttpStatus.OK);
79+
var wrapped = new IntrospectingClientHttpResponse(response);
80+
assertThat(wrapped.hasEmptyMessageBody()).isFalse();
81+
}
82+
83+
@Test
84+
void emptyMessageWhenEOFException() throws Exception {
85+
ClientHttpResponse mockResponse = mock();
86+
InputStream stream = mock();
87+
given(mockResponse.getBody()).willReturn(stream);
88+
given(stream.read()).willThrow(new EOFException());
89+
var wrappedMock = new IntrospectingClientHttpResponse(mockResponse);
90+
assertThat(wrappedMock.hasEmptyMessageBody()).isTrue();
5491
}
5592

5693
}

0 commit comments

Comments
 (0)