-
Notifications
You must be signed in to change notification settings - Fork 38.7k
Add compression support in JdkClientHttpRequestFactory #35225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add compression support in JdkClientHttpRequestFactory #35225
Conversation
Added new flag to JdkClientHttpRequest and created new DecompressingBodyHandler class to handle HttpClient response body decompression when encoded. Closes spring-projectsgh-35222 Signed-off-by: spicydev <vivek@mirchi.dev>
d20e827
to
3015e29
Compare
Should we also support the "deflate" compression algorithm? Also, I am wondering if we really need an option that enables compression. Should we enable this no matter what? What do you think? |
Hi @bclozel, First off let me start off by apologizing for raising the PR in such bad state. I’ll work on unit tests and follow style guide once we can finalize on the requirements. coming to your questions, Yes we can add support for multiple encoding types but following other clients like apache httpclient & netty, they only support gzip by default out of the box. I think at-least for now we can add support for gzip to JDK http client. I think it’s better to have the control to the users but by default we can enable compression as it causes no harm but improves performance for all users. Let me know your thoughts, based on that i can go ahead with changes. |
@spicydev no worries, we are here to help. I think Apache HttpClient supports both. And so does Netty. |
Added support for deflate encoded response using InflaterInputStream and added Unit Test cases. Signed-off-by: spicydev <vivek@mirchi.dev>
b4e19ea
to
e463d77
Compare
Hi @bclozel , Need your help here. When testing the Inflater with JDK http client I'm seeing error like below |
Good stuff! Suggestion for the future for Spring Maintainers: Make Default Body Handler overridable from the outside, so we can implement workarounds |
@@ -98,7 +110,7 @@ protected ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body | |||
CompletableFuture<HttpResponse<InputStream>> responseFuture = null; | |||
try { | |||
HttpRequest request = buildRequest(headers, body); | |||
responseFuture = this.httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofInputStream()); | |||
responseFuture = this.httpClient.sendAsync(request, new DecompressingBodyHandler()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this Handler a parameter ? This will make Workarounds easier
@@ -43,6 +43,8 @@ public class JdkClientHttpRequestFactory implements ClientHttpRequestFactory { | |||
|
|||
private @Nullable Duration readTimeout; | |||
|
|||
private boolean compressionEnabled; | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add Optional BodyRequestHandler
parameter, defaulting to the new one you just created
@@ -43,6 +43,8 @@ public class JdkClientHttpRequestFactory implements ClientHttpRequestFactory { | |||
|
|||
private @Nullable Duration readTimeout; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Create JdkClientHttpRequestFactoryConfig
with all parameters
|
||
@Override | ||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException { | ||
return new JdkClientHttpRequest(this.httpClient, uri, httpMethod, this.executor, this.readTimeout); | ||
return new JdkClientHttpRequest(this.httpClient, uri, httpMethod, this.executor, this.readTimeout, this.compressionEnabled); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: With above suggestion, inject JdkClientHttpRequestFactoryConfig
else if(request.getTarget().startsWith("/compress/")) { | ||
String encoding = request.getTarget().replace("/compress/",""); | ||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); | ||
if (encoding.equals("gzip")) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use improved switch?
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>
Thanks a lot @spicydev for your first contribution! Tests were initially failing because the test server was writing non-printable characters as a String instead of bytes directly. @driverpt I don't think we should open the API more here. A |
Will this be backported into current Spring Boot 3.5.x?
@bclozel, Actually IMHO you do, given that If you check the call @Override
@SuppressWarnings("NullAway")
protected ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body body) throws IOException {
// ....
HttpRequest request = buildRequest(headers, body);
responseFuture = this.httpClient.sendAsync(request,
new DecompressingBodyHandler() // <--- This part is not customizable
);
// ....
} |
Yes, that's exactly the point 👍 |
Agree, but IMHO you should make if configurable, defaulting to |
Title
Implement custom BodyHandler for GZIP decompression in JdkClientHttpRequest
Description
Problem: JDK HTTP Client doesn't automatically decompress responses.
Solution: creating a custom BodyHandler that checks Content-Encoding and applies GZIPInputStream when necessary.
Key Changes:
compressionEnabled
toJdkClientHttpRequestFactory
andJdkClientHttpRequest
.buildRequest()
method to addAccept-Encoding
header withgzip
value when compression is enabled.DecompressingBodyHandler
that implementsBodyHandler<InputStream>
. The apply() method in the custom handler checks theContent-Encoding
header.gzip
, it uses BodySubscribers.mapping to wrap the InputStream with GZIPInputStream.Otherwise, it uses the default BodySubscribers.ofInputStream().
Testing Steps: