You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Apache HTTP Client带代理认证检测代理可用性时出现握手终止错误的问题排查

使用Apache HTTP Client带代理认证检测代理可用性时出现握手终止错误的问题排查

你遇到的这个「Remote host terminated the handshake」错误,通常和代理连接时的SSL握手失败、代理协议配置不匹配,或者认证方式不正确有关。咱们先一步步拆解问题,看看哪里出了问题。

首先先还原你的错误场景:

Checking proxy global.711proxy.com:10000
Proxy check failed: Remote host terminated the handshake
Proxy is dead or not in USA. Timezone: null

你的Proxy类整体逻辑是通顺的,但在带认证的代理配置上有几个容易踩的坑,咱们逐一排查修复:

1. 代理协议与目标站点不匹配

你创建代理HttpHost时用的是new HttpHost("http", host, port),但目标站点是HTTPS协议的worldtimeapi.org:443。如果你的代理本身是HTTPS代理,这里协议设置成http就会直接导致SSL握手失败。很多付费代理同时支持HTTP和HTTPS,但需要明确指定对应协议。

修复方案:
将代理协议改成https,适配目标站点的HTTPS请求:

final HttpHost proxy = new HttpHost("https", host, port);

2. 代理认证范围配置不准确

你设置的AuthScope是new AuthScope(host, port),但用HttpHost对象直接作为AuthScope参数会更准确,能避免因域名/端口匹配规则导致的认证失败。另外,部分代理可能需要明确指定认证方案(比如Basic)。

修复方案:
调整CredentialsProvider的配置:

credsProvider.setCredentials(
    new AuthScope(proxy), // 直接传入代理HttpHost,匹配更精准
    new UsernamePasswordCredentials(username, password.toCharArray())
);

3. SSL上下文配置缺失

通过代理访问HTTPS站点时,如果代理或目标站点的SSL证书不被本地信任,就会触发握手终止。测试环境下可以临时配置信任所有证书(生产环境请勿使用,需指定合法信任库)。

修复方案:
添加SSL上下文配置:

// 创建信任所有证书的SSL上下文(仅测试用)
SSLContext sslContext = SSLContexts.custom()
    .loadTrustMaterial((chain, authType) -> true)
    .build();

// 构建客户端时注入SSL配置
httpClient = HttpClients.custom()
    .setProxy(proxy)
    .setDefaultCredentialsProvider(credsProvider)
    .setSSLContext(sslContext)
    .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
    .build();

4. 简化请求路径写法

你当前用new HttpGet("/api/ip")配合target主机的写法是可行的,但直接使用完整URL会更直观,也能避免部分代理对路径拼接的兼容性问题。

修复方案:
直接使用完整目标URL:

final HttpGet request = new HttpGet("https://worldtimeapi.org/api/ip");
// 执行时无需再指定target
httpClient.execute(request, response -> {
    // ... 原有逻辑
});

整合修复后的checkProxy方法示例

把上面的修复点整合后,你的checkProxy方法可以改成这样:

private void checkProxy() {
    try {
        System.out.println("Checking proxy " + host + ":" + port);
        
        final String targetUrl = "https://worldtimeapi.org/api/ip";
        final HttpHost proxy = new HttpHost("https", host, port);

        final CloseableHttpClient httpClient;
        if (isAuth) {
            BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
            credsProvider.setCredentials(
                new AuthScope(proxy),
                new UsernamePasswordCredentials(username, password.toCharArray())
            );
            
            SSLContext sslContext = SSLContexts.custom()
                .loadTrustMaterial((chain, authType) -> true)
                .build();
            
            httpClient = HttpClients.custom()
                .setProxy(proxy)
                .setDefaultCredentialsProvider(credsProvider)
                .setSSLContext(sslContext)
                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                .build();
        } else {
            SSLContext sslContext = SSLContexts.custom()
                .loadTrustMaterial((chain, authType) -> true)
                .build();
            
            httpClient = HttpClients.custom()
                .setProxy(proxy)
                .setSSLContext(sslContext)
                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                .build();
        }

        try {
            final HttpGet request = new HttpGet(targetUrl);
            
            httpClient.execute(request, response -> {
                this.isLive = (response.getCode() >= 200 && response.getCode() < 400);
                
                if (this.isLive) {
                    String jsonStr = EntityUtils.toString(response.getEntity());
                    JSONObject json = new JSONObject(jsonStr);
                    this.timezone = json.getString("timezone");
                    System.out.println("Proxy check successful. Timezone: " + timezone);
                }
                
                return null;
            });
        } finally {
            httpClient.close();
        }
        
    } catch (Exception e) {
        System.err.println("Proxy check failed: " + e.getMessage());
        e.printStackTrace(); // 打印完整栈轨迹,方便排查深层原因
        this.isLive = false;
        this.timezone = null;
    }
}

额外排查建议

  • 先手动用curl测试代理可用性:curl -x https://username:password@global.711proxy.com:10000 https://worldtimeapi.org/api/ip,确认代理本身能正常工作。
  • 保留e.printStackTrace()打印完整异常栈,能帮你定位更具体的错误原因(比如是证书问题、认证失败还是连接超时)。

备注:内容来源于stack exchange,提问作者Jacob Krumholz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 17:23:07