> responseHeaders) {
- if(null == cookieManager || MapUtil.isEmpty(responseHeaders)) {
- // 全局Cookie管理器关闭或头信息为空
- return;
- }
-
- try {
- cookieManager.put(getURI(conn), responseHeaders);
- } catch (final IOException e) {
- throw new IORuntimeException(e);
- }
- }
-
- /**
- * 获取连接的URL中URI信息
- * @param conn HttpConnection
- * @return URI
- */
- private static URI getURI(final JdkHttpConnection conn){
- return UrlUtil.toURI(conn.getUrl());
- }
-}
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngine.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngine.java
index f30a7325e..3baf5f513 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngine.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngine.java
@@ -16,6 +16,7 @@
package org.dromara.hutool.http.client.engine;
+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;
@@ -35,7 +36,7 @@ public interface ClientEngine extends Closeable {
* 对不同引擎个性化配置,使用对应的{@link ClientConfig} 子类:
*
*
- * - HttpClient4和HttpClient5使用{@link org.dromara.hutool.http.client.HttpClientConfig}
+ * - HttpClient4和HttpClient5使用{@link ApacheHttpClientConfig}
* - OkHttp使用{@link org.dromara.hutool.http.client.engine.okhttp.OkHttpClientConfig}
*
*
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java
index 791bc0822..3d2171fcf 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/ClientEngineFactory.java
@@ -21,6 +21,7 @@ import org.dromara.hutool.core.spi.ServiceLoader;
import org.dromara.hutool.core.spi.SpiUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.http.HttpException;
+import org.dromara.hutool.http.client.ApacheHttpClientConfig;
import org.dromara.hutool.http.client.ClientConfig;
import org.dromara.hutool.log.LogUtil;
@@ -47,7 +48,7 @@ public class ClientEngineFactory {
* 对不同引擎个性化配置,使用对应的{@link ClientConfig} 子类:
*
*
- * - HttpClient4和HttpClient5使用{@link org.dromara.hutool.http.client.HttpClientConfig}
+ * - HttpClient4和HttpClient5使用{@link ApacheHttpClientConfig}
* - OkHttp使用{@link org.dromara.hutool.http.client.engine.okhttp.OkHttpClientConfig}
*
*
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 01591d730..63a731392 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
@@ -20,6 +20,7 @@ import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
@@ -34,7 +35,7 @@ import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.http.GlobalHeaders;
import org.dromara.hutool.http.HttpException;
import org.dromara.hutool.http.client.ClientConfig;
-import org.dromara.hutool.http.client.HttpClientConfig;
+import org.dromara.hutool.http.client.ApacheHttpClientConfig;
import org.dromara.hutool.http.client.Request;
import org.dromara.hutool.http.client.Response;
import org.dromara.hutool.http.client.body.HttpBody;
@@ -59,6 +60,7 @@ import java.util.Map;
public class HttpClient4Engine extends AbstractClientEngine {
private CloseableHttpClient engine;
+ private CookieStore cookieStore;
/**
* 构造
@@ -69,6 +71,15 @@ public class HttpClient4Engine extends AbstractClientEngine {
Assert.notNull(CloseableHttpClient.class);
}
+ /**
+ * 获得Cookie存储器
+ *
+ * @return Cookie存储器
+ */
+ public CookieStore getCookieStore() {
+ return this.cookieStore;
+ }
+
@Override
public Response send(final Request message) {
initEngine();
@@ -105,7 +116,7 @@ public class HttpClient4Engine extends AbstractClientEngine {
}
final HttpClientBuilder clientBuilder = HttpClients.custom();
- final ClientConfig config = ObjUtil.defaultIfNull(this.config, HttpClientConfig::of);
+ final ClientConfig config = ObjUtil.defaultIfNull(this.config, ApacheHttpClientConfig::of);
// SSL配置
final SSLInfo sslInfo = config.getSslInfo();
@@ -113,7 +124,7 @@ public class HttpClient4Engine extends AbstractClientEngine {
clientBuilder.setSSLSocketFactory(buildSocketFactory(sslInfo));
}
- // 连接配置
+ // 连接配置,包括连接池
clientBuilder.setConnectionManager(buildConnectionManager(config));
// 实例级别默认请求配置
@@ -137,6 +148,12 @@ public class HttpClient4Engine extends AbstractClientEngine {
// 设置代理
setProxy(clientBuilder, config);
+ // Cookie管理
+ if(config.isUseCookieManager()){
+ this.cookieStore = new BasicCookieStore();
+ clientBuilder.setDefaultCookieStore(this.cookieStore);
+ }
+
this.engine = clientBuilder.build();
}
@@ -208,10 +225,10 @@ public class HttpClient4Engine extends AbstractClientEngine {
final PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
// 连接池配置
- if (config instanceof HttpClientConfig) {
- final HttpClientConfig httpClientConfig = (HttpClientConfig) config;
- manager.setMaxTotal(httpClientConfig.getMaxTotal());
- manager.setDefaultMaxPerRoute(httpClientConfig.getMaxPerRoute());
+ if (config instanceof ApacheHttpClientConfig) {
+ final ApacheHttpClientConfig apacheHttpClientConfig = (ApacheHttpClientConfig) config;
+ manager.setMaxTotal(apacheHttpClientConfig.getMaxTotal());
+ manager.setDefaultMaxPerRoute(apacheHttpClientConfig.getMaxPerRoute());
}
return manager;
@@ -255,8 +272,8 @@ public class HttpClient4Engine extends AbstractClientEngine {
if (readTimeout > 0) {
requestConfigBuilder.setSocketTimeout(readTimeout);
}
- if (config instanceof HttpClientConfig) {
- requestConfigBuilder.setMaxRedirects(((HttpClientConfig) config).getMaxRedirects());
+ if (config instanceof ApacheHttpClientConfig) {
+ requestConfigBuilder.setMaxRedirects(((ApacheHttpClientConfig) config).getMaxRedirects());
}
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 dcfce4a06..8729ad73d 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
@@ -21,6 +21,8 @@ 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.cookie.BasicCookieStore;
+import org.apache.hc.client5.http.cookie.CookieStore;
import org.apache.hc.client5.http.impl.DefaultRedirectStrategy;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
@@ -40,7 +42,7 @@ import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.http.GlobalHeaders;
import org.dromara.hutool.http.HttpException;
import org.dromara.hutool.http.client.ClientConfig;
-import org.dromara.hutool.http.client.HttpClientConfig;
+import org.dromara.hutool.http.client.ApacheHttpClientConfig;
import org.dromara.hutool.http.client.Request;
import org.dromara.hutool.http.client.Response;
import org.dromara.hutool.http.client.body.HttpBody;
@@ -66,6 +68,7 @@ import java.util.concurrent.TimeUnit;
public class HttpClient5Engine extends AbstractClientEngine {
private CloseableHttpClient engine;
+ private CookieStore cookieStore;
/**
* 构造
@@ -76,6 +79,15 @@ public class HttpClient5Engine extends AbstractClientEngine {
Assert.notNull(CloseableHttpClient.class);
}
+ /**
+ * 获得Cookie存储器
+ *
+ * @return Cookie存储器
+ */
+ public CookieStore getCookieStore() {
+ return this.cookieStore;
+ }
+
@Override
public Response send(final Request message) {
initEngine();
@@ -112,7 +124,7 @@ public class HttpClient5Engine extends AbstractClientEngine {
}
final HttpClientBuilder clientBuilder = HttpClients.custom();
- final ClientConfig config = ObjUtil.defaultIfNull(this.config, HttpClientConfig::of);
+ final ClientConfig config = ObjUtil.defaultIfNull(this.config, ApacheHttpClientConfig::of);
// 连接配置,包括SSL配置等
clientBuilder.setConnectionManager(buildConnectionManager(config));
@@ -138,6 +150,12 @@ public class HttpClient5Engine extends AbstractClientEngine {
// 设置代理
setProxy(clientBuilder, config);
+ // Cookie管理
+ if(config.isUseCookieManager()){
+ this.cookieStore = new BasicCookieStore();
+ clientBuilder.setDefaultCookieStore(this.cookieStore);
+ }
+
this.engine = clientBuilder.build();
}
@@ -211,10 +229,10 @@ public class HttpClient5Engine extends AbstractClientEngine {
}
// 连接池配置
- if (config instanceof HttpClientConfig) {
- final HttpClientConfig httpClientConfig = (HttpClientConfig) config;
- final int maxTotal = httpClientConfig.getMaxTotal();
- final int maxPerRoute = httpClientConfig.getMaxPerRoute();
+ if (config instanceof ApacheHttpClientConfig) {
+ final ApacheHttpClientConfig apacheHttpClientConfig = (ApacheHttpClientConfig) config;
+ final int maxTotal = apacheHttpClientConfig.getMaxTotal();
+ final int maxPerRoute = apacheHttpClientConfig.getMaxPerRoute();
if (maxTotal > 0) {
connectionManagerBuilder.setMaxConnTotal(maxTotal);
}
@@ -262,8 +280,8 @@ public class HttpClient5Engine extends AbstractClientEngine {
if (readTimeout > 0) {
requestConfigBuilder.setResponseTimeout(readTimeout, TimeUnit.MILLISECONDS);
}
- if (config instanceof HttpClientConfig) {
- requestConfigBuilder.setMaxRedirects(((HttpClientConfig) config).getMaxRedirects());
+ if (config instanceof ApacheHttpClientConfig) {
+ requestConfigBuilder.setMaxRedirects(((ApacheHttpClientConfig) config).getMaxRedirects());
}
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 2ed5e237e..efe97e278 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
@@ -28,7 +28,6 @@ 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.GlobalCookieManager;
import org.dromara.hutool.http.client.engine.AbstractClientEngine;
import org.dromara.hutool.http.meta.HeaderName;
import org.dromara.hutool.http.meta.HttpStatus;
@@ -45,11 +44,24 @@ import java.util.List;
*/
public class JdkClientEngine extends AbstractClientEngine {
+ /**
+ * Cookie管理
+ */
+ private JdkCookieManager cookieManager;
+
/**
* 构造
*/
public JdkClientEngine() {
- // 无需检查类是否存在
+ }
+
+ /**
+ * 获取Cookie管理器
+ *
+ * @return Cookie管理器
+ */
+ public JdkCookieManager getCookieManager() {
+ return this.cookieManager;
}
@Override
@@ -84,7 +96,8 @@ public class JdkClientEngine extends AbstractClientEngine {
@Override
public void close() {
- // do nothing
+ // 关闭Cookie管理器
+ this.cookieManager = null;
}
@Override
@@ -94,7 +107,7 @@ public class JdkClientEngine extends AbstractClientEngine {
@Override
protected void initEngine() {
- // do nothing
+ this.cookieManager = this.config.isUseCookieManager() ? new JdkCookieManager() : new JdkCookieManager(null);
}
/**
@@ -148,8 +161,9 @@ public class JdkClientEngine extends AbstractClientEngine {
}
if (null == message.header(HeaderName.COOKIE)) {
- // 用户没有自定义Cookie,则读取全局Cookie信息并附带到请求中
- GlobalCookieManager.add(conn);
+ // 用户没有自定义Cookie,则读取Cookie管理器中的信息并附带到请求中
+ // 不覆盖模式回填Cookie头,这样用户定义的Cookie将优先
+ conn.header(this.cookieManager.loadForRequest(conn), false);
}
return conn;
@@ -195,7 +209,7 @@ public class JdkClientEngine extends AbstractClientEngine {
}
// 最终页面
- return new JdkHttpResponse(conn, true, message.charset(), isAsync, message.method().isIgnoreBody());
+ return new JdkHttpResponse(conn, this.cookieManager, true, message.charset(), isAsync, message.method().isIgnoreBody());
}
/**
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
new file mode 100644
index 000000000..31127067d
--- /dev/null
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/jdk/JdkCookieManager.java
@@ -0,0 +1,173 @@
+/*
+ * 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.io.IORuntimeException;
+import org.dromara.hutool.core.map.MapUtil;
+import org.dromara.hutool.core.net.url.UrlUtil;
+import org.dromara.hutool.http.client.cookie.ThreadLocalCookieStore;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.CookieManager;
+import java.net.CookiePolicy;
+import java.net.HttpCookie;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * JDK Cookie管理器,用于管理Cookie信息,包括:
+ *
+ *
+ * 1. 存储Cookie信息到本地
+ * 2. 从本地获取Cookie信息,添加到请求头中
+ *
+ *
+ * @author Looly
+ * @since 6.0.0
+ */
+public class JdkCookieManager implements Closeable {
+
+ private CookieManager cookieManager;
+
+ /**
+ * 构造
+ */
+ public JdkCookieManager() {
+ this(new CookieManager(new ThreadLocalCookieStore(), CookiePolicy.ACCEPT_ALL));
+ }
+
+ /**
+ * 构造
+ *
+ * @param raw 原始对象
+ */
+ public JdkCookieManager(final CookieManager raw) {
+ this.cookieManager = raw;
+ }
+
+ /**
+ * 是否启用Cookie管理
+ *
+ * @return 是否启用Cookie管理
+ */
+ public boolean isEnable() {
+ return null != this.cookieManager;
+ }
+
+ @Override
+ public void close() {
+ this.cookieManager = null;
+ }
+
+ /**
+ * 获取{@link CookieManager}
+ *
+ * @return {@link CookieManager}
+ */
+ public CookieManager getCookieManager() {
+ return this.cookieManager;
+ }
+
+ /**
+ * 自定义{@link CookieManager}
+ *
+ * @param cookieManager 自定义的{@link CookieManager}
+ * @return this
+ */
+ public JdkCookieManager setCookieManager(final CookieManager cookieManager) {
+ this.cookieManager = cookieManager;
+ return this;
+ }
+
+ /**
+ * 获取指定域名下所有Cookie信息
+ *
+ * @param conn HTTP连接
+ * @return Cookie信息列表
+ * @since 4.6.9
+ */
+ public List getCookies(final JdkHttpConnection conn) {
+ if (null == this.cookieManager) {
+ return null;
+ }
+ return this.cookieManager.getCookieStore().get(getURI(conn));
+ }
+
+ /**
+ * 将本地存储的Cookie信息附带到Http请求中,不覆盖用户定义好的Cookie
+ *
+ * @param conn {@link JdkHttpConnection}
+ * @return this
+ */
+ public Map> loadForRequest(final JdkHttpConnection conn) {
+ if (null == cookieManager) {
+ // Cookie管理器关闭
+ return null;
+ }
+
+ try {
+ return this.cookieManager.get(getURI(conn), new HashMap<>(0));
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ }
+ }
+
+ /**
+ * 存储响应的Cookie信息到本地
+ *
+ * @param conn {@link JdkHttpConnection}
+ * @return this
+ */
+ public JdkCookieManager saveFromResponse(final JdkHttpConnection conn) {
+ return saveFromResponse(conn, conn.headers());
+ }
+
+ /**
+ * 存储响应的Cookie信息到本地
+ * 通过读取
+ *
+ * @param conn {@link JdkHttpConnection}
+ * @param responseHeaders 头信息Map
+ * @return this
+ */
+ public JdkCookieManager saveFromResponse(final JdkHttpConnection conn, final Map> responseHeaders) {
+ if (null == this.cookieManager || MapUtil.isEmpty(responseHeaders)) {
+ // Cookie管理器关闭或头信息为空
+ return this;
+ }
+
+ try {
+ cookieManager.put(getURI(conn), responseHeaders);
+ } catch (final IOException e) {
+ throw new IORuntimeException(e);
+ }
+ return this;
+ }
+
+ /**
+ * 获取连接的URL中URI信息
+ *
+ * @param conn HttpConnection
+ * @return URI
+ */
+ private static URI getURI(final JdkHttpConnection conn) {
+ return UrlUtil.toURI(conn.getUrl());
+ }
+}
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 6baeb88e7..326f0c755 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
@@ -23,7 +23,7 @@ import org.dromara.hutool.http.HttpException;
import org.dromara.hutool.http.HttpUtil;
import org.dromara.hutool.http.client.Response;
import org.dromara.hutool.http.client.body.ResponseBody;
-import org.dromara.hutool.http.client.cookie.GlobalCookieManager;
+import org.dromara.hutool.http.meta.HeaderName;
import java.io.Closeable;
import java.io.FileNotFoundException;
@@ -46,10 +46,17 @@ public class JdkHttpResponse implements Response, Closeable {
* 请求时的默认编码
*/
private final Charset requestCharset;
+ /**
+ * Cookie管理器
+ */
+ private final JdkCookieManager cookieManager;
/**
* 响应内容体,{@code null} 表示无内容
*/
private ResponseBody body;
+ /**
+ * 响应头
+ */
private Map> headers;
/**
@@ -72,14 +79,20 @@ public class JdkHttpResponse implements Response, Closeable {
* 构造
*
* @param httpConnection {@link JdkHttpConnection}
+ * @param cookieManager Cookie管理器
* @param ignoreEOFError 是否忽略响应读取时可能的EOF异常
* @param requestCharset 编码,从请求编码中获取默认编码
* @param isAsync 是否异步
* @param isIgnoreBody 是否忽略读取响应体
*/
- protected JdkHttpResponse(final JdkHttpConnection httpConnection, final boolean ignoreEOFError,
- final Charset requestCharset, final boolean isAsync, final boolean isIgnoreBody) {
+ protected JdkHttpResponse(final JdkHttpConnection httpConnection,
+ final JdkCookieManager cookieManager,
+ final boolean ignoreEOFError,
+ final Charset requestCharset,
+ final boolean isAsync,
+ final boolean isIgnoreBody) {
this.httpConnection = httpConnection;
+ this.cookieManager = cookieManager;
this.ignoreEOFError = ignoreEOFError;
this.requestCharset = requestCharset;
init(isAsync, isIgnoreBody);
@@ -133,13 +146,16 @@ public class JdkHttpResponse implements Response, Closeable {
// ---------------------------------------------------------------- Http Response Header start
/**
- * 获取Cookie
+ * 获取Cookie
+ * 如果启用Cookie管理器,则返回这个站点相关的所有Cookie信息,否则返回当前的响应的Cookie信息
*
* @return Cookie列表
- * @since 3.1.1
*/
public List getCookies() {
- return GlobalCookieManager.getCookies(this.httpConnection);
+ if (this.cookieManager.isEnable()) {
+ return this.cookieManager.getCookies(this.httpConnection);
+ }
+ return HttpCookie.parse(this.header(HeaderName.SET_COOKIE));
}
/**
@@ -256,7 +272,7 @@ public class JdkHttpResponse implements Response, Closeable {
}
// 存储服务端设置的Cookie信息
- GlobalCookieManager.store(httpConnection, this.headers);
+ this.cookieManager.saveFromResponse(this.httpConnection, this.headers);
// 获取响应内容流
if (!isIgnoreBody) {
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/CookieJarImpl.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/CookieJarImpl.java
new file mode 100644
index 000000000..327e4b802
--- /dev/null
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/CookieJarImpl.java
@@ -0,0 +1,69 @@
+/*
+ * 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.Cookie;
+import okhttp3.CookieJar;
+import okhttp3.HttpUrl;
+
+import java.util.List;
+
+/**
+ * CookieJar实现,用于OkHttp3
+ *
+ * @author Looly
+ * @since 6.0.0
+ */
+public class CookieJarImpl implements CookieJar {
+
+ private final OkCookieStore cookieStore;
+
+ /**
+ * 构造
+ */
+ public CookieJarImpl() {
+ this(new InMemoryOkCookieStore());
+ }
+
+ /**
+ * 获取Cookie存储器
+ *
+ * @return Cookie存储器
+ */
+ public OkCookieStore getCookieStore() {
+ return this.cookieStore;
+ }
+
+ /**
+ * 构造
+ *
+ * @param cookieStore Cookie存储器,用于自定义Cookie存储实现
+ */
+ public CookieJarImpl(final OkCookieStore cookieStore) {
+ this.cookieStore = cookieStore;
+ }
+
+ @Override
+ public List loadForRequest(final HttpUrl httpUrl) {
+ return this.cookieStore.get(httpUrl);
+ }
+
+ @Override
+ public void saveFromResponse(final HttpUrl httpUrl, final List list) {
+ this.cookieStore.add(httpUrl, list);
+ }
+}
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/InMemoryOkCookieStore.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/InMemoryOkCookieStore.java
new file mode 100644
index 000000000..909ad5a66
--- /dev/null
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/InMemoryOkCookieStore.java
@@ -0,0 +1,149 @@
+/*
+ * 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.Cookie;
+import okhttp3.HttpUrl;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 内存CookieStore实现,用于OkHttp3
+ *
+ * @author Looly
+ * @since 6.0.0
+ */
+public class InMemoryOkCookieStore implements OkCookieStore {
+
+ private final HashMap> cookies;
+
+ /**
+ * 构造
+ */
+ public InMemoryOkCookieStore() {
+ this.cookies = new HashMap<>();
+ }
+
+ @Override
+ public void add(final HttpUrl httpUrl, final Cookie cookie) {
+ if (!cookie.persistent()) {
+ return;
+ }
+
+ final String name = this.cookieName(cookie);
+ final String hostKey = httpUrl.host();
+
+ if (!this.cookies.containsKey(hostKey)) {
+ this.cookies.put(hostKey, new ConcurrentHashMap<>());
+ }
+ cookies.get(hostKey).put(name, cookie);
+ }
+
+ @Override
+ public void add(final HttpUrl httpUrl, final List cookies) {
+ for (final Cookie cookie : cookies) {
+ if (isCookieExpired(cookie)) {
+ continue;
+ }
+ this.add(httpUrl, cookie);
+ }
+ }
+
+ @Override
+ public List get(final HttpUrl httpUrl) {
+ return this.get(httpUrl.host());
+ }
+
+ @Override
+ public List getCookies() {
+ final ArrayList result = new ArrayList<>();
+
+ for (final String hostKey : this.cookies.keySet()) {
+ result.addAll(this.get(hostKey));
+ }
+
+ return result;
+ }
+
+ /**
+ * 获取cookie集合
+ */
+ private List get(final String hostKey) {
+ final ArrayList result = new ArrayList<>();
+
+ if (this.cookies.containsKey(hostKey)) {
+ final Collection cookies = this.cookies.get(hostKey).values();
+ for (final Cookie cookie : cookies) {
+ if (isCookieExpired(cookie)) {
+ this.remove(hostKey, cookie);
+ } else {
+ result.add(cookie);
+ }
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean remove(final HttpUrl httpUrl, final Cookie cookie) {
+ return this.remove(httpUrl.host(), cookie);
+ }
+
+ /**
+ * 从缓存中移除cookie
+ *
+ * @param hostKey hostKey
+ * @param cookie cookie
+ */
+ private boolean remove(final String hostKey, final Cookie cookie) {
+ final String name = this.cookieName(cookie);
+ if (this.cookies.containsKey(hostKey) && this.cookies.get(hostKey).containsKey(name)) {
+ // 从内存中移除httpUrl对应的cookie
+ this.cookies.get(hostKey).remove(name);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean removeAll() {
+ this.cookies.clear();
+ return true;
+ }
+
+ /**
+ * 判断cookie是否失效
+ */
+ private boolean isCookieExpired(final Cookie cookie) {
+ return cookie.expiresAt() < System.currentTimeMillis();
+ }
+
+ /**
+ * 获取cookie的key
+ * 格式:name + domain
+ *
+ * @param cookie cookie
+ * @return cookie的key
+ */
+ private String cookieName(final Cookie cookie) {
+ return cookie == null ? null : cookie.name() + cookie.domain();
+ }
+}
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkCookieStore.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkCookieStore.java
new file mode 100644
index 000000000..9585dedd9
--- /dev/null
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkCookieStore.java
@@ -0,0 +1,78 @@
+/*
+ * 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.Cookie;
+import okhttp3.HttpUrl;
+
+import java.util.List;
+
+/**
+ * OkHttp3 CookieStore接口
+ *
+ * @author Looly
+ * @since 6.0.0
+ */
+public interface OkCookieStore {
+
+ /**
+ * 添加cookie
+ *
+ * @param httpUrl HTTP url 地址
+ * @param cookie cookie
+ */
+ void add(HttpUrl httpUrl, Cookie cookie);
+
+ /**
+ * 添加指定 http url cookie集合
+ *
+ * @param httpUrl HTTP url 地址
+ * @param cookies cookie列表
+ */
+ void add(HttpUrl httpUrl, List cookies);
+
+ /**
+ * 根据HttpUrl从缓存中读取cookie集合
+ *
+ * @param httpUrl HTTP url 地址
+ * @return cookie集合
+ */
+ List get(HttpUrl httpUrl);
+
+ /**
+ * 获取全部缓存cookie
+ *
+ * @return cookie集合
+ */
+ List getCookies();
+
+ /**
+ * 移除指定http url cookie集合
+ *
+ * @param httpUrl HTTP url 地址
+ * @param cookie cookie
+ * @return 是否移除成功
+ */
+ boolean remove(HttpUrl httpUrl, Cookie cookie);
+
+ /**
+ * 移除所有cookie
+ *
+ * @return 是否移除成功
+ */
+ boolean removeAll();
+}
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 1a330adb4..67b80b281 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
@@ -43,6 +43,7 @@ import java.util.concurrent.TimeUnit;
public class OkHttpEngine extends AbstractClientEngine {
private OkHttpClient client;
+ private OkCookieStore cookieStore;
/**
* 构造
@@ -53,6 +54,15 @@ public class OkHttpEngine extends AbstractClientEngine {
Assert.notNull(OkHttpClient.class);
}
+ /**
+ * 获得Cookie存储器
+ *
+ * @return Cookie存储器
+ */
+ public OkCookieStore getCookieStore() {
+ return this.cookieStore;
+ }
+
@Override
public OkHttpEngine init(final ClientConfig config) {
this.config = config;
@@ -96,13 +106,24 @@ public class OkHttpEngine extends AbstractClientEngine {
}
final OkHttpClient.Builder builder = new OkHttpClient.Builder();
-
final ClientConfig config = ObjUtil.defaultIfNull(this.config, ClientConfig::of);
+
+ // SSL
+ final SSLInfo sslInfo = config.getSslInfo();
+ if (null != sslInfo) {
+ final SSLSocketFactory socketFactory = sslInfo.getSocketFactory();
+ final X509TrustManager trustManager = sslInfo.getTrustManager();
+ if (null != socketFactory && null != trustManager) {
+ builder.sslSocketFactory(socketFactory, trustManager);
+ }
+ }
+
// 连接超时
final int connectionTimeout = config.getConnectionTimeout();
if (connectionTimeout > 0) {
builder.connectTimeout(connectionTimeout, TimeUnit.MILLISECONDS);
}
+
// 读写超时
final int readTimeout = config.getReadTimeout();
if (readTimeout > 0) {
@@ -112,25 +133,21 @@ public class OkHttpEngine extends AbstractClientEngine {
}
// 连接池
- if(config instanceof OkHttpClientConfig){
+ if (config instanceof OkHttpClientConfig) {
builder.connectionPool(((OkHttpClientConfig) config).getConnectionPool());
}
- // SSL
- final SSLInfo sslInfo = config.getSslInfo();
- if (null != sslInfo){
- final SSLSocketFactory socketFactory = sslInfo.getSocketFactory();
- final X509TrustManager trustManager = sslInfo.getTrustManager();
- if(null != socketFactory && null != trustManager){
- builder.sslSocketFactory(socketFactory, trustManager);
- }
- }
+ // 重定向
+ builder.followRedirects(config.isFollowRedirects());
// 设置代理
setProxy(builder, config);
- // 重定向
- builder.followRedirects(config.isFollowRedirects());
+ // Cookie管理
+ if (this.config.isUseCookieManager()) {
+ this.cookieStore = new InMemoryOkCookieStore();
+ builder.cookieJar(new CookieJarImpl(this.cookieStore));
+ }
this.client = builder.build();
}
@@ -156,7 +173,7 @@ public class OkHttpEngine extends AbstractClientEngine {
}
// 填充头信息
- message.headers().forEach((key, values)-> values.forEach(value-> builder.addHeader(key, value)));
+ message.headers().forEach((key, values) -> values.forEach(value -> builder.addHeader(key, value)));
return builder.build();
}
diff --git a/hutool-http/src/test/java/org/dromara/hutool/http/webservice/SoapClientTest.java b/hutool-http/src/test/java/org/dromara/hutool/http/webservice/SoapClientTest.java
index 13a61fb35..676773c56 100644
--- a/hutool-http/src/test/java/org/dromara/hutool/http/webservice/SoapClientTest.java
+++ b/hutool-http/src/test/java/org/dromara/hutool/http/webservice/SoapClientTest.java
@@ -21,9 +21,6 @@ import org.dromara.hutool.core.util.CharsetUtil;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
-import jakarta.xml.soap.SOAPException;
-import jakarta.xml.soap.SOAPMessage;
-
/**
* SOAP相关单元测试
*
@@ -42,17 +39,16 @@ public class SoapClientTest {
Console.log(client.getMsgStr(true));
- Console.log(client.send(true));
+ Console.log(client.send().getResponseStr(true));
}
@Test
@Disabled
- public void requestForMessageTest() throws SOAPException {
+ public void requestForMessageTest() {
final SoapClient client = SoapClient.of("http://www.webxml.com.cn/WebServices/IpAddressSearchWebService.asmx")
.setMethod("web:getCountryCityByIp", "http://WebXml.com.cn/")
.setParam("theIpAddress", "218.21.240.106");
- final SOAPMessage message = client.sendForMessage();
- Console.log(message.getSOAPBody().getTextContent());
+ Console.log(client.send().getBodyText());
}
}