add brotli support

This commit is contained in:
Looly 2022-10-24 17:18:18 +08:00
parent 51e4a14388
commit 79e7e51727
6 changed files with 104 additions and 28 deletions

View File

@ -22,6 +22,12 @@
<artifactId>hutool-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.soap</groupId>
<artifactId>javax.xml.soap-api</artifactId>
<version>1.4.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-json</artifactId>
@ -29,10 +35,10 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.xml.soap</groupId>
<artifactId>javax.xml.soap-api</artifactId>
<version>1.4.0</version>
<scope>provided</scope>
<groupId>org.brotli</groupId>
<artifactId>dec</artifactId>
<version>0.1.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,57 @@
package cn.hutool.http;
import cn.hutool.core.map.CaseInsensitiveMap;
import java.io.InputStream;
import java.util.Map;
/**
* 全局响应内容压缩解压器注册中心<br>
* 通过注册指定Accept-Encoding的流来包装响应内容流从而支持特殊压缩算法
*
* @author looly
* @since 6.0.0
*/
public enum GlobalCompressStreamRegister {
/**
* 单例对象
*/
INSTANCE;
/**
* 存储内容压缩流信息
*/
private final Map<String, Class<? extends InputStream>> compressMap = new CaseInsensitiveMap<>();
GlobalCompressStreamRegister() {
}
/**
* 获取解压器
*
* @param contentEncoding Accept-Encoding名称如gzipdefaltebr等不区分大小写
* @return 解压器
*/
public Class<? extends InputStream> get(final String contentEncoding) {
return compressMap.get(contentEncoding);
}
/**
* 注册解压器
*
* @param contentEncoding Accept-Encoding名称如gzipdefaltebr等不区分大小写
* @param streamClass 解压类
*/
synchronized public void register(final String contentEncoding, final Class<? extends InputStream> streamClass) {
this.compressMap.put(contentEncoding, streamClass);
}
/**
* 注销压缩器
*
* @param contentEncoding Accept-Encoding名称如gzipdefaltebr等不区分大小写
*/
synchronized public void unRegister(final String contentEncoding) {
this.compressMap.remove(contentEncoding);
}
}

View File

@ -18,6 +18,9 @@ import java.util.Map.Entry;
* @author looly
*/
public enum GlobalHeaders {
/**
* 单例对象
*/
INSTANCE;
/**

View File

@ -1,5 +1,6 @@
package cn.hutool.http;
import cn.hutool.core.reflect.ConstructorUtil;
import cn.hutool.core.text.StrUtil;
import java.io.ByteArrayInputStream;
@ -92,17 +93,27 @@ public class HttpInputStream extends InputStream {
return;
}
if (response.isGzip() && false == (response.in instanceof GZIPInputStream)) {
final String contentEncoding = response.contentEncoding();
if (StrUtil.equalsIgnoreCase("gzip", contentEncoding) && false == (response.in instanceof GZIPInputStream)) {
// Accept-Encoding: gzip
try {
this.in = new GZIPInputStream(this.in);
} catch (final IOException e) {
} catch (final IOException ignore) {
// 在类似于Head等方法中无body返回此时GZIPInputStream构造会出现错误在此忽略此错误读取普通数据
// ignore
}
} else if (response.isDeflate() && false == (this.in instanceof InflaterInputStream)) {
} else if (StrUtil.equalsIgnoreCase("deflate", contentEncoding) && false == (this.in instanceof InflaterInputStream)) {
// Accept-Encoding: defalte
this.in = new InflaterInputStream(this.in, new Inflater(true));
} else{
final Class<? extends InputStream> streamClass = GlobalCompressStreamRegister.INSTANCE.get(contentEncoding);
if(null != streamClass){
try {
this.in = ConstructorUtil.newInstance(streamClass, this.in);
} catch (final Exception ignore) {
// 对于构造错误的压缩算法跳过之
}
}
}
}
}

View File

@ -143,27 +143,6 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
return contentLength;
}
/**
* 是否为gzip压缩过的内容
*
* @return 是否为gzip压缩过的内容
*/
public boolean isGzip() {
final String contentEncoding = contentEncoding();
return "gzip".equalsIgnoreCase(contentEncoding);
}
/**
* 是否为zlib(Deflate)压缩过的内容
*
* @return 是否为zlib(Deflate)压缩过的内容
* @since 4.5.7
*/
public boolean isDeflate() {
final String contentEncoding = contentEncoding();
return "deflate".equalsIgnoreCase(contentEncoding);
}
/**
* 是否为Transfer-Encoding:Chunked的内容
*

View File

@ -0,0 +1,20 @@
package cn.hutool.http;
import cn.hutool.core.lang.Console;
import org.brotli.dec.BrotliInputStream;
import org.junit.Ignore;
import org.junit.Test;
public class IssueI5XBCFTest {
@Test
@Ignore
public void getTest() {
GlobalCompressStreamRegister.INSTANCE.register("br", BrotliInputStream.class);
final HttpResponse s = HttpUtil.createGet("https://static-exp1.licdn.com/sc/h/br/1cp0oqz322bdprj3qd4pojqix")
.header(Header.ACCEPT_ENCODING, "br")
.execute();
Console.log(s.body());
}
}