diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/EngineRequestBuilder.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/EngineRequestBuilder.java
new file mode 100644
index 000000000..1020f8ef5
--- /dev/null
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/EngineRequestBuilder.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2024 Hutool Team and hutool.cn
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.dromara.hutool.http.client.engine;
+
+import org.dromara.hutool.http.client.Request;
+
+/**
+ * 引擎的请求对象构建器
引擎请求对象类型
+ */
+public interface EngineRequestBuilder {
+
+ /**
+ * 构建引擎请求对象
+ *
+ * @param message 请求对象
+ * @return 引擎请求对象
+ */
+ T build(Request message);
+}
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Engine.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Engine.java
index aa74f486d..5ead5af0e 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Engine.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Engine.java
@@ -23,15 +23,16 @@ import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.impl.client.*;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
import org.apache.http.message.BasicHeader;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.lang.Assert;
-import org.dromara.hutool.core.net.url.UrlBuilder;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.http.GlobalHeaders;
import org.dromara.hutool.http.HttpException;
@@ -39,10 +40,8 @@ import org.dromara.hutool.http.client.ApacheHttpClientConfig;
import org.dromara.hutool.http.client.ClientConfig;
import org.dromara.hutool.http.client.Request;
import org.dromara.hutool.http.client.Response;
-import org.dromara.hutool.http.client.body.HttpBody;
import org.dromara.hutool.http.client.cookie.InMemoryCookieStore;
import org.dromara.hutool.http.client.engine.AbstractClientEngine;
-import org.dromara.hutool.http.meta.HeaderName;
import org.dromara.hutool.http.proxy.ProxyInfo;
import org.dromara.hutool.http.ssl.SSLInfo;
@@ -77,10 +76,9 @@ public class HttpClient4Engine extends AbstractClientEngine {
public Response send(final Request message) {
initEngine();
- final HttpUriRequest request = buildRequest(message);
+ final HttpUriRequest request = HttpUriRequestBuilder.INSTANCE.build(message);
final CloseableHttpResponse response;
try {
- //return this.engine.execute(request, response -> new HttpClient4Response(response, message));
response = this.engine.execute(request);
} catch (final IOException e) {
throw new HttpException(e);
@@ -124,7 +122,7 @@ public class HttpClient4Engine extends AbstractClientEngine {
clientBuilder.setConnectionManager(buildConnectionManager(config));
// 实例级别默认请求配置
- clientBuilder.setDefaultRequestConfig(buildRequestConfig(config));
+ clientBuilder.setDefaultRequestConfig(buildDefaultRequestConfig(config));
// 缓存
if (config.isDisableCache()) {
@@ -146,40 +144,6 @@ public class HttpClient4Engine extends AbstractClientEngine {
this.engine = clientBuilder.build();
}
- /**
- * 构建请求体
- *
- * @param message {@link Request}
- * @return {@link HttpUriRequest}
- */
- private static HttpUriRequest buildRequest(final Request message) {
- final UrlBuilder url = message.handledUrl();
- Assert.notNull(url, "Request URL must be not null!");
-
- final RequestBuilder requestBuilder = RequestBuilder
- .create(message.method().name())
- .setUri(url.toURI());
-
- // 自定义单次请求配置
- requestBuilder.setConfig(buildRequestConfig(message));
-
- // 填充自定义头
- message.headers().forEach((k, v1) -> v1.forEach((v2) -> requestBuilder.addHeader(k, v2)));
-
- // 填充自定义消息体
- final HttpBody body = message.handledBody();
- if (null != body) {
- requestBuilder.setEntity(new HttpClient4BodyEntity(
- // 用户自定义的内容类型
- message.header(HeaderName.CONTENT_TYPE),
- message.contentEncoding(),
- message.isChunked(),
- body));
- }
-
- return requestBuilder.build();
- }
-
/**
* 获取默认头列表
*
@@ -229,31 +193,13 @@ public class HttpClient4Engine extends AbstractClientEngine {
return manager;
}
- /**
- * 构建请求配置,包括重定向
- *
- * @param request 请求
- * @return {@link RequestConfig}
- */
- private static RequestConfig buildRequestConfig(final Request request) {
- final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
- final int maxRedirects = request.maxRedirects();
- if (maxRedirects > 0) {
- requestConfigBuilder.setMaxRedirects(maxRedirects);
- } else {
- requestConfigBuilder.setRedirectsEnabled(false);
- }
-
- return requestConfigBuilder.build();
- }
-
/**
* 构建请求配置,包括连接请求超时和响应(读取)超时
*
* @param config {@link ClientConfig}
* @return {@link RequestConfig}
*/
- private static RequestConfig buildRequestConfig(final ClientConfig config) {
+ private static RequestConfig buildDefaultRequestConfig(final ClientConfig config) {
// 请求配置
final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpUriRequestBuilder.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpUriRequestBuilder.java
new file mode 100644
index 000000000..8a0ca5e08
--- /dev/null
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpUriRequestBuilder.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2024 Hutool Team and hutool.cn
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.dromara.hutool.http.client.engine.httpclient4;
+
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.client.methods.RequestBuilder;
+import org.dromara.hutool.core.lang.Assert;
+import org.dromara.hutool.core.net.url.UrlBuilder;
+import org.dromara.hutool.http.client.Request;
+import org.dromara.hutool.http.client.body.HttpBody;
+import org.dromara.hutool.http.client.engine.EngineRequestBuilder;
+import org.dromara.hutool.http.meta.HeaderName;
+
+/**
+ * HttpClient4请求构建器
+ *
+ * @author Looly
+ * @since 6.0.0
+ */
+public class HttpUriRequestBuilder implements EngineRequestBuilder {
+
+ /**
+ * 单例
+ */
+ public static final HttpUriRequestBuilder INSTANCE = new HttpUriRequestBuilder();
+
+ @Override
+ public HttpUriRequest build(final Request message) {
+ final UrlBuilder url = message.handledUrl();
+ Assert.notNull(url, "Request URL must be not null!");
+
+ final RequestBuilder requestBuilder = RequestBuilder
+ .create(message.method().name())
+ .setUri(url.toURI());
+
+ // 自定义单次请求配置
+ requestBuilder.setConfig(buildRequestConfig(message));
+
+ // 填充自定义头
+ message.headers().forEach((k, v1) -> v1.forEach((v2) -> requestBuilder.addHeader(k, v2)));
+
+ // 填充自定义消息体
+ final HttpBody body = message.handledBody();
+ if (null != body) {
+ requestBuilder.setEntity(new HttpClient4BodyEntity(
+ // 用户自定义的内容类型
+ message.header(HeaderName.CONTENT_TYPE),
+ message.contentEncoding(),
+ message.isChunked(),
+ body));
+ }
+
+ return requestBuilder.build();
+ }
+
+ /**
+ * 构建请求配置,包括重定向
+ *
+ * @param request 请求
+ * @return {@link RequestConfig}
+ */
+ private static RequestConfig buildRequestConfig(final Request request) {
+ final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
+ final int maxRedirects = request.maxRedirects();
+ if (maxRedirects > 0) {
+ requestConfigBuilder.setMaxRedirects(maxRedirects);
+ } else {
+ requestConfigBuilder.setRedirectsEnabled(false);
+ }
+
+ return requestConfigBuilder.build();
+ }
+}
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/ClassicHttpRequestBuilder.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/ClassicHttpRequestBuilder.java
new file mode 100644
index 000000000..9a0f8e4c7
--- /dev/null
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/ClassicHttpRequestBuilder.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2024 Hutool Team and hutool.cn
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.dromara.hutool.http.client.engine.httpclient5;
+
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.dromara.hutool.core.lang.Assert;
+import org.dromara.hutool.core.net.url.UrlBuilder;
+import org.dromara.hutool.http.client.Request;
+import org.dromara.hutool.http.client.body.HttpBody;
+import org.dromara.hutool.http.client.engine.EngineRequestBuilder;
+import org.dromara.hutool.http.meta.HeaderName;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * HttpClient5请求构建器
+ *
+ * @author Looly
+ * @since 6.0.0
+ */
+public class ClassicHttpRequestBuilder implements EngineRequestBuilder {
+
+ /**
+ * 单例
+ */
+ public static ClassicHttpRequestBuilder INSTANCE = new ClassicHttpRequestBuilder();
+
+ @Override
+ public ClassicHttpRequest build(final Request message) {
+ final UrlBuilder url = message.handledUrl();
+ Assert.notNull(url, "Request URL must be not null!");
+
+ final HttpUriRequestBase request = new HttpUriRequestBase(message.method().name(), url.toURI());
+
+ // 自定义单次请求配置
+ request.setConfig(buildRequestConfig(message));
+
+ // 填充自定义头
+ request.setHeaders(toHeaderList(message.headers()).toArray(new Header[0]));
+
+ // 填充自定义消息体
+ final HttpBody body = message.handledBody();
+ if (null != body) {
+ request.setEntity(new HttpClient5BodyEntity(
+ // 用户自定义的内容类型
+ message.header(HeaderName.CONTENT_TYPE),
+ message.contentEncoding(),
+ 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;
+ }
+
+ /**
+ * 构建请求配置,包括重定向配置
+ *
+ * @param request 请求
+ * @return {@link RequestConfig}
+ */
+ private static RequestConfig buildRequestConfig(final Request request) {
+ final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
+ final int maxRedirects = request.maxRedirects();
+ if (maxRedirects > 0) {
+ requestConfigBuilder.setMaxRedirects(maxRedirects);
+ } else {
+ requestConfigBuilder.setRedirectsEnabled(false);
+ }
+
+ return requestConfigBuilder.build();
+ }
+}
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Engine.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Engine.java
index f0e4ac950..b6518e660 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Engine.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Engine.java
@@ -18,7 +18,6 @@ package org.dromara.hutool.http.client.engine.httpclient5;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
-import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
@@ -34,7 +33,6 @@ import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.message.BasicHeader;
import org.dromara.hutool.core.io.IoUtil;
import org.dromara.hutool.core.lang.Assert;
-import org.dromara.hutool.core.net.url.UrlBuilder;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.http.GlobalHeaders;
import org.dromara.hutool.http.HttpException;
@@ -42,10 +40,8 @@ import org.dromara.hutool.http.client.ApacheHttpClientConfig;
import org.dromara.hutool.http.client.ClientConfig;
import org.dromara.hutool.http.client.Request;
import org.dromara.hutool.http.client.Response;
-import org.dromara.hutool.http.client.body.HttpBody;
import org.dromara.hutool.http.client.cookie.InMemoryCookieStore;
import org.dromara.hutool.http.client.engine.AbstractClientEngine;
-import org.dromara.hutool.http.meta.HeaderName;
import org.dromara.hutool.http.proxy.ProxyInfo;
import org.dromara.hutool.http.ssl.SSLInfo;
@@ -80,7 +76,7 @@ public class HttpClient5Engine extends AbstractClientEngine {
public Response send(final Request message) {
initEngine();
- final ClassicHttpRequest request = buildRequest(message);
+ final ClassicHttpRequest request = ClassicHttpRequestBuilder.INSTANCE.build(message);
final ClassicHttpResponse response;
try {
//return this.engine.execute(request, (response -> new HttpClient5Response(response, message)));
@@ -143,39 +139,6 @@ public class HttpClient5Engine extends AbstractClientEngine {
this.engine = clientBuilder.build();
}
- /**
- * 构建请求体
- *
- * @param message {@link Request}
- * @return {@link ClassicHttpRequest}
- */
- @SuppressWarnings("ConstantConditions")
- private static ClassicHttpRequest buildRequest(final Request message) {
- final UrlBuilder url = message.handledUrl();
- Assert.notNull(url, "Request URL must be not null!");
-
- final HttpUriRequestBase request = new HttpUriRequestBase(message.method().name(), url.toURI());
-
- // 自定义单次请求配置
- request.setConfig(buildRequestConfig(message));
-
- // 填充自定义头
- request.setHeaders(toHeaderList(message.headers()).toArray(new Header[0]));
-
- // 填充自定义消息体
- final HttpBody body = message.handledBody();
- if (null != body) {
- request.setEntity(new HttpClient5BodyEntity(
- // 用户自定义的内容类型
- message.header(HeaderName.CONTENT_TYPE),
- message.contentEncoding(),
- message.isChunked(),
- body));
- }
-
- return request;
- }
-
/**
* 获取默认头列表
*
@@ -234,24 +197,6 @@ public class HttpClient5Engine extends AbstractClientEngine {
return connectionManagerBuilder.build();
}
- /**
- * 构建请求配置,包括重定向配置
- *
- * @param request 请求
- * @return {@link RequestConfig}
- */
- private static RequestConfig buildRequestConfig(final Request request) {
- final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom();
- final int maxRedirects = request.maxRedirects();
- if (maxRedirects > 0) {
- requestConfigBuilder.setMaxRedirects(maxRedirects);
- } else {
- requestConfigBuilder.setRedirectsEnabled(false);
- }
-
- return requestConfigBuilder.build();
- }
-
/**
* 构建默认请求配置,包括连接请求超时和响应(读取)超时
*
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkClientEngine.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkClientEngine.java
index 3830322a1..cb9764677 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkClientEngine.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkClientEngine.java
@@ -17,22 +17,17 @@
package org.dromara.hutool.http.client.engine.jdk;
import org.dromara.hutool.core.io.IoUtil;
-import org.dromara.hutool.core.net.url.UrlUtil;
-import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.http.HttpException;
-import org.dromara.hutool.http.client.ClientConfig;
import org.dromara.hutool.http.client.Request;
import org.dromara.hutool.http.client.RequestContext;
import org.dromara.hutool.http.client.body.HttpBody;
+import org.dromara.hutool.http.client.cookie.InMemoryCookieStore;
import org.dromara.hutool.http.client.engine.AbstractClientEngine;
import org.dromara.hutool.http.meta.HeaderName;
import org.dromara.hutool.http.meta.HttpStatus;
import org.dromara.hutool.http.meta.Method;
-import org.dromara.hutool.http.proxy.ProxyInfo;
import java.io.IOException;
-import java.net.Proxy;
-import java.net.URL;
/**
* 基于JDK的UrlConnection的Http客户端引擎实现
@@ -63,6 +58,7 @@ public class JdkClientEngine extends AbstractClientEngine {
@Override
public JdkHttpResponse send(final Request message) {
+ initEngine();
return doSend(new RequestContext(message));
}
@@ -84,12 +80,24 @@ public class JdkClientEngine extends AbstractClientEngine {
@Override
protected void initEngine() {
- this.cookieManager = (null != this.config && this.config.isUseCookieManager()) ? new JdkCookieManager() : new JdkCookieManager(null);
+ if(null != this.cookieManager){
+ return;
+ }
+ if(null != this.config && this.config.isUseCookieManager()){
+ this.cookieStore = new InMemoryCookieStore();
+ this.cookieManager = new JdkCookieManager(this.cookieStore);
+ }
}
+ /**
+ * 发送请求
+ *
+ * @param context 请求上下文
+ * @return 响应对象
+ */
private JdkHttpResponse doSend(final RequestContext context) {
final Request message = context.getRequest();
- final JdkHttpConnection conn = buildConn(message);
+ final JdkHttpConnection conn = new JdkRequestBuilder(this.config, this.cookieManager).build(message);
try {
doSend(conn, message);
} catch (final IOException e) {
@@ -146,52 +154,4 @@ public class JdkClientEngine extends AbstractClientEngine {
// 非Rest简单GET请求
conn.connect();
}
-
- /**
- * 构建{@link JdkHttpConnection}
- *
- * @param message {@link Request}消息
- * @return {@link JdkHttpConnection}
- */
- private JdkHttpConnection buildConn(final Request message) {
- final ClientConfig config = ObjUtil.defaultIfNull(this.config, ClientConfig::of);
-
- final URL url = message.handledUrl().toURL();
- Proxy proxy = null;
- final ProxyInfo proxyInfo = config.getProxy();
- if (null != proxyInfo) {
- proxy = proxyInfo.selectFirst(UrlUtil.toURI(url));
- }
- final JdkHttpConnection conn = JdkHttpConnection
- .of(url, proxy)
- .setConnectTimeout(config.getConnectionTimeout())
- .setReadTimeout(config.getReadTimeout())
- .setMethod(message.method())//
- .setSSLInfo(config.getSslInfo())
- // 关闭自动重定向,手动处理重定向
- .setInstanceFollowRedirects(false)
- .setDisableCache(config.isDisableCache())
- // 覆盖默认Header
- .header(message.headers(), true);
-
- if (!message.method().isIgnoreBody()) {
- // 在允许发送body的情况下,如果用户自定义了Content-Length,则使用用户定义的值
- final long contentLength = message.contentLength();
- if (contentLength > 0) {
- // 固定请求长度
- conn.setFixedLengthStreamingMode(contentLength);
- } else if (message.isChunked()) {
- conn.setChunkedStreamingMode(4096);
- }
- }
-
- // Cookie管理
- if (null == message.header(HeaderName.COOKIE) && null != this.cookieManager) {
- // 用户没有自定义Cookie,则读取Cookie管理器中的信息并附带到请求中
- // 不覆盖模式回填Cookie头,这样用户定义的Cookie将优先
- conn.header(this.cookieManager.loadForRequest(conn), false);
- }
-
- return conn;
- }
}
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkCookieManager.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkCookieManager.java
index f24ce1c78..194a417a2 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkCookieManager.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkCookieManager.java
@@ -19,9 +19,8 @@ package org.dromara.hutool.http.client.engine.jdk;
import org.dromara.hutool.core.io.IORuntimeException;
import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.net.url.UrlUtil;
-import org.dromara.hutool.http.client.cookie.InMemoryCookieStore;
+import org.dromara.hutool.http.client.cookie.CookieStoreSpi;
-import java.io.Closeable;
import java.io.IOException;
import java.net.CookieManager;
import java.net.CookiePolicy;
@@ -42,15 +41,17 @@ import java.util.Map;
* @author Looly
* @since 6.0.0
*/
-public class JdkCookieManager implements Closeable {
+public class JdkCookieManager {
private CookieManager cookieManager;
/**
* 构造
+ *
+ * @param cookieStore {@link CookieStoreSpi}
*/
- public JdkCookieManager() {
- this(new CookieManager(new JdkCookieStore(new InMemoryCookieStore()), CookiePolicy.ACCEPT_ALL));
+ public JdkCookieManager(final CookieStoreSpi cookieStore) {
+ this(new CookieManager(new JdkCookieStore(cookieStore), CookiePolicy.ACCEPT_ALL));
}
/**
@@ -62,20 +63,6 @@ public class JdkCookieManager implements Closeable {
this.cookieManager = raw;
}
- /**
- * 是否启用Cookie管理
- *
- * @return 是否启用Cookie管理
- */
- public boolean isEnable() {
- return null != this.cookieManager;
- }
-
- @Override
- public void close() {
- this.cookieManager = null;
- }
-
/**
* 获取{@link CookieManager}
*
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpResponse.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpResponse.java
index df341f879..ac00befc2 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpResponse.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkHttpResponse.java
@@ -133,7 +133,7 @@ public class JdkHttpResponse implements Response, Closeable {
* @return Cookie列表
*/
public List getCookies() {
- if (this.cookieManager.isEnable()) {
+ if (null != this.cookieManager) {
return this.cookieManager.getCookies(this.httpConnection);
}
return HttpCookie.parse(this.header(HeaderName.SET_COOKIE));
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkRequestBuilder.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkRequestBuilder.java
new file mode 100644
index 000000000..c497ab527
--- /dev/null
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkRequestBuilder.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2024 Hutool Team and hutool.cn
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.dromara.hutool.http.client.engine.jdk;
+
+import org.dromara.hutool.core.net.url.UrlUtil;
+import org.dromara.hutool.core.util.ObjUtil;
+import org.dromara.hutool.http.client.ClientConfig;
+import org.dromara.hutool.http.client.Request;
+import org.dromara.hutool.http.client.engine.EngineRequestBuilder;
+import org.dromara.hutool.http.meta.HeaderName;
+import org.dromara.hutool.http.proxy.ProxyInfo;
+
+import java.net.Proxy;
+import java.net.URL;
+
+/**
+ * JDK Http请求构建器
+ *
+ * @author Looly
+ * @since 6.0.0
+ */
+public class JdkRequestBuilder implements EngineRequestBuilder {
+
+ private final ClientConfig config;
+ private final JdkCookieManager cookieManager;
+
+ /**
+ * 构造
+ *
+ * @param config 客户端配置,为null则使用默认配置
+ * @param cookieManager Cookie管理器
+ */
+ public JdkRequestBuilder(final ClientConfig config, final JdkCookieManager cookieManager) {
+ this.config = ObjUtil.defaultIfNull(config, ClientConfig::of);
+ this.cookieManager = cookieManager;
+ }
+
+ @Override
+ public JdkHttpConnection build(final Request message) {
+ final ClientConfig config = this.config;
+
+ final URL url = message.handledUrl().toURL();
+ Proxy proxy = null;
+ final ProxyInfo proxyInfo = config.getProxy();
+ if (null != proxyInfo) {
+ proxy = proxyInfo.selectFirst(UrlUtil.toURI(url));
+ }
+ final JdkHttpConnection conn = JdkHttpConnection
+ .of(url, proxy)
+ .setConnectTimeout(config.getConnectionTimeout())
+ .setReadTimeout(config.getReadTimeout())
+ .setMethod(message.method())//
+ .setSSLInfo(config.getSslInfo())
+ // 关闭自动重定向,手动处理重定向
+ .setInstanceFollowRedirects(false)
+ .setDisableCache(config.isDisableCache())
+ // 覆盖默认Header
+ .header(message.headers(), true);
+
+ if (!message.method().isIgnoreBody()) {
+ // 在允许发送body的情况下,如果用户自定义了Content-Length,则使用用户定义的值
+ final long contentLength = message.contentLength();
+ if (contentLength > 0) {
+ // 固定请求长度
+ conn.setFixedLengthStreamingMode(contentLength);
+ } else if (message.isChunked()) {
+ conn.setChunkedStreamingMode(4096);
+ }
+ }
+
+ // Cookie管理
+ if (null == message.header(HeaderName.COOKIE) && null != this.cookieManager) {
+ // 用户没有自定义Cookie,则读取Cookie管理器中的信息并附带到请求中
+ // 不覆盖模式回填Cookie头,这样用户定义的Cookie将优先
+ conn.header(this.cookieManager.loadForRequest(conn), false);
+ }
+
+ return conn;
+ }
+}
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpEngine.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpEngine.java
index 30de8e216..bab4f08ac 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpEngine.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpEngine.java
@@ -17,7 +17,6 @@
package org.dromara.hutool.http.client.engine.okhttp;
import okhttp3.OkHttpClient;
-import okhttp3.internal.http.HttpMethod;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.http.HttpException;
@@ -25,7 +24,6 @@ import org.dromara.hutool.http.client.ClientConfig;
import org.dromara.hutool.http.client.Request;
import org.dromara.hutool.http.client.RequestContext;
import org.dromara.hutool.http.client.Response;
-import org.dromara.hutool.http.client.body.HttpBody;
import org.dromara.hutool.http.client.cookie.InMemoryCookieStore;
import org.dromara.hutool.http.client.engine.AbstractClientEngine;
import org.dromara.hutool.http.meta.HeaderName;
@@ -149,9 +147,11 @@ public class OkHttpEngine extends AbstractClientEngine {
*/
private Response doSend(final RequestContext context) {
final Request message = context.getRequest();
+
+ final okhttp3.Request request = OkHttpRequestBuilder.INSTANCE.build(message);
final okhttp3.Response response;
try {
- response = client.newCall(buildRequest(message)).execute();
+ response = client.newCall(request).execute();
} catch (final IOException e) {
throw new HttpException(e);
}
@@ -178,32 +178,6 @@ public class OkHttpEngine extends AbstractClientEngine {
return new OkHttpResponse(response, message);
}
- /**
- * 构建请求体
- *
- * @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.handledUrl().toURL());
-
- // 填充方法
- final String method = message.method().name();
- final HttpBody body = message.handledBody();
- if (null != body || HttpMethod.requiresRequestBody(method)) {
- // okhttp中,POST等请求必须提供body,否则会抛异常,此处传空的OkHttpRequestBody
- // 为了兼容支持rest请求,在此不区分是否为GET等方法,一律按照body是否有值填充,兼容
- builder.method(method, new OkHttpRequestBody(body));
- } else {
- builder.method(method, null);
- }
-
- // 填充头信息
- message.headers().forEach((key, values) -> values.forEach(value -> builder.addHeader(key, value)));
- return builder.build();
- }
-
/**
* 设置代理信息
*
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpRequestBuilder.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpRequestBuilder.java
new file mode 100644
index 000000000..16fd92119
--- /dev/null
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpRequestBuilder.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2024 Hutool Team and hutool.cn
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.dromara.hutool.http.client.engine.okhttp;
+
+import okhttp3.internal.http.HttpMethod;
+import org.dromara.hutool.http.client.Request;
+import org.dromara.hutool.http.client.body.HttpBody;
+import org.dromara.hutool.http.client.engine.EngineRequestBuilder;
+
+/**
+ * OkHttp请求构建器
+ *
+ * @author Looly
+ * @since 6.0.0
+ */
+public class OkHttpRequestBuilder implements EngineRequestBuilder {
+
+ /**
+ * 单例
+ */
+ public static final OkHttpRequestBuilder INSTANCE = new OkHttpRequestBuilder();
+
+ @Override
+ public okhttp3.Request build(final Request message) {
+ final okhttp3.Request.Builder builder = new okhttp3.Request.Builder()
+ .url(message.handledUrl().toURL());
+
+ // 填充方法
+ final String method = message.method().name();
+ final HttpBody body = message.handledBody();
+ if (null != body || HttpMethod.requiresRequestBody(method)) {
+ // okhttp中,POST等请求必须提供body,否则会抛异常,此处传空的OkHttpRequestBody
+ // 为了兼容支持rest请求,在此不区分是否为GET等方法,一律按照body是否有值填充,兼容
+ builder.method(method, new OkHttpRequestBody(body));
+ } else {
+ builder.method(method, null);
+ }
+
+ // 填充头信息
+ message.headers().forEach((key, values) -> values.forEach(value -> builder.addHeader(key, value)));
+ return builder.build();
+ }
+}