diff --git a/hutool-core/src/main/java/cn/hutool/core/io/stream/SyncInputStream.java b/hutool-core/src/main/java/cn/hutool/core/io/stream/SyncInputStream.java
new file mode 100755
index 000000000..08b22c990
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/io/stream/SyncInputStream.java
@@ -0,0 +1,112 @@
+package cn.hutool.core.io.stream;
+
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.io.StreamProgress;
+import cn.hutool.core.text.StrUtil;
+
+import java.io.ByteArrayInputStream;
+import java.io.EOFException;
+import java.io.FileNotFoundException;
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * 同步流,可将包装的流同步为ByteArrayInputStream,以便持有内容并关闭原流
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class SyncInputStream extends FilterInputStream {
+
+ private final long length;
+ private final boolean isIgnoreEOFError;
+ /**
+ * 是否异步,异步下只持有流,否则将在初始化时直接读取body内容
+ */
+ private volatile boolean asyncFlag = true;
+
+ /**
+ * 构造
+ * 如果isAsync为{@code true},则直接持有原有流,{@code false},则将流中内容,按照给定length读到{@link ByteArrayInputStream}中备用
+ *
+ * @param in 数据流
+ * @param length 限定长度,-1表示未知长度
+ * @param isAsync 是否异步
+ * @param isIgnoreEOFError 是否忽略EOF错误,在Http协议中,对于Transfer-Encoding: Chunked在正常情况下末尾会写入一个Length为0的的chunk标识完整结束
+ * 如果服务端未遵循这个规范或响应没有正常结束,会报EOF异常,此选项用于是否忽略这个异常。
+ */
+ public SyncInputStream(final InputStream in, final long length, final boolean isAsync, final boolean isIgnoreEOFError) {
+ super(in);
+ this.length = length;
+ this.isIgnoreEOFError = isIgnoreEOFError;
+ if (false == isAsync) {
+ sync();
+ }
+ }
+
+ /**
+ * 同步数据到内存
+ */
+ public void sync() {
+ if (false == asyncFlag) {
+ // 已经是同步模式
+ return;
+ }
+
+ this.in = new ByteArrayInputStream(readBytes());
+ this.asyncFlag = false;
+ }
+
+ /**
+ * 读取流中所有bytes
+ *
+ * @return bytes
+ */
+ public byte[] readBytes() {
+ final FastByteArrayOutputStream bytesOut = new FastByteArrayOutputStream(length > 0 ? (int)length : 1024);
+ final long length = copyTo(bytesOut, null);
+ return length > 0 ? bytesOut.toByteArray() : new byte[0];
+ }
+
+ /**
+ * 将流的内容拷贝到输出流
+ * @param out 输出流
+ * @param streamProgress 进度条
+ * @return 拷贝长度
+ */
+ public long copyTo(final OutputStream out, final StreamProgress streamProgress){
+ long copyLength = -1;
+ try {
+ copyLength = IoUtil.copy(this.in, out, IoUtil.DEFAULT_BUFFER_SIZE, this.length, streamProgress);
+ } catch (final IORuntimeException e) {
+ if (false == (isIgnoreEOFError && isEOFException(e.getCause()))) {
+ throw e;
+ }
+ // 忽略读取流中的EOF错误
+ }finally {
+ // 读取结束
+ IoUtil.close(in);
+ }
+ return copyLength;
+ }
+
+ /**
+ * 是否为EOF异常,包括
+ *
+ * - FileNotFoundException:服务端无返回内容
+ * - EOFException:EOF异常
+ *
+ *
+ * @param e 异常
+ * @return 是否EOF异常
+ */
+ private static boolean isEOFException(final Throwable e) {
+ if (e instanceof FileNotFoundException) {
+ // 服务器无返回内容,忽略之
+ return true;
+ }
+ return e instanceof EOFException || StrUtil.containsIgnoreCase(e.getMessage(), "Premature EOF");
+ }
+}
diff --git a/hutool-http/pom.xml b/hutool-http/pom.xml
index d4d2c26ef..b3e67831f 100755
--- a/hutool-http/pom.xml
+++ b/hutool-http/pom.xml
@@ -73,5 +73,11 @@
1.7.25
test
+
+ com.squareup.okhttp3
+ mockwebserver
+ 4.10.0
+ test
+
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java b/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java
index c6852b90f..03aa7d0d0 100755
--- a/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java
+++ b/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java
@@ -704,7 +704,7 @@ public class HttpUtil {
* @param conn HTTP连接对象
* @return 字符集
*/
- public static String getCharset(final HttpURLConnection conn) {
+ public static Charset getCharset(final HttpURLConnection conn) {
if (conn == null) {
return null;
}
@@ -719,7 +719,19 @@ public class HttpUtil {
* @return 字符集
* @since 5.2.6
*/
- public static String getCharset(final String contentType) {
+ public static Charset getCharset(final String contentType) {
+ return CharsetUtil.parse(getCharsetName(contentType), null);
+ }
+
+ /**
+ * 从Http连接的头信息中获得字符集
+ * 从ContentType中获取
+ *
+ * @param contentType Content-Type
+ * @return 字符集
+ * @since 5.2.6
+ */
+ public static String getCharsetName(final String contentType) {
if (StrUtil.isBlank(contentType)) {
return null;
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/Response.java b/hutool-http/src/main/java/cn/hutool/http/client/Response.java
index b6e74878d..b8c59c4b6 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/Response.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/Response.java
@@ -1,7 +1,6 @@
package cn.hutool.http.client;
import cn.hutool.core.convert.Convert;
-import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpUtil;
@@ -37,11 +36,13 @@ public interface Response extends Closeable {
String header(final String name);
/**
- * 获取字符集编码
+ * 获取字符集编码,默认为响应头中的编码
*
* @return 字符集
*/
- Charset charset();
+ default Charset charset(){
+ return HttpUtil.getCharset(header(Header.CONTENT_TYPE));
+ }
/**
* 获得服务区响应流
@@ -56,7 +57,7 @@ public interface Response extends Closeable {
* @return {@link ResponseBody}
*/
default ResponseBody body(){
- return new ResponseBody(this, true);
+ return new ResponseBody(this, bodyStream(), false, true);
}
/**
@@ -76,7 +77,7 @@ public interface Response extends Closeable {
* @return byte[]
*/
default byte[] bodyBytes() {
- return IoUtil.readBytes(bodyStream());
+ return body().getBytes();
}
/**
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/body/ResponseBody.java b/hutool-http/src/main/java/cn/hutool/http/client/body/ResponseBody.java
index 3a2510771..ae5d3f3a8 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/body/ResponseBody.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/body/ResponseBody.java
@@ -1,10 +1,10 @@
package cn.hutool.http.client.body;
import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.io.file.FileNameUtil;
+import cn.hutool.core.io.stream.SyncInputStream;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.regex.ReUtil;
import cn.hutool.core.text.StrUtil;
@@ -13,8 +13,9 @@ import cn.hutool.http.HttpException;
import cn.hutool.http.client.Response;
import cn.hutool.http.meta.Header;
-import java.io.EOFException;
+import java.io.Closeable;
import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -23,25 +24,25 @@ import java.io.OutputStream;
*
* @author looly
*/
-public class ResponseBody implements HttpBody {
+public class ResponseBody implements HttpBody, Closeable {
private final Response response;
/**
- * 是否忽略响应读取时可能的EOF异常。
- * 在Http协议中,对于Transfer-Encoding: Chunked在正常情况下末尾会写入一个Length为0的的chunk标识完整结束。
- * 如果服务端未遵循这个规范或响应没有正常结束,会报EOF异常,此选项用于是否忽略这个异常。
+ * Http请求原始流
*/
- private final boolean isIgnoreEOFError;
+ private final SyncInputStream bodyStream;
/**
* 构造
*
* @param response 响应体
+ * @param in HTTP主体响应流
+ * @param isAsync 是否异步模式
* @param isIgnoreEOFError 是否忽略EOF错误
*/
- public ResponseBody(final Response response, final boolean isIgnoreEOFError) {
+ public ResponseBody(final Response response, final InputStream in, final boolean isAsync, final boolean isIgnoreEOFError) {
this.response = response;
- this.isIgnoreEOFError = isIgnoreEOFError;
+ this.bodyStream = new SyncInputStream(in, response.contentLength(), isAsync, isIgnoreEOFError);
}
@Override
@@ -51,7 +52,7 @@ public class ResponseBody implements HttpBody {
@Override
public InputStream getStream() {
- return response.bodyStream();
+ return this.bodyStream;
}
@Override
@@ -59,6 +60,25 @@ public class ResponseBody implements HttpBody {
write(out, false, null);
}
+ /**
+ * 同步数据到内存,以bytes形式存储
+ *
+ * @return this
+ */
+ public ResponseBody sync() {
+ this.bodyStream.sync();
+ return this;
+ }
+
+ /**
+ * 获取响应内容的bytes
+ *
+ * @return 响应内容bytes
+ */
+ public byte[] getBytes() {
+ return this.bodyStream.readBytes();
+ }
+
/**
* 将响应内容写出到{@link OutputStream}
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出
@@ -72,9 +92,8 @@ public class ResponseBody implements HttpBody {
*/
public long write(final OutputStream out, final boolean isCloseOut, final StreamProgress streamProgress) {
Assert.notNull(out, "[out] must be not null!");
- final long contentLength = response.contentLength();
try {
- return copyBody(getStream(), out, contentLength, streamProgress, isIgnoreEOFError);
+ return this.bodyStream.copyTo(out, streamProgress);
} finally {
if (isCloseOut) {
IoUtil.close(out);
@@ -165,6 +184,11 @@ public class ResponseBody implements HttpBody {
return outFile;
}
+ @Override
+ public void close() throws IOException {
+ this.bodyStream.close();
+ }
+
// region ---------------------------------------------------------------------------- Private Methods
/**
@@ -207,37 +231,5 @@ public class ResponseBody implements HttpBody {
}
return fileName;
}
-
- /**
- * 将响应内容写出到{@link OutputStream}
- * 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出
- * 写出后会关闭Http流(异步模式)
- *
- * @param in 输入流
- * @param out 写出的流
- * @param contentLength 总长度,-1表示未知
- * @param streamProgress 进度显示接口,通过实现此接口显示下载进度
- * @param isIgnoreEOFError 是否忽略响应读取时可能的EOF异常
- * @return 拷贝长度
- */
- private static long copyBody(final InputStream in, final OutputStream out, final long contentLength, final StreamProgress streamProgress, final boolean isIgnoreEOFError) {
- 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 (final IORuntimeException e) {
- //noinspection StatementWithEmptyBody
- if (isIgnoreEOFError
- && (e.getCause() instanceof EOFException || StrUtil.containsIgnoreCase(e.getMessage(), "Premature EOF"))) {
- // 忽略读取HTTP流中的EOF错误
- } else {
- throw e;
- }
- }
- return copyLength;
- }
// endregion ---------------------------------------------------------------------------- Private Methods
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Response.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Response.java
index 562ed9be6..cea125b19 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Response.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Response.java
@@ -8,7 +8,6 @@ import cn.hutool.http.client.Response;
import org.apache.http.Header;
import org.apache.http.ParseException;
import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.entity.ContentType;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
@@ -67,8 +66,7 @@ public class HttpClient4Response implements Response {
@Override
public Charset charset() {
- final Charset charset = ContentType.parse(rawRes.getEntity().getContentType().getValue()).getCharset();
- return ObjUtil.defaultIfNull(charset, requestCharset);
+ return ObjUtil.defaultIfNull(Response.super.charset(), requestCharset);
}
@Override
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Response.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Response.java
index d9cffc62f..3b48847f0 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Response.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Response.java
@@ -6,7 +6,6 @@ import cn.hutool.core.util.ObjUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.client.Response;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
-import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
@@ -67,8 +66,7 @@ public class HttpClient5Response implements Response {
@Override
public Charset charset() {
- final Charset charset = ContentType.parse(rawRes.getEntity().getContentType()).getCharset();
- return ObjUtil.defaultIfNull(charset, requestCharset);
+ return ObjUtil.defaultIfNull(Response.super.charset(), requestCharset);
}
@Override
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpConnection.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpConnection.java
index 956837980..6d4072780 100644
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpConnection.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpConnection.java
@@ -5,7 +5,6 @@ import cn.hutool.core.reflect.FieldUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.http.HttpException;
-import cn.hutool.http.HttpUtil;
import cn.hutool.http.client.HeaderOperation;
import cn.hutool.http.meta.Method;
import cn.hutool.http.ssl.DefaultSSLInfo;
@@ -20,8 +19,6 @@ import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.Proxy;
import java.net.URL;
-import java.nio.charset.Charset;
-import java.nio.charset.UnsupportedCharsetException;
import java.util.List;
import java.util.Map;
@@ -403,38 +400,6 @@ public class HttpConnection implements HeaderOperation {
return 0;
}
- /**
- * 获得字符集编码
- * 从Http连接的头信息中获得字符集
- * 从ContentType中获取
- *
- * @return 字符集编码
- */
- public String getCharsetName() {
- return HttpUtil.getCharset(conn);
- }
-
- /**
- * 获取字符集编码
- * 从Http连接的头信息中获得字符集
- * 从ContentType中获取
- *
- * @return {@link Charset}编码
- * @since 3.0.9
- */
- public Charset getCharset() {
- Charset charset = null;
- final String charsetName = getCharsetName();
- if (StrUtil.isNotBlank(charsetName)) {
- try {
- charset = Charset.forName(charsetName);
- } catch (final UnsupportedCharsetException e) {
- // ignore
- }
- }
- return charset;
- }
-
@Override
public String toString() {
final StringBuilder sb = StrUtil.builder();
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpRequest.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpRequest.java
index feb0ac0c2..d3500251d 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpRequest.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpRequest.java
@@ -50,6 +50,7 @@ import java.util.function.Function;
*
* @author Looly
*/
+@Deprecated
public class HttpRequest extends HttpBase {
// ---------------------------------------------------------------- static Http Method start
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpResponse.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpResponse.java
index c27da6469..5240a651a 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpResponse.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpResponse.java
@@ -1,22 +1,24 @@
package cn.hutool.http.client.engine.jdk;
-import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.io.stream.FastByteArrayOutputStream;
+import cn.hutool.core.io.stream.EmptyInputStream;
import cn.hutool.core.text.StrUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.client.Response;
import cn.hutool.http.client.body.ResponseBody;
import cn.hutool.http.client.cookie.GlobalCookieManager;
-import java.io.ByteArrayInputStream;
import java.io.Closeable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpCookie;
import java.nio.charset.Charset;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
/**
@@ -25,55 +27,48 @@ import java.util.Map.Entry;
*
* @author Looly
*/
-public class HttpResponse extends HttpBase implements Response, Closeable {
+public class HttpResponse implements Response, Closeable {
+
+ /**
+ * 请求时的默认编码
+ */
+ private final Charset requestCharset;
+ /**
+ * 响应内容体,{@code null} 表示无内容
+ */
+ private ResponseBody body;
+ private Map> headers;
/**
* 是否忽略响应读取时可能的EOF异常。
* 在Http协议中,对于Transfer-Encoding: Chunked在正常情况下末尾会写入一个Length为0的的chunk标识完整结束。
* 如果服务端未遵循这个规范或响应没有正常结束,会报EOF异常,此选项用于是否忽略这个异常。
*/
- protected boolean ignoreEOFError;
+ private final boolean ignoreEOFError;
+
/**
* 持有连接对象
*/
protected HttpConnection httpConnection;
- /**
- * Http请求原始流
- */
- protected InputStream in;
- /**
- * 是否异步,异步下只持有流,否则将在初始化时直接读取body内容
- */
- private volatile boolean isAsync;
/**
* 响应状态码
*/
protected int status;
- /**
- * 是否忽略读取Http响应体
- */
- private final boolean ignoreBody;
- /**
- * 从响应中获取的编码
- */
- private Charset charsetFromResponse;
/**
* 构造
*
* @param httpConnection {@link HttpConnection}
* @param ignoreEOFError 是否忽略响应读取时可能的EOF异常
- * @param charset 编码,从请求编码中获取默认编码
+ * @param requestCharset 编码,从请求编码中获取默认编码
* @param isAsync 是否异步
* @param isIgnoreBody 是否忽略读取响应体
*/
- protected HttpResponse(final HttpConnection httpConnection, final boolean ignoreEOFError, final Charset charset, final boolean isAsync, final boolean isIgnoreBody) {
+ protected HttpResponse(final HttpConnection httpConnection, final boolean ignoreEOFError, final Charset requestCharset, final boolean isAsync, final boolean isIgnoreBody) {
this.httpConnection = httpConnection;
this.ignoreEOFError = ignoreEOFError;
- this.charset = charset;
- this.isAsync = isAsync;
- this.ignoreBody = isIgnoreBody;
- initWithDisconnect();
+ this.requestCharset = requestCharset;
+ init(isAsync, isIgnoreBody);
}
/**
@@ -86,6 +81,29 @@ public class HttpResponse extends HttpBase implements Response, Cl
return this.status;
}
+ @Override
+ public String header(final String name) {
+ final List headerValues = this.headers.get(name);
+ if (ArrayUtil.isNotEmpty(headerValues)) {
+ return headerValues.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * 获取headers
+ *
+ * @return Headers Map
+ */
+ public Map> headers() {
+ return Collections.unmodifiableMap(headers);
+ }
+
+ @Override
+ public Charset charset() {
+ return ObjUtil.defaultIfNull(Response.super.charset(), requestCharset);
+ }
+
/**
* 同步
* 如果为异步状态,则暂时不读取服务器中响应的内容,而是持有Http链接的{@link InputStream}。
@@ -94,7 +112,11 @@ public class HttpResponse extends HttpBase implements Response, Cl
* @return this
*/
public HttpResponse sync() {
- return this.isAsync ? forceSync() : this;
+ if (null != this.body) {
+ this.body.sync();
+ }
+ close();
+ return this;
}
// ---------------------------------------------------------------- Http Response Header start
@@ -153,15 +175,13 @@ public class HttpResponse extends HttpBase implements Response, Cl
*/
@Override
public InputStream bodyStream() {
- if (isAsync) {
- return this.in;
- }
- return new ByteArrayInputStream(this.bodyBytes);
+ // 使用ResponseBody中的stream有利于控制响应数据的同步与否
+ return null == this.body ? EmptyInputStream.INSTANCE : this.body.getStream();
}
@Override
public ResponseBody body() {
- return new ResponseBody(this, this.ignoreEOFError);
+ return this.body;
}
/**
@@ -174,30 +194,14 @@ public class HttpResponse extends HttpBase implements Response, Cl
@Override
public byte[] bodyBytes() {
sync();
- return this.bodyBytes;
- }
-
- /**
- * 设置主体字节码,一般用于拦截器修改响应内容
- * 需在此方法调用前使用charset方法设置编码,否则使用默认编码UTF-8
- *
- * @param bodyBytes 主体
- * @return this
- */
- @SuppressWarnings("resource")
- public HttpResponse body(final byte[] bodyBytes) {
- sync();
- if (null != bodyBytes) {
- this.bodyBytes = bodyBytes;
- }
- return this;
+ return body().getBytes();
}
// ---------------------------------------------------------------- Body end
@Override
public void close() {
- IoUtil.close(this.in);
- this.in = null;
+ // 关闭流
+ IoUtil.close(this.body);
// 关闭连接
this.httpConnection.disconnectQuietly();
}
@@ -211,39 +215,13 @@ public class HttpResponse extends HttpBase implements Response, Cl
}
sb.append("Response Body: ").append(StrUtil.CRLF);
- sb.append(" ").append(this.body()).append(StrUtil.CRLF);
+ sb.append(" ").append(this.bodyStr()).append(StrUtil.CRLF);
return sb.toString();
}
- @Override
- public String header(final String name) {
- return super.header(name);
- }
-
// ---------------------------------------------------------------- Private method start
- /**
- * 初始化Http响应,并在报错时关闭连接。
- * 初始化包括:
- *
- *
- * 1、读取Http状态
- * 2、读取头信息
- * 3、持有Http流,并不关闭流
- *
- *
- * @throws HttpException IO异常
- */
- private void initWithDisconnect() throws HttpException {
- try {
- init();
- } catch (final HttpException e) {
- this.httpConnection.disconnectQuietly();
- throw e;
- }
- }
-
/**
* 初始化Http响应
* 初始化包括:
@@ -257,7 +235,7 @@ public class HttpResponse extends HttpBase implements Response, Cl
* @throws HttpException IO异常
*/
@SuppressWarnings("resource")
- private void init() throws HttpException {
+ private void init(final boolean isAsync, final boolean isIgnoreBody) throws HttpException {
// 获取响应状态码
try {
this.status = httpConnection.getCode();
@@ -276,73 +254,13 @@ public class HttpResponse extends HttpBase implements Response, Cl
// StaticLog.warn(e, e.getMessage());
}
-
// 存储服务端设置的Cookie信息
GlobalCookieManager.store(httpConnection, this.headers);
- // 获取响应编码,如果非空,替换用户定义的编码
- final Charset charsetFromResponse = httpConnection.getCharset();
- if (null != charsetFromResponse) {
- this.charset = charsetFromResponse;
- }
-
// 获取响应内容流
- this.in = new HttpInputStream(this);
-
- // 同步情况下强制同步
- if (!this.isAsync) {
- forceSync();
+ if (false == isIgnoreBody) {
+ this.body = new ResponseBody(this, new HttpInputStream(this), isAsync, this.ignoreEOFError);
}
}
-
- /**
- * 强制同步,用于初始化
- * 强制同步后变化如下:
- *
- *
- * 1、读取body内容到内存
- * 2、异步状态设为false(变为同步状态)
- * 3、关闭Http流
- * 4、断开与服务器连接
- *
- *
- * @return this
- */
- private HttpResponse forceSync() {
- // 非同步状态转为同步状态
- try {
- this.readBody(this.in);
- } catch (final IORuntimeException e) {
- //noinspection StatementWithEmptyBody
- if (e.getCause() instanceof FileNotFoundException) {
- // 服务器无返回内容,忽略之
- } else {
- throw new HttpException(e);
- }
- } finally {
- if (this.isAsync) {
- this.isAsync = false;
- }
- this.close();
- }
- return this;
- }
-
- /**
- * 读取主体,忽略EOFException异常
- *
- * @param in 输入流
- * @throws IORuntimeException IO异常
- */
- private void readBody(final InputStream in) throws IORuntimeException {
- if (ignoreBody) {
- return;
- }
-
- final long contentLength = contentLength();
- final FastByteArrayOutputStream out = new FastByteArrayOutputStream((int) contentLength);
- body().writeClose(out);
- this.bodyBytes = out.toByteArray();
- }
// ---------------------------------------------------------------- Private method end
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpResponse.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpResponse.java
index 228dcd7ab..5e1808f85 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpResponse.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpResponse.java
@@ -1,11 +1,8 @@
package cn.hutool.http.client.engine.okhttp;
import cn.hutool.core.io.stream.EmptyInputStream;
-import cn.hutool.core.text.StrUtil;
-import cn.hutool.core.util.CharsetUtil;
-import cn.hutool.http.HttpUtil;
+import cn.hutool.core.util.ObjUtil;
import cn.hutool.http.client.Response;
-import cn.hutool.http.meta.Header;
import okhttp3.ResponseBody;
import java.io.IOException;
@@ -46,12 +43,7 @@ public class OkHttpResponse implements Response {
@Override
public Charset charset() {
- final String contentType = rawRes.header(Header.CONTENT_TYPE.getValue());
- if(StrUtil.isNotEmpty(contentType)){
- final String charset = HttpUtil.getCharset(contentType);
- CharsetUtil.parse(charset, this.requestCharset);
- }
- return this.requestCharset;
+ return ObjUtil.defaultIfNull(Response.super.charset(), requestCharset);
}
@Override
diff --git a/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java b/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java
index 4f17a9557..9cec2c3d9 100644
--- a/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java
+++ b/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java
@@ -11,6 +11,7 @@ import cn.hutool.core.net.multipart.UploadSetting;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.ObjUtil;
import cn.hutool.http.meta.Header;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.meta.Method;
@@ -169,8 +170,7 @@ public class HttpServerRequest extends HttpServerBase {
public Charset getCharset() {
if(null == this.charsetCache){
final String contentType = getContentType();
- final String charsetStr = HttpUtil.getCharset(contentType);
- this.charsetCache = CharsetUtil.parse(charsetStr, DEFAULT_CHARSET);
+ this.charsetCache = ObjUtil.defaultIfNull(HttpUtil.getCharset(contentType), DEFAULT_CHARSET);
}
return this.charsetCache;
diff --git a/hutool-http/src/test/java/cn/hutool/http/MockServerTest.java b/hutool-http/src/test/java/cn/hutool/http/MockServerTest.java
new file mode 100755
index 000000000..a419b6567
--- /dev/null
+++ b/hutool-http/src/test/java/cn/hutool/http/MockServerTest.java
@@ -0,0 +1,16 @@
+package cn.hutool.http;
+
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+
+import java.io.IOException;
+
+public class MockServerTest {
+ public static void main(final String[] args) throws IOException {
+ //noinspection resource
+ final MockWebServer server = new MockWebServer();
+ final MockResponse mockResponse = new MockResponse().setBody("hello, world!");
+ server.enqueue(mockResponse);
+ server.start(8080);
+ }
+}
diff --git a/hutool-http/src/test/java/cn/hutool/http/client/JdkEngineTest.java b/hutool-http/src/test/java/cn/hutool/http/client/JdkEngineTest.java
new file mode 100755
index 000000000..0e69f5155
--- /dev/null
+++ b/hutool-http/src/test/java/cn/hutool/http/client/JdkEngineTest.java
@@ -0,0 +1,22 @@
+package cn.hutool.http.client;
+
+import cn.hutool.core.lang.Console;
+import cn.hutool.http.client.engine.jdk.JdkClientEngine;
+import cn.hutool.http.meta.Method;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class JdkEngineTest {
+
+ @Test
+ @Ignore
+ public void getTest(){
+ final ClientEngine engine = new JdkClientEngine();
+
+ final Request req = Request.of("https://www.hutool.cn/").method(Method.GET);
+ final Response res = engine.send(req);
+
+ Console.log(res.getStatus());
+ Console.log(res.bodyStr());
+ }
+}