All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.btr.proxy.selector.pac.PacProxySelector Maven / Gradle / Ivy

There is a newer version: 2.0.2
Show newest version
package com.btr.proxy.selector.pac;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.lang3.StringUtils;

/*****************************************************************************
 * ProxySelector that will use a PAC script to find an proxy for a given URI.
 *
 * @author Bernd Rosstauscher ([email protected]) Copyright 2009
 ****************************************************************************/
public class PacProxySelector extends ProxySelector {
  protected static final Logger log = Logger.getLogger(PacProxySelector.class
      .getCanonicalName());

  private final static int PROXY_TYPE_LEN = 6;
  private static final String PAC_PROXY = "PROXY";
  private static final String PAC_SOCKS = "SOCKS";
  private static final String PAC_DIRECT = "DIRECT";

  private PacScriptParser pacScriptParser;

  private static volatile boolean enabled = true;

  /*************************************************************************
   * Constructor
   *
   * @param pacSource
   *          the source for the PAC file.
   ************************************************************************/

  public PacProxySelector(PacScriptSource pacSource) {
    super();
    selectEngine(pacSource);
  }

  /*************************************************************************
   * Can be used to enable / disable the proxy selector. If disabled it will
   * return DIRECT for all urls.
   *
   * @param enable
   *          the new status to set.
   ************************************************************************/

  public static void setEnabled(boolean enable) {
    enabled = enable;
  }

  /*************************************************************************
   * Checks if the selector is currently enabled.
   *
   * @return true if enabled else false.
   ************************************************************************/

  public static boolean isEnabled() {
    return enabled;
  }

  /*************************************************************************
   * Selects one of the available PAC parser engines.
   *
   * @param pacSource
   *          to use as input.
   ************************************************************************/

  private void selectEngine(PacScriptSource pacSource) {
    try {
      log.log(Level.INFO, "Using javax.script JavaScript engine.");
      this.pacScriptParser = new JavaxPacScriptParser(pacSource);
    } catch (Exception e) {
      log.log(Level.INFO, "PAC parser error.", e);
    }
  }

  /*************************************************************************
   * connectFailed
   *
   * @see java.net.ProxySelector#connectFailed(java.net.URI,
   *      java.net.SocketAddress, java.io.IOException)
   ************************************************************************/
  @Override
  public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
    // Not used.
  }

  /*************************************************************************
   * select
   *
   * @see java.net.ProxySelector#select(java.net.URI)
   ************************************************************************/
  @Override
  public List select(URI uri) {
    if (uri == null) {
      throw new IllegalArgumentException("URI must not be null.");
    }

    // Fix for Java 1.6.16+ where we get a infinite loop because
    // URL.connect(Proxy.NO_PROXY) does not work as expected.
    if (!enabled) {
      return noProxyList();
    }

    return findProxy(uri);
  }

  /*************************************************************************
   * Evaluation of the given URL with the PAC-file.
   *
   * Two cases can be handled here: DIRECT Fetch the object directly from the
   * content HTTP server denoted by its URL PROXY name:port Fetch the object via
   * the proxy HTTP server at the given location (name and port)
   *
   * @param uri
   *          URI to be evaluated.
   * @return Proxy-object list as result of the evaluation.
   ************************************************************************/

  private List findProxy(URI uri) {
    try {
      List proxies = new ArrayList();
      String parseResult = this.pacScriptParser.evaluate(uri.toString(),
          uri.getHost());
      String[] proxyDefinitions = parseResult.split("[;]");
      for (String proxyDef : proxyDefinitions) {
        if (!StringUtils.isBlank(proxyDef)) {
          proxies.add(buildProxyFromPacResult(proxyDef));
        }
      }
      return proxies;
    } catch (ProxyEvaluationException e) {
      log.log(Level.INFO, "PAC resolving error.", e);
      return noProxyList();
    }
  }

  /*************************************************************************
   * The proxy evaluator will return a proxy string. This method will take this
   * string and build a matching Proxy for it.
   *
   * @param pacResult
   *          the result from the PAC parser.
   * @return a Proxy
   ************************************************************************/
  public static Proxy buildProxyFromPacResult(String pacResult) {
    if (StringUtils.isBlank(pacResult) || pacResult.trim().toUpperCase(Locale.ENGLISH).startsWith(PAC_DIRECT)) {
      return Proxy.NO_PROXY;
    }
    String proxyDef = pacResult.trim();
    Proxy.Type type = Proxy.Type.HTTP;
    if (proxyDef.toUpperCase(Locale.ENGLISH).startsWith(PAC_SOCKS)) {
      type = Proxy.Type.SOCKS;
    } else if (!proxyDef.toUpperCase(Locale.ENGLISH).startsWith(PAC_PROXY)) {
      return Proxy.NO_PROXY;
    }

    String host = proxyDef.substring(PROXY_TYPE_LEN);
    Integer port = DEFAULT_PROXY_PORT;

    // Split port from host
    int indexOfPort = host.indexOf(':');
    if (indexOfPort != -1) {
      port = Integer.parseInt(host.substring(indexOfPort + 1).trim());
      host = host.substring(0, indexOfPort).trim();
    }

    SocketAddress adr = InetSocketAddress.createUnresolved(host, port);
    return new Proxy(type, adr);
  }

  public static final int DEFAULT_PROXY_PORT = 80;

  private static List noProxyList;

  /*************************************************************************
   * Gets an unmodifiable proxy list that will have as it's only entry an DIRECT
   * proxy.
   *
   * @return a list with a DIRECT proxy in it.
   ************************************************************************/

  public static synchronized List noProxyList() {
    if (noProxyList == null) {
      ArrayList list = new ArrayList(1);
      list.add(Proxy.NO_PROXY);
      noProxyList = Collections.unmodifiableList(list);
    }
    return noProxyList;
  }

  /*************************************************************************
   * Build a PAC proxy selector for the given URL.
   *
   * @param url
   *          to fetch the PAC script from.
   * @return a PacProxySelector or null if it is not possible to build a working
   *         selector.
   ************************************************************************/

  public static PacProxySelector buildPacSelectorForUrl(String url) {
    PacProxySelector result = null;
    PacScriptSource pacSource = new UrlPacScriptSource(url);
    if (pacSource.isScriptValid()) {
      result = new PacProxySelector(pacSource);
    }
    return result;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy