This commit is contained in:
Looly 2020-01-29 10:58:45 +08:00
parent 1c13d73010
commit 63a8c5766d
10 changed files with 129 additions and 49 deletions

View File

@ -9,6 +9,8 @@
* 【core 】 XmlUtil支持可选是否输出omit xml declarationpr#732@Github * 【core 】 XmlUtil支持可选是否输出omit xml declarationpr#732@Github
* 【core 】 车牌号校验兼容新能源车牌pr#92@Gitee * 【core 】 车牌号校验兼容新能源车牌pr#92@Gitee
* 【core 】 在NetUtil中新增ping功能pr#91@Gitee * 【core 】 在NetUtil中新增ping功能pr#91@Gitee
* 【core 】 DateUtil.offset不支持ERA增加异常提示issue#I18KD5@Gitee
* 【http 】 改进HttpUtil访问HTTPS接口性能问题SSL证书使用单例issue#I18AL1@Gitee
### Bug修复 ### Bug修复
* 【core 】 修复isExpired的bugissue#733@Gtihub * 【core 】 修复isExpired的bugissue#733@Gtihub

View File

@ -268,6 +268,10 @@ public class DateTime extends Date {
* @return 如果此对象为可变对象返回自身否则返回新对象 * @return 如果此对象为可变对象返回自身否则返回新对象
*/ */
public DateTime offset(DateField datePart, int offset) { public DateTime offset(DateField datePart, int offset) {
if(DateField.ERA == datePart){
throw new IllegalArgumentException("ERA is not support offset!");
}
final Calendar cal = toCalendar(); final Calendar cal = toCalendar();
//noinspection MagicConstant //noinspection MagicConstant
cal.add(datePart.getValue(), offset); cal.add(datePart.getValue(), offset);

View File

@ -645,6 +645,7 @@ public class DateUtilTest {
DateTime startDate = DateUtil.parse("2019-12-01 17:02:30"); DateTime startDate = DateUtil.parse("2019-12-01 17:02:30");
DateTime endDate = DateUtil.parse("2019-12-02 17:02:30"); DateTime endDate = DateUtil.parse("2019-12-02 17:02:30");
int length = 3; int length = 3;
//noinspection deprecation
boolean expired = DateUtil.isExpired(startDate, DateField.DAY_OF_YEAR, length, endDate); boolean expired = DateUtil.isExpired(startDate, DateField.DAY_OF_YEAR, length, endDate);
Assert.assertTrue(expired); Assert.assertTrue(expired);
} }

View File

@ -1,5 +1,16 @@
package cn.hutool.http; 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.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -16,17 +27,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; 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的包装 * http连接对象对HttpURLConnection的包装
* *
@ -262,20 +262,8 @@ public class HttpConnection {
// Https请求 // Https请求
final HttpsURLConnection httpsConn = (HttpsURLConnection) conn; final HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
// 验证域 // 验证域
httpsConn.setHostnameVerifier(null != hostnameVerifier ? hostnameVerifier : new TrustAnyHostnameVerifier()); httpsConn.setHostnameVerifier(ObjectUtil.defaultIfNull(hostnameVerifier, DefaultSSLInfo.TRUST_ANY_HOSTNAME_VERIFIER));
if (null == ssf) { httpsConn.setSSLSocketFactory(ObjectUtil.defaultIfNull(ssf, DefaultSSLInfo.DEFAULT_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);
} }
return this; return this;

View File

@ -857,12 +857,11 @@ public class HttpRequest extends HttpBase<HttpRequest> {
* @see #setSSLSocketFactory(SSLSocketFactory) * @see #setSSLSocketFactory(SSLSocketFactory)
*/ */
public HttpRequest setSSLProtocol(String protocol) { public HttpRequest setSSLProtocol(String protocol) {
if (null == this.ssf) { Assert.notBlank(protocol, "protocol must be not blank!");
try { try {
this.ssf = SSLSocketFactoryBuilder.create().setProtocol(protocol).build(); this.ssf = SSLSocketFactoryBuilder.create().setProtocol(protocol).build();
} catch (Exception e) { } catch (Exception e) {
throw new HttpException(e); throw new HttpException(e);
}
} }
return this; return this;
} }
@ -930,13 +929,13 @@ public class HttpRequest extends HttpBase<HttpRequest> {
this.url = HttpUtil.encodeParams(this.url, this.charset); this.url = HttpUtil.encodeParams(this.url, this.charset);
} }
// 初始化 connection // 初始化 connection
initConnecton(); initConnection();
// 发送请求 // 发送请求
send(); send();
// 手动实现重定向 // 手动实现重定向
HttpResponse httpResponse = sendRedirectIfPosible(); HttpResponse httpResponse = sendRedirectIfPossible();
// 获取响应 // 获取响应
if (null == httpResponse) { if (null == httpResponse) {
@ -966,7 +965,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
/** /**
* 初始化网络连接 * 初始化网络连接
*/ */
private void initConnecton() { private void initConnection() {
if (null != this.httpConnection) { if (null != this.httpConnection) {
// 执行下次请求时自动关闭上次请求常用于转发 // 执行下次请求时自动关闭上次请求常用于转发
this.httpConnection.disconnectQuietly(); this.httpConnection.disconnectQuietly();
@ -1018,7 +1017,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
* *
* @return {@link HttpResponse}无转发返回 <code>null</code> * @return {@link HttpResponse}无转发返回 <code>null</code>
*/ */
private HttpResponse sendRedirectIfPosible() { private HttpResponse sendRedirectIfPossible() {
if (this.maxRedirectCount < 1) { if (this.maxRedirectCount < 1) {
// 不重定向 // 不重定向
return null; return null;

View File

@ -1,5 +1,7 @@
package cn.hutool.http.ssl; package cn.hutool.http.ssl;
import cn.hutool.core.util.ArrayUtil;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
@ -13,7 +15,6 @@ import javax.net.ssl.SSLSocketFactory;
* 自定义支持协议类型的SSLSocketFactory * 自定义支持协议类型的SSLSocketFactory
* *
* @author looly * @author looly
*
*/ */
public class CustomProtocolsSSLFactory extends SSLSocketFactory { public class CustomProtocolsSSLFactory extends SSLSocketFactory {
@ -45,45 +46,42 @@ public class CustomProtocolsSSLFactory extends SSLSocketFactory {
@Override @Override
public Socket createSocket() throws IOException { public Socket createSocket() throws IOException {
SSLSocket sslSocket = (SSLSocket) base.createSocket(); final SSLSocket sslSocket = (SSLSocket) base.createSocket();
resetProtocols(sslSocket); resetProtocols(sslSocket);
return sslSocket; return sslSocket;
} }
@Override @Override
public SSLSocket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { 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); resetProtocols(socket);
return socket; return socket;
} }
@Override @Override
public Socket createSocket(String host, int port) throws IOException { 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); resetProtocols(socket);
return socket; return socket;
} }
@Override @Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
final SSLSocket socket = (SSLSocket) base.createSocket(host, port, localHost, localPort);
SSLSocket socket = (SSLSocket) base.createSocket(host, port, localHost, localPort);
resetProtocols(socket); resetProtocols(socket);
return socket; return socket;
} }
@Override @Override
public Socket createSocket(InetAddress host, int port) throws IOException { public Socket createSocket(InetAddress host, int port) throws IOException {
final SSLSocket socket = (SSLSocket) base.createSocket(host, port);
SSLSocket socket = (SSLSocket) base.createSocket(host, port);
resetProtocols(socket); resetProtocols(socket);
return socket; return socket;
} }
@Override @Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
final SSLSocket socket = (SSLSocket) base.createSocket(address, port, localAddress, localPort);
SSLSocket socket = (SSLSocket) base.createSocket(address, port, localAddress, localPort);
resetProtocols(socket); resetProtocols(socket);
return socket; return socket;
} }
@ -94,7 +92,9 @@ public class CustomProtocolsSSLFactory extends SSLSocketFactory {
* @param socket SSLSocket * @param socket SSLSocket
*/ */
private void resetProtocols(SSLSocket socket) { private void resetProtocols(SSLSocket socket) {
socket.setEnabledProtocols(protocols); if(ArrayUtil.isNotEmpty(this.protocols)){
socket.setEnabledProtocols(this.protocols);
}
} }
} }

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -7,8 +7,8 @@ import javax.net.ssl.X509TrustManager;
/** /**
* 证书管理 * 证书管理
* @author Looly
* *
* @author Looly
*/ */
public class DefaultTrustManager implements X509TrustManager { public class DefaultTrustManager implements X509TrustManager {
@ -18,10 +18,10 @@ public class DefaultTrustManager implements X509TrustManager {
} }
@Override @Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { public void checkClientTrusted(X509Certificate[] chain, String authType) {
} }
@Override @Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { public void checkServerTrusted(X509Certificate[] chain, String authType) {
} }
} }

View File

@ -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);
}
}