From 602b232a06ede86999aa362a12eb28cbc782dc1d Mon Sep 17 00:00:00 2001 From: Jason Monk Date: Wed, 3 Jul 2013 17:04:33 -0400 Subject: Add PAC File support for proxy configuration PAC (Proxy auto-config) files contain a single javascript function, FindProxyForURL(url, host). It gets called to determine what proxy should be used for a specific request. This adds PAC support to the system. The ProxyProperties has been modified to hold the PAC file when one is present. The Proxy method setHttpProxySystemProperty has been modified to insert a PacProxySelector as the default ProxySelector when it is required. This new ProxySelector makes calls to the ConnectivityService to parse the PAC file. The ConnectivityService and the WifiConfigStore have been modified to support saving the extra PAC file data. The ConnectivityService now has a class attached (PacProxyNative) that interfaces to the native calls for PAC files. The parsing of the PAC file is handled by libpac (which is being added to external/) which utilizes libv8 to parse the javascript. As a fallback to applications that don't use the java ProxySelector, the proxy is setup to point to a local proxy server that will handle the pac parsing. bug:10182711 Change-Id: I5eb8df893c632fd3e1b732385cb7720ad646f401 --- core/java/android/net/PacProxySelector.java | 80 +++++++++++++++++++++++++++++ core/java/android/net/Proxy.java | 48 ++++++++--------- core/java/android/net/ProxyProperties.java | 45 ++++++++++++++-- 3 files changed, 146 insertions(+), 27 deletions(-) create mode 100644 core/java/android/net/PacProxySelector.java (limited to 'core/java/android/net') diff --git a/core/java/android/net/PacProxySelector.java b/core/java/android/net/PacProxySelector.java new file mode 100644 index 0000000..be3a31d --- /dev/null +++ b/core/java/android/net/PacProxySelector.java @@ -0,0 +1,80 @@ + +package android.net; + +import android.os.RemoteException; +import android.os.ServiceManager; + +import com.android.net.IProxyService; +import com.google.android.collect.Lists; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.MalformedURLException; +import java.net.Proxy; +import java.net.Proxy.Type; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.List; + +/** + * @hide + */ +public class PacProxySelector extends ProxySelector { + public static final String PROXY_SERVICE = "com.android.net.IProxyService"; + private IProxyService mProxyService; + + public PacProxySelector() { + mProxyService = IProxyService.Stub.asInterface( + ServiceManager.getService(PROXY_SERVICE)); + } + + @Override + public List select(URI uri) { + String response = null; + String urlString; + try { + urlString = uri.toURL().toString(); + } catch (MalformedURLException e) { + urlString = uri.getHost(); + } + try { + response = mProxyService.resolvePacFile(uri.getHost(), urlString); + } catch (RemoteException e) { + e.printStackTrace(); + } + + return parseResponse(response); + } + + private static List parseResponse(String response) { + String[] split = response.split(";"); + List ret = Lists.newArrayList(); + for (String s : split) { + String trimmed = s.trim(); + if (trimmed.equals("DIRECT")) { + ret.add(java.net.Proxy.NO_PROXY); + } else if (trimmed.startsWith("PROXY ")) { + String[] hostPort = trimmed.substring(6).split(":"); + String host = hostPort[0]; + int port; + try { + port = Integer.parseInt(hostPort[1]); + } catch (Exception e) { + port = 8080; + } + ret.add(new Proxy(Type.HTTP, new InetSocketAddress(host, port))); + } + } + if (ret.size() == 0) { + ret.add(java.net.Proxy.NO_PROXY); + } + return ret; + } + + @Override + public void connectFailed(URI uri, SocketAddress address, IOException failure) { + + } + +} diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java index a408ea0..5b38f57 100644 --- a/core/java/android/net/Proxy.java +++ b/core/java/android/net/Proxy.java @@ -18,38 +18,25 @@ package android.net; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.content.ContentResolver; import android.content.Context; -import android.database.ContentObserver; -import android.net.ProxyProperties; -import android.os.Handler; -import android.os.SystemProperties; import android.text.TextUtils; -import android.provider.Settings; import android.util.Log; -import java.net.InetAddress; + +import org.apache.http.HttpHost; +import org.apache.http.HttpRequest; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.routing.HttpRoutePlanner; +import org.apache.http.conn.scheme.SchemeRegistry; +import org.apache.http.protocol.HttpContext; + import java.net.InetSocketAddress; import java.net.ProxySelector; -import java.net.SocketAddress; import java.net.URI; -import java.net.UnknownHostException; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import junit.framework.Assert; - -import org.apache.http.conn.routing.HttpRoute; -import org.apache.http.conn.routing.HttpRoutePlanner; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.HttpHost; -import org.apache.http.HttpRequest; -import org.apache.http.impl.conn.ProxySelectorRoutePlanner; -import org.apache.http.protocol.HttpContext; - /** * A convenience class for accessing the user and default proxy * settings. @@ -60,6 +47,9 @@ public final class Proxy { private static final boolean DEBUG = false; private static final String TAG = "Proxy"; + private static final ProxySelector sDefaultProxySelector; + private static PacProxySelector sPacProxySelector; + /** * Used to notify an app that's caching the default connection proxy * that either the default connection or its proxy has changed. @@ -96,6 +86,7 @@ public final class Proxy { static { HOSTNAME_PATTERN = Pattern.compile(HOSTNAME_REGEXP); EXCLLIST_PATTERN = Pattern.compile(EXCLLIST_REGEXP); + sDefaultProxySelector = ProxySelector.getDefault(); } /** @@ -325,16 +316,19 @@ public final class Proxy { String host = null; String port = null; String exclList = null; + String pacFileUrl = null; if (p != null) { host = p.getHost(); port = Integer.toString(p.getPort()); exclList = p.getExclusionList(); + pacFileUrl = p.getPacFileUrl(); } - setHttpProxySystemProperty(host, port, exclList); + setHttpProxySystemProperty(host, port, exclList, pacFileUrl); } /** @hide */ - public static final void setHttpProxySystemProperty(String host, String port, String exclList) { + public static final void setHttpProxySystemProperty(String host, String port, String exclList, + String pacFileUrl) { if (exclList != null) exclList = exclList.replace(",", "|"); if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList); if (host != null) { @@ -358,5 +352,13 @@ public final class Proxy { System.clearProperty("http.nonProxyHosts"); System.clearProperty("https.nonProxyHosts"); } + if ((pacFileUrl != null) && !TextUtils.isEmpty(pacFileUrl)) { + if (sPacProxySelector == null) { + sPacProxySelector = new PacProxySelector(); + } + ProxySelector.setDefault(sPacProxySelector); + } else { + ProxySelector.setDefault(sDefaultProxySelector); + } } } diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyProperties.java index 9c4772b..76aea9f 100644 --- a/core/java/android/net/ProxyProperties.java +++ b/core/java/android/net/ProxyProperties.java @@ -20,9 +20,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; -import android.util.Log; -import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.util.Locale; @@ -38,17 +36,30 @@ public class ProxyProperties implements Parcelable { private String mExclusionList; private String[] mParsedExclusionList; + private String mPacFileUrl; + public static final String LOCAL_EXCL_LIST = ""; + public static final int LOCAL_PORT = 8182; + public static final String LOCAL_HOST = "localhost"; + public ProxyProperties(String host, int port, String exclList) { mHost = host; mPort = port; setExclusionList(exclList); } + public ProxyProperties(String pacFileUrl) { + mHost = LOCAL_HOST; + mPort = LOCAL_PORT; + setExclusionList(LOCAL_EXCL_LIST); + mPacFileUrl = pacFileUrl; + } + private ProxyProperties(String host, int port, String exclList, String[] parsedExclList) { mHost = host; mPort = port; mExclusionList = exclList; mParsedExclusionList = parsedExclList; + mPacFileUrl = null; } // copy constructor instead of clone @@ -56,6 +67,7 @@ public class ProxyProperties implements Parcelable { if (source != null) { mHost = source.getHost(); mPort = source.getPort(); + mPacFileUrl = source.getPacFileUrl(); mExclusionList = source.getExclusionList(); mParsedExclusionList = source.mParsedExclusionList; } @@ -69,6 +81,10 @@ public class ProxyProperties implements Parcelable { return inetSocketAddress; } + public String getPacFileUrl() { + return mPacFileUrl; + } + public String getHost() { return mHost; } @@ -130,7 +146,10 @@ public class ProxyProperties implements Parcelable { @Override public String toString() { StringBuilder sb = new StringBuilder(); - if (mHost != null) { + if (mPacFileUrl != null) { + sb.append("PAC Script: "); + sb.append(mPacFileUrl); + } else if (mHost != null) { sb.append("["); sb.append(mHost); sb.append("] "); @@ -148,6 +167,14 @@ public class ProxyProperties implements Parcelable { public boolean equals(Object o) { if (!(o instanceof ProxyProperties)) return false; ProxyProperties p = (ProxyProperties)o; + // If PAC URL is present in either then they must be equal. + // Other parameters will only be for fall back. + if (!TextUtils.isEmpty(mPacFileUrl)) { + return mPacFileUrl.equals(p.getPacFileUrl()); + } + if (!TextUtils.isEmpty(p.getPacFileUrl())) { + return false; + } if (mExclusionList != null && !mExclusionList.equals(p.getExclusionList())) return false; if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) { return false; @@ -181,6 +208,13 @@ public class ProxyProperties implements Parcelable { * @hide */ public void writeToParcel(Parcel dest, int flags) { + if (mPacFileUrl != null) { + dest.writeByte((byte)1); + dest.writeString(mPacFileUrl); + return; + } else { + dest.writeByte((byte)0); + } if (mHost != null) { dest.writeByte((byte)1); dest.writeString(mHost); @@ -201,7 +235,10 @@ public class ProxyProperties implements Parcelable { public ProxyProperties createFromParcel(Parcel in) { String host = null; int port = 0; - if (in.readByte() == 1) { + if (in.readByte() != 0) { + return new ProxyProperties(in.readString()); + } + if (in.readByte() != 0) { host = in.readString(); port = in.readInt(); } -- cgit v1.1