Add compression support in JdkClientHttpRequestFactory

This commit ensures that the "Accept-Encoding" header is present for HTTP
requests sent by the `JdkClientHttpRequestFactory`. Only "gzip" and
"deflate" encodings are supported.

This also adds a custom `BodyHandler` that decompresses HTTP response
bodies if the "Content-Encoding" header lists a supported variant.

This feature is enabled by default and can be disabled on the request
factory.

See gh-35225

Signed-off-by: spicydev <vivek@mirchi.dev>
[brian.clozel@broadcom.com: squash commits]
Signed-off-by: Brian Clozel <brian.clozel@broadcom.com>
This commit is contained in:
spicydev
2025-07-20 12:05:03 -04:00
committed by Brian Clozel
parent fdfd15b9b3
commit 4101714830
4 changed files with 134 additions and 4 deletions
@@ -23,8 +23,14 @@ import mockwebserver3.RecordedRequest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.http.HttpHeaders;
import org.springframework.util.StringUtils;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPOutputStream;
import static org.assertj.core.api.Assertions.assertThat;
/**
@@ -106,6 +112,26 @@ 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/")) {
String encoding = request.getTarget().replace("/compress/","");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
if (encoding.equals("gzip")) {
try(GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream)) {
gzipOutputStream.write("Test Payload".getBytes());
gzipOutputStream.flush();
}
}
else if(encoding.equals("deflate")) {
try(DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream)) {
deflaterOutputStream.write("Test Payload".getBytes());
deflaterOutputStream.flush();
}
} else {
byteArrayOutputStream.write("Test Payload".getBytes());
}
return new MockResponse.Builder().body(byteArrayOutputStream.toString(StandardCharsets.ISO_8859_1))
.code(200).setHeader(HttpHeaders.CONTENT_ENCODING, encoding).build();
}
return new MockResponse.Builder().code(404).build();
}
catch (Throwable ex) {
@@ -108,6 +108,44 @@ class JdkClientHttpRequestFactoryTests extends AbstractHttpRequestFactoryTests {
}
}
@Test
void compressionDisabled() throws IOException {
URI uri = URI.create(baseUrl + "/compress/");
ClientHttpRequest request = this.factory.createRequest(uri, HttpMethod.GET);
try (ClientHttpResponse response = request.execute()) {
assertThat(response.getStatusCode()).as("Invalid response status").isEqualTo(HttpStatus.OK);
assertThat(StreamUtils.copyToString(response.getBody(), StandardCharsets.ISO_8859_1))
.as("Invalid request body").isEqualTo("Test Payload");
}
}
@Test
void compressionGzip() throws IOException {
URI uri = URI.create(baseUrl + "/compress/gzip");
JdkClientHttpRequestFactory requestFactory = (JdkClientHttpRequestFactory) this.factory;
requestFactory.setCompressionEnabled(true);
ClientHttpRequest request = requestFactory.createRequest(uri, HttpMethod.GET);
try (ClientHttpResponse response = request.execute()) {
assertThat(response.getStatusCode()).as("Invalid response status").isEqualTo(HttpStatus.OK);
assertThat(StreamUtils.copyToString(response.getBody(), StandardCharsets.ISO_8859_1))
.as("Invalid request body").isEqualTo("Test Payload");
}
}
@Test
void compressionDeflate() throws IOException {
URI uri = URI.create(baseUrl + "/compress/deflate");
JdkClientHttpRequestFactory requestFactory = (JdkClientHttpRequestFactory) this.factory;
requestFactory.setCompressionEnabled(true);
ClientHttpRequest request = requestFactory.createRequest(uri, HttpMethod.GET);
try (ClientHttpResponse response = request.execute()) {
assertThat(response.getStatusCode()).as("Invalid response status").isEqualTo(HttpStatus.OK);
assertThat(StreamUtils.copyToString(response.getBody(), StandardCharsets.ISO_8859_1))
.as("Invalid request body").isEqualTo("Test Payload");
}
}
@Test // gh-34971
@EnabledForJreRange(min = JRE.JAVA_19) // behavior fixed in Java 19
void requestContentLengthHeaderWhenNoBody() throws Exception {