diff --git a/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java b/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java
index e7af04b7e..8d0468072 100644
--- a/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java
+++ b/hutool-core/src/main/java/cn/hutool/core/map/multi/MultiValueMap.java
@@ -21,12 +21,14 @@ import java.util.stream.Collectors;
*
当通过实例方法获得值集合时,若该集合允许修改,则对值集合的修改将会影响到其所属的{@link MultiValueMap}实例,反之亦然。
* 因此当同时遍历当前实例或者值集合时,若存在写操作,则需要注意可能引发的{@link ConcurrentModificationException}。
*
+ * @param 键类型
+ * @param 值类型
* @author huangchengxing
- * @since 6.0.0
* @see AbsCollValueMap
* @see CollectionValueMap
* @see ListValueMap
* @see SetValueMap
+ * @since 6.0.0
*/
public interface MultiValueMap extends Map> {
@@ -64,7 +66,7 @@ public interface MultiValueMap extends Map> {
* Collection coll = entry.getValues();
* for (V val : coll) {
* map.putValue(key, val)
- * }
+ * }
* }
* }
*
@@ -81,7 +83,7 @@ public interface MultiValueMap extends Map> {
* {@code
* for (V val : coll) {
* map.putValue(key, val)
- * }
+ * }
* }
*
* @param key 键
@@ -95,7 +97,7 @@ public interface MultiValueMap extends Map> {
* {@code
* for (V val : values) {
* map.putValue(key, val)
- * }
+ * }
* }
*
* @param key 键
@@ -137,7 +139,7 @@ public interface MultiValueMap extends Map> {
/**
* 将一批值从指定键下的值集合中删除
*
- * @param key 键
+ * @param key 键
* @param values 值数组
* @return 是否成功删除
*/
@@ -149,7 +151,7 @@ public interface MultiValueMap extends Map> {
/**
* 将一批值从指定键下的值集合中删除
*
- * @param key 键
+ * @param key 键
* @param values 值集合
* @return 是否成功删除
*/
@@ -236,7 +238,7 @@ public interface MultiValueMap extends Map> {
* Collection coll = entry.getValues();
* for (V val : coll) {
* consumer.accept(key, val);
- * }
+ * }
* }
* }
*
@@ -259,8 +261,8 @@ public interface MultiValueMap extends Map> {
*/
default Collection allValues() {
return values().stream()
- .flatMap(Collection::stream)
- .collect(Collectors.toList());
+ .flatMap(Collection::stream)
+ .collect(Collectors.toList());
}
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/url/URLUtil.java b/hutool-core/src/main/java/cn/hutool/core/net/url/URLUtil.java
index 69d1213a4..4527681df 100644
--- a/hutool-core/src/main/java/cn/hutool/core/net/url/URLUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/net/url/URLUtil.java
@@ -558,7 +558,6 @@ public class URLUtil {
if (StrUtil.isNotEmpty(body)) {
// 去除开头的\或者/
- //noinspection ConstantConditions
body = body.replaceAll("^[\\\\/]+", StrUtil.EMPTY);
// 替换\为/
body = body.replace("\\", "/");
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/url/UrlQueryUtil.java b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlQueryUtil.java
new file mode 100755
index 000000000..07e620ad9
--- /dev/null
+++ b/hutool-core/src/main/java/cn/hutool/core/net/url/UrlQueryUtil.java
@@ -0,0 +1,215 @@
+package cn.hutool.core.net.url;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.text.StrUtil;
+import cn.hutool.core.util.CharsetUtil;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class UrlQueryUtil {
+ /**
+ * 将Map形式的Form表单数据转换为Url参数形式,会自动url编码键和值
+ *
+ * @param paramMap 表单数据
+ * @return url参数
+ */
+ public static String toQuery(final Map paramMap) {
+ return toQuery(paramMap, CharsetUtil.UTF_8);
+ }
+
+ /**
+ * 将Map形式的Form表单数据转换为Url参数形式
+ * paramMap中如果key为空(null和"")会被忽略,如果value为null,会被做为空白符("")
+ * 会自动url编码键和值
+ * 此方法用于拼接URL中的Query部分,并不适用于POST请求中的表单
+ *
+ *
+ * key1=v1&key2=&key3=v3
+ *
+ *
+ * @param paramMap 表单数据
+ * @param charset 编码,{@code null} 表示不encode键值对
+ * @return url参数
+ * @see #toQuery(Map, Charset, boolean)
+ */
+ public static String toQuery(final Map paramMap, final Charset charset) {
+ return toQuery(paramMap, charset, false);
+ }
+
+ /**
+ * 将Map形式的Form表单数据转换为Url参数形式
+ * paramMap中如果key为空(null和"")会被忽略,如果value为null,会被做为空白符("")
+ * 会自动url编码键和值
+ *
+ *
+ * key1=v1&key2=&key3=v3
+ *
+ *
+ * @param paramMap 表单数据
+ * @param charset 编码,null表示不encode键值对
+ * @param isFormUrlEncoded 是否为x-www-form-urlencoded模式,此模式下空格会编码为'+'
+ * @return url参数
+ * @since 5.7.16
+ */
+ public static String toQuery(final Map paramMap, final Charset charset, final boolean isFormUrlEncoded) {
+ return UrlQuery.of(paramMap, isFormUrlEncoded).build(charset);
+ }
+
+ /**
+ * 对URL参数做编码,只编码键和值
+ * 提供的值可以是url附带参数,但是不能只是url
+ *
+ * 注意,此方法只能标准化整个URL,并不适合于单独编码参数值
+ *
+ * @param urlWithParams url和参数,可以包含url本身,也可以单独参数
+ * @param charset 编码
+ * @return 编码后的url和参数
+ * @since 4.0.1
+ */
+ public static String encodeQuery(final String urlWithParams, final Charset charset) {
+ if (StrUtil.isBlank(urlWithParams)) {
+ return StrUtil.EMPTY;
+ }
+
+ String urlPart = null; // url部分,不包括问号
+ String paramPart; // 参数部分
+ final int pathEndPos = urlWithParams.indexOf('?');
+ if (pathEndPos > -1) {
+ // url + 参数
+ urlPart = StrUtil.subPre(urlWithParams, pathEndPos);
+ paramPart = StrUtil.subSuf(urlWithParams, pathEndPos + 1);
+ if (StrUtil.isBlank(paramPart)) {
+ // 无参数,返回url
+ return urlPart;
+ }
+ } else if (false == StrUtil.contains(urlWithParams, '=')) {
+ // 无参数的URL
+ return urlWithParams;
+ } else {
+ // 无URL的参数
+ paramPart = urlWithParams;
+ }
+
+ paramPart = normalizeQuery(paramPart, charset);
+
+ return StrUtil.isBlank(urlPart) ? paramPart : urlPart + "?" + paramPart;
+ }
+
+ /**
+ * 标准化参数字符串,即URL中?后的部分
+ *
+ * 注意,此方法只能标准化整个URL,并不适合于单独编码参数值
+ *
+ * @param queryPart 参数字符串
+ * @param charset 编码
+ * @return 标准化的参数字符串
+ * @since 4.5.2
+ */
+ public static String normalizeQuery(final String queryPart, final Charset charset) {
+ if (StrUtil.isEmpty(queryPart)) {
+ return queryPart;
+ }
+ final StringBuilder builder = new StringBuilder(queryPart.length() + 16);
+ final int len = queryPart.length();
+ String name = null;
+ int pos = 0; // 未处理字符开始位置
+ char c; // 当前字符
+ int i; // 当前字符位置
+ for (i = 0; i < len; i++) {
+ c = queryPart.charAt(i);
+ if (c == '=') { // 键值对的分界点
+ if (null == name) {
+ // 只有=前未定义name时被当作键值分界符,否则做为普通字符
+ name = (pos == i) ? StrUtil.EMPTY : queryPart.substring(pos, i);
+ pos = i + 1;
+ }
+ } else if (c == '&') { // 参数对的分界点
+ if (pos != i) {
+ if (null == name) {
+ // 对于像&a&这类无参数值的字符串,我们将name为a的值设为""
+ name = queryPart.substring(pos, i);
+ builder.append(RFC3986.QUERY_PARAM_NAME.encode(name, charset)).append('=');
+ } else {
+ builder.append(RFC3986.QUERY_PARAM_NAME.encode(name, charset)).append('=')
+ .append(RFC3986.QUERY_PARAM_VALUE.encode(queryPart.substring(pos, i), charset)).append('&');
+ }
+ name = null;
+ }
+ pos = i + 1;
+ }
+ }
+
+ // 结尾处理
+ if (null != name) {
+ builder.append(URLEncoder.encodeQuery(name, charset)).append('=');
+ }
+ if (pos != i) {
+ if (null == name && pos > 0) {
+ builder.append('=');
+ }
+ builder.append(URLEncoder.encodeQuery(queryPart.substring(pos, i), charset));
+ }
+
+ // 以&结尾则去除之
+ final int lastIndex = builder.length() - 1;
+ if ('&' == builder.charAt(lastIndex)) {
+ builder.delete(lastIndex, builder.length());
+ }
+ return builder.toString();
+ }
+
+ /**
+ * 将URL参数解析为Map(也可以解析Post中的键值对参数)
+ *
+ * @param paramsStr 参数字符串(或者带参数的Path)
+ * @param charset 字符集
+ * @return 参数Map
+ * @since 5.2.6
+ */
+ public static Map decodeQuery(final String paramsStr, final Charset charset) {
+ final Map queryMap = UrlQuery.of(paramsStr, charset).getQueryMap();
+ if (MapUtil.isEmpty(queryMap)) {
+ return MapUtil.empty();
+ }
+ return Convert.toMap(String.class, String.class, queryMap);
+ }
+
+ /**
+ * 将URL参数解析为Map(也可以解析Post中的键值对参数)
+ *
+ * @param paramsStr 参数字符串(或者带参数的Path)
+ * @param charset 字符集
+ * @return 参数Map
+ */
+ public static Map> decodeQuery(final String paramsStr, final String charset) {
+ return decodeQueryList(paramsStr, CharsetUtil.charset(charset));
+ }
+
+ /**
+ * 将URL参数解析为Map(也可以解析Post中的键值对参数)
+ *
+ * @param paramsStr 参数字符串(或者带参数的Path)
+ * @param charset 字符集
+ * @return 参数Map
+ * @since 5.2.6
+ */
+ public static Map> decodeQueryList(final String paramsStr, final Charset charset) {
+ final Map queryMap = UrlQuery.of(paramsStr, charset).getQueryMap();
+ if (MapUtil.isEmpty(queryMap)) {
+ return MapUtil.empty();
+ }
+
+ final Map> params = new LinkedHashMap<>();
+ queryMap.forEach((key, value) -> {
+ final List values = params.computeIfAbsent(StrUtil.str(key), k -> new ArrayList<>(1));
+ // 一般是一个参数
+ values.add(StrUtil.str(value));
+ });
+ return params;
+ }
+}
diff --git a/hutool-core/src/test/java/cn/hutool/core/net/url/UrlQueryUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/net/url/UrlQueryUtilTest.java
new file mode 100755
index 000000000..17994e794
--- /dev/null
+++ b/hutool-core/src/test/java/cn/hutool/core/net/url/UrlQueryUtilTest.java
@@ -0,0 +1,153 @@
+package cn.hutool.core.net.url;
+
+import cn.hutool.core.util.CharsetUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Map;
+
+public class UrlQueryUtilTest {
+ @Test
+ public void decodeQueryTest() {
+ final String paramsStr = "uuuu=0&a=b&c=%3F%23%40!%24%25%5E%26%3Ddsssss555555";
+ final Map> map = UrlQueryUtil.decodeQuery(paramsStr, CharsetUtil.NAME_UTF_8);
+ Assert.assertEquals("0", map.get("uuuu").get(0));
+ Assert.assertEquals("b", map.get("a").get(0));
+ Assert.assertEquals("?#@!$%^&=dsssss555555", map.get("c").get(0));
+ }
+
+ @Test
+ public void decodeQueryTest2() {
+ // 参数值存在分界标记等号时
+ final Map paramMap = UrlQueryUtil.decodeQuery("https://www.xxx.com/api.action?aa=123&f_token=NzBkMjQxNDM1MDVlMDliZTk1OTU3ZDI1OTI0NTBiOWQ=", CharsetUtil.UTF_8);
+ Assert.assertEquals("123",paramMap.get("aa"));
+ Assert.assertEquals("NzBkMjQxNDM1MDVlMDliZTk1OTU3ZDI1OTI0NTBiOWQ=",paramMap.get("f_token"));
+ }
+
+ @Test
+ public void toQueryTest() {
+ final String paramsStr = "uuuu=0&a=b&c=3Ddsssss555555";
+ final Map> map = UrlQueryUtil.decodeQuery(paramsStr, CharsetUtil.NAME_UTF_8);
+
+ final String encodedParams = UrlQueryUtil.toQuery(map);
+ Assert.assertEquals(paramsStr, encodedParams);
+ }
+
+ @Test
+ public void encodeParamTest() {
+ // ?单独存在去除之,&单位位于末尾去除之
+ String paramsStr = "?a=b&c=d&";
+ String encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
+ Assert.assertEquals("a=b&c=d", encode);
+
+ // url不参与转码
+ paramsStr = "http://www.abc.dd?a=b&c=d&";
+ encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
+ Assert.assertEquals("http://www.abc.dd?a=b&c=d", encode);
+
+ // b=b中的=被当作值的一部分,不做encode
+ paramsStr = "a=b=b&c=d&";
+ encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
+ Assert.assertEquals("a=b=b&c=d", encode);
+
+ // =d的情况被处理为key为空
+ paramsStr = "a=bbb&c=d&=d";
+ encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
+ Assert.assertEquals("a=bbb&c=d&=d", encode);
+
+ // d=的情况被处理为value为空
+ paramsStr = "a=bbb&c=d&d=";
+ encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
+ Assert.assertEquals("a=bbb&c=d&d=", encode);
+
+ // 多个&&被处理为单个,相当于空条件
+ paramsStr = "a=bbb&c=d&&&d=";
+ encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
+ Assert.assertEquals("a=bbb&c=d&d=", encode);
+
+ // &d&相当于只有键,无值得情况
+ paramsStr = "a=bbb&c=d&d&";
+ encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
+ Assert.assertEquals("a=bbb&c=d&d=", encode);
+
+ // 中文的键和值被编码
+ paramsStr = "a=bbb&c=你好&哈喽&";
+ encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
+ Assert.assertEquals("a=bbb&c=%E4%BD%A0%E5%A5%BD&%E5%93%88%E5%96%BD=", encode);
+
+ // URL原样输出
+ paramsStr = "https://www.hutool.cn/";
+ encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
+ Assert.assertEquals(paramsStr, encode);
+
+ // URL原样输出
+ paramsStr = "https://www.hutool.cn/?";
+ encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
+ Assert.assertEquals("https://www.hutool.cn/", encode);
+ }
+
+ @Test
+ public void decodeParamTest() {
+ // 开头的?被去除
+ String a = "?a=b&c=d&";
+ Map> map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
+ Assert.assertEquals("b", map.get("a").get(0));
+ Assert.assertEquals("d", map.get("c").get(0));
+
+ // =e被当作空为key,e为value
+ a = "?a=b&c=d&=e";
+ map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
+ Assert.assertEquals("b", map.get("a").get(0));
+ Assert.assertEquals("d", map.get("c").get(0));
+ Assert.assertEquals("e", map.get("").get(0));
+
+ // 多余的&去除
+ a = "?a=b&c=d&=e&&&&";
+ map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
+ Assert.assertEquals("b", map.get("a").get(0));
+ Assert.assertEquals("d", map.get("c").get(0));
+ Assert.assertEquals("e", map.get("").get(0));
+
+ // 值为空
+ a = "?a=b&c=d&e=";
+ map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
+ Assert.assertEquals("b", map.get("a").get(0));
+ Assert.assertEquals("d", map.get("c").get(0));
+ Assert.assertEquals("", map.get("e").get(0));
+
+ // &=被作为键和值都为空
+ a = "a=b&c=d&=";
+ map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
+ Assert.assertEquals("b", map.get("a").get(0));
+ Assert.assertEquals("d", map.get("c").get(0));
+ Assert.assertEquals("", map.get("").get(0));
+
+ // &e&这类单独的字符串被当作key
+ a = "a=b&c=d&e&";
+ map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
+ Assert.assertEquals("b", map.get("a").get(0));
+ Assert.assertEquals("d", map.get("c").get(0));
+ Assert.assertNull(map.get("e").get(0));
+ Assert.assertNull(map.get("").get(0));
+
+ // 被编码的键和值被还原
+ a = "a=bbb&c=%E4%BD%A0%E5%A5%BD&%E5%93%88%E5%96%BD=";
+ map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
+ Assert.assertEquals("bbb", map.get("a").get(0));
+ Assert.assertEquals("你好", map.get("c").get(0));
+ Assert.assertEquals("", map.get("哈喽").get(0));
+ }
+
+ @Test
+ public void normalizeQueryTest() {
+ final String encodeResult = UrlQueryUtil.normalizeQuery("参数", CharsetUtil.UTF_8);
+ Assert.assertEquals("%E5%8F%82%E6%95%B0", encodeResult);
+ }
+
+ @Test
+ public void normalizeBlankQueryTest() {
+ final String encodeResult = UrlQueryUtil.normalizeQuery("", CharsetUtil.UTF_8);
+ Assert.assertEquals("", encodeResult);
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java b/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java
index 382c3f827..2d3fcd114 100644
--- a/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java
+++ b/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java
@@ -3,7 +3,6 @@ package cn.hutool.http;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.StrUtil;
-import cn.hutool.http.client.engine.jdk.HttpRequest;
import cn.hutool.http.meta.Header;
import java.util.ArrayList;
@@ -15,7 +14,7 @@ import java.util.Map.Entry;
/**
* 全局头部信息
- * 所有Http请求将共用此全局头部信息,除非在{@link HttpRequest}中自定义头部信息覆盖之
+ * 所有Http请求将共用此全局头部信息
*
* @author looly
*/
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java b/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java
index 03aa7d0d0..9fd7e52fb 100755
--- a/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java
+++ b/hutool-http/src/main/java/cn/hutool/http/HttpUtil.java
@@ -1,34 +1,26 @@
package cn.hutool.http;
import cn.hutool.core.codec.BaseN.Base64;
-import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.io.StreamProgress;
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.net.url.RFC3986;
-import cn.hutool.core.net.url.URLEncoder;
-import cn.hutool.core.net.url.UrlQuery;
+import cn.hutool.core.net.url.UrlQueryUtil;
import cn.hutool.core.regex.ReUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjUtil;
-import cn.hutool.http.client.HttpDownloader;
+import cn.hutool.http.client.ClientConfig;
+import cn.hutool.http.client.Request;
+import cn.hutool.http.client.Response;
import cn.hutool.http.client.cookie.GlobalCookieManager;
-import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.client.engine.ClientEngineFactory;
import cn.hutool.http.meta.ContentType;
import cn.hutool.http.meta.Method;
import cn.hutool.http.server.SimpleServer;
-import java.io.File;
import java.io.InputStream;
-import java.io.OutputStream;
import java.net.CookieManager;
import java.net.HttpURLConnection;
import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
@@ -69,52 +61,6 @@ public class HttpUtil {
return StrUtil.startWithIgnoreCase(url, "http:");
}
- /**
- * 创建Http请求对象
- *
- * @param method 方法枚举{@link Method}
- * @param url 请求的URL,可以使HTTP或者HTTPS
- * @return {@link HttpRequest}
- * @since 3.0.9
- */
- public static HttpRequest createRequest(final Method method, final String url) {
- return HttpRequest.of(url).method(method);
- }
-
- /**
- * 创建Http GET请求对象
- *
- * @param url 请求的URL,可以使HTTP或者HTTPS
- * @return {@link HttpRequest}
- * @since 3.2.0
- */
- public static HttpRequest createGet(final String url) {
- return createGet(url, false);
- }
-
- /**
- * 创建Http GET请求对象
- *
- * @param url 请求的URL,可以使HTTP或者HTTPS
- * @param isFollowRedirects 是否打开重定向
- * @return {@link HttpRequest}
- * @since 5.6.4
- */
- public static HttpRequest createGet(final String url, final boolean isFollowRedirects) {
- return HttpRequest.get(url).setFollowRedirects(isFollowRedirects);
- }
-
- /**
- * 创建Http POST请求对象
- *
- * @param url 请求的URL,可以使HTTP或者HTTPS
- * @return {@link HttpRequest}
- * @since 3.2.0
- */
- public static HttpRequest createPost(final String url) {
- return HttpRequest.post(url);
- }
-
/**
* 发送get请求
*
@@ -124,7 +70,7 @@ public class HttpUtil {
*/
@SuppressWarnings("resource")
public static String get(final String urlString, final Charset customCharset) {
- return HttpRequest.get(urlString).charset(customCharset).execute().bodyStr();
+ return send(Request.of(urlString).charset(customCharset)).bodyStr();
}
/**
@@ -147,7 +93,9 @@ public class HttpUtil {
*/
@SuppressWarnings("resource")
public static String get(final String urlString, final int timeout) {
- return HttpRequest.get(urlString).timeout(timeout).execute().bodyStr();
+ return ClientEngineFactory.get()
+ .setConfig(ClientConfig.of().setConnectionTimeout(timeout).setReadTimeout(timeout))
+ .send(Request.of(urlString)).bodyStr();
}
/**
@@ -159,21 +107,8 @@ public class HttpUtil {
*/
@SuppressWarnings("resource")
public static String get(final String urlString, final Map paramMap) {
- return HttpRequest.get(urlString).form(paramMap).execute().bodyStr();
- }
-
- /**
- * 发送get请求
- *
- * @param urlString 网址
- * @param paramMap post表单数据
- * @param timeout 超时时长,-1表示默认超时,单位毫秒
- * @return 返回数据
- * @since 3.3.0
- */
- @SuppressWarnings("resource")
- public static String get(final String urlString, final Map paramMap, final int timeout) {
- return HttpRequest.get(urlString).form(paramMap).timeout(timeout).execute().bodyStr();
+ return send(Request.of(urlString).form(paramMap))
+ .bodyStr();
}
/**
@@ -183,22 +118,10 @@ public class HttpUtil {
* @param paramMap post表单数据
* @return 返回数据
*/
+ @SuppressWarnings("resource")
public static String post(final String urlString, final Map paramMap) {
- return post(urlString, paramMap, HttpGlobalConfig.getTimeout());
- }
-
- /**
- * 发送post请求
- *
- * @param urlString 网址
- * @param paramMap post表单数据
- * @param timeout 超时时长,-1表示默认超时,单位毫秒
- * @return 返回数据
- * @since 3.2.0
- */
- @SuppressWarnings("resource")
- public static String post(final String urlString, final Map paramMap, final int timeout) {
- return HttpRequest.post(urlString).form(paramMap).timeout(timeout).execute().bodyStr();
+ return send(Request.of(urlString).method(Method.POST).form(paramMap))
+ .bodyStr();
}
/**
@@ -214,426 +137,21 @@ public class HttpUtil {
* @param body post表单数据
* @return 返回数据
*/
+ @SuppressWarnings("resource")
public static String post(final String urlString, final String body) {
- return post(urlString, body, HttpGlobalConfig.getTimeout());
+ return send(Request.of(urlString).method(Method.POST).body(body))
+ .bodyStr();
}
/**
- * 发送post请求
- * 请求体body参数支持两种类型:
+ * 使用默认HTTP引擎,发送HTTP请求
*
- *
- * 1. 标准参数,例如 a=1&b=2 这种格式
- * 2. Rest模式,此时body需要传入一个JSON或者XML字符串,Hutool会自动绑定其对应的Content-Type
- *
- *
- * @param urlString 网址
- * @param body post表单数据
- * @param timeout 超时时长,-1表示默认超时,单位毫秒
- * @return 返回数据
- * @since 3.2.0
+ * @param request HTTP请求
+ * @return HTTP响应
+ * @see ClientEngineFactory#get()#send(Request)
*/
- @SuppressWarnings("resource")
- public static String post(final String urlString, final String body, final int timeout) {
- return HttpRequest.post(urlString).timeout(timeout).body(body).execute().bodyStr();
- }
-
- // ---------------------------------------------------------------------------------------- download
-
- /**
- * 下载远程文本
- *
- * @param url 请求的url
- * @param customCharsetName 自定义的字符集
- * @return 文本
- */
- public static String downloadString(final String url, final String customCharsetName) {
- return downloadString(url, CharsetUtil.charset(customCharsetName), null);
- }
-
- /**
- * 下载远程文本
- *
- * @param url 请求的url
- * @param customCharset 自定义的字符集,可以使用{@link CharsetUtil#charset} 方法转换
- * @return 文本
- */
- public static String downloadString(final String url, final Charset customCharset) {
- return downloadString(url, customCharset, null);
- }
-
- /**
- * 下载远程文本
- *
- * @param url 请求的url
- * @param customCharset 自定义的字符集,可以使用{@link CharsetUtil#charset} 方法转换
- * @param streamPress 进度条 {@link StreamProgress}
- * @return 文本
- */
- public static String downloadString(final String url, final Charset customCharset, final StreamProgress streamPress) {
- return HttpDownloader.downloadString(url, customCharset, streamPress);
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param dest 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @return 文件
- */
- public static File downloadFile(final String url, final String dest) {
- return downloadFile(url, FileUtil.file(dest));
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @return 文件
- */
- public static File downloadFile(final String url, final File destFile) {
- return downloadFile(url, destFile, null);
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @param timeout 超时,单位毫秒,-1表示默认超时
- * @return 文件
- * @since 4.0.4
- */
- public static File downloadFile(final String url, final File destFile, final int timeout) {
- return downloadFile(url, destFile, timeout, null);
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @param streamProgress 进度条
- * @return 文件
- */
- public static File downloadFile(final String url, final File destFile, final StreamProgress streamProgress) {
- return downloadFile(url, destFile, -1, streamProgress);
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @param timeout 超时,单位毫秒,-1表示默认超时
- * @param streamProgress 进度条
- * @return 文件
- * @since 4.0.4
- */
- public static File downloadFile(final String url, final File destFile, final int timeout, final StreamProgress streamProgress) {
- return HttpDownloader.downloadFile(url, destFile, timeout, streamProgress);
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param dest 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @return 下载的文件对象
- * @since 5.4.1
- */
- public static File downloadFileFromUrl(final String url, final String dest) {
- return downloadFileFromUrl(url, FileUtil.file(dest));
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @return 下载的文件对象
- * @since 5.4.1
- */
- public static File downloadFileFromUrl(final String url, final File destFile) {
- return downloadFileFromUrl(url, destFile, null);
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @param timeout 超时,单位毫秒,-1表示默认超时
- * @return 下载的文件对象
- * @since 5.4.1
- */
- public static File downloadFileFromUrl(final String url, final File destFile, final int timeout) {
- return downloadFileFromUrl(url, destFile, timeout, null);
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @param streamProgress 进度条
- * @return 下载的文件对象
- * @since 5.4.1
- */
- public static File downloadFileFromUrl(final String url, final File destFile, final StreamProgress streamProgress) {
- return downloadFileFromUrl(url, destFile, -1, streamProgress);
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @param timeout 超时,单位毫秒,-1表示默认超时
- * @param streamProgress 进度条
- * @return 下载的文件对象
- * @since 5.4.1
- */
- public static File downloadFileFromUrl(final String url, final File destFile, final int timeout, final StreamProgress streamProgress) {
- return HttpDownloader.downloadForFile(url, destFile, timeout, streamProgress);
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param out 将下载内容写到输出流中 {@link OutputStream}
- * @param isCloseOut 是否关闭输出流
- * @return 文件大小
- */
- public static long download(final String url, final OutputStream out, final boolean isCloseOut) {
- return download(url, out, isCloseOut, null);
- }
-
- /**
- * 下载远程文件
- *
- * @param url 请求的url
- * @param out 将下载内容写到输出流中 {@link OutputStream}
- * @param isCloseOut 是否关闭输出流
- * @param streamProgress 进度条
- * @return 文件大小
- */
- public static long download(final String url, final OutputStream out, final boolean isCloseOut, final StreamProgress streamProgress) {
- return HttpDownloader.download(url, out, isCloseOut, streamProgress);
- }
-
- /**
- * 下载远程文件数据,支持30x跳转
- *
- * @param url 请求的url
- * @return 文件数据
- * @since 5.3.6
- */
- public static byte[] downloadBytes(final String url) {
- return HttpDownloader.downloadBytes(url);
- }
-
- /**
- * 将Map形式的Form表单数据转换为Url参数形式,会自动url编码键和值
- *
- * @param paramMap 表单数据
- * @return url参数
- */
- public static String toParams(final Map paramMap) {
- return toParams(paramMap, CharsetUtil.UTF_8);
- }
-
- /**
- * 将Map形式的Form表单数据转换为Url参数形式
- * paramMap中如果key为空(null和"")会被忽略,如果value为null,会被做为空白符("")
- * 会自动url编码键和值
- * 此方法用于拼接URL中的Query部分,并不适用于POST请求中的表单
- *
- *
- * key1=v1&key2=&key3=v3
- *
- *
- * @param paramMap 表单数据
- * @param charset 编码,{@code null} 表示不encode键值对
- * @return url参数
- * @see #toParams(Map, Charset, boolean)
- */
- public static String toParams(final Map paramMap, final Charset charset) {
- return toParams(paramMap, charset, false);
- }
-
- /**
- * 将Map形式的Form表单数据转换为Url参数形式
- * paramMap中如果key为空(null和"")会被忽略,如果value为null,会被做为空白符("")
- * 会自动url编码键和值
- *
- *
- * key1=v1&key2=&key3=v3
- *
- *
- * @param paramMap 表单数据
- * @param charset 编码,null表示不encode键值对
- * @param isFormUrlEncoded 是否为x-www-form-urlencoded模式,此模式下空格会编码为'+'
- * @return url参数
- * @since 5.7.16
- */
- public static String toParams(final Map paramMap, final Charset charset, final boolean isFormUrlEncoded) {
- return UrlQuery.of(paramMap, isFormUrlEncoded).build(charset);
- }
-
- /**
- * 对URL参数做编码,只编码键和值
- * 提供的值可以是url附带参数,但是不能只是url
- *
- * 注意,此方法只能标准化整个URL,并不适合于单独编码参数值
- *
- * @param urlWithParams url和参数,可以包含url本身,也可以单独参数
- * @param charset 编码
- * @return 编码后的url和参数
- * @since 4.0.1
- */
- public static String encodeParams(final String urlWithParams, final Charset charset) {
- if (StrUtil.isBlank(urlWithParams)) {
- return StrUtil.EMPTY;
- }
-
- String urlPart = null; // url部分,不包括问号
- String paramPart; // 参数部分
- final int pathEndPos = urlWithParams.indexOf('?');
- if (pathEndPos > -1) {
- // url + 参数
- urlPart = StrUtil.subPre(urlWithParams, pathEndPos);
- paramPart = StrUtil.subSuf(urlWithParams, pathEndPos + 1);
- if (StrUtil.isBlank(paramPart)) {
- // 无参数,返回url
- return urlPart;
- }
- } else if (false == StrUtil.contains(urlWithParams, '=')) {
- // 无参数的URL
- return urlWithParams;
- } else {
- // 无URL的参数
- paramPart = urlWithParams;
- }
-
- paramPart = normalizeParams(paramPart, charset);
-
- return StrUtil.isBlank(urlPart) ? paramPart : urlPart + "?" + paramPart;
- }
-
- /**
- * 标准化参数字符串,即URL中?后的部分
- *
- * 注意,此方法只能标准化整个URL,并不适合于单独编码参数值
- *
- * @param paramPart 参数字符串
- * @param charset 编码
- * @return 标准化的参数字符串
- * @since 4.5.2
- */
- public static String normalizeParams(final String paramPart, final Charset charset) {
- if (StrUtil.isEmpty(paramPart)) {
- return paramPart;
- }
- final StringBuilder builder = new StringBuilder(paramPart.length() + 16);
- final int len = paramPart.length();
- String name = null;
- int pos = 0; // 未处理字符开始位置
- char c; // 当前字符
- int i; // 当前字符位置
- for (i = 0; i < len; i++) {
- c = paramPart.charAt(i);
- if (c == '=') { // 键值对的分界点
- if (null == name) {
- // 只有=前未定义name时被当作键值分界符,否则做为普通字符
- name = (pos == i) ? StrUtil.EMPTY : paramPart.substring(pos, i);
- pos = i + 1;
- }
- } else if (c == '&') { // 参数对的分界点
- if (pos != i) {
- if (null == name) {
- // 对于像&a&这类无参数值的字符串,我们将name为a的值设为""
- name = paramPart.substring(pos, i);
- builder.append(RFC3986.QUERY_PARAM_NAME.encode(name, charset)).append('=');
- } else {
- builder.append(RFC3986.QUERY_PARAM_NAME.encode(name, charset)).append('=')
- .append(RFC3986.QUERY_PARAM_VALUE.encode(paramPart.substring(pos, i), charset)).append('&');
- }
- name = null;
- }
- pos = i + 1;
- }
- }
-
- // 结尾处理
- if (null != name) {
- builder.append(URLEncoder.encodeQuery(name, charset)).append('=');
- }
- if (pos != i) {
- if (null == name && pos > 0) {
- builder.append('=');
- }
- builder.append(URLEncoder.encodeQuery(paramPart.substring(pos, i), charset));
- }
-
- // 以&结尾则去除之
- final int lastIndex = builder.length() - 1;
- if ('&' == builder.charAt(lastIndex)) {
- builder.delete(lastIndex, builder.length());
- }
- return builder.toString();
- }
-
- /**
- * 将URL参数解析为Map(也可以解析Post中的键值对参数)
- *
- * @param paramsStr 参数字符串(或者带参数的Path)
- * @param charset 字符集
- * @return 参数Map
- * @since 5.2.6
- */
- public static Map decodeParamMap(final String paramsStr, final Charset charset) {
- final Map queryMap = UrlQuery.of(paramsStr, charset).getQueryMap();
- if (MapUtil.isEmpty(queryMap)) {
- return MapUtil.empty();
- }
- return Convert.toMap(String.class, String.class, queryMap);
- }
-
- /**
- * 将URL参数解析为Map(也可以解析Post中的键值对参数)
- *
- * @param paramsStr 参数字符串(或者带参数的Path)
- * @param charset 字符集
- * @return 参数Map
- */
- public static Map> decodeParams(final String paramsStr, final String charset) {
- return decodeParams(paramsStr, CharsetUtil.charset(charset));
- }
-
- /**
- * 将URL参数解析为Map(也可以解析Post中的键值对参数)
- *
- * @param paramsStr 参数字符串(或者带参数的Path)
- * @param charset 字符集
- * @return 参数Map
- * @since 5.2.6
- */
- public static Map> decodeParams(final String paramsStr, final Charset charset) {
- final Map queryMap = UrlQuery.of(paramsStr, charset).getQueryMap();
- if (MapUtil.isEmpty(queryMap)) {
- return MapUtil.empty();
- }
-
- final Map> params = new LinkedHashMap<>();
- queryMap.forEach((key, value) -> {
- final List values = params.computeIfAbsent(StrUtil.str(key), k -> new ArrayList<>(1));
- // 一般是一个参数
- values.add(StrUtil.str(value));
- });
- return params;
+ public static Response send(final Request request){
+ return ClientEngineFactory.get().send(request);
}
/**
@@ -649,11 +167,11 @@ public class HttpUtil {
public static String urlWithForm(String url, final Map form, final Charset charset, final boolean isEncodeParams) {
if (isEncodeParams && StrUtil.contains(url, '?')) {
// 在需要编码的情况下,如果url中已经有部分参数,则编码之
- url = encodeParams(url, charset);
+ url = UrlQueryUtil.encodeQuery(url, charset);
}
// url和参数是分别编码的
- return urlWithForm(url, toParams(form, charset), charset, false);
+ return urlWithForm(url, UrlQueryUtil.toQuery(form, charset), charset, false);
}
/**
@@ -670,7 +188,7 @@ public class HttpUtil {
// 无额外参数
if (StrUtil.contains(url, '?')) {
// url中包含参数
- return isEncode ? encodeParams(url, charset) : url;
+ return isEncode ? UrlQueryUtil.encodeQuery(url, charset) : url;
}
return url;
}
@@ -680,7 +198,7 @@ public class HttpUtil {
final int qmIndex = url.indexOf('?');
if (qmIndex > 0) {
// 原URL带参数,则对这部分参数单独编码(如果选项为进行编码)
- urlBuilder.append(isEncode ? encodeParams(url, charset) : url);
+ urlBuilder.append(isEncode ? UrlQueryUtil.encodeQuery(url, charset) : url);
if (false == StrUtil.endWith(url, '&')) {
// 已经带参数的情况下追加参数
urlBuilder.append('&');
@@ -693,7 +211,7 @@ public class HttpUtil {
urlBuilder.append('?');
}
}
- urlBuilder.append(isEncode ? encodeParams(queryString, charset) : queryString);
+ urlBuilder.append(isEncode ? UrlQueryUtil.encodeQuery(queryString, charset) : queryString);
return urlBuilder.toString();
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/ClientConfig.java b/hutool-http/src/main/java/cn/hutool/http/client/ClientConfig.java
index dbd94c587..d07d27a40 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/ClientConfig.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/ClientConfig.java
@@ -46,11 +46,11 @@ public class ClientConfig {
/**
* 是否禁用缓存
*/
- public boolean disableCache;
+ private boolean disableCache;
/**
* 代理
*/
- public Proxy proxy;
+ private Proxy proxy;
/**
* 构造
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/ClientEngine.java b/hutool-http/src/main/java/cn/hutool/http/client/ClientEngine.java
index d485f8d61..1621d4090 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/ClientEngine.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/ClientEngine.java
@@ -10,6 +10,13 @@ import java.io.Closeable;
*/
public interface ClientEngine extends Closeable {
+ /**
+ * 设置客户端引擎参数,如超时、代理等信息
+ * @param config 客户端设置
+ * @return this
+ */
+ ClientEngine setConfig(ClientConfig config);
+
/**
* 发送HTTP请求
* @param message HTTP请求消息
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/HeaderOperation.java b/hutool-http/src/main/java/cn/hutool/http/client/HeaderOperation.java
index 5bd7682ec..f7376880d 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/HeaderOperation.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/HeaderOperation.java
@@ -47,7 +47,7 @@ public interface HeaderOperation> {
* @return header值
*/
default String header(final Header header) {
- return header(header.name());
+ return header(header.getValue());
}
/**
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/HttpDownloader.java b/hutool-http/src/main/java/cn/hutool/http/client/HttpDownloader.java
index ad58f5510..feb4d2ee4 100644
--- a/hutool-http/src/main/java/cn/hutool/http/client/HttpDownloader.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/HttpDownloader.java
@@ -1,11 +1,10 @@
package cn.hutool.http.client;
-import cn.hutool.core.io.stream.FastByteArrayOutputStream;
import cn.hutool.core.io.StreamProgress;
+import cn.hutool.core.io.stream.FastByteArrayOutputStream;
import cn.hutool.core.lang.Assert;
import cn.hutool.http.HttpException;
-import cn.hutool.http.HttpUtil;
-import cn.hutool.http.client.engine.jdk.HttpResponse;
+import cn.hutool.http.client.engine.ClientEngineFactory;
import java.io.File;
import java.io.OutputStream;
@@ -44,6 +43,29 @@ public class HttpDownloader {
return requestDownload(url, -1).bodyBytes();
}
+ /**
+ * 下载远程文件
+ *
+ * @param url 请求的url
+ * @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
+ * @return 文件
+ */
+ public static File downloadFile(final String url, final File targetFileOrDir) {
+ return downloadFile(url, targetFileOrDir, -1);
+ }
+
+ /**
+ * 下载远程文件
+ *
+ * @param url 请求的url
+ * @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
+ * @param timeout 超时,单位毫秒,-1表示默认超时
+ * @return 文件
+ */
+ public static File downloadFile(final String url, final File targetFileOrDir, final int timeout) {
+ return downloadFile(url, targetFileOrDir, timeout, null);
+ }
+
/**
* 下载远程文件
*
@@ -74,19 +96,6 @@ public class HttpDownloader {
return requestDownload(url, timeout).body().write(targetFileOrDir, tempFileSuffix, streamProgress);
}
- /**
- * 下载远程文件,返回文件
- *
- * @param url 请求的url
- * @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
- * @param timeout 超时,单位毫秒,-1表示默认超时
- * @param streamProgress 进度条
- * @return 文件
- */
- public static File downloadForFile(final String url, final File targetFileOrDir, final int timeout, final StreamProgress streamProgress) {
- return requestDownload(url, timeout).body().write(targetFileOrDir, streamProgress);
- }
-
/**
* 下载远程文件
*
@@ -110,12 +119,12 @@ public class HttpDownloader {
* @return HttpResponse
* @since 5.4.1
*/
- private static HttpResponse requestDownload(final String url, final int timeout) {
+ private static Response requestDownload(final String url, final int timeout) {
Assert.notBlank(url, "[url] is blank !");
- final HttpResponse response = HttpUtil.createGet(url, true)
- .timeout(timeout)
- .executeAsync();
+ final Response response = ClientEngineFactory.get()
+ .setConfig(ClientConfig.of().setConnectionTimeout(timeout).setReadTimeout(timeout))
+ .send(Request.of(url));
if (response.isOk()) {
return response;
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/Request.java b/hutool-http/src/main/java/cn/hutool/http/client/Request.java
index 4be1df12a..b22c395a7 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/Request.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/Request.java
@@ -1,19 +1,23 @@
package cn.hutool.http.client;
import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjUtil;
+import cn.hutool.http.GlobalHeaders;
import cn.hutool.http.HttpGlobalConfig;
+import cn.hutool.http.HttpUtil;
import cn.hutool.http.client.body.HttpBody;
+import cn.hutool.http.client.body.StringBody;
+import cn.hutool.http.client.body.UrlEncodedFormBody;
import cn.hutool.http.meta.Header;
import cn.hutool.http.meta.Method;
import java.nio.charset.Charset;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -96,6 +100,9 @@ public class Request implements HeaderOperation {
method = Method.GET;
headers = new HashMap<>();
maxRedirectCount = HttpGlobalConfig.getMaxRedirectCount();
+
+ // 全局默认请求头
+ header(GlobalHeaders.INSTANCE.headers(), false);
}
/**
@@ -198,9 +205,7 @@ public class Request implements HeaderOperation {
final List values = headers.get(name.trim());
if (isOverride || CollUtil.isEmpty(values)) {
- final ArrayList valueList = new ArrayList<>();
- valueList.add(value);
- headers.put(name.trim(), valueList);
+ headers.put(name.trim(), ListUtil.of(value));
} else {
values.add(value.trim());
}
@@ -216,6 +221,26 @@ public class Request implements HeaderOperation {
return this.body;
}
+ /**
+ * 添加请求表单内容
+ *
+ * @param formMap 表单内容
+ * @return this
+ */
+ public Request form(final Map formMap) {
+ return body(new UrlEncodedFormBody(formMap, charset()));
+ }
+
+ /**
+ * 添加字符串请求体
+ *
+ * @param body 请求体
+ * @return this
+ */
+ public Request body(final String body) {
+ return body(new StringBody(body, charset()));
+ }
+
/**
* 添加请求体
*
@@ -227,7 +252,7 @@ public class Request implements HeaderOperation {
// 根据内容赋值默认Content-Type
if (StrUtil.isBlank(header(Header.CONTENT_TYPE))) {
- header(Header.CONTENT_TYPE, body.getContentType(), true);
+ header(Header.CONTENT_TYPE, body.getContentType(charset()), true);
}
return this;
@@ -253,4 +278,13 @@ public class Request implements HeaderOperation {
this.maxRedirectCount = Math.max(maxRedirectCount, 0);
return this;
}
+
+ /**
+ * 发送请求
+ *
+ * @return 响应内容
+ */
+ public Response send() {
+ return HttpUtil.send(this);
+ }
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/Response.java b/hutool-http/src/main/java/cn/hutool/http/client/Response.java
index b8c59c4b6..39586c476 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/Response.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/Response.java
@@ -10,6 +10,9 @@ import cn.hutool.http.meta.Header;
import java.io.Closeable;
import java.io.InputStream;
import java.nio.charset.Charset;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
/**
* 响应内容接口,包括响应状态码、HTTP消息头、响应体等信息
@@ -35,12 +38,19 @@ public interface Response extends Closeable {
*/
String header(final String name);
+ /**
+ * 获取headers
+ *
+ * @return Headers Map
+ */
+ Map> headers();
+
/**
* 获取字符集编码,默认为响应头中的编码
*
* @return 字符集
*/
- default Charset charset(){
+ default Charset charset() {
return HttpUtil.getCharset(header(Header.CONTENT_TYPE));
}
@@ -54,9 +64,10 @@ public interface Response extends Closeable {
/**
* 获取响应体,包含服务端返回的内容和Content-Type信息
+ *
* @return {@link ResponseBody}
*/
- default ResponseBody body(){
+ default ResponseBody body() {
return new ResponseBody(this, bodyStream(), false, true);
}
@@ -152,4 +163,13 @@ public interface Response extends Closeable {
default String getCookieStr() {
return header(Header.SET_COOKIE);
}
+
+ /**
+ * 链式处理结果
+ *
+ * @param consumer {@link Consumer}
+ */
+ default void then(final Consumer consumer) {
+ consumer.accept(this);
+ }
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/body/HttpBody.java b/hutool-http/src/main/java/cn/hutool/http/client/body/HttpBody.java
index 09ff0ca1c..aa755f8ef 100644
--- a/hutool-http/src/main/java/cn/hutool/http/client/body/HttpBody.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/body/HttpBody.java
@@ -5,6 +5,7 @@ import cn.hutool.core.io.IoUtil;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.charset.Charset;
/**
* 定义请求体接口
@@ -25,6 +26,20 @@ public interface HttpBody {
*/
String getContentType();
+ /**
+ * 获取指定编码的Content-Type,类似于:application/json;charset=UTF-8
+ * @param charset 编码
+ * @return Content-Type
+ */
+ default String getContentType(final Charset charset){
+ final String contentType = getContentType();
+ if(null == contentType){
+ return null;
+ }
+
+ return contentType + ";charset=" + charset.name();
+ }
+
/**
* 写出并关闭{@link OutputStream}
*
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/ClientEngineFactory.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/ClientEngineFactory.java
index d29e8f259..511ec716b 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/ClientEngineFactory.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/ClientEngineFactory.java
@@ -25,7 +25,7 @@ public class ClientEngineFactory {
}
/**
- * 根据用户引入的拼音引擎jar,自动创建对应的拼音引擎对象
+ * 根据用户引入的HTTP客户端引擎jar,自动创建对应的拼音引擎对象
* 推荐创建的引擎单例使用,此方法每次调用会返回新的引擎
*
* @return {@code ClientEngine}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Engine.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Engine.java
index 2d5d69d6c..830fd98f1 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Engine.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Engine.java
@@ -1,14 +1,18 @@
package cn.hutool.http.client.engine.httpclient4;
+import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.http.GlobalHeaders;
import cn.hutool.http.HttpException;
+import cn.hutool.http.client.ClientConfig;
import cn.hutool.http.client.ClientEngine;
import cn.hutool.http.client.Request;
import cn.hutool.http.client.Response;
import cn.hutool.http.client.body.HttpBody;
+
import org.apache.http.Header;
+import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
@@ -29,20 +33,27 @@ import java.util.Map;
*/
public class HttpClient4Engine implements ClientEngine {
- private final CloseableHttpClient engine;
+ private ClientConfig config;
+ private CloseableHttpClient engine;
/**
* 构造
*/
- public HttpClient4Engine() {
- this.engine = HttpClients.custom()
- // 设置默认头信息
- .setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()))
- .build();
+ public HttpClient4Engine() {}
+
+ @Override
+ public HttpClient4Engine setConfig(final ClientConfig config) {
+ this.config = config;
+ // 重置客户端
+ IoUtil.close(this.engine);
+ this.engine = null;
+ return this;
}
@Override
public Response send(final Request message) {
+ initEngine();
+
final HttpEntityEnclosingRequestBase request = buildRequest(message);
final CloseableHttpResponse response;
try {
@@ -64,6 +75,29 @@ public class HttpClient4Engine implements ClientEngine {
this.engine.close();
}
+ /**
+ * 初始化引擎
+ */
+ private void initEngine(){
+ if(null != this.engine){
+ return;
+ }
+
+ RequestConfig requestConfig = null;
+ if(null != this.config){
+ requestConfig = RequestConfig.custom()
+ .setConnectTimeout(this.config.getConnectionTimeout())
+ .setConnectionRequestTimeout(this.config.getConnectionTimeout())
+ .build();
+ }
+
+ this.engine = HttpClients.custom()
+ // 设置默认头信息
+ .setDefaultRequestConfig(requestConfig)
+ .setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()))
+ .build();
+ }
+
/**
* 构建请求体
*
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Response.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Response.java
index cea125b19..85790372c 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Response.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient4/HttpClient4Response.java
@@ -13,6 +13,11 @@ import org.apache.http.util.EntityUtils;
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;
/**
* HttpClient响应包装
@@ -59,6 +64,17 @@ public class HttpClient4Response implements Response {
return null;
}
+ @Override
+ public Map> headers() {
+ final Header[] headers = rawRes.getAllHeaders();
+ final HashMap> result = new LinkedHashMap<>(headers.length, 1);
+ for (final Header header : headers) {
+ final List valueList = result.computeIfAbsent(header.getName(), k -> new ArrayList<>());
+ valueList.add(header.getValue());
+ }
+ return result;
+ }
+
@Override
public long contentLength() {
return rawRes.getEntity().getContentLength();
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Engine.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Engine.java
index 7214ff404..cd4bf9546 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Engine.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Engine.java
@@ -1,16 +1,20 @@
package cn.hutool.http.client.engine.httpclient5;
+import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.http.GlobalHeaders;
import cn.hutool.http.HttpException;
+import cn.hutool.http.client.ClientConfig;
import cn.hutool.http.client.ClientEngine;
import cn.hutool.http.client.Request;
import cn.hutool.http.client.Response;
import cn.hutool.http.client.body.HttpBody;
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
+import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.Header;
@@ -21,6 +25,7 @@ import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
/**
* Apache HttpClient5的HTTP请求引擎
@@ -30,20 +35,27 @@ import java.util.Map;
*/
public class HttpClient5Engine implements ClientEngine {
- private final CloseableHttpClient engine;
+ private ClientConfig config;
+ private CloseableHttpClient engine;
/**
* 构造
*/
- public HttpClient5Engine() {
- this.engine = HttpClients.custom()
- // 设置默认头信息
- .setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()))
- .build();
+ public HttpClient5Engine() {}
+
+ @Override
+ public HttpClient5Engine setConfig(final ClientConfig config) {
+ this.config = config;
+ // 重置客户端
+ IoUtil.close(this.engine);
+ this.engine = null;
+ return this;
}
@Override
public Response send(final Request message) {
+ initEngine();
+
final ClassicHttpRequest request = buildRequest(message);
final CloseableHttpResponse response;
try {
@@ -65,6 +77,33 @@ public class HttpClient5Engine implements ClientEngine {
this.engine.close();
}
+ /**
+ * 初始化引擎
+ */
+ private void initEngine(){
+ if(null != this.engine){
+ return;
+ }
+
+ RequestConfig requestConfig = null;
+ if(null != this.config){
+ requestConfig = RequestConfig.custom()
+ .setConnectTimeout(this.config.getConnectionTimeout(), TimeUnit.MILLISECONDS)
+ .setConnectionRequestTimeout(this.config.getConnectionTimeout(), TimeUnit.MILLISECONDS)
+ .setResponseTimeout(this.config.getReadTimeout(), TimeUnit.MILLISECONDS)
+ .build();
+ }
+
+ final HttpClientBuilder builder = HttpClients.custom()
+ // 设置默认头信息
+ .setDefaultRequestConfig(requestConfig)
+ .setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()));
+
+ // TODO 设置代理
+
+ this.engine = builder.build();
+ }
+
/**
* 构建请求体
*
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Response.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Response.java
index 3b48847f0..02baef3d2 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Response.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/httpclient5/HttpClient5Response.java
@@ -13,6 +13,11 @@ import org.apache.hc.core5.http.io.entity.EntityUtils;
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;
/**
* HttpClient响应包装
@@ -59,6 +64,17 @@ public class HttpClient5Response implements Response {
return null;
}
+ @Override
+ public Map> headers() {
+ final Header[] headers = rawRes.getHeaders();
+ final HashMap> result = new LinkedHashMap<>(headers.length, 1);
+ for (final Header header : headers) {
+ final List valueList = result.computeIfAbsent(header.getName(), k -> new ArrayList<>());
+ valueList.add(header.getValue());
+ }
+ return result;
+ }
+
@Override
public long contentLength() {
return rawRes.getEntity().getContentLength();
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpRequest.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpRequest.java
deleted file mode 100755
index d3500251d..000000000
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpRequest.java
+++ /dev/null
@@ -1,1222 +0,0 @@
-package cn.hutool.http.client.engine.jdk;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.convert.Convert;
-import cn.hutool.core.io.IORuntimeException;
-import cn.hutool.core.io.resource.BytesResource;
-import cn.hutool.core.io.resource.FileResource;
-import cn.hutool.core.io.resource.MultiFileResource;
-import cn.hutool.core.io.resource.Resource;
-import cn.hutool.core.lang.Assert;
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.map.TableMap;
-import cn.hutool.core.net.ssl.SSLUtil;
-import cn.hutool.core.net.url.UrlBuilder;
-import cn.hutool.core.net.url.UrlQuery;
-import cn.hutool.core.text.StrUtil;
-import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.util.ObjUtil;
-import cn.hutool.http.GlobalHeaders;
-import cn.hutool.http.HttpConfig;
-import cn.hutool.http.HttpException;
-import cn.hutool.http.HttpGlobalConfig;
-import cn.hutool.http.HttpUtil;
-import cn.hutool.http.client.body.BytesBody;
-import cn.hutool.http.client.body.MultipartBody;
-import cn.hutool.http.client.body.HttpBody;
-import cn.hutool.http.client.body.UrlEncodedFormBody;
-import cn.hutool.http.client.cookie.GlobalCookieManager;
-import cn.hutool.http.meta.ContentType;
-import cn.hutool.http.meta.Header;
-import cn.hutool.http.meta.HttpStatus;
-import cn.hutool.http.meta.Method;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLSocketFactory;
-import java.io.File;
-import java.io.IOException;
-import java.net.CookieManager;
-import java.net.HttpURLConnection;
-import java.net.Proxy;
-import java.net.URLStreamHandler;
-import java.nio.charset.Charset;
-import java.util.Map;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-/**
- * http请求类
- * Http请求类用于构建Http请求并同步获取结果,此类通过CookieManager持有域名对应的Cookie值,再次请求时会自动附带Cookie信息
- *
- * @author Looly
- */
-@Deprecated
-public class HttpRequest extends HttpBase {
-
- // ---------------------------------------------------------------- static Http Method start
-
- /**
- * POST请求
- *
- * @param url URL
- * @return HttpRequest
- */
- public static HttpRequest post(final String url) {
- return of(url).method(Method.POST);
- }
-
- /**
- * GET请求
- *
- * @param url URL
- * @return HttpRequest
- */
- public static HttpRequest get(final String url) {
- return of(url).method(Method.GET);
- }
-
- /**
- * HEAD请求
- *
- * @param url URL
- * @return HttpRequest
- */
- public static HttpRequest head(final String url) {
- return of(url).method(Method.HEAD);
- }
-
- /**
- * OPTIONS请求
- *
- * @param url URL
- * @return HttpRequest
- */
- public static HttpRequest options(final String url) {
- return of(url).method(Method.OPTIONS);
- }
-
- /**
- * PUT请求
- *
- * @param url URL
- * @return HttpRequest
- */
- public static HttpRequest put(final String url) {
- return of(url).method(Method.PUT);
- }
-
- /**
- * PATCH请求
- *
- * @param url URL
- * @return HttpRequest
- * @since 3.0.9
- */
- public static HttpRequest patch(final String url) {
- return of(url).method(Method.PATCH);
- }
-
- /**
- * DELETE请求
- *
- * @param url URL
- * @return HttpRequest
- */
- public static HttpRequest delete(final String url) {
- return of(url).method(Method.DELETE);
- }
-
- /**
- * TRACE请求
- *
- * @param url URL
- * @return HttpRequest
- */
- public static HttpRequest trace(final String url) {
- return of(url).method(Method.TRACE);
- }
-
- /**
- * 构建一个HTTP请求
- * 对于传入的URL,可以自定义是否解码已经编码的内容,设置见{@link HttpGlobalConfig#setDecodeUrl(boolean)}
- * 在构建Http请求时,用户传入的URL可能有编码后和未编码的内容混合在一起,如果{@link HttpGlobalConfig#isDecodeUrl()}为{@code true},则会统一解码编码后的参数,
- * 按照RFC3986规范,在发送请求时,全部编码之。如果为{@code false},则不会解码已经编码的内容,在请求时只编码需要编码的部分。
- *
- * @param url URL链接,默认自动编码URL中的参数等信息
- * @return HttpRequest
- * @since 5.7.18
- */
- public static HttpRequest of(final String url) {
- return of(url, HttpGlobalConfig.isDecodeUrl() ? DEFAULT_CHARSET : null);
- }
-
- /**
- * 构建一个HTTP请求
- * 对于传入的URL,可以自定义是否解码已经编码的内容。
- * 在构建Http请求时,用户传入的URL可能有编码后和未编码的内容混合在一起,如果charset参数不为{@code null},则会统一解码编码后的参数,
- * 按照RFC3986规范,在发送请求时,全部编码之。如果为{@code false},则不会解码已经编码的内容,在请求时只编码需要编码的部分。
- *
- * @param url URL链接
- * @param charset 编码,如果为{@code null}不自动解码编码URL
- * @return HttpRequest
- * @since 5.7.18
- */
- public static HttpRequest of(final String url, final Charset charset) {
- return of(UrlBuilder.ofHttp(url, charset));
- }
-
- /**
- * 构建一个HTTP请求
- *
- * @param url {@link UrlBuilder}
- * @return HttpRequest
- * @since 5.8.0
- */
- public static HttpRequest of(final UrlBuilder url) {
- return new HttpRequest(url);
- }
-
- /**
- * 设置全局默认的连接和读取超时时长
- *
- * @param customTimeout 超时时长
- * @see HttpGlobalConfig#setTimeout(int)
- * @since 4.6.2
- */
- public static void setGlobalTimeout(final int customTimeout) {
- HttpGlobalConfig.setTimeout(customTimeout);
- }
-
- /**
- * 获取Cookie管理器,用于自定义Cookie管理
- *
- * @return {@link CookieManager}
- * @see GlobalCookieManager#getCookieManager()
- * @since 4.1.0
- */
- public static CookieManager getCookieManager() {
- return GlobalCookieManager.getCookieManager();
- }
-
- /**
- * 自定义{@link CookieManager}
- *
- * @param customCookieManager 自定义的{@link CookieManager}
- * @see GlobalCookieManager#setCookieManager(CookieManager)
- * @since 4.5.14
- */
- public static void setCookieManager(final CookieManager customCookieManager) {
- GlobalCookieManager.setCookieManager(customCookieManager);
- }
-
- /**
- * 关闭Cookie
- *
- * @see GlobalCookieManager#setCookieManager(CookieManager)
- * @since 4.1.9
- */
- public static void closeCookie() {
- GlobalCookieManager.setCookieManager(null);
- }
- // ---------------------------------------------------------------- static Http Method end
-
- private HttpConfig config = HttpConfig.of();
- private UrlBuilder url;
- private URLStreamHandler urlHandler;
- private Method method = Method.GET;
- /**
- * 连接对象
- */
- private HttpConnection httpConnection;
-
- /**
- * 存储表单数据
- */
- private Map form;
- /**
- * Cookie
- */
- private String cookie;
- /**
- * 是否为Multipart表单
- */
- private boolean isMultiPart;
- /**
- * 是否是REST请求模式
- */
- private boolean isRest;
- /**
- * 重定向次数计数器,内部使用
- */
- private int redirectCount;
-
- /**
- * 构造
- *
- * @param url {@link UrlBuilder}
- */
- public HttpRequest(final UrlBuilder url) {
- this.url = Assert.notNull(url, "URL must be not null!");
- // 给定默认URL编码
- final Charset charset = url.getCharset();
- if (null != charset) {
- this.charset(charset);
- }
- // 给定一个默认头信息
- this.header(GlobalHeaders.INSTANCE.headers());
- }
-
- /**
- * 获取请求URL
- *
- * @return URL字符串
- * @since 4.1.8
- */
- public String getUrl() {
- return url.toString();
- }
-
- /**
- * 设置URL
- *
- * @param url url字符串
- * @return this
- * @since 4.1.8
- */
- public HttpRequest setUrl(final String url) {
- return setUrl(UrlBuilder.ofHttp(url, this.charset));
- }
-
- /**
- * 设置URL
- *
- * @param urlBuilder url字符串
- * @return this
- * @since 5.3.1
- */
- public HttpRequest setUrl(final UrlBuilder urlBuilder) {
- this.url = urlBuilder;
- return this;
- }
-
- /**
- * 设置{@link URLStreamHandler}
- *
- * 部分环境下需要单独设置此项,例如当 WebLogic Server 实例充当 SSL 客户端角色(它会尝试通过 SSL 连接到其他服务器或应用程序)时,
- * 它会验证 SSL 服务器在数字证书中返回的主机名是否与用于连接 SSL 服务器的 URL 主机名相匹配。如果主机名不匹配,则删除此连接。
- * 因此weblogic不支持https的sni协议的主机名验证,此时需要将此值设置为sun.net.www.protocol.https.Handler对象。
- *
- * 相关issue见:https://gitee.com/dromara/hutool/issues/IMD1X
- *
- * @param urlHandler {@link URLStreamHandler}
- * @return this
- * @since 4.1.9
- */
- public HttpRequest setUrlHandler(final URLStreamHandler urlHandler) {
- this.urlHandler = urlHandler;
- return this;
- }
-
- /**
- * 获取{@link HttpConnection}
- * 在{@link #execute()} 执行前此对象为null
- *
- * @return {@link HttpConnection}
- * @since 4.2.2
- */
- public HttpConnection getConnection() {
- return this.httpConnection;
- }
-
- /**
- * 获取Http请求方法
- *
- * @return {@link Method}
- * @since 4.1.8
- */
- public Method getMethod() {
- return this.method;
- }
-
- /**
- * 设置请求方法
- *
- * @param method HTTP方法
- * @return HttpRequest
- */
- public HttpRequest method(final Method method) {
- this.method = method;
- return this;
- }
-
- // ---------------------------------------------------------------- Http Request Header start
-
- /**
- * @return 获取是否为长连接
- */
- public boolean isKeepAlive() {
- final String connection = header(Header.CONNECTION);
- if (connection == null) {
- return false == HTTP_1_0.equalsIgnoreCase(httpVersion);
- }
-
- return false == "close".equalsIgnoreCase(connection);
- }
-
- /**
- * 获取内容长度
- *
- * @return String
- */
- public String contentLength() {
- return header(Header.CONTENT_LENGTH);
- }
-
- /**
- * 设置内容长度
- *
- * @param value 长度
- * @return HttpRequest
- */
- public HttpRequest contentLength(final int value) {
- header(Header.CONTENT_LENGTH, String.valueOf(value));
- return this;
- }
-
- /**
- * 设置Cookie
- * 自定义Cookie后会覆盖Hutool的默认Cookie行为
- *
- * @param cookie Cookie值,如果为{@code null}则设置无效,使用默认Cookie行为
- * @return this
- * @since 3.0.7
- */
- public HttpRequest cookie(final String cookie) {
- this.cookie = cookie;
- return this;
- }
- // ---------------------------------------------------------------- Http Request Header end
-
- // ---------------------------------------------------------------- Form start
-
- /**
- * 设置表单数据
- *
- * @param name 名
- * @param value 值
- * @return this
- */
- public HttpRequest form(final String name, final Object value) {
- if (StrUtil.isBlank(name) || ObjUtil.isNull(value)) {
- return this; // 忽略非法的form表单项内容;
- }
-
- // 停用body
- this.bodyBytes = null;
-
- if (value instanceof File) {
- // 文件上传
- return this.form(name, (File) value);
- }
-
- if (value instanceof Resource) {
- return form(name, (Resource) value);
- }
-
- // 普通值
- final String strValue;
- if (value instanceof Iterable) {
- // 列表对象
- strValue = CollUtil.join((Iterable>) value, ",");
- } else if (ArrayUtil.isArray(value)) {
- if (File.class == ArrayUtil.getComponentType(value)) {
- // 多文件
- return this.form(name, (File[]) value);
- }
- // 数组对象
- strValue = ArrayUtil.join((Object[]) value, ",");
- } else {
- // 其他对象一律转换为字符串
- strValue = Convert.toStr(value, null);
- }
-
- return putToForm(name, strValue);
- }
-
- /**
- * 设置表单数据
- *
- * @param name 名
- * @param value 值
- * @param parameters 参数对,奇数为名,偶数为值
- * @return this
- */
- public HttpRequest form(final String name, final Object value, final Object... parameters) {
- form(name, value);
-
- for (int i = 0; i < parameters.length; i += 2) {
- form(parameters[i].toString(), parameters[i + 1]);
- }
- return this;
- }
-
- /**
- * 设置map类型表单数据
- *
- * @param formMap 表单内容
- * @return this
- */
- public HttpRequest form(final Map formMap) {
- if (MapUtil.isNotEmpty(formMap)) {
- formMap.forEach(this::form);
- }
- return this;
- }
-
- /**
- * 设置map<String, String>类型表单数据
- *
- * @param formMapStr 表单内容
- * @return this
- * @since 5.6.7
- */
- public HttpRequest formStr(final Map formMapStr) {
- if (MapUtil.isNotEmpty(formMapStr)) {
- formMapStr.forEach(this::form);
- }
- return this;
- }
-
- /**
- * 文件表单项
- * 一旦有文件加入,表单变为multipart/form-data
- *
- * @param name 名
- * @param files 需要上传的文件,为空跳过
- * @return this
- */
- public HttpRequest form(final String name, final File... files) {
- if (ArrayUtil.isEmpty(files)) {
- return this;
- }
- if (1 == files.length) {
- final File file = files[0];
- return form(name, file, file.getName());
- }
- return form(name, new MultiFileResource(files));
- }
-
- /**
- * 文件表单项
- * 一旦有文件加入,表单变为multipart/form-data
- *
- * @param name 名
- * @param file 需要上传的文件
- * @return this
- */
- public HttpRequest form(final String name, final File file) {
- return form(name, file, file.getName());
- }
-
- /**
- * 文件表单项
- * 一旦有文件加入,表单变为multipart/form-data
- *
- * @param name 名
- * @param file 需要上传的文件
- * @param fileName 文件名,为空使用文件默认的文件名
- * @return this
- */
- public HttpRequest form(final String name, final File file, final String fileName) {
- if (null != file) {
- form(name, new FileResource(file, fileName));
- }
- return this;
- }
-
- /**
- * 文件byte[]表单项
- * 一旦有文件加入,表单变为multipart/form-data
- *
- * @param name 名
- * @param fileBytes 需要上传的文件
- * @param fileName 文件名
- * @return this
- * @since 4.1.0
- */
- public HttpRequest form(final String name, final byte[] fileBytes, final String fileName) {
- if (null != fileBytes) {
- form(name, new BytesResource(fileBytes, fileName));
- }
- return this;
- }
-
- /**
- * 文件表单项
- * 一旦有文件加入,表单变为multipart/form-data
- *
- * @param name 名
- * @param resource 数据源,文件可以使用{@link FileResource}包装使用
- * @return this
- * @since 4.0.9
- */
- public HttpRequest form(final String name, final Resource resource) {
- if (null != resource) {
- if (false == isKeepAlive()) {
- keepAlive(true);
- }
-
- this.isMultiPart = true;
- return putToForm(name, resource);
- }
- return this;
- }
-
- /**
- * 获取表单数据
- *
- * @return 表单Map
- */
- public Map form() {
- return this.form;
- }
-
- /**
- * 获取文件表单数据
- *
- * @return 文件表单Map
- * @since 3.3.0
- */
- public Map fileForm() {
- final Map result = MapUtil.newHashMap();
- this.form.forEach((key, value) -> {
- if (value instanceof Resource) {
- result.put(key, (Resource) value);
- }
- });
- return result;
- }
- // ---------------------------------------------------------------- Form end
-
- // ---------------------------------------------------------------- Body start
-
- /**
- * 设置内容主体
- * 请求体body参数支持两种类型:
- *
- *
- * 1. 标准参数,例如 a=1&b=2 这种格式
- * 2. Rest模式,此时body需要传入一个JSON或者XML字符串,Hutool会自动绑定其对应的Content-Type
- *
- *
- * @param body 请求体
- * @return this
- */
- public HttpRequest body(final String body) {
- return this.body(body, null);
- }
-
- /**
- * 设置内容主体
- * 请求体body参数支持两种类型:
- *
- *
- * 1. 标准参数,例如 a=1&b=2 这种格式
- * 2. Rest模式,此时body需要传入一个JSON或者XML字符串,Hutool会自动绑定其对应的Content-Type
- *
- *
- * @param body 请求体
- * @param contentType 请求体类型,{@code null}表示自动判断类型
- * @return this
- */
- public HttpRequest body(final String body, String contentType) {
- final byte[] bytes = StrUtil.bytes(body, this.charset);
- body(bytes);
- this.form = null; // 当使用body时,停止form的使用
-
- if (null != contentType) {
- // Content-Type自定义设置
- this.contentType(contentType);
- } else {
- // 在用户未自定义的情况下自动根据内容判断
- contentType = HttpUtil.getContentTypeByRequestBody(body);
- if (null != contentType && ContentType.isDefault(this.header(Header.CONTENT_TYPE))) {
- if (null != this.charset) {
- // 附加编码信息
- contentType = ContentType.build(contentType, this.charset);
- }
- this.contentType(contentType);
- }
- }
-
- // 判断是否为rest请求
- if (StrUtil.containsAnyIgnoreCase(contentType, "json", "xml")) {
- this.isRest = true;
- contentLength(bytes.length);
- }
- return this;
- }
-
- /**
- * 设置主体字节码
- * 需在此方法调用前使用charset方法设置编码,否则使用默认编码UTF-8
- *
- * @param bodyBytes 主体
- * @return this
- */
- public HttpRequest body(final byte[] bodyBytes) {
- if (null != bodyBytes) {
- this.bodyBytes = bodyBytes;
- }
- return this;
- }
- // ---------------------------------------------------------------- Body end
-
- /**
- * 将新的配置加入
- * 注意加入的配置可能被修改
- *
- * @param config 配置
- * @return this
- */
- public HttpRequest setConfig(final HttpConfig config) {
- this.config = config;
- return this;
- }
-
- /**
- * 设置超时,单位:毫秒
- * 超时包括:
- *
- *
- * 1. 连接超时
- * 2. 读取响应超时
- *
- *
- * @param milliseconds 超时毫秒数
- * @return this
- * @see #setConnectionTimeout(int)
- * @see #setReadTimeout(int)
- */
- public HttpRequest timeout(final int milliseconds) {
- config.timeout(milliseconds);
- return this;
- }
-
- /**
- * 设置连接超时,单位:毫秒
- *
- * @param milliseconds 超时毫秒数
- * @return this
- * @since 4.5.6
- */
- public HttpRequest setConnectionTimeout(final int milliseconds) {
- config.setConnectionTimeout(milliseconds);
- return this;
- }
-
- /**
- * 设置连接超时,单位:毫秒
- *
- * @param milliseconds 超时毫秒数
- * @return this
- * @since 4.5.6
- */
- public HttpRequest setReadTimeout(final int milliseconds) {
- config.setReadTimeout(milliseconds);
- return this;
- }
-
- /**
- * 禁用缓存
- *
- * @return this
- */
- public HttpRequest disableCache() {
- config.disableCache();
- return this;
- }
-
- /**
- * 设置是否打开重定向,如果打开默认重定向次数为2
- * 此方法效果与{@link #setMaxRedirectCount(int)} 一致
- *
- *
- * 需要注意的是,当设置为{@code true}时,如果全局重定向次数非0,直接复用,否则设置默认2次。
- * 当设置为{@code false}时,无论全局是否设置次数,都设置为0。
- * 不调用此方法的情况下,使用全局默认的次数。
- *
- *
- * @param isFollowRedirects 是否打开重定向
- * @return this
- */
- public HttpRequest setFollowRedirects(final boolean isFollowRedirects) {
- if (isFollowRedirects) {
- if (config.maxRedirectCount <= 0) {
- // 默认两次跳转
- return setMaxRedirectCount(2);
- }
- } else {
- // 手动强制关闭重定向,此时不受全局重定向设置影响
- if (config.maxRedirectCount < 0) {
- return setMaxRedirectCount(0);
- }
- }
- return this;
- }
-
- /**
- * 设置最大重定向次数
- * 如果次数小于1则表示不重定向,大于等于1表示打开重定向
- *
- * @param maxRedirectCount 最大重定向次数
- * @return this
- * @since 3.3.0
- */
- public HttpRequest setMaxRedirectCount(final int maxRedirectCount) {
- config.setMaxRedirectCount(maxRedirectCount);
- return this;
- }
-
- /**
- * 设置域名验证器
- * 只针对HTTPS请求,如果不设置,不做验证,所有域名被信任
- *
- * @param hostnameVerifier HostnameVerifier
- * @return this
- */
- public HttpRequest setHostnameVerifier(final HostnameVerifier hostnameVerifier) {
- config.setHostnameVerifier(hostnameVerifier);
- return this;
- }
-
- /**
- * 设置Http代理
- *
- * @param host 代理 主机
- * @param port 代理 端口
- * @return this
- * @since 5.4.5
- */
- public HttpRequest setHttpProxy(final String host, final int port) {
- config.setHttpProxy(host, port);
- return this;
- }
-
- /**
- * 设置代理
- *
- * @param proxy 代理 {@link Proxy}
- * @return this
- */
- public HttpRequest setProxy(final Proxy proxy) {
- config.setProxy(proxy);
- return this;
- }
-
- /**
- * 设置SSLSocketFactory
- * 只针对HTTPS请求,如果不设置,使用默认的SSLSocketFactory
- * 默认SSLSocketFactory为:SSLSocketFactoryBuilder.create().build();
- *
- * @param ssf SSLScketFactory
- * @return this
- */
- public HttpRequest setSSLSocketFactory(final SSLSocketFactory ssf) {
- config.setSSLSocketFactory(ssf);
- return this;
- }
-
- /**
- * 设置HTTPS安全连接协议,只针对HTTPS请求,可以使用的协议包括:
- * 此方法调用后{@link #setSSLSocketFactory(SSLSocketFactory)} 将被覆盖。
- *
- *
- * 1. TLSv1.2
- * 2. TLSv1.1
- * 3. SSLv3
- * ...
- *
- *
- * @param protocol 协议
- * @return this
- * @see SSLUtil#createSSLContext(String)
- * @see #setSSLSocketFactory(SSLSocketFactory)
- */
- public HttpRequest setSSLProtocol(final String protocol) {
- config.setSSLProtocol(protocol);
- return this;
- }
-
- /**
- * 设置是否rest模式
- * rest模式下get请求不会把参数附加到URL之后
- *
- * @param isRest 是否rest模式
- * @return this
- * @since 4.5.0
- */
- public HttpRequest setRest(final boolean isRest) {
- this.isRest = isRest;
- return this;
- }
-
- /**
- * 采用流方式上传数据,无需本地缓存数据。
- * HttpUrlConnection默认是将所有数据读到本地缓存,然后再发送给服务器,这样上传大文件时就会导致内存溢出。
- *
- * @param blockSize 块大小(bytes数),0或小于0表示不设置Chuncked模式
- * @return this
- * @since 4.6.5
- */
- public HttpRequest setChunkedStreamingMode(final int blockSize) {
- config.setBlockSize(blockSize);
- return this;
- }
-
- /**
- * 执行Reuqest请求
- *
- * @return this
- */
- public HttpResponse execute() {
- return this.execute(false);
- }
-
- /**
- * 异步请求
- * 异步请求后获取的{@link HttpResponse} 为异步模式,执行完此方法后发送请求到服务器,但是并不立即读取响应内容。
- * 此时保持Http连接不关闭,直调用获取内容方法为止。
- *
- *
- * 一般执行完execute之后会把响应内容全部读出来放在一个 byte数组里,如果你响应的内容太多内存就爆了,此法是发送完请求不直接读响应内容,等有需要的时候读。
- *
- * @return 异步对象,使用get方法获取HttpResponse对象
- */
- public HttpResponse executeAsync() {
- return this.execute(true);
- }
-
- /**
- * 执行Reuqest请求
- *
- * @param isAsync 是否异步
- * @return this
- */
- public HttpResponse execute(final boolean isAsync) {
- return doExecute(isAsync);
- }
-
- /**
- * 执行Request请求后,对响应内容后续处理
- * 处理结束后关闭连接
- *
- * @param consumer 响应内容处理函数
- * @since 5.7.8
- */
- public void then(final Consumer consumer) {
- thenFunction(httpResponse -> {
- consumer.accept(httpResponse);
- return null;
- });
- }
-
- /**
- * 执行Request请求后,对响应内容后续处理
- * 处理结束后关闭连接
- *
- * @param 结果类型
- * @param function 响应内容处理函数
- * @return 结果值
- * @since 5.8.5
- */
- public T thenFunction(final Function function) {
- try (final HttpResponse response = execute(true)) {
- return function.apply(response);
- }
- }
-
- /**
- * 简单验证,生成的头信息类似于:
- *
- * Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
- *
- *
- * @param username 用户名
- * @param password 密码
- * @return this
- */
- public HttpRequest basicAuth(final String username, final String password) {
- return auth(HttpUtil.buildBasicAuth(username, password, charset));
- }
-
- /**
- * 简单代理验证,生成的头信息类似于:
- *
- * Proxy-Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
- *
- *
- * @param username 用户名
- * @param password 密码
- * @return this
- * @since 5.4.6
- */
- public HttpRequest basicProxyAuth(final String username, final String password) {
- return proxyAuth(HttpUtil.buildBasicAuth(username, password, charset));
- }
-
- @Override
- public String toString() {
- final StringBuilder sb = StrUtil.builder();
- sb.append("Request Url: ").append(this.url.setCharset(this.charset)).append(StrUtil.CRLF);
- sb.append(super.toString());
- return sb.toString();
- }
-
- // ---------------------------------------------------------------- Private method start
-
- /**
- * 执行Reuqest请求
- *
- * @param isAsync 是否异步
- * @return this
- */
- private HttpResponse doExecute(final boolean isAsync) {
- // 初始化URL
- urlWithParamIfGet();
- // 初始化 connection
- initConnection();
- // 发送请求
- send();
-
- // 手动实现重定向
- HttpResponse httpResponse = sendRedirectIfPossible(isAsync);
-
- // 获取响应
- if (null == httpResponse) {
- httpResponse = new HttpResponse(this.httpConnection, this.config.isIgnoreEOFError(), this.charset, isAsync, isIgnoreResponseBody());
- }
-
- return httpResponse;
- }
-
- /**
- * 初始化网络连接
- */
- private void initConnection() {
- if (null != this.httpConnection) {
- // 执行下次请求时自动关闭上次请求(常用于转发)
- this.httpConnection.disconnectQuietly();
- }
-
- this.httpConnection = HttpConnection
- // issue#I50NHQ
- // 在生成正式URL前,设置自定义编码
- .of(this.url.setCharset(this.charset).toURL(this.urlHandler), config.proxy)//
- .setConnectTimeout(config.connectionTimeout)//
- .setReadTimeout(config.readTimeout)//
- .setMethod(this.method)//
- .setHttpsInfo(config.hostnameVerifier, config.ssf)//
- // 关闭JDK自动转发,采用手动转发方式
- .setInstanceFollowRedirects(false)
- // 流方式上传数据
- .setChunkedStreamingMode(config.blockSize)
- // 覆盖默认Header
- .header(this.headers, true);
-
- if (null != this.cookie) {
- // 当用户自定义Cookie时,全局Cookie自动失效
- this.httpConnection.cookie(this.cookie);
- } else {
- // 读取全局Cookie信息并附带到请求中
- GlobalCookieManager.add(this.httpConnection);
- }
-
- // 是否禁用缓存
- this.httpConnection.setDisableCache(config.isDisableCache);
- }
-
- /**
- * 对于GET请求将参数加到URL中
- * 此处不对URL中的特殊字符做单独编码
- * 对于非rest的GET请求,且处于重定向时,参数丢弃
- */
- private void urlWithParamIfGet() {
- if (Method.GET.equals(method) && false == this.isRest && this.redirectCount <= 0) {
- UrlQuery query = this.url.getQuery();
- if (null == query) {
- query = new UrlQuery();
- this.url.setQuery(query);
- }
-
- // 优先使用body形式的参数,不存在使用form
- if (ArrayUtil.isNotEmpty(this.bodyBytes)) {
- query.parse(StrUtil.str(this.bodyBytes, this.charset), this.charset);
- } else {
- query.addAll(this.form);
- }
- }
- }
-
- /**
- * 调用转发,如果需要转发返回转发结果,否则返回{@code null}
- *
- * @param isAsync 是否异步
- * @return {@link HttpResponse},无转发返回 {@code null}
- */
- private HttpResponse sendRedirectIfPossible(final boolean isAsync) {
- // 手动实现重定向
- if (config.maxRedirectCount > 0) {
- final int code;
- try {
- code = httpConnection.getCode();
- } catch (final IOException e) {
- // 错误时静默关闭连接
- this.httpConnection.disconnectQuietly();
- throw new HttpException(e);
- }
-
- if (code != HttpURLConnection.HTTP_OK) {
- if (HttpStatus.isRedirected(code)) {
-
- final UrlBuilder redirectUrl;
- String location = httpConnection.header(Header.LOCATION);
- if (false == HttpUtil.isHttp(location) && false == HttpUtil.isHttps(location)) {
- // issue#I5TPSY
- // location可能为相对路径
- if (false == location.startsWith("/")) {
- location = StrUtil.addSuffixIfNot(this.url.getPathStr(), "/") + location;
- }
- redirectUrl = UrlBuilder.of(this.url.getScheme(), this.url.getHost(), this.url.getPort()
- , location, null, null, this.charset);
- } else {
- redirectUrl = UrlBuilder.ofHttpWithoutEncode(location);
- }
-
- setUrl(redirectUrl);
- if (redirectCount < config.maxRedirectCount) {
- redirectCount++;
- return doExecute(isAsync);
- }
- }
- }
- }
- return null;
- }
-
- /**
- * 发送数据流
- *
- * @throws IORuntimeException IO异常
- */
- private void send() throws IORuntimeException {
- try {
- if (Method.POST.equals(this.method) //
- || Method.PUT.equals(this.method) //
- || Method.DELETE.equals(this.method) //
- || this.isRest) {
- if (isMultipart()) {
- sendMultipart(); // 文件上传表单
- } else {
- sendFormUrlEncoded();// 普通表单
- }
- } else {
- this.httpConnection.connect();
- }
- } catch (final IOException e) {
- // 异常时关闭连接
- this.httpConnection.disconnectQuietly();
- throw new IORuntimeException(e);
- }
- }
-
- /**
- * 发送普通表单
- * 发送数据后自动关闭输出流
- *
- * @throws IOException IO异常
- */
- private void sendFormUrlEncoded() throws IOException {
- if (StrUtil.isBlank(this.header(Header.CONTENT_TYPE))) {
- // 如果未自定义Content-Type,使用默认的application/x-www-form-urlencoded
- this.httpConnection.header(Header.CONTENT_TYPE, ContentType.FORM_URLENCODED.toString(this.charset), true);
- }
-
- // Write的时候会优先使用body中的内容,write时自动关闭OutputStream
- final HttpBody body;
- if (ArrayUtil.isNotEmpty(this.bodyBytes)) {
- body = BytesBody.of(this.bodyBytes);
- } else {
- body = UrlEncodedFormBody.of(this.form, this.charset);
- }
- body.writeClose(this.httpConnection.getOutputStream());
- }
-
- /**
- * 发送多组件请求(例如包含文件的表单)
- * 发送数据后自动关闭输出流
- *
- * @throws IOException IO异常
- */
- private void sendMultipart() throws IOException {
- final MultipartBody multipartBody = MultipartBody.of(this.form, this.charset);
- //设置表单类型为Multipart(文件上传)
- this.httpConnection.header(Header.CONTENT_TYPE, multipartBody.getContentType(), true);
- multipartBody.writeClose(this.httpConnection.getOutputStream());
- }
-
- /**
- * 是否忽略读取响应body部分
- * HEAD、CONNECT、OPTIONS、TRACE方法将不读取响应体
- *
- * @return 是否需要忽略响应body部分
- * @since 3.1.2
- */
- private boolean isIgnoreResponseBody() {
- return Method.HEAD == this.method //
- || Method.CONNECT == this.method //
- || Method.OPTIONS == this.method //
- || Method.TRACE == this.method;
- }
-
- /**
- * 判断是否为multipart/form-data表单,条件如下:
- *
- *
- * 1. 存在资源对象(fileForm非空)
- * 2. 用户自定义头为multipart/form-data开头
- *
- *
- * @return 是否为multipart/form-data表单
- * @since 5.3.5
- */
- private boolean isMultipart() {
- if (this.isMultiPart) {
- return true;
- }
-
- final String contentType = header(Header.CONTENT_TYPE);
- return StrUtil.isNotEmpty(contentType) &&
- contentType.startsWith(ContentType.MULTIPART.getValue());
- }
-
- /**
- * 将参数加入到form中,如果form为空,新建之。
- *
- * @param name 表单属性名
- * @param value 属性值
- * @return this
- */
- private HttpRequest putToForm(final String name, final Object value) {
- if (null == name || null == value) {
- return this;
- }
- if (null == this.form) {
- this.form = new TableMap<>(16);
- }
- this.form.put(name, value);
- return this;
- }
- // ---------------------------------------------------------------- Private method end
-
-}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpResponse.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpResponse.java
index 5240a651a..d22002d39 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpResponse.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/HttpResponse.java
@@ -95,6 +95,7 @@ public class HttpResponse implements Response, Closeable {
*
* @return Headers Map
*/
+ @Override
public Map> headers() {
return Collections.unmodifiableMap(headers);
}
@@ -234,7 +235,6 @@ public class HttpResponse implements Response, Closeable {
*
* @throws HttpException IO异常
*/
- @SuppressWarnings("resource")
private void init(final boolean isAsync, final boolean isIgnoreBody) throws HttpException {
// 获取响应状态码
try {
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/JdkClientEngine.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/JdkClientEngine.java
index b6e570438..0541fbf60 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/JdkClientEngine.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/jdk/JdkClientEngine.java
@@ -3,6 +3,7 @@ package cn.hutool.http.client.engine.jdk;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.text.StrUtil;
+import cn.hutool.core.util.ObjUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.client.ClientConfig;
@@ -25,7 +26,7 @@ import java.net.HttpURLConnection;
*/
public class JdkClientEngine implements ClientEngine {
- private final ClientConfig config;
+ private ClientConfig config;
private HttpConnection conn;
/**
* 重定向次数计数器,内部使用
@@ -35,13 +36,21 @@ public class JdkClientEngine implements ClientEngine {
/**
* 构造
*/
- public JdkClientEngine() {
- this.config = ClientConfig.of();
+ public JdkClientEngine() {}
+
+ @Override
+ public JdkClientEngine setConfig(final ClientConfig config) {
+ this.config = config;
+ if(null != this.conn){
+ this.conn.disconnectQuietly();
+ this.conn = null;
+ }
+ return this;
}
@Override
public Response send(final Request message) {
- return send(message, false);
+ return send(message, true);
}
/**
@@ -113,8 +122,10 @@ public class JdkClientEngine implements ClientEngine {
* @return {@link HttpConnection}
*/
private HttpConnection buildConn(final Request message) {
+ final ClientConfig config = ObjUtil.defaultIfNull(this.config, ClientConfig::of);
+
final HttpConnection conn = HttpConnection
- .of(message.url().toURL(), config.proxy)
+ .of(message.url().toURL(), config.getProxy())
.setConnectTimeout(config.getConnectionTimeout())
.setReadTimeout(config.getReadTimeout())
.setMethod(message.method())//
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpEngine.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpEngine.java
index 9b97237dc..e3c6fbc13 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpEngine.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpEngine.java
@@ -1,6 +1,8 @@
package cn.hutool.http.client.engine.okhttp;
import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.util.ObjUtil;
+import cn.hutool.http.client.ClientConfig;
import cn.hutool.http.client.ClientEngine;
import cn.hutool.http.client.Request;
import cn.hutool.http.client.Response;
@@ -8,6 +10,8 @@ import okhttp3.OkHttpClient;
import okhttp3.internal.http.HttpMethod;
import java.io.IOException;
+import java.net.Proxy;
+import java.util.concurrent.TimeUnit;
/**
* OkHttp3客户端引擎封装
@@ -17,7 +21,8 @@ import java.io.IOException;
*/
public class OkHttpEngine implements ClientEngine {
- private final OkHttpClient client;
+ private ClientConfig config;
+ private OkHttpClient client;
/**
* 构造
@@ -26,8 +31,18 @@ public class OkHttpEngine implements ClientEngine {
this.client = new OkHttpClient();
}
+ @Override
+ public OkHttpEngine setConfig(final ClientConfig config) {
+ this.config = config;
+ // 重置客户端
+ this.client = null;
+ return this;
+ }
+
@Override
public Response send(final Request message) {
+ initEngine();
+
final okhttp3.Response response;
try {
response = client.newCall(buildRequest(message)).execute();
@@ -48,6 +63,28 @@ public class OkHttpEngine implements ClientEngine {
// ignore
}
+ /**
+ * 初始化引擎
+ */
+ private void initEngine() {
+ if (null != this.client) {
+ return;
+ }
+
+ final ClientConfig config = ObjUtil.defaultIfNull(this.config, ClientConfig::of);
+ final OkHttpClient.Builder builder = new OkHttpClient.Builder()
+ .connectTimeout(config.getConnectionTimeout(), TimeUnit.MILLISECONDS)
+ .readTimeout(config.getReadTimeout(), TimeUnit.MILLISECONDS);
+
+ // 设置代理
+ final Proxy proxy = config.getProxy();
+ if(null != proxy){
+ builder.proxy(proxy);
+ }
+
+ this.client = builder.build();
+ }
+
/**
* 构建请求体
*
@@ -59,9 +96,9 @@ public class OkHttpEngine implements ClientEngine {
.url(message.url().toURL());
final String method = message.method().name();
- if(HttpMethod.permitsRequestBody(method)){
+ if (HttpMethod.permitsRequestBody(method)) {
builder.method(method, new OkHttpRequestBody(message.body()));
- }else{
+ } else {
builder.method(method, null);
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpResponse.java b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpResponse.java
index 5e1808f85..c5d13e56d 100755
--- a/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpResponse.java
+++ b/hutool-http/src/main/java/cn/hutool/http/client/engine/okhttp/OkHttpResponse.java
@@ -3,11 +3,18 @@ package cn.hutool.http.client.engine.okhttp;
import cn.hutool.core.io.stream.EmptyInputStream;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.http.client.Response;
+import kotlin.Pair;
+import okhttp3.Headers;
import okhttp3.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;
/**
* OkHttp3的{@link okhttp3.Response} 响应包装
@@ -41,6 +48,17 @@ public class OkHttpResponse implements Response {
return rawRes.header(name);
}
+ @Override
+ public Map> headers() {
+ final Headers headers = rawRes.headers();
+ final HashMap> result = new LinkedHashMap<>(headers.size(), 1);
+ for (final Pair extends String, ? extends String> header : headers) {
+ final List valueList = result.computeIfAbsent(header.getFirst(), k -> new ArrayList<>());
+ valueList.add(header.getSecond());
+ }
+ return result;
+ }
+
@Override
public Charset charset() {
return ObjUtil.defaultIfNull(Response.super.charset(), requestCharset);
diff --git a/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java b/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java
index 9cec2c3d9..12deeb661 100644
--- a/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java
+++ b/hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java
@@ -8,6 +8,7 @@ import cn.hutool.core.map.multi.ListValueMap;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.net.multipart.MultipartFormData;
import cn.hutool.core.net.multipart.UploadSetting;
+import cn.hutool.core.net.url.UrlQueryUtil;
import cn.hutool.core.text.StrUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
@@ -329,7 +330,7 @@ public class HttpServerRequest extends HttpServerBase {
//解析URL中的参数
final String query = getQuery();
if(StrUtil.isNotBlank(query)){
- this.paramsCache.putAll(HttpUtil.decodeParams(query, charset));
+ this.paramsCache.putAll(UrlQueryUtil.decodeQueryList(query, charset));
}
// 解析multipart中的参数
@@ -339,7 +340,7 @@ public class HttpServerRequest extends HttpServerBase {
// 解析body中的参数
final String body = getBody();
if(StrUtil.isNotBlank(body)){
- this.paramsCache.putAll(HttpUtil.decodeParams(body, charset));
+ this.paramsCache.putAll(UrlQueryUtil.decodeQueryList(body, charset));
}
}
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/webservice/SoapClient.java b/hutool-http/src/main/java/cn/hutool/http/webservice/SoapClient.java
index 0fe866a66..5754f581b 100644
--- a/hutool-http/src/main/java/cn/hutool/http/webservice/SoapClient.java
+++ b/hutool-http/src/main/java/cn/hutool/http/webservice/SoapClient.java
@@ -3,13 +3,14 @@ package cn.hutool.http.webservice;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.text.StrUtil;
+import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.XmlUtil;
-import cn.hutool.http.client.engine.jdk.HttpBase;
import cn.hutool.http.HttpGlobalConfig;
-import cn.hutool.http.client.engine.jdk.HttpRequest;
-import cn.hutool.http.client.engine.jdk.HttpResponse;
+import cn.hutool.http.client.Request;
+import cn.hutool.http.client.Response;
+import cn.hutool.http.client.engine.ClientEngineFactory;
+import cn.hutool.http.client.engine.jdk.HttpBase;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
@@ -542,7 +543,7 @@ public class SoapClient extends HttpBase {
* @return 返回结果
*/
public SOAPMessage sendForMessage() {
- final HttpResponse res = sendForResponse();
+ final Response res = sendForResponse();
final MimeHeaders headers = new MimeHeaders();
for (final Entry> entry : res.headers().entrySet()) {
if (StrUtil.isNotEmpty(entry.getKey())) {
@@ -585,15 +586,13 @@ public class SoapClient extends HttpBase {
*
* @return 响应对象
*/
- public HttpResponse sendForResponse() {
- return HttpRequest.post(this.url)//
- .setFollowRedirects(true)//
- .setConnectionTimeout(this.connectionTimeout)
- .setReadTimeout(this.readTimeout)
- .contentType(getXmlContentType())//
- .header(this.headers())
- .body(getMsgStr(false))//
- .executeAsync();
+ public Response sendForResponse() {
+ final Request request = Request.of(this.url)
+ .setMaxRedirectCount(2)
+ .contentType(getXmlContentType())
+ .header(this.headers, false)
+ .body(getMsgStr(false));
+ return ClientEngineFactory.get().send(request);
}
/**
diff --git a/hutool-http/src/test/java/cn/hutool/http/DownloadTest.java b/hutool-http/src/test/java/cn/hutool/http/DownloadTest.java
index 8460794ae..15e73e42d 100644
--- a/hutool-http/src/test/java/cn/hutool/http/DownloadTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/DownloadTest.java
@@ -1,14 +1,19 @@
package cn.hutool.http;
+import cn.hutool.core.codec.BaseN.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.StreamProgress;
import cn.hutool.core.lang.Console;
-import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.http.client.HttpDownloader;
+import cn.hutool.http.client.Request;
+import cn.hutool.http.client.engine.ClientEngineFactory;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
@@ -26,7 +31,7 @@ public class DownloadTest {
@Ignore
public void downloadPicTest() {
final String url = "http://wx.qlogo.cn/mmopen/vKhlFcibVUtNBVDjcIowlg0X8aJfHXrTNCEFBukWVH9ta99pfEN88lU39MKspCUCOP3yrFBH3y2NbV7sYtIIlon8XxLwAEqv2/0";
- HttpUtil.downloadFile(url, "e:/pic/t3.jpg");
+ HttpDownloader.downloadFile(url, new File("e:/pic/t3.jpg"));
Console.log("ok");
}
@@ -34,13 +39,14 @@ public class DownloadTest {
@Ignore
public void downloadSizeTest() {
final String url = "https://res.t-io.org/im/upload/img/67/8948/1119501/88097554/74541310922/85/231910/366466 - 副本.jpg";
- HttpRequest.get(url).setSSLProtocol("TLSv1.2").executeAsync().body().write("e:/pic/366466.jpg");
+ ClientEngineFactory.get().send(Request.of(url)).body().write("e:/pic/366466.jpg");
+ //HttpRequest.get(url).setSSLProtocol("TLSv1.2").executeAsync().body().write("e:/pic/366466.jpg");
}
@Test
@Ignore
public void downloadTest1() {
- final File size = HttpUtil.downloadFile("http://explorer.bbfriend.com/crossdomain.xml", "e:/temp/");
+ final File size = HttpDownloader.downloadFile("http://explorer.bbfriend.com/crossdomain.xml", new File("e:/temp/"));
System.out.println("Download size: " + size);
}
@@ -48,7 +54,7 @@ public class DownloadTest {
@Ignore
public void downloadTest() {
// 带进度显示的文件下载
- HttpUtil.downloadFile("http://mirrors.sohu.com/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2009.iso", FileUtil.file("d:/"), new StreamProgress() {
+ HttpDownloader.downloadFile("http://mirrors.sohu.com/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2009.iso", FileUtil.file("d:/"), -1, new StreamProgress() {
final long time = System.currentTimeMillis();
@@ -73,7 +79,7 @@ public class DownloadTest {
@Test
@Ignore
public void downloadFileFromUrlTest1() {
- final File file = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", "d:/download/temp");
+ final File file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", new File("d:/download/temp"));
Assert.assertNotNull(file);
Assert.assertTrue(file.isFile());
Assert.assertTrue(file.length() > 0);
@@ -84,7 +90,7 @@ public class DownloadTest {
public void downloadFileFromUrlTest2() {
File file = null;
try {
- file = HttpUtil.downloadFileFromUrl("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.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() {
@Override
public void start() {
System.out.println("start");
@@ -118,7 +124,7 @@ public class DownloadTest {
public void downloadFileFromUrlTest3() {
File file = null;
try {
- file = HttpUtil.downloadFileFromUrl("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar", FileUtil.file("d:/download/temp"), new StreamProgress() {
+ 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() {
@Override
public void start() {
System.out.println("start");
@@ -150,7 +156,7 @@ public class DownloadTest {
public void downloadFileFromUrlTest4() {
File file = null;
try {
- file = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"), 1);
+ file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"), 1);
Assert.assertNotNull(file);
Assert.assertTrue(file.exists());
@@ -170,7 +176,7 @@ public class DownloadTest {
public void downloadFileFromUrlTest5() {
File file = null;
try {
- file = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp", UUID.randomUUID().toString()));
+ file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp", UUID.randomUUID().toString()));
Assert.assertNotNull(file);
Assert.assertTrue(file.exists());
@@ -182,7 +188,7 @@ public class DownloadTest {
File file1 = null;
try {
- file1 = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"));
+ file1 = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"));
Assert.assertNotNull(file1);
Assert.assertTrue(file1.exists());
@@ -200,7 +206,34 @@ public class DownloadTest {
final String url = "https://download.teamviewer.com/download/TeamViewer_Setup_x64.exe";
HttpGlobalConfig.setMaxRedirectCount(20);
final Path temp = Files.createTempFile("tmp", ".exe");
- final File file = HttpUtil.downloadFileFromUrl(url, temp.toFile());
+ final File file = HttpDownloader.downloadFile(url, temp.toFile());
Console.log(file.length());
}
+
+ @Test
+ @Ignore
+ 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
+ @Ignore
+ 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
+ @Ignore
+ 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));
+ }
}
diff --git a/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java b/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java
index 2c96ed6f4..c2beb362e 100644
--- a/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java
@@ -4,11 +4,11 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Console;
-import cn.hutool.core.net.ssl.SSLProtocols;
+import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.util.CharsetUtil;
-import cn.hutool.http.client.engine.jdk.HttpRequest;
-import cn.hutool.http.client.engine.jdk.HttpResponse;
+import cn.hutool.http.client.Request;
+import cn.hutool.http.client.Response;
import cn.hutool.http.meta.Header;
import cn.hutool.http.meta.Method;
import org.junit.Ignore;
@@ -20,7 +20,7 @@ import java.util.List;
import java.util.Map;
/**
- * {@link HttpRequest}单元测试
+ * {@link Request}单元测试
*
* @author Looly
*/
@@ -28,18 +28,11 @@ import java.util.Map;
public class HttpRequestTest {
final String url = "http://photo.qzone.qq.com/fcgi-bin/fcg_list_album?uin=88888&outstyle=2";
- @Test
- @Ignore
- public void getHttpsTest() {
- final String body = HttpRequest.get("https://www.hutool.cn/").timeout(10).execute().bodyStr();
- Console.log(body);
- }
-
@Test
@Ignore
public void getHttpsThenTest() {
- HttpRequest
- .get("https://hutool.cn")
+ Request.of("https://hutool.cn")
+ .send()
.then(response -> Console.log(response.body()));
}
@@ -47,9 +40,9 @@ public class HttpRequestTest {
@Ignore
public void getCookiesTest() {
// 检查在Connection关闭情况下Cookie是否可以正常获取
- final HttpResponse res = HttpRequest.get("https://www.oschina.net/").execute();
+ final Response res = Request.of("https://www.oschina.net/").send();
final String body = res.bodyStr();
- Console.log(res.getCookies());
+ Console.log(res.getCookieStr());
Console.log(body);
}
@@ -58,14 +51,14 @@ public class HttpRequestTest {
public void toStringTest() {
final String url = "http://gc.ditu.aliyun.com/geocoding?ccc=你好";
- final HttpRequest request = HttpRequest.get(url).body("a=乌海");
+ final Request request = Request.of(url).body("a=乌海");
Console.log(request.toString());
}
@Test
@Ignore
public void asyncHeadTest() {
- final HttpResponse response = HttpRequest.head(url).execute();
+ final Response response = Request.of(url).method(Method.HEAD).send();
final Map> headers = response.headers();
Console.log(headers);
Console.log(response.body());
@@ -76,7 +69,7 @@ public class HttpRequestTest {
public void asyncGetTest() {
final StopWatch timer = DateUtil.createStopWatch();
timer.start();
- final HttpResponse body = HttpRequest.get(url).charset("GBK").executeAsync();
+ final Response body = Request.of(url).charset(CharsetUtil.GBK).send();
timer.stop();
final long interval = timer.getLastTaskTimeMillis();
timer.start();
@@ -91,7 +84,7 @@ public class HttpRequestTest {
public void syncGetTest() {
final StopWatch timer = DateUtil.createStopWatch();
timer.start();
- final HttpResponse body = HttpRequest.get(url).charset("GBK").execute();
+ final Response body = Request.of(url).charset(CharsetUtil.GBK).send();
timer.stop();
final long interval = timer.getLastTaskTimeMillis();
@@ -102,26 +95,12 @@ public class HttpRequestTest {
Console.log("Async response spend {}ms, body spend {}ms", interval, interval2);
}
- @Test
- @Ignore
- public void customGetTest() {
- // 自定义构建HTTP GET请求,发送Http GET请求,针对HTTPS安全加密,可以自定义SSL
- final HttpRequest request = HttpRequest.get(url)
- // 自定义返回编码
- .charset(CharsetUtil.GBK)
- // 禁用缓存
- .disableCache()
- // 自定义SSL版本
- .setSSLProtocol(SSLProtocols.TLSv12);
- Console.log(request.execute().body());
- }
-
@Test
@Ignore
public void getDeflateTest() {
- final HttpResponse res = HttpRequest.get("https://comment.bilibili.com/67573272.xml")
+ final Response res = Request.of("https://comment.bilibili.com/67573272.xml")
.header(Header.ACCEPT_ENCODING, "deflate")
- .execute();
+ .send();
Console.log(res.header(Header.CONTENT_ENCODING));
Console.log(res.body());
}
@@ -129,7 +108,7 @@ public class HttpRequestTest {
@Test
@Ignore
public void bodyTest() {
- final String ddddd1 = HttpRequest.get("https://baijiahao.baidu.com/s").body("id=1625528941695652600").execute().bodyStr();
+ final String ddddd1 = Request.of("https://baijiahao.baidu.com/s").body("id=1625528941695652600").send().bodyStr();
Console.log(ddddd1);
}
@@ -149,9 +128,9 @@ public class HttpRequestTest {
map.put("size", "2");
map.put("sizes", list);
- HttpRequest
- .get("http://localhost:8888/get")
- .form(map)
+ Request
+ .of("http://localhost:8888/get")
+ .form(map).send()
.then(resp -> Console.log(resp.body()));
}
@@ -159,9 +138,9 @@ public class HttpRequestTest {
@Ignore
public void getWithoutEncodeTest() {
final String url = "https://img-cloud.voc.com.cn/140/2020/09/03/c3d41b93e0d32138574af8e8b50928b376ca5ba61599127028157.png?imageMogr2/auto-orient/thumbnail/500&pid=259848";
- final HttpRequest get = HttpUtil.createGet(url);
- Console.log(get.getUrl());
- final HttpResponse execute = get.execute();
+ final Request get = Request.of(url);
+ Console.log(get.url());
+ final Response execute = get.send();
Console.log(execute.body());
}
@@ -176,11 +155,11 @@ public class HttpRequestTest {
// 方式1:全局设置
HttpGlobalConfig.setMaxRedirectCount(1);
- HttpResponse execute = HttpRequest.get(url).execute();
+ Response execute = Request.of(url).send();
Console.log(execute.getStatus(), execute.header(Header.LOCATION));
// 方式2,单独设置
- execute = HttpRequest.get(url).setMaxRedirectCount(1).execute();
+ execute = Request.of(url).setMaxRedirectCount(1).send();
Console.log(execute.getStatus(), execute.header(Header.LOCATION));
}
@@ -190,8 +169,8 @@ public class HttpRequestTest {
final String url = "https://postman-echo.com/get";
final Map map = new HashMap<>();
map.put("aaa", "application+1@qqq.com");
- final HttpRequest request =HttpUtil.createGet(url).form(map);
- Console.log(request.execute().body());
+ final Request request =Request.of(url).form(map);
+ Console.log(request.send().body());
}
@Test
@@ -200,51 +179,51 @@ public class HttpRequestTest {
final UrlBuilder urlBuilder = new UrlBuilder();
urlBuilder.setScheme("https").setHost("hutool.cn");
- final HttpRequest httpRequest = new HttpRequest(urlBuilder);
- httpRequest.method(Method.GET).execute();
+ final Request httpRequest = Request.of(urlBuilder);
+ httpRequest.method(Method.GET).send();
}
@Test
@Ignore
public void getCookieTest(){
- final HttpResponse execute = HttpRequest.get("http://localhost:8888/getCookier").execute();
- Console.log(execute.getCookies());
+ final Response execute = Request.of("http://localhost:8888/getCookier").send();
+ Console.log(execute.getCookieStr());
}
@Test
public void optionsTest() {
- final HttpRequest options = HttpRequest.options("https://hutool.cn");
+ final Request options = Request.of("https://hutool.cn").method(Method.OPTIONS);
Assert.notNull(options.toString());
}
@Test
public void deleteTest() {
- final HttpRequest options = HttpRequest.delete("https://hutool.cn");
+ final Request options = Request.of("https://hutool.cn").method(Method.DELETE);
Assert.notNull(options.toString());
}
@Test
public void traceTest() {
- final HttpRequest options = HttpRequest.trace("https://hutool.cn");
+ final Request options = Request.of("https://hutool.cn").method(Method.TRACE);
Assert.notNull(options.toString());
}
@Test
public void getToStringTest() {
- final HttpRequest a = HttpRequest.get("https://hutool.cn/").form("a", 1);
+ final Request a = Request.of("https://hutool.cn/").form(MapUtil.of("a", 1));
Assert.notNull(a.toString());
}
@Test
public void postToStringTest() {
- final HttpRequest a = HttpRequest.post("https://hutool.cn/").form("a", 1);
+ final Request a = Request.of("https://hutool.cn/").method(Method.POST).form(MapUtil.of("a", 1));
Console.log(a.toString());
}
@Test
@Ignore
public void issueI5Y68WTest() {
- final HttpResponse httpResponse = HttpRequest.get("http://82.157.17.173:8100/app/getAddress").execute();
+ final Response httpResponse = Request.of("http://82.157.17.173:8100/app/getAddress").send();
Console.log(httpResponse.body());
}
}
diff --git a/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java b/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java
index 6ab2686f0..b39864316 100755
--- a/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/HttpUtilTest.java
@@ -1,18 +1,16 @@
package cn.hutool.http;
-import cn.hutool.core.codec.BaseN.Base64;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Console;
-import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.regex.ReUtil;
-import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.core.util.CharsetUtil;
+import cn.hutool.http.client.Request;
import cn.hutool.http.meta.Header;
import cn.hutool.http.meta.Method;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
-import java.io.ByteArrayOutputStream;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -35,23 +33,13 @@ public class HttpUtilTest {
Assert.assertFalse(HttpUtil.isHttps("ftp://aaa.bbb"));
}
- @Test
- @Ignore
- public void postTest() {
- final String result = HttpUtil.createPost("api.uhaozu.com/goods/description/1120448506")
- .charset(CharsetUtil.NAME_UTF_8)
- .execute().bodyStr();
- Console.log(result);
- }
-
@Test
@Ignore
public void postTest2() {
// 某些接口对Accept头有特殊要求,此处自定义头
- final String result = HttpUtil
- .createPost("http://cmp.ishanghome.com/cmp/v1/community/queryClusterCommunity")
- .header(Header.ACCEPT, "*/*")
- .execute()
+ final String result = HttpUtil.send(Request
+ .of("http://cmp.ishanghome.com/cmp/v1/community/queryClusterCommunity")
+ .header(Header.ACCEPT, "*/*"))
.bodyStr();
Console.log(result);
}
@@ -68,9 +56,9 @@ public class HttpUtilTest {
public void getTest2() {
// 此链接较为特殊,User-Agent去掉后进入一个JS跳转页面,如果设置了,需要开启302跳转
// 自定义的默认header无效
- final String result = HttpRequest
- .get("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=101457313&redirect_uri=http%3A%2F%2Fwww.benmovip.com%2Fpay-cloud%2Fqqlogin%2FgetCode&state=ok")
- .removeHeader(Header.USER_AGENT).execute().bodyStr();
+ final String result = Request
+ .of("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=101457313&redirect_uri=http%3A%2F%2Fwww.benmovip.com%2Fpay-cloud%2Fqqlogin%2FgetCode&state=ok")
+ .header(Header.USER_AGENT, null).send().bodyStr();
Console.log(result);
}
@@ -86,38 +74,17 @@ public class HttpUtilTest {
@Ignore
public void getTest4() {
// 测试url中带有空格的情况
- final byte[] str = HttpRequest.get("http://img01.fs.yiban.cn/mobile/2D0Y71").execute().bodyBytes();
+ final byte[] str = Request.of("http://img01.fs.yiban.cn/mobile/2D0Y71").send().bodyBytes();
FileUtil.writeBytes(str, "f:/test/2D.jpg");
Console.log(str);
}
- @Test
- @Ignore
- public void getTest5() {
- String url2 = "http://storage.chancecloud.com.cn/20200413_%E7%B2%A4B12313_386.pdf";
- final ByteArrayOutputStream os2 = new ByteArrayOutputStream();
- HttpUtil.download(url2, os2, false);
-
- url2 = "http://storage.chancecloud.com.cn/20200413_粤B12313_386.pdf";
- HttpUtil.download(url2, os2, false);
- }
-
@Test
@Ignore
public void get12306Test() {
- HttpRequest.get("https://kyfw.12306.cn/otn/")
- .setFollowRedirects(true)
- .then(response -> Console.log(response.body()));
- }
-
- @Test
- @Ignore
- public void downloadStringTest() {
- final String url = "https://www.baidu.com";
- // 从远程直接读取字符串,需要自定义编码,直接调用JDK方法
- final String content2 = HttpUtil.downloadString(url, CharsetUtil.NAME_UTF_8);
- Console.log(content2);
+ HttpUtil.send(Request.of("https://kyfw.12306.cn/otn/").setMaxRedirectCount(2))
+ .then(response -> Console.log(response.bodyStr()));
}
@Test
@@ -138,141 +105,10 @@ public class HttpUtilTest {
}
@Test
- public void decodeParamsTest() {
- final String paramsStr = "uuuu=0&a=b&c=%3F%23%40!%24%25%5E%26%3Ddsssss555555";
- final Map> map = HttpUtil.decodeParams(paramsStr, CharsetUtil.NAME_UTF_8);
- Assert.assertEquals("0", map.get("uuuu").get(0));
- Assert.assertEquals("b", map.get("a").get(0));
- Assert.assertEquals("?#@!$%^&=dsssss555555", map.get("c").get(0));
- }
-
- @Test
- public void decodeParamMapTest() {
- // 参数值存在分界标记等号时
- final Map paramMap = HttpUtil.decodeParamMap("https://www.xxx.com/api.action?aa=123&f_token=NzBkMjQxNDM1MDVlMDliZTk1OTU3ZDI1OTI0NTBiOWQ=", CharsetUtil.UTF_8);
- Assert.assertEquals("123",paramMap.get("aa"));
- Assert.assertEquals("NzBkMjQxNDM1MDVlMDliZTk1OTU3ZDI1OTI0NTBiOWQ=",paramMap.get("f_token"));
- }
-
- @Test
- public void toParamsTest() {
- final String paramsStr = "uuuu=0&a=b&c=3Ddsssss555555";
- final Map> map = HttpUtil.decodeParams(paramsStr, CharsetUtil.NAME_UTF_8);
-
- final String encodedParams = HttpUtil.toParams(map);
- Assert.assertEquals(paramsStr, encodedParams);
- }
-
- @Test
- public void encodeParamTest() {
- // ?单独存在去除之,&单位位于末尾去除之
- String paramsStr = "?a=b&c=d&";
- String encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
- Assert.assertEquals("a=b&c=d", encode);
-
- // url不参与转码
- paramsStr = "http://www.abc.dd?a=b&c=d&";
- encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
- Assert.assertEquals("http://www.abc.dd?a=b&c=d", encode);
-
- // b=b中的=被当作值的一部分,不做encode
- paramsStr = "a=b=b&c=d&";
- encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
- Assert.assertEquals("a=b=b&c=d", encode);
-
- // =d的情况被处理为key为空
- paramsStr = "a=bbb&c=d&=d";
- encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
- Assert.assertEquals("a=bbb&c=d&=d", encode);
-
- // d=的情况被处理为value为空
- paramsStr = "a=bbb&c=d&d=";
- encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
- Assert.assertEquals("a=bbb&c=d&d=", encode);
-
- // 多个&&被处理为单个,相当于空条件
- paramsStr = "a=bbb&c=d&&&d=";
- encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
- Assert.assertEquals("a=bbb&c=d&d=", encode);
-
- // &d&相当于只有键,无值得情况
- paramsStr = "a=bbb&c=d&d&";
- encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
- Assert.assertEquals("a=bbb&c=d&d=", encode);
-
- // 中文的键和值被编码
- paramsStr = "a=bbb&c=你好&哈喽&";
- encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
- Assert.assertEquals("a=bbb&c=%E4%BD%A0%E5%A5%BD&%E5%93%88%E5%96%BD=", encode);
-
- // URL原样输出
- paramsStr = "https://www.hutool.cn/";
- encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
- Assert.assertEquals(paramsStr, encode);
-
- // URL原样输出
- paramsStr = "https://www.hutool.cn/?";
- encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
- Assert.assertEquals("https://www.hutool.cn/", encode);
- }
-
- @Test
- public void decodeParamTest() {
- // 开头的?被去除
- String a = "?a=b&c=d&";
- Map> map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
- Assert.assertEquals("b", map.get("a").get(0));
- Assert.assertEquals("d", map.get("c").get(0));
-
- // =e被当作空为key,e为value
- a = "?a=b&c=d&=e";
- map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
- Assert.assertEquals("b", map.get("a").get(0));
- Assert.assertEquals("d", map.get("c").get(0));
- Assert.assertEquals("e", map.get("").get(0));
-
- // 多余的&去除
- a = "?a=b&c=d&=e&&&&";
- map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
- Assert.assertEquals("b", map.get("a").get(0));
- Assert.assertEquals("d", map.get("c").get(0));
- Assert.assertEquals("e", map.get("").get(0));
-
- // 值为空
- a = "?a=b&c=d&e=";
- map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
- Assert.assertEquals("b", map.get("a").get(0));
- Assert.assertEquals("d", map.get("c").get(0));
- Assert.assertEquals("", map.get("e").get(0));
-
- // &=被作为键和值都为空
- a = "a=b&c=d&=";
- map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
- Assert.assertEquals("b", map.get("a").get(0));
- Assert.assertEquals("d", map.get("c").get(0));
- Assert.assertEquals("", map.get("").get(0));
-
- // &e&这类单独的字符串被当作key
- a = "a=b&c=d&e&";
- map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
- Assert.assertEquals("b", map.get("a").get(0));
- Assert.assertEquals("d", map.get("c").get(0));
- Assert.assertNull(map.get("e").get(0));
- Assert.assertNull(map.get("").get(0));
-
- // 被编码的键和值被还原
- a = "a=bbb&c=%E4%BD%A0%E5%A5%BD&%E5%93%88%E5%96%BD=";
- map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
- Assert.assertEquals("bbb", map.get("a").get(0));
- Assert.assertEquals("你好", map.get("c").get(0));
- Assert.assertEquals("", map.get("哈喽").get(0));
- }
-
- @Test
- @Ignore
+ //@Ignore
public void patchTest() {
// 验证patch请求是否可用
- final String body = HttpRequest.patch("https://www.baidu.com").execute().bodyStr();
+ final String body = HttpUtil.send(Request.of("https://hutool.cn").method(Method.PATCH)).bodyStr();
Console.log(body);
}
@@ -315,18 +151,6 @@ public class HttpUtilTest {
Assert.assertEquals("utf-8", charsetName);
}
- @Test
- public void normalizeParamsTest() {
- final String encodeResult = HttpUtil.normalizeParams("参数", CharsetUtil.UTF_8);
- Assert.assertEquals("%E5%8F%82%E6%95%B0", encodeResult);
- }
-
- @Test
- public void normalizeBlankParamsTest() {
- final String encodeResult = HttpUtil.normalizeParams("", CharsetUtil.UTF_8);
- Assert.assertEquals("", encodeResult);
- }
-
@Test
public void getMimeTypeTest() {
final String mimeType = HttpUtil.getMimeType("aaa.aaa");
@@ -357,18 +181,10 @@ public class HttpUtilTest {
Console.log(s);
}
- @Test
- @Ignore
- public void gimg2Test(){
- final byte[] bytes = HttpUtil.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));
- }
-
@Test
@Ignore
public void acplayTest(){
- final String body = HttpRequest.get("https://api.acplay.net/api/v2/bangumi/9541")
- .execute().bodyStr();
+ final String body = HttpUtil.send(Request.of("https://api.acplay.net/api/v2/bangumi/9541")).bodyStr();
Console.log(body);
}
@@ -378,7 +194,7 @@ public class HttpUtilTest {
HttpGlobalConfig.setDecodeUrl(false);
final String url = "https://p3-sign.douyinpic.com/tos-cn-i-0813/f41afb2e79a94dcf80970affb9a69415~noop.webp?x-expires=1647738000&x-signature=%2Br1ekUCGjXiu50Y%2Bk0MO4ovulK8%3D&from=4257465056&s=PackSourceEnum_DOUYIN_REFLOW&se=false&sh=&sc=&l=2022021809224601020810013524310DD3&biz_tag=aweme_images";
- final HttpRequest request = HttpRequest.of(url).method(Method.GET);
- Console.log(request.execute().body());
+ final String body = HttpUtil.send(Request.of(url)).bodyStr();
+ Console.log(body);
}
}
diff --git a/hutool-http/src/test/java/cn/hutool/http/Issue2531Test.java b/hutool-http/src/test/java/cn/hutool/http/Issue2531Test.java
index 88d928c3f..90fe889a9 100755
--- a/hutool-http/src/test/java/cn/hutool/http/Issue2531Test.java
+++ b/hutool-http/src/test/java/cn/hutool/http/Issue2531Test.java
@@ -3,8 +3,8 @@ package cn.hutool.http;
import cn.hutool.core.lang.Console;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.url.UrlBuilder;
-import cn.hutool.http.client.engine.jdk.HttpRequest;
-import cn.hutool.http.client.engine.jdk.HttpResponse;
+import cn.hutool.http.client.Request;
+import cn.hutool.http.client.Response;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
@@ -23,10 +23,10 @@ public class Issue2531Test {
final String queryParam = MapUtil.join(map, "&", "=");//返回str=+123
Console.log(queryParam);
- final HttpRequest request = HttpUtil.createGet("http://localhost:8888/formTest?" + queryParam);
+ final Request request = Request.of("http://localhost:8888/formTest?" + queryParam);
//request.setUrl("http://localhost:8888/formTest" + "?" + queryParam);
//noinspection resource
- final HttpResponse execute = request.execute();
+ final Response execute = request.send();
Console.log(execute.body());
}
diff --git a/hutool-http/src/test/java/cn/hutool/http/IssueI5TPSYTest.java b/hutool-http/src/test/java/cn/hutool/http/IssueI5TPSYTest.java
index 6b8133729..8bfb5e705 100755
--- a/hutool-http/src/test/java/cn/hutool/http/IssueI5TPSYTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/IssueI5TPSYTest.java
@@ -1,7 +1,8 @@
package cn.hutool.http;
import cn.hutool.core.lang.Console;
-import cn.hutool.http.client.engine.jdk.HttpResponse;
+import cn.hutool.http.client.Request;
+import cn.hutool.http.client.Response;
import cn.hutool.http.meta.Header;
import org.junit.Ignore;
import org.junit.Test;
@@ -12,10 +13,10 @@ public class IssueI5TPSYTest {
@Ignore
public void redirectTest() {
final String url = "https://bsxt.gdzwfw.gov.cn/UnifiedReporting/auth/newIndex";
- final HttpResponse res = HttpUtil.createGet(url).setFollowRedirects(true)
+ final Response res = HttpUtil.send(Request.of(url)
+ .setMaxRedirectCount(2)
.header(Header.USER_AGENT, "PostmanRuntime/7.29.2")
- .cookie("jsessionid=s%3ANq6YTcIHQWrHkEqOSxiQNijDMhoFNV4_.h2MVD1CkW7sOZ60OSnPs7m4K%2FhENfYy%2FdzjKvSiZF4E")
- .execute();
+ .cookie("jsessionid=s%3ANq6YTcIHQWrHkEqOSxiQNijDMhoFNV4_.h2MVD1CkW7sOZ60OSnPs7m4K%2FhENfYy%2FdzjKvSiZF4E"));
Console.log(res.body());
}
}
diff --git a/hutool-http/src/test/java/cn/hutool/http/IssueI5WAV4Test.java b/hutool-http/src/test/java/cn/hutool/http/IssueI5WAV4Test.java
index dd324f36e..dc8687d10 100755
--- a/hutool-http/src/test/java/cn/hutool/http/IssueI5WAV4Test.java
+++ b/hutool-http/src/test/java/cn/hutool/http/IssueI5WAV4Test.java
@@ -1,6 +1,6 @@
package cn.hutool.http;
-import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.client.Request;
import cn.hutool.json.JSONUtil;
import org.junit.Ignore;
import org.junit.Test;
@@ -19,7 +19,8 @@ public class IssueI5WAV4Test {
map.put("flightID", 2879);
- final String body = HttpRequest.get("http://localhost:8884/api/test/testHttpUtilGetWithBody").body(JSONUtil.toJsonStr(map)).execute().bodyStr();
+ @SuppressWarnings("resource")
+ final String body = Request.of("http://localhost:8884/api/test/testHttpUtilGetWithBody").body(JSONUtil.toJsonStr(map)).send().bodyStr();
System.out.println("使用hutool返回结果:" + body);
}
}
diff --git a/hutool-http/src/test/java/cn/hutool/http/IssueI5XBCFTest.java b/hutool-http/src/test/java/cn/hutool/http/IssueI5XBCFTest.java
index 2dabccc15..43b0d5e5d 100755
--- a/hutool-http/src/test/java/cn/hutool/http/IssueI5XBCFTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/IssueI5XBCFTest.java
@@ -1,7 +1,8 @@
package cn.hutool.http;
import cn.hutool.core.lang.Console;
-import cn.hutool.http.client.engine.jdk.HttpResponse;
+import cn.hutool.http.client.Request;
+import cn.hutool.http.client.Response;
import cn.hutool.http.meta.Header;
import org.brotli.dec.BrotliInputStream;
import org.junit.Ignore;
@@ -14,9 +15,10 @@ public class IssueI5XBCFTest {
public void getTest() {
GlobalCompressStreamRegister.INSTANCE.register("br", BrotliInputStream.class);
- @SuppressWarnings("resource") final HttpResponse s = HttpUtil.createGet("https://static-exp1.licdn.com/sc/h/br/1cp0oqz322bdprj3qd4pojqix")
+ @SuppressWarnings("resource")
+ final Response s = Request.of("https://static-exp1.licdn.com/sc/h/br/1cp0oqz322bdprj3qd4pojqix")
.header(Header.ACCEPT_ENCODING, "br")
- .execute();
+ .send();
Console.log(s.body());
}
}
diff --git a/hutool-http/src/test/java/cn/hutool/http/RestTest.java b/hutool-http/src/test/java/cn/hutool/http/RestTest.java
index 5ccbba7af..555a36872 100644
--- a/hutool-http/src/test/java/cn/hutool/http/RestTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/RestTest.java
@@ -1,8 +1,9 @@
package cn.hutool.http;
import cn.hutool.core.lang.Console;
-import cn.hutool.http.client.engine.jdk.HttpRequest;
+import cn.hutool.http.client.Request;
import cn.hutool.http.meta.Header;
+import cn.hutool.http.meta.Method;
import cn.hutool.json.JSONUtil;
import org.junit.Assert;
import org.junit.Ignore;
@@ -18,21 +19,24 @@ public class RestTest {
@Test
public void contentTypeTest() {
- final HttpRequest request = HttpRequest.post("http://localhost:8090/rest/restTest/")//
+ final Request request = Request.of("http://localhost:8090/rest/restTest/")
+ .method(Method.POST)
.body(JSONUtil.ofObj()
.set("aaa", "aaaValue")
.set("键2", "值2").toString());
Assert.assertEquals("application/json;charset=UTF-8", request.header(Header.CONTENT_TYPE));
}
+ @SuppressWarnings("resource")
@Test
@Ignore
public void postTest() {
- final HttpRequest request = HttpRequest.post("http://localhost:8090/rest/restTest/")//
+ final Request request = Request.of("http://localhost:8090/rest/restTest/")
+ .method(Method.POST)
.body(JSONUtil.ofObj()
.set("aaa", "aaaValue")
.set("键2", "值2").toString());
- Console.log(request.execute().body());
+ Console.log(request.send().body());
}
@Test
@@ -47,25 +51,12 @@ public class RestTest {
@Test
@Ignore
public void getWithBodyTest() {
- final HttpRequest request = HttpRequest.get("http://localhost:8888/restTest")//
+ final Request request = Request.of("http://localhost:8888/restTest")//
.header(Header.CONTENT_TYPE, "application/json")
.body(JSONUtil.ofObj()
.set("aaa", "aaaValue")
.set("键2", "值2").toString());
- Console.log(request.execute().body());
- }
-
- @Test
- @Ignore
- public void getWithBodyTest2() {
- final HttpRequest request = HttpRequest.get("https://ad.oceanengine.com/open_api/2/advertiser/info/")//
- // Charles代理
- .setHttpProxy("localhost", 8888)
- .header("Access-Token","")
- .body(JSONUtil.ofObj()
- .set("advertiser_ids", new Long[] {1690657248243790L})
- .set("fields", new String[] {"id", "name", "status"}).toString());
- Console.log(request);
- Console.log(request.execute().body());
+ //noinspection resource
+ Console.log(request.send().body());
}
}
diff --git a/hutool-http/src/test/java/cn/hutool/http/UploadTest.java b/hutool-http/src/test/java/cn/hutool/http/UploadTest.java
index 144f01196..b6095fadf 100644
--- a/hutool-http/src/test/java/cn/hutool/http/UploadTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/UploadTest.java
@@ -1,16 +1,17 @@
package cn.hutool.http;
import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.resource.MultiFileResource;
import cn.hutool.core.lang.Console;
-import cn.hutool.http.client.engine.jdk.HttpRequest;
-import cn.hutool.http.client.engine.jdk.HttpResponse;
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.http.client.Request;
+import cn.hutool.http.client.Response;
import cn.hutool.http.meta.Header;
+import cn.hutool.http.meta.Method;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
@@ -30,12 +31,18 @@ public class UploadTest {
final File file = FileUtil.file("d:\\图片1.JPG");
final File file2 = FileUtil.file("d:\\图片3.png");
+ final Map form = MapUtil.builder(new HashMap())
+ .put("file", new MultiFileResource(file2, file))
+ .put("fileType", "图片")
+ .build();
+
// 方法一:自定义构建表单
- final HttpRequest request = HttpRequest//
- .post("http://localhost:8888/file")//
- .form("file", file2, file)//
- .form("fileType", "图片");
- final HttpResponse response = request.execute();
+ final Request request = Request//
+ .of("http://localhost:8888/file")//
+ .method(Method.POST)
+ .form(form);
+ //noinspection resource
+ final Response response = request.send();
Console.log(response.body());
}
@@ -52,26 +59,6 @@ public class UploadTest {
System.out.println(result);
}
- @Test
- @Ignore
- public void uploadTest2() {
- //客户端
- final String url = "http://192.168.1.200:8888/meta/upload/img";
- final Path file = Paths.get("D:\\test\\testBigData_upload.xlsx");
- final Map headers = new HashMap<>(16);
- headers.put("md5", "aaaaaaaa");
-
- final Map params = new HashMap<>(16);
- params.put("fileName", file.toFile().getName());
- params.put("file", file.toFile());
- final HttpRequest httpRequest = HttpRequest.post(url)
- .setChunkedStreamingMode(1024 * 1024)
- .headerMap(headers, false)
- .form(params);
- final HttpResponse httpResponse = httpRequest.execute();
- Console.log(httpResponse);
- }
-
@Test
@Ignore
public void smmsTest(){
@@ -79,11 +66,13 @@ public class UploadTest {
// hutool的user agent 被封了
final String token = "test";
final String url = "https://sm.ms/api/v2/upload";
- final String result = HttpUtil.createPost(url)
+ //noinspection resource
+ final String result = Request.of(url)
+ .method(Method.POST)
.header(Header.USER_AGENT, "PostmanRuntime/7.28.4")
.auth(token)
- .form("smfile", FileUtil.file("d:/test/qrcodeCustom.png"))
- .execute().bodyStr();
+ .form(MapUtil.of("smfile", FileUtil.file("d:/test/qrcodeCustom.png")))
+ .send().bodyStr();
Console.log(result);
}