This commit is contained in:
Looly 2021-08-16 22:33:30 +08:00
parent f288c497b8
commit 3fe722554c
6 changed files with 84 additions and 34 deletions

View File

@ -11,6 +11,7 @@
* *
### 🐞Bug修复 ### 🐞Bug修复
* 【extra 】 修复TinyPinyinEngine空构造造成可能的误判问题 * 【extra 】 修复TinyPinyinEngine空构造造成可能的误判问题
* 【http 】 修复在gzip模式下Content-Length服务端设置异常导致的阶段
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------

View File

@ -22,6 +22,9 @@ public class FastByteArrayOutputStream extends OutputStream {
private final FastByteBuffer buffer; private final FastByteBuffer buffer;
/**
* 构造
*/
public FastByteArrayOutputStream() { public FastByteArrayOutputStream() {
this(1024); this(1024);
} }

View File

@ -40,10 +40,13 @@ public class FastByteBuffer {
private final int minChunkLen; private final int minChunkLen;
public FastByteBuffer() { public FastByteBuffer() {
this.minChunkLen = 1024; this(1024);
} }
public FastByteBuffer(int size) { public FastByteBuffer(int size) {
if(size <= 0){
size = 1024;
}
this.minChunkLen = Math.abs(size); this.minChunkLen = Math.abs(size);
} }

View File

@ -102,7 +102,7 @@ public class IoUtil extends NioUtil {
* @return 传输的byte数 * @return 传输的byte数
* @throws IORuntimeException IO异常 * @throws IORuntimeException IO异常
*/ */
public static long copy(Reader reader, Writer writer, int bufferSize, int count, StreamProgress streamProgress) throws IORuntimeException { public static long copy(Reader reader, Writer writer, int bufferSize, long count, StreamProgress streamProgress) throws IORuntimeException {
return new ReaderWriterCopier(bufferSize, count, streamProgress).copy(reader, writer); return new ReaderWriterCopier(bufferSize, count, streamProgress).copy(reader, writer);
} }
@ -157,7 +157,7 @@ public class IoUtil extends NioUtil {
* @throws IORuntimeException IO异常 * @throws IORuntimeException IO异常
* @since 5.7.8 * @since 5.7.8
*/ */
public static long copy(InputStream in, OutputStream out, int bufferSize, int count, StreamProgress streamProgress) throws IORuntimeException { public static long copy(InputStream in, OutputStream out, int bufferSize, long count, StreamProgress streamProgress) throws IORuntimeException {
return new StreamCopier(bufferSize, count, streamProgress).copy(in, out); return new StreamCopier(bufferSize, count, streamProgress).copy(in, out);
} }

View File

@ -117,6 +117,26 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
return header(Header.CONTENT_ENCODING); return header(Header.CONTENT_ENCODING);
} }
/**
* 获取内容长度以下情况长度无效
* <ul>
* <li>Transfer-Encoding: Chunked</li>
* <li>Content-Encoding: XXX</li>
* </ul>
* 参考https://blog.csdn.net/jiang7701037/article/details/86304302
*
* @return 长度-1表示服务端未返回或长度无效
* @since 5.7.9
*/
public long contentLength() {
long contentLength = Convert.toLong(header(Header.CONTENT_LENGTH), -1L);
if (contentLength > 0 && (isChunked() || StrUtil.isNotBlank(contentEncoding()))) {
//按照HTTP协议规范 Transfer-Encoding和Content-Encoding设置后 Content-Length 无效
contentLength = -1;
}
return contentLength;
}
/** /**
* 是否为gzip压缩过的内容 * 是否为gzip压缩过的内容
* *
@ -254,9 +274,9 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
if (null == out) { if (null == out) {
throw new NullPointerException("[out] is null!"); throw new NullPointerException("[out] is null!");
} }
final int contentLength = Convert.toInt(header(Header.CONTENT_LENGTH), -1); final long contentLength = contentLength();
try { try {
return IoUtil.copyByNIO(bodyStream(), out, IoUtil.DEFAULT_BUFFER_SIZE, contentLength, streamProgress); return copyBody(bodyStream(), out, contentLength, streamProgress);
} finally { } finally {
IoUtil.close(this); IoUtil.close(this);
if (isCloseOut) { if (isCloseOut) {
@ -452,33 +472,6 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
return this.isAsync ? this : forceSync(); return this.isAsync ? this : forceSync();
} }
/**
* 读取主体忽略EOFException异常
*
* @param in 输入流
* @throws IORuntimeException IO异常
*/
private void readBody(InputStream in) throws IORuntimeException {
if (ignoreBody) {
return;
}
final int contentLength = Convert.toInt(header(Header.CONTENT_LENGTH), -1);
final FastByteArrayOutputStream out = contentLength > 0 ?
new FastByteArrayOutputStream(contentLength) : new FastByteArrayOutputStream();
try {
IoUtil.copy(in, out, -1, contentLength, null);
} catch (IORuntimeException e) {
//noinspection StatementWithEmptyBody
if (e.getCause() instanceof EOFException || StrUtil.containsIgnoreCase(e.getMessage(), "Premature EOF")) {
// 忽略读取HTTP流中的EOF错误
} else {
throw e;
}
}
this.bodyBytes = out.toByteArray();
}
/** /**
* 强制同步用于初始化<br> * 强制同步用于初始化<br>
* 强制同步后变化如下 * 强制同步后变化如下
@ -512,6 +505,53 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
return this; return this;
} }
/**
* 读取主体忽略EOFException异常
*
* @param in 输入流
* @throws IORuntimeException IO异常
*/
private void readBody(InputStream in) throws IORuntimeException {
if (ignoreBody) {
return;
}
final long contentLength = contentLength();
final FastByteArrayOutputStream out = new FastByteArrayOutputStream((int)contentLength);
copyBody(in, out, contentLength, null);
this.bodyBytes = out.toByteArray();
}
/**
* 将响应内容写出到{@link OutputStream}<br>
* 异步模式下直接读取Http流写出同步模式下将存储在内存中的响应内容写出<br>
* 写出后会关闭Http流异步模式
*
* @param in 输入流
* @param out 写出的流
* @param contentLength 总长度-1表示未知
* @param streamProgress 进度显示接口通过实现此接口显示下载进度
* @return 拷贝长度
*/
private static long copyBody(InputStream in, OutputStream out, long contentLength, StreamProgress streamProgress) {
if (null == out) {
throw new NullPointerException("[out] is null!");
}
long copyLength = -1;
try {
copyLength = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE, contentLength, streamProgress);
} catch (IORuntimeException e) {
//noinspection StatementWithEmptyBody
if (e.getCause() instanceof EOFException || StrUtil.containsIgnoreCase(e.getMessage(), "Premature EOF")) {
// 忽略读取HTTP流中的EOF错误
} else {
throw e;
}
}
return copyLength;
}
/** /**
* 从Content-Disposition头中获取文件名 * 从Content-Disposition头中获取文件名
* *

View File

@ -19,7 +19,9 @@ public class HttpUtilTest {
@Test @Test
@Ignore @Ignore
public void postTest() { public void postTest() {
String result = HttpUtil.createPost("api.uhaozu.com/goods/description/1120448506").charset(CharsetUtil.UTF_8).execute().body(); String result = HttpUtil.createPost("api.uhaozu.com/goods/description/1120448506")
.charset(CharsetUtil.UTF_8)
.execute().body();
Console.log(result); Console.log(result);
} }
@ -45,6 +47,7 @@ public class HttpUtilTest {
@Test @Test
@Ignore @Ignore
public void getTest2() { public void getTest2() {
// 此链接较为特殊User-Agent去掉后进入一个JS跳转页面如果设置了需要开启302跳转
// 自定义的默认header无效 // 自定义的默认header无效
String result = HttpRequest String result = HttpRequest
.get("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=101457313&redirect_uri=http%3A%2F%2Fwww.benmovip.com%2Fpay-cloud%2Fqqlogin%2FgetCode&state=ok") .get("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=101457313&redirect_uri=http%3A%2F%2Fwww.benmovip.com%2Fpay-cloud%2Fqqlogin%2FgetCode&state=ok")