mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
fix bug
This commit is contained in:
parent
ae1f3635ce
commit
be3ee886c0
@ -58,7 +58,8 @@ public class SyncInputStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步数据到内存
|
* 同步数据到内存,同步后关闭原流
|
||||||
|
*
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public SyncInputStream sync() {
|
public SyncInputStream sync() {
|
||||||
@ -76,18 +77,19 @@ public class SyncInputStream extends FilterInputStream {
|
|||||||
* @return bytes
|
* @return bytes
|
||||||
*/
|
*/
|
||||||
public byte[] readBytes() {
|
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);
|
final long length = copyTo(bytesOut, null);
|
||||||
return length > 0 ? bytesOut.toByteArray() : new byte[0];
|
return length > 0 ? bytesOut.toByteArray() : new byte[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将流的内容拷贝到输出流
|
* 将流的内容拷贝到输出流,拷贝结束后关闭输入流
|
||||||
* @param out 输出流
|
*
|
||||||
|
* @param out 输出流
|
||||||
* @param streamProgress 进度条
|
* @param streamProgress 进度条
|
||||||
* @return 拷贝长度
|
* @return 拷贝长度
|
||||||
*/
|
*/
|
||||||
public long copyTo(final OutputStream out, final StreamProgress streamProgress){
|
public long copyTo(final OutputStream out, final StreamProgress streamProgress) {
|
||||||
long copyLength = -1;
|
long copyLength = -1;
|
||||||
try {
|
try {
|
||||||
copyLength = IoUtil.copy(this.in, out, IoUtil.DEFAULT_BUFFER_SIZE, this.length, streamProgress);
|
copyLength = IoUtil.copy(this.in, out, IoUtil.DEFAULT_BUFFER_SIZE, this.length, streamProgress);
|
||||||
@ -96,7 +98,7 @@ public class SyncInputStream extends FilterInputStream {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
// 忽略读取流中的EOF错误
|
// 忽略读取流中的EOF错误
|
||||||
}finally {
|
} finally {
|
||||||
// 读取结束
|
// 读取结束
|
||||||
IoUtil.closeQuietly(in);
|
IoUtil.closeQuietly(in);
|
||||||
}
|
}
|
||||||
|
@ -82,13 +82,21 @@ public interface Response extends Closeable {
|
|||||||
InputStream bodyStream();
|
InputStream bodyStream();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取响应体,包含服务端返回的内容和Content-Type信息
|
* 同步<br>
|
||||||
|
* 如果为异步状态,则暂时不读取服务器中响应的内容,而是持有Http链接的{@link InputStream}。<br>
|
||||||
|
* 当调用此方法时,异步状态转为同步状态,此时从Http链接流中读取body内容并暂存在内容(内存)中。如果已经是同步状态,则不进行任何操作。
|
||||||
|
*
|
||||||
|
* @return this
|
||||||
|
*/
|
||||||
|
Response sync();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取响应体,包含服务端返回的内容和Content-Type信息<br>
|
||||||
|
* 如果为HEAD、CONNECT、TRACE等方法无响应体,则返回{@code null}
|
||||||
*
|
*
|
||||||
* @return {@link ResponseBody}
|
* @return {@link ResponseBody}
|
||||||
*/
|
*/
|
||||||
default ResponseBody body() {
|
ResponseBody body();
|
||||||
return new ResponseBody(this, bodyStream(), false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取响应主体
|
* 获取响应主体
|
||||||
@ -112,7 +120,7 @@ public interface Response extends Closeable {
|
|||||||
*/
|
*/
|
||||||
default byte[] bodyBytes() {
|
default byte[] bodyBytes() {
|
||||||
try (final ResponseBody body = body()) {
|
try (final ResponseBody body = body()) {
|
||||||
return body.getBytes();
|
return null == body ? null : body.getBytes();
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,16 @@ public class ResponseBody implements HttpBody, Closeable {
|
|||||||
*/
|
*/
|
||||||
private final SyncInputStream bodyStream;
|
private final SyncInputStream bodyStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造,不读取响应体,忽略响应体EOF错误
|
||||||
|
*
|
||||||
|
* @param response 响应体
|
||||||
|
* @param in HTTP主体响应流
|
||||||
|
*/
|
||||||
|
public ResponseBody(final Response response, final InputStream in) {
|
||||||
|
this(response, in, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造
|
* 构造
|
||||||
*
|
*
|
||||||
|
@ -21,6 +21,7 @@ import org.apache.http.HttpHost;
|
|||||||
import org.apache.http.auth.AuthScope;
|
import org.apache.http.auth.AuthScope;
|
||||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||||
import org.apache.http.client.config.RequestConfig;
|
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.HttpUriRequest;
|
||||||
import org.apache.http.client.methods.RequestBuilder;
|
import org.apache.http.client.methods.RequestBuilder;
|
||||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||||
@ -75,11 +76,14 @@ public class HttpClient4Engine extends AbstractClientEngine {
|
|||||||
initEngine();
|
initEngine();
|
||||||
|
|
||||||
final HttpUriRequest request = buildRequest(message);
|
final HttpUriRequest request = buildRequest(message);
|
||||||
|
final CloseableHttpResponse response;
|
||||||
try {
|
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) {
|
} catch (final IOException e) {
|
||||||
throw new HttpException(e);
|
throw new HttpException(e);
|
||||||
}
|
}
|
||||||
|
return new HttpClient4Response(response, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -24,13 +24,15 @@ import org.apache.http.client.methods.CloseableHttpResponse;
|
|||||||
import org.apache.http.util.EntityUtils;
|
import org.apache.http.util.EntityUtils;
|
||||||
import org.dromara.hutool.core.array.ArrayUtil;
|
import org.dromara.hutool.core.array.ArrayUtil;
|
||||||
import org.dromara.hutool.core.io.IORuntimeException;
|
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.lang.wrapper.SimpleWrapper;
|
||||||
import org.dromara.hutool.core.util.ObjUtil;
|
import org.dromara.hutool.core.util.ObjUtil;
|
||||||
import org.dromara.hutool.http.HttpException;
|
import org.dromara.hutool.http.HttpException;
|
||||||
import org.dromara.hutool.http.HttpUtil;
|
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.Response;
|
||||||
|
import org.dromara.hutool.http.client.body.ResponseBody;
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
@ -52,18 +54,20 @@ public class HttpClient4Response extends SimpleWrapper<HttpResponse> implements
|
|||||||
* 请求时的默认编码
|
* 请求时的默认编码
|
||||||
*/
|
*/
|
||||||
private final Charset requestCharset;
|
private final Charset requestCharset;
|
||||||
|
private final ResponseBody body;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造<br>
|
* 构造<br>
|
||||||
* 通过传入一个请求时的编码,当无法获取响应内容的编码时,默认使用响应时的编码
|
* 通过传入一个请求时的编码,当无法获取响应内容的编码时,默认使用响应时的编码
|
||||||
*
|
*
|
||||||
* @param rawRes {@link HttpResponse}
|
* @param rawRes {@link HttpResponse}
|
||||||
* @param requestCharset 请求时的编码
|
* @param message 请求消息
|
||||||
*/
|
*/
|
||||||
public HttpClient4Response(final HttpResponse rawRes, final Charset requestCharset) {
|
public HttpClient4Response(final HttpResponse rawRes, final Request message) {
|
||||||
super(rawRes);
|
super(rawRes);
|
||||||
this.entity = rawRes.getEntity();
|
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<HttpResponse> 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
|
@Override
|
||||||
public String bodyStr() throws HttpException {
|
public String bodyStr() throws HttpException {
|
||||||
try {
|
try {
|
||||||
@ -124,10 +143,8 @@ public class HttpClient4Response extends SimpleWrapper<HttpResponse> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() {
|
||||||
if(this.raw instanceof Closeable){
|
IoUtil.closeIfPossible(this.raw);
|
||||||
((Closeable) this.raw).close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -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.PoolingHttpClientConnectionManager;
|
||||||
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
|
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
|
||||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
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.Header;
|
||||||
import org.apache.hc.core5.http.HttpHost;
|
import org.apache.hc.core5.http.HttpHost;
|
||||||
import org.apache.hc.core5.http.message.BasicHeader;
|
import org.apache.hc.core5.http.message.BasicHeader;
|
||||||
@ -81,11 +82,14 @@ public class HttpClient5Engine extends AbstractClientEngine {
|
|||||||
initEngine();
|
initEngine();
|
||||||
|
|
||||||
final ClassicHttpRequest request = buildRequest(message);
|
final ClassicHttpRequest request = buildRequest(message);
|
||||||
|
final ClassicHttpResponse response;
|
||||||
try {
|
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) {
|
} catch (final IOException e) {
|
||||||
throw new HttpException(e);
|
throw new HttpException(e);
|
||||||
}
|
}
|
||||||
|
return new HttpClient5Response(response, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,28 +16,27 @@
|
|||||||
|
|
||||||
package org.dromara.hutool.http.client.engine.httpclient5;
|
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.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.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.lang.wrapper.SimpleWrapper;
|
||||||
import org.dromara.hutool.core.util.ObjUtil;
|
import org.dromara.hutool.core.util.ObjUtil;
|
||||||
import org.dromara.hutool.http.HttpException;
|
import org.dromara.hutool.http.HttpException;
|
||||||
import org.dromara.hutool.http.HttpUtil;
|
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.Response;
|
||||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
|
import org.dromara.hutool.http.client.body.ResponseBody;
|
||||||
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 java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HttpClient响应包装<br>
|
* HttpClient响应包装<br>
|
||||||
@ -52,18 +51,20 @@ public class HttpClient5Response extends SimpleWrapper<ClassicHttpResponse> impl
|
|||||||
* 请求时的默认编码
|
* 请求时的默认编码
|
||||||
*/
|
*/
|
||||||
private final Charset requestCharset;
|
private final Charset requestCharset;
|
||||||
|
private final ResponseBody body;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造<br>
|
* 构造<br>
|
||||||
* 通过传入一个请求时的编码,当无法获取响应内容的编码时,默认使用响应时的编码
|
* 通过传入一个请求时的编码,当无法获取响应内容的编码时,默认使用响应时的编码
|
||||||
*
|
*
|
||||||
* @param rawRes {@link CloseableHttpResponse}
|
* @param rawRes {@link CloseableHttpResponse}
|
||||||
* @param requestCharset 请求时的编码
|
* @param message 请求消息
|
||||||
*/
|
*/
|
||||||
public HttpClient5Response(final ClassicHttpResponse rawRes, final Charset requestCharset) {
|
public HttpClient5Response(final ClassicHttpResponse rawRes, final Request message) {
|
||||||
super(rawRes);
|
super(rawRes);
|
||||||
this.entity = rawRes.getEntity();
|
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<ClassicHttpResponse> 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
|
@Override
|
||||||
public String bodyStr() throws HttpException {
|
public String bodyStr() throws HttpException {
|
||||||
try {
|
try {
|
||||||
|
@ -26,7 +26,6 @@ import org.dromara.hutool.http.HttpException;
|
|||||||
import org.dromara.hutool.http.HttpUtil;
|
import org.dromara.hutool.http.HttpUtil;
|
||||||
import org.dromara.hutool.http.client.ClientConfig;
|
import org.dromara.hutool.http.client.ClientConfig;
|
||||||
import org.dromara.hutool.http.client.Request;
|
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.body.HttpBody;
|
||||||
import org.dromara.hutool.http.client.engine.AbstractClientEngine;
|
import org.dromara.hutool.http.client.engine.AbstractClientEngine;
|
||||||
import org.dromara.hutool.http.meta.HeaderName;
|
import org.dromara.hutool.http.meta.HeaderName;
|
||||||
@ -65,18 +64,7 @@ public class JdkClientEngine extends AbstractClientEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response send(final Request message) {
|
public JdkHttpResponse send(final Request message) {
|
||||||
return send(message, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送请求
|
|
||||||
*
|
|
||||||
* @param message 请求消息
|
|
||||||
* @param isAsync 是否异步,异步不会立即读取响应内容
|
|
||||||
* @return {@link Response}
|
|
||||||
*/
|
|
||||||
public JdkHttpResponse send(final Request message, final boolean isAsync) {
|
|
||||||
final JdkHttpConnection conn = buildConn(message);
|
final JdkHttpConnection conn = buildConn(message);
|
||||||
try {
|
try {
|
||||||
doSend(conn, message);
|
doSend(conn, message);
|
||||||
@ -86,7 +74,7 @@ public class JdkClientEngine extends AbstractClientEngine {
|
|||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sendRedirectIfPossible(conn, message, isAsync);
|
return sendRedirectIfPossible(conn, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -173,11 +161,10 @@ public class JdkClientEngine extends AbstractClientEngine {
|
|||||||
/**
|
/**
|
||||||
* 调用转发,如果需要转发返回转发结果,否则返回{@code null}
|
* 调用转发,如果需要转发返回转发结果,否则返回{@code null}
|
||||||
*
|
*
|
||||||
* @param conn {@link JdkHttpConnection}}
|
* @param conn {@link JdkHttpConnection}}
|
||||||
* @param isAsync 最终请求是否异步
|
|
||||||
* @return {@link JdkHttpResponse},无转发返回 {@code null}
|
* @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) {
|
if (message.maxRedirects() > 0) {
|
||||||
final int code;
|
final int code;
|
||||||
@ -203,14 +190,14 @@ public class JdkClientEngine extends AbstractClientEngine {
|
|||||||
|
|
||||||
if (conn.redirectCount < message.maxRedirects()) {
|
if (conn.redirectCount < message.maxRedirects()) {
|
||||||
conn.redirectCount++;
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,6 +21,7 @@ import org.dromara.hutool.core.io.stream.EmptyInputStream;
|
|||||||
import org.dromara.hutool.core.util.ObjUtil;
|
import org.dromara.hutool.core.util.ObjUtil;
|
||||||
import org.dromara.hutool.http.HttpException;
|
import org.dromara.hutool.http.HttpException;
|
||||||
import org.dromara.hutool.http.HttpUtil;
|
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.Response;
|
||||||
import org.dromara.hutool.http.client.body.ResponseBody;
|
import org.dromara.hutool.http.client.body.ResponseBody;
|
||||||
import org.dromara.hutool.http.meta.HeaderName;
|
import org.dromara.hutool.http.meta.HeaderName;
|
||||||
@ -42,6 +43,10 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public class JdkHttpResponse implements Response, Closeable {
|
public class JdkHttpResponse implements Response, Closeable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 持有连接对象
|
||||||
|
*/
|
||||||
|
protected JdkHttpConnection httpConnection;
|
||||||
/**
|
/**
|
||||||
* 请求时的默认编码
|
* 请求时的默认编码
|
||||||
*/
|
*/
|
||||||
@ -50,26 +55,14 @@ public class JdkHttpResponse implements Response, Closeable {
|
|||||||
* Cookie管理器
|
* Cookie管理器
|
||||||
*/
|
*/
|
||||||
private final JdkCookieManager cookieManager;
|
private final JdkCookieManager cookieManager;
|
||||||
/**
|
|
||||||
* 响应内容体,{@code null} 表示无内容
|
|
||||||
*/
|
|
||||||
private ResponseBody body;
|
|
||||||
/**
|
/**
|
||||||
* 响应头
|
* 响应头
|
||||||
*/
|
*/
|
||||||
private Map<String, List<String>> headers;
|
private Map<String, List<String>> headers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否忽略响应读取时可能的EOF异常。<br>
|
* 响应内容体,{@code null} 表示无内容
|
||||||
* 在Http协议中,对于Transfer-Encoding: Chunked在正常情况下末尾会写入一个Length为0的的chunk标识完整结束。<br>
|
|
||||||
* 如果服务端未遵循这个规范或响应没有正常结束,会报EOF异常,此选项用于是否忽略这个异常。
|
|
||||||
*/
|
*/
|
||||||
private final boolean ignoreEOFError;
|
private ResponseBody body;
|
||||||
|
|
||||||
/**
|
|
||||||
* 持有连接对象
|
|
||||||
*/
|
|
||||||
protected JdkHttpConnection httpConnection;
|
|
||||||
/**
|
/**
|
||||||
* 响应状态码
|
* 响应状态码
|
||||||
*/
|
*/
|
||||||
@ -80,22 +73,15 @@ public class JdkHttpResponse implements Response, Closeable {
|
|||||||
*
|
*
|
||||||
* @param httpConnection {@link JdkHttpConnection}
|
* @param httpConnection {@link JdkHttpConnection}
|
||||||
* @param cookieManager Cookie管理器
|
* @param cookieManager Cookie管理器
|
||||||
* @param ignoreEOFError 是否忽略响应读取时可能的EOF异常
|
* @param message 请求消息
|
||||||
* @param requestCharset 编码,从请求编码中获取默认编码
|
|
||||||
* @param isAsync 是否异步
|
|
||||||
* @param isIgnoreBody 是否忽略读取响应体
|
|
||||||
*/
|
*/
|
||||||
protected JdkHttpResponse(final JdkHttpConnection httpConnection,
|
protected JdkHttpResponse(final JdkHttpConnection httpConnection,
|
||||||
final JdkCookieManager cookieManager,
|
final JdkCookieManager cookieManager,
|
||||||
final boolean ignoreEOFError,
|
final Request message) {
|
||||||
final Charset requestCharset,
|
|
||||||
final boolean isAsync,
|
|
||||||
final boolean isIgnoreBody) {
|
|
||||||
this.httpConnection = httpConnection;
|
this.httpConnection = httpConnection;
|
||||||
this.cookieManager = cookieManager;
|
this.cookieManager = cookieManager;
|
||||||
this.ignoreEOFError = ignoreEOFError;
|
this.requestCharset = message.charset();
|
||||||
this.requestCharset = requestCharset;
|
init(message.method().isIgnoreBody());
|
||||||
init(isAsync, isIgnoreBody);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,18 +114,13 @@ public class JdkHttpResponse implements Response, Closeable {
|
|||||||
return ObjUtil.defaultIfNull(Response.super.charset(), requestCharset);
|
return ObjUtil.defaultIfNull(Response.super.charset(), requestCharset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* 同步<br>
|
|
||||||
* 如果为异步状态,则暂时不读取服务器中响应的内容,而是持有Http链接的{@link InputStream}。<br>
|
|
||||||
* 当调用此方法时,异步状态转为同步状态,此时从Http链接流中读取body内容并暂存在内容中。如果已经是同步状态,则不进行任何操作。
|
|
||||||
*
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public JdkHttpResponse sync() {
|
public JdkHttpResponse sync() {
|
||||||
if (null != this.body) {
|
if (null != this.body) {
|
||||||
this.body.sync();
|
this.body.sync();
|
||||||
}
|
}
|
||||||
close();
|
// 关闭连接
|
||||||
|
this.httpConnection.closeQuietly();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,9 +231,10 @@ public class JdkHttpResponse implements Response, Closeable {
|
|||||||
* 3、持有Http流,并不关闭流
|
* 3、持有Http流,并不关闭流
|
||||||
* </pre>
|
* </pre>
|
||||||
*
|
*
|
||||||
|
* @param isIgnoreBody 是否忽略消息体
|
||||||
* @throws HttpException IO异常
|
* @throws HttpException IO异常
|
||||||
*/
|
*/
|
||||||
private void init(final boolean isAsync, final boolean isIgnoreBody) throws HttpException {
|
private void init(final boolean isIgnoreBody) throws HttpException {
|
||||||
// 获取响应状态码
|
// 获取响应状态码
|
||||||
try {
|
try {
|
||||||
this.status = httpConnection.getCode();
|
this.status = httpConnection.getCode();
|
||||||
@ -272,13 +254,13 @@ public class JdkHttpResponse implements Response, Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 存储服务端设置的Cookie信息
|
// 存储服务端设置的Cookie信息
|
||||||
if(null != this.cookieManager){
|
if (null != this.cookieManager) {
|
||||||
this.cookieManager.saveFromResponse(this.httpConnection, this.headers);
|
this.cookieManager.saveFromResponse(this.httpConnection, this.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取响应内容流
|
// 获取响应内容流
|
||||||
if (!isIgnoreBody) {
|
if (!isIgnoreBody) {
|
||||||
this.body = new ResponseBody(this, new JdkHttpInputStream(this), isAsync, this.ignoreEOFError);
|
this.body = new ResponseBody(this, new JdkHttpInputStream(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ---------------------------------------------------------------- Private method end
|
// ---------------------------------------------------------------- Private method end
|
||||||
|
@ -71,7 +71,7 @@ public class OkHttpEngine extends AbstractClientEngine {
|
|||||||
throw new IORuntimeException(e);
|
throw new IORuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new OkHttpResponse(response, message.charset());
|
return new OkHttpResponse(response, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -16,19 +16,20 @@
|
|||||||
|
|
||||||
package org.dromara.hutool.http.client.engine.okhttp;
|
package org.dromara.hutool.http.client.engine.okhttp;
|
||||||
|
|
||||||
import kotlin.Pair;
|
import org.dromara.hutool.core.io.IoUtil;
|
||||||
import okhttp3.Headers;
|
|
||||||
import okhttp3.ResponseBody;
|
|
||||||
import org.dromara.hutool.core.io.stream.EmptyInputStream;
|
import org.dromara.hutool.core.io.stream.EmptyInputStream;
|
||||||
import org.dromara.hutool.core.util.ObjUtil;
|
import org.dromara.hutool.core.util.ObjUtil;
|
||||||
import org.dromara.hutool.http.GlobalCompressStreamRegister;
|
import org.dromara.hutool.http.GlobalCompressStreamRegister;
|
||||||
import org.dromara.hutool.http.HttpUtil;
|
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.Response;
|
||||||
|
import org.dromara.hutool.http.client.body.ResponseBody;
|
||||||
import org.dromara.hutool.http.meta.HeaderName;
|
import org.dromara.hutool.http.meta.HeaderName;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OkHttp3的{@link okhttp3.Response} 响应包装
|
* OkHttp3的{@link okhttp3.Response} 响应包装
|
||||||
@ -42,14 +43,16 @@ public class OkHttpResponse implements Response {
|
|||||||
* 请求时的默认编码
|
* 请求时的默认编码
|
||||||
*/
|
*/
|
||||||
private final Charset requestCharset;
|
private final Charset requestCharset;
|
||||||
|
private final ResponseBody body;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param rawRes {@link okhttp3.Response}
|
* @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.rawRes = rawRes;
|
||||||
this.requestCharset = requestCharset;
|
this.requestCharset = message.charset();
|
||||||
|
this.body = message.method().isIgnoreBody() ? null : new ResponseBody(this, bodyStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -74,7 +77,7 @@ public class OkHttpResponse implements Response {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream bodyStream() {
|
public InputStream bodyStream() {
|
||||||
final ResponseBody body = rawRes.body();
|
final okhttp3.ResponseBody body = rawRes.body();
|
||||||
if(null == body){
|
if(null == body){
|
||||||
return EmptyInputStream.INSTANCE;
|
return EmptyInputStream.INSTANCE;
|
||||||
}
|
}
|
||||||
@ -84,10 +87,22 @@ public class OkHttpResponse implements Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public OkHttpResponse sync() {
|
||||||
if(null != this.rawRes){
|
if (null != this.body) {
|
||||||
rawRes.close();
|
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
|
@Override
|
||||||
|
@ -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/");
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user