如何将 HttpClientBuilder 与 Http 代理一起使用? [英] How to use HttpClientBuilder with Http proxy?

查看:48
本文介绍了如何将 HttpClientBuilder 与 Http 代理一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为我使用 HttpClientBuilder 发出的请求设置代理,如下所示:

 CredentialsProvider credsProvider = new BasicCredentialsProvider();UsernamePasswordCredentials usernamePasswordCredentials = new UsernamePasswordCredentials(proxyUser, proxyPassword);credsProvider.setCredentials(new AuthScope(proxyHost, proxyPort), usernamePasswordCredentials);builder.useSystemProperties();builder.setProxy(new HttpHost(proxyHost, proxyPort));builder.setDefaultCredentialsProvider(credsProvider);builder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());

建造者在哪里:

 HttpClientBuilder builder = HttpClientBuilder.create();

但是,当我执行此请求时出现此异常:

java.lang.RuntimeException: org.apache.http.conn.UnsupportedSchemeException: 不支持 http 协议引起:org.apache.http.conn.UnsupportedSchemeException:不支持http协议在 org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:108) ~[httpclient-4.5.1.jar:4.5.1]在 org.apache.http.impl.conn.BasicHttpClientConnectionManager.connect(BasicHttpClientConnectionManager.java:338) ~[httpclient-4.5.1.jar:4.5.1]在 org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:388) ~[httpclient-4.5.1.jar:4.5.1]在 org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) ~[httpclient-4.5.1.jar:4.5.1]在 org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184) ~[httpclient-4.5.1.jar:4.5.1]在 org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88) ~[httpclient-4.5.1.jar:4.5.1]在 org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) ~[httpclient-4.5.1.jar:4.5.1]在 org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184) ~[httpclient-4.5.1.jar:4.5.1]在 org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82) ~[httpclient-4.5.1.jar:4.5.1]在 org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107) ~[httpclient-4.5.1.jar:4.5.1]在 org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55) ~[httpclient-4.5.1.jar:4.5.1]

(为简洁起见缩短了例外)

由于这是一个 HTTP 代理,我不想将方案更改为 HTTPS,这无论如何都行不通.我如何让它工作?

解决方案

java.lang.RuntimeException:org.apache.http.conn.UnsupportedSchemeException:http 协议不是支持

为什么会出现这个问题发生?

回答:这实际上是因为您忘记为http"方案注册连接套接字工厂.

Plain 'http' 方案必须用于建立中间连接在可以使用 'https' 隧道之前到代理本身.

<小时>

出于操作目的,您可以尝试以下代码:

CloseableHttpClient 客户端 = HttpClients.custom().setRoutePlanner(新SystemDefaultRoutePlanner(ProxySelector.getDefault())).建造();

<小时>

我还会为您的研究推荐简单的代码.希望它能救你.

ClientExecuteProxy.爪哇

package org.apache.http.examples.client;导入 org.apache.http.HttpHost;导入 org.apache.http.client.config.RequestConfig;导入 org.apache.http.client.methods.CloseableHttpResponse;导入 org.apache.http.client.methods.HttpGet;导入 org.apache.http.impl.client.CloseableHttpClient;导入 org.apache.http.impl.client.HttpClients;导入 org.apache.http.util.EntityUtils;/*** 如何通过代理发送请求.** @自 4.0*/公共类 ClientExecuteProxy {public static void main(String[] args)抛出异常{CloseableHttpClient httpclient = HttpClients.createDefault();尝试 {HttpHost target = new HttpHost("httpbin.org", 443, "https");HttpHost 代理 = new HttpHost("127.0.0.1", 8080, "http");RequestConfig 配置 = RequestConfig.custom().setProxy(代理).建造();HttpGet request = new HttpGet("/");request.setConfig(config);System.out.println("执行请求" + request.getRequestLine() + " to " + target + " via " + proxy);CloseableHttpResponse response = httpclient.execute(target, request);尝试 {System.out.println("----------------------------------------");System.out.println(response.getStatusLine());System.out.println(EntityUtils.toString(response.getEntity()));} 最后 {response.close();}} 最后 {httpclient.close();}}}

您是否使用 CloudantClient java API for Cloudant DB?

答案:

如果是,那么结果证明设置代理时 HTTP 的问题是我们最终的错误(对此很抱歉).我们发布了 1.2.1 并修复了这个问题.您可以从此处下载 jar 文件.(摘自mike-rhodes 的回答)

<小时>

更新

<块引用>

如何在此处指定代理的凭据?

来自 HTTP 身份验证

默认情况下,httpclient 不会抢先提供凭据,它会先创建一个没有身份验证参数的 HTTP 请求.这是设计使然,作为安全预防措施,并且作为规范的一部分.但是,如果您不重试连接,或者您连接到的任何地方都希望您在第一次连接时发送身份验证详细信息,则会导致问题.它还会给请求造成额外的延迟,因为您需要进行多次调用,并导致 401 出现在日志中.

解决方法是使用身份验证缓存来假装您已经连接到服务器一次.这意味着您只会进行一次 HTTP 调用.

CloseableHttpClient httpclient = HttpClientBuilder.create().build();HttpHost targetHost = new HttpHost("localhost", 80, "http");CredentialsProvider credsProvider = new BasicCredentialsProvider();credsProvider.setCredentials(新 AuthScope(targetHost.getHostName(), targetHost.getPort()),new UsernamePasswordCredentials("username", "password"));//创建 AuthCache 实例AuthCache authCache = new BasicAuthCache();//生成 BASIC 方案对象并将其添加到本地 auth 缓存BasicScheme basicAuth = new BasicScheme();authCache.put(targetHost, basicAuth);//将 AuthCache 添加到执行上下文HttpClientContext 上下文 = HttpClientContext.create();context.setCredentialsProvider(credsProvider);context.setAuthCache(authCache);HttpGet httpget = new HttpGet("/");for (int i = 0; i <3; i++) {CloseableHttpResponse 响应 = httpclient.execute(目标主机,httpget,上下文);尝试 {HttpEntity entity = response.getEntity();} 最后 {response.close();}}

<块引用>

注意:您需要信任您要连接的主机,如果您是使用 HTTP,您的用户名和密码将以明文形式发送(好吧,base64,但这不算数).

您还应该使用更具体的 Authscope 而不是依赖 AuthScope.ANY_HOSTAuthScope.ANY_PORT 就像在你的示例.

信用转到 Cetra

相关链接:

  1. HttpClientBuilder 基本身份验证
  2. Apache HttpClient 4.1 - 代理身份验证

I am trying to set proxy for a request I am making using HttpClientBuilder as follows:

        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        UsernamePasswordCredentials usernamePasswordCredentials = new UsernamePasswordCredentials(proxyUser, proxyPassword);
        credsProvider.setCredentials(new AuthScope(proxyHost, proxyPort), usernamePasswordCredentials);

        builder.useSystemProperties();
        builder.setProxy(new HttpHost(proxyHost, proxyPort));
        builder.setDefaultCredentialsProvider(credsProvider);
        builder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());

where builder is:

    HttpClientBuilder builder = HttpClientBuilder.create();

However, I get this exception when I execute this request:

java.lang.RuntimeException: org.apache.http.conn.UnsupportedSchemeException: http protocol is not supported
Caused by: org.apache.http.conn.UnsupportedSchemeException: http protocol is not supported
        at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:108) ~[httpclient-4.5.1.jar:4.5.1]
        at org.apache.http.impl.conn.BasicHttpClientConnectionManager.connect(BasicHttpClientConnectionManager.java:338) ~[httpclient-4.5.1.jar:4.5.1]
        at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:388) ~[httpclient-4.5.1.jar:4.5.1]
        at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) ~[httpclient-4.5.1.jar:4.5.1]
        at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184) ~[httpclient-4.5.1.jar:4.5.1]
        at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88) ~[httpclient-4.5.1.jar:4.5.1]
        at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) ~[httpclient-4.5.1.jar:4.5.1]
        at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184) ~[httpclient-4.5.1.jar:4.5.1]
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82) ~[httpclient-4.5.1.jar:4.5.1]
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107) ~[httpclient-4.5.1.jar:4.5.1]
        at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55) ~[httpclient-4.5.1.jar:4.5.1]

(exception shortened for brevity)

Since this is an HTTP proxy, I don't want to change the scheme to HTTPS, which anyways won't work. How do I get this working?

解决方案

java.lang.RuntimeException: org.apache.http.conn.UnsupportedSchemeException: http protocol is not supported

Why this problem occurs?

Ans: This actually happens because you forget to register a connection socket factory for the 'http' scheme.

Plain 'http' scheme must be used to establish an intermediate connection to the proxy itself before 'https' tunneling could be employed.


For operational purpose, you can try this code:

CloseableHttpClient client = HttpClients.custom()
           .setRoutePlanner(new
 SystemDefaultRoutePlanner(ProxySelector.getDefault()))
           .build();


I would also suggest simple code for your research. Hope it can save you.

ClientExecuteProxy.java

package org.apache.http.examples.client;

import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

/**
 * How to send a request via proxy.
 *
 * @since 4.0
 */
public class ClientExecuteProxy {

    public static void main(String[] args)throws Exception {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        try {
            HttpHost target = new HttpHost("httpbin.org", 443, "https");
            HttpHost proxy = new HttpHost("127.0.0.1", 8080, "http");

            RequestConfig config = RequestConfig.custom()
                    .setProxy(proxy)
                    .build();
            HttpGet request = new HttpGet("/");
            request.setConfig(config);

            System.out.println("Executing request " + request.getRequestLine() + " to " + target + " via " + proxy);

            CloseableHttpResponse response = httpclient.execute(target, request);
            try {
                System.out.println("----------------------------------------");
                System.out.println(response.getStatusLine());
                System.out.println(EntityUtils.toString(response.getEntity()));
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }

}

Are you using using CloudantClient java API for Cloudant DB?

Ans:

If YES, then It turned out the issue with HTTP when setting a proxy was a bug at our end (sorry about that). We released 1.2.1 with the fix for this problem. You can download jar file from here. (Collected from mike-rhodes's answer)


UPDATE

How do I specify the credentials for the proxy here?

From HTTP authentication,

By default, httpclient will not provide credentials preemptively, it will first create a HTTP request without authentication parameters. This is by design, as a security precaution, and as part of the spec. But, this causes issues if you don't retry the connection, or wherever you're connecting to expects you to send authentication details on the first connection. It also causes extra latency to a request, as you need to make multiple calls, and causes 401s to appear in the logs.

The workaround is to use an authentication cache to pretend that you've already connected to the server once. This means you'll only make one HTTP call.

CloseableHttpClient httpclient = HttpClientBuilder.create().build();

HttpHost targetHost = new HttpHost("localhost", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
        new AuthScope(targetHost.getHostName(), targetHost.getPort()),
        new UsernamePasswordCredentials("username", "password"));

// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put(targetHost, basicAuth);

// Add AuthCache to the execution context
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);

HttpGet httpget = new HttpGet("/");
for (int i = 0; i < 3; i++) {
    CloseableHttpResponse response = httpclient.execute(
            targetHost, httpget, context);
    try {
        HttpEntity entity = response.getEntity();

    } finally {
        response.close();
    }
}

N.B: You need to trust the host you're connecting to, and if you're using HTTP, your username and password will be sent in cleartext (well, base64, but that doesn't count).

You should also be using a much more specific Authscope rather than relying on AuthScope.ANY_HOST and AuthScope.ANY_PORT like in your example.

Credit goes to Cetra

Related Links:

  1. HttpClientBuilder basic auth
  2. Apache HttpClient 4.1 - Proxy Authentication

这篇关于如何将 HttpClientBuilder 与 Http 代理一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆