apache HttpClient异常-ProtocolException: Target host is not specified

发布时间 2023-12-19 19:31:41作者: buguge

昨夜,甘肃临夏州积石山县发生6.2级地震,影响到甘肃、青海地区。截至目前,已有100多人遇难。百度了一下当地天气,还挺冷,夜间温度低到-15℃。祈祷难民早日得到救援吧。

 

 

分享今天解决的一个生产问题告警。

如下HTTP工具类中的httpClientPost方法使用apache(maven坐标:org.apache.httpcomponents:httpclient:4.5.12)的HttpClient发送POST请求。当传入的url参数非法时(如为null、为空串、不是有效的http地址),在执行client.execute方法时,程序会抛出异常“ProtocolException: Target host is not specified”。

public static CloseableHttpClient client;

static {
    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(CONNECT_TIMEOUT)
            .setSocketTimeout(SOCKET_TIME_OUT)
            .setConnectionRequestTimeout(30000)
            .build();
    client = HttpClients.custom()
            .setMaxConnPerRoute(50)
            .setMaxConnTotal(200)
            .setDefaultRequestConfig(requestConfig)
            .setKeepAliveStrategy((response, context) -> 15 * 1000) // 设置Keepalive的时间为60s→2023-9-11 尝试规避NoHttpResponseException,与红洁/海鹏沟通,改为<60s
            .build();
}
public static String httpClientPost(String url, Map<String, Object> params, String charset, int connectTimeout, int socketTimeout) {
    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).setConnectionRequestTimeout(30000)
            .build();
    HttpPost httpPost = new HttpPost(url);
    httpPost.setConfig(requestConfig);
    ...
    httpPost.setEntity(entity);
    
    HttpResponse response = client.execute(httpPost);
    // 读取服务器响应数据
    return getHttpResult(response.getEntity().getContent(), charset);
}

异常stacktrace:

org.apache.http.client.ClientProtocolException
    at com.emax.channel.provider.modules.serviceprovider.util.HttpClientHelper.httpClientPost(HttpClientHelper.java:393)
    at com.emax.channel.provider.modules.serviceprovider.util.HttpClientHelper.main(HttpClientHelper.java:758)
Caused by: org.apache.http.client.ClientProtocolException
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:187)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
    at com.emax.channel.provider.modules.serviceprovider.util.HttpClientHelper.httpClientPost(HttpClientHelper.java:389)
    ... 1 more
Caused by: org.apache.http.ProtocolException: Target host is not specified
    at org.apache.http.impl.conn.DefaultRoutePlanner.determineRoute(DefaultRoutePlanner.java:71)
    at org.apache.http.impl.client.InternalHttpClient.determineRoute(InternalHttpClient.java:125)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
    ... 4 more

 

 

因此,程序应先校验url参数的合法性,然后再执行http请求。

改造后的httpClientPost方法如下。

public static String httpClientPost(String url, Map<String, Object> params, String charset, int connectTimeout, int socketTimeout) {
    if (StringUtils.isBlank(url)) {
        throw new IllegalArgumentException("请求地址为空");
    }
    if (!url.toLowerCase().startsWith("http")) {
        throw new IllegalArgumentException("请求地址非法");
    }
    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).setConnectionRequestTimeout(30000)
            .build();
    HttpPost httpPost = new HttpPost(url);
    httpPost.setConfig(requestConfig);
    ...
    httpPost.setEntity(entity);
    
    try {
        HttpResponse response = client.execute(httpPost);
        // 读取服务器响应数据
        return getHttpResult(response.getEntity().getContent(), charset);
    } catch (Exception e) {
        Throwable cause = e.getCause();
        //单独处理“Caused by: org.apache.http.ProtocolException: Target host is not specified”,转换成人话
        if (cause instanceof org.apache.http.ProtocolException) {
//                System.out.println("-----------------"+ cause.getMessage());Target host is not specified
            throw new RuntimeException("请确保正确设置了目标主机的URL");
        }
        throw new RuntimeException(e);
    }
}