Skip to content
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

[HTTPCLIENT-1843] - Delegate compression handling to Apache Commons Compress #580

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ final class StandardTestClientBuilder implements TestClientBuilder {

private HttpClientConnectionManager connectionManager;

private boolean noWrap;

public StandardTestClientBuilder() {
this.clientBuilder = HttpClientBuilder.create();
}
Expand All @@ -72,6 +74,12 @@ public TestClientBuilder setTimeout(final Timeout timeout) {
return this;
}

@Override
public TestClientBuilder setNoWrap(final boolean noWrap) {
this.noWrap = noWrap;
return this;
}

@Override
public TestClientBuilder setConnectionManager(final HttpClientConnectionManager connectionManager) {
this.connectionManager = connectionManager;
Expand Down Expand Up @@ -165,6 +173,7 @@ public TestClient build() throws Exception {

final CloseableHttpClient client = clientBuilder
.setConnectionManager(connectionManagerCopy)
.setNoWrap(noWrap)
.build();
return new TestClient(client, connectionManagerCopy);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ default TestClientBuilder addExecInterceptorLast(String name, ExecChainHandler i
throw new UnsupportedOperationException("Operation not supported by " + getProtocolLevel());
}

/**
* Configures whether the client builder should wrap requests and responses.
*
* @param noWrap {@code true} to disable wrapping; {@code false} to enable wrapping.
* @return this builder instance.
* @since 5.5
*/
default TestClientBuilder setNoWrap(boolean noWrap) {
return this;
}

TestClient build() throws Exception;

}
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,17 @@ public TestClient client() throws Exception {
return client;
}

/**
* Creates a configured {@link TestClient} instance.
*
* @param noWrap {@code true} to disable wrapping; {@code false} to enable wrapping.
* @return a {@link TestClient} instance.
* @throws Exception if an error occurs during client creation.
* @since 5.5
*/
public TestClient client(final boolean noWrap) throws Exception {
clientBuilder.setNoWrap(noWrap);
return client();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,16 @@ public TestClient client() throws Exception {
return testResources.client();
}

/**
* Retrieves a {@link TestClient} instance configured with the specified wrapping option.
*
* @param noWrap {@code true} to disable wrapping; {@code false} to enable wrapping.
* @return a {@link TestClient} instance.
* @throws Exception if an error occurs during client retrieval or configuration.
* @since 5.5
*/
public TestClient client(final boolean noWrap) throws Exception {
return testResources.client(noWrap);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ void testDeflateSupportForServerReturningRfc1950Stream() throws Exception {

final HttpHost target = startServer();

final TestClient client = client();
final TestClient client = client(true);

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

final HttpHost target = startServer();

final TestClient client = client();
final TestClient client = client(false);

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

final HttpHost target = startServer();

final TestClient client = client();
final TestClient client = client(true);

final HttpGet request = new HttpGet("/some-resource");
final String response = client.execute(target, request, new BasicHttpClientResponseHandler());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -639,8 +639,8 @@ public void handle(final ClassicHttpRequest request,
Assertions.assertEquals(new URIBuilder().setHttpHost(target).setPath("/random/100").build(),
reqWrapper.getUri());

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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/

package org.apache.hc.client5.testing.sync.compress;

import java.util.Arrays;
import java.util.List;

import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.testing.classic.ClassicTestServer;

/**
* Demonstrates handling of HTTP responses with content compression using Apache HttpClient.
* <p>
* This example sets up a local test server that simulates compressed HTTP responses. It then
* creates a custom HttpClient configured to handle compression. The client makes a request to
* the test server, receives a compressed response, and decompresses the content to verify the
* process.
* <p>
* The main focus of this example is to illustrate the use of a custom HttpClient that can
* handle compressed HTTP responses transparently, simulating a real-world scenario where
* responses from a server might be compressed to reduce bandwidth usage.
*/
public class CompressedResponseHandlingExample {

public static void main(final String[] args) {

final ClassicTestServer server = new ClassicTestServer();
try {
server.register("/compressed", (request, response, context) -> {
final String uncompressedContent = "This is the uncompressed response content";
response.setEntity(compress(uncompressedContent, "gzip"));
response.addHeader(HttpHeaders.CONTENT_ENCODING, "gzip");
});

server.start();

final HttpHost target = new HttpHost("localhost", server.getPort());

final List<String> encodingList = Arrays.asList("gz", "deflate");

try (final CloseableHttpClient httpclient = HttpClients
.custom()
.setEncodings(encodingList)
.build()) {
final ClassicHttpRequest httpGet = ClassicRequestBuilder.get()
.setHttpHost(target)
.setPath("/compressed")
.build();

System.out.println("Executing request " + httpGet.getMethod() + " " + httpGet.getUri());
httpclient.execute(httpGet, response -> {
System.out.println("----------------------------------------");
System.out.println(httpGet + "->" + response.getCode() + " " + response.getReasonPhrase());

final HttpEntity responseEntity = response.getEntity();
final String responseBody = EntityUtils.toString(responseEntity);
System.out.println("Response content: " + responseBody);

return null;
});
}

} catch (final Exception e) {
e.printStackTrace();
} finally {
server.shutdown(CloseMode.GRACEFUL);
}
}


private static HttpEntity compress(final String data, final String name) {
final StringEntity originalEntity = new StringEntity(data, ContentType.TEXT_PLAIN);
return CompressingFactory.INSTANCE.compressEntity(originalEntity, name);
}

}
4 changes: 4 additions & 0 deletions httpclient5/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
*/
package org.apache.hc.client5.http.entity;

import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.core5.http.HttpEntity;

/**
Expand All @@ -34,7 +35,9 @@
*
* @see GzipDecompressingEntity
* @since 5.2
* @deprecated Use {@link CompressingFactory} for handling Brotli decompression.
*/
@Deprecated
public class BrotliDecompressingEntity extends DecompressingEntity {
/**
* Creates a new {@link DecompressingEntity}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.io.IOException;
import java.io.InputStream;

import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.brotli.dec.BrotliInputStream;
Expand All @@ -37,7 +38,9 @@
* {@link InputStreamFactory} for handling Brotli Content Coded responses.
*
* @since 5.2
* @deprecated Use {@link CompressingFactory} for handling Brotli compression.
*/
@Deprecated
@Contract(threading = ThreadingBehavior.STATELESS)
public class BrotliInputStreamFactory implements InputStreamFactory {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
* Common base class for decompressing {@link HttpEntity} implementations.
*
* @since 4.4
* @deprecated Use {{@link org.apache.hc.client5.http.entity.compress.DecompressingEntity}
*/
@Deprecated
public class DecompressingEntity extends HttpEntityWrapper {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
*/
package org.apache.hc.client5.http.entity;

import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.client5.http.entity.compress.DecompressingEntity;
import org.apache.hc.core5.http.HttpEntity;

/**
Expand All @@ -43,8 +45,10 @@
* @see GzipDecompressingEntity
*
* @since 4.1
* @deprecated Use {@link DecompressingEntity} or {@link CompressingFactory} for decompression handling.
*/
public class DeflateDecompressingEntity extends DecompressingEntity {
@Deprecated
public class DeflateDecompressingEntity extends org.apache.hc.client5.http.entity.DecompressingEntity {

/**
* Creates a new {@link DeflateDecompressingEntity} which will wrap the specified
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,14 @@
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipException;

import org.apache.hc.client5.http.entity.compress.CompressingFactory;

/**
* Deflates an input stream. This class includes logic needed for various RFCs in order
* to reasonably implement the "deflate" compression algorithm.
* @deprecated Use {@link CompressingFactory} for handling Deflate compression.
*/
@Deprecated
public class DeflateInputStream extends FilterInputStream {

public DeflateInputStream(final InputStream wrapped) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,17 @@
import java.io.IOException;
import java.io.InputStream;

import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;

/**
* {@link InputStreamFactory} for handling Deflate Content Coded responses.
*
* @since 5.0
* @deprecated Use {@link CompressingFactory}.
*/
@Deprecated
@Contract(threading = ThreadingBehavior.STATELESS)
public class DeflateInputStreamFactory implements InputStreamFactory {

Expand Down
Loading