diff --git a/hutool-core/src/main/java/cn/hutool/core/io/EmptyInputStream.java b/hutool-core/src/main/java/cn/hutool/core/io/EmptyInputStream.java
new file mode 100755
index 000000000..d13c53e43
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/io/EmptyInputStream.java
@@ -0,0 +1,61 @@
+package cn.hutool.core.io;
+
+import java.io.InputStream;
+
+/**
+ * 空的流
+ *
+ * @author looly
+ */
+@SuppressWarnings("NullableProblems")
+public final class EmptyInputStream extends InputStream {
+ /**
+ * 单例实例
+ */
+ public static final EmptyInputStream INSTANCE = new EmptyInputStream();
+
+ private EmptyInputStream() {
+ }
+
+ @Override
+ public int available() {
+ return 0;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void mark(final int readLimit) {
+ }
+
+ @Override
+ public boolean markSupported() {
+ return true;
+ }
+
+ @Override
+ public int read() {
+ return -1;
+ }
+
+ @Override
+ public int read(final byte[] buf) {
+ return -1;
+ }
+
+ @Override
+ public int read(final byte[] buf, final int off, final int len) {
+ return -1;
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ public long skip(final long n) {
+ return 0L;
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpResource.java b/hutool-core/src/main/java/cn/hutool/core/io/resource/HttpResource.java
similarity index 90%
rename from hutool-http/src/main/java/cn/hutool/http/HttpResource.java
rename to hutool-core/src/main/java/cn/hutool/core/io/resource/HttpResource.java
index 3a3c2121b..cd24f976b 100644
--- a/hutool-http/src/main/java/cn/hutool/http/HttpResource.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/resource/HttpResource.java
@@ -1,6 +1,5 @@
-package cn.hutool.http;
+package cn.hutool.core.io.resource;
-import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.lang.Assert;
import java.io.InputStream;
@@ -8,7 +7,7 @@ import java.io.Serializable;
import java.net.URL;
/**
- * HTTP资源,可自定义Content-Type
+ * HTTP资源,用于自定义表单数据,可自定义Content-Type
*
* @author looly
* @since 5.7.17
diff --git a/hutool-http/pom.xml b/hutool-http/pom.xml
index 5c7d4eba2..0959c5227 100755
--- a/hutool-http/pom.xml
+++ b/hutool-http/pom.xml
@@ -22,6 +22,14 @@
hutool-core
${project.parent.version}
+
+ javax.xml.soap
+ javax.xml.soap-api
+ 1.4.0
+ provided
+
+
+
org.apache.httpcomponents.client5
httpclient5
@@ -29,11 +37,18 @@
provided
- javax.xml.soap
- javax.xml.soap-api
- 1.4.0
+ org.apache.httpcomponents
+ httpclient
+ 4.5.13
provided
+
+ com.squareup.okhttp3
+ okhttp
+ 4.10.0
+ provided
+
+
cn.hutool
hutool-json
@@ -46,5 +61,12 @@
0.1.2
test
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.25
+ test
+
diff --git a/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java b/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java
index f3e6d539b..382c3f827 100644
--- a/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java
+++ b/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java
@@ -3,6 +3,8 @@ package cn.hutool.http;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrUtil;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.meta.Header;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/hutool-http/src/main/java/cn/hutool/http/GlobalInterceptor.java b/hutool-http/src/main/java/cn/hutool/http/GlobalInterceptor.java
index 387e44932..f988379a9 100755
--- a/hutool-http/src/main/java/cn/hutool/http/GlobalInterceptor.java
+++ b/hutool-http/src/main/java/cn/hutool/http/GlobalInterceptor.java
@@ -1,5 +1,9 @@
package cn.hutool.http;
+import cn.hutool.http.client.engine.jdk.HttpInterceptor;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.client.engine.jdk.HttpResponse;
+
/**
* 全局的拦截器
* 包括请求拦截器和响应拦截器
@@ -69,7 +73,7 @@ public enum GlobalInterceptor {
/**
* 复制请求过滤器列表
*
- * @return {@link cn.hutool.http.HttpInterceptor.Chain}
+ * @return {@link HttpInterceptor.Chain}
*/
HttpInterceptor.Chain getCopiedRequestInterceptor() {
final HttpInterceptor.Chain copied = new HttpInterceptor.Chain<>();
@@ -82,7 +86,7 @@ public enum GlobalInterceptor {
/**
* 复制响应过滤器列表
*
- * @return {@link cn.hutool.http.HttpInterceptor.Chain}
+ * @return {@link HttpInterceptor.Chain}
*/
HttpInterceptor.Chain getCopiedResponseInterceptor() {
final HttpInterceptor.Chain copied = new HttpInterceptor.Chain<>();
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpConfig.java b/hutool-http/src/main/java/cn/hutool/http/HttpConfig.java
index ee3f2e8fd..a4d5aa09b 100755
--- a/hutool-http/src/main/java/cn/hutool/http/HttpConfig.java
+++ b/hutool-http/src/main/java/cn/hutool/http/HttpConfig.java
@@ -2,6 +2,9 @@ package cn.hutool.http;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.net.ssl.SSLUtil;
+import cn.hutool.http.client.engine.jdk.HttpInterceptor;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.client.engine.jdk.HttpResponse;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
@@ -28,40 +31,40 @@ public class HttpConfig {
/**
* 默认连接超时
*/
- int connectionTimeout = HttpGlobalConfig.getTimeout();
+ public int connectionTimeout = HttpGlobalConfig.getTimeout();
/**
* 默认读取超时
*/
- int readTimeout = HttpGlobalConfig.getTimeout();
+ public int readTimeout = HttpGlobalConfig.getTimeout();
/**
* 是否禁用缓存
*/
- boolean isDisableCache;
+ public boolean isDisableCache;
/**
* 最大重定向次数
*/
- int maxRedirectCount = HttpGlobalConfig.getMaxRedirectCount();
+ public int maxRedirectCount = HttpGlobalConfig.getMaxRedirectCount();
/**
* 代理
*/
- Proxy proxy;
+ public Proxy proxy;
/**
* HostnameVerifier,用于HTTPS安全连接
*/
- HostnameVerifier hostnameVerifier;
+ public HostnameVerifier hostnameVerifier;
/**
* SSLSocketFactory,用于HTTPS安全连接
*/
- SSLSocketFactory ssf;
+ public SSLSocketFactory ssf;
/**
* Chuncked块大小,0或小于0表示不设置Chuncked模式
*/
- int blockSize;
+ public int blockSize;
/**
* 获取是否忽略响应读取时可能的EOF异常。
@@ -79,16 +82,16 @@ public class HttpConfig {
/**
* 请求前的拦截器,用于在请求前重新编辑请求
*/
- final HttpInterceptor.Chain requestInterceptors = GlobalInterceptor.INSTANCE.getCopiedRequestInterceptor();
+ public final HttpInterceptor.Chain requestInterceptors = GlobalInterceptor.INSTANCE.getCopiedRequestInterceptor();
/**
* 响应后的拦截器,用于在响应后处理逻辑
*/
- final HttpInterceptor.Chain responseInterceptors = GlobalInterceptor.INSTANCE.getCopiedResponseInterceptor();
+ public final HttpInterceptor.Chain responseInterceptors = GlobalInterceptor.INSTANCE.getCopiedResponseInterceptor();
/**
* 重定向时是否使用拦截器
*/
- boolean interceptorOnRedirect;
+ public boolean interceptorOnRedirect;
/**
* 设置超时,单位:毫秒
@@ -297,4 +300,13 @@ public class HttpConfig {
this.interceptorOnRedirect = interceptorOnRedirect;
return this;
}
+
+ /**
+ * 获取是否忽略响应读取时可能的EOF异常。
+ *
+ * @return 是否忽略响应读取时可能的EOF异常。
+ */
+ public boolean isIgnoreEOFError() {
+ return ignoreEOFError;
+ }
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpGlobalConfig.java b/hutool-http/src/main/java/cn/hutool/http/HttpGlobalConfig.java
index 026348596..7706f94e9 100755
--- a/hutool-http/src/main/java/cn/hutool/http/HttpGlobalConfig.java
+++ b/hutool-http/src/main/java/cn/hutool/http/HttpGlobalConfig.java
@@ -3,7 +3,7 @@ package cn.hutool.http;
import cn.hutool.core.reflect.FieldUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.RandomUtil;
-import cn.hutool.http.cookie.GlobalCookieManager;
+import cn.hutool.http.client.cookie.GlobalCookieManager;
import java.io.Serializable;
import java.lang.reflect.Field;
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpStatus.java b/hutool-http/src/main/java/cn/hutool/http/HttpStatus.java
deleted file mode 100644
index a020828c6..000000000
--- a/hutool-http/src/main/java/cn/hutool/http/HttpStatus.java
+++ /dev/null
@@ -1,222 +0,0 @@
-package cn.hutool.http;
-
-/**
- * HTTP状态码
- *
- * @author Looly
- * @see java.net.HttpURLConnection
- *
- */
-public class HttpStatus {
-
- /* 2XX: generally "OK" */
-
- /**
- * HTTP Status-Code 200: OK.
- */
- public static final int HTTP_OK = 200;
-
- /**
- * HTTP Status-Code 201: Created.
- */
- public static final int HTTP_CREATED = 201;
-
- /**
- * HTTP Status-Code 202: Accepted.
- */
- public static final int HTTP_ACCEPTED = 202;
-
- /**
- * HTTP Status-Code 203: Non-Authoritative Information.
- */
- public static final int HTTP_NOT_AUTHORITATIVE = 203;
-
- /**
- * HTTP Status-Code 204: No Content.
- */
- public static final int HTTP_NO_CONTENT = 204;
-
- /**
- * HTTP Status-Code 205: Reset Content.
- */
- public static final int HTTP_RESET = 205;
-
- /**
- * HTTP Status-Code 206: Partial Content.
- */
- public static final int HTTP_PARTIAL = 206;
-
- /* 3XX: relocation/redirect */
-
- /**
- * HTTP Status-Code 300: Multiple Choices.
- */
- public static final int HTTP_MULT_CHOICE = 300;
-
- /**
- * HTTP Status-Code 301: Moved Permanently.
- */
- public static final int HTTP_MOVED_PERM = 301;
-
- /**
- * HTTP Status-Code 302: Temporary Redirect.
- */
- public static final int HTTP_MOVED_TEMP = 302;
-
- /**
- * HTTP Status-Code 303: See Other.
- */
- public static final int HTTP_SEE_OTHER = 303;
-
- /**
- * HTTP Status-Code 304: Not Modified.
- */
- public static final int HTTP_NOT_MODIFIED = 304;
-
- /**
- * HTTP Status-Code 305: Use Proxy.
- */
- public static final int HTTP_USE_PROXY = 305;
-
- /**
- * HTTP 1.1 Status-Code 307: Temporary Redirect.
- * 见:RFC-7231
- */
- public static final int HTTP_TEMP_REDIRECT = 307;
-
- /**
- * HTTP 1.1 Status-Code 308: Permanent Redirect 永久重定向
- * 见:RFC-7231
- */
- public static final int HTTP_PERMANENT_REDIRECT = 308;
-
- /* 4XX: client error */
-
- /**
- * HTTP Status-Code 400: Bad Request.
- */
- public static final int HTTP_BAD_REQUEST = 400;
-
- /**
- * HTTP Status-Code 401: Unauthorized.
- */
- public static final int HTTP_UNAUTHORIZED = 401;
-
- /**
- * HTTP Status-Code 402: Payment Required.
- */
- public static final int HTTP_PAYMENT_REQUIRED = 402;
-
- /**
- * HTTP Status-Code 403: Forbidden.
- */
- public static final int HTTP_FORBIDDEN = 403;
-
- /**
- * HTTP Status-Code 404: Not Found.
- */
- public static final int HTTP_NOT_FOUND = 404;
-
- /**
- * HTTP Status-Code 405: Method Not Allowed.
- */
- public static final int HTTP_BAD_METHOD = 405;
-
- /**
- * HTTP Status-Code 406: Not Acceptable.
- */
- public static final int HTTP_NOT_ACCEPTABLE = 406;
-
- /**
- * HTTP Status-Code 407: Proxy Authentication Required.
- */
- public static final int HTTP_PROXY_AUTH = 407;
-
- /**
- * HTTP Status-Code 408: Request Time-Out.
- */
- public static final int HTTP_CLIENT_TIMEOUT = 408;
-
- /**
- * HTTP Status-Code 409: Conflict.
- */
- public static final int HTTP_CONFLICT = 409;
-
- /**
- * HTTP Status-Code 410: Gone.
- */
- public static final int HTTP_GONE = 410;
-
- /**
- * HTTP Status-Code 411: Length Required.
- */
- public static final int HTTP_LENGTH_REQUIRED = 411;
-
- /**
- * HTTP Status-Code 412: Precondition Failed.
- */
- public static final int HTTP_PRECON_FAILED = 412;
-
- /**
- * HTTP Status-Code 413: Request Entity Too Large.
- */
- public static final int HTTP_ENTITY_TOO_LARGE = 413;
-
- /**
- * HTTP Status-Code 414: Request-URI Too Large.
- */
- public static final int HTTP_REQ_TOO_LONG = 414;
-
- /**
- * HTTP Status-Code 415: Unsupported Media Type.
- */
- public static final int HTTP_UNSUPPORTED_TYPE = 415;
-
- /* 5XX: server error */
-
- /**
- * HTTP Status-Code 500: Internal Server Error.
- */
- public static final int HTTP_INTERNAL_ERROR = 500;
-
- /**
- * HTTP Status-Code 501: Not Implemented.
- */
- public static final int HTTP_NOT_IMPLEMENTED = 501;
-
- /**
- * HTTP Status-Code 502: Bad Gateway.
- */
- public static final int HTTP_BAD_GATEWAY = 502;
-
- /**
- * HTTP Status-Code 503: Service Unavailable.
- */
- public static final int HTTP_UNAVAILABLE = 503;
-
- /**
- * HTTP Status-Code 504: Gateway Timeout.
- */
- public static final int HTTP_GATEWAY_TIMEOUT = 504;
-
- /**
- * HTTP Status-Code 505: HTTP Version Not Supported.
- */
- public static final int HTTP_VERSION = 505;
-
- /**
- * 是否为重定向状态码
- * @param responseCode 被检查的状态码
- * @return 是否为重定向状态码
- * @since 5.6.3
- */
- public static boolean isRedirected(final int responseCode){
- return responseCode == HTTP_MOVED_PERM
- || responseCode == HTTP_MOVED_TEMP
- || responseCode == HTTP_SEE_OTHER
- // issue#1504@Github,307和308是RFC 7538中http 1.1定义的规范
- || responseCode == HTTP_TEMP_REDIRECT
- || responseCode == HTTP_PERMANENT_REDIRECT;
-
- }
-}
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 70681a590..276fd74ef 100755
--- a/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java
+++ b/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java
@@ -13,7 +13,11 @@ import cn.hutool.core.regex.ReUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjUtil;
-import cn.hutool.http.cookie.GlobalCookieManager;
+import cn.hutool.http.client.HttpDownloader;
+import cn.hutool.http.client.cookie.GlobalCookieManager;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.meta.ContentType;
+import cn.hutool.http.meta.Method;
import cn.hutool.http.server.SimpleServer;
import java.io.File;
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/ClientEngine.java b/hutool-http/src/main/java/cn/hutool/http/client/ClientEngine.java
new file mode 100755
index 000000000..d485f8d61
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/ClientEngine.java
@@ -0,0 +1,27 @@
+package cn.hutool.http.client;
+
+import java.io.Closeable;
+
+/**
+ * HTTP客户端引擎接口,通过不同实现,完成HTTP请求发送
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public interface ClientEngine extends Closeable {
+
+ /**
+ * 发送HTTP请求
+ * @param message HTTP请求消息
+ * @return 响应内容
+ */
+ Response send(Request message);
+
+ /**
+ * 获取原始引擎的钩子方法,用于自定义特殊属性,如插件等
+ *
+ * @return 对应HTTP客户端实现的引擎对象
+ * @since 6.0.0
+ */
+ Object getRawEngine();
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/Headers.java b/hutool-http/src/main/java/cn/hutool/http/client/Headers.java
new file mode 100755
index 000000000..cdc84b9d5
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/Headers.java
@@ -0,0 +1,227 @@
+package cn.hutool.http.client;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.StrUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.http.meta.Header;
+
+import java.net.HttpCookie;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * HTTP请求头的存储和相关方法
+ *
+ * @param 返回对象类型,方便链式编程
+ */
+@SuppressWarnings("unchecked")
+public interface Headers> {
+
+ // region ----------------------------------------------------------- headers
+
+ /**
+ * 获取headers
+ *
+ * @return Headers Map
+ */
+ Map> headers();
+
+ /**
+ * 设置一个header
+ * 如果覆盖模式,则替换之前的值,否则加入到值列表中
+ * 如果给定值为{@code null},则删除这个头信息
+ *
+ * @param name Header名,{@code null}跳过
+ * @param value Header值,{@code null}表示删除name对应的头
+ * @param isOverride 是否覆盖已有值
+ * @return this
+ */
+ T header(final String name, final String value, final boolean isOverride);
+
+ /**
+ * 获取指定的Header值,如果不存在返回{@code null}
+ *
+ * @param header header名
+ * @return header值
+ */
+ default String header(final Header header) {
+ return header(header.name());
+ }
+
+ /**
+ * 获取指定的Header值,如果不存在返回{@code null}
+ *
+ * @param name header名
+ * @return header值
+ */
+ default String header(final String name) {
+ final List values = headers().get(name);
+ if (ArrayUtil.isNotEmpty(values)) {
+ return values.get(0);
+ }
+
+ return null;
+ }
+
+ /**
+ * 设置一个header
+ * 如果覆盖模式,则替换之前的值,否则加入到值列表中
+ *
+ * @param name Header名
+ * @param value Header值
+ * @param isOverride 是否覆盖已有值
+ * @return T 本身
+ */
+ default T header(final Header name, final String value, final boolean isOverride) {
+ return header(name.toString(), value, isOverride);
+ }
+
+ /**
+ * 设置一个header
+ * 覆盖模式,则替换之前的值
+ *
+ * @param name Header名
+ * @param value Header值
+ * @return T 本身
+ */
+ default T header(final Header name, final String value) {
+ return header(name.toString(), value, true);
+ }
+
+ /**
+ * 添加请求头,默认覆盖原有头参数
+ *
+ * @param name 请求头参数名称
+ * @param value 参数值
+ * @return this
+ */
+ default T header(final String name, final String value) {
+ return header(name, value, true);
+ }
+
+ /**
+ * 设置contentType
+ *
+ * @param contentType contentType
+ * @return T
+ */
+ default T contentType(final String contentType) {
+ header(Header.CONTENT_TYPE, contentType);
+ return (T) this;
+ }
+
+ /**
+ * 设置是否为长连接
+ *
+ * @param isKeepAlive 是否长连接
+ * @return T
+ */
+ default T keepAlive(final boolean isKeepAlive) {
+ header(Header.CONNECTION, isKeepAlive ? "Keep-Alive" : "Close");
+ return (T) this;
+ }
+
+ // endregion ----------------------------------------------------------- headers
+
+ // region ----------------------------------------------------------- auth
+
+ /**
+ * 令牌验证,生成的头类似于:"Authorization: Bearer XXXXX",一般用于JWT
+ *
+ * @param token 令牌内容
+ * @return T this
+ */
+ default T bearerAuth(final String token) {
+ return auth("Bearer " + token);
+ }
+
+ /**
+ * 验证,简单插入Authorization头
+ *
+ * @param content 验证内容
+ * @return T this
+ */
+ default T auth(final String content) {
+ header(Header.AUTHORIZATION, content, true);
+ return (T) this;
+ }
+
+ /**
+ * 验证,简单插入Authorization头
+ *
+ * @param content 验证内容
+ * @return T this
+ */
+ default T proxyAuth(final String content) {
+ header(Header.PROXY_AUTHORIZATION, content, true);
+ return (T) this;
+ }
+
+ // endregion ----------------------------------------------------------- auth
+ // region ----------------------------------------------------------- Cookies
+
+ /**
+ * 设置Cookie
+ * 自定义Cookie后会覆盖Hutool的默认Cookie行为
+ *
+ * @param cookies Cookie值数组,如果为{@code null}则设置无效,使用默认Cookie行为
+ * @return this
+ * @since 5.4.1
+ */
+ default T cookie(final Collection cookies) {
+ return cookie(CollUtil.isEmpty(cookies) ? null : cookies.toArray(new HttpCookie[0]));
+ }
+
+ /**
+ * 设置Cookie
+ * 自定义Cookie后会覆盖Hutool的默认Cookie行为
+ *
+ * @param cookies Cookie值数组,如果为{@code null}则设置无效,使用默认Cookie行为
+ * @return this
+ * @since 3.1.1
+ */
+ default T cookie(final HttpCookie... cookies) {
+ if (ArrayUtil.isEmpty(cookies)) {
+ return disableCookie();
+ }
+ // 名称/值对之间用分号和空格 ('; ')
+ // https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cookie
+ return cookie(ArrayUtil.join(cookies, "; "));
+ }
+
+ /**
+ * 设置Cookie
+ * 自定义Cookie后会覆盖Hutool的默认Cookie行为
+ *
+ * @param cookie Cookie值,如果为{@code null}则设置无效,使用默认Cookie行为
+ * @return this
+ * @since 3.0.7
+ */
+ default T cookie(final String cookie) {
+ return header(Header.COOKIE, cookie, true);
+ }
+
+ /**
+ * 禁用默认Cookie行为,此方法调用后会将Cookie置为空。
+ * 如果想重新启用Cookie,请调用:{@link #cookie(String)}方法自定义Cookie。
+ * 如果想启动默认的Cookie行为(自动回填服务器传回的Cookie),则调用{@link #enableDefaultCookie()}
+ *
+ * @return this
+ * @since 3.0.7
+ */
+ default T disableCookie() {
+ return cookie(StrUtil.EMPTY);
+ }
+
+ /**
+ * 打开默认的Cookie行为(自动回填服务器传回的Cookie)
+ *
+ * @return this
+ */
+ default T enableDefaultCookie() {
+ return cookie((String) null);
+ }
+
+ // endregion ----------------------------------------------------------- Cookies
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpDownloader.java b/hutool-http/src/main/java/cn/hutool/http/client/HttpDownloader.java
similarity index 96%
rename from hutool-http/src/main/java/cn/hutool/http/HttpDownloader.java
rename to hutool-http/src/main/java/cn/hutool/http/client/HttpDownloader.java
index 4100c6480..8bcf252d0 100644
--- a/hutool-http/src/main/java/cn/hutool/http/HttpDownloader.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/HttpDownloader.java
@@ -1,8 +1,11 @@
-package cn.hutool.http;
+package cn.hutool.http.client;
import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.lang.Assert;
+import cn.hutool.http.HttpException;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.http.client.engine.jdk.HttpResponse;
import java.io.File;
import java.io.OutputStream;
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/Request.java b/hutool-http/src/main/java/cn/hutool/http/client/Request.java
new file mode 100755
index 000000000..b1532586d
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/Request.java
@@ -0,0 +1,212 @@
+package cn.hutool.http.client;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.net.url.UrlBuilder;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.http.HttpGlobalConfig;
+import cn.hutool.http.client.body.RequestBody;
+import cn.hutool.http.meta.Header;
+import cn.hutool.http.meta.Method;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 请求消息体,包括请求的URI、请求头、请求体等
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class Request implements Headers {
+
+ /**
+ * 构建一个HTTP请求
+ * 对于传入的URL,可以自定义是否解码已经编码的内容,设置见{@link HttpGlobalConfig#setDecodeUrl(boolean)}
+ * 在构建Http请求时,用户传入的URL可能有编码后和未编码的内容混合在一起,如果{@link HttpGlobalConfig#isDecodeUrl()}为{@code true},则会统一解码编码后的参数,
+ * 按照RFC3986规范,在发送请求时,全部编码之。如果为{@code false},则不会解码已经编码的内容,在请求时只编码需要编码的部分。
+ *
+ * @param url URL链接,默认自动编码URL中的参数等信息
+ * @return HttpRequest
+ */
+ public static Request of(final String url) {
+ return of(url, HttpGlobalConfig.isDecodeUrl() ? DEFAULT_CHARSET : null);
+ }
+
+ /**
+ * 构建一个HTTP请求
+ * 对于传入的URL,可以自定义是否解码已经编码的内容。
+ * 在构建Http请求时,用户传入的URL可能有编码后和未编码的内容混合在一起,如果charset参数不为{@code null},则会统一解码编码后的参数,
+ * 按照RFC3986规范,在发送请求时,全部编码之。如果为{@code false},则不会解码已经编码的内容,在请求时只编码需要编码的部分。
+ *
+ * @param url URL链接
+ * @param charset 编码,如果为{@code null}不自动解码编码URL
+ * @return HttpRequest
+ */
+ public static Request of(final String url, final Charset charset) {
+ return of(UrlBuilder.ofHttp(url, charset));
+ }
+
+ /**
+ * 构建一个HTTP请求
+ *
+ * @param url {@link UrlBuilder}
+ * @return HttpRequest
+ */
+ public static Request of(final UrlBuilder url) {
+ return new Request().url(url);
+ }
+
+ /**
+ * 默认的请求编码、URL的encode、decode编码
+ */
+ private static final Charset DEFAULT_CHARSET = CharsetUtil.UTF_8;
+
+ /**
+ * 请求方法
+ */
+ private Method method = Method.GET;
+ /**
+ * 请求的URL
+ */
+ private UrlBuilder url;
+ /**
+ * 存储头信息
+ */
+ private final Map> headers = new HashMap<>();
+ private RequestBody body;
+
+ /**
+ * 获取Http请求方法
+ *
+ * @return {@link Method}
+ * @since 4.1.8
+ */
+ public Method method() {
+ return this.method;
+ }
+
+ /**
+ * 设置请求方法
+ *
+ * @param method HTTP方法
+ * @return HttpRequest
+ */
+ public Request method(final Method method) {
+ this.method = method;
+ return this;
+ }
+
+ /**
+ * 获取请求的URL
+ *
+ * @return URL
+ */
+ public UrlBuilder url() {
+ return url;
+ }
+
+ /**
+ * 设置URL
+ *
+ * @param url URL
+ * @return this
+ */
+ public Request url(final UrlBuilder url) {
+ this.url = url;
+ return this;
+ }
+
+ /**
+ * 设置编码
+ *
+ * @param charset 编码
+ * @return this
+ */
+ public Request charset(final Charset charset) {
+ Assert.notNull(this.url, "You must be set request url first.");
+ this.url.setCharset(charset);
+ return this;
+ }
+
+ /**
+ * 获取请求编码,如果用户未设置,返回{@link #DEFAULT_CHARSET}
+ *
+ * @return 编码
+ */
+ public Charset charset() {
+ Assert.notNull(this.url, "You must be set request url first.");
+ return ObjUtil.defaultIfNull(this.url.getCharset(), DEFAULT_CHARSET);
+ }
+
+ @Override
+ public Map> headers() {
+ return MapUtil.view(this.headers);
+ }
+
+ /**
+ * 是否为Transfer-Encoding:Chunked的内容
+ *
+ * @return 是否为Transfer-Encoding:Chunked的内容
+ */
+ public boolean isChunked() {
+ final String transferEncoding = header(Header.TRANSFER_ENCODING);
+ return "Chunked".equalsIgnoreCase(transferEncoding);
+ }
+
+ /**
+ * 设置一个header
+ * 如果覆盖模式,则替换之前的值,否则加入到值列表中
+ * 如果给定值为{@code null},则删除这个头信息
+ *
+ * @param name Header名,{@code null}跳过
+ * @param value Header值,{@code null}表示删除name对应的头
+ * @param isOverride 是否覆盖已有值
+ * @return this
+ */
+ @Override
+ public Request header(final String name, final String value, final boolean isOverride) {
+ if (null == name) {
+ return this;
+ }
+ if (null == value) {
+ headers.remove(name);
+ return this;
+ }
+
+ final List values = headers.get(name.trim());
+ if (isOverride || CollUtil.isEmpty(values)) {
+ final ArrayList valueList = new ArrayList<>();
+ valueList.add(value);
+ headers.put(name.trim(), valueList);
+ } else {
+ values.add(value.trim());
+ }
+ return this;
+ }
+
+ /**
+ * 获取请求体
+ *
+ * @return this
+ */
+ public RequestBody body() {
+ return this.body;
+ }
+
+ /**
+ * 添加请求体
+ *
+ * @param body 请求体,可以是文本、表单、流、byte[] 或 Multipart
+ * @return this
+ */
+ public Request body(final RequestBody body) {
+ this.body = body;
+ return this;
+ }
+}
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
new file mode 100755
index 000000000..98070a750
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/Response.java
@@ -0,0 +1,134 @@
+package cn.hutool.http.client;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.text.StrUtil;
+import cn.hutool.http.meta.Header;
+import cn.hutool.http.HttpException;
+import cn.hutool.http.HttpUtil;
+
+import java.io.Closeable;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+/**
+ * 响应内容接口,包括响应状态码、HTTP消息头、响应体等信息
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public interface Response extends Closeable {
+
+ /**
+ * 获取状态码
+ *
+ * @return 状态码
+ */
+ int getStatus();
+
+ /**
+ * 根据name获取头信息
+ * 根据RFC2616规范,header的name不区分大小写
+ *
+ * @param name Header名
+ * @return Header值
+ */
+ String header(final String name);
+
+ /**
+ * 获取字符集编码
+ *
+ * @return 字符集
+ */
+ Charset charset();
+
+ /**
+ * 获得服务区响应流
+ * 流获取后处理完毕需关闭此类
+ *
+ * @return 响应流
+ */
+ InputStream bodyStream();
+
+ /**
+ * 获取响应主体
+ *
+ * @return String
+ * @throws HttpException 包装IO异常
+ */
+ default String body() throws HttpException {
+ return HttpUtil.getString(bodyStream(), charset(), true);
+ }
+
+ /**
+ * 请求是否成功,判断依据为:状态码范围在200~299内。
+ *
+ * @return 是否成功请求
+ */
+ default boolean isOk() {
+ final int status = getStatus();
+ return status >= 200 && status < 300;
+ }
+
+ /**
+ * 根据name获取头信息
+ *
+ * @param name Header名
+ * @return Header值
+ */
+ default String header(final Header name) {
+ if (null == name) {
+ return null;
+ }
+ return header(name.toString());
+ }
+
+ /**
+ * 获取内容编码
+ *
+ * @return String
+ */
+ default String contentEncoding() {
+ return header(Header.CONTENT_ENCODING);
+ }
+
+ /**
+ * 获取内容长度,以下情况长度无效:
+ *
+ * - Transfer-Encoding: Chunked
+ * - Content-Encoding: XXX
+ *
+ * 参考:https://blog.csdn.net/jiang7701037/article/details/86304302
+ *
+ * @return 长度,-1表示服务端未返回或长度无效
+ * @since 5.7.9
+ */
+ default 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;
+ }
+
+ /**
+ * 是否为Transfer-Encoding:Chunked的内容
+ *
+ * @return 是否为Transfer-Encoding:Chunked的内容
+ * @since 4.6.2
+ */
+ default boolean isChunked() {
+ final String transferEncoding = header(Header.TRANSFER_ENCODING);
+ return "Chunked".equalsIgnoreCase(transferEncoding);
+ }
+
+ /**
+ * 获取本次请求服务器返回的Cookie信息
+ *
+ * @return Cookie字符串
+ * @since 3.1.1
+ */
+ default String getCookieStr() {
+ return header(Header.SET_COOKIE);
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/body/BytesBody.java b/hutool-http/src/main/java/cn/hutool/http/client/body/BytesBody.java
similarity index 60%
rename from hutool-http/src/main/java/cn/hutool/http/body/BytesBody.java
rename to hutool-http/src/main/java/cn/hutool/http/client/body/BytesBody.java
index ef234b495..8ed4f196f 100644
--- a/hutool-http/src/main/java/cn/hutool/http/body/BytesBody.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/body/BytesBody.java
@@ -1,8 +1,6 @@
-package cn.hutool.http.body;
+package cn.hutool.http.client.body;
-import cn.hutool.core.io.IoUtil;
-
-import java.io.OutputStream;
+import cn.hutool.core.io.resource.BytesResource;
/**
* bytes类型的Http request body,主要发送编码后的表单数据或rest body(如JSON或XML)
@@ -10,9 +8,7 @@ import java.io.OutputStream;
* @since 5.7.17
* @author looly
*/
-public class BytesBody implements RequestBody {
-
- private final byte[] content;
+public class BytesBody extends ResourceBody {
/**
* 创建 Http request body
@@ -29,11 +25,6 @@ public class BytesBody implements RequestBody {
* @param content Body内容,编码后
*/
public BytesBody(final byte[] content) {
- this.content = content;
- }
-
- @Override
- public void write(final OutputStream out) {
- IoUtil.write(out, false, content);
+ super(new BytesResource(content));
}
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/body/FormUrlEncodedBody.java b/hutool-http/src/main/java/cn/hutool/http/client/body/FormUrlEncodedBody.java
similarity index 95%
rename from hutool-http/src/main/java/cn/hutool/http/body/FormUrlEncodedBody.java
rename to hutool-http/src/main/java/cn/hutool/http/client/body/FormUrlEncodedBody.java
index e62d3c09d..4160852c5 100644
--- a/hutool-http/src/main/java/cn/hutool/http/body/FormUrlEncodedBody.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/body/FormUrlEncodedBody.java
@@ -1,4 +1,4 @@
-package cn.hutool.http.body;
+package cn.hutool.http.client.body;
import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.text.StrUtil;
diff --git a/hutool-http/src/main/java/cn/hutool/http/body/MultipartBody.java b/hutool-http/src/main/java/cn/hutool/http/client/body/MultipartBody.java
similarity index 94%
rename from hutool-http/src/main/java/cn/hutool/http/body/MultipartBody.java
rename to hutool-http/src/main/java/cn/hutool/http/client/body/MultipartBody.java
index b15f7cfe3..8382562f6 100644
--- a/hutool-http/src/main/java/cn/hutool/http/body/MultipartBody.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/body/MultipartBody.java
@@ -1,10 +1,9 @@
-package cn.hutool.http.body;
+package cn.hutool.http.client.body;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
-import cn.hutool.http.ContentType;
+import cn.hutool.http.meta.ContentType;
import cn.hutool.http.HttpGlobalConfig;
-import cn.hutool.http.MultipartOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
diff --git a/hutool-http/src/main/java/cn/hutool/http/MultipartOutputStream.java b/hutool-http/src/main/java/cn/hutool/http/client/body/MultipartOutputStream.java
similarity index 96%
rename from hutool-http/src/main/java/cn/hutool/http/MultipartOutputStream.java
rename to hutool-http/src/main/java/cn/hutool/http/client/body/MultipartOutputStream.java
index 810e8e713..795a84ab0 100644
--- a/hutool-http/src/main/java/cn/hutool/http/MultipartOutputStream.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/body/MultipartOutputStream.java
@@ -1,4 +1,4 @@
-package cn.hutool.http;
+package cn.hutool.http.client.body;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IORuntimeException;
@@ -7,6 +7,10 @@ import cn.hutool.core.io.resource.MultiResource;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.io.resource.StringResource;
import cn.hutool.core.text.StrUtil;
+import cn.hutool.http.meta.ContentType;
+import cn.hutool.http.HttpGlobalConfig;
+import cn.hutool.core.io.resource.HttpResource;
+import cn.hutool.http.HttpUtil;
import java.io.IOException;
import java.io.OutputStream;
diff --git a/hutool-http/src/main/java/cn/hutool/http/body/RequestBody.java b/hutool-http/src/main/java/cn/hutool/http/client/body/RequestBody.java
similarity index 92%
rename from hutool-http/src/main/java/cn/hutool/http/body/RequestBody.java
rename to hutool-http/src/main/java/cn/hutool/http/client/body/RequestBody.java
index de8b7b44a..ae875d68e 100644
--- a/hutool-http/src/main/java/cn/hutool/http/body/RequestBody.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/body/RequestBody.java
@@ -1,4 +1,4 @@
-package cn.hutool.http.body;
+package cn.hutool.http.client.body;
import cn.hutool.core.io.IoUtil;
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/body/ResourceBody.java b/hutool-http/src/main/java/cn/hutool/http/client/body/ResourceBody.java
new file mode 100755
index 000000000..4c3c6ee55
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/body/ResourceBody.java
@@ -0,0 +1,49 @@
+package cn.hutool.http.client.body;
+
+import cn.hutool.core.io.resource.Resource;
+
+import java.io.OutputStream;
+
+/**
+ * {@link Resource}类型的Http request body,主要发送资源文件中的内容
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class ResourceBody implements RequestBody {
+
+ private final Resource resource;
+
+ /**
+ * 创建 Http request body
+ *
+ * @param resource body内容
+ * @return BytesBody
+ */
+ public static ResourceBody of(final Resource resource) {
+ return new ResourceBody(resource);
+ }
+
+ /**
+ * 构造
+ *
+ * @param resource Body内容
+ */
+ public ResourceBody(final Resource resource) {
+ this.resource = resource;
+ }
+
+ /**
+ * 获取资源
+ *
+ * @return 资源
+ */
+ public Resource getResource() {
+ return this.resource;
+ }
+
+ @Override
+ public void write(final OutputStream out) {
+ resource.writeTo(out);
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/body/package-info.java b/hutool-http/src/main/java/cn/hutool/http/client/body/package-info.java
similarity index 57%
rename from hutool-http/src/main/java/cn/hutool/http/body/package-info.java
rename to hutool-http/src/main/java/cn/hutool/http/client/body/package-info.java
index 3e20d6096..326bf9c54 100644
--- a/hutool-http/src/main/java/cn/hutool/http/body/package-info.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/body/package-info.java
@@ -1,7 +1,7 @@
/**
* 请求体封装实现
- *
+ *
* @author looly
*
*/
-package cn.hutool.http.body;
\ No newline at end of file
+package cn.hutool.http.client.body;
diff --git a/hutool-http/src/main/java/cn/hutool/http/cookie/GlobalCookieManager.java b/hutool-http/src/main/java/cn/hutool/http/client/cookie/GlobalCookieManager.java
similarity index 96%
rename from hutool-http/src/main/java/cn/hutool/http/cookie/GlobalCookieManager.java
rename to hutool-http/src/main/java/cn/hutool/http/client/cookie/GlobalCookieManager.java
index 0ce8eefee..9b53a5014 100644
--- a/hutool-http/src/main/java/cn/hutool/http/cookie/GlobalCookieManager.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/cookie/GlobalCookieManager.java
@@ -1,9 +1,9 @@
-package cn.hutool.http.cookie;
+package cn.hutool.http.client.cookie;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.URLUtil;
-import cn.hutool.http.HttpConnection;
+import cn.hutool.http.client.engine.jdk.HttpConnection;
import java.io.IOException;
import java.net.CookieManager;
diff --git a/hutool-http/src/main/java/cn/hutool/http/cookie/ThreadLocalCookieStore.java b/hutool-http/src/main/java/cn/hutool/http/client/cookie/ThreadLocalCookieStore.java
similarity index 97%
rename from hutool-http/src/main/java/cn/hutool/http/cookie/ThreadLocalCookieStore.java
rename to hutool-http/src/main/java/cn/hutool/http/client/cookie/ThreadLocalCookieStore.java
index 112e5528b..2bb0df838 100644
--- a/hutool-http/src/main/java/cn/hutool/http/cookie/ThreadLocalCookieStore.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/cookie/ThreadLocalCookieStore.java
@@ -1,4 +1,4 @@
-package cn.hutool.http.cookie;
+package cn.hutool.http.client.cookie;
import java.net.CookieManager;
import java.net.CookieStore;
diff --git a/hutool-http/src/main/java/cn/hutool/http/cookie/package-info.java b/hutool-http/src/main/java/cn/hutool/http/client/cookie/package-info.java
similarity index 53%
rename from hutool-http/src/main/java/cn/hutool/http/cookie/package-info.java
rename to hutool-http/src/main/java/cn/hutool/http/client/cookie/package-info.java
index c800f3a64..86ade1aa5 100644
--- a/hutool-http/src/main/java/cn/hutool/http/cookie/package-info.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/cookie/package-info.java
@@ -1,7 +1,7 @@
/**
* 自定义Cookie
- *
+ *
* @author looly
*
*/
-package cn.hutool.http.cookie;
\ No newline at end of file
+package cn.hutool.http.client.cookie;
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/EngineFactory.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/EngineFactory.java
new file mode 100755
index 000000000..a0db70ef8
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/EngineFactory.java
@@ -0,0 +1,13 @@
+package cn.hutool.http.client.engine;
+
+import cn.hutool.http.client.ClientEngine;
+
+/**
+ * Http客户端引擎工厂类
+ */
+public class EngineFactory {
+
+ public static ClientEngine getEngine(){
+ return null;
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Engine.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Engine.java
new file mode 100755
index 000000000..e9f19dfeb
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Engine.java
@@ -0,0 +1,112 @@
+package cn.hutool.http.client.engine.httpclient4;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.net.url.UrlBuilder;
+import cn.hutool.http.GlobalHeaders;
+import cn.hutool.http.HttpException;
+import cn.hutool.http.client.ClientEngine;
+import cn.hutool.http.client.Request;
+import cn.hutool.http.client.Response;
+import cn.hutool.http.client.body.RequestBody;
+import org.apache.http.Header;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicHeader;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Apache HttpClient5的HTTP请求引擎
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class HttpClient4Engine implements ClientEngine {
+
+ private final CloseableHttpClient engine;
+
+ /**
+ * 构造
+ */
+ public HttpClient4Engine() {
+ this.engine = HttpClients.custom()
+ // 设置默认头信息
+ .setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()))
+ .build();
+ }
+
+ @Override
+ public Response send(final Request message) {
+ final HttpEntityEnclosingRequestBase request = buildRequest(message);
+ final CloseableHttpResponse response;
+ try {
+ response = this.engine.execute(request);
+ } catch (final IOException e) {
+ throw new HttpException(e);
+ }
+
+ return new HttpClient4Response(response, message.charset());
+ }
+
+ @Override
+ public Object getRawEngine() {
+ return this.engine;
+ }
+
+ @Override
+ public void close() throws IOException {
+ this.engine.close();
+ }
+
+ /**
+ * 构建请求体
+ *
+ * @param message {@link Request}
+ * @return {@link HttpEntityEnclosingRequestBase}
+ */
+ private static HttpEntityEnclosingRequestBase buildRequest(final Request message) {
+ final UrlBuilder url = message.url();
+ Assert.notNull(url, "Request URL must be not null!");
+ final URI uri = url.toURI();
+
+ final HttpEntityEnclosingRequestBase request = new HttpEntityEnclosingRequestBase() {
+ @Override
+ public String getMethod() {
+ return message.method().name();
+ }
+ };
+ request.setURI(uri);
+
+ // 填充自定义头
+ request.setHeaders(toHeaderList(message.headers()).toArray(new Header[0]));
+
+ // 填充自定义消息体
+ final RequestBody body = message.body();
+ request.setEntity(new RequestBodyEntity(
+ // 用户自定义的内容类型
+ message.header(cn.hutool.http.meta.Header.CONTENT_TYPE),
+ // 用户自定义编码
+ message.charset(),
+ message.isChunked(),
+ body));
+
+ return request;
+ }
+
+ /**
+ * 获取默认头列表
+ *
+ * @return 默认头列表
+ */
+ private static List toHeaderList(final Map> headersMap) {
+ final List result = new ArrayList<>();
+ headersMap.forEach((k, v1) -> v1.forEach((v2) -> result.add(new BasicHeader(k, v2))));
+ return result;
+ }
+}
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
new file mode 100755
index 000000000..d0f5b84b7
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Response.java
@@ -0,0 +1,98 @@
+package cn.hutool.http.client.engine.httpclient4;
+
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.http.HttpException;
+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;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+/**
+ * HttpClient响应包装
+ * 通过包装{@link CloseableHttpResponse},实现获取响应状态码、响应头、响应体等内容
+ *
+ * @author looly
+ */
+public class HttpClient4Response implements Response {
+
+ /**
+ * HttpClient的响应对象
+ */
+ private final CloseableHttpResponse rawRes;
+ /**
+ * 请求时的默认编码
+ */
+ private final Charset requestCharset;
+
+ /**
+ * 构造
+ * 通过传入一个请求时的编码,当无法获取响应内容的编码时,默认使用响应时的编码
+ *
+ * @param rawRes {@link CloseableHttpResponse}
+ * @param requestCharset 请求时的编码
+ */
+ public HttpClient4Response(final CloseableHttpResponse rawRes, final Charset requestCharset) {
+ this.rawRes = rawRes;
+ this.requestCharset = requestCharset;
+ }
+
+
+ @Override
+ public int getStatus() {
+ return rawRes.getStatusLine().getStatusCode();
+ }
+
+ @Override
+ public String header(final String name) {
+ final Header[] headers = rawRes.getHeaders(name);
+ if (ArrayUtil.isNotEmpty(headers)) {
+ return headers[0].getValue();
+ }
+
+ return null;
+ }
+
+ @Override
+ public long contentLength() {
+ return rawRes.getEntity().getContentLength();
+ }
+
+ @Override
+ public Charset charset() {
+ final Charset charset = ContentType.parse(rawRes.getEntity().getContentType().getValue()).getCharset();
+ return ObjUtil.defaultIfNull(charset, requestCharset);
+ }
+
+ @Override
+ public InputStream bodyStream() {
+ try {
+ return rawRes.getEntity().getContent();
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ }
+ }
+
+ @Override
+ public String body() throws HttpException {
+ try {
+ return EntityUtils.toString(rawRes.getEntity(), charset());
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ } catch (final ParseException e) {
+ throw new HttpException(e);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ rawRes.close();
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/RequestBodyEntity.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/RequestBodyEntity.java
new file mode 100755
index 000000000..8cb2810ad
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/RequestBodyEntity.java
@@ -0,0 +1,72 @@
+package cn.hutool.http.client.engine.httpclient4;
+
+import cn.hutool.core.io.FastByteArrayOutputStream;
+import cn.hutool.http.client.body.BytesBody;
+import cn.hutool.http.client.body.RequestBody;
+import cn.hutool.http.client.body.ResourceBody;
+import org.apache.http.entity.AbstractHttpEntity;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+
+/**
+ * {@link RequestBody}转换为{@link org.apache.hc.core5.http.HttpEntity}对象
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class RequestBodyEntity extends AbstractHttpEntity {
+
+ private final RequestBody body;
+
+ /**
+ * 构造
+ *
+ * @param contentType Content-Type类型
+ * @param charset 自定义请求编码
+ * @param chunked 是否块模式传输
+ * @param body {@link RequestBody}
+ */
+ public RequestBodyEntity(final String contentType, final Charset charset, final boolean chunked, final RequestBody body) {
+ super();
+ setContentType(contentType);
+ setContentEncoding(null == charset ? null : charset.name());
+ setChunked(chunked);
+ this.body = body;
+ }
+
+ @Override
+ public void writeTo(final OutputStream outStream) {
+ if (null != body) {
+ body.writeClose(outStream);
+ }
+ }
+
+ @Override
+ public InputStream getContent() {
+ if (body instanceof ResourceBody) {
+ return ((ResourceBody) body).getResource().getStream();
+ } else {
+ final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
+ body.writeClose(out);
+ return new ByteArrayInputStream(out.toByteArray());
+ }
+ }
+
+ @Override
+ public boolean isStreaming() {
+ return body instanceof BytesBody;
+ }
+
+ @Override
+ public boolean isRepeatable() {
+ return false;
+ }
+
+ @Override
+ public long getContentLength() {
+ return 0;
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/package-info.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/package-info.java
new file mode 100755
index 000000000..eebd97547
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * Apache HttpClient 4.x实现
+ * 文档见:https://hc.apache.org/httpcomponents-client-4.5.x/index.html
+ *
+ * @author looly
+ */
+package cn.hutool.http.client.engine.httpclient4;
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/pom.xml b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/pom.xml
new file mode 100755
index 000000000..ea20fe814
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/pom.xml
@@ -0,0 +1,66 @@
+
+
+ 4.0.0
+
+ jar
+
+
+ cn.hutool
+ hutool-parent
+ 6.0.0.M1
+
+
+ hutool-http
+ ${project.artifactId}
+ Hutool Http客户端
+
+
+
+ cn.hutool
+ hutool-core
+ ${project.parent.version}
+
+
+ javax.xml.soap
+ javax.xml.soap-api
+ 1.4.0
+ provided
+
+
+
+
+ org.apache.httpcomponents.client5
+ httpclient5
+ 5.1.3
+ provided
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.13
+ provided
+
+
+
+ cn.hutool
+ hutool-json
+ ${project.parent.version}
+ test
+
+
+ org.brotli
+ dec
+ 0.1.2
+ test
+
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.25
+ test
+
+
+
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Engine.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Engine.java
new file mode 100755
index 000000000..620f0ba6e
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Engine.java
@@ -0,0 +1,108 @@
+package cn.hutool.http.client.engine.httpclient5;
+
+import cn.hutool.core.lang.Assert;
+import cn.hutool.core.net.url.UrlBuilder;
+import cn.hutool.http.GlobalHeaders;
+import cn.hutool.http.HttpException;
+import cn.hutool.http.client.ClientEngine;
+import cn.hutool.http.client.Request;
+import cn.hutool.http.client.Response;
+import cn.hutool.http.client.body.RequestBody;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.message.BasicHeader;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Apache HttpClient5的HTTP请求引擎
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class HttpClient5Engine implements ClientEngine {
+
+ private final CloseableHttpClient engine;
+
+ /**
+ * 构造
+ */
+ public HttpClient5Engine() {
+ this.engine = HttpClients.custom()
+ // 设置默认头信息
+ .setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()))
+ .build();
+ }
+
+ @Override
+ public Response send(final Request message) {
+ final ClassicHttpRequest request = buildRequest(message);
+ final CloseableHttpResponse response;
+ try {
+ response = this.engine.execute(request);
+ } catch (final IOException e) {
+ throw new HttpException(e);
+ }
+
+ return new HttpClient5Response(response, message.charset());
+ }
+
+ @Override
+ public Object getRawEngine() {
+ return this.engine;
+ }
+
+ @Override
+ public void close() throws IOException {
+ this.engine.close();
+ }
+
+ /**
+ * 构建请求体
+ *
+ * @param message {@link Request}
+ * @return {@link ClassicHttpRequest}
+ */
+ @SuppressWarnings("ConstantConditions")
+ private static ClassicHttpRequest buildRequest(final Request message) {
+ final UrlBuilder url = message.url();
+ Assert.notNull(url, "Request URL must be not null!");
+ final URI uri = url.toURI();
+
+ final ClassicHttpRequest request = new HttpUriRequestBase(message.method().name(), uri);
+
+ // 填充自定义头
+ request.setHeaders(toHeaderList(message.headers()).toArray(new Header[0]));
+
+ // 填充自定义消息体
+ final RequestBody body = message.body();
+ request.setEntity(new RequestBodyEntity(
+ // 用户自定义的内容类型
+ message.header(cn.hutool.http.meta.Header.CONTENT_TYPE),
+ // 用户自定义编码
+ message.charset(),
+ message.isChunked(),
+ body));
+
+ return request;
+ }
+
+ /**
+ * 获取默认头列表
+ *
+ * @return 默认头列表
+ */
+ private static List toHeaderList(final Map> headersMap) {
+ final List result = new ArrayList<>();
+ headersMap.forEach((k, v1) -> v1.forEach((v2) -> result.add(new BasicHeader(k, v2))));
+ return result;
+ }
+}
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
new file mode 100755
index 000000000..4d1ee55f3
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Response.java
@@ -0,0 +1,98 @@
+package cn.hutool.http.client.engine.httpclient5;
+
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.util.ArrayUtil;
+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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+/**
+ * HttpClient响应包装
+ * 通过包装{@link CloseableHttpResponse},实现获取响应状态码、响应头、响应体等内容
+ *
+ * @author looly
+ */
+public class HttpClient5Response implements Response {
+
+ /**
+ * HttpClient的响应对象
+ */
+ private final CloseableHttpResponse rawRes;
+ /**
+ * 请求时的默认编码
+ */
+ private final Charset requestCharset;
+
+ /**
+ * 构造
+ * 通过传入一个请求时的编码,当无法获取响应内容的编码时,默认使用响应时的编码
+ *
+ * @param rawRes {@link CloseableHttpResponse}
+ * @param requestCharset 请求时的编码
+ */
+ public HttpClient5Response(final CloseableHttpResponse rawRes, final Charset requestCharset) {
+ this.rawRes = rawRes;
+ this.requestCharset = requestCharset;
+ }
+
+
+ @Override
+ public int getStatus() {
+ return rawRes.getCode();
+ }
+
+ @Override
+ public String header(final String name) {
+ final Header[] headers = rawRes.getHeaders(name);
+ if (ArrayUtil.isNotEmpty(headers)) {
+ return headers[0].getValue();
+ }
+
+ return null;
+ }
+
+ @Override
+ public long contentLength() {
+ return rawRes.getEntity().getContentLength();
+ }
+
+ @Override
+ public Charset charset() {
+ final Charset charset = ContentType.parse(rawRes.getEntity().getContentType()).getCharset();
+ return ObjUtil.defaultIfNull(charset, requestCharset);
+ }
+
+ @Override
+ public InputStream bodyStream() {
+ try {
+ return rawRes.getEntity().getContent();
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ }
+ }
+
+ @Override
+ public String body() throws HttpException {
+ try {
+ return EntityUtils.toString(rawRes.getEntity(), charset());
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ } catch (final ParseException e) {
+ throw new HttpException(e);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ rawRes.close();
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/RequestBodyEntity.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/RequestBodyEntity.java
new file mode 100755
index 000000000..158bd16f9
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/RequestBodyEntity.java
@@ -0,0 +1,70 @@
+package cn.hutool.http.client.engine.httpclient5;
+
+import cn.hutool.core.io.FastByteArrayOutputStream;
+import cn.hutool.http.client.body.BytesBody;
+import cn.hutool.http.client.body.RequestBody;
+import cn.hutool.http.client.body.ResourceBody;
+import org.apache.hc.core5.http.io.entity.AbstractHttpEntity;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+
+/**
+ * {@link RequestBody}转换为{@link org.apache.hc.core5.http.HttpEntity}对象
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class RequestBodyEntity extends AbstractHttpEntity {
+
+ private final RequestBody body;
+
+ /**
+ * 构造
+ *
+ * @param contentType Content-Type类型
+ * @param charset 自定义请求编码
+ * @param chunked 是否块模式传输
+ * @param body {@link RequestBody}
+ */
+ public RequestBodyEntity(final String contentType, final Charset charset, final boolean chunked, final RequestBody body) {
+ super(contentType, null == charset ? null : charset.name(), chunked);
+ this.body = body;
+ }
+
+ @Override
+ public void writeTo(final OutputStream outStream) {
+ if(null != body){
+ body.writeClose(outStream);
+ }
+ }
+
+ @Override
+ public InputStream getContent() {
+ if (body instanceof ResourceBody) {
+ return ((ResourceBody) body).getResource().getStream();
+ } else {
+ final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
+ body.writeClose(out);
+ return new ByteArrayInputStream(out.toByteArray());
+ }
+ }
+
+ @Override
+ public boolean isStreaming() {
+ return body instanceof BytesBody;
+ }
+
+ @Override
+ public void close() throws IOException {
+
+ }
+
+ @Override
+ public long getContentLength() {
+ return 0;
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/package-info.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/package-info.java
new file mode 100755
index 000000000..f8825d514
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * Apache HttpClient 5.1.x实现
+ * 文档见:https://hc.apache.org/httpcomponents-client-5.1.x/index.html
+ *
+ * @author looly
+ */
+package cn.hutool.http.client.engine.httpclient5;
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpBase.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpBase.java
similarity index 82%
rename from hutool-http/src/main/java/cn/hutool/http/HttpBase.java
rename to hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpBase.java
index ea4cdf0d3..91005f87f 100644
--- a/hutool-http/src/main/java/cn/hutool/http/HttpBase.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpBase.java
@@ -1,10 +1,12 @@
-package cn.hutool.http;
+package cn.hutool.http.client.engine.jdk;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.CaseInsensitiveMap;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.http.meta.Header;
+import cn.hutool.http.client.Headers;
import java.nio.charset.Charset;
import java.util.ArrayList;
@@ -21,7 +23,7 @@ import java.util.Map.Entry;
* @author Looly
*/
@SuppressWarnings("unchecked")
-public abstract class HttpBase {
+public abstract class HttpBase> implements Headers {
/**
* 默认的请求编码、URL的encode、decode编码
@@ -56,21 +58,6 @@ public abstract class HttpBase {
// ---------------------------------------------------------------- Headers start
- /**
- * 根据name获取头信息
- * 根据RFC2616规范,header的name不区分大小写
- *
- * @param name Header名
- * @return Header值
- */
- public String header(final String name) {
- final List values = headerList(name);
- if (CollUtil.isEmpty(values)) {
- return null;
- }
- return values.get(0);
- }
-
/**
* 根据name获取头信息列表
*
@@ -90,14 +77,14 @@ public abstract class HttpBase {
/**
* 根据name获取头信息
*
- * @param name Header名
+ * @param header Header名
* @return Header值
*/
- public String header(final Header name) {
- if (null == name) {
+ public String header(final Header header) {
+ if (null == header) {
return null;
}
- return header(name.toString());
+ return header(header.toString());
}
/**
@@ -109,6 +96,7 @@ public abstract class HttpBase {
* @param isOverride 是否覆盖已有值
* @return T 本身
*/
+ @Override
public T header(final String name, final String value, final boolean isOverride) {
if (null != name && null != value) {
final List values = headers.get(name.trim());
@@ -123,43 +111,6 @@ public abstract class HttpBase {
return (T) this;
}
- /**
- * 设置一个header
- * 如果覆盖模式,则替换之前的值,否则加入到值列表中
- *
- * @param name Header名
- * @param value Header值
- * @param isOverride 是否覆盖已有值
- * @return T 本身
- */
- public T header(final Header name, final String value, final boolean isOverride) {
- return header(name.toString(), value, isOverride);
- }
-
- /**
- * 设置一个header
- * 覆盖模式,则替换之前的值
- *
- * @param name Header名
- * @param value Header值
- * @return T 本身
- */
- public T header(final Header name, final String value) {
- return header(name.toString(), value, true);
- }
-
- /**
- * 设置一个header
- * 覆盖模式,则替换之前的值
- *
- * @param name Header名
- * @param value Header值
- * @return T 本身
- */
- public T header(final String name, final String value) {
- return header(name, value, true);
- }
-
/**
* 设置请求头
*
@@ -260,6 +211,7 @@ public abstract class HttpBase {
*
* @return Headers Map
*/
+ @Override
public Map> headers() {
return Collections.unmodifiableMap(headers);
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpConnection.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpConnection.java
similarity index 98%
rename from hutool-http/src/main/java/cn/hutool/http/HttpConnection.java
rename to hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpConnection.java
index 1d4cda745..1cf9121f1 100644
--- a/hutool-http/src/main/java/cn/hutool/http/HttpConnection.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpConnection.java
@@ -1,10 +1,15 @@
-package cn.hutool.http;
+package cn.hutool.http.client.engine.jdk;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.URLUtil;
import cn.hutool.core.reflect.FieldUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ObjUtil;
+import cn.hutool.http.meta.Header;
+import cn.hutool.http.HttpException;
+import cn.hutool.http.HttpGlobalConfig;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.http.meta.Method;
import cn.hutool.http.ssl.DefaultSSLInfo;
import javax.net.ssl.HostnameVerifier;
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpInputStream.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpInputStream.java
similarity index 93%
rename from hutool-http/src/main/java/cn/hutool/http/HttpInputStream.java
rename to hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpInputStream.java
index 384c64bd0..3c3e2b8a6 100644
--- a/hutool-http/src/main/java/cn/hutool/http/HttpInputStream.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpInputStream.java
@@ -1,7 +1,10 @@
-package cn.hutool.http;
+package cn.hutool.http.client.engine.jdk;
import cn.hutool.core.reflect.ConstructorUtil;
import cn.hutool.core.text.StrUtil;
+import cn.hutool.http.GlobalCompressStreamRegister;
+import cn.hutool.http.HttpException;
+import cn.hutool.http.meta.HttpStatus;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpInterceptor.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpInterceptor.java
similarity index 96%
rename from hutool-http/src/main/java/cn/hutool/http/HttpInterceptor.java
rename to hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpInterceptor.java
index 7ad6ee067..fd3df8629 100644
--- a/hutool-http/src/main/java/cn/hutool/http/HttpInterceptor.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpInterceptor.java
@@ -1,4 +1,4 @@
-package cn.hutool.http;
+package cn.hutool.http.client.engine.jdk;
import java.util.Iterator;
import java.util.LinkedList;
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpRequest.java
similarity index 98%
rename from hutool-http/src/main/java/cn/hutool/http/HttpRequest.java
rename to hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpRequest.java
index d0a8ffab7..7602c5ed1 100755
--- a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpRequest.java
@@ -1,4 +1,4 @@
-package cn.hutool.http;
+package cn.hutool.http.client.engine.jdk;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
@@ -16,11 +16,20 @@ import cn.hutool.core.net.url.UrlQuery;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjUtil;
-import cn.hutool.http.body.BytesBody;
-import cn.hutool.http.body.FormUrlEncodedBody;
-import cn.hutool.http.body.MultipartBody;
-import cn.hutool.http.body.RequestBody;
-import cn.hutool.http.cookie.GlobalCookieManager;
+import cn.hutool.http.meta.ContentType;
+import cn.hutool.http.GlobalHeaders;
+import cn.hutool.http.meta.Header;
+import cn.hutool.http.HttpConfig;
+import cn.hutool.http.HttpException;
+import cn.hutool.http.HttpGlobalConfig;
+import cn.hutool.http.meta.HttpStatus;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.http.meta.Method;
+import cn.hutool.http.client.body.BytesBody;
+import cn.hutool.http.client.body.FormUrlEncodedBody;
+import cn.hutool.http.client.body.MultipartBody;
+import cn.hutool.http.client.body.RequestBody;
+import cn.hutool.http.client.cookie.GlobalCookieManager;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
@@ -255,7 +264,7 @@ public class HttpRequest extends HttpBase {
this.charset(charset);
}
// 给定一个默认头信息
- this.header(GlobalHeaders.INSTANCE.headers);
+ this.header(GlobalHeaders.INSTANCE.headers());
}
/**
@@ -309,28 +318,6 @@ public class HttpRequest extends HttpBase {
return this;
}
- /**
- * 获取Http请求方法
- *
- * @return {@link Method}
- * @since 4.1.8
- */
- public Method getMethod() {
- return this.method;
- }
-
- /**
- * 设置请求方法
- *
- * @param method HTTP方法
- * @return HttpRequest
- * @see #method(Method)
- * @since 4.1.8
- */
- public HttpRequest setMethod(final Method method) {
- return method(method);
- }
-
/**
* 获取{@link HttpConnection}
* 在{@link #execute()} 执行前此对象为null
@@ -342,6 +329,16 @@ public class HttpRequest extends HttpBase {
return this.httpConnection;
}
+ /**
+ * 获取Http请求方法
+ *
+ * @return {@link Method}
+ * @since 4.1.8
+ */
+ public Method getMethod() {
+ return this.method;
+ }
+
/**
* 设置请求方法
*
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpResponse.java
similarity index 98%
rename from hutool-http/src/main/java/cn/hutool/http/HttpResponse.java
rename to hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpResponse.java
index 078eaf700..5bec6c58d 100755
--- a/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpResponse.java
@@ -1,4 +1,4 @@
-package cn.hutool.http;
+package cn.hutool.http.client.engine.jdk;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FastByteArrayOutputStream;
@@ -11,7 +11,11 @@ import cn.hutool.core.net.url.URLEncoder;
import cn.hutool.core.regex.ReUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ObjUtil;
-import cn.hutool.http.cookie.GlobalCookieManager;
+import cn.hutool.http.meta.Header;
+import cn.hutool.http.HttpConfig;
+import cn.hutool.http.HttpException;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.http.client.cookie.GlobalCookieManager;
import java.io.ByteArrayInputStream;
import java.io.Closeable;
@@ -261,7 +265,7 @@ public class HttpResponse extends HttpBase implements Closeable {
Assert.notNull(out, "[out] must be not null!");
final long contentLength = contentLength();
try {
- return copyBody(bodyStream(), out, contentLength, streamProgress, this.config.ignoreEOFError);
+ return copyBody(bodyStream(), out, contentLength, streamProgress, this.config.isIgnoreEOFError());
} finally {
IoUtil.close(this);
if (isCloseOut) {
@@ -587,7 +591,7 @@ public class HttpResponse extends HttpBase implements Closeable {
final long contentLength = contentLength();
final FastByteArrayOutputStream out = new FastByteArrayOutputStream((int) contentLength);
- copyBody(in, out, contentLength, null, this.config.ignoreEOFError);
+ copyBody(in, out, contentLength, null, this.config.isIgnoreEOFError());
this.bodyBytes = out.toByteArray();
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/package-info.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/package-info.java
new file mode 100755
index 000000000..b241a664b
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * 基于JDK的HttpUrlConnection封装的HTTP客户端
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+package cn.hutool.http.client.engine.jdk;
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpEngine.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpEngine.java
new file mode 100755
index 000000000..9b97237dc
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpEngine.java
@@ -0,0 +1,70 @@
+package cn.hutool.http.client.engine.okhttp;
+
+import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.http.client.ClientEngine;
+import cn.hutool.http.client.Request;
+import cn.hutool.http.client.Response;
+import okhttp3.OkHttpClient;
+import okhttp3.internal.http.HttpMethod;
+
+import java.io.IOException;
+
+/**
+ * OkHttp3客户端引擎封装
+ *
+ * @author looly
+ * @since 6.0.0
+ */
+public class OkHttpEngine implements ClientEngine {
+
+ private final OkHttpClient client;
+
+ /**
+ * 构造
+ */
+ public OkHttpEngine() {
+ this.client = new OkHttpClient();
+ }
+
+ @Override
+ public Response send(final Request message) {
+ final okhttp3.Response response;
+ try {
+ response = client.newCall(buildRequest(message)).execute();
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ }
+
+ return new OkHttpResponse(response, message.charset());
+ }
+
+ @Override
+ public Object getRawEngine() {
+ return this.client;
+ }
+
+ @Override
+ public void close() throws IOException {
+ // ignore
+ }
+
+ /**
+ * 构建请求体
+ *
+ * @param message {@link Request}
+ * @return {@link okhttp3.Request}
+ */
+ private static okhttp3.Request buildRequest(final Request message) {
+ final okhttp3.Request.Builder builder = new okhttp3.Request.Builder()
+ .url(message.url().toURL());
+
+ final String method = message.method().name();
+ if(HttpMethod.permitsRequestBody(method)){
+ builder.method(method, new OkHttpRequestBody(message.body()));
+ }else{
+ builder.method(method, null);
+ }
+
+ return builder.build();
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpRequestBody.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpRequestBody.java
new file mode 100755
index 000000000..9060dde8f
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpRequestBody.java
@@ -0,0 +1,33 @@
+package cn.hutool.http.client.engine.okhttp;
+
+import cn.hutool.http.client.body.RequestBody;
+import okhttp3.MediaType;
+import okio.BufferedSink;
+
+/**
+ * OkHttp的请求体实现,通过{@link RequestBody}转换实现
+ *
+ * @author looly
+ */
+public class OkHttpRequestBody extends okhttp3.RequestBody {
+
+ private final RequestBody body;
+
+ /**
+ * 构造
+ *
+ * @param body 请求体{@link RequestBody}
+ */
+ public OkHttpRequestBody(final RequestBody body) {
+ this.body = body;
+ }
+
+ public MediaType contentType() {
+ return null;
+ }
+
+ @Override
+ public void writeTo(final BufferedSink bufferedSink) {
+ body.writeClose(bufferedSink.outputStream());
+ }
+}
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
new file mode 100755
index 000000000..657395901
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpResponse.java
@@ -0,0 +1,70 @@
+package cn.hutool.http.client.engine.okhttp;
+
+import cn.hutool.core.io.EmptyInputStream;
+import cn.hutool.core.text.StrUtil;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.http.client.Response;
+import cn.hutool.http.meta.Header;
+import okhttp3.ResponseBody;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
+/**
+ * OkHttp3的{@link okhttp3.Response} 响应包装
+ *
+ * @author looly
+ */
+public class OkHttpResponse implements Response {
+
+ private final okhttp3.Response rawRes;
+ /**
+ * 请求时的默认编码
+ */
+ private final Charset requestCharset;
+
+ /**
+ * @param rawRes {@link okhttp3.Response}
+ * @param requestCharset 请求时的默认编码
+ */
+ public OkHttpResponse(final okhttp3.Response rawRes, final Charset requestCharset) {
+ this.rawRes = rawRes;
+ this.requestCharset = requestCharset;
+ }
+
+ @Override
+ public int getStatus() {
+ return rawRes.code();
+ }
+
+ @Override
+ public String header(final String name) {
+ return rawRes.header(name);
+ }
+
+ @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;
+ }
+
+ @Override
+ public InputStream bodyStream() {
+ final ResponseBody body = rawRes.body();
+ if(null == body){
+ return EmptyInputStream.INSTANCE;
+ }
+ return body.byteStream();
+ }
+
+ @Override
+ public void close() throws IOException {
+ rawRes.close();
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/package-info.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/package-info.java
new file mode 100755
index 000000000..198ac3aac
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * OKHttp3封装
+ * 文档见:https://square.github.io/okhttp/
+ */
+package cn.hutool.http.client.engine.okhttp;
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/package-info.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/package-info.java
new file mode 100755
index 000000000..72f5b456f
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Http客户端引擎实现
+ *
+ * @author looly
+ */
+package cn.hutool.http.client.engine;
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/package-info.java b/hutool-http/src/main/java/cn/hutool/http/client/package-info.java
new file mode 100755
index 000000000..5b74a29f0
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/client/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * HTTP请求客户端封装
+ * @author looly
+ */
+package cn.hutool.http.client;
diff --git a/hutool-http/src/main/java/cn/hutool/http/ContentType.java b/hutool-http/src/main/java/cn/hutool/http/meta/ContentType.java
similarity index 99%
rename from hutool-http/src/main/java/cn/hutool/http/ContentType.java
rename to hutool-http/src/main/java/cn/hutool/http/meta/ContentType.java
index 81682b008..69b7aea1c 100644
--- a/hutool-http/src/main/java/cn/hutool/http/ContentType.java
+++ b/hutool-http/src/main/java/cn/hutool/http/meta/ContentType.java
@@ -1,4 +1,4 @@
-package cn.hutool.http;
+package cn.hutool.http.meta;
import cn.hutool.core.text.StrUtil;
diff --git a/hutool-http/src/main/java/cn/hutool/http/Header.java b/hutool-http/src/main/java/cn/hutool/http/meta/Header.java
similarity index 99%
rename from hutool-http/src/main/java/cn/hutool/http/Header.java
rename to hutool-http/src/main/java/cn/hutool/http/meta/Header.java
index 79727ce2d..6f5e4c3d0 100644
--- a/hutool-http/src/main/java/cn/hutool/http/Header.java
+++ b/hutool-http/src/main/java/cn/hutool/http/meta/Header.java
@@ -1,4 +1,4 @@
-package cn.hutool.http;
+package cn.hutool.http.meta;
/**
* Http 头域
diff --git a/hutool-http/src/main/java/cn/hutool/http/Status.java b/hutool-http/src/main/java/cn/hutool/http/meta/HttpStatus.java
similarity index 77%
rename from hutool-http/src/main/java/cn/hutool/http/Status.java
rename to hutool-http/src/main/java/cn/hutool/http/meta/HttpStatus.java
index 91f1be71b..1282987ff 100644
--- a/hutool-http/src/main/java/cn/hutool/http/Status.java
+++ b/hutool-http/src/main/java/cn/hutool/http/meta/HttpStatus.java
@@ -1,11 +1,16 @@
-package cn.hutool.http;
+package cn.hutool.http.meta;
/**
- * 返回状态码
+ * HTTP状态码
+ *
* @author Looly
+ * @see java.net.HttpURLConnection
*
*/
-interface Status {
+public interface HttpStatus {
+
+ /* 2XX: generally "OK" */
+
/**
* HTTP Status-Code 200: OK.
*/
@@ -73,6 +78,18 @@ interface Status {
*/
int HTTP_USE_PROXY = 305;
+ /**
+ * HTTP 1.1 Status-Code 307: Temporary Redirect.
+ * 见:RFC-7231
+ */
+ int HTTP_TEMP_REDIRECT = 307;
+
+ /**
+ * HTTP 1.1 Status-Code 308: Permanent Redirect 永久重定向
+ * 见:RFC-7231
+ */
+ int HTTP_PERMANENT_REDIRECT = 308;
+
/* 4XX: client error */
/**
@@ -186,4 +203,20 @@ interface Status {
* HTTP Status-Code 505: HTTP Version Not Supported.
*/
int HTTP_VERSION = 505;
+
+ /**
+ * 是否为重定向状态码
+ * @param responseCode 被检查的状态码
+ * @return 是否为重定向状态码
+ * @since 5.6.3
+ */
+ static boolean isRedirected(final int responseCode){
+ return responseCode == HTTP_MOVED_PERM
+ || responseCode == HTTP_MOVED_TEMP
+ || responseCode == HTTP_SEE_OTHER
+ // issue#1504@Github,307和308是RFC 7538中http 1.1定义的规范
+ || responseCode == HTTP_TEMP_REDIRECT
+ || responseCode == HTTP_PERMANENT_REDIRECT;
+
+ }
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/Method.java b/hutool-http/src/main/java/cn/hutool/http/meta/Method.java
similarity index 82%
rename from hutool-http/src/main/java/cn/hutool/http/Method.java
rename to hutool-http/src/main/java/cn/hutool/http/meta/Method.java
index 325349fb5..67036cdc9 100644
--- a/hutool-http/src/main/java/cn/hutool/http/Method.java
+++ b/hutool-http/src/main/java/cn/hutool/http/meta/Method.java
@@ -1,4 +1,4 @@
-package cn.hutool.http;
+package cn.hutool.http.meta;
/**
* Http方法枚举
diff --git a/hutool-http/src/main/java/cn/hutool/http/meta/package-info.java b/hutool-http/src/main/java/cn/hutool/http/meta/package-info.java
new file mode 100755
index 000000000..f0644baf1
--- /dev/null
+++ b/hutool-http/src/main/java/cn/hutool/http/meta/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * Http元数据信息,包括Header枚举、状态码、Http方法、枚举Content-Type等
+ *
+ * @author looly
+ */
+package cn.hutool.http.meta;
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 d31f9ab0f..4f17a9557 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,9 +11,9 @@ 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.http.Header;
+import cn.hutool.http.meta.Header;
import cn.hutool.http.HttpUtil;
-import cn.hutool.http.Method;
+import cn.hutool.http.meta.Method;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import com.sun.net.httpserver.Headers;
diff --git a/hutool-http/src/main/java/cn/hutool/http/server/HttpServerResponse.java b/hutool-http/src/main/java/cn/hutool/http/server/HttpServerResponse.java
index 04e716927..a3250f3aa 100644
--- a/hutool-http/src/main/java/cn/hutool/http/server/HttpServerResponse.java
+++ b/hutool-http/src/main/java/cn/hutool/http/server/HttpServerResponse.java
@@ -6,9 +6,9 @@ import cn.hutool.core.io.IoUtil;
import cn.hutool.core.net.url.URLEncoder;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.text.StrUtil;
-import cn.hutool.http.ContentType;
-import cn.hutool.http.Header;
-import cn.hutool.http.HttpStatus;
+import cn.hutool.http.meta.ContentType;
+import cn.hutool.http.meta.Header;
+import cn.hutool.http.meta.HttpStatus;
import cn.hutool.http.HttpUtil;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
diff --git a/hutool-http/src/main/java/cn/hutool/http/useragent/Engine.java b/hutool-http/src/main/java/cn/hutool/http/useragent/BrowserEngine.java
similarity index 52%
rename from hutool-http/src/main/java/cn/hutool/http/useragent/Engine.java
rename to hutool-http/src/main/java/cn/hutool/http/useragent/BrowserEngine.java
index a5053626c..4e6f32336 100755
--- a/hutool-http/src/main/java/cn/hutool/http/useragent/Engine.java
+++ b/hutool-http/src/main/java/cn/hutool/http/useragent/BrowserEngine.java
@@ -7,30 +7,32 @@ import java.util.List;
import java.util.regex.Pattern;
/**
- * 引擎对象
+ * 浏览器引擎对象
*
* @author looly
* @since 4.2.1
*/
-public class Engine extends UserAgentInfo {
+public class BrowserEngine extends UserAgentInfo {
private static final long serialVersionUID = 1L;
- /** 未知 */
- public static final Engine Unknown = new Engine(NameUnknown, null);
+ /**
+ * 未知
+ */
+ public static final BrowserEngine Unknown = new BrowserEngine(NameUnknown, null);
/**
* 支持的引擎类型
*/
- public static final List engines = ListUtil.view(
- new Engine("Trident", "trident"),
- new Engine("Webkit", "webkit"),
- new Engine("Chrome", "chrome"),
- new Engine("Opera", "opera"),
- new Engine("Presto", "presto"),
- new Engine("Gecko", "gecko"),
- new Engine("KHTML", "khtml"),
- new Engine("Konqueror", "konqueror"),
- new Engine("MIDP", "MIDP")
+ public static final List engines = ListUtil.view(
+ new BrowserEngine("Trident", "trident"),
+ new BrowserEngine("Webkit", "webkit"),
+ new BrowserEngine("Chrome", "chrome"),
+ new BrowserEngine("Opera", "opera"),
+ new BrowserEngine("Presto", "presto"),
+ new BrowserEngine("Gecko", "gecko"),
+ new BrowserEngine("KHTML", "khtml"),
+ new BrowserEngine("Konqueror", "konqueror"),
+ new BrowserEngine("MIDP", "MIDP")
);
private final Pattern versionPattern;
@@ -38,10 +40,10 @@ public class Engine extends UserAgentInfo {
/**
* 构造
*
- * @param name 引擎名称
+ * @param name 引擎名称
* @param regex 关键字或表达式
*/
- public Engine(final String name, final String regex) {
+ public BrowserEngine(final String name, final String regex) {
super(name, regex);
this.versionPattern = Pattern.compile(name + "[/\\- ]([\\w.\\-]+)", Pattern.CASE_INSENSITIVE);
}
@@ -54,7 +56,7 @@ public class Engine extends UserAgentInfo {
* @since 5.7.4
*/
public String getVersion(final String userAgentString) {
- if(isUnknown()){
+ if (isUnknown()) {
return null;
}
return ReUtil.getGroup1(this.versionPattern, userAgentString);
diff --git a/hutool-http/src/main/java/cn/hutool/http/useragent/UserAgent.java b/hutool-http/src/main/java/cn/hutool/http/useragent/UserAgent.java
index 494227d4a..2d4f5f452 100644
--- a/hutool-http/src/main/java/cn/hutool/http/useragent/UserAgent.java
+++ b/hutool-http/src/main/java/cn/hutool/http/useragent/UserAgent.java
@@ -41,7 +41,7 @@ public class UserAgent implements Serializable {
/**
* 引擎类型
*/
- private Engine engine;
+ private BrowserEngine engine;
/**
* 引擎版本
*/
@@ -144,7 +144,7 @@ public class UserAgent implements Serializable {
*
* @return 引擎类型
*/
- public Engine getEngine() {
+ public BrowserEngine getEngine() {
return engine;
}
@@ -153,7 +153,7 @@ public class UserAgent implements Serializable {
*
* @param engine 引擎类型
*/
- public void setEngine(final Engine engine) {
+ public void setEngine(final BrowserEngine engine) {
this.engine = engine;
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/useragent/UserAgentParser.java b/hutool-http/src/main/java/cn/hutool/http/useragent/UserAgentParser.java
index 54e27cf9a..eb5be46f3 100644
--- a/hutool-http/src/main/java/cn/hutool/http/useragent/UserAgentParser.java
+++ b/hutool-http/src/main/java/cn/hutool/http/useragent/UserAgentParser.java
@@ -28,7 +28,7 @@ public class UserAgentParser {
userAgent.setVersion(browser.getVersion(userAgentString));
// 浏览器引擎
- final Engine engine = parseEngine(userAgentString);
+ final BrowserEngine engine = parseEngine(userAgentString);
userAgent.setEngine(engine);
userAgent.setEngineVersion(engine.getVersion(userAgentString));
@@ -67,13 +67,13 @@ public class UserAgentParser {
* @param userAgentString User-Agent字符串
* @return 引擎类型
*/
- private static Engine parseEngine(final String userAgentString) {
- for (final Engine engine : Engine.engines) {
+ private static BrowserEngine parseEngine(final String userAgentString) {
+ for (final BrowserEngine engine : BrowserEngine.engines) {
if (engine.isMatch(userAgentString)) {
return engine;
}
}
- return Engine.Unknown;
+ return BrowserEngine.Unknown;
}
/**
diff --git a/hutool-http/src/main/java/cn/hutool/http/webservice/SoapClient.java b/hutool-http/src/main/java/cn/hutool/http/webservice/SoapClient.java
index 4a3646e72..0be4bacd6 100644
--- a/hutool-http/src/main/java/cn/hutool/http/webservice/SoapClient.java
+++ b/hutool-http/src/main/java/cn/hutool/http/webservice/SoapClient.java
@@ -6,10 +6,10 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.XmlUtil;
-import cn.hutool.http.HttpBase;
+import cn.hutool.http.client.engine.jdk.HttpBase;
import cn.hutool.http.HttpGlobalConfig;
-import cn.hutool.http.HttpRequest;
-import cn.hutool.http.HttpResponse;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.client.engine.jdk.HttpResponse;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
diff --git a/hutool-http/src/test/java/cn/hutool/http/ContentTypeTest.java b/hutool-http/src/test/java/cn/hutool/http/ContentTypeTest.java
index 9ac184452..de3834ab4 100644
--- a/hutool-http/src/test/java/cn/hutool/http/ContentTypeTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/ContentTypeTest.java
@@ -1,6 +1,7 @@
package cn.hutool.http;
import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.http.meta.ContentType;
import org.junit.Assert;
import org.junit.Test;
diff --git a/hutool-http/src/test/java/cn/hutool/http/DownloadTest.java b/hutool-http/src/test/java/cn/hutool/http/DownloadTest.java
index 888f5d765..0860a2cf5 100644
--- a/hutool-http/src/test/java/cn/hutool/http/DownloadTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/DownloadTest.java
@@ -4,6 +4,7 @@ import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.lang.Console;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
diff --git a/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java b/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java
index 8553a6173..2f21cfe1f 100644
--- a/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java
@@ -7,6 +7,10 @@ import cn.hutool.core.lang.Console;
import cn.hutool.core.net.ssl.SSLProtocols;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.client.engine.jdk.HttpResponse;
+import cn.hutool.http.meta.Header;
+import cn.hutool.http.meta.Method;
import org.junit.Ignore;
import org.junit.Test;
@@ -213,7 +217,7 @@ public class HttpRequestTest {
urlBuilder.setScheme("https").setHost("hutool.cn");
final HttpRequest httpRequest = new HttpRequest(urlBuilder);
- httpRequest.setMethod(Method.GET).execute();
+ httpRequest.method(Method.GET).execute();
}
@Test
diff --git a/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java b/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java
index 56c06b816..8487e9105 100755
--- a/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java
@@ -5,6 +5,9 @@ import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.regex.ReUtil;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.meta.Header;
+import cn.hutool.http.meta.Method;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
diff --git a/hutool-http/src/test/java/cn/hutool/http/Issue2531Test.java b/hutool-http/src/test/java/cn/hutool/http/Issue2531Test.java
index caf2dddc8..88d928c3f 100755
--- a/hutool-http/src/test/java/cn/hutool/http/Issue2531Test.java
+++ b/hutool-http/src/test/java/cn/hutool/http/Issue2531Test.java
@@ -3,6 +3,8 @@ package cn.hutool.http;
import cn.hutool.core.lang.Console;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.UrlBuilder;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.client.engine.jdk.HttpResponse;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
diff --git a/hutool-http/src/test/java/cn/hutool/http/IssueI5TPSYTest.java b/hutool-http/src/test/java/cn/hutool/http/IssueI5TPSYTest.java
index 3de700e58..6b8133729 100755
--- a/hutool-http/src/test/java/cn/hutool/http/IssueI5TPSYTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/IssueI5TPSYTest.java
@@ -1,6 +1,8 @@
package cn.hutool.http;
import cn.hutool.core.lang.Console;
+import cn.hutool.http.client.engine.jdk.HttpResponse;
+import cn.hutool.http.meta.Header;
import org.junit.Ignore;
import org.junit.Test;
diff --git a/hutool-http/src/test/java/cn/hutool/http/IssueI5WAV4Test.java b/hutool-http/src/test/java/cn/hutool/http/IssueI5WAV4Test.java
index 1bef9bfca..2db3bfa8c 100755
--- a/hutool-http/src/test/java/cn/hutool/http/IssueI5WAV4Test.java
+++ b/hutool-http/src/test/java/cn/hutool/http/IssueI5WAV4Test.java
@@ -1,5 +1,6 @@
package cn.hutool.http;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
import cn.hutool.json.JSONUtil;
import org.junit.Ignore;
import org.junit.Test;
diff --git a/hutool-http/src/test/java/cn/hutool/http/IssueI5XBCFTest.java b/hutool-http/src/test/java/cn/hutool/http/IssueI5XBCFTest.java
index f41f0199a..2dabccc15 100755
--- a/hutool-http/src/test/java/cn/hutool/http/IssueI5XBCFTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/IssueI5XBCFTest.java
@@ -1,6 +1,8 @@
package cn.hutool.http;
import cn.hutool.core.lang.Console;
+import cn.hutool.http.client.engine.jdk.HttpResponse;
+import cn.hutool.http.meta.Header;
import org.brotli.dec.BrotliInputStream;
import org.junit.Ignore;
import org.junit.Test;
diff --git a/hutool-http/src/test/java/cn/hutool/http/RestTest.java b/hutool-http/src/test/java/cn/hutool/http/RestTest.java
index 8a5da21fe..5ccbba7af 100644
--- a/hutool-http/src/test/java/cn/hutool/http/RestTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/RestTest.java
@@ -1,6 +1,8 @@
package cn.hutool.http;
import cn.hutool.core.lang.Console;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.meta.Header;
import cn.hutool.json.JSONUtil;
import org.junit.Assert;
import org.junit.Ignore;
@@ -20,7 +22,7 @@ public class RestTest {
.body(JSONUtil.ofObj()
.set("aaa", "aaaValue")
.set("键2", "值2").toString());
- Assert.assertEquals("application/json;charset=UTF-8", request.header("Content-Type"));
+ Assert.assertEquals("application/json;charset=UTF-8", request.header(Header.CONTENT_TYPE));
}
@Test
diff --git a/hutool-http/src/test/java/cn/hutool/http/UploadTest.java b/hutool-http/src/test/java/cn/hutool/http/UploadTest.java
index 26e7da5b7..497eb44e3 100644
--- a/hutool-http/src/test/java/cn/hutool/http/UploadTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/UploadTest.java
@@ -2,6 +2,9 @@ package cn.hutool.http;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console;
+import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.client.engine.jdk.HttpResponse;
+import cn.hutool.http.meta.Header;
import org.junit.Ignore;
import org.junit.Test;
diff --git a/hutool-http/src/test/java/cn/hutool/http/body/MultipartBodyTest.java b/hutool-http/src/test/java/cn/hutool/http/body/MultipartBodyTest.java
index 3a895bd62..86270de53 100644
--- a/hutool-http/src/test/java/cn/hutool/http/body/MultipartBodyTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/body/MultipartBodyTest.java
@@ -2,7 +2,8 @@ package cn.hutool.http.body;
import cn.hutool.core.io.resource.StringResource;
import cn.hutool.core.util.CharsetUtil;
-import cn.hutool.http.HttpResource;
+import cn.hutool.core.io.resource.HttpResource;
+import cn.hutool.http.client.body.MultipartBody;
import org.junit.Assert;
import org.junit.Test;
diff --git a/hutool-http/src/test/java/cn/hutool/http/client/HttpClient4EngineTest.java b/hutool-http/src/test/java/cn/hutool/http/client/HttpClient4EngineTest.java
new file mode 100755
index 000000000..04fed763c
--- /dev/null
+++ b/hutool-http/src/test/java/cn/hutool/http/client/HttpClient4EngineTest.java
@@ -0,0 +1,23 @@
+package cn.hutool.http.client;
+
+import cn.hutool.core.lang.Console;
+import cn.hutool.http.meta.Method;
+import cn.hutool.http.client.engine.httpclient4.HttpClient4Engine;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class HttpClient4EngineTest {
+
+ @SuppressWarnings("resource")
+ @Test
+ @Ignore
+ public void getTest() {
+ final ClientEngine engine = new HttpClient4Engine();
+
+ 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.body());
+ }
+}
diff --git a/hutool-http/src/test/java/cn/hutool/http/client/HttpClient5EngineTest.java b/hutool-http/src/test/java/cn/hutool/http/client/HttpClient5EngineTest.java
new file mode 100755
index 000000000..6f069722b
--- /dev/null
+++ b/hutool-http/src/test/java/cn/hutool/http/client/HttpClient5EngineTest.java
@@ -0,0 +1,23 @@
+package cn.hutool.http.client;
+
+import cn.hutool.core.lang.Console;
+import cn.hutool.http.meta.Method;
+import cn.hutool.http.client.engine.httpclient5.HttpClient5Engine;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class HttpClient5EngineTest {
+
+ @SuppressWarnings("resource")
+ @Test
+ @Ignore
+ public void getTest() {
+ final ClientEngine engine = new HttpClient5Engine();
+
+ 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.body());
+ }
+}
diff --git a/hutool-http/src/test/java/cn/hutool/http/client/OkHttpEngineTest.java b/hutool-http/src/test/java/cn/hutool/http/client/OkHttpEngineTest.java
new file mode 100755
index 000000000..83714ffb6
--- /dev/null
+++ b/hutool-http/src/test/java/cn/hutool/http/client/OkHttpEngineTest.java
@@ -0,0 +1,23 @@
+package cn.hutool.http.client;
+
+import cn.hutool.core.lang.Console;
+import cn.hutool.http.client.engine.okhttp.OkHttpEngine;
+import cn.hutool.http.meta.Method;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class OkHttpEngineTest {
+
+ @SuppressWarnings("resource")
+ @Test
+ @Ignore
+ public void getTest(){
+ final ClientEngine engine = new OkHttpEngine();
+
+ 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.body());
+ }
+}
diff --git a/hutool-http/src/test/java/cn/hutool/http/server/BlankServerTest.java b/hutool-http/src/test/java/cn/hutool/http/server/BlankServerTest.java
index 2bc1914a5..37670d0a6 100755
--- a/hutool-http/src/test/java/cn/hutool/http/server/BlankServerTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/server/BlankServerTest.java
@@ -1,6 +1,6 @@
package cn.hutool.http.server;
-import cn.hutool.http.ContentType;
+import cn.hutool.http.meta.ContentType;
import cn.hutool.http.HttpUtil;
public class BlankServerTest {
diff --git a/hutool-http/src/test/java/cn/hutool/http/server/SimpleServerTest.java b/hutool-http/src/test/java/cn/hutool/http/server/SimpleServerTest.java
index ad9f60a3f..8b7a8be18 100644
--- a/hutool-http/src/test/java/cn/hutool/http/server/SimpleServerTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/server/SimpleServerTest.java
@@ -4,8 +4,8 @@ import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.net.multipart.UploadFile;
-import cn.hutool.http.ContentType;
-import cn.hutool.http.Header;
+import cn.hutool.http.meta.ContentType;
+import cn.hutool.http.meta.Header;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
diff --git a/hutool-log/pom.xml b/hutool-log/pom.xml
index 49c78221b..d361408e2 100755
--- a/hutool-log/pom.xml
+++ b/hutool-log/pom.xml
@@ -18,7 +18,7 @@
- 2.0.1
+ 2.0.3
1.3.0
1.2.17
@@ -27,7 +27,7 @@
1.3.6
3.4.3.Final
- 0.43.4
+ 0.44.3