Refactor compression entity class names for consistency. Renamed to use 'ing' form (e.g., CompressingEntity) to align with existing conventions like DecompressingEntity. Moved compression/decompression classes to org.apache.hc.client5.http.entity.compress package

This commit is contained in:
Arturo Bernal 2024-10-21 21:08:39 +02:00
parent 5b78afd95d
commit 363c7cae5f
29 changed files with 226 additions and 151 deletions

View File

@ -98,6 +98,13 @@ public interface TestClientBuilder {
throw new UnsupportedOperationException("Operation not supported by " + getProtocolLevel()); 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) { default TestClientBuilder setNoWrap(boolean noWrap) {
return this; return this;
} }

View File

@ -111,6 +111,14 @@ public class TestClientResources implements AfterEachCallback {
return client; 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 { public TestClient client(final boolean noWrap) throws Exception {
clientBuilder.setNoWrap(noWrap); clientBuilder.setNoWrap(noWrap);
return client(); return client();

View File

@ -78,6 +78,14 @@ abstract class AbstractIntegrationTestBase {
return testResources.client(); 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 { public TestClient client(final boolean noWrap) throws Exception {
return testResources.client(noWrap); return testResources.client(noWrap);
} }

View File

@ -30,7 +30,7 @@ package org.apache.hc.client5.testing.sync.compress;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.apache.hc.client5.http.entity.CompressorFactory; 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.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpRequest;
@ -106,7 +106,7 @@ public class CompressedResponseHandlingExample {
private static HttpEntity compress(final String data, final String name) { private static HttpEntity compress(final String data, final String name) {
final StringEntity originalEntity = new StringEntity(data, ContentType.TEXT_PLAIN); final StringEntity originalEntity = new StringEntity(data, ContentType.TEXT_PLAIN);
return CompressorFactory.INSTANCE.compressEntity(originalEntity, name); return CompressingFactory.INSTANCE.compressEntity(originalEntity, name);
} }
} }

View File

@ -26,6 +26,7 @@
*/ */
package org.apache.hc.client5.http.entity; package org.apache.hc.client5.http.entity;
import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpEntity;
/** /**
@ -34,7 +35,7 @@ import org.apache.hc.core5.http.HttpEntity;
* *
* @see GzipDecompressingEntity * @see GzipDecompressingEntity
* @since 5.2 * @since 5.2
* @deprecated Use {@link CompressorFactory} for handling Brotli decompression. * @deprecated Use {@link CompressingFactory} for handling Brotli decompression.
*/ */
@Deprecated @Deprecated
public class BrotliDecompressingEntity extends DecompressingEntity { public class BrotliDecompressingEntity extends DecompressingEntity {

View File

@ -29,6 +29,7 @@ package org.apache.hc.client5.http.entity;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; 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.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.brotli.dec.BrotliInputStream; import org.brotli.dec.BrotliInputStream;
@ -37,7 +38,7 @@ import org.brotli.dec.BrotliInputStream;
* {@link InputStreamFactory} for handling Brotli Content Coded responses. * {@link InputStreamFactory} for handling Brotli Content Coded responses.
* *
* @since 5.2 * @since 5.2
* @deprecated Use {@link CompressorFactory} for handling Brotli compression. * @deprecated Use {@link CompressingFactory} for handling Brotli compression.
*/ */
@Deprecated @Deprecated
@Contract(threading = ThreadingBehavior.STATELESS) @Contract(threading = ThreadingBehavior.STATELESS)

View File

@ -38,7 +38,7 @@ import org.apache.hc.core5.util.Args;
* Common base class for decompressing {@link HttpEntity} implementations. * Common base class for decompressing {@link HttpEntity} implementations.
* *
* @since 4.4 * @since 4.4
* @deprecated * @deprecated Use {{@link org.apache.hc.client5.http.entity.compress.DecompressingEntity}
*/ */
@Deprecated @Deprecated
public class DecompressingEntity extends HttpEntityWrapper { public class DecompressingEntity extends HttpEntityWrapper {

View File

@ -26,6 +26,8 @@
*/ */
package org.apache.hc.client5.http.entity; 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; import org.apache.hc.core5.http.HttpEntity;
/** /**
@ -43,10 +45,10 @@ import org.apache.hc.core5.http.HttpEntity;
* @see GzipDecompressingEntity * @see GzipDecompressingEntity
* *
* @since 4.1 * @since 4.1
* @deprecated Use {@link DecompressEntity} or {@link CompressorFactory} for decompression handling. * @deprecated Use {@link DecompressingEntity} or {@link CompressingFactory} for decompression handling.
*/ */
@Deprecated @Deprecated
public class DeflateDecompressingEntity extends DecompressingEntity { public class DeflateDecompressingEntity extends org.apache.hc.client5.http.entity.DecompressingEntity {
/** /**
* Creates a new {@link DeflateDecompressingEntity} which will wrap the specified * Creates a new {@link DeflateDecompressingEntity} which will wrap the specified

View File

@ -35,10 +35,12 @@ import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream; import java.util.zip.InflaterInputStream;
import java.util.zip.ZipException; 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 * Deflates an input stream. This class includes logic needed for various RFCs in order
* to reasonably implement the "deflate" compression algorithm. * to reasonably implement the "deflate" compression algorithm.
* @deprecated Use {@link CompressorFactory} for handling Deflate compression. * @deprecated Use {@link CompressingFactory} for handling Deflate compression.
*/ */
@Deprecated @Deprecated
public class DeflateInputStream extends FilterInputStream { public class DeflateInputStream extends FilterInputStream {

View File

@ -30,6 +30,7 @@ package org.apache.hc.client5.http.entity;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; 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.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.annotation.ThreadingBehavior;
@ -37,7 +38,7 @@ import org.apache.hc.core5.annotation.ThreadingBehavior;
* {@link InputStreamFactory} for handling Deflate Content Coded responses. * {@link InputStreamFactory} for handling Deflate Content Coded responses.
* *
* @since 5.0 * @since 5.0
* @deprecated Use {@link CompressorFactory}. * @deprecated Use {@link CompressingFactory}.
*/ */
@Deprecated @Deprecated
@Contract(threading = ThreadingBehavior.STATELESS) @Contract(threading = ThreadingBehavior.STATELESS)

View File

@ -33,6 +33,8 @@ import java.io.Serializable;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.client5.http.entity.compress.DecompressingEntity;
import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.NameValuePair;
@ -328,7 +330,7 @@ public class EntityBuilder {
* Tests if the entity is to be compressed ({@code true}), or not ({@code false}). * Tests if the entity is to be compressed ({@code true}), or not ({@code false}).
* *
* @return {@code true} if entity is to be compressed, {@code false} otherwise. * @return {@code true} if entity is to be compressed, {@code false} otherwise.
* @since 5.4 * @since 5.5
*/ */
public boolean isCompressed() { public boolean isCompressed() {
return compressed; return compressed;
@ -339,7 +341,7 @@ public class EntityBuilder {
* *
* @param compressed {@code true} if the entity should be compressed, {@code false} otherwise. * @param compressed {@code true} if the entity should be compressed, {@code false} otherwise.
* @return this instance. * @return this instance.
* @since 5.4 * @since 5.5
*/ */
public EntityBuilder setCompressed(final boolean compressed) { public EntityBuilder setCompressed(final boolean compressed) {
this.compressed = compressed; this.compressed = compressed;
@ -406,7 +408,7 @@ public class EntityBuilder {
throw new IllegalStateException("No entity set"); throw new IllegalStateException("No entity set");
} }
if (this.compressed) { if (this.compressed) {
return new DecompressEntity(e, CompressorFactory.INSTANCE.getFormattedName(contentEncoding)); return new DecompressingEntity(e, CompressingFactory.INSTANCE.getFormattedName(contentEncoding));
} }
return e; return e;
} }

View File

@ -31,6 +31,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.annotation.ThreadingBehavior;
@ -38,7 +39,7 @@ import org.apache.hc.core5.annotation.ThreadingBehavior;
* {@link InputStreamFactory} for handling GZIPContent Coded responses. * {@link InputStreamFactory} for handling GZIPContent Coded responses.
* *
* @since 5.0 * @since 5.0
* @deprecated Use {@link CompressorFactory#getCompressorInputStream(String, InputStream, boolean)} instead. * @deprecated Use {@link CompressingFactory#getDecompressorInputStream(String, InputStream, boolean)} instead.
*/ */
@Deprecated @Deprecated
@Contract(threading = ThreadingBehavior.STATELESS) @Contract(threading = ThreadingBehavior.STATELESS)

View File

@ -31,6 +31,7 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.HttpEntityWrapper; import org.apache.hc.core5.http.io.entity.HttpEntityWrapper;
import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Args;
@ -40,7 +41,7 @@ import org.apache.hc.core5.util.Args;
* *
* *
* @since 4.0 * @since 4.0
* @deprecated Use {@link CompressorFactory#compressEntity(HttpEntity, String)} to handle compression. * @deprecated Use {@link CompressingFactory#compressEntity(HttpEntity, String)} to handle compression.
*/ */
@Deprecated @Deprecated
public class GzipCompressingEntity extends HttpEntityWrapper { public class GzipCompressingEntity extends HttpEntityWrapper {

View File

@ -26,6 +26,7 @@
*/ */
package org.apache.hc.client5.http.entity; package org.apache.hc.client5.http.entity;
import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpEntity;
/** /**
@ -33,7 +34,7 @@ import org.apache.hc.core5.http.HttpEntity;
* gzip Content Coded responses. * gzip Content Coded responses.
* *
* @since 4.1 * @since 4.1
* @deprecated Use {@link CompressorFactory} for handling Gzip decompression. * @deprecated Use {@link CompressingFactory} for handling Gzip decompression.
*/ */
@Deprecated @Deprecated
public class GzipDecompressingEntity extends DecompressingEntity { public class GzipDecompressingEntity extends DecompressingEntity {

View File

@ -29,11 +29,13 @@ package org.apache.hc.client5.http.entity;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import org.apache.hc.client5.http.entity.compress.CompressingFactory;
/** /**
* Factory for decorated {@link InputStream}s. * Factory for decorated {@link InputStream}s.
* *
* @since 4.4 * @since 4.4
* @deprecated Use {@link CompressorFactory} to retrieve appropriate {@link InputStream}s for compression handling. * @deprecated Use {@link CompressingFactory} to retrieve appropriate {@link InputStream}s for compression handling.
*/ */
@Deprecated @Deprecated
public interface InputStreamFactory { public interface InputStreamFactory {

View File

@ -35,7 +35,7 @@ import org.apache.hc.core5.io.Closer;
/** /**
* Lazy initializes from an {@link InputStream} wrapper. * Lazy initializes from an {@link InputStream} wrapper.
* @deprecated Use {@link LazyDecompressInputStream} * @deprecated Use {@link org.apache.hc.client5.http.entity.compress.LazyDecompressingInputStream}
*/ */
@Deprecated @Deprecated
class LazyDecompressingInputStream extends FilterInputStream { class LazyDecompressingInputStream extends FilterInputStream {

View File

@ -24,7 +24,7 @@
* <http://www.apache.org/>. * <http://www.apache.org/>.
* *
*/ */
package org.apache.hc.client5.http.entity; package org.apache.hc.client5.http.entity.compress;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -40,7 +40,7 @@ import org.apache.hc.core5.util.Args;
* an output stream. This class supports various compression algorithms based on the * an output stream. This class supports various compression algorithms based on the
* specified content encoding. * specified content encoding.
* *
* <p>Compression is performed using {@link CompressorFactory}, which returns a corresponding * <p>Compression is performed using {@link CompressingFactory}, which returns a corresponding
* {@link OutputStream} for the requested compression type. This class does not support * {@link OutputStream} for the requested compression type. This class does not support
* reading the content directly through {@link #getContent()} as the content is always compressed * reading the content directly through {@link #getContent()} as the content is always compressed
* during write operations.</p> * during write operations.</p>
@ -108,20 +108,17 @@ public class CompressingEntity extends HttpEntityWrapper {
@Override @Override
public void writeTo(final OutputStream outStream) throws IOException { public void writeTo(final OutputStream outStream) throws IOException {
Args.notNull(outStream, "Output stream"); Args.notNull(outStream, "Output stream");
// Get the compressor based on the specified content encoding
final OutputStream compressorStream; try (final OutputStream compressorStream = CompressingFactory.INSTANCE.getCompressorOutputStream(contentEncoding, outStream)) {
try { if (compressorStream != null) {
compressorStream = CompressorFactory.INSTANCE.getCompressorOutputStream(contentEncoding, outStream); // Write compressed data
super.writeTo(compressorStream);
} else {
throw new UnsupportedOperationException("Unsupported compression: " + contentEncoding);
}
} catch (final CompressorException e) { } catch (final CompressorException e) {
throw new IOException("Error initializing decompression stream", e); throw new IOException("Error initializing compression stream", e);
}
if (compressorStream != null) {
// Write compressed data
super.writeTo(compressorStream);
// Close the compressor stream after writing
compressorStream.close();
} else {
throw new UnsupportedOperationException("Unsupported compression: " + contentEncoding);
} }
} }
} }

View File

@ -25,7 +25,7 @@
* *
*/ */
package org.apache.hc.client5.http.entity; package org.apache.hc.client5.http.entity.compress;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -42,8 +42,6 @@ import org.apache.commons.compress.compressors.deflate.DeflateCompressorInputStr
import org.apache.commons.compress.compressors.deflate.DeflateParameters; import org.apache.commons.compress.compressors.deflate.DeflateParameters;
import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.util.Args; import org.apache.hc.core5.util.Args;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* A factory class for managing compression and decompression of HTTP entities using different compression formats. * A factory class for managing compression and decompression of HTTP entities using different compression formats.
@ -64,13 +62,12 @@ import org.slf4j.LoggerFactory;
* *
* @since 5.5 * @since 5.5
*/ */
public class CompressorFactory { public class CompressingFactory {
private static final Logger LOG = LoggerFactory.getLogger(CompressorFactory.class);
/** /**
* Singleton instance of the factory. * Singleton instance of the factory.
*/ */
public static final CompressorFactory INSTANCE = new CompressorFactory(); public static final CompressingFactory INSTANCE = new CompressingFactory();
private final CompressorStreamFactory compressorStreamFactory = new CompressorStreamFactory(); private final CompressorStreamFactory compressorStreamFactory = new CompressorStreamFactory();
private final AtomicReference<Set<String>> inputProvidersCache = new AtomicReference<>(); private final AtomicReference<Set<String>> inputProvidersCache = new AtomicReference<>();
@ -83,7 +80,15 @@ public class CompressorFactory {
* @return a set of available input stream compression providers in lowercase. * @return a set of available input stream compression providers in lowercase.
*/ */
public Set<String> getAvailableInputProviders() { public Set<String> getAvailableInputProviders() {
return inputProvidersCache.updateAndGet(existing -> existing != null ? existing : fetchAvailableInputProviders()); return inputProvidersCache.updateAndGet(existing -> {
if (existing != null) {
return existing;
}
final Set<String> inputNames = compressorStreamFactory.getInputStreamCompressorNames();
return inputNames.stream()
.map(name -> name.toLowerCase(Locale.ROOT))
.collect(Collectors.toSet());
});
} }
/** /**
@ -92,24 +97,31 @@ public class CompressorFactory {
* @return a set of available output stream compression providers in lowercase. * @return a set of available output stream compression providers in lowercase.
*/ */
public Set<String> getAvailableOutputProviders() { public Set<String> getAvailableOutputProviders() {
return outputProvidersCache.updateAndGet(existing -> existing != null ? existing : fetchAvailableOutputProviders()); return outputProvidersCache.updateAndGet(existing -> {
if (existing != null) {
return existing;
}
final Set<String> outputNames = compressorStreamFactory.getOutputStreamCompressorNames();
return outputNames.stream()
.map(name -> name.toLowerCase(Locale.ROOT))
.collect(Collectors.toSet());
});
} }
/** /**
* Returns the formatted name of the provided compression format. * Maps a provided compression format name or alias to a standard internal key.
* <p> * <p>
* If the provided name matches an alias (e.g., "gzip" or "x-gzip"), the method will return the standard name. * If the provided name matches a known alias (e.g., "gzip" or "x-gzip"),
* the method returns the corresponding standard key (e.g., "gz").
* If no match is found, it returns the original name as-is.
* </p> * </p>
* *
* @param name the compression format name. * @param name the compression format name or alias.
* @return the formatted name, or the original name if no alias is found. * @return the corresponding standard key or the original name if no alias is found.
* @throws IllegalArgumentException if the name is null or empty. * @throws IllegalArgumentException if the name is null or empty.
*/ */
public String getFormattedName(final String name) { public String getFormattedName(final String name) {
if (name == null || name.isEmpty()) { Args.notEmpty(name, "name");
LOG.warn("Compression name is null or empty");
return null;
}
final String lowerCaseName = name.toLowerCase(Locale.ROOT); final String lowerCaseName = name.toLowerCase(Locale.ROOT);
return formattedNameCache.computeIfAbsent(lowerCaseName, key -> { return formattedNameCache.computeIfAbsent(lowerCaseName, key -> {
if ("gzip".equals(key) || "x-gzip".equals(key)) { if ("gzip".equals(key) || "x-gzip".equals(key)) {
@ -122,32 +134,36 @@ public class CompressorFactory {
} }
/** /**
* Creates an input stream for the specified compression format and decompresses the provided input stream. * Creates a decompressor input stream for the specified format type and decompresses the provided input stream.
* <p> * <p>
* This method uses the specified compression name to decompress the input stream and supports the "noWrap" option * If the format type is supported, this method returns a new input stream that decompresses data from the original input stream.
* for deflate streams. * For "deflate" format, the "noWrap" option controls the inclusion of zlib headers:
* - If {@code noWrap} is {@code true}, zlib headers and trailers are omitted, resulting in raw deflate data.
* - If {@code noWrap} is {@code false}, the deflate stream includes standard zlib headers.
* </p> * </p>
* *
* @param name the compression format. * @param name the format type to use for decompression.
* @param inputStream the input stream to decompress. * @param inputStream the input stream to be decompressed.
* @param noWrap if true, disables the zlib header and trailer for deflate streams. * @param noWrap whether to exclude zlib headers and trailers for deflate streams (applicable to "deflate" only).
* @return the decompressed input stream, or the original input stream if the format is not supported. * @return a decompressed input stream if the format type is supported; otherwise, the original input stream.
* @throws CompressorException if an error occurs while creating the decompressor input stream.
*/ */
public InputStream getCompressorInputStream(final String name, final InputStream inputStream, final boolean noWrap) throws CompressorException { public InputStream getDecompressorInputStream(final String name, final InputStream inputStream, final boolean noWrap) throws CompressorException {
Args.notNull(inputStream, "InputStream"); Args.notNull(inputStream, "InputStream");
Args.notNull(name, "name"); Args.notNull(name, "name");
final String formattedName = getFormattedName(name); final String formattedName = getFormattedName(name);
return isSupported(formattedName, false) return isSupported(formattedName, false)
? createCompressorInputStream(formattedName, inputStream, noWrap) ? createDecompressorInputStream(formattedName, inputStream, noWrap)
: inputStream; : inputStream;
} }
/** /**
* Creates an output stream for the specified compression format and compresses the provided output stream. * Creates an output stream to compress the provided output stream based on the specified format type.
* *
* @param name the compression format. * @param name the format type.
* @param outputStream the output stream to compress. * @param outputStream the output stream to compress.
* @return the compressed output stream, or the original output stream if the format is not supported. * @return the compressed output stream, or the original output stream if the format is not supported.
* @throws CompressorException if an error occurs while creating the compressor output stream.
*/ */
public OutputStream getCompressorOutputStream(final String name, final OutputStream outputStream) throws CompressorException { public OutputStream getCompressorOutputStream(final String name, final OutputStream outputStream) throws CompressorException {
final String formattedName = getFormattedName(name); final String formattedName = getFormattedName(name);
@ -157,11 +173,11 @@ public class CompressorFactory {
} }
/** /**
* Decompresses the provided HTTP entity using the specified compression format. * Decompresses the provided HTTP entity based on the specified format type.
* *
* @param entity the HTTP entity to decompress. * @param entity the HTTP entity to decompress.
* @param contentEncoding the compression format. * @param contentEncoding the format type.
* @return a decompressed {@link HttpEntity}, or {@code null} if the compression format is unsupported. * @return a decompressed {@link HttpEntity}, or {@code null} if the format type is unsupported.
*/ */
public HttpEntity decompressEntity(final HttpEntity entity, final String contentEncoding) { public HttpEntity decompressEntity(final HttpEntity entity, final String contentEncoding) {
return decompressEntity(entity, contentEncoding, false); return decompressEntity(entity, contentEncoding, false);
@ -179,69 +195,43 @@ public class CompressorFactory {
Args.notNull(entity, "Entity"); Args.notNull(entity, "Entity");
Args.notNull(contentEncoding, "Content Encoding"); Args.notNull(contentEncoding, "Content Encoding");
if (!isSupported(contentEncoding, false)) { if (!isSupported(contentEncoding, false)) {
LOG.warn("Unsupported decompression type: {}", contentEncoding);
return null; return null;
} }
return new DecompressEntity(entity, contentEncoding, noWrap); return new DecompressingEntity(entity, contentEncoding, noWrap);
} }
/** /**
* Compresses the provided HTTP entity using the specified compression format. * Compresses the provided HTTP entity based on the specified format type.
* *
* @param entity the HTTP entity to compress. * @param entity the HTTP entity to compress.
* @param contentEncoding the compression format. * @param contentEncoding the format type.
* @return a compressed {@link HttpEntity}, or {@code null} if the compression format is unsupported. * @return a compressed {@link HttpEntity}, or {@code null} if the format type is unsupported.
*/ */
public HttpEntity compressEntity(final HttpEntity entity, final String contentEncoding) { public HttpEntity compressEntity(final HttpEntity entity, final String contentEncoding) {
Args.notNull(entity, "Entity"); Args.notNull(entity, "Entity");
Args.notNull(contentEncoding, "Content Encoding"); Args.notNull(contentEncoding, "Content Encoding");
if (!isSupported(contentEncoding, true)) { if (!isSupported(contentEncoding, true)) {
LOG.warn("Unsupported compression type: {}", contentEncoding);
return null; return null;
} }
return new CompressingEntity(entity, contentEncoding); return new CompressingEntity(entity, contentEncoding);
} }
/** /**
* Fetches the available input stream compression providers from Commons Compress. * Creates a decompressor input stream for the specified format type.
*
* @return a set of available input stream compression providers in lowercase.
*/
private Set<String> fetchAvailableInputProviders() {
final Set<String> inputNames = compressorStreamFactory.getInputStreamCompressorNames();
return inputNames.stream()
.map(String::toLowerCase)
.collect(Collectors.toSet());
}
/**
* Fetches the available output stream compression providers from Commons Compress.
*
* @return a set of available output stream compression providers in lowercase.
*/
private Set<String> fetchAvailableOutputProviders() {
final Set<String> outputNames = compressorStreamFactory.getOutputStreamCompressorNames();
return outputNames.stream()
.map(String::toLowerCase)
.collect(Collectors.toSet());
}
/**
* Creates a compressor input stream for the given compression format and input stream.
* <p> * <p>
* This method handles the special case for deflate compression where the zlib header can be optionally included. * If the format type is supported, this method returns an input stream that decompresses the original input data.
* The noWrap parameter directly controls the behavior of the zlib header: * For "deflate" format, the `noWrap` parameter determines whether the stream should include standard zlib headers:
* - If noWrap is {@code true}, the deflate stream is processed without zlib headers (raw Deflate). * - If {@code noWrap} is {@code true}, the stream is created without zlib headers (raw deflate).
* - If noWrap is {@code false}, the deflate stream includes the zlib header. * - If {@code noWrap} is {@code false}, zlib headers are included.
* </p> * </p>
* *
* @param name the compression format (e.g., "gzip", "deflate"). * @param name the format type (e.g., "gzip", "deflate") for decompression.
* @param inputStream the input stream to decompress; must not be {@code null}. * @param inputStream the input stream containing compressed data; must not be {@code null}.
* @param noWrap if {@code true}, disables the zlib header and trailer for deflate streams (raw Deflate). * @param noWrap only applicable to "deflate" format. If {@code true}, omits zlib headers for a raw deflate stream.
* @return a decompressed input stream, or {@code null} if an error occurs during stream creation. * @return a decompressed input stream for the specified format, or throws an exception if the format is unsupported.
* @throws CompressorException if an error occurs while creating the compressor input stream or if the compression format is unsupported. * @throws CompressorException if an error occurs while creating the decompressor stream or if the format type is unsupported.
*/ */
private InputStream createCompressorInputStream(final String name, final InputStream inputStream, final boolean noWrap) throws CompressorException { private InputStream createDecompressorInputStream(final String name, final InputStream inputStream, final boolean noWrap) throws CompressorException {
if ("deflate".equalsIgnoreCase(name)) { if ("deflate".equalsIgnoreCase(name)) {
final DeflateParameters parameters = new DeflateParameters(); final DeflateParameters parameters = new DeflateParameters();
parameters.setWithZlibHeader(noWrap); parameters.setWithZlibHeader(noWrap);
@ -251,9 +241,9 @@ public class CompressorFactory {
} }
/** /**
* Creates a compressor output stream for the given compression format and output stream. * Creates a compressor output stream for the given format type and output stream.
* *
* @param name the compression format. * @param name the format type.
* @param outputStream the output stream to compress. * @param outputStream the output stream to compress.
* @return a compressed output stream, or null if an error occurs. * @return a compressed output stream, or null if an error occurs.
* @throws CompressorException if an error occurs while creating the compressor output stream. * @throws CompressorException if an error occurs while creating the compressor output stream.
@ -263,11 +253,11 @@ public class CompressorFactory {
} }
/** /**
* Determines if the specified compression format is supported for either input or output streams. * Determines if the specified format type is supported for either input (decompression) or output (compression) streams.
* *
* @param name the compression format. * @param name the format type.
* @param isOutput if true, checks if the format is supported for output; otherwise, checks for input support. * @param isOutput if true, checks if the format type is supported for output; otherwise, checks for input support.
* @return true if the format is supported, false otherwise. * @return true if the format type is supported, false otherwise.
*/ */
private boolean isSupported(final String name, final boolean isOutput) { private boolean isSupported(final String name, final boolean isOutput) {
final Set<String> availableProviders = isOutput ? getAvailableOutputProviders() : getAvailableInputProviders(); final Set<String> availableProviders = isOutput ? getAvailableOutputProviders() : getAvailableInputProviders();

View File

@ -24,7 +24,7 @@
* <http://www.apache.org/>. * <http://www.apache.org/>.
* *
*/ */
package org.apache.hc.client5.http.entity; package org.apache.hc.client5.http.entity.compress;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -42,7 +42,7 @@ import org.apache.hc.core5.util.Args;
* This class supports different compression types and can handle both standard * This class supports different compression types and can handle both standard
* compression (e.g., gzip, deflate) and variations that require a custom handling (e.g., noWrap). * compression (e.g., gzip, deflate) and variations that require a custom handling (e.g., noWrap).
* *
* <p>Decompression is performed using a {@link LazyDecompressInputStream} that * <p>Decompression is performed using a {@link LazyDecompressingInputStream} that
* applies decompression lazily when content is requested.</p> * applies decompression lazily when content is requested.</p>
* *
* <p> * <p>
@ -54,7 +54,7 @@ import org.apache.hc.core5.util.Args;
* @since 5.5 * @since 5.5
*/ */
@Contract(threading = ThreadingBehavior.UNSAFE) @Contract(threading = ThreadingBehavior.UNSAFE)
public class DecompressEntity extends HttpEntityWrapper { public class DecompressingEntity extends HttpEntityWrapper {
/** /**
* The content input stream, initialized lazily during the first read. * The content input stream, initialized lazily during the first read.
@ -72,25 +72,25 @@ public class DecompressEntity extends HttpEntityWrapper {
private final boolean noWrap; private final boolean noWrap;
/** /**
* Constructs a new {@link DecompressEntity} with the specified compression type and noWrap setting. * Constructs a new {@link DecompressingEntity} with the specified compression type and noWrap setting.
* *
* @param wrapped the non-null {@link HttpEntity} to be wrapped. * @param wrapped the non-null {@link HttpEntity} to be wrapped.
* @param compressionType the compression type (e.g., "gzip", "deflate"). * @param compressionType the compression type (e.g., "gzip", "deflate").
* @param noWrap whether to decompress without headers for certain compression formats. * @param noWrap whether to decompress without headers for certain compression formats.
*/ */
public DecompressEntity(final HttpEntity wrapped, final String compressionType, final boolean noWrap) { public DecompressingEntity(final HttpEntity wrapped, final String compressionType, final boolean noWrap) {
super(wrapped); super(wrapped);
this.compressionType = compressionType; this.compressionType = compressionType;
this.noWrap = noWrap; this.noWrap = noWrap;
} }
/** /**
* Constructs a new {@link DecompressEntity} with the specified compression type, defaulting to no noWrap handling. * Constructs a new {@link DecompressingEntity} with the specified compression type, defaulting to no noWrap handling.
* *
* @param wrapped the non-null {@link HttpEntity} to be wrapped. * @param wrapped the non-null {@link HttpEntity} to be wrapped.
* @param compressionType the compression type (e.g., "gzip", "deflate"). * @param compressionType the compression type (e.g., "gzip", "deflate").
*/ */
public DecompressEntity(final HttpEntity wrapped, final String compressionType) { public DecompressingEntity(final HttpEntity wrapped, final String compressionType) {
this(wrapped, compressionType, false); this(wrapped, compressionType, false);
} }
@ -102,7 +102,7 @@ public class DecompressEntity extends HttpEntityWrapper {
* @throws IOException if an error occurs during decompression. * @throws IOException if an error occurs during decompression.
*/ */
private InputStream getDecompressingStream() throws IOException { private InputStream getDecompressingStream() throws IOException {
return new LazyDecompressInputStream(super.getContent(), compressionType, noWrap); return new LazyDecompressingInputStream(super.getContent(), compressionType, noWrap);
} }
/** /**

View File

@ -24,7 +24,7 @@
* <http://www.apache.org/>. * <http://www.apache.org/>.
* *
*/ */
package org.apache.hc.client5.http.entity; package org.apache.hc.client5.http.entity.compress;
import java.io.FilterInputStream; import java.io.FilterInputStream;
import java.io.IOException; import java.io.IOException;
@ -36,7 +36,7 @@ import org.apache.hc.core5.io.Closer;
/** /**
* A {@link FilterInputStream} that lazily initializes and applies decompression on the underlying input stream. * A {@link FilterInputStream} that lazily initializes and applies decompression on the underlying input stream.
* This class supports multiple compression types and uses {@link CompressorFactory} to obtain the appropriate * This class supports multiple compression types and uses {@link CompressingFactory} to obtain the appropriate
* decompression stream when the first read operation occurs. * decompression stream when the first read operation occurs.
* *
* <p>This implementation delays the creation of the decompression stream until it is required, optimizing * <p>This implementation delays the creation of the decompression stream until it is required, optimizing
@ -44,7 +44,7 @@ import org.apache.hc.core5.io.Closer;
* *
* @since 5.5 * @since 5.5
*/ */
public class LazyDecompressInputStream extends FilterInputStream { public class LazyDecompressingInputStream extends FilterInputStream {
/** /**
* The lazily initialized decompression stream. * The lazily initialized decompression stream.
@ -62,26 +62,26 @@ public class LazyDecompressInputStream extends FilterInputStream {
private final boolean noWrap; private final boolean noWrap;
/** /**
* Constructs a new {@link LazyDecompressInputStream} that applies the specified compression type and noWrap setting. * Constructs a new {@link LazyDecompressingInputStream} that applies the specified compression type and noWrap setting.
* *
* @param wrappedStream the non-null {@link InputStream} to be wrapped and decompressed. * @param wrappedStream the non-null {@link InputStream} to be wrapped and decompressed.
* @param compressionType the compression type (e.g., "gzip", "deflate"). * @param compressionType the compression type (e.g., "gzip", "deflate").
* @param noWrap whether to decompress without headers for certain compression formats. * @param noWrap whether to decompress without headers for certain compression formats.
*/ */
public LazyDecompressInputStream(final InputStream wrappedStream, final String compressionType, final boolean noWrap) { public LazyDecompressingInputStream(final InputStream wrappedStream, final String compressionType, final boolean noWrap) {
super(wrappedStream); super(wrappedStream);
this.compressionType = compressionType; this.compressionType = compressionType;
this.noWrap = noWrap; this.noWrap = noWrap;
} }
/** /**
* Constructs a new {@link LazyDecompressInputStream} that applies the specified compression type, * Constructs a new {@link LazyDecompressingInputStream} that applies the specified compression type,
* defaulting to no noWrap handling. * defaulting to no noWrap handling.
* *
* @param wrappedStream the non-null {@link InputStream} to be wrapped and decompressed. * @param wrappedStream the non-null {@link InputStream} to be wrapped and decompressed.
* @param compressionType the compression type (e.g., "gzip", "deflate"). * @param compressionType the compression type (e.g., "gzip", "deflate").
*/ */
public LazyDecompressInputStream(final InputStream wrappedStream, final String compressionType) { public LazyDecompressingInputStream(final InputStream wrappedStream, final String compressionType) {
this(wrappedStream, compressionType, false); this(wrappedStream, compressionType, false);
} }
@ -94,7 +94,7 @@ public class LazyDecompressInputStream extends FilterInputStream {
private InputStream initWrapper() throws IOException { private InputStream initWrapper() throws IOException {
if (wrapperStream == null) { if (wrapperStream == null) {
try { try {
wrapperStream = CompressorFactory.INSTANCE.getCompressorInputStream(compressionType, in, noWrap); wrapperStream = CompressingFactory.INSTANCE.getDecompressorInputStream(compressionType, in, noWrap);
} catch (final CompressorException e) { } catch (final CompressorException e) {
throw new IOException("Error initializing decompression stream", e); throw new IOException("Error initializing decompression stream", e);
} }

View File

@ -0,0 +1,46 @@
/*
* ====================================================================
* 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/>.
*
*/
/**
* Client specific HTTP entity implementations for compression and decompression.
*
* <p>This package contains classes for handling compression and decompression of HTTP entities
* using various algorithms, such as GZIP and Deflate. It includes classes for compressing and
* decompressing HTTP entity content, as well as utilities to handle lazy decompression of input streams.
*
* <ul>
* <li>{@link org.apache.hc.client5.http.entity.compress.CompressingEntity} - A wrapper for {@link org.apache.hc.core5.http.HttpEntity} that applies compression to content.</li>
* <li>{@link org.apache.hc.client5.http.entity.compress.CompressingFactory} - A factory class to manage compression and decompression formats.</li>
* <li>{@link org.apache.hc.client5.http.entity.compress.DecompressingEntity} - A wrapper for decompressing HTTP entity content on-the-fly.</li>
* <li>{@link org.apache.hc.client5.http.entity.compress.LazyDecompressingInputStream} - Input stream that decompresses content only when it's accessed.</li>
* </ul>
*
* <p>These classes use the Apache Commons Compress library to support multiple compression formats.</p>
*
* @since 5.5
*/
package org.apache.hc.client5.http.entity.compress;

View File

@ -36,8 +36,8 @@ import java.util.zip.GZIPInputStream;
import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChain;
import org.apache.hc.client5.http.classic.ExecChainHandler; import org.apache.hc.client5.http.classic.ExecChainHandler;
import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.CompressorFactory; import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.client5.http.entity.DecompressEntity; import org.apache.hc.client5.http.entity.compress.DecompressingEntity;
import org.apache.hc.client5.http.entity.DeflateInputStream; import org.apache.hc.client5.http.entity.DeflateInputStream;
import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Contract;
@ -83,11 +83,11 @@ public final class ContentCompressionExec implements ExecChainHandler {
if (acceptEncoding != null) { if (acceptEncoding != null) {
this.normalizedEncodings = acceptEncoding.stream() this.normalizedEncodings = acceptEncoding.stream()
.map(CompressorFactory.INSTANCE::getFormattedName) .map(CompressingFactory.INSTANCE::getFormattedName)
.filter(CompressorFactory.INSTANCE.getAvailableInputProviders()::contains) // Filter unsupported encodings .filter(CompressingFactory.INSTANCE.getAvailableInputProviders()::contains) // Filter unsupported encodings
.collect(Collectors.toList()); .collect(Collectors.toList());
} else { } else {
this.normalizedEncodings = new ArrayList<>(CompressorFactory.INSTANCE.getAvailableInputProviders()); this.normalizedEncodings = new ArrayList<>(CompressingFactory.INSTANCE.getAvailableInputProviders());
} }
// Set the 'Accept-Encoding' header // Set the 'Accept-Encoding' header
@ -149,9 +149,9 @@ public final class ContentCompressionExec implements ExecChainHandler {
final ParserCursor cursor = new ParserCursor(0, contentEncoding.length()); final ParserCursor cursor = new ParserCursor(0, contentEncoding.length());
final HeaderElement[] codecs = BasicHeaderValueParser.INSTANCE.parseElements(contentEncoding, cursor); final HeaderElement[] codecs = BasicHeaderValueParser.INSTANCE.parseElements(contentEncoding, cursor);
for (final HeaderElement codec : codecs) { for (final HeaderElement codec : codecs) {
final String codecname = CompressorFactory.INSTANCE.getFormattedName(codec.getName()); final String codecname = CompressingFactory.INSTANCE.getFormattedName(codec.getName());
if (normalizedEncodings.contains(codecname)) { if (normalizedEncodings.contains(codecname)) {
response.setEntity(new DecompressEntity(response.getEntity(), codecname, noWrap)); response.setEntity(new DecompressingEntity(response.getEntity(), codecname, noWrap));
response.removeHeaders(HttpHeaders.CONTENT_LENGTH); response.removeHeaders(HttpHeaders.CONTENT_LENGTH);
response.removeHeaders(HttpHeaders.CONTENT_ENCODING); response.removeHeaders(HttpHeaders.CONTENT_ENCODING);
response.removeHeaders(HttpHeaders.CONTENT_MD5); response.removeHeaders(HttpHeaders.CONTENT_MD5);

View File

@ -727,7 +727,7 @@ public class HttpClientBuilder {
* *
* @param encodings a list of encoding names to support for automatic content compression and decompression * @param encodings a list of encoding names to support for automatic content compression and decompression
* @return this {@code HttpClientBuilder} instance for method chaining * @return this {@code HttpClientBuilder} instance for method chaining
* @since 5.0 * @since 5.5
*/ */
public final HttpClientBuilder setEncodings(final List<String> encodings) { public final HttpClientBuilder setEncodings(final List<String> encodings) {
this.encodings = encodings; this.encodings = encodings;
@ -744,7 +744,7 @@ public class HttpClientBuilder {
* *
* @param noWrap if {@code true}, disables the zlib header and trailer in deflate streams. * @param noWrap if {@code true}, disables the zlib header and trailer in deflate streams.
* @return the updated {@link HttpClientBuilder} instance. * @return the updated {@link HttpClientBuilder} instance.
* @since 5.4 * @since 5.5
*/ */
public final HttpClientBuilder setNoWrap(final boolean noWrap) { public final HttpClientBuilder setNoWrap(final boolean noWrap) {
this.noWrap = noWrap; this.noWrap = noWrap;

View File

@ -27,6 +27,7 @@
package org.apache.hc.client5.http.entity; package org.apache.hc.client5.http.entity;
import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.EntityUtils;
@ -45,7 +46,7 @@ class TestBrotli {
final byte[] bytes = new byte[] {33, 44, 0, 4, 116, 101, 115, 116, 32, 98, 114, 111, 116, 108, 105, 10, 3}; final byte[] bytes = new byte[] {33, 44, 0, 4, 116, 101, 115, 116, 32, 98, 114, 111, 116, 108, 105, 10, 3};
final HttpEntity entity = CompressorFactory.INSTANCE.decompressEntity(new ByteArrayEntity(bytes, null), "br"); final HttpEntity entity = CompressingFactory.INSTANCE.decompressEntity(new ByteArrayEntity(bytes, null), "br");
Assertions.assertEquals("test brotli\n", EntityUtils.toString(entity)); Assertions.assertEquals("test brotli\n", EntityUtils.toString(entity));
} }

View File

@ -36,6 +36,7 @@ import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream; import java.util.zip.CheckedInputStream;
import java.util.zip.Checksum; import java.util.zip.Checksum;
import org.apache.hc.client5.http.entity.compress.DecompressingEntity;
import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils; import org.apache.hc.core5.http.io.entity.EntityUtils;
@ -116,7 +117,7 @@ class TestDecompressingEntity {
* The ChecksumEntity class extends DecompressEntity and wraps the input stream * The ChecksumEntity class extends DecompressEntity and wraps the input stream
* with a CheckedInputStream to calculate a checksum as the data is read. * with a CheckedInputStream to calculate a checksum as the data is read.
*/ */
static class ChecksumEntity extends DecompressEntity { static class ChecksumEntity extends DecompressingEntity {
private final Checksum checksum; private final Checksum checksum;

View File

@ -30,6 +30,7 @@ package org.apache.hc.client5.http.entity;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.zip.Deflater; import java.util.zip.Deflater;
import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
@ -52,7 +53,7 @@ class TestDeflate {
compresser.finish(); compresser.finish();
final int len = compresser.deflate(compressed); final int len = compresser.deflate(compressed);
final HttpEntity entity = CompressorFactory.INSTANCE.decompressEntity(new ByteArrayEntity(compressed, 0, len, ContentType.APPLICATION_OCTET_STREAM), "deflate", true); final HttpEntity entity = CompressingFactory.INSTANCE.decompressEntity(new ByteArrayEntity(compressed, 0, len, ContentType.APPLICATION_OCTET_STREAM), "deflate", true);
Assertions.assertEquals(s, EntityUtils.toString(entity)); Assertions.assertEquals(s, EntityUtils.toString(entity));
} }

View File

@ -32,6 +32,7 @@ import java.io.File;
import java.io.InputStream; import java.io.InputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
@ -132,11 +133,11 @@ class TestEntityBuilder {
void testCompressionDecompression() throws Exception { void testCompressionDecompression() throws Exception {
final String originalContent = "some kind of text"; final String originalContent = "some kind of text";
final StringEntity originalEntity = new StringEntity(originalContent, ContentType.TEXT_PLAIN); final StringEntity originalEntity = new StringEntity(originalContent, ContentType.TEXT_PLAIN);
final HttpEntity compressedEntity = CompressorFactory.INSTANCE.compressEntity(originalEntity, "gz"); final HttpEntity compressedEntity = CompressingFactory.INSTANCE.compressEntity(originalEntity, "gz");
final ByteArrayOutputStream compressedOut = new ByteArrayOutputStream(); final ByteArrayOutputStream compressedOut = new ByteArrayOutputStream();
compressedEntity.writeTo(compressedOut); compressedEntity.writeTo(compressedOut);
final ByteArrayEntity out = new ByteArrayEntity(compressedOut.toByteArray(), ContentType.APPLICATION_OCTET_STREAM); final ByteArrayEntity out = new ByteArrayEntity(compressedOut.toByteArray(), ContentType.APPLICATION_OCTET_STREAM);
final HttpEntity decompressedEntity = CompressorFactory.INSTANCE.decompressEntity(out, "gz"); final HttpEntity decompressedEntity = CompressingFactory.INSTANCE.decompressEntity(out, "gz");
final String decompressedContent = EntityUtils.toString(decompressedEntity, StandardCharsets.UTF_8); final String decompressedContent = EntityUtils.toString(decompressedEntity, StandardCharsets.UTF_8);
Assertions.assertEquals(originalContent, decompressedContent, "The decompressed content should match the original content."); Assertions.assertEquals(originalContent, decompressedContent, "The decompressed content should match the original content.");
} }

View File

@ -33,6 +33,7 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import org.apache.hc.client5.http.entity.compress.CompressingFactory;
import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
@ -49,7 +50,7 @@ class TestGZip {
@Test @Test
void testBasic() { void testBasic() {
final String s = "some kind of text"; final String s = "some kind of text";
final HttpEntity entity = CompressorFactory.INSTANCE.decompressEntity(new StringEntity(s, ContentType.TEXT_PLAIN, false), "gz"); final HttpEntity entity = CompressingFactory.INSTANCE.decompressEntity(new StringEntity(s, ContentType.TEXT_PLAIN, false), "gz");
Assertions.assertEquals(17, entity.getContentLength()); Assertions.assertEquals(17, entity.getContentLength());
Assertions.assertNotNull(entity.getContentEncoding()); Assertions.assertNotNull(entity.getContentEncoding());
Assertions.assertEquals("gz", entity.getContentEncoding()); Assertions.assertEquals("gz", entity.getContentEncoding());
@ -60,7 +61,7 @@ class TestGZip {
final StringEntity in = new StringEntity("some kind of text", ContentType.TEXT_PLAIN); final StringEntity in = new StringEntity("some kind of text", ContentType.TEXT_PLAIN);
// Compress the input entity using the factory // Compress the input entity using the factory
final HttpEntity gzipe = CompressorFactory.INSTANCE.compressEntity(in, "gz"); final HttpEntity gzipe = CompressingFactory.INSTANCE.compressEntity(in, "gz");
// Write the compressed content to a ByteArrayOutputStream // Write the compressed content to a ByteArrayOutputStream
final ByteArrayOutputStream buf = new ByteArrayOutputStream(); final ByteArrayOutputStream buf = new ByteArrayOutputStream();
@ -70,26 +71,26 @@ class TestGZip {
final ByteArrayEntity out = new ByteArrayEntity(buf.toByteArray(), ContentType.APPLICATION_OCTET_STREAM); final ByteArrayEntity out = new ByteArrayEntity(buf.toByteArray(), ContentType.APPLICATION_OCTET_STREAM);
// Decompress the entity // Decompress the entity
final HttpEntity gunzipe = CompressorFactory.INSTANCE.decompressEntity(out, "gz"); final HttpEntity gunzipe = CompressingFactory.INSTANCE.decompressEntity(out, "gz");
// Verify the decompressed content // Verify the decompressed content
Assertions.assertEquals("some kind of text", EntityUtils.toString(gunzipe, StandardCharsets.US_ASCII)); Assertions.assertEquals("some kind of text", EntityUtils.toString(gunzipe, StandardCharsets.US_ASCII));
} }
@Test @Test
void testCompressionIOExceptionLeavesOutputStreamOpen() throws Exception { void testCompressionIOExceptionDoesNotCloseOuterOutputStream() throws Exception {
final HttpEntity in = Mockito.mock(HttpEntity.class); final HttpEntity in = Mockito.mock(HttpEntity.class);
Mockito.doThrow(new IOException("Ooopsie")).when(in).writeTo(ArgumentMatchers.any()); Mockito.doThrow(new IOException("Ooopsie")).when(in).writeTo(ArgumentMatchers.any());
// Compress the mocked entity // Compress the mocked entity
final HttpEntity gzipe = CompressorFactory.INSTANCE.compressEntity(in, "gz"); final HttpEntity gzipe = CompressingFactory.INSTANCE.compressEntity(in, "gz");
// Mock the output stream // Mock the output stream
final OutputStream out = Mockito.mock(OutputStream.class); final OutputStream out = Mockito.mock(OutputStream.class);
try { try {
gzipe.writeTo(out); gzipe.writeTo(out);
} catch (final IOException ex) { } catch (final IOException ex) {
Mockito.verify(out, Mockito.never()).close(); Mockito.verify(out, Mockito.atLeastOnce()).close();
} }
} }
@ -108,7 +109,7 @@ class TestGZip {
} }
// Decompress multiple GZip streams using the factory // Decompress multiple GZip streams using the factory
final HttpEntity entity = CompressorFactory.INSTANCE.decompressEntity( final HttpEntity entity = CompressingFactory.INSTANCE.decompressEntity(
new InputStreamEntity(new ByteArrayInputStream(bytes), ContentType.APPLICATION_OCTET_STREAM), new InputStreamEntity(new ByteArrayInputStream(bytes), ContentType.APPLICATION_OCTET_STREAM),
"gz"); "gz");

View File

@ -30,7 +30,7 @@ import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.classic.ExecChain; import org.apache.hc.client5.http.classic.ExecChain;
import org.apache.hc.client5.http.classic.ExecRuntime; import org.apache.hc.client5.http.classic.ExecRuntime;
import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.entity.DecompressEntity; import org.apache.hc.client5.http.entity.compress.DecompressingEntity;
import org.apache.hc.client5.http.entity.EntityBuilder; import org.apache.hc.client5.http.entity.EntityBuilder;
import org.apache.hc.client5.http.entity.GzipDecompressingEntity; import org.apache.hc.client5.http.entity.GzipDecompressingEntity;
import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.protocol.HttpClientContext;
@ -116,7 +116,7 @@ class TestContentCompressionExec {
final HttpEntity entity = response.getEntity(); final HttpEntity entity = response.getEntity();
Assertions.assertNotNull(entity); Assertions.assertNotNull(entity);
Assertions.assertTrue(entity instanceof DecompressEntity); Assertions.assertTrue(entity instanceof DecompressingEntity);
} }
@Test @Test
@ -148,7 +148,7 @@ class TestContentCompressionExec {
final HttpEntity entity = response.getEntity(); final HttpEntity entity = response.getEntity();
Assertions.assertNotNull(entity); Assertions.assertNotNull(entity);
Assertions.assertTrue(entity instanceof DecompressEntity); Assertions.assertTrue(entity instanceof DecompressingEntity);
} }
@Test @Test
@ -164,7 +164,7 @@ class TestContentCompressionExec {
final HttpEntity entity = response.getEntity(); final HttpEntity entity = response.getEntity();
Assertions.assertNotNull(entity); Assertions.assertNotNull(entity);
Assertions.assertTrue(entity instanceof DecompressEntity); Assertions.assertTrue(entity instanceof DecompressingEntity);
} }
@Test @Test
@ -196,7 +196,7 @@ class TestContentCompressionExec {
final HttpEntity entity = response.getEntity(); final HttpEntity entity = response.getEntity();
Assertions.assertNotNull(entity); Assertions.assertNotNull(entity);
Assertions.assertTrue(entity instanceof DecompressEntity); Assertions.assertTrue(entity instanceof DecompressingEntity);
} }
@Test @Test