add br decompression support (#363)
This commit is contained in:
parent
35732cacb2
commit
db47570efe
|
@ -81,6 +81,11 @@
|
|||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.brotli</groupId>
|
||||
<artifactId>dec</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* 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.http.entity;
|
||||
|
||||
import org.apache.hc.core5.http.HttpEntity;
|
||||
|
||||
/**
|
||||
* {@link org.apache.hc.core5.http.io.entity.HttpEntityWrapper} responsible for
|
||||
* handling br Content Coded responses.
|
||||
*
|
||||
* @see GzipDecompressingEntity
|
||||
* @since 5.2
|
||||
*/
|
||||
public class BrotliDecompressingEntity extends DecompressingEntity {
|
||||
/**
|
||||
* Creates a new {@link DecompressingEntity}.
|
||||
*
|
||||
* @param entity factory to create decompressing stream.
|
||||
*/
|
||||
public BrotliDecompressingEntity(final HttpEntity entity) {
|
||||
super(entity, BrotliInputStreamFactory.getInstance());
|
||||
}
|
||||
|
||||
public static boolean isAvailable() {
|
||||
try {
|
||||
Class.forName("org.brotli.dec.BrotliInputStream");
|
||||
return true;
|
||||
} catch (final ClassNotFoundException | NoClassDefFoundError e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* 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.http.entity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.hc.core5.annotation.Contract;
|
||||
import org.apache.hc.core5.annotation.ThreadingBehavior;
|
||||
import org.brotli.dec.BrotliInputStream;
|
||||
|
||||
/**
|
||||
* {@link InputStreamFactory} for handling Brotli Content Coded responses.
|
||||
*
|
||||
* @since 5.2
|
||||
*/
|
||||
@Contract(threading = ThreadingBehavior.STATELESS)
|
||||
public class BrotliInputStreamFactory implements InputStreamFactory {
|
||||
|
||||
private static final BrotliInputStreamFactory INSTANCE = new BrotliInputStreamFactory();
|
||||
|
||||
public static BrotliInputStreamFactory getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream create(final InputStream inputStream) throws IOException {
|
||||
return new BrotliInputStream(inputStream);
|
||||
}
|
||||
}
|
|
@ -34,6 +34,8 @@ import java.util.Locale;
|
|||
import org.apache.hc.client5.http.classic.ExecChain;
|
||||
import org.apache.hc.client5.http.classic.ExecChainHandler;
|
||||
import org.apache.hc.client5.http.config.RequestConfig;
|
||||
import org.apache.hc.client5.http.entity.BrotliDecompressingEntity;
|
||||
import org.apache.hc.client5.http.entity.BrotliInputStreamFactory;
|
||||
import org.apache.hc.client5.http.entity.DecompressingEntity;
|
||||
import org.apache.hc.client5.http.entity.DeflateInputStreamFactory;
|
||||
import org.apache.hc.client5.http.entity.GZIPInputStreamFactory;
|
||||
|
@ -84,16 +86,32 @@ public final class ContentCompressionExec implements ExecChainHandler {
|
|||
final List<String> acceptEncoding,
|
||||
final Lookup<InputStreamFactory> decoderRegistry,
|
||||
final boolean ignoreUnknown) {
|
||||
|
||||
final boolean brotliSupported = BrotliDecompressingEntity.isAvailable();
|
||||
final String[] encoding;
|
||||
if (brotliSupported) {
|
||||
encoding = new String[] {"gzip", "x-gzip", "deflate", "br"};
|
||||
} else {
|
||||
encoding = new String[] {"gzip", "x-gzip", "deflate"};
|
||||
}
|
||||
this.acceptEncoding = MessageSupport.format(HttpHeaders.ACCEPT_ENCODING,
|
||||
acceptEncoding != null ? acceptEncoding.toArray(
|
||||
EMPTY_STRING_ARRAY) : new String[] {"gzip", "x-gzip", "deflate"});
|
||||
EMPTY_STRING_ARRAY) : encoding);
|
||||
|
||||
this.decoderRegistry = decoderRegistry != null ? decoderRegistry :
|
||||
RegistryBuilder.<InputStreamFactory>create()
|
||||
if (decoderRegistry != null) {
|
||||
this.decoderRegistry = decoderRegistry;
|
||||
} else {
|
||||
final RegistryBuilder<InputStreamFactory> builder = RegistryBuilder.<InputStreamFactory>create()
|
||||
.register("gzip", GZIPInputStreamFactory.getInstance())
|
||||
.register("x-gzip", GZIPInputStreamFactory.getInstance())
|
||||
.register("deflate", DeflateInputStreamFactory.getInstance())
|
||||
.build();
|
||||
.register("deflate", DeflateInputStreamFactory.getInstance());
|
||||
if (brotliSupported) {
|
||||
builder.register("br", BrotliInputStreamFactory.getInstance());
|
||||
}
|
||||
this.decoderRegistry = builder.build();
|
||||
}
|
||||
|
||||
|
||||
this.ignoreUnknown = ignoreUnknown;
|
||||
}
|
||||
|
||||
|
@ -107,6 +125,7 @@ public final class ContentCompressionExec implements ExecChainHandler {
|
|||
* <ul>
|
||||
* <li>gzip - see {@link java.util.zip.GZIPInputStream}</li>
|
||||
* <li>deflate - see {@link org.apache.hc.client5.http.entity.DeflateInputStream}</li>
|
||||
* <li>brotli - see {@link org.brotli.dec.BrotliInputStream}</li>
|
||||
* </ul>
|
||||
*/
|
||||
public ContentCompressionExec() {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* 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.http.entity;
|
||||
|
||||
import org.apache.hc.core5.http.HttpEntity;
|
||||
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
|
||||
import org.apache.hc.core5.http.io.entity.EntityUtils;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class TestBrotli {
|
||||
|
||||
/**
|
||||
* Brotli decompression test implemented by request with specified response encoding br
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void testDecompressionWithBrotli() throws Exception {
|
||||
|
||||
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 = new BrotliDecompressingEntity(new ByteArrayEntity(bytes, null));
|
||||
Assertions.assertEquals("test brotli\n", EntityUtils.toString(entity));
|
||||
}
|
||||
|
||||
}
|
|
@ -183,6 +183,22 @@ public class TestContentCompressionExec {
|
|||
Assertions.assertTrue(entity instanceof StringEntity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrotliContentEncoding() throws Exception {
|
||||
final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/");
|
||||
final ClassicHttpResponse response = new BasicClassicHttpResponse(200, "OK");
|
||||
final HttpEntity original = EntityBuilder.create().setText("encoded stuff").setContentEncoding("br").build();
|
||||
response.setEntity(original);
|
||||
|
||||
Mockito.when(execChain.proceed(request, scope)).thenReturn(response);
|
||||
|
||||
impl.execute(request, scope, execChain);
|
||||
|
||||
final HttpEntity entity = response.getEntity();
|
||||
Assertions.assertNotNull(entity);
|
||||
Assertions.assertTrue(entity instanceof DecompressingEntity);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnknownContentEncoding() throws Exception {
|
||||
final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/");
|
||||
|
|
6
pom.xml
6
pom.xml
|
@ -65,6 +65,7 @@
|
|||
<httpcore.version>5.2-beta1</httpcore.version>
|
||||
<log4j.version>2.17.0</log4j.version>
|
||||
<commons-codec.version>1.15</commons-codec.version>
|
||||
<brotli.version>0.1.2</brotli.version>
|
||||
<conscrypt.version>2.5.2</conscrypt.version>
|
||||
<ehcache.version>3.9.6</ehcache.version>
|
||||
<memcached.version>2.12.3</memcached.version>
|
||||
|
@ -148,6 +149,11 @@
|
|||
<artifactId>commons-codec</artifactId>
|
||||
<version>${commons-codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.brotli</groupId>
|
||||
<artifactId>dec</artifactId>
|
||||
<version>${brotli.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.conscrypt</groupId>
|
||||
<artifactId>conscrypt-openjdk-uber</artifactId>
|
||||
|
|
Loading…
Reference in New Issue