diff --git a/CHANGELOG.md b/CHANGELOG.md index f00e2626d..7dbd82766 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ * 【core 】 XmlUtil支持可选是否输出omit xml declaration(pr#732@Github) * 【core 】 车牌号校验兼容新能源车牌(pr#92@Gitee) * 【core 】 在NetUtil中新增ping功能(pr#91@Gitee) +* 【core 】 DateUtil.offset不支持ERA,增加异常提示(issue#I18KD5@Gitee) +* 【http 】 改进HttpUtil访问HTTPS接口性能问题,SSL证书使用单例(issue#I18AL1@Gitee) ### Bug修复 * 【core 】 修复isExpired的bug(issue#733@Gtihub) diff --git a/hutool-core/src/main/java/cn/hutool/core/date/DateTime.java b/hutool-core/src/main/java/cn/hutool/core/date/DateTime.java index b592cc0d4..1a0758155 100644 --- a/hutool-core/src/main/java/cn/hutool/core/date/DateTime.java +++ b/hutool-core/src/main/java/cn/hutool/core/date/DateTime.java @@ -268,6 +268,10 @@ public class DateTime extends Date { * @return 如果此对象为可变对象,返回自身,否则返回新对象 */ public DateTime offset(DateField datePart, int offset) { + if(DateField.ERA == datePart){ + throw new IllegalArgumentException("ERA is not support offset!"); + } + final Calendar cal = toCalendar(); //noinspection MagicConstant cal.add(datePart.getValue(), offset); diff --git a/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java index d3cfe146e..67f57b139 100644 --- a/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/date/DateUtilTest.java @@ -645,6 +645,7 @@ public class DateUtilTest { DateTime startDate = DateUtil.parse("2019-12-01 17:02:30"); DateTime endDate = DateUtil.parse("2019-12-02 17:02:30"); int length = 3; + //noinspection deprecation boolean expired = DateUtil.isExpired(startDate, DateField.DAY_OF_YEAR, length, endDate); Assert.assertTrue(expired); } diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpConnection.java b/hutool-http/src/main/java/cn/hutool/http/HttpConnection.java index 090ada2b7..1a954063f 100644 --- a/hutool-http/src/main/java/cn/hutool/http/HttpConnection.java +++ b/hutool-http/src/main/java/cn/hutool/http/HttpConnection.java @@ -1,5 +1,16 @@ package cn.hutool.http; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; +import cn.hutool.http.ssl.AndroidSupportSSLFactory; +import cn.hutool.http.ssl.DefaultSSLInfo; +import cn.hutool.http.ssl.SSLSocketFactoryBuilder; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -16,17 +27,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import javax.net.ssl.HostnameVerifier; -import javax.net.ssl.HttpsURLConnection; -import javax.net.ssl.SSLSocketFactory; - -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.core.util.URLUtil; -import cn.hutool.http.ssl.AndroidSupportSSLFactory; -import cn.hutool.http.ssl.SSLSocketFactoryBuilder; -import cn.hutool.http.ssl.TrustAnyHostnameVerifier; - /** * http连接对象,对HttpURLConnection的包装 * @@ -262,20 +262,8 @@ public class HttpConnection { // Https请求 final HttpsURLConnection httpsConn = (HttpsURLConnection) conn; // 验证域 - httpsConn.setHostnameVerifier(null != hostnameVerifier ? hostnameVerifier : new TrustAnyHostnameVerifier()); - if (null == ssf) { - try { - if (StrUtil.equalsIgnoreCase("dalvik", System.getProperty("java.vm.name"))) { - // 兼容android低版本SSL连接 - ssf = new AndroidSupportSSLFactory(); - } else { - ssf = SSLSocketFactoryBuilder.create().build(); - } - } catch (KeyManagementException | NoSuchAlgorithmException e) { - throw new HttpException(e); - } - } - httpsConn.setSSLSocketFactory(ssf); + httpsConn.setHostnameVerifier(ObjectUtil.defaultIfNull(hostnameVerifier, DefaultSSLInfo.TRUST_ANY_HOSTNAME_VERIFIER)); + httpsConn.setSSLSocketFactory(ObjectUtil.defaultIfNull(ssf, DefaultSSLInfo.DEFAULT_SSF)); } return this; diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java index b7b1adc5f..cfcae98fe 100644 --- a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java +++ b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java @@ -857,12 +857,11 @@ public class HttpRequest extends HttpBase { * @see #setSSLSocketFactory(SSLSocketFactory) */ public HttpRequest setSSLProtocol(String protocol) { - if (null == this.ssf) { - try { - this.ssf = SSLSocketFactoryBuilder.create().setProtocol(protocol).build(); - } catch (Exception e) { - throw new HttpException(e); - } + Assert.notBlank(protocol, "protocol must be not blank!"); + try { + this.ssf = SSLSocketFactoryBuilder.create().setProtocol(protocol).build(); + } catch (Exception e) { + throw new HttpException(e); } return this; } @@ -930,13 +929,13 @@ public class HttpRequest extends HttpBase { this.url = HttpUtil.encodeParams(this.url, this.charset); } // 初始化 connection - initConnecton(); + initConnection(); // 发送请求 send(); // 手动实现重定向 - HttpResponse httpResponse = sendRedirectIfPosible(); + HttpResponse httpResponse = sendRedirectIfPossible(); // 获取响应 if (null == httpResponse) { @@ -966,7 +965,7 @@ public class HttpRequest extends HttpBase { /** * 初始化网络连接 */ - private void initConnecton() { + private void initConnection() { if (null != this.httpConnection) { // 执行下次请求时自动关闭上次请求(常用于转发) this.httpConnection.disconnectQuietly(); @@ -1018,7 +1017,7 @@ public class HttpRequest extends HttpBase { * * @return {@link HttpResponse},无转发返回 null */ - private HttpResponse sendRedirectIfPosible() { + private HttpResponse sendRedirectIfPossible() { if (this.maxRedirectCount < 1) { // 不重定向 return null; diff --git a/hutool-http/src/main/java/cn/hutool/http/ssl/CustomProtocolsSSLFactory.java b/hutool-http/src/main/java/cn/hutool/http/ssl/CustomProtocolsSSLFactory.java index 1c7c521f0..90ce12fb0 100644 --- a/hutool-http/src/main/java/cn/hutool/http/ssl/CustomProtocolsSSLFactory.java +++ b/hutool-http/src/main/java/cn/hutool/http/ssl/CustomProtocolsSSLFactory.java @@ -1,5 +1,7 @@ package cn.hutool.http.ssl; +import cn.hutool.core.util.ArrayUtil; + import java.io.IOException; import java.net.InetAddress; import java.net.Socket; @@ -13,7 +15,6 @@ import javax.net.ssl.SSLSocketFactory; * 自定义支持协议类型的SSLSocketFactory * * @author looly - * */ public class CustomProtocolsSSLFactory extends SSLSocketFactory { @@ -45,45 +46,42 @@ public class CustomProtocolsSSLFactory extends SSLSocketFactory { @Override public Socket createSocket() throws IOException { - SSLSocket sslSocket = (SSLSocket) base.createSocket(); + final SSLSocket sslSocket = (SSLSocket) base.createSocket(); resetProtocols(sslSocket); return sslSocket; } @Override public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { - SSLSocket socket = (SSLSocket) base.createSocket(s, host, port, autoClose); + final SSLSocket socket = (SSLSocket) base.createSocket(s, host, port, autoClose); resetProtocols(socket); return socket; } @Override public Socket createSocket(String host, int port) throws IOException { - SSLSocket socket = (SSLSocket) base.createSocket(host, port); + final SSLSocket socket = (SSLSocket) base.createSocket(host, port); resetProtocols(socket); return socket; } @Override public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { - - SSLSocket socket = (SSLSocket) base.createSocket(host, port, localHost, localPort); + final SSLSocket socket = (SSLSocket) base.createSocket(host, port, localHost, localPort); resetProtocols(socket); return socket; } @Override public Socket createSocket(InetAddress host, int port) throws IOException { - - SSLSocket socket = (SSLSocket) base.createSocket(host, port); + final SSLSocket socket = (SSLSocket) base.createSocket(host, port); resetProtocols(socket); return socket; } @Override public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { - - SSLSocket socket = (SSLSocket) base.createSocket(address, port, localAddress, localPort); + final SSLSocket socket = (SSLSocket) base.createSocket(address, port, localAddress, localPort); resetProtocols(socket); return socket; } @@ -94,7 +92,9 @@ public class CustomProtocolsSSLFactory extends SSLSocketFactory { * @param socket SSLSocket */ private void resetProtocols(SSLSocket socket) { - socket.setEnabledProtocols(protocols); + if(ArrayUtil.isNotEmpty(this.protocols)){ + socket.setEnabledProtocols(this.protocols); + } } } \ No newline at end of file diff --git a/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultSSLFactory.java b/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultSSLFactory.java new file mode 100644 index 000000000..a2626c6aa --- /dev/null +++ b/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultSSLFactory.java @@ -0,0 +1,18 @@ +package cn.hutool.http.ssl; + +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +/** + * 默认的SSLSocketFactory + * + * @author Looly + * @since 5.1.2 + */ +public class DefaultSSLFactory extends CustomProtocolsSSLFactory { + + public DefaultSSLFactory() throws KeyManagementException, NoSuchAlgorithmException { + super(); + } + +} \ No newline at end of file diff --git a/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultSSLInfo.java b/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultSSLInfo.java new file mode 100644 index 000000000..727d58b4b --- /dev/null +++ b/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultSSLInfo.java @@ -0,0 +1,40 @@ +package cn.hutool.http.ssl; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpException; + +import javax.net.ssl.SSLSocketFactory; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +/** + * 默认的SSL配置,当用户未设置相关信息时,使用默认设置,默认设置为单例模式。 + * + * @author looly + * @since 5.1.2 + */ +public class DefaultSSLInfo { + /** + * 默认信任全部的域名校验器 + */ + public static final TrustAnyHostnameVerifier TRUST_ANY_HOSTNAME_VERIFIER; + /** + * 默认的SSLSocketFactory,区分安卓 + */ + public static final SSLSocketFactory DEFAULT_SSF; + + static { + TRUST_ANY_HOSTNAME_VERIFIER = new TrustAnyHostnameVerifier(); + + try { + if (StrUtil.equalsIgnoreCase("dalvik", System.getProperty("java.vm.name"))) { + // 兼容android低版本SSL连接 + DEFAULT_SSF = new AndroidSupportSSLFactory(); + } else { + DEFAULT_SSF = new DefaultSSLFactory(); + } + } catch (KeyManagementException | NoSuchAlgorithmException e) { + throw new HttpException(e); + } + } +} diff --git a/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultTrustManager.java b/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultTrustManager.java index 5159718c5..0b848842e 100644 --- a/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultTrustManager.java +++ b/hutool-http/src/main/java/cn/hutool/http/ssl/DefaultTrustManager.java @@ -7,8 +7,8 @@ import javax.net.ssl.X509TrustManager; /** * 证书管理 - * @author Looly * + * @author Looly */ public class DefaultTrustManager implements X509TrustManager { @@ -18,10 +18,10 @@ public class DefaultTrustManager implements X509TrustManager { } @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + public void checkServerTrusted(X509Certificate[] chain, String authType) { } } diff --git a/hutool-http/src/test/java/cn/hutool/http/test/HttpsTest.java b/hutool-http/src/test/java/cn/hutool/http/test/HttpsTest.java new file mode 100644 index 000000000..fca3e6cc1 --- /dev/null +++ b/hutool-http/src/test/java/cn/hutool/http/test/HttpsTest.java @@ -0,0 +1,28 @@ +package cn.hutool.http.test; + +import cn.hutool.core.lang.Console; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.http.HttpUtil; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicInteger; + +public class HttpsTest { + + /** + * 测试单例的SSLSocketFactory是否有线程安全问题 + */ + @Test + @Ignore + public void getTest() { + final AtomicInteger count = new AtomicInteger(); + for(int i =0; i < 100; i++){ + ThreadUtil.execute(()->{ + final String s = HttpUtil.get("https://www.baidu.com/"); + Console.log(count.incrementAndGet()); + }); + } + ThreadUtil.sync(this); + } +}