一、问题
最近把环境切换到灰度环境,有一个接口一直报“javax.net.ssl.SSLHandshakeException: Handshake failed”。
二、分析
看这个描述,是网络连接时握手失败。这下不得不跟到源码里看了,联想到OkHttp的网络请求流程,出问题的地方应该是网络连接的地方出了问题,即ConnectInterceptor,打断点跟进去,果然,在执行到
HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
出了异常,这里面涉及到一堆核心源码:
StreamAllocation.java
public HttpCodec newStream(
OkHttpClient client, Interceptor.Chain chain, boolean doExtensiveHealthChecks) {
int connectTimeout = chain.connectTimeoutMillis();
int readTimeout = chain.readTimeoutMillis();
int writeTimeout = chain.writeTimeoutMillis();
int pingIntervalMillis = client.pingIntervalMillis();
boolean connectionRetryEnabled = client.retryOnConnectionFailure();
try {
// 核心
RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
writeTimeout, pingIntervalMillis, connectionRetryEnabled, doExtensiveHealthChecks);
HttpCodec resultCodec = resultConnection.newCodec(client, chain, this);
synchronized (connectionPool) {
codec = resultCodec;
return resultCodec;
}
} catch (IOException e) {
throw new RouteException(e);
}
}
接下来就是往RealConnection类里跟代码,由于代码篇幅太大,这里我还是简单说下位置,在connectTls方法中,会去做htts的连接,里面就有相关的握手代码:
// Force handshake. This can throw!
sslSocket.startHandshake();
// block for session establishment
SSLSession sslSocketSession = sslSocket.getSession();
if (!isValid(sslSocketSession)) {
throw new IOException("a valid ssl session was not established");
}
Handshake unverifiedHandshake = Handshake.get(sslSocketSession);
然而,这样看似乎还是找不到答案,因为代码太长了,有个办法,看异常日志。Ok3里代码一旦出现异常,就喜欢抛异常日志,这里我把日志翻出来:
根据日志里提到的行号:okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:302)
确定了执行sslSocket.startHandshake();这句出现的问题,至于原因,上面的日志中也有提到:
啥意思?我也不知道,但结合上面的信息,我能猜出来出问题的方向,日志里反复提到SSL这个词,说明是https建立握手链接时出现了异常了。https链接出现异常有两种情况
- 1、网络安全证书没有设置,就是这个X509TrustManager。
- 2、当前接口不支持服务端不支持https。
朝着这两个方向,我检查了代码,第一种情况排除。问题出现第二种情况,即当前的灰度地址不支持https!!!做安全优化时,服务端无视了灰度环境的安全优化。。。。
三、解决
让服务端改,或者我来把https换成http.
四、总结
这个问题充分验证了读源码的重要性,还好前两年我读过OK3的源码,对于网络执行的流程有个了解,这节省了我排查问题的时间,提升效率