Apache HttpClient 在使用 SSL 时抛出 SocketTimeoutException
Apache HttpClient throws SocketTimeoutException when using SSL
我正在使用 Apache httpclient 4.3.6 和 httpcore 4.3.3(与 opensaml 3.3.0 捆绑在一起)。我试图通过 HTTPS 通过代理获取网页,但每次都得到 SocketTimeoutException。 HTTP 连接工作正常。具体超时并不重要;使用更高的值失败需要更长的时间。
示例代码:
public class TestGet {
private static final int TIMEOUT = 7000;
public static void main(String[] args) throws Exception {
URL url = new URL("https://www.google.com");
URL proxy = new URL("https://myproxy:9090/");
try {
String s = get(url, proxy);
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
private static String get(URL url, URL proxy) throws IOException, URISyntaxException {
CloseableHttpClient client = HttpClients.createDefault();
HttpGet request = new HttpGet(url.toURI());
HttpHost host = null;
if (proxy != null) {
host = new HttpHost(proxy.getHost(), proxy.getPort(), proxy.getProtocol());
}
RequestConfig config = RequestConfig.custom()
.setProxy(host)
.setConnectionRequestTimeout(TIMEOUT)
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build();
request.setConfig(config);
try {
CloseableHttpResponse response = client.execute(request);
try {
System.out.println(response.getStatusLine());
return EntityUtils.toString(response.getEntity());
} finally {
response.close();
}
} finally {
client.close();
}
}
}
尝试连接到代理时抛出异常。堆栈跟踪是
org.apache.http.conn.ConnectTimeoutException: Connect to <proxy> failed: Read timed out
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:134)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:319)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:371)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
at authentication.TestGet.get(TestGet.java:47)
at authentication.TestGet.main(TestGet.java:22)
Caused by: java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at sun.security.ssl.InputRecord.readFully(Unknown Source)
at sun.security.ssl.InputRecord.read(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:290)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:259)
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:125)
... 11 more
这看起来很像 Apache 问题 HTTPCLIENT-1478,它与未设置套接字超时有关。该错误在 4.3.4 中被标记为已修复(我使用的是 4.3.6),虽然一些评论表明在更高版本中仍然需要解决方法,但它们对我不起作用。更重要的是,我已经进入 SSLConnectionSocketFactory.createConnection() 并确认套接字超时设置为非零值。
我卡住了。我不知道为什么连接失败并且无法比调用 SSLSocketImpl.startHandshake() 更深入地进行故障排除。可能出了什么问题,我该如何解决?
不要使用.setConnectionRequestTimeout(TIMEOUT)
或者,等待很长时间,看看会发生什么。
阅读 this 了解更多信息。
[问题作者回答;可能对其他人的适用性有限]
确认代理服务器支持HTTPS。即使在与目标服务器的连接上使用了 HTTPS,它也可能期望纯 HTTP。如果代理不支持 HTTPS,它不会响应客户端问候消息;从而导致套接字超时。
将 javax.net.debug
系统 属性 设置为 ssl
将启用 SSL 协商的调试日志记录。如果您看到一个 ClientHello 而没有后续的 ServerHello,则代理服务器可能不支持 HTTPS。将代理从 https://
更改为 http://
,然后重试。
我正在使用 Apache httpclient 4.3.6 和 httpcore 4.3.3(与 opensaml 3.3.0 捆绑在一起)。我试图通过 HTTPS 通过代理获取网页,但每次都得到 SocketTimeoutException。 HTTP 连接工作正常。具体超时并不重要;使用更高的值失败需要更长的时间。
示例代码:
public class TestGet {
private static final int TIMEOUT = 7000;
public static void main(String[] args) throws Exception {
URL url = new URL("https://www.google.com");
URL proxy = new URL("https://myproxy:9090/");
try {
String s = get(url, proxy);
System.out.println(s);
} catch (Exception e) {
e.printStackTrace();
}
}
private static String get(URL url, URL proxy) throws IOException, URISyntaxException {
CloseableHttpClient client = HttpClients.createDefault();
HttpGet request = new HttpGet(url.toURI());
HttpHost host = null;
if (proxy != null) {
host = new HttpHost(proxy.getHost(), proxy.getPort(), proxy.getProtocol());
}
RequestConfig config = RequestConfig.custom()
.setProxy(host)
.setConnectionRequestTimeout(TIMEOUT)
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build();
request.setConfig(config);
try {
CloseableHttpResponse response = client.execute(request);
try {
System.out.println(response.getStatusLine());
return EntityUtils.toString(response.getEntity());
} finally {
response.close();
}
} finally {
client.close();
}
}
}
尝试连接到代理时抛出异常。堆栈跟踪是
org.apache.http.conn.ConnectTimeoutException: Connect to <proxy> failed: Read timed out
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:134)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:319)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:371)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:219)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:195)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:86)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:108)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:106)
at authentication.TestGet.get(TestGet.java:47)
at authentication.TestGet.main(TestGet.java:22)
Caused by: java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at sun.security.ssl.InputRecord.readFully(Unknown Source)
at sun.security.ssl.InputRecord.read(Unknown Source)
at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:290)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:259)
at org.apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.java:125)
... 11 more
这看起来很像 Apache 问题 HTTPCLIENT-1478,它与未设置套接字超时有关。该错误在 4.3.4 中被标记为已修复(我使用的是 4.3.6),虽然一些评论表明在更高版本中仍然需要解决方法,但它们对我不起作用。更重要的是,我已经进入 SSLConnectionSocketFactory.createConnection() 并确认套接字超时设置为非零值。
我卡住了。我不知道为什么连接失败并且无法比调用 SSLSocketImpl.startHandshake() 更深入地进行故障排除。可能出了什么问题,我该如何解决?
不要使用.setConnectionRequestTimeout(TIMEOUT)
或者,等待很长时间,看看会发生什么。
阅读 this 了解更多信息。
[问题作者回答;可能对其他人的适用性有限]
确认代理服务器支持HTTPS。即使在与目标服务器的连接上使用了 HTTPS,它也可能期望纯 HTTP。如果代理不支持 HTTPS,它不会响应客户端问候消息;从而导致套接字超时。
将 javax.net.debug
系统 属性 设置为 ssl
将启用 SSL 协商的调试日志记录。如果您看到一个 ClientHello 而没有后续的 ServerHello,则代理服务器可能不支持 HTTPS。将代理从 https://
更改为 http://
,然后重试。