java 使用不同的代理连接到特定地址

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2175742/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 19:52:56  来源:igfitidea点击:

Connecting with different Proxies to specific addresses

javahttpproxysocks

提问by Crazy Doc

I am developing a Java webservice application (with JAX-WS) that has to use two different proxies to establish separated connections to internet and an intranet. As solution I tried to write my own java.net.ProxySelectorthat returns a java.net.Proxyinstance (of type HTTP) for internet or intranet.

我正在开发一个 Java webservice 应用程序(使用 JAX-WS),它必须使用两个不同的代理来建立到 Internet 和 Intranet 的独立连接。作为解决方案,我尝试编写自己的java.net.ProxySelector,它为 Internet 或 Intranet返回一个java.net.Proxy实例(HTTP 类型)。

In a little test application I try to download webpage via URL.openConnection(), and before I replaced the default ProxySelector with my own. But it results in an exception:

在一个小测试应用程序中,我尝试通过URL.openConnection()下载网页,然后在我用我自己的替换默认 ProxySelector 之前。但这会导致异常:

java.net.SocketException: Unknown proxy type : HTTP at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:370) at java.net.Socket.connect(Socket.java:519) at java.net.Socket.connect(Socket.java:469) at sun.net.NetworkClient.doConnect(NetworkClient.java:163) at sun.net.www.http.HttpClient.openServer(HttpClient.java:394) at sun.net.www.http.HttpClient.openServer(HttpClient.java:529) at sun.net.www.http.HttpClient.(HttpClient.java:233) at sun.net.www.http.HttpClient.New(HttpClient.java:306) at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:844) at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:792) at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:703) at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1026) at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:373) at norman.test.ProxyTest.conntectToRmViaProxy(ProxyTest.java:42) at norman.test.ProxyTest.main(ProxyTest.java:65)

java.net.SocketException:未知代理类型:HTTP at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:370) at java.net.Socket.connect(Socket.java:519) at java.net.Socket.connect( Socket.java:469) at sun.net.NetworkClient.doConnect(NetworkClient.java:163) at sun.net.www.http.HttpClient.openServer(HttpClient.java:394) at sun.net.www.http.HttpClient .openServer(HttpClient.java:529) at sun.net.www.http.HttpClient.(HttpClient.java:233) at sun.net.www.http.HttpClient.New(HttpClient.java:306) at sun.net .www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:844) at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:792) at sun.net.www.protocol.http.HttpURLConnection .connect(HttpURLConnection.java:703) 在 sun.net.www.protocol.http。HttpURLConnection.getInputStream(HttpURLConnection.java:1026) at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:373) at norman.test.ProxyTest.conntectToRmViaProxy(ProxyTest.java:42) at norman.test.ProxyTest.main(ProxyTest) .java:65)

  1. Question: "Why tries the application to establish a connection via SOCKS, if my ProxySelector only returns a HTTP Proxy?"
  1. 问题:“如果我的 ProxySelector 只返回 HTTP 代理,为什么要尝试应用程序通过 SOCKS 建立连接?”

2 Question: "Is there a alternative, to define different proxies for each connection?"

2 问题:“有没有替代方案,为每个连接定义不同的代理?”

This is my ProxySelector:

这是我的 ProxySelector:

public class OwnProxySelector extends ProxySelector {
private Proxy intranetProxy;
private Proxy extranetProxy;
private Proxy directConnection = Proxy.NO_PROXY;
private URI intranetAddress;
private URI extranetAddress;

/* (non-Javadoc)
 * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException)
 */
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
    // Nothing to do
}

/* (non-Javadoc)
 * @see java.net.ProxySelector#select(java.net.URI)
 */
public List select(URI uri) {
    ArrayList<Proxy> result = new ArrayList<Proxy>();

    if(intranetAddress.getHost().equals(uri.getHost()) && intranetAddress.getPort()==uri.getPort()){
        result.add(intranetProxy);
        System.out.println("Adding intranet Proxy!");
    }
    else if(extranetAddress.getHost().equals(uri.getHost()) && extranetAddress.getPort()==uri.getPort()){
        result.add(extranetProxy);
        System.out.println("Adding extranet Proxy!");
    }
    else{
        result.add(directConnection);
        System.out.println("Adding direct connection!");
    }

    return result;
}

public void setIntranetProxy(String proxyAddress, int proxyPort){
    if(proxyAddress==null || proxyAddress.isEmpty()){
        intranetProxy = Proxy.NO_PROXY;
    }
    else{
        SocketAddress address = new InetSocketAddress(proxyAddress, proxyPort);
        intranetProxy = new Proxy(Proxy.Type.HTTP, address);
    }
}

public void setExtranetProxy(String proxyAddress, int proxyPort){
    if(proxyAddress==null || proxyAddress.isEmpty()){
        extranetProxy = Proxy.NO_PROXY;
    }
    else{
        SocketAddress address = new InetSocketAddress(proxyAddress, proxyPort);
        extranetProxy = new Proxy(Proxy.Type.HTTP, address);
    }
}

public void clearIntranetProxy(){
    intranetProxy = Proxy.NO_PROXY;
}

public void clearExtranetProxy(){
    extranetProxy = Proxy.NO_PROXY;
}

public void setIntranetAddress(String address) throws URISyntaxException{
    intranetAddress = new URI(address);
}

public void setExtranetAddress(String address) throws URISyntaxException{
    extranetAddress = new URI(address);
}
}

This is the test class:

这是测试类:

public class ProxyTest {
OwnProxySelector ownSelector = new OwnProxySelector();

public ProxyTest(){
    ownSelector.setIntranetProxy("intranet.proxy", 8123);
    try {
        ownSelector.setIntranetAddress("http://intranet:80");
    } catch (URISyntaxException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    ownSelector.setExtranetProxy("", 0);
    try {
        ownSelector.setExtranetAddress("http://www.example.com:80");
    } catch (URISyntaxException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }


    ProxySelector.setDefault(ownSelector);
}

public void conntectToRmViaProxy(boolean internal, String connectAddress){
    try {
        URL url = new URL(connectAddress);

        HttpURLConnection conn = (HttpURLConnection)url.openConnection();

        conn.setRequestMethod("GET");
          if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) {
            System.out.println(conn.getResponseMessage());
          }
          else{
              BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
              int tmp = reader.read();
              while(tmp != -1){
                  System.out.print((char)tmp);
                  tmp = reader.read();
              }
          }

    } catch (MalformedURLException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

public static void main(String[] args){
    ProxyTest proxyText = new ProxyTest();
    proxyText.conntectToRmViaProxy(true, "http://intranet:80");
}
}

采纳答案by Crazy Doc

Ok, I have found the problem.

好的,我找到了问题所在。

The HttpURLConnection did the OwnProxySelector.select() twice if the requested URL does not contain a port.

如果请求的 URL 不包含端口,则 HttpURLConnection 执行 OwnProxySelector.select() 两次。

At first, HttpURLConnection invoked the select() with an URI, with the Scheme of "http" but no port. The select() checks whether the host address and port are euqal to intranetAddress or extranetAddress. This didn't match, because the port was not given. So the select return a Proxy for a direct connection.

起初,HttpURLConnection 使用 URI 调用 select(),使用“http”方案但没有端口。select() 检查主机地址和端口是否等同于内联网地址或外联网地址。这不匹配,因为没有给出端口。所以选择返回一个用于直接连接的代理。

At the second HttpURLConnection invoked the select() with an URI, with the Scheme of "socket" and port 80. So, because the select() checks host address and port, but not the scheme, it returned a HTTP proxy.

在第二次 HttpURLConnection 调用带有 URI 的 select() 时,使用“socket”和端口 80 的方案。因此,因为 select() 检查主机地址和端口,而不是方案,它返回一个 HTTP 代理。

Now here is my corrected version of OwnProxySelector. It checks the scheme and sets the default port for HTTP or HTTPS if the port is not given by the URI. Also it asks the Java standard ProxySelector, if no HTTP or HTTPS scheme is given.

现在这是我修正过的 OwnProxySelector 版本。如果 URI 未提供端口,它会检查方案并设置 HTTP 或 HTTPS 的默认端口。如果没有给出 HTTP 或 HTTPS 方案,它还会询问 Java 标准 ProxySelector。

public class OwnProxySelector extends ProxySelector {
private ProxySelector defaultProxySelector;
private Proxy intranetProxy;
private Proxy extranetProxy;
private Proxy directConnection = Proxy.NO_PROXY;
private URI intranetAddress;
private URI extranetAddress;


public OwnProxySelector(ProxySelector defaultProxySelector){
    this.defaultProxySelector = defaultProxySelector;
}

/* (non-Javadoc)
 * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException)
 */
public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
    // Nothing to do
}

/* (non-Javadoc)
 * @see java.net.ProxySelector#select(java.net.URI)
 */
public List select(URI uri) {
    ArrayList<Proxy> result = new ArrayList<Proxy>();

    if(uri.getScheme().equalsIgnoreCase("http") || uri.getScheme().equalsIgnoreCase("https")){
        int uriPort = uri.getPort();

        // set default http and https ports if port is not given in URI
        if(uriPort<1){
            if(uri.getScheme().equalsIgnoreCase("http")){
                uriPort = 80;
            }
            else if(uri.getScheme().equalsIgnoreCase("http")){
                uriPort = 443;
            }
        }

        if(intranetAddress.getHost().equals(uri.getHost()) && intranetAddress.getPort()==uriPort){
            result.add(intranetProxy);
            System.out.println("Adding intranet Proxy!");
        }
        else if(extranetAddress.getHost().equals(uri.getHost()) && extranetAddress.getPort()==uriPort){
            result.add(extranetProxy);
            System.out.println("Adding extranet Proxy!");
        }
    }

    if(result.isEmpty()){
        List<Proxy> defaultResult = defaultProxySelector.select(uri);
        if(defaultResult!=null && !defaultResult.isEmpty()){
            result.addAll(defaultResult);
            System.out.println("Adding Proxis from default selector.");
        }
        else{
            result.add(directConnection);
            System.out.println("Adding direct connection, because requested URI does not match any Proxy");
        }
    }

    return result;
}

public void setIntranetProxy(String proxyAddress, int proxyPort){
    if(proxyAddress==null || proxyAddress.isEmpty()){
        intranetProxy = Proxy.NO_PROXY;
    }
    else{
        SocketAddress address = new InetSocketAddress(proxyAddress, proxyPort);
        intranetProxy = new Proxy(Proxy.Type.HTTP, address);
    }
}

public void setExtranetProxy(String proxyAddress, int proxyPort){
    if(proxyAddress==null || proxyAddress.isEmpty()){
        extranetProxy = Proxy.NO_PROXY;
    }
    else{
        SocketAddress address = new InetSocketAddress(proxyAddress, proxyPort);
        extranetProxy = new Proxy(Proxy.Type.HTTP, address);
    }
}

public void clearIntranetProxy(){
    intranetProxy = Proxy.NO_PROXY;
}

public void clearExtranetProxy(){
    extranetProxy = Proxy.NO_PROXY;
}

public void setIntranetAddress(String address) throws URISyntaxException{
    intranetAddress = new URI(address);
}

public void setExtranetAddress(String address) throws URISyntaxException{
    extranetAddress = new URI(address);
}

}

}

But it is curious to me, that the HttpURLConnection did a second invoke of select(), when it got a direct connection Proxy from the first invoke.

但令我感到好奇的是,当 HttpURLConnection 从第一次调用获得直接连接代理时,它对 select() 进行了第二次调用。