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(); + } +}