如何通过设置属性使用HttpsURLConnection代理?

16

网络环境:

Https客户端 <=============> 代理服务器 <==============> Https服务器
                192.168.17.11 <-----外网------> 192.168.17.22
10.100.21.10 <----内网----> 10.100.21.11

注:Http客户端没有默认网关,但可以ping通10.100.21.11

说明:

操作系统:3个主机上的Ubuntu 12.04
Https客户端:使用java(openjdk-6)实现,有一个网络接口。
代理服务器:Apache2.2,有两个网络接口。
Https服务器:Tomcat6,有一个网络接口。

我使用了两种方法通过代理实现httpsurlconnection:
(为方便起见,我没有写出检查serverTrustedhostnameVerifier问题的ssl处理函数。如有需要,我将更新。)

1.Proxy类

InetSocketAddress proxyInet = new InetSocketAddress("10.100.21.11",80);
Proxy proxy = new Proxy(Proxy.Type.HTTP, proxyInet);
URL httpsUrl = new URL("https://192.168.17.22:8443/test");
HttpsURLConnection httpsCon = (HttpsURLConnection) httpsUrl.openConnection(proxy);

httpsCon.setDoOutput(true);
httpsCon.setDoInput(true);
httpsCon.setRequestMethod("POST");
OutputStream out = httpsCon.getOutputStream();
OutputStreamWriter owriter = new OutputStreamWriter(out);

owriter.write("<request>test</request>");
owriter.flush();
owriter.close();
...

这种方法可行,我观察到数据包的流动也符合我的期望。
HttpClient ---> ProxyServer ---> HttpServer

但是,当我使用 setProperty 方法时:

2.setProperty

System.setProperty("http.proxySet", "true");
System.setProperty("http.proxyHost",10.100.21.11);
System.setProperty("http.proxyPort","80");

URL httpsUrl = new URL("https://192.168.17.22:8443/test");
HttpsURLConnection httpsCon = (HttpsURLConnection)httpsUrl.openConnection();

httpsCon.setDoOutput(true);
httpsCon.setDoInput(true);
httpsCon.setRequestMethod("POST");
OutputStream out = httpsCon.getOutputStream();
OutputStreamWriter owriter = new OutputStreamWriter(out);

owriter.write("<request>test</request>");
owriter.flush();
owriter.close();
...

我遇到了一个 NoRouteToHostException: Network is unreachable 的错误。
这让我感到困惑,因为在 HttpClient 和 ProxyServer 之间没有看到任何数据包传输。
但是 HttpClient 可以 ping 到 ProxyServer(10.100.12.10 ping 10.100.21.11)。

于是我去掉了代理设置(不使用代理):
同样得到了 NoRouteToHostException: Network is unreachable 的错误提示。
我认为这是合理的,因为连外网都没有路线。

我猜测好像是 httpsUrlConnection 内部的 setProperty 方法会检查该 URL 是否能够被访问。

但这很奇怪,第一种方法可以成功。

有什么想法吗?或者第一种和第二种方法之间有什么不同吗?

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

更新

System.setProperty("https.proxyHost",10.100.21.11);
System.setProperty("https.proxyPort","80"); 

它可以工作,数据包流是我所期望的正确的流。
但是对我来说设置 https.proxyPort=443 是不可行的。

System.setProperty("https.proxyPort","443");

它将抛出以下异常:

java.net.SocketException: Unexpected end of file from server 
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:770)
....

所以我认为Apache代理也必须进行正确的配置修改。


没有名为http.proxySet的属性。 - user207421
是的,这个属性真的不需要设置。谢谢~ - CJeremy
3个回答

19

您的URL连接是https,而您只设置了http代理。

尝试设置https代理。

//System.setProperty("https.proxySet", "true"); 
 System.setProperty("https.proxyHost",10.100.21.11);
 System.setProperty("https.proxyPort","443");

编辑 @EJP是正确的。没有https.proxySet.. 我复制了你的原始问题并包含在答案中。


没有名为 https.proxySet 的属性。 - user207421
我认为在发送请求时,使用"https" URL是正确的答案。而 setProperty 的关键是 https.proxyHost/proxyPort 而不是 http.proxyHost/porxyPort。 - CJeremy

12
您需要为此创建一个Proxy对象。以下是创建方法:
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyServer, Integer.parseInt(proxyPort)));

现在使用这个代理来创建HttpURLConnection对象。

HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(proxy);

如果您必须为代理设置凭据,请设置Proxy-Authorization请求属性:

String uname_pwd = proxyUsername + ":" + proxyPassword
String authString = "Basic " + new sun.misc.BASE64Encoder().encode(uname_pwd.getBytes())
connection.setRequestProperty("Proxy-Authorization", authString);

最后,您连接:

connection.connect();

谢谢您的回复。 首先,我使用的第一种方法是创建代理对象,对我来说可行。 其次,我需要的URL是“https”,所以我认为使用HttpsURLConnection是正确的。 - CJeremy

1

谢谢 @divinedragon!

在 Kotlin 上的相同代码:

 fun testProxy(login: String, pass: String, proxyData: ProxyData): String {
    val url = URL("http://api.ipify.org")
    val proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress(proxyData.ip, proxyData.port))
    val connection = url.openConnection(proxy) as HttpURLConnection

    val loginPass = "$login:$pass"
    val encodedLoginPass = Base64.getEncoder().encodeToString(loginPass.toByteArray())
    val authString = "Basic $encodedLoginPass"
    connection.setRequestProperty("Proxy-Authorization", authString);
    with(connection) {
        requestMethod = "GET"  // optional default is GET
        connectTimeout = 2000
        readTimeout = 2000
        return inputStream.bufferedReader().readText()
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接