mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
fix redirect bug
This commit is contained in:
parent
c2ea26e40d
commit
9cc75d5e34
@ -56,10 +56,6 @@ public class ClientConfig {
|
||||
* 代理
|
||||
*/
|
||||
private ProxyInfo proxy;
|
||||
/**
|
||||
* 是否遇到响应状态码3xx时自动重定向请求
|
||||
*/
|
||||
private boolean followRedirects;
|
||||
/**
|
||||
* 是否使用引擎默认的Cookie管理器,默认为true<br>
|
||||
* 默认情况下每个客户端维护一个自己的Cookie管理器,这个管理器用于在多次请求中记录并自动附带Cookie信息<br>
|
||||
@ -215,28 +211,6 @@ public class ClientConfig {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否遇到响应状态码3xx时自动重定向请求<br>
|
||||
* 注意:当打开客户端级别的自动重定向,则{@link Request#maxRedirects()}无效
|
||||
*
|
||||
* @return 是否遇到响应状态码3xx时自动重定向请求
|
||||
*/
|
||||
public boolean isFollowRedirects() {
|
||||
return followRedirects;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否遇到响应状态码3xx时自动重定向请求<br>
|
||||
* 注意:当打开客户端级别的自动重定向,则{@link Request#maxRedirects()}无效
|
||||
*
|
||||
* @param followRedirects 是否遇到响应状态码3xx时自动重定向请求
|
||||
* @return this
|
||||
*/
|
||||
public ClientConfig setFollowRedirects(final boolean followRedirects) {
|
||||
this.followRedirects = followRedirects;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否使用引擎默认的Cookie管理器,默认为true<br>
|
||||
* 默认情况下每个客户端维护一个自己的Cookie管理器,这个管理器用于在多次请求中记录并自动附带Cookie信息<br>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
|
||||
* 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.
|
||||
@ -16,155 +16,189 @@
|
||||
|
||||
package org.dromara.hutool.http.client;
|
||||
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.io.StreamProgress;
|
||||
import org.dromara.hutool.core.io.stream.FastByteArrayOutputStream;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.http.HttpException;
|
||||
import org.dromara.hutool.http.client.body.ResponseBody;
|
||||
import org.dromara.hutool.http.client.engine.ClientEngine;
|
||||
import org.dromara.hutool.http.client.engine.ClientEngineFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 下载封装,下载统一使用{@code GET}请求,默认支持30x跳转
|
||||
* HTTP下载器,两种使用方式:<br>
|
||||
* 1. 一次性使用:
|
||||
* <pre>{@code HttpDownloader.of(url).downloadFile(file)}</pre>
|
||||
* 2. 多次下载复用:
|
||||
* <pre>{@code
|
||||
* HttpDownloader downloader = HttpDownloader.of(url).setCloseEngine(false);
|
||||
* downloader.downloadFile(file);
|
||||
* downloader.downloadFile(file2);
|
||||
* downloader.close();
|
||||
* }</pre>
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.6.4
|
||||
* @since 6.0.0
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public class HttpDownloader {
|
||||
|
||||
/**
|
||||
* 下载远程文本
|
||||
* 创建下载器
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param customCharset 自定义的字符集,可以使用{@code CharsetUtil#charset} 方法转换
|
||||
* @param streamPress 进度条 {@link StreamProgress}
|
||||
* @return 文本
|
||||
* @param url 请求地址
|
||||
* @return 下载器
|
||||
*/
|
||||
public static String downloadString(final String url, final Charset customCharset, final StreamProgress streamPress) {
|
||||
final FastByteArrayOutputStream out = new FastByteArrayOutputStream();
|
||||
download(url, out, true, streamPress);
|
||||
return null == customCharset ? out.toString() : out.toString(customCharset);
|
||||
public static HttpDownloader of(final String url) {
|
||||
return new HttpDownloader(url);
|
||||
}
|
||||
|
||||
private final Request request;
|
||||
private ClientConfig config;
|
||||
private ClientEngine engine;
|
||||
private StreamProgress streamProgress;
|
||||
private boolean closeEngine = true;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param url 请求地址
|
||||
*/
|
||||
public HttpDownloader(final String url) {
|
||||
this.request = Request.of(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件数据,支持30x跳转
|
||||
* 设置请求头
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @return 文件数据
|
||||
* @param headers 请求头
|
||||
* @return this
|
||||
*/
|
||||
public static byte[] downloadBytes(final String url) {
|
||||
return downloadBytes(url, 0);
|
||||
public HttpDownloader header(final Map<String, String> headers) {
|
||||
this.request.header(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件数据,支持30x跳转
|
||||
* 设置配置
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param timeout 超时毫秒数
|
||||
* @return 文件数据
|
||||
* @since 5.8.28
|
||||
* @param config 配置
|
||||
* @return this
|
||||
*/
|
||||
public static byte[] downloadBytes(final String url, final int timeout) {
|
||||
return requestDownload(url, timeout).bodyBytes();
|
||||
public HttpDownloader setConfig(final ClientConfig config) {
|
||||
this.config = config;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
* 设置超时
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @return 文件
|
||||
* @param milliseconds 超时毫秒数
|
||||
* @return this
|
||||
*/
|
||||
public static File downloadFile(final String url, final File targetFileOrDir) {
|
||||
return downloadFile(url, targetFileOrDir, -1);
|
||||
public HttpDownloader setTimeout(final int milliseconds) {
|
||||
if (null == this.config) {
|
||||
this.config = ClientConfig.of();
|
||||
}
|
||||
this.config.setTimeout(milliseconds);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
* 设置引擎,用于自定义引擎
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||
* @return 文件
|
||||
* @param engine 引擎
|
||||
* @return this
|
||||
*/
|
||||
public static File downloadFile(final String url, final File targetFileOrDir, final int timeout) {
|
||||
Assert.notNull(targetFileOrDir, "[targetFileOrDir] is null !");
|
||||
return downloadFile(url, targetFileOrDir, timeout, null);
|
||||
public HttpDownloader setEngine(final ClientEngine engine) {
|
||||
this.engine = engine;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
* 设置进度条
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||
* @param streamProgress 进度条
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadFile(final String url, final File targetFileOrDir, final int timeout, final StreamProgress streamProgress) {
|
||||
Assert.notNull(targetFileOrDir, "[targetFileOrDir] is null !");
|
||||
return requestDownload(url, timeout).body().write(targetFileOrDir, streamProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件-避免未完成的文件<br>
|
||||
* 来自:https://gitee.com/dromara/hutool/pulls/407<br>
|
||||
* 此方法原理是先在目标文件同级目录下创建临时文件,下载之,等下载完毕后重命名,避免因下载错误导致的文件不完整。
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param tempFileSuffix 临时文件后缀,默认".temp"
|
||||
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||
* @param streamProgress 进度条
|
||||
* @return 文件
|
||||
* @since 5.7.12
|
||||
*/
|
||||
public static File downloadFile(final String url, final File targetFileOrDir, final String tempFileSuffix, final int timeout, final StreamProgress streamProgress) {
|
||||
Assert.notNull(targetFileOrDir, "[targetFileOrDir] is null !");
|
||||
return requestDownload(url, timeout).body().write(targetFileOrDir, tempFileSuffix, streamProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param out 将下载内容写到输出流中 {@link OutputStream}
|
||||
* @param isCloseOut 是否关闭输出流
|
||||
* @param streamProgress 进度条
|
||||
* @return 文件大小
|
||||
* @return this
|
||||
*/
|
||||
public static long download(final String url, final OutputStream out, final boolean isCloseOut, final StreamProgress streamProgress) {
|
||||
Assert.notNull(out, "[out] is null !");
|
||||
return requestDownload(url, -1).body().write(out, isCloseOut, streamProgress);
|
||||
public HttpDownloader setStreamProgress(final StreamProgress streamProgress) {
|
||||
this.streamProgress = streamProgress;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求下载文件
|
||||
* 设置是否关闭引擎,默认为true,即自动关闭引擎
|
||||
*
|
||||
* @param url 请求下载文件地址
|
||||
* @param timeout 超时时间
|
||||
* @return HttpResponse
|
||||
* @since 5.4.1
|
||||
* @param closeEngine 是否关闭引擎
|
||||
* @return this
|
||||
*/
|
||||
private static Response requestDownload(final String url, final int timeout) {
|
||||
Assert.notBlank(url, "[url] is blank !");
|
||||
public HttpDownloader setCloseEngine(final boolean closeEngine) {
|
||||
this.closeEngine = closeEngine;
|
||||
return this;
|
||||
}
|
||||
|
||||
final ClientConfig config = ClientConfig.of();
|
||||
if(timeout > 0){
|
||||
config.setTimeout(timeout);
|
||||
/**
|
||||
* 下载文件
|
||||
*
|
||||
* @param targetFileOrDir 目标文件或目录,当为目录时,自动使用文件名作为下载文件名
|
||||
* @return 下载文件
|
||||
*/
|
||||
public File downloadFile(final File targetFileOrDir) {
|
||||
return downloadFile(targetFileOrDir, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*
|
||||
* @param targetFileOrDir 目标文件或目录,当为目录时,自动使用文件名作为下载文件名
|
||||
* @param tempFileSuffix 临时文件后缀
|
||||
* @return 下载文件
|
||||
*/
|
||||
public File downloadFile(final File targetFileOrDir, final String tempFileSuffix) {
|
||||
try (final ResponseBody body = send()) {
|
||||
return body.write(targetFileOrDir, tempFileSuffix, this.streamProgress);
|
||||
} catch (final IOException e) {
|
||||
throw new HttpException(e);
|
||||
} finally {
|
||||
if (this.closeEngine) {
|
||||
IoUtil.closeQuietly(this.engine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Response response = ClientEngineFactory.getEngine()
|
||||
.init(config)
|
||||
.send(Request.of(url));
|
||||
|
||||
if (response.isOk()) {
|
||||
return response;
|
||||
/**
|
||||
* 下载文件
|
||||
*
|
||||
* @param out 输出流
|
||||
* @param isCloseOut 是否关闭输出流,true关闭
|
||||
* @return 下载的字节数
|
||||
*/
|
||||
public long download(final OutputStream out, final boolean isCloseOut) {
|
||||
try (final ResponseBody body = send()) {
|
||||
return body.write(out, isCloseOut, this.streamProgress);
|
||||
} catch (final IOException e) {
|
||||
throw new HttpException(e);
|
||||
} finally {
|
||||
if (this.closeEngine) {
|
||||
IoUtil.closeQuietly(this.engine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new HttpException("Server response error with status code: [{}]", response.getStatus());
|
||||
/**
|
||||
* 发送请求,获取响应
|
||||
*
|
||||
* @return 响应
|
||||
*/
|
||||
private ResponseBody send() {
|
||||
if (null == this.engine) {
|
||||
this.engine = ClientEngineFactory.createEngine();
|
||||
}
|
||||
if (null != this.config) {
|
||||
this.engine.init(this.config);
|
||||
}
|
||||
return engine.send(this.request).body();
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +138,11 @@ public class Request implements HeaderOperation<Request> {
|
||||
* 是否是REST请求模式,REST模式运行GET请求附带body
|
||||
*/
|
||||
private boolean isRest;
|
||||
/**
|
||||
* 重定向计数器,内部使用<br>
|
||||
* 单次请求时,重定向次数内部自增
|
||||
*/
|
||||
private int redirectCount;
|
||||
|
||||
/**
|
||||
* 默认构造
|
||||
@ -399,7 +404,7 @@ public class Request implements HeaderOperation<Request> {
|
||||
|
||||
/**
|
||||
* 获取最大重定向请求次数<br>
|
||||
* 注意:当{@link ClientConfig#isFollowRedirects()}为{@code true}时,此参数无效
|
||||
* 如果次数小于1则表示不重定向,大于等于1表示打开重定向。
|
||||
*
|
||||
* @return 最大重定向请求次数
|
||||
*/
|
||||
@ -410,7 +415,6 @@ public class Request implements HeaderOperation<Request> {
|
||||
/**
|
||||
* 设置最大重定向次数<br>
|
||||
* 如果次数小于1则表示不重定向,大于等于1表示打开重定向<br>
|
||||
* 注意:当{@link ClientConfig#isFollowRedirects()}为{@code true}时,此参数无效
|
||||
*
|
||||
* @param maxRedirects 最大重定向次数
|
||||
* @return this
|
||||
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 请求上下文,用于在多次请求时保存状态信息<br>
|
||||
* 非线程安全。
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public class RequestContext {
|
||||
|
||||
private Request request;
|
||||
private int redirectCount;
|
||||
|
||||
/**
|
||||
* 获取请求
|
||||
*
|
||||
* @return 请求
|
||||
*/
|
||||
public Request getRequest() {
|
||||
return request;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置请求
|
||||
*
|
||||
* @param request 请求
|
||||
* @return this
|
||||
*/
|
||||
public RequestContext setRequest(final Request request) {
|
||||
this.request = request;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许重定向,如果允许,则重定向计数器+1
|
||||
*
|
||||
* @return 是否允许重定向
|
||||
*/
|
||||
public boolean canRedirect(){
|
||||
final int maxRedirects = request.maxRedirects();
|
||||
if(maxRedirects > 0 && redirectCount < maxRedirects){
|
||||
redirectCount++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -134,13 +134,6 @@ public class HttpClient4Engine extends AbstractClientEngine {
|
||||
// 设置默认头信息
|
||||
clientBuilder.setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()));
|
||||
|
||||
// 重定向
|
||||
if (config.isFollowRedirects()) {
|
||||
clientBuilder.setRedirectStrategy(LaxRedirectStrategy.INSTANCE);
|
||||
} else {
|
||||
clientBuilder.disableRedirectHandling();
|
||||
}
|
||||
|
||||
// 设置代理
|
||||
setProxy(clientBuilder, config);
|
||||
|
||||
|
@ -21,7 +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.impl.DefaultRedirectStrategy;
|
||||
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
|
||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
|
||||
@ -122,7 +121,7 @@ public class HttpClient5Engine extends AbstractClientEngine {
|
||||
clientBuilder.setConnectionManager(buildConnectionManager(config));
|
||||
|
||||
// 实例级别默认请求配置
|
||||
clientBuilder.setDefaultRequestConfig(buildRequestConfig(config));
|
||||
clientBuilder.setDefaultRequestConfig(buildDefaultRequestConfig(config));
|
||||
|
||||
// 缓存
|
||||
if (config.isDisableCache()) {
|
||||
@ -132,13 +131,6 @@ public class HttpClient5Engine extends AbstractClientEngine {
|
||||
// 设置默认头信息
|
||||
clientBuilder.setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()));
|
||||
|
||||
// 重定向
|
||||
if (config.isFollowRedirects()) {
|
||||
clientBuilder.setRedirectStrategy(DefaultRedirectStrategy.INSTANCE);
|
||||
} else {
|
||||
clientBuilder.disableRedirectHandling();
|
||||
}
|
||||
|
||||
// 设置代理
|
||||
setProxy(clientBuilder, config);
|
||||
|
||||
@ -261,12 +253,12 @@ public class HttpClient5Engine extends AbstractClientEngine {
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建请求配置,包括连接请求超时和响应(读取)超时
|
||||
* 构建默认请求配置,包括连接请求超时和响应(读取)超时
|
||||
*
|
||||
* @param config {@link ClientConfig}
|
||||
* @return {@link RequestConfig}
|
||||
*/
|
||||
private static RequestConfig buildRequestConfig(final ClientConfig config) {
|
||||
private static RequestConfig buildDefaultRequestConfig(final ClientConfig config) {
|
||||
final int connectionTimeout = config.getConnectionTimeout();
|
||||
|
||||
// 请求配置
|
||||
|
@ -27,6 +27,7 @@ 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.RequestContext;
|
||||
import org.dromara.hutool.http.client.body.HttpBody;
|
||||
import org.dromara.hutool.http.client.engine.AbstractClientEngine;
|
||||
import org.dromara.hutool.http.meta.HeaderName;
|
||||
@ -69,16 +70,7 @@ public class JdkClientEngine extends AbstractClientEngine {
|
||||
|
||||
@Override
|
||||
public JdkHttpResponse send(final Request message) {
|
||||
final JdkHttpConnection conn = buildConn(message);
|
||||
try {
|
||||
doSend(conn, message);
|
||||
} catch (final IOException e) {
|
||||
// 出错后关闭连接
|
||||
IoUtil.closeQuietly(conn);
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
return sendRedirectIfPossible(conn, message);
|
||||
return doSend(new RequestContext().setRequest(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -102,6 +94,20 @@ public class JdkClientEngine extends AbstractClientEngine {
|
||||
this.cookieManager = (null != this.config && this.config.isUseCookieManager()) ? new JdkCookieManager() : new JdkCookieManager(null);
|
||||
}
|
||||
|
||||
private JdkHttpResponse doSend(final RequestContext context) {
|
||||
final Request message = context.getRequest();
|
||||
final JdkHttpConnection conn = buildConn(message);
|
||||
try {
|
||||
doSend(conn, message);
|
||||
} catch (final IOException e) {
|
||||
// 出错后关闭连接
|
||||
IoUtil.closeQuietly(conn);
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
|
||||
return sendRedirectIfPossible(conn, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行发送
|
||||
*
|
||||
@ -132,7 +138,7 @@ public class JdkClientEngine extends AbstractClientEngine {
|
||||
final URL url = message.handledUrl().toURL();
|
||||
Proxy proxy = null;
|
||||
final ProxyInfo proxyInfo = config.getProxy();
|
||||
if(null != proxyInfo){
|
||||
if (null != proxyInfo) {
|
||||
proxy = proxyInfo.selectFirst(UrlUtil.toURI(url));
|
||||
}
|
||||
final JdkHttpConnection conn = JdkHttpConnection
|
||||
@ -141,8 +147,8 @@ public class JdkClientEngine extends AbstractClientEngine {
|
||||
.setReadTimeout(config.getReadTimeout())
|
||||
.setMethod(message.method())//
|
||||
.setSSLInfo(config.getSslInfo())
|
||||
// 如果客户端设置自动重定向,则Request中maxRedirectCount无效
|
||||
.setInstanceFollowRedirects(config.isFollowRedirects())
|
||||
// 关闭自动重定向,手动处理重定向
|
||||
.setInstanceFollowRedirects(false)
|
||||
.setDisableCache(config.isDisableCache())
|
||||
// 覆盖默认Header
|
||||
.header(message.headers(), true);
|
||||
@ -171,10 +177,12 @@ public class JdkClientEngine extends AbstractClientEngine {
|
||||
/**
|
||||
* 调用转发,如果需要转发返回转发结果,否则返回{@code null}
|
||||
*
|
||||
* @param conn {@link JdkHttpConnection}}
|
||||
* @param conn {@link JdkHttpConnection}}
|
||||
* @param context 请求上下文
|
||||
* @return {@link JdkHttpResponse},无转发返回 {@code null}
|
||||
*/
|
||||
private JdkHttpResponse sendRedirectIfPossible(final JdkHttpConnection conn, final Request message) {
|
||||
private JdkHttpResponse sendRedirectIfPossible(final JdkHttpConnection conn, final RequestContext context) {
|
||||
final Request message = context.getRequest();
|
||||
// 手动实现重定向
|
||||
if (message.maxRedirects() > 0) {
|
||||
final int code;
|
||||
@ -198,16 +206,15 @@ public class JdkClientEngine extends AbstractClientEngine {
|
||||
message.method(Method.GET);
|
||||
}
|
||||
|
||||
if (conn.redirectCount < message.maxRedirects()) {
|
||||
conn.redirectCount++;
|
||||
return send(message);
|
||||
if (context.canRedirect()) {
|
||||
return doSend(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 最终页面
|
||||
return new JdkHttpResponse(conn, this.cookieManager, message);
|
||||
return new JdkHttpResponse(conn, this.cookieManager, context.getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,10 +47,6 @@ public class JdkHttpConnection implements HeaderOperation<JdkHttpConnection>, Cl
|
||||
private final URL url;
|
||||
private final Proxy proxy;
|
||||
private final HttpURLConnection conn;
|
||||
/**
|
||||
* 重定向次数计数器,内部使用
|
||||
*/
|
||||
protected int redirectCount;
|
||||
|
||||
/**
|
||||
* 创建HttpConnection
|
||||
|
@ -130,8 +130,8 @@ public class OkHttpEngine extends AbstractClientEngine {
|
||||
builder.connectionPool(((OkHttpClientConfig) config).getConnectionPool());
|
||||
}
|
||||
|
||||
// 重定向
|
||||
builder.followRedirects(config.isFollowRedirects());
|
||||
// 关闭自动重定向,手动实现
|
||||
builder.followRedirects(false);
|
||||
|
||||
// 设置代理
|
||||
setProxy(builder, config);
|
||||
@ -168,7 +168,6 @@ public class OkHttpEngine extends AbstractClientEngine {
|
||||
|
||||
// 填充头信息
|
||||
message.headers().forEach((key, values) -> values.forEach(value -> builder.addHeader(key, value)));
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
@ -16,23 +16,17 @@
|
||||
|
||||
package org.dromara.hutool.http.client;
|
||||
|
||||
import org.dromara.hutool.core.codec.binary.Base64;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.core.io.StreamProgress;
|
||||
import org.dromara.hutool.core.io.file.FileUtil;
|
||||
import org.dromara.hutool.core.lang.Console;
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import org.dromara.hutool.http.HttpGlobalConfig;
|
||||
import org.dromara.hutool.http.client.engine.ClientEngineFactory;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@ -46,31 +40,22 @@ public class DownloadTest {
|
||||
@Disabled
|
||||
public void downloadPicTest() {
|
||||
final String url = "http://wx.qlogo.cn/mmopen/vKhlFcibVUtNBVDjcIowlg0X8aJfHXrTNCEFBukWVH9ta99pfEN88lU39MKspCUCOP3yrFBH3y2NbV7sYtIIlon8XxLwAEqv2/0";
|
||||
HttpDownloader.downloadFile(url, new File("e:/shape/t3.jpg"));
|
||||
HttpDownloader.of(url).downloadFile(new File("d:/test/download/t3.jpg"));
|
||||
Console.log("ok");
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Test
|
||||
@Disabled
|
||||
public void downloadSizeTest() {
|
||||
final String url = "https://res.t-io.org/im/upload/img/67/8948/1119501/88097554/74541310922/85/231910/366466 - 副本.jpg";
|
||||
ClientEngineFactory.getEngine().send(Request.of(url)).body().write("e:/shape/366466.jpg");
|
||||
//HttpRequest.get(url).setSSLProtocol("TLSv1.2").executeAsync().body().write("e:/shape/366466.jpg");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void downloadTest1() {
|
||||
final File size = HttpDownloader.downloadFile("http://explorer.bbfriend.com/crossdomain.xml", new File("e:/temp/"));
|
||||
System.out.println("Download size: " + size);
|
||||
final File size = HttpDownloader.of("http://explorer.bbfriend.com/crossdomain.xml").downloadFile(new File("d:test/download/temp/"));
|
||||
Console.log("Download size: " + size);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void downloadTest() {
|
||||
// 带进度显示的文件下载
|
||||
HttpDownloader.downloadFile("http://mirrors.sohu.com/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2009.iso", FileUtil.file("d:/"), -1, new StreamProgress() {
|
||||
HttpDownloader.of("http://mirrors.sohu.com/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2009.iso").setStreamProgress(new StreamProgress() {
|
||||
|
||||
final long time = System.currentTimeMillis();
|
||||
|
||||
@ -89,13 +74,14 @@ public class DownloadTest {
|
||||
public void finish() {
|
||||
Console.log("下载完成!");
|
||||
}
|
||||
});
|
||||
}).downloadFile(FileUtil.file("d:/test/"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void downloadFileFromUrlTest1() {
|
||||
final File file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", new File("d:/download/temp"));
|
||||
final File file = HttpDownloader.of("http://groovy-lang.org/changelogs/changelog-3.0.5.html")
|
||||
.downloadFile(new File("d:/download/temp"));
|
||||
Assertions.assertNotNull(file);
|
||||
Assertions.assertTrue(file.isFile());
|
||||
Assertions.assertTrue(file.length() > 0);
|
||||
@ -106,7 +92,8 @@ public class DownloadTest {
|
||||
public void downloadFileFromUrlTest2() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpDownloader.downloadFile("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar", FileUtil.file("d:/download/temp"), 1, new StreamProgress() {
|
||||
file = HttpDownloader.of("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar")
|
||||
.setStreamProgress(new StreamProgress() {
|
||||
@Override
|
||||
public void start() {
|
||||
System.out.println("start");
|
||||
@ -121,15 +108,13 @@ public class DownloadTest {
|
||||
public void finish() {
|
||||
System.out.println("end");
|
||||
}
|
||||
});
|
||||
}).downloadFile(FileUtil.file("d:/download/temp"));
|
||||
|
||||
Assertions.assertNotNull(file);
|
||||
Assertions.assertTrue(file.exists());
|
||||
Assertions.assertTrue(file.isFile());
|
||||
Assertions.assertTrue(file.length() > 0);
|
||||
Assertions.assertFalse(file.getName().isEmpty());
|
||||
} catch (final Exception e) {
|
||||
Assertions.assertInstanceOf(IORuntimeException.class, e);
|
||||
} finally {
|
||||
FileUtil.del(file);
|
||||
}
|
||||
@ -140,7 +125,7 @@ public class DownloadTest {
|
||||
public void downloadFileFromUrlTest3() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpDownloader.downloadFile("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar", FileUtil.file("d:/download/temp"), -1, new StreamProgress() {
|
||||
file = HttpDownloader.of("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar").setStreamProgress(new StreamProgress() {
|
||||
@Override
|
||||
public void start() {
|
||||
System.out.println("start");
|
||||
@ -155,7 +140,7 @@ public class DownloadTest {
|
||||
public void finish() {
|
||||
System.out.println("end");
|
||||
}
|
||||
});
|
||||
}).downloadFile(FileUtil.file("d:/download/temp"));
|
||||
|
||||
Assertions.assertNotNull(file);
|
||||
Assertions.assertTrue(file.exists());
|
||||
@ -172,15 +157,14 @@ public class DownloadTest {
|
||||
public void downloadFileFromUrlTest4() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"), 1);
|
||||
file = HttpDownloader.of("http://groovy-lang.org/changelogs/changelog-3.0.5.html")
|
||||
.downloadFile(FileUtil.file("d:/download/temp"));
|
||||
|
||||
Assertions.assertNotNull(file);
|
||||
Assertions.assertTrue(file.exists());
|
||||
Assertions.assertTrue(file.isFile());
|
||||
Assertions.assertTrue(file.length() > 0);
|
||||
Assertions.assertFalse(file.getName().isEmpty());
|
||||
} catch (final Exception e) {
|
||||
Assertions.assertInstanceOf(IORuntimeException.class, e);
|
||||
} finally {
|
||||
FileUtil.del(file);
|
||||
}
|
||||
@ -192,7 +176,8 @@ public class DownloadTest {
|
||||
public void downloadFileFromUrlTest5() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp", UUID.randomUUID().toString()));
|
||||
file = HttpDownloader.of("http://groovy-lang.org/changelogs/changelog-3.0.5.html")
|
||||
.downloadFile(FileUtil.file("d:/download/temp", UUID.randomUUID().toString()));
|
||||
|
||||
Assertions.assertNotNull(file);
|
||||
Assertions.assertTrue(file.exists());
|
||||
@ -204,7 +189,8 @@ public class DownloadTest {
|
||||
|
||||
File file1 = null;
|
||||
try {
|
||||
file1 = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"));
|
||||
file1 = HttpDownloader.of("http://groovy-lang.org/changelogs/changelog-3.0.5.html")
|
||||
.downloadFile(FileUtil.file("d:/download/temp"));
|
||||
|
||||
Assertions.assertNotNull(file1);
|
||||
Assertions.assertTrue(file1.exists());
|
||||
@ -220,36 +206,10 @@ public class DownloadTest {
|
||||
public void downloadTeamViewerTest() throws IOException {
|
||||
// 此URL有3次重定向, 需要请求4次
|
||||
final String url = "https://download.teamviewer.com/download/TeamViewer_Setup_x64.exe";
|
||||
HttpGlobalConfig.setMaxRedirects(20);
|
||||
final Path temp = Files.createTempFile("tmp", ".exe");
|
||||
final File file = HttpDownloader.downloadFile(url, temp.toFile());
|
||||
Console.log(file.length());
|
||||
}
|
||||
HttpGlobalConfig.setMaxRedirects(2);
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void downloadToStreamTest() {
|
||||
String url2 = "http://storage.chancecloud.com.cn/20200413_%E7%B2%A4B12313_386.pdf";
|
||||
final ByteArrayOutputStream os2 = new ByteArrayOutputStream();
|
||||
HttpDownloader.download(url2, os2, false, null);
|
||||
|
||||
url2 = "http://storage.chancecloud.com.cn/20200413_粤B12313_386.pdf";
|
||||
HttpDownloader.download(url2, os2, false, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void downloadStringTest() {
|
||||
final String url = "https://www.baidu.com";
|
||||
// 从远程直接读取字符串,需要自定义编码,直接调用JDK方法
|
||||
final String content2 = HttpDownloader.downloadString(url, CharsetUtil.UTF_8, null);
|
||||
Console.log(content2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Disabled
|
||||
public void gimg2Test(){
|
||||
final byte[] bytes = HttpDownloader.downloadBytes("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621996490&t=8c384c2823ea453da15a1b9cd5183eea");
|
||||
Console.log(Base64.encode(bytes));
|
||||
final Response send = Request.of(url).send(ClientEngineFactory.createEngine("jdkClient"));
|
||||
Console.log(send.getStatus());
|
||||
Console.log(send.headers());
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user