mirror of
https://gitee.com/dromara/hutool.git
synced 2025-11-18 08:50:42 +08:00
add http engine
This commit is contained in:
@@ -22,6 +22,14 @@
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.soap</groupId>
|
||||
<artifactId>javax.xml.soap-api</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 第三方HTTP客户端库 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
@@ -29,11 +37,18 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.soap</groupId>
|
||||
<artifactId>javax.xml.soap-api</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.13</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>4.10.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-json</artifactId>
|
||||
@@ -46,5 +61,12 @@
|
||||
<version>0.1.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- 仅用于测试 -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.7.25</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 全局的拦截器<br>
|
||||
* 包括请求拦截器和响应拦截器
|
||||
@@ -69,7 +73,7 @@ public enum GlobalInterceptor {
|
||||
/**
|
||||
* 复制请求过滤器列表
|
||||
*
|
||||
* @return {@link cn.hutool.http.HttpInterceptor.Chain}
|
||||
* @return {@link HttpInterceptor.Chain}
|
||||
*/
|
||||
HttpInterceptor.Chain<HttpRequest> getCopiedRequestInterceptor() {
|
||||
final HttpInterceptor.Chain<HttpRequest> copied = new HttpInterceptor.Chain<>();
|
||||
@@ -82,7 +86,7 @@ public enum GlobalInterceptor {
|
||||
/**
|
||||
* 复制响应过滤器列表
|
||||
*
|
||||
* @return {@link cn.hutool.http.HttpInterceptor.Chain}
|
||||
* @return {@link HttpInterceptor.Chain}
|
||||
*/
|
||||
HttpInterceptor.Chain<HttpResponse> getCopiedResponseInterceptor() {
|
||||
final HttpInterceptor.Chain<HttpResponse> copied = new HttpInterceptor.Chain<>();
|
||||
|
||||
@@ -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异常。<br>
|
||||
@@ -79,16 +82,16 @@ public class HttpConfig {
|
||||
/**
|
||||
* 请求前的拦截器,用于在请求前重新编辑请求
|
||||
*/
|
||||
final HttpInterceptor.Chain<HttpRequest> requestInterceptors = GlobalInterceptor.INSTANCE.getCopiedRequestInterceptor();
|
||||
public final HttpInterceptor.Chain<HttpRequest> requestInterceptors = GlobalInterceptor.INSTANCE.getCopiedRequestInterceptor();
|
||||
/**
|
||||
* 响应后的拦截器,用于在响应后处理逻辑
|
||||
*/
|
||||
final HttpInterceptor.Chain<HttpResponse> responseInterceptors = GlobalInterceptor.INSTANCE.getCopiedResponseInterceptor();
|
||||
public final HttpInterceptor.Chain<HttpResponse> responseInterceptors = GlobalInterceptor.INSTANCE.getCopiedResponseInterceptor();
|
||||
|
||||
/**
|
||||
* 重定向时是否使用拦截器
|
||||
*/
|
||||
boolean interceptorOnRedirect;
|
||||
public boolean interceptorOnRedirect;
|
||||
|
||||
/**
|
||||
* 设置超时,单位:毫秒<br>
|
||||
@@ -297,4 +300,13 @@ public class HttpConfig {
|
||||
this.interceptorOnRedirect = interceptorOnRedirect;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否忽略响应读取时可能的EOF异常。
|
||||
*
|
||||
* @return 是否忽略响应读取时可能的EOF异常。
|
||||
*/
|
||||
public boolean isIgnoreEOFError() {
|
||||
return ignoreEOFError;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* HTTP资源,可自定义Content-Type
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.17
|
||||
*/
|
||||
public class HttpResource implements Resource, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Resource resource;
|
||||
private final String contentType;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param resource 资源,非空
|
||||
* @param contentType Content-Type类型,{@code null}表示不设置
|
||||
*/
|
||||
public HttpResource(final Resource resource, final String contentType) {
|
||||
this.resource = Assert.notNull(resource, "Resource must be not null !");
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return resource.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() {
|
||||
return resource.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getStream() {
|
||||
return resource.getStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义Content-Type类型
|
||||
*
|
||||
* @return Content-Type类型
|
||||
*/
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
}
|
||||
@@ -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.<br>
|
||||
* 见:RFC-7231
|
||||
*/
|
||||
public static final int HTTP_TEMP_REDIRECT = 307;
|
||||
|
||||
/**
|
||||
* HTTP 1.1 Status-Code 308: Permanent Redirect 永久重定向<br>
|
||||
* 见: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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
27
hutool-http/src/main/java/cn/hutool/http/client/ClientEngine.java
Executable file
27
hutool-http/src/main/java/cn/hutool/http/client/ClientEngine.java
Executable file
@@ -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();
|
||||
}
|
||||
227
hutool-http/src/main/java/cn/hutool/http/client/Headers.java
Executable file
227
hutool-http/src/main/java/cn/hutool/http/client/Headers.java
Executable file
@@ -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 <T> 返回对象类型,方便链式编程
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public interface Headers<T extends Headers<T>> {
|
||||
|
||||
// region ----------------------------------------------------------- headers
|
||||
|
||||
/**
|
||||
* 获取headers
|
||||
*
|
||||
* @return Headers Map
|
||||
*/
|
||||
Map<String, List<String>> headers();
|
||||
|
||||
/**
|
||||
* 设置一个header<br>
|
||||
* 如果覆盖模式,则替换之前的值,否则加入到值列表中<br>
|
||||
* 如果给定值为{@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<String> values = headers().get(name);
|
||||
if (ArrayUtil.isNotEmpty(values)) {
|
||||
return values.get(0);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置一个header<br>
|
||||
* 如果覆盖模式,则替换之前的值,否则加入到值列表中
|
||||
*
|
||||
* @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<br>
|
||||
* 覆盖模式,则替换之前的值
|
||||
*
|
||||
* @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<br>
|
||||
* 自定义Cookie后会覆盖Hutool的默认Cookie行为
|
||||
*
|
||||
* @param cookies Cookie值数组,如果为{@code null}则设置无效,使用默认Cookie行为
|
||||
* @return this
|
||||
* @since 5.4.1
|
||||
*/
|
||||
default T cookie(final Collection<HttpCookie> cookies) {
|
||||
return cookie(CollUtil.isEmpty(cookies) ? null : cookies.toArray(new HttpCookie[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Cookie<br>
|
||||
* 自定义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<br>
|
||||
* 自定义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置为空。<br>
|
||||
* 如果想重新启用Cookie,请调用:{@link #cookie(String)}方法自定义Cookie。<br>
|
||||
* 如果想启动默认的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
|
||||
}
|
||||
@@ -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;
|
||||
212
hutool-http/src/main/java/cn/hutool/http/client/Request.java
Executable file
212
hutool-http/src/main/java/cn/hutool/http/client/Request.java
Executable file
@@ -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<Request> {
|
||||
|
||||
/**
|
||||
* 构建一个HTTP请求<br>
|
||||
* 对于传入的URL,可以自定义是否解码已经编码的内容,设置见{@link HttpGlobalConfig#setDecodeUrl(boolean)}<br>
|
||||
* 在构建Http请求时,用户传入的URL可能有编码后和未编码的内容混合在一起,如果{@link HttpGlobalConfig#isDecodeUrl()}为{@code true},则会统一解码编码后的参数,<br>
|
||||
* 按照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请求<br>
|
||||
* 对于传入的URL,可以自定义是否解码已经编码的内容。<br>
|
||||
* 在构建Http请求时,用户传入的URL可能有编码后和未编码的内容混合在一起,如果charset参数不为{@code null},则会统一解码编码后的参数,<br>
|
||||
* 按照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请求<br>
|
||||
*
|
||||
* @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<String, List<String>> 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<String, List<String>> 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<br>
|
||||
* 如果覆盖模式,则替换之前的值,否则加入到值列表中<br>
|
||||
* 如果给定值为{@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<String> values = headers.get(name.trim());
|
||||
if (isOverride || CollUtil.isEmpty(values)) {
|
||||
final ArrayList<String> 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;
|
||||
}
|
||||
}
|
||||
134
hutool-http/src/main/java/cn/hutool/http/client/Response.java
Executable file
134
hutool-http/src/main/java/cn/hutool/http/client/Response.java
Executable file
@@ -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获取头信息<br>
|
||||
* 根据RFC2616规范,header的name不区分大小写
|
||||
*
|
||||
* @param name Header名
|
||||
* @return Header值
|
||||
*/
|
||||
String header(final String name);
|
||||
|
||||
/**
|
||||
* 获取字符集编码
|
||||
*
|
||||
* @return 字符集
|
||||
*/
|
||||
Charset charset();
|
||||
|
||||
/**
|
||||
* 获得服务区响应流<br>
|
||||
* 流获取后处理完毕需关闭此类
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取内容长度,以下情况长度无效:
|
||||
* <ul>
|
||||
* <li>Transfer-Encoding: Chunked</li>
|
||||
* <li>Content-Encoding: XXX</li>
|
||||
* </ul>
|
||||
* 参考:<a href="https://blog.csdn.net/jiang7701037/article/details/86304302">https://blog.csdn.net/jiang7701037/article/details/86304302</a>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.hutool.http.body;
|
||||
package cn.hutool.http.client.body;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
|
||||
49
hutool-http/src/main/java/cn/hutool/http/client/body/ResourceBody.java
Executable file
49
hutool-http/src/main/java/cn/hutool/http/client/body/ResourceBody.java
Executable file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 请求体封装实现
|
||||
*
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.http.body;
|
||||
package cn.hutool.http.client.body;
|
||||
@@ -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;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.hutool.http.cookie;
|
||||
package cn.hutool.http.client.cookie;
|
||||
|
||||
import java.net.CookieManager;
|
||||
import java.net.CookieStore;
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* 自定义Cookie
|
||||
*
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.http.cookie;
|
||||
package cn.hutool.http.client.cookie;
|
||||
13
hutool-http/src/main/java/cn/hutool/http/client/engine/EngineFactory.java
Executable file
13
hutool-http/src/main/java/cn/hutool/http/client/engine/EngineFactory.java
Executable file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<Header> toHeaderList(final Map<String, List<String>> headersMap) {
|
||||
final List<Header> result = new ArrayList<>();
|
||||
headersMap.forEach((k, v1) -> v1.forEach((v2) -> result.add(new BasicHeader(k, v2))));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -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响应包装<br>
|
||||
* 通过包装{@link CloseableHttpResponse},实现获取响应状态码、响应头、响应体等内容
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public class HttpClient4Response implements Response {
|
||||
|
||||
/**
|
||||
* HttpClient的响应对象
|
||||
*/
|
||||
private final CloseableHttpResponse rawRes;
|
||||
/**
|
||||
* 请求时的默认编码
|
||||
*/
|
||||
private final Charset requestCharset;
|
||||
|
||||
/**
|
||||
* 构造<br>
|
||||
* 通过传入一个请求时的编码,当无法获取响应内容的编码时,默认使用响应时的编码
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Apache HttpClient 4.x实现<br>
|
||||
* 文档见:https://hc.apache.org/httpcomponents-client-4.5.x/index.html
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package cn.hutool.http.client.engine.httpclient4;
|
||||
66
hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/pom.xml
Executable file
66
hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/pom.xml
Executable file
@@ -0,0 +1,66 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>6.0.0.M1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-http</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>Hutool Http客户端</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml.soap</groupId>
|
||||
<artifactId>javax.xml.soap-api</artifactId>
|
||||
<version>1.4.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 第三方HTTP客户端库 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
<artifactId>httpclient5</artifactId>
|
||||
<version>5.1.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.13</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-json</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.brotli</groupId>
|
||||
<artifactId>dec</artifactId>
|
||||
<version>0.1.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<!-- 仅用于测试 -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
<version>1.7.25</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -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<Header> toHeaderList(final Map<String, List<String>> headersMap) {
|
||||
final List<Header> result = new ArrayList<>();
|
||||
headersMap.forEach((k, v1) -> v1.forEach((v2) -> result.add(new BasicHeader(k, v2))));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -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响应包装<br>
|
||||
* 通过包装{@link CloseableHttpResponse},实现获取响应状态码、响应头、响应体等内容
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public class HttpClient5Response implements Response {
|
||||
|
||||
/**
|
||||
* HttpClient的响应对象
|
||||
*/
|
||||
private final CloseableHttpResponse rawRes;
|
||||
/**
|
||||
* 请求时的默认编码
|
||||
*/
|
||||
private final Charset requestCharset;
|
||||
|
||||
/**
|
||||
* 构造<br>
|
||||
* 通过传入一个请求时的编码,当无法获取响应内容的编码时,默认使用响应时的编码
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Apache HttpClient 5.1.x实现<br>
|
||||
* 文档见:https://hc.apache.org/httpcomponents-client-5.1.x/index.html
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package cn.hutool.http.client.engine.httpclient5;
|
||||
@@ -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<T> {
|
||||
public abstract class HttpBase<T extends HttpBase<T>> implements Headers<T> {
|
||||
|
||||
/**
|
||||
* 默认的请求编码、URL的encode、decode编码
|
||||
@@ -56,21 +58,6 @@ public abstract class HttpBase<T> {
|
||||
|
||||
// ---------------------------------------------------------------- Headers start
|
||||
|
||||
/**
|
||||
* 根据name获取头信息<br>
|
||||
* 根据RFC2616规范,header的name不区分大小写
|
||||
*
|
||||
* @param name Header名
|
||||
* @return Header值
|
||||
*/
|
||||
public String header(final String name) {
|
||||
final List<String> values = headerList(name);
|
||||
if (CollUtil.isEmpty(values)) {
|
||||
return null;
|
||||
}
|
||||
return values.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据name获取头信息列表
|
||||
*
|
||||
@@ -90,14 +77,14 @@ public abstract class HttpBase<T> {
|
||||
/**
|
||||
* 根据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<T> {
|
||||
* @param isOverride 是否覆盖已有值
|
||||
* @return T 本身
|
||||
*/
|
||||
@Override
|
||||
public T header(final String name, final String value, final boolean isOverride) {
|
||||
if (null != name && null != value) {
|
||||
final List<String> values = headers.get(name.trim());
|
||||
@@ -123,43 +111,6 @@ public abstract class HttpBase<T> {
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置一个header<br>
|
||||
* 如果覆盖模式,则替换之前的值,否则加入到值列表中
|
||||
*
|
||||
* @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<br>
|
||||
* 覆盖模式,则替换之前的值
|
||||
*
|
||||
* @param name Header名
|
||||
* @param value Header值
|
||||
* @return T 本身
|
||||
*/
|
||||
public T header(final Header name, final String value) {
|
||||
return header(name.toString(), value, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置一个header<br>
|
||||
* 覆盖模式,则替换之前的值
|
||||
*
|
||||
* @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<T> {
|
||||
*
|
||||
* @return Headers Map
|
||||
*/
|
||||
@Override
|
||||
public Map<String, List<String>> headers() {
|
||||
return Collections.unmodifiableMap(headers);
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.hutool.http;
|
||||
package cn.hutool.http.client.engine.jdk;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
@@ -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<HttpRequest> {
|
||||
this.charset(charset);
|
||||
}
|
||||
// 给定一个默认头信息
|
||||
this.header(GlobalHeaders.INSTANCE.headers);
|
||||
this.header(GlobalHeaders.INSTANCE.headers());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -309,28 +318,6 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
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}<br>
|
||||
* 在{@link #execute()} 执行前此对象为null
|
||||
@@ -342,6 +329,16 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
return this.httpConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Http请求方法
|
||||
*
|
||||
* @return {@link Method}
|
||||
* @since 4.1.8
|
||||
*/
|
||||
public Method getMethod() {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置请求方法
|
||||
*
|
||||
@@ -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<HttpResponse> 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<HttpResponse> 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();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 基于JDK的HttpUrlConnection封装的HTTP客户端
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
package cn.hutool.http.client.engine.jdk;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* OKHttp3封装<br>
|
||||
* 文档见:https://square.github.io/okhttp/
|
||||
*/
|
||||
package cn.hutool.http.client.engine.okhttp;
|
||||
6
hutool-http/src/main/java/cn/hutool/http/client/engine/package-info.java
Executable file
6
hutool-http/src/main/java/cn/hutool/http/client/engine/package-info.java
Executable file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Http客户端引擎实现
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package cn.hutool.http.client.engine;
|
||||
5
hutool-http/src/main/java/cn/hutool/http/client/package-info.java
Executable file
5
hutool-http/src/main/java/cn/hutool/http/client/package-info.java
Executable file
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* HTTP请求客户端封装
|
||||
* @author looly
|
||||
*/
|
||||
package cn.hutool.http.client;
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.hutool.http;
|
||||
package cn.hutool.http.meta;
|
||||
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.hutool.http;
|
||||
package cn.hutool.http.meta;
|
||||
|
||||
/**
|
||||
* Http 头域
|
||||
@@ -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.<br>
|
||||
* 见:RFC-7231
|
||||
*/
|
||||
int HTTP_TEMP_REDIRECT = 307;
|
||||
|
||||
/**
|
||||
* HTTP 1.1 Status-Code 308: Permanent Redirect 永久重定向<br>
|
||||
* 见: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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package cn.hutool.http;
|
||||
package cn.hutool.http.meta;
|
||||
|
||||
/**
|
||||
* Http方法枚举
|
||||
6
hutool-http/src/main/java/cn/hutool/http/meta/package-info.java
Executable file
6
hutool-http/src/main/java/cn/hutool/http/meta/package-info.java
Executable file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Http元数据信息,包括Header枚举、状态码、Http方法、枚举Content-Type等
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
package cn.hutool.http.meta;
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Engine> 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<BrowserEngine> 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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
23
hutool-http/src/test/java/cn/hutool/http/client/HttpClient4EngineTest.java
Executable file
23
hutool-http/src/test/java/cn/hutool/http/client/HttpClient4EngineTest.java
Executable file
@@ -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());
|
||||
}
|
||||
}
|
||||
23
hutool-http/src/test/java/cn/hutool/http/client/HttpClient5EngineTest.java
Executable file
23
hutool-http/src/test/java/cn/hutool/http/client/HttpClient5EngineTest.java
Executable file
@@ -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());
|
||||
}
|
||||
}
|
||||
23
hutool-http/src/test/java/cn/hutool/http/client/OkHttpEngineTest.java
Executable file
23
hutool-http/src/test/java/cn/hutool/http/client/OkHttpEngineTest.java
Executable file
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user