Skip to content

Commit ebfbd8c

Browse files
committed
[HTTPCLIENT-1843] - Delegate compression handling to Apache Commons Compress
* Integrated Apache Commons Compress into CompressorFactory to handle compression and decompression of HTTP entities using supported algorithms (gzip, deflate, etc.).
1 parent aad0e9a commit ebfbd8c

33 files changed

Lines changed: 1160 additions & 111 deletions

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/StandardTestClientBuilder.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ final class StandardTestClientBuilder implements TestClientBuilder {
5757

5858
private HttpClientConnectionManager connectionManager;
5959

60+
private boolean noWrap;
61+
6062
public StandardTestClientBuilder() {
6163
this.clientBuilder = HttpClientBuilder.create();
6264
}
@@ -72,6 +74,12 @@ public TestClientBuilder setTimeout(final Timeout timeout) {
7274
return this;
7375
}
7476

77+
@Override
78+
public TestClientBuilder setNoWrap(final boolean noWrap) {
79+
this.noWrap = noWrap;
80+
return this;
81+
}
82+
7583
@Override
7684
public TestClientBuilder setConnectionManager(final HttpClientConnectionManager connectionManager) {
7785
this.connectionManager = connectionManager;
@@ -165,6 +173,7 @@ public TestClient build() throws Exception {
165173

166174
final CloseableHttpClient client = clientBuilder
167175
.setConnectionManager(connectionManagerCopy)
176+
.setNoWrap(noWrap)
168177
.build();
169178
return new TestClient(client, connectionManagerCopy);
170179
}

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/TestClientBuilder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ default TestClientBuilder addExecInterceptorLast(String name, ExecChainHandler
9898
throw new UnsupportedOperationException("Operation not supported by " + getProtocolLevel());
9999
}
100100

101+
default TestClientBuilder setNoWrap(boolean noWrap){
102+
return this;
103+
}
104+
101105
TestClient build() throws Exception;
102106

103107
}

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/TestClientResources.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,9 @@ public TestClient client() throws Exception {
118118
return client;
119119
}
120120

121+
public TestClient client(final boolean noWrap) throws Exception {
122+
clientBuilder.setNoWrap(noWrap);
123+
return client();
124+
}
125+
121126
}

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/AbstractIntegrationTestBase.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,8 @@ public TestClient client() throws Exception {
7878
return testResources.client();
7979
}
8080

81+
public TestClient client(final boolean noWrap) throws Exception {
82+
return testResources.client(noWrap);
83+
}
84+
8185
}

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestContentCodings.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ void testDeflateSupportForServerReturningRfc1950Stream() throws Exception {
108108

109109
final HttpHost target = startServer();
110110

111-
final TestClient client = client();
111+
final TestClient client = client(true);
112112

113113
final HttpGet request = new HttpGet("/some-resource");
114114
client.execute(target, request, response -> {
@@ -133,7 +133,7 @@ void testDeflateSupportForServerReturningRfc1951Stream() throws Exception {
133133

134134
final HttpHost target = startServer();
135135

136-
final TestClient client = client();
136+
final TestClient client = client(false);
137137

138138
final HttpGet request = new HttpGet("/some-resource");
139139
client.execute(target, request, response -> {
@@ -289,7 +289,7 @@ void deflateResponsesWorkWithBasicResponseHandler() throws Exception {
289289

290290
final HttpHost target = startServer();
291291

292-
final TestClient client = client();
292+
final TestClient client = client(true);
293293

294294
final HttpGet request = new HttpGet("/some-resource");
295295
final String response = client.execute(target, request, new BasicHttpClientResponseHandler());

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestRedirects.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -639,8 +639,8 @@ public void handle(final ClassicHttpRequest request,
639639
Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/100").build(),
640640
reqWrapper.getUri());
641641

642-
assertThat(values.poll(), CoreMatchers.equalTo("gzip, x-gzip, deflate"));
643-
assertThat(values.poll(), CoreMatchers.equalTo("gzip, x-gzip, deflate"));
642+
assertThat(values.poll(), CoreMatchers.equalTo("snappy-raw, xz, snappy-framed, bzip2, lz4-framed, deflate64, br, lzma, zstd, lz4-block, gz, deflate, z, pack200"));
643+
assertThat(values.poll(), CoreMatchers.equalTo("snappy-raw, xz, snappy-framed, bzip2, lz4-framed, deflate64, br, lzma, zstd, lz4-block, gz, deflate, z, pack200"));
644644
assertThat(values.poll(), CoreMatchers.nullValue());
645645
}
646646

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* ====================================================================
3+
* Licensed to the Apache Software Foundation (ASF) under one
4+
* or more contributor license agreements. See the NOTICE file
5+
* distributed with this work for additional information
6+
* regarding copyright ownership. The ASF licenses this file
7+
* to you under the Apache License, Version 2.0 (the
8+
* "License"); you may not use this file except in compliance
9+
* with the License. You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing,
14+
* software distributed under the License is distributed on an
15+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
* KIND, either express or implied. See the License for the
17+
* specific language governing permissions and limitations
18+
* under the License.
19+
* ====================================================================
20+
*
21+
* This software consists of voluntary contributions made by many
22+
* individuals on behalf of the Apache Software Foundation. For more
23+
* information on the Apache Software Foundation, please see
24+
* <http://www.apache.org/>.
25+
*
26+
*/
27+
28+
package org.apache.hc.client5.testing.sync.compress;
29+
30+
import java.util.Arrays;
31+
import java.util.List;
32+
33+
import org.apache.hc.client5.http.entity.CompressorFactory;
34+
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
35+
import org.apache.hc.client5.http.impl.classic.HttpClients;
36+
import org.apache.hc.core5.http.ClassicHttpRequest;
37+
import org.apache.hc.core5.http.ContentType;
38+
import org.apache.hc.core5.http.HttpEntity;
39+
import org.apache.hc.core5.http.HttpHeaders;
40+
import org.apache.hc.core5.http.HttpHost;
41+
import org.apache.hc.core5.http.io.entity.EntityUtils;
42+
import org.apache.hc.core5.http.io.entity.StringEntity;
43+
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
44+
import org.apache.hc.core5.io.CloseMode;
45+
import org.apache.hc.core5.testing.classic.ClassicTestServer;
46+
47+
48+
/**
49+
* Demonstrates handling of HTTP responses with content compression using Apache HttpClient.
50+
* <p>
51+
* This example sets up a local test server that simulates compressed HTTP responses. It then
52+
* creates a custom HttpClient configured to handle compression. The client makes a request to
53+
* the test server, receives a compressed response, and decompresses the content to verify the
54+
* process.
55+
* <p>
56+
* The main focus of this example is to illustrate the use of a custom HttpClient that can
57+
* handle compressed HTTP responses transparently, simulating a real-world scenario where
58+
* responses from a server might be compressed to reduce bandwidth usage.
59+
*/
60+
public class CompressedResponseHandlingExample {
61+
62+
public static void main(final String[] args) {
63+
64+
final ClassicTestServer server = new ClassicTestServer();
65+
try {
66+
server.register("/compressed", (request, response, context) -> {
67+
final String uncompressedContent = "This is the uncompressed response content";
68+
response.setEntity(compress(uncompressedContent, "gzip"));
69+
response.addHeader(HttpHeaders.CONTENT_ENCODING, "gzip");
70+
});
71+
72+
server.start();
73+
74+
final HttpHost target = new HttpHost("localhost", server.getPort());
75+
76+
final List<String> encodingList = Arrays.asList("gz", "deflate");
77+
78+
try (final CloseableHttpClient httpclient = HttpClients
79+
.custom()
80+
.setEncodings(encodingList)
81+
.build()) {
82+
final ClassicHttpRequest httpGet = ClassicRequestBuilder.get()
83+
.setHttpHost(target)
84+
.setPath("/compressed")
85+
.build();
86+
87+
System.out.println("Executing request " + httpGet.getMethod() + " " + httpGet.getUri());
88+
httpclient.execute(httpGet, response -> {
89+
System.out.println("----------------------------------------");
90+
System.out.println(httpGet + "->" + response.getCode() + " " + response.getReasonPhrase());
91+
92+
final HttpEntity responseEntity = response.getEntity();
93+
final String responseBody = EntityUtils.toString(responseEntity);
94+
System.out.println("Response content: " + responseBody);
95+
96+
return null;
97+
});
98+
}
99+
100+
} catch (final Exception e) {
101+
e.printStackTrace();
102+
} finally {
103+
server.shutdown(CloseMode.GRACEFUL);
104+
}
105+
}
106+
107+
108+
private static HttpEntity compress(final String data, final String name) {
109+
final StringEntity originalEntity = new StringEntity(data, ContentType.TEXT_PLAIN);
110+
return CompressorFactory.INSTANCE.compressEntity(originalEntity, name);
111+
}
112+
113+
}

httpclient5/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@
9797
<artifactId>mockito-core</artifactId>
9898
<scope>test</scope>
9999
</dependency>
100+
<dependency>
101+
<groupId>org.apache.commons</groupId>
102+
<artifactId>commons-compress</artifactId>
103+
</dependency>
100104
</dependencies>
101105

102106
<build>

httpclient5/src/main/java/org/apache/hc/client5/http/entity/BrotliDecompressingEntity.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
*
3535
* @see GzipDecompressingEntity
3636
* @since 5.2
37+
* @deprecated Use {@link CompressorFactory} for handling Brotli decompression.
3738
*/
39+
@Deprecated
3840
public class BrotliDecompressingEntity extends DecompressingEntity {
3941
/**
4042
* Creates a new {@link DecompressingEntity}.

httpclient5/src/main/java/org/apache/hc/client5/http/entity/BrotliInputStreamFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@
3737
* {@link InputStreamFactory} for handling Brotli Content Coded responses.
3838
*
3939
* @since 5.2
40+
* @deprecated Use {@link CompressorFactory} for handling Brotli compression.
4041
*/
42+
@Deprecated
4143
@Contract(threading = ThreadingBehavior.STATELESS)
4244
public class BrotliInputStreamFactory implements InputStreamFactory {
4345

0 commit comments

Comments
 (0)