From be3ee886c0adc4dbf9dcefe58e43e9cb5362911f Mon Sep 17 00:00:00 2001 From: Looly Date: Thu, 10 Oct 2024 22:24:51 +0800 Subject: [PATCH] fix bug --- .../core/io/stream/SyncInputStream.java | 14 ++--- .../dromara/hutool/http/client/Response.java | 18 +++++-- .../hutool/http/client/body/ResponseBody.java | 10 ++++ .../engine/httpclient4/HttpClient4Engine.java | 6 ++- .../httpclient4/HttpClient4Response.java | 35 ++++++++---- .../engine/httpclient5/HttpClient5Engine.java | 6 ++- .../httpclient5/HttpClient5Response.java | 46 ++++++++++------ .../client/engine/jdk/JdkClientEngine.java | 25 +++------ .../client/engine/jdk/JdkHttpResponse.java | 54 +++++++------------ .../client/engine/okhttp/OkHttpEngine.java | 2 +- .../client/engine/okhttp/OkHttpResponse.java | 37 +++++++++---- .../hutool/http/client/Issue3765Test.java | 41 ++++++++++++++ 12 files changed, 190 insertions(+), 104 deletions(-) create mode 100644 hutool-http/src/test/java/org/dromara/hutool/http/client/Issue3765Test.java diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/io/stream/SyncInputStream.java b/hutool-core/src/main/java/org/dromara/hutool/core/io/stream/SyncInputStream.java index 1b41760c2..c2e8e3e12 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/io/stream/SyncInputStream.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/io/stream/SyncInputStream.java @@ -58,7 +58,8 @@ public class SyncInputStream extends FilterInputStream { } /** - * 同步数据到内存 + * 同步数据到内存,同步后关闭原流 + * * @return this */ public SyncInputStream sync() { @@ -76,18 +77,19 @@ public class SyncInputStream extends FilterInputStream { * @return bytes */ public byte[] readBytes() { - final FastByteArrayOutputStream bytesOut = new FastByteArrayOutputStream(length > 0 ? (int)length : 1024); + final FastByteArrayOutputStream bytesOut = new FastByteArrayOutputStream(length > 0 ? (int) length : 1024); final long length = copyTo(bytesOut, null); return length > 0 ? bytesOut.toByteArray() : new byte[0]; } /** - * 将流的内容拷贝到输出流 - * @param out 输出流 + * 将流的内容拷贝到输出流,拷贝结束后关闭输入流 + * + * @param out 输出流 * @param streamProgress 进度条 * @return 拷贝长度 */ - public long copyTo(final OutputStream out, final StreamProgress streamProgress){ + public long copyTo(final OutputStream out, final StreamProgress streamProgress) { long copyLength = -1; try { copyLength = IoUtil.copy(this.in, out, IoUtil.DEFAULT_BUFFER_SIZE, this.length, streamProgress); @@ -96,7 +98,7 @@ public class SyncInputStream extends FilterInputStream { throw e; } // 忽略读取流中的EOF错误 - }finally { + } finally { // 读取结束 IoUtil.closeQuietly(in); } diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/Response.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/Response.java index 097efacb4..a9545b361 100644 --- a/hutool-http/src/main/java/org/dromara/hutool/http/client/Response.java +++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/Response.java @@ -82,13 +82,21 @@ public interface Response extends Closeable { InputStream bodyStream(); /** - * 获取响应体,包含服务端返回的内容和Content-Type信息 + * 同步
+ * 如果为异步状态,则暂时不读取服务器中响应的内容,而是持有Http链接的{@link InputStream}。
+ * 当调用此方法时,异步状态转为同步状态,此时从Http链接流中读取body内容并暂存在内容(内存)中。如果已经是同步状态,则不进行任何操作。 + * + * @return this + */ + Response sync(); + + /** + * 获取响应体,包含服务端返回的内容和Content-Type信息
+ * 如果为HEAD、CONNECT、TRACE等方法无响应体,则返回{@code null} * * @return {@link ResponseBody} */ - default ResponseBody body() { - return new ResponseBody(this, bodyStream(), false, true); - } + ResponseBody body(); /** * 获取响应主体 @@ -112,7 +120,7 @@ public interface Response extends Closeable { */ default byte[] bodyBytes() { try (final ResponseBody body = body()) { - return body.getBytes(); + return null == body ? null : body.getBytes(); } catch (final IOException e) { throw new IORuntimeException(e); } diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/body/ResponseBody.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/body/ResponseBody.java index 974962885..8163f99fe 100644 --- a/hutool-http/src/main/java/org/dromara/hutool/http/client/body/ResponseBody.java +++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/body/ResponseBody.java @@ -44,6 +44,16 @@ public class ResponseBody implements HttpBody, Closeable { */ private final SyncInputStream bodyStream; + /** + * 构造,不读取响应体,忽略响应体EOF错误 + * + * @param response 响应体 + * @param in HTTP主体响应流 + */ + public ResponseBody(final Response response, final InputStream in) { + this(response, in, true, true); + } + /** * 构造 * 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 eb86f796a..4e724002b 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 @@ -21,6 +21,7 @@ import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; 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; @@ -75,11 +76,14 @@ public class HttpClient4Engine extends AbstractClientEngine { initEngine(); final HttpUriRequest request = buildRequest(message); + final CloseableHttpResponse response; try { - return this.engine.execute(request, response -> new HttpClient4Response(response, message.charset())); + //return this.engine.execute(request, response -> new HttpClient4Response(response, message)); + response = this.engine.execute(request); } catch (final IOException e) { throw new HttpException(e); } + return new HttpClient4Response(response, message); } @Override diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Response.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Response.java index c3ff0a554..ce2537603 100644 --- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Response.java +++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient4/HttpClient4Response.java @@ -24,13 +24,15 @@ import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.util.EntityUtils; import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.io.IORuntimeException; +import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.lang.wrapper.SimpleWrapper; import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.http.HttpException; import org.dromara.hutool.http.HttpUtil; +import org.dromara.hutool.http.client.Request; import org.dromara.hutool.http.client.Response; +import org.dromara.hutool.http.client.body.ResponseBody; -import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; @@ -52,18 +54,20 @@ public class HttpClient4Response extends SimpleWrapper implements * 请求时的默认编码 */ private final Charset requestCharset; + private final ResponseBody body; /** * 构造
* 通过传入一个请求时的编码,当无法获取响应内容的编码时,默认使用响应时的编码 * - * @param rawRes {@link HttpResponse} - * @param requestCharset 请求时的编码 + * @param rawRes {@link HttpResponse} + * @param message 请求消息 */ - public HttpClient4Response(final HttpResponse rawRes, final Charset requestCharset) { + public HttpClient4Response(final HttpResponse rawRes, final Request message) { super(rawRes); this.entity = rawRes.getEntity(); - this.requestCharset = requestCharset; + this.requestCharset = message.charset(); + this.body = message.method().isIgnoreBody() ? null : new ResponseBody(this, bodyStream()); } @@ -112,6 +116,21 @@ public class HttpClient4Response extends SimpleWrapper implements } } + @Override + public HttpClient4Response sync() { + final ResponseBody body = this.body; + if(null != body){ + body.sync(); + } + IoUtil.closeIfPossible(this.raw); + return this; + } + + @Override + public ResponseBody body() { + return this.body; + } + @Override public String bodyStr() throws HttpException { try { @@ -124,10 +143,8 @@ public class HttpClient4Response extends SimpleWrapper implements } @Override - public void close() throws IOException { - if(this.raw instanceof Closeable){ - ((Closeable) this.raw).close(); - } + public void close() { + IoUtil.closeIfPossible(this.raw); } @Override 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 e91ca3512..cbb735a1a 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 @@ -29,6 +29,7 @@ import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.message.BasicHeader; @@ -81,11 +82,14 @@ public class HttpClient5Engine extends AbstractClientEngine { initEngine(); final ClassicHttpRequest request = buildRequest(message); + final ClassicHttpResponse response; try { - return this.engine.execute(request, (response -> new HttpClient5Response(response, message.charset()))); + //return this.engine.execute(request, (response -> new HttpClient5Response(response, message))); + response = this.engine.executeOpen(null, request, null); } catch (final IOException e) { throw new HttpException(e); } + return new HttpClient5Response(response, message); } @Override diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Response.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Response.java index 57c002bd5..f21b08a4a 100644 --- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Response.java +++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/httpclient5/HttpClient5Response.java @@ -16,28 +16,27 @@ package org.dromara.hutool.http.client.engine.httpclient5; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ClassicHttpResponse; +import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; -import org.dromara.hutool.core.io.IORuntimeException; +import org.apache.hc.core5.http.ParseException; +import org.apache.hc.core5.http.io.entity.EntityUtils; import org.dromara.hutool.core.array.ArrayUtil; +import org.dromara.hutool.core.io.IORuntimeException; +import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.lang.wrapper.SimpleWrapper; import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.http.HttpException; import org.dromara.hutool.http.HttpUtil; +import org.dromara.hutool.http.client.Request; import org.dromara.hutool.http.client.Response; -import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; -import org.apache.hc.core5.http.ClassicHttpResponse; -import org.apache.hc.core5.http.Header; -import org.apache.hc.core5.http.ParseException; -import org.apache.hc.core5.http.io.entity.EntityUtils; +import org.dromara.hutool.http.client.body.ResponseBody; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * HttpClient响应包装
@@ -52,18 +51,20 @@ public class HttpClient5Response extends SimpleWrapper impl * 请求时的默认编码 */ private final Charset requestCharset; + private final ResponseBody body; /** * 构造
* 通过传入一个请求时的编码,当无法获取响应内容的编码时,默认使用响应时的编码 * - * @param rawRes {@link CloseableHttpResponse} - * @param requestCharset 请求时的编码 + * @param rawRes {@link CloseableHttpResponse} + * @param message 请求消息 */ - public HttpClient5Response(final ClassicHttpResponse rawRes, final Charset requestCharset) { + public HttpClient5Response(final ClassicHttpResponse rawRes, final Request message) { super(rawRes); this.entity = rawRes.getEntity(); - this.requestCharset = requestCharset; + this.requestCharset = message.charset(); + this.body = message.method().isIgnoreBody() ? null : new ResponseBody(this, bodyStream()); } @@ -112,6 +113,21 @@ public class HttpClient5Response extends SimpleWrapper impl } } + @Override + public HttpClient5Response sync() { + final ResponseBody body = this.body; + if(null != body){ + body.sync(); + } + IoUtil.closeQuietly(this.raw); + return this; + } + + @Override + public ResponseBody body() { + return this.body; + } + @Override public String bodyStr() throws HttpException { try { 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 dfed64f97..53626c693 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 @@ -26,7 +26,6 @@ import org.dromara.hutool.http.HttpException; import org.dromara.hutool.http.HttpUtil; 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.engine.AbstractClientEngine; import org.dromara.hutool.http.meta.HeaderName; @@ -65,18 +64,7 @@ public class JdkClientEngine extends AbstractClientEngine { } @Override - public Response send(final Request message) { - return send(message, true); - } - - /** - * 发送请求 - * - * @param message 请求消息 - * @param isAsync 是否异步,异步不会立即读取响应内容 - * @return {@link Response} - */ - public JdkHttpResponse send(final Request message, final boolean isAsync) { + public JdkHttpResponse send(final Request message) { final JdkHttpConnection conn = buildConn(message); try { doSend(conn, message); @@ -86,7 +74,7 @@ public class JdkClientEngine extends AbstractClientEngine { throw new IORuntimeException(e); } - return sendRedirectIfPossible(conn, message, isAsync); + return sendRedirectIfPossible(conn, message); } @Override @@ -173,11 +161,10 @@ public class JdkClientEngine extends AbstractClientEngine { /** * 调用转发,如果需要转发返回转发结果,否则返回{@code null} * - * @param conn {@link JdkHttpConnection}} - * @param isAsync 最终请求是否异步 + * @param conn {@link JdkHttpConnection}} * @return {@link JdkHttpResponse},无转发返回 {@code null} */ - private JdkHttpResponse sendRedirectIfPossible(final JdkHttpConnection conn, final Request message, final boolean isAsync) { + private JdkHttpResponse sendRedirectIfPossible(final JdkHttpConnection conn, final Request message) { // 手动实现重定向 if (message.maxRedirects() > 0) { final int code; @@ -203,14 +190,14 @@ public class JdkClientEngine extends AbstractClientEngine { if (conn.redirectCount < message.maxRedirects()) { conn.redirectCount++; - return send(message, isAsync); + return send(message); } } } } // 最终页面 - return new JdkHttpResponse(conn, this.cookieManager, true, message.charset(), isAsync, message.method().isIgnoreBody()); + return new JdkHttpResponse(conn, this.cookieManager, message); } /** 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 9cdd0d3d0..df341f879 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 @@ -21,6 +21,7 @@ import org.dromara.hutool.core.io.stream.EmptyInputStream; import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.http.HttpException; import org.dromara.hutool.http.HttpUtil; +import org.dromara.hutool.http.client.Request; import org.dromara.hutool.http.client.Response; import org.dromara.hutool.http.client.body.ResponseBody; import org.dromara.hutool.http.meta.HeaderName; @@ -42,6 +43,10 @@ import java.util.Map; */ public class JdkHttpResponse implements Response, Closeable { + /** + * 持有连接对象 + */ + protected JdkHttpConnection httpConnection; /** * 请求时的默认编码 */ @@ -50,26 +55,14 @@ public class JdkHttpResponse implements Response, Closeable { * Cookie管理器 */ private final JdkCookieManager cookieManager; - /** - * 响应内容体,{@code null} 表示无内容 - */ - private ResponseBody body; /** * 响应头 */ private Map> headers; - /** - * 是否忽略响应读取时可能的EOF异常。
- * 在Http协议中,对于Transfer-Encoding: Chunked在正常情况下末尾会写入一个Length为0的的chunk标识完整结束。
- * 如果服务端未遵循这个规范或响应没有正常结束,会报EOF异常,此选项用于是否忽略这个异常。 + * 响应内容体,{@code null} 表示无内容 */ - private final boolean ignoreEOFError; - - /** - * 持有连接对象 - */ - protected JdkHttpConnection httpConnection; + private ResponseBody body; /** * 响应状态码 */ @@ -80,22 +73,15 @@ public class JdkHttpResponse implements Response, Closeable { * * @param httpConnection {@link JdkHttpConnection} * @param cookieManager Cookie管理器 - * @param ignoreEOFError 是否忽略响应读取时可能的EOF异常 - * @param requestCharset 编码,从请求编码中获取默认编码 - * @param isAsync 是否异步 - * @param isIgnoreBody 是否忽略读取响应体 + * @param message 请求消息 */ protected JdkHttpResponse(final JdkHttpConnection httpConnection, final JdkCookieManager cookieManager, - final boolean ignoreEOFError, - final Charset requestCharset, - final boolean isAsync, - final boolean isIgnoreBody) { + final Request message) { this.httpConnection = httpConnection; this.cookieManager = cookieManager; - this.ignoreEOFError = ignoreEOFError; - this.requestCharset = requestCharset; - init(isAsync, isIgnoreBody); + this.requestCharset = message.charset(); + init(message.method().isIgnoreBody()); } /** @@ -128,18 +114,13 @@ public class JdkHttpResponse implements Response, Closeable { return ObjUtil.defaultIfNull(Response.super.charset(), requestCharset); } - /** - * 同步
- * 如果为异步状态,则暂时不读取服务器中响应的内容,而是持有Http链接的{@link InputStream}。
- * 当调用此方法时,异步状态转为同步状态,此时从Http链接流中读取body内容并暂存在内容中。如果已经是同步状态,则不进行任何操作。 - * - * @return this - */ + @Override public JdkHttpResponse sync() { if (null != this.body) { this.body.sync(); } - close(); + // 关闭连接 + this.httpConnection.closeQuietly(); return this; } @@ -250,9 +231,10 @@ public class JdkHttpResponse implements Response, Closeable { * 3、持有Http流,并不关闭流 * * + * @param isIgnoreBody 是否忽略消息体 * @throws HttpException IO异常 */ - private void init(final boolean isAsync, final boolean isIgnoreBody) throws HttpException { + private void init(final boolean isIgnoreBody) throws HttpException { // 获取响应状态码 try { this.status = httpConnection.getCode(); @@ -272,13 +254,13 @@ public class JdkHttpResponse implements Response, Closeable { } // 存储服务端设置的Cookie信息 - if(null != this.cookieManager){ + if (null != this.cookieManager) { this.cookieManager.saveFromResponse(this.httpConnection, this.headers); } // 获取响应内容流 if (!isIgnoreBody) { - this.body = new ResponseBody(this, new JdkHttpInputStream(this), isAsync, this.ignoreEOFError); + this.body = new ResponseBody(this, new JdkHttpInputStream(this)); } } // ---------------------------------------------------------------- Private method end 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 da84d98fc..3aa5dac82 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 @@ -71,7 +71,7 @@ public class OkHttpEngine extends AbstractClientEngine { throw new IORuntimeException(e); } - return new OkHttpResponse(response, message.charset()); + return new OkHttpResponse(response, message); } @Override diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpResponse.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpResponse.java index 1ea5ca628..feb553f58 100644 --- a/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpResponse.java +++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/engine/okhttp/OkHttpResponse.java @@ -16,19 +16,20 @@ package org.dromara.hutool.http.client.engine.okhttp; -import kotlin.Pair; -import okhttp3.Headers; -import okhttp3.ResponseBody; +import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.stream.EmptyInputStream; import org.dromara.hutool.core.util.ObjUtil; import org.dromara.hutool.http.GlobalCompressStreamRegister; import org.dromara.hutool.http.HttpUtil; +import org.dromara.hutool.http.client.Request; import org.dromara.hutool.http.client.Response; +import org.dromara.hutool.http.client.body.ResponseBody; import org.dromara.hutool.http.meta.HeaderName; import java.io.InputStream; import java.nio.charset.Charset; -import java.util.*; +import java.util.List; +import java.util.Map; /** * OkHttp3的{@link okhttp3.Response} 响应包装 @@ -42,14 +43,16 @@ public class OkHttpResponse implements Response { * 请求时的默认编码 */ private final Charset requestCharset; + private final ResponseBody body; /** * @param rawRes {@link okhttp3.Response} - * @param requestCharset 请求时的默认编码 + * @param message 请求对象 */ - public OkHttpResponse(final okhttp3.Response rawRes, final Charset requestCharset) { + public OkHttpResponse(final okhttp3.Response rawRes, final Request message) { this.rawRes = rawRes; - this.requestCharset = requestCharset; + this.requestCharset = message.charset(); + this.body = message.method().isIgnoreBody() ? null : new ResponseBody(this, bodyStream()); } @Override @@ -74,7 +77,7 @@ public class OkHttpResponse implements Response { @Override public InputStream bodyStream() { - final ResponseBody body = rawRes.body(); + final okhttp3.ResponseBody body = rawRes.body(); if(null == body){ return EmptyInputStream.INSTANCE; } @@ -84,10 +87,22 @@ public class OkHttpResponse implements Response { } @Override - public void close() { - if(null != this.rawRes){ - rawRes.close(); + public OkHttpResponse sync() { + if (null != this.body) { + this.body.sync(); } + IoUtil.closeQuietly(this.rawRes); + return this; + } + + @Override + public ResponseBody body() { + return this.body; + } + + @Override + public void close() { + IoUtil.closeQuietly(this.rawRes); } @Override diff --git a/hutool-http/src/test/java/org/dromara/hutool/http/client/Issue3765Test.java b/hutool-http/src/test/java/org/dromara/hutool/http/client/Issue3765Test.java new file mode 100644 index 000000000..a8b207704 --- /dev/null +++ b/hutool-http/src/test/java/org/dromara/hutool/http/client/Issue3765Test.java @@ -0,0 +1,41 @@ +/* + * 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; + +import org.dromara.hutool.http.HttpUtil; +import org.dromara.hutool.http.client.engine.ClientEngine; +import org.dromara.hutool.http.client.engine.ClientEngineFactory; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +public class Issue3765Test { + + public static void main(String[] args) { + HttpUtil.createServer(8888) + .setRoot("d:/test/www") + .start(); + } + @Test + @Disabled + void downloadTest() { + final String url = "http://localhost:8888/a.mp3"; + final ClientEngine engine = ClientEngineFactory.createEngine("httpclient4"); + Request.of(url) + .send(engine) + .body().write("d:/test/"); + } +}