2525import java .net .http .HttpRequest ;
2626import java .net .http .HttpResponse ;
2727import java .net .http .HttpTimeoutException ;
28+ import java .net .http .HttpResponse .BodyHandler ;
29+ import java .net .http .HttpResponse .BodySubscriber ;
30+ import java .net .http .HttpResponse .BodySubscribers ;
31+ import java .net .http .HttpResponse .ResponseInfo ;
2832import java .nio .ByteBuffer ;
2933import java .time .Duration ;
3034import java .util .Collections ;
3741import java .util .concurrent .Executor ;
3842import java .util .concurrent .Flow ;
3943import java .util .concurrent .TimeUnit ;
44+ import java .util .zip .GZIPInputStream ;
4045
4146import org .jspecify .annotations .Nullable ;
4247
@@ -70,15 +75,18 @@ class JdkClientHttpRequest extends AbstractStreamingClientHttpRequest {
7075
7176 private final @ Nullable Duration timeout ;
7277
78+ private final boolean compressionEnabled ;
79+
7380
7481 public JdkClientHttpRequest (HttpClient httpClient , URI uri , HttpMethod method , Executor executor ,
75- @ Nullable Duration readTimeout ) {
82+ @ Nullable Duration readTimeout , boolean compressionEnabled ) {
7683
7784 this .httpClient = httpClient ;
7885 this .uri = uri ;
7986 this .method = method ;
8087 this .executor = executor ;
8188 this .timeout = readTimeout ;
89+ this .compressionEnabled = compressionEnabled ;
8290 }
8391
8492
@@ -98,7 +106,11 @@ protected ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body
98106 CompletableFuture <HttpResponse <InputStream >> responseFuture = null ;
99107 try {
100108 HttpRequest request = buildRequest (headers , body );
101- responseFuture = this .httpClient .sendAsync (request , HttpResponse .BodyHandlers .ofInputStream ());
109+ if (compressionEnabled ) {
110+ responseFuture = this .httpClient .sendAsync (request , new DecompressingBodyHandler ());
111+ } else {
112+ responseFuture = this .httpClient .sendAsync (request , HttpResponse .BodyHandlers .ofInputStream ());
113+ }
102114
103115 if (this .timeout != null ) {
104116 TimeoutHandler timeoutHandler = new TimeoutHandler (responseFuture , this .timeout );
@@ -140,6 +152,10 @@ else if (cause instanceof IOException ioEx) {
140152
141153 private HttpRequest buildRequest (HttpHeaders headers , @ Nullable Body body ) {
142154 HttpRequest .Builder builder = HttpRequest .newBuilder ().uri (this .uri );
155+
156+ if (compressionEnabled ) {
157+ headers .add (HttpHeaders .ACCEPT_ENCODING , "gzip" );
158+ }
143159
144160 headers .forEach ((headerName , headerValues ) -> {
145161 if (!DISALLOWED_HEADERS .contains (headerName .toLowerCase (Locale .ROOT ))) {
@@ -269,4 +285,30 @@ public void close() throws IOException {
269285 }
270286 }
271287
288+ /**
289+ * Custom BodyHandler that checks the Content-Encoding header and applies the appropriate decompression algorithm.
290+ */
291+ public static final class DecompressingBodyHandler implements BodyHandler <InputStream > {
292+
293+ @ Override
294+ public BodySubscriber <InputStream > apply (ResponseInfo responseInfo ) {
295+ String contentEncoding = responseInfo .headers ().firstValue (HttpHeaders .CONTENT_ENCODING ).orElse ("" );
296+ if (contentEncoding .equalsIgnoreCase ("gzip" )) {
297+ // If the content is gzipped, wrap the InputStream with a GZIPInputStream
298+ return BodySubscribers .mapping (
299+ BodySubscribers .ofInputStream (),
300+ (InputStream is ) -> {
301+ try {
302+ return new GZIPInputStream (is );
303+ } catch (IOException e ) {
304+ throw new UncheckedIOException (e ); // Propagate IOExceptions
305+ }
306+ });
307+ } else {
308+ // Otherwise, return a standard InputStream BodySubscriber
309+ return BodySubscribers .ofInputStream ();
310+ }
311+ }
312+ }
313+
272314}
0 commit comments