Fix compressed HEAD requests handling in JDK client

Prior to this commit, the `JdkClientHttpRequestFactory` would support
decompressing gziped/deflate encoded response bodies but would fail if
the response has no body but has a "Content-Encoding" response header.
This happens as a response to HEAD requests.

This commit ensures that only responses with actual message bodies are
decompressed.

Fixes gh-35966
This commit is contained in:
Brian Clozel
2025-12-08 14:50:23 +01:00
parent df27627516
commit 12c3dc0cbe
3 changed files with 77 additions and 18 deletions
@@ -112,7 +112,7 @@ public abstract class AbstractMockWebServerTests {
String headerName = request.getTarget().replace("/header/","");
return new MockResponse.Builder().body(headerName + ":" + request.getHeaders().get(headerName)).code(200).build();
}
else if(request.getTarget().startsWith("/compress/") && request.getBody() != null) {
else if(request.getMethod().equals("POST") && request.getTarget().startsWith("/compress/") && request.getBody() != null) {
String encoding = request.getTarget().replace("/compress/","");
String requestBody = request.getBody().utf8();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
@@ -139,6 +139,13 @@ public abstract class AbstractMockWebServerTests {
builder.setHeader(HttpHeaders.CONTENT_LENGTH, buffer.size());
return builder.build();
}
else if (request.getMethod().equals("HEAD") && request.getTarget().startsWith("/headforcompress/")) {
String encoding = request.getTarget().replace("/headforcompress/","");
MockResponse.Builder builder = new MockResponse.Builder().code(200)
.setHeader(HttpHeaders.CONTENT_LENGTH, 500)
.setHeader(HttpHeaders.CONTENT_ENCODING, encoding);
return builder.build();
}
return new MockResponse.Builder().code(404).build();
}
catch (Throwable ex) {
@@ -26,6 +26,8 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
@@ -159,6 +161,23 @@ class JdkClientHttpRequestFactoryTests extends AbstractHttpRequestFactoryTests {
}
}
@ParameterizedTest
@ValueSource(strings = {"gzip", "deflate"})
void gzipCompressionWithHeadRequest(String compression) throws IOException {
URI uri = URI.create(baseUrl + "/headforcompress/" + compression);
JdkClientHttpRequestFactory requestFactory = (JdkClientHttpRequestFactory) this.factory;
requestFactory.enableCompression(true);
ClientHttpRequest request = requestFactory.createRequest(uri, HttpMethod.HEAD);
try (ClientHttpResponse response = request.execute()) {
assertThat(response.getStatusCode()).as("Invalid response status").isEqualTo(HttpStatus.OK);
assertThat(response.getHeaders().getFirst("Content-Encoding"))
.as("Content Encoding should be removed").isNull();
assertThat(response.getHeaders().getFirst("Content-Length"))
.as("Content-Length should be removed").isNull();
assertThat(response.getBody()).as("Invalid response body").isEmpty();
}
}
@Test // gh-34971
@EnabledForJreRange(min = JRE.JAVA_19) // behavior fixed in Java 19
void requestContentLengthHeaderWhenNoBody() throws Exception {