From c1fe82d2ce18525689603b1bb6e8439a67278a0d Mon Sep 17 00:00:00 2001 From: Looly Date: Wed, 26 Oct 2022 00:11:19 +0800 Subject: [PATCH] add http engine --- .../cn/hutool/core/io/EmptyInputStream.java | 61 +++++ .../core/io/resource}/HttpResource.java | 5 +- hutool-http/pom.xml | 28 ++- .../java/cn/hutool/http/GlobalHeaders.java | 2 + .../cn/hutool/http/GlobalInterceptor.java | 8 +- .../main/java/cn/hutool/http/HttpConfig.java | 34 ++- .../java/cn/hutool/http/HttpGlobalConfig.java | 2 +- .../main/java/cn/hutool/http/HttpStatus.java | 222 ----------------- .../main/java/cn/hutool/http/HttpUtil.java | 6 +- .../cn/hutool/http/client/ClientEngine.java | 27 +++ .../java/cn/hutool/http/client/Headers.java | 227 ++++++++++++++++++ .../http/{ => client}/HttpDownloader.java | 5 +- .../java/cn/hutool/http/client/Request.java | 212 ++++++++++++++++ .../java/cn/hutool/http/client/Response.java | 134 +++++++++++ .../http/{ => client}/body/BytesBody.java | 17 +- .../{ => client}/body/FormUrlEncodedBody.java | 2 +- .../http/{ => client}/body/MultipartBody.java | 5 +- .../body}/MultipartOutputStream.java | 6 +- .../http/{ => client}/body/RequestBody.java | 2 +- .../hutool/http/client/body/ResourceBody.java | 49 ++++ .../http/{ => client}/body/package-info.java | 4 +- .../cookie/GlobalCookieManager.java | 4 +- .../cookie/ThreadLocalCookieStore.java | 2 +- .../{ => client}/cookie/package-info.java | 4 +- .../http/client/engine/EngineFactory.java | 13 + .../engine/httpclient4/HttpClient4Engine.java | 112 +++++++++ .../httpclient4/HttpClient4Response.java | 98 ++++++++ .../engine/httpclient4/RequestBodyEntity.java | 72 ++++++ .../engine/httpclient4/package-info.java | 7 + .../http/client/engine/httpclient4/pom.xml | 66 +++++ .../engine/httpclient5/HttpClient5Engine.java | 108 +++++++++ .../httpclient5/HttpClient5Response.java | 98 ++++++++ .../engine/httpclient5/RequestBodyEntity.java | 70 ++++++ .../engine/httpclient5/package-info.java | 7 + .../{ => client/engine/jdk}/HttpBase.java | 68 +----- .../engine/jdk}/HttpConnection.java | 7 +- .../engine/jdk}/HttpInputStream.java | 5 +- .../engine/jdk}/HttpInterceptor.java | 2 +- .../{ => client/engine/jdk}/HttpRequest.java | 55 ++--- .../{ => client/engine/jdk}/HttpResponse.java | 12 +- .../http/client/engine/jdk/package-info.java | 7 + .../client/engine/okhttp/OkHttpEngine.java | 70 ++++++ .../engine/okhttp/OkHttpRequestBody.java | 33 +++ .../client/engine/okhttp/OkHttpResponse.java | 70 ++++++ .../client/engine/okhttp/package-info.java | 5 + .../http/client/engine/package-info.java | 6 + .../cn/hutool/http/client/package-info.java | 5 + .../hutool/http/{ => meta}/ContentType.java | 2 +- .../cn/hutool/http/{ => meta}/Header.java | 2 +- .../{Status.java => meta/HttpStatus.java} | 39 ++- .../cn/hutool/http/{ => meta}/Method.java | 2 +- .../cn/hutool/http/meta/package-info.java | 6 + .../hutool/http/server/HttpServerRequest.java | 4 +- .../http/server/HttpServerResponse.java | 6 +- .../{Engine.java => BrowserEngine.java} | 36 +-- .../cn/hutool/http/useragent/UserAgent.java | 6 +- .../http/useragent/UserAgentParser.java | 8 +- .../cn/hutool/http/webservice/SoapClient.java | 6 +- .../java/cn/hutool/http/ContentTypeTest.java | 1 + .../java/cn/hutool/http/DownloadTest.java | 1 + .../java/cn/hutool/http/HttpRequestTest.java | 6 +- .../java/cn/hutool/http/HttpUtilTest.java | 3 + .../java/cn/hutool/http/Issue2531Test.java | 2 + .../java/cn/hutool/http/IssueI5TPSYTest.java | 2 + .../java/cn/hutool/http/IssueI5WAV4Test.java | 1 + .../java/cn/hutool/http/IssueI5XBCFTest.java | 2 + .../test/java/cn/hutool/http/RestTest.java | 4 +- .../test/java/cn/hutool/http/UploadTest.java | 3 + .../hutool/http/body/MultipartBodyTest.java | 3 +- .../http/client/HttpClient4EngineTest.java | 23 ++ .../http/client/HttpClient5EngineTest.java | 23 ++ .../hutool/http/client/OkHttpEngineTest.java | 23 ++ .../hutool/http/server/BlankServerTest.java | 2 +- .../hutool/http/server/SimpleServerTest.java | 4 +- hutool-log/pom.xml | 4 +- 75 files changed, 1878 insertions(+), 410 deletions(-) create mode 100755 hutool-core/src/main/java/cn/hutool/core/io/EmptyInputStream.java rename {hutool-http/src/main/java/cn/hutool/http => hutool-core/src/main/java/cn/hutool/core/io/resource}/HttpResource.java (90%) delete mode 100644 hutool-http/src/main/java/cn/hutool/http/HttpStatus.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/ClientEngine.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/Headers.java rename hutool-http/src/main/java/cn/hutool/http/{ => client}/HttpDownloader.java (96%) create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/Request.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/Response.java rename hutool-http/src/main/java/cn/hutool/http/{ => client}/body/BytesBody.java (60%) rename hutool-http/src/main/java/cn/hutool/http/{ => client}/body/FormUrlEncodedBody.java (95%) rename hutool-http/src/main/java/cn/hutool/http/{ => client}/body/MultipartBody.java (94%) rename hutool-http/src/main/java/cn/hutool/http/{ => client/body}/MultipartOutputStream.java (96%) rename hutool-http/src/main/java/cn/hutool/http/{ => client}/body/RequestBody.java (92%) create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/body/ResourceBody.java rename hutool-http/src/main/java/cn/hutool/http/{ => client}/body/package-info.java (57%) rename hutool-http/src/main/java/cn/hutool/http/{ => client}/cookie/GlobalCookieManager.java (96%) rename hutool-http/src/main/java/cn/hutool/http/{ => client}/cookie/ThreadLocalCookieStore.java (97%) rename hutool-http/src/main/java/cn/hutool/http/{ => client}/cookie/package-info.java (53%) create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/EngineFactory.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Engine.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Response.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/RequestBodyEntity.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/package-info.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/pom.xml create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Engine.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Response.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/RequestBodyEntity.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/package-info.java rename hutool-http/src/main/java/cn/hutool/http/{ => client/engine/jdk}/HttpBase.java (82%) rename hutool-http/src/main/java/cn/hutool/http/{ => client/engine/jdk}/HttpConnection.java (98%) rename hutool-http/src/main/java/cn/hutool/http/{ => client/engine/jdk}/HttpInputStream.java (93%) rename hutool-http/src/main/java/cn/hutool/http/{ => client/engine/jdk}/HttpInterceptor.java (96%) rename hutool-http/src/main/java/cn/hutool/http/{ => client/engine/jdk}/HttpRequest.java (98%) rename hutool-http/src/main/java/cn/hutool/http/{ => client/engine/jdk}/HttpResponse.java (98%) create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/package-info.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpEngine.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpRequestBody.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpResponse.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/package-info.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/engine/package-info.java create mode 100755 hutool-http/src/main/java/cn/hutool/http/client/package-info.java rename hutool-http/src/main/java/cn/hutool/http/{ => meta}/ContentType.java (99%) rename hutool-http/src/main/java/cn/hutool/http/{ => meta}/Header.java (99%) rename hutool-http/src/main/java/cn/hutool/http/{Status.java => meta/HttpStatus.java} (77%) rename hutool-http/src/main/java/cn/hutool/http/{ => meta}/Method.java (82%) create mode 100755 hutool-http/src/main/java/cn/hutool/http/meta/package-info.java rename hutool-http/src/main/java/cn/hutool/http/useragent/{Engine.java => BrowserEngine.java} (52%) create mode 100755 hutool-http/src/test/java/cn/hutool/http/client/HttpClient4EngineTest.java create mode 100755 hutool-http/src/test/java/cn/hutool/http/client/HttpClient5EngineTest.java create mode 100755 hutool-http/src/test/java/cn/hutool/http/client/OkHttpEngineTest.java 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