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

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


  • 问题:“如果我的ProxySelector只返回一个HTTP代理,为什么要尝试通过SOCKS建立连接?”

  • 2问题:“是否有替代方法,为每个连接定义不同的代理?”

    这是我的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);
    }
    }
    

    这是测试类:
    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");
    }
    }
    

    最佳答案

    好的,我发现了问题。
    如果请求的URL不包含端口,则HttpURLConnection会对OwnProxySelector.select()进行两次。
    首先,HttpURLConnection用URI调用select(),其方案为“http”,但没有端口。 select()检查主机地址和端口是否与intranetAddress或ExtranetAddress等效。这不匹配,因为未提供端口。因此,选择返回直接连接的代理。
    在第二个HttpURLConnection上,使用URI以及方案“socket”和端口80调用带有URI的select()。因此,由于select()检查主机地址和端口,而不是方案,所以它返回了HTTP代理。
    现在,这是我的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("https")){
                    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);
    }
    
    }
    但是令我感到奇怪的是,当它从第一次调用获得直接连接Proxy时,HttpURLConnection进行了select()的第二次调用。

    10-08 15:16