Handling Proxy Auto Config (PAC) scripts in Java

The built in support for detecting proxies in Java is rather limited. In a recent project I ran into problems due to the lack of visible support for PAC (even though it is part of Java Web Start). In this post I outline how this can be solved by running JavaScript within a Java application and letting the JavaScript call methods defined in Java.

Before writing your own implementation it is worth noting that there are existing solutions available, e.g. Proxy Vole.

What is PAC?
Proxy auto-config was defined by Netscape for their Netscape Navigator 2.0 a long time ago (1996). It is a very dynamic method for configuring what proxy to use for accessing a specific URL. It works in the following way:

  • A PAC file holding a JavaScript is placed on a web server. The file is required to contain the following JavaScript function FindProxyForURL(url, host).
  • The client is told to use the PAC file. Either by configuring the URL or using WPAD. How to find the stored URL depends on the OS.
  • Before attempting to access a URL the client will call the JavaScript function FindProxyForURL(url, host) to get the proxy configuration to use. The configuration is in the form of a string with ; as separator, for example:
    PROXY proxy1.server.com:8080;DIRECT

    This means use proxy1 to connect to the URL, if this fails don’t use any proxy.

Using JavaScript from within Java to Evaluating PAC scripts
How can the FindProxyForURL(url,host) possibly find what URL to use? The answer is that it can use a number of pre-defined functions, see Navigator Proxy Auto-Config File Format. At first look the number of functions looks daunting but most of them can easily be implemented. We wanted to use Java as much as possible and therefore created a Java class holding methods with the same signatures as the JavaScript functions. It is called PACScriptMethods in the example code below which shows how the PAC script can be evaluated:

// First we get a JavaScript engine
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");

// Create a Java binding to be used from the JavaScript execution
engine.put("MyJavaPacImpl", new PACScriptMethods());

// Add the required JavaScript methods by bridging to the Java binding
for(Method method : PACScriptMethods.class.getMethods())
{
   String bridgeFunctionDef = defineBridgeFunction(
         method.getName(),
         method.getParameterTypes().length);
   engine.eval(bridgeFunctionDef);
}

// The engine is now ready to be used to evaluate the PAC script
// (passed in as a string)
engine.eval(pacScript);
// Now let's use the FindProxyForURL function to get the proxy
// for the URL we want to access
Invocable invocableEngine = (Invocable) engine;
Object resultObj = invocableEngine.invokeFunction(
      "FindProxyForURL",
      url.toString(),
      url.getHost());
String proxyConfig = String.valueOf(resultObj);

The careful reader may have spotted that I left out the defineBridgeFunction(...). It generates the strings defining JavaScript methods that simply call the method in our Java object. For example defineBridgeFunction("myFunction", 2) results in:

myFunction=function(arg0, arg1){
   return MyJavaPacImpl.myFunction(arg0, arg1);
}

This uses the binding set on the JavaScript engine to call the corresponding Java method (same name and number of arguments).

The proxy configuration in proxyConfig can now easily be parsed (remember “;” as separator) to create a list of standard Java Proxy objects holding the proxy settings.

This Post Has 3 Comments

  1. Nice share on working with proxies in java. btw, I have also blogged on using proxies in Java program. Do let me know how do you like it.

  2. In my case I didn’t have a chance to retrieve the PAC-script. What worked for me — set up the strategy (from Proxy Vole Wiki Usage Examples) and then use the code from the previous comment. Thanks all!

  3. Still working fine using proxy-vole mentioned above with JDK 8. Thanks.

Leave a Reply

Close Menu