add CookieSpi

This commit is contained in:
Looly 2024-09-09 20:12:18 +08:00
parent 426d3acd9c
commit 937f36328f
19 changed files with 1015 additions and 248 deletions

View File

@ -3,11 +3,10 @@
-------------------------------------------------------------------------------------------------------------
# 6.0.0-M17 (2024-08-30)
# 6.0.0-M17 (2024-09-09)
### 计划实现
* 【db 】 增加DDL封装
* 【http 】 redirect跳转和Cookie
### ❌不兼容特性

View File

@ -0,0 +1,122 @@
/*
* 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.cookie;
import java.time.Instant;
/**
* Cookie SPI接口用于自定义Cookie的实现<br>
* 遵循RFC6265规范https://datatracker.ietf.org/doc/html/rfc6265<br>
* 参考https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Cookies<br>
* Cookie 主要用于以下三个方面
* <ul>
* <li>会话状态管理 用户登录状态购物车游戏分数或其他需要记录的信息</li>
* <li>个性化设置 如用户自定义设置主题和其他设置</li>
* <li>浏览器行为跟踪 如跟踪分析用户行为等</li>
* </ul>
*
* @author Looly
* @since 6.0.0
*/
public interface CookieSpi {
/**
* 获取Cookie名称
*
* @return Cookie名称
*/
String getName();
/**
* 获取Cookie值
*
* @return Cookie值
*/
String getValue();
/**
* 限制访问 Cookie<br>
* 标记为 Secure Cookie 只应通过被 HTTPS 协议加密过的请求发送给服务端<br>
*
* @return 是否限制访问 Cookie
*/
boolean isSecure();
/**
* 限制 Cookie 的作用域<br>
* 标记为 HttpOnly Cookie 只能在 HTTP 协议中访问不能通过脚本语言 JavaScript访问<br>
*
* @return 是否限制 Cookie 的作用域
*/
boolean isHttpOnly();
/**
* 限制 Cookie 主机作用域一般包含子域名<br>
* Cookie 作用域默认为空表示所有域名下生效<br>
*
* @return Cookie 作用域
*/
boolean isHostOnly();
/**
* 限制 Cookie 主机作用域一般包含子域名<br>
* Cookie 作用域默认为空表示所有域名下生效<br>
*
* @return Cookie 作用域
*/
String getDomain();
/**
* 限制 Cookie URL路径作用域 URL 路径必须存在于请求的 URL 子路径也会匹配<br>
* Cookie 作用域默认为空表示所有路径下生效<br>
*
* @return Cookie 作用域
*/
String getPath();
/**
* Cookie是否过期
*
* @param now 当前时间用于判断是否过期
* @return 是否过期
*/
boolean isExpired(Instant now);
/**
* Cookie是否过期
*
* @return 是否过期
*/
default boolean isExpired() {
return isExpired(Instant.now());
}
/**
* 自定义属性用于扩展
*
* @param name 属性名
* @return 属性值
*/
String getAttribute(String name);
/**
* 是否持久化即Cookie是否在Session关闭前一直有效
*
* @return 是否持久化
*/
boolean isPersistent();
}

View File

@ -14,20 +14,25 @@
* limitations under the License.
*/
package org.dromara.hutool.http.client.engine.okhttp;
import okhttp3.Cookie;
import okhttp3.HttpUrl;
package org.dromara.hutool.http.client.cookie;
import java.net.URI;
import java.util.List;
/**
* OkHttp3 CookieStore接口
* CookieStore接口
*
* @author Looly
* @since 6.0.0
*/
public interface OkCookieStore {
public interface CookieStoreSpi {
/**
* 获取所有Http URI
*
* @return 所有Http URI
*/
List<URI> getURIs();
/**
* 添加cookie
@ -35,7 +40,7 @@ public interface OkCookieStore {
* @param httpUrl HTTP url 地址
* @param cookie cookie
*/
void add(HttpUrl httpUrl, Cookie cookie);
void add(URI httpUrl, CookieSpi cookie);
/**
* 添加指定 http url cookie集合
@ -43,7 +48,11 @@ public interface OkCookieStore {
* @param httpUrl HTTP url 地址
* @param cookies cookie列表
*/
void add(HttpUrl httpUrl, List<Cookie> cookies);
default void add(final URI httpUrl, final List<CookieSpi> cookies){
for (final CookieSpi cookie : cookies) {
add(httpUrl, cookie);
}
}
/**
* 根据HttpUrl从缓存中读取cookie集合
@ -51,14 +60,14 @@ public interface OkCookieStore {
* @param httpUrl HTTP url 地址
* @return cookie集合
*/
List<Cookie> get(HttpUrl httpUrl);
List<CookieSpi> get(URI httpUrl);
/**
* 获取全部缓存cookie
*
* @return cookie集合
*/
List<Cookie> getCookies();
List<CookieSpi> getCookies();
/**
* 移除指定http url cookie集合
@ -67,12 +76,12 @@ public interface OkCookieStore {
* @param cookie cookie
* @return 是否移除成功
*/
boolean remove(HttpUrl httpUrl, Cookie cookie);
boolean remove(URI httpUrl, CookieSpi cookie);
/**
* 移除所有cookie
*
* @return 是否移除成功
*/
boolean removeAll();
boolean clear();
}

View File

@ -0,0 +1,147 @@
/*
* 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.cookie;
import org.dromara.hutool.http.HttpException;
import org.dromara.hutool.log.Log;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 基于内存的Cookie存储实现线程安全
*
* @author looly
* @since 6.0.0
*/
public class InMemoryCookieStore implements CookieStoreSpi {
private static final Log LOG = Log.get();
private final Map<String, ConcurrentHashMap<String, CookieSpi>> cookies;
/**
* 构造
*/
public InMemoryCookieStore() {
this.cookies = new ConcurrentHashMap<>();
}
@Override
public List<URI> getURIs() {
final Set<String> keySet = cookies.keySet();
final List<URI> uris = new ArrayList<>(keySet.size());
try {
for (final String hostKey : keySet) {
uris.add(new URI("http", hostKey, null, null));
}
} catch (final URISyntaxException e) {
throw new HttpException(e);
}
return uris;
}
@Override
public void add(final URI httpUrl, final CookieSpi cookie) {
if (null == cookie || !cookie.isPersistent() || cookie.isExpired()) {
return;
}
final String hostKey = httpUrl.getHost();
this.cookies.computeIfAbsent(hostKey, k -> new ConcurrentHashMap<>());
final String cookieKey = this.cookieKey(cookie);
LOG.debug("Add cookie {}: {}", cookieKey, cookie);
cookies.get(hostKey).put(cookieKey, cookie);
}
@Override
public List<CookieSpi> get(final URI httpUrl) {
final String hostKey = httpUrl.getHost();
final List<CookieSpi> result = this.get(httpUrl.getHost());
LOG.debug("Get cookies {}: {}", hostKey, result);
return result;
}
@Override
public List<CookieSpi> getCookies() {
final List<CookieSpi> result = new ArrayList<>();
for (final String hostKey : this.cookies.keySet()) {
result.addAll(this.get(hostKey));
}
return result;
}
@Override
public boolean remove(final URI httpUrl, final CookieSpi cookie) {
return this.remove(httpUrl.getHost(), cookie);
}
@Override
public boolean clear() {
this.cookies.clear();
return true;
}
/**
* 获取cookie集合
*/
private List<CookieSpi> get(final String hostKey) {
final List<CookieSpi> result = new ArrayList<>();
if (this.cookies.containsKey(hostKey)) {
final Collection<CookieSpi> cookies = this.cookies.get(hostKey).values();
for (final CookieSpi cookie : cookies) {
if (cookie.isExpired()) {
this.remove(hostKey, cookie);
} else {
result.add(cookie);
}
}
}
return result;
}
/**
* 从缓存中移除cookie
*
* @param hostKey hostKey
* @param cookie cookie
*/
private boolean remove(final String hostKey, final CookieSpi cookie) {
final String cookieKey = this.cookieKey(cookie);
if (this.cookies.containsKey(hostKey) && this.cookies.get(hostKey).containsKey(cookieKey)) {
// 从内存中移除httpUrl对应的cookie
this.cookies.get(hostKey).remove(cookieKey);
return true;
}
return false;
}
/**
* 获取cookie的key<br>
* 格式name + domain
*
* @param cookie cookie
* @return cookie的key
*/
private String cookieKey(final CookieSpi cookie) {
return cookie == null ? null : cookie.getName() + cookie.getDomain();
}
}

View File

@ -16,9 +16,6 @@
package org.dromara.hutool.http.client.cookie;
import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.util.List;
@ -28,15 +25,15 @@ import java.util.List;
* https://stackoverflow.com/questions/16305486/cookiemanager-for-multiple-threads
*
* @author looly
* @since 4.1.18
* @since 6.0.0
*/
public class ThreadLocalCookieStore implements CookieStore {
public class ThreadLocalCookieStore implements CookieStoreSpi {
private final static ThreadLocal<CookieStore> STORES = new ThreadLocal<CookieStore>() {
private final static ThreadLocal<CookieStoreSpi> STORES = new ThreadLocal<CookieStoreSpi>() {
@Override
protected synchronized CookieStore initialValue() {
protected synchronized CookieStoreSpi initialValue() {
/* InMemoryCookieStore */
return (new CookieManager()).getCookieStore();
return new InMemoryCookieStore();
}
};
@ -45,7 +42,7 @@ public class ThreadLocalCookieStore implements CookieStore {
*
* @return CookieStore
*/
public CookieStore getCookieStore() {
public CookieStoreSpi getCookieStore() {
return STORES.get();
}
@ -59,33 +56,33 @@ public class ThreadLocalCookieStore implements CookieStore {
return this;
}
@Override
public void add(final URI uri, final HttpCookie cookie) {
getCookieStore().add(uri, cookie);
}
@Override
public List<HttpCookie> get(final URI uri) {
return getCookieStore().get(uri);
}
@Override
public List<HttpCookie> getCookies() {
return getCookieStore().getCookies();
}
@Override
public List<URI> getURIs() {
return getCookieStore().getURIs();
}
@Override
public boolean remove(final URI uri, final HttpCookie cookie) {
return getCookieStore().remove(uri, cookie);
public void add(final URI httpUrl, final CookieSpi cookie) {
getCookieStore().add(httpUrl, cookie);
}
@Override
public boolean removeAll() {
return getCookieStore().removeAll();
public List<CookieSpi> get(final URI httpUrl) {
return getCookieStore().get(httpUrl);
}
@Override
public List<CookieSpi> getCookies() {
return getCookieStore().getCookies();
}
@Override
public boolean remove(final URI httpUrl, final CookieSpi cookie) {
return getCookieStore().remove(httpUrl, cookie);
}
@Override
public boolean clear() {
return getCookieStore().clear();
}
}

View File

@ -17,6 +17,7 @@
package org.dromara.hutool.http.client.engine;
import org.dromara.hutool.http.client.ClientConfig;
import org.dromara.hutool.http.client.cookie.CookieStoreSpi;
/**
* 客户端引擎抽象类用于保存配置和定义初始化并提供
@ -31,6 +32,16 @@ import org.dromara.hutool.http.client.ClientConfig;
public abstract class AbstractClientEngine implements ClientEngine{
protected ClientConfig config;
protected CookieStoreSpi cookieStore;
/**
* 获得Cookie存储器
*
* @return Cookie存储器
*/
public CookieStoreSpi getCookieStore() {
return this.cookieStore;
}
@Override
public ClientEngine init(final ClientConfig config) {

View File

@ -0,0 +1,97 @@
/*
* 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.cookie.Cookie;
import org.dromara.hutool.core.date.DateUtil;
import org.dromara.hutool.core.lang.wrapper.SimpleWrapper;
import org.dromara.hutool.http.client.cookie.CookieSpi;
import java.time.Instant;
/**
* HttpClient4 Cookie实现
*
* @author looly
* @since 6.0.0
*/
public class HttpClient4Cookie extends SimpleWrapper<Cookie> implements CookieSpi {
/**
* 构造
*
* @param raw 原始对象
*/
public HttpClient4Cookie(final Cookie raw) {
super(raw);
}
@Override
public String getName() {
return raw.getName();
}
@Override
public String getValue() {
return raw.getValue();
}
@Override
public boolean isSecure() {
return raw.isSecure();
}
@Override
public boolean isHttpOnly() {
return false;
}
@Override
public boolean isHostOnly() {
return false;
}
@Override
public String getDomain() {
return raw.getDomain();
}
@Override
public String getPath() {
return raw.getPath();
}
@Override
public boolean isExpired(final Instant now) {
return raw.isExpired(DateUtil.date(now));
}
@Override
public String getAttribute(final String name) {
throw new UnsupportedOperationException();
}
@Override
public boolean isPersistent() {
return raw.isPersistent();
}
@Override
public String toString() {
return raw.toString();
}
}

View File

@ -0,0 +1,82 @@
/*
* 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.CookieStore;
import org.apache.http.cookie.Cookie;
import org.dromara.hutool.core.lang.wrapper.SimpleWrapper;
import org.dromara.hutool.http.HttpException;
import org.dromara.hutool.http.client.cookie.CookieSpi;
import org.dromara.hutool.http.client.cookie.CookieStoreSpi;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Apache HttpClient4的Cookie存储器实现
*
* @author looly
* @since 6.0.0
*/
public class HttpClient4CookieStore extends SimpleWrapper<CookieStoreSpi> implements CookieStore {
/**
* 构造
*
* @param raw Cookie存储器
*/
public HttpClient4CookieStore(final CookieStoreSpi raw) {
super(raw);
}
@Override
public void addCookie(final Cookie cookie) {
final URI uri;
try {
uri = new URI("http", cookie.getDomain(), cookie.getPath(), null);
} catch (final URISyntaxException e) {
throw new HttpException(e);
}
this.raw.add(uri, new HttpClient4Cookie(cookie));
}
@Override
public List<Cookie> getCookies() {
final List<CookieSpi> cookies = this.raw.getCookies();
final List<Cookie> result = new ArrayList<>(cookies.size());
for (final CookieSpi cookie : cookies) {
result.add(((HttpClient4Cookie) cookie).getRaw());
}
return result;
}
@Override
public boolean clearExpired(final Date date) {
// get时检查过期
this.raw.getCookies();
return true;
}
@Override
public void clear() {
this.raw.clear();
}
}

View File

@ -20,7 +20,6 @@ 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,11 +33,12 @@ 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;
import org.dromara.hutool.http.client.ClientConfig;
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.HttpProxy;
@ -60,7 +60,6 @@ import java.util.Map;
public class HttpClient4Engine extends AbstractClientEngine {
private CloseableHttpClient engine;
private CookieStore cookieStore;
/**
* 构造
@ -71,15 +70,6 @@ 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();
@ -150,8 +140,8 @@ public class HttpClient4Engine extends AbstractClientEngine {
// Cookie管理
if(config.isUseCookieManager()){
this.cookieStore = new BasicCookieStore();
clientBuilder.setDefaultCookieStore(this.cookieStore);
this.cookieStore = new InMemoryCookieStore();
clientBuilder.setDefaultCookieStore(new HttpClient4CookieStore(this.cookieStore));
}
this.engine = clientBuilder.build();

View File

@ -0,0 +1,96 @@
/*
* 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.cookie.Cookie;
import org.dromara.hutool.core.lang.wrapper.SimpleWrapper;
import org.dromara.hutool.http.client.cookie.CookieSpi;
import java.time.Instant;
/**
* HttpClient5 Cookie实现
*
* @author looly
* @since 6.0.0
*/
public class HttpClient5Cookie extends SimpleWrapper<Cookie> implements CookieSpi {
/**
* 构造
*
* @param raw 原始对象
*/
public HttpClient5Cookie(final Cookie raw) {
super(raw);
}
@Override
public String getName() {
return raw.getName();
}
@Override
public String getValue() {
return raw.getValue();
}
@Override
public boolean isSecure() {
return raw.isSecure();
}
@Override
public boolean isHttpOnly() {
return raw.isHttpOnly();
}
@Override
public boolean isHostOnly() {
return false;
}
@Override
public String getDomain() {
return raw.getDomain();
}
@Override
public String getPath() {
return raw.getPath();
}
@Override
public boolean isExpired(final Instant now) {
return raw.isExpired(now);
}
@Override
public String getAttribute(final String name) {
return raw.getAttribute(name);
}
@Override
public boolean isPersistent() {
return raw.isPersistent();
}
@Override
public String toString() {
return raw.toString();
}
}

View File

@ -0,0 +1,82 @@
/*
* 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.cookie.Cookie;
import org.apache.hc.client5.http.cookie.CookieStore;
import org.dromara.hutool.core.lang.wrapper.SimpleWrapper;
import org.dromara.hutool.http.HttpException;
import org.dromara.hutool.http.client.cookie.CookieSpi;
import org.dromara.hutool.http.client.cookie.CookieStoreSpi;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* HttpClient5 Cookie存储器实现
*
* @author looly
* @since 6.0.0
*/
public class HttpClient5CookieStore extends SimpleWrapper<CookieStoreSpi> implements CookieStore {
/**
* 构造
*
* @param cookieStore Cookie存储器
*/
public HttpClient5CookieStore(final CookieStoreSpi cookieStore) {
super(cookieStore);
}
@Override
public void addCookie(final Cookie cookie) {
final URI uri;
try {
uri = new URI("http", cookie.getDomain(), cookie.getPath(), null);
} catch (final URISyntaxException e) {
throw new HttpException(e);
}
this.raw.add(uri, new HttpClient5Cookie(cookie));
}
@Override
public List<Cookie> getCookies() {
final List<CookieSpi> cookies = this.raw.getCookies();
final List<Cookie> result = new ArrayList<>(cookies.size());
for (final CookieSpi cookie : cookies) {
result.add(((HttpClient5Cookie) cookie).getRaw());
}
return result;
}
@Override
public boolean clearExpired(final Date date) {
// get时检查过期
this.raw.getCookies();
return true;
}
@Override
public void clear() {
this.raw.clear();
}
}

View File

@ -21,8 +21,6 @@ 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;
@ -41,11 +39,12 @@ 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;
import org.dromara.hutool.http.client.ClientConfig;
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.HttpProxy;
@ -68,7 +67,6 @@ import java.util.concurrent.TimeUnit;
public class HttpClient5Engine extends AbstractClientEngine {
private CloseableHttpClient engine;
private CookieStore cookieStore;
/**
* 构造
@ -79,15 +77,6 @@ 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();
@ -152,8 +141,8 @@ public class HttpClient5Engine extends AbstractClientEngine {
// Cookie管理
if(config.isUseCookieManager()){
this.cookieStore = new BasicCookieStore();
clientBuilder.setDefaultCookieStore(this.cookieStore);
this.cookieStore = new InMemoryCookieStore();
clientBuilder.setDefaultCookieStore(new HttpClient5CookieStore(this.cookieStore));
}
this.engine = clientBuilder.build();

View File

@ -0,0 +1,108 @@
/*
* 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.lang.wrapper.SimpleWrapper;
import org.dromara.hutool.core.reflect.method.MethodUtil;
import org.dromara.hutool.http.client.cookie.CookieSpi;
import java.net.HttpCookie;
import java.time.Instant;
/**
* JDK Cookie实现
*
* @author looly
* @since 6.0.0
*/
public class JdkCookie extends SimpleWrapper<HttpCookie> implements CookieSpi {
/**
* 构造
*
* @param httpCookie 原始对象
*/
public JdkCookie(final HttpCookie httpCookie) {
super(httpCookie);
}
@Override
public String getName() {
return raw.getName();
}
@Override
public String getValue() {
return raw.getValue();
}
@Override
public boolean isSecure() {
return raw.getSecure();
}
@Override
public boolean isHttpOnly() {
return raw.isHttpOnly();
}
@Override
public boolean isHostOnly() {
return false;
}
@Override
public String getDomain() {
return raw.getDomain();
}
@Override
public String getPath() {
return raw.getPath();
}
@Override
public boolean isExpired() {
return raw.hasExpired();
}
@Override
public boolean isExpired(final Instant now) {
final long maxAge = raw.getMaxAge();
if (maxAge == 0) {
return true;
}
if (maxAge < 0) {
return false;
}
final long creationTime = MethodUtil.invoke(raw, "getCreationTime");
final long deltaSecond = (now.toEpochMilli() - creationTime) / 1000;
return deltaSecond > maxAge;
}
@Override
public String getAttribute(final String name) {
throw new UnsupportedOperationException();
}
@Override
public boolean isPersistent() {
return true;
}
}

View File

@ -19,7 +19,7 @@ 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 org.dromara.hutool.http.client.cookie.InMemoryCookieStore;
import java.io.Closeable;
import java.io.IOException;
@ -50,7 +50,7 @@ public class JdkCookieManager implements Closeable {
* 构造
*/
public JdkCookieManager() {
this(new CookieManager(new ThreadLocalCookieStore(), CookiePolicy.ACCEPT_ALL));
this(new CookieManager(new JdkCookieStore(new InMemoryCookieStore()), CookiePolicy.ACCEPT_ALL));
}
/**

View File

@ -0,0 +1,92 @@
/*
* 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.lang.wrapper.SimpleWrapper;
import org.dromara.hutool.http.client.cookie.CookieSpi;
import org.dromara.hutool.http.client.cookie.CookieStoreSpi;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* JDK CookieStore实现
*
* @author looly
* @since 6.0.0
*/
public class JdkCookieStore extends SimpleWrapper<CookieStoreSpi> implements CookieStore {
/**
* 构造
*
* @param raw 原始对象
*/
public JdkCookieStore(final CookieStoreSpi raw) {
super(raw);
}
@Override
public void add(final URI uri, final HttpCookie cookie) {
this.raw.add(uri, new JdkCookie(cookie));
}
@Override
public List<HttpCookie> get(final URI uri) {
final List<CookieSpi> cookieSpis = this.raw.get(uri);
if (null == cookieSpis) {
return null;
}
final List<HttpCookie> result = new ArrayList<>(cookieSpis.size());
for (final CookieSpi cookieSpi : cookieSpis) {
result.add(((JdkCookie) cookieSpi).getRaw());
}
return result;
}
@Override
public List<HttpCookie> getCookies() {
final List<CookieSpi> cookieSpis = this.raw.getCookies();
if (null == cookieSpis) {
return null;
}
final List<HttpCookie> result = new ArrayList<>(cookieSpis.size());
for (final CookieSpi cookieSpi : cookieSpis) {
result.add(((JdkCookie) cookieSpi).getRaw());
}
return result;
}
@Override
public List<URI> getURIs() {
return this.raw.getURIs();
}
@Override
public boolean remove(final URI uri, final HttpCookie cookie) {
return false;
}
@Override
public boolean removeAll() {
return false;
}
}

View File

@ -19,7 +19,11 @@ package org.dromara.hutool.http.client.engine.okhttp;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.HttpUrl;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.http.client.cookie.CookieSpi;
import org.dromara.hutool.http.client.cookie.CookieStoreSpi;
import java.util.ArrayList;
import java.util.List;
/**
@ -30,13 +34,15 @@ import java.util.List;
*/
public class CookieJarImpl implements CookieJar {
private final OkCookieStore cookieStore;
private final CookieStoreSpi cookieStore;
/**
* 构造
*
* @param cookieStore Cookie存储器用于自定义Cookie存储实现
*/
public CookieJarImpl() {
this(new InMemoryOkCookieStore());
public CookieJarImpl(final CookieStoreSpi cookieStore) {
this.cookieStore = cookieStore;
}
/**
@ -44,26 +50,28 @@ public class CookieJarImpl implements CookieJar {
*
* @return Cookie存储器
*/
public OkCookieStore getCookieStore() {
public CookieStoreSpi getCookieStore() {
return this.cookieStore;
}
/**
* 构造
*
* @param cookieStore Cookie存储器用于自定义Cookie存储实现
*/
public CookieJarImpl(final OkCookieStore cookieStore) {
this.cookieStore = cookieStore;
}
@Override
public List<Cookie> loadForRequest(final HttpUrl httpUrl) {
return this.cookieStore.get(httpUrl);
final List<CookieSpi> cookieSpis = this.cookieStore.get(httpUrl.uri());
final List<Cookie> cookies = new ArrayList<>(cookieSpis.size());
for (final CookieSpi cookieSpi : cookieSpis) {
cookies.add(((OkCookie)cookieSpi).getRaw());
}
return cookies;
}
@Override
public void saveFromResponse(final HttpUrl httpUrl, final List<Cookie> list) {
this.cookieStore.add(httpUrl, list);
if(CollUtil.isEmpty(list)){
return;
}
for (final Cookie cookie : list) {
this.cookieStore.add(httpUrl.uri(), new OkCookie(cookie));
}
}
}

View File

@ -1,149 +0,0 @@
/*
* 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<String, ConcurrentHashMap<String, Cookie>> 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<Cookie> cookies) {
for (final Cookie cookie : cookies) {
if (isCookieExpired(cookie)) {
continue;
}
this.add(httpUrl, cookie);
}
}
@Override
public List<Cookie> get(final HttpUrl httpUrl) {
return this.get(httpUrl.host());
}
@Override
public List<Cookie> getCookies() {
final ArrayList<Cookie> result = new ArrayList<>();
for (final String hostKey : this.cookies.keySet()) {
result.addAll(this.get(hostKey));
}
return result;
}
/**
* 获取cookie集合
*/
private List<Cookie> get(final String hostKey) {
final ArrayList<Cookie> result = new ArrayList<>();
if (this.cookies.containsKey(hostKey)) {
final Collection<Cookie> 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<br>
* 格式name + domain
*
* @param cookie cookie
* @return cookie的key
*/
private String cookieName(final Cookie cookie) {
return cookie == null ? null : cookie.name() + cookie.domain();
}
}

View File

@ -0,0 +1,96 @@
/*
* 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 org.dromara.hutool.core.lang.wrapper.SimpleWrapper;
import org.dromara.hutool.http.client.cookie.CookieSpi;
import java.time.Instant;
/**
* OkHttp Cookie实现
*
* @author looly
* @since 6.0.0
*/
public class OkCookie extends SimpleWrapper<Cookie> implements CookieSpi {
/**
* 构造
*
* @param raw 原始对象
*/
public OkCookie(final Cookie raw) {
super(raw);
}
@Override
public String getName() {
return raw.name();
}
@Override
public String getValue() {
return raw.value();
}
@Override
public boolean isSecure() {
return raw.secure();
}
@Override
public boolean isHttpOnly() {
return raw.httpOnly();
}
@Override
public boolean isHostOnly() {
return raw.hostOnly();
}
@Override
public String getDomain() {
return raw.domain();
}
@Override
public String getPath() {
return raw.path();
}
@Override
public boolean isExpired(final Instant now) {
return raw.expiresAt() < now.toEpochMilli();
}
@Override
public String getAttribute(final String name) {
throw new UnsupportedOperationException();
}
@Override
public boolean isPersistent() {
return raw.persistent();
}
@Override
public String toString() {
return raw.toString();
}
}

View File

@ -24,6 +24,7 @@ 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.proxy.HttpProxy;
import org.dromara.hutool.http.ssl.SSLInfo;
@ -43,7 +44,6 @@ import java.util.concurrent.TimeUnit;
public class OkHttpEngine extends AbstractClientEngine {
private OkHttpClient client;
private OkCookieStore cookieStore;
/**
* 构造
@ -54,15 +54,6 @@ 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;
@ -145,7 +136,7 @@ public class OkHttpEngine extends AbstractClientEngine {
// Cookie管理
if (this.config.isUseCookieManager()) {
this.cookieStore = new InMemoryOkCookieStore();
this.cookieStore = new InMemoryCookieStore();
builder.cookieJar(new CookieJarImpl(this.cookieStore));
}