summaryrefslogtreecommitdiffstats
path: root/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java')
-rw-r--r--src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java270
1 files changed, 270 insertions, 0 deletions
diff --git a/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java b/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java
new file mode 100644
index 0000000..6ac8faa
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java
@@ -0,0 +1,270 @@
+package com.btr.proxy.selector.pac;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Script source that will load the content of a PAC file from an webserver.
+ * The script content is cached once it was downloaded.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class UrlPacScriptSource implements PacScriptSource {
+
+ private static final int DEFAULT_CONNECT_TIMEOUT = 15 * 1000; // seconds
+ private static final int DEFAULT_READ_TIMEOUT = 20 * 1000; // seconds
+ public static final String OVERRIDE_CONNECT_TIMEOUT = "com.btr.proxy.url.connectTimeout";
+ public static final String OVERRIDE_READ_TIMEOUT = "com.btr.proxy.url.readTimeout";
+
+ private final String scriptUrl;
+ private String scriptContent;
+ private long expireAtMillis;
+
+ /*************************************************************************
+ * Constructor
+ * @param url the URL to download the script from.
+ ************************************************************************/
+
+ public UrlPacScriptSource(String url) {
+ super();
+ this.expireAtMillis = 0;
+ this.scriptUrl = url;
+ }
+
+ /*************************************************************************
+ * getScriptContent
+ * @see com.btr.proxy.selector.pac.PacScriptSource#getScriptContent()
+ ************************************************************************/
+
+ public synchronized String getScriptContent() throws IOException {
+ if (this.scriptContent == null ||
+ (this.expireAtMillis > 0
+ && this.expireAtMillis > System.currentTimeMillis())) {
+ try {
+ if (this.scriptUrl.startsWith("file:/") || this.scriptUrl.indexOf(":/") == -1) {
+ this.scriptContent = readPacFileContent(this.scriptUrl);
+ } else {
+ this.scriptContent = downloadPacContent(this.scriptUrl);
+ }
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Loading script failed from: {0} with error {1}", this.scriptUrl, e);
+ this.scriptContent = "";
+ throw e;
+ }
+ }
+ return this.scriptContent;
+ }
+
+ /*************************************************************************
+ * Reads a PAC script from a local file.
+ * @param scriptUrl
+ * @return the content of the script file.
+ * @throws IOException
+ * @throws URISyntaxException
+ ************************************************************************/
+
+ private String readPacFileContent(String scriptUrl) throws IOException {
+ try {
+ File file = null;
+ if (scriptUrl.indexOf(":/") == -1) {
+ file = new File(scriptUrl);
+ } else {
+ file = new File(new URL(scriptUrl).toURI());
+ }
+ BufferedReader r = new BufferedReader(new FileReader(file));
+ StringBuilder result = new StringBuilder();
+ try {
+ String line;
+ while ((line = r.readLine()) != null) {
+ result.append(line).append("\n");
+ }
+ } finally {
+ r.close();
+ }
+ return result.toString();
+ } catch (Exception e) {
+ Logger.log(getClass(), LogLevel.ERROR, "File reading error.", e);
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ /*************************************************************************
+ * Downloads the script from a webserver.
+ * @param url the URL to the script file.
+ * @return the script content.
+ * @throws IOException on read error.
+ ************************************************************************/
+
+ private String downloadPacContent(String url) throws IOException {
+ if (url == null) {
+ throw new IOException("Invalid PAC script URL: null");
+ }
+
+ setPacProxySelectorEnabled(false);
+
+ HttpURLConnection con = null;
+ try {
+ con = setupHTTPConnection(url);
+ if (con.getResponseCode() != 200) {
+ throw new IOException("Server returned: "+con.getResponseCode()+" "+con.getResponseMessage());
+ }
+ // Read expire date.
+ this.expireAtMillis = con.getExpiration();
+
+ BufferedReader r = getReader(con);
+ String result = readAllContent(r);
+ r.close();
+ return result;
+ } finally {
+ setPacProxySelectorEnabled(true);
+ if (con != null) {
+ con.disconnect();
+ }
+ }
+ }
+
+ /*************************************************************************
+ * Enables/disables the PAC proxy selector while we download to prevent recursion.
+ * See issue: 26 in the change tracker.
+ ************************************************************************/
+
+ private void setPacProxySelectorEnabled(boolean enable) {
+ PacProxySelector.setEnabled(enable);
+ }
+
+ /*************************************************************************
+ * Reads the whole content available into a String.
+ * @param r to read from.
+ * @return the complete PAC file content.
+ * @throws IOException
+ ************************************************************************/
+
+ private String readAllContent(BufferedReader r) throws IOException {
+ StringBuilder result = new StringBuilder();
+ String line;
+ while ((line = r.readLine()) != null) {
+ result.append(line).append("\n");
+ }
+ return result.toString();
+ }
+
+ /*************************************************************************
+ * Build a BufferedReader around the open HTTP connection.
+ * @param con to read from
+ * @return the BufferedReader.
+ * @throws UnsupportedEncodingException
+ * @throws IOException
+ ************************************************************************/
+
+ private BufferedReader getReader(HttpURLConnection con)
+ throws UnsupportedEncodingException, IOException {
+ String charsetName = parseCharsetFromHeader(con.getContentType());
+ BufferedReader r = new BufferedReader(new InputStreamReader(con.getInputStream(), charsetName));
+ return r;
+ }
+
+ /*************************************************************************
+ * Configure the connection to download from.
+ * @param url to get the pac file content from
+ * @return a HTTPUrlConnecion to this url.
+ * @throws IOException
+ * @throws MalformedURLException
+ ************************************************************************/
+
+ private HttpURLConnection setupHTTPConnection(String url)
+ throws IOException, MalformedURLException {
+ HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(Proxy.NO_PROXY);
+ con.setConnectTimeout(getTimeOut(OVERRIDE_CONNECT_TIMEOUT, DEFAULT_CONNECT_TIMEOUT));
+ con.setReadTimeout(getTimeOut(OVERRIDE_READ_TIMEOUT, DEFAULT_READ_TIMEOUT));
+ con.setInstanceFollowRedirects(true);
+ con.setRequestProperty("accept", "application/x-ns-proxy-autoconfig, */*;q=0.8");
+ return con;
+ }
+
+ /*************************************************************************
+ * Gets the timeout value from a property or uses the given default value if
+ * the property cannot be parsed.
+ * @param overrideProperty the property to define the timeout value in milliseconds
+ * @param defaultValue the default timeout value in milliseconds.
+ * @return the value to use.
+ ************************************************************************/
+
+ protected int getTimeOut(String overrideProperty, int defaultValue) {
+ int timeout = defaultValue;
+ String prop = System.getProperty(overrideProperty);
+ if (prop != null && prop.trim().length() > 0) {
+ try {
+ timeout = Integer.parseInt(prop.trim());
+ } catch (NumberFormatException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Invalid override property : {0}={1}", overrideProperty, prop);
+ // In this case use the default value.
+ }
+ }
+ return timeout;
+ }
+
+ /*************************************************************************
+ * Response Content-Type could be something like this:
+ * application/x-ns-proxy-autoconfig; charset=UTF-8
+ * @param contentType header field.
+ * @return the extracted charset if set else a default charset.
+ ************************************************************************/
+
+ String parseCharsetFromHeader(String contentType) {
+ String result = "ISO-8859-1";
+ if (contentType != null) {
+ String[] paramList = contentType.split(";");
+ for (String param : paramList) {
+ if (param.toLowerCase().trim().startsWith("charset") && param.indexOf("=") != -1) {
+ result = param.substring(param.indexOf("=")+1).trim();
+ }
+ }
+ }
+ return result;
+ }
+
+ /***************************************************************************
+ * @see java.lang.Object#toString()
+ **************************************************************************/
+ @Override
+ public String toString() {
+ return this.scriptUrl;
+ }
+
+ /*************************************************************************
+ * isScriptValid
+ * @see com.btr.proxy.selector.pac.PacScriptSource#isScriptValid()
+ ************************************************************************/
+
+ public boolean isScriptValid() {
+ try {
+ String script = getScriptContent();
+ if (script == null || script.trim().length() == 0) {
+ Logger.log(getClass(), LogLevel.DEBUG, "PAC script is empty. Skipping script!");
+ return false;
+ }
+ if (script.indexOf("FindProxyForURL") == -1) {
+ Logger.log(getClass(), LogLevel.DEBUG, "PAC script entry point FindProxyForURL not found. Skipping script!");
+ return false;
+ }
+ return true;
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "File reading error: {0}", e);
+ return false;
+ }
+ }
+
+}