| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.net.spi; |
| |
| 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.List; |
| import java.util.StringTokenizer; |
| import java.io.IOException; |
| import sun.misc.RegexpPool; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import sun.net.NetProperties; |
| import sun.net.SocksProxy; |
| |
| /** |
| * Supports proxy settings using system properties This proxy selector |
| * provides backward compatibility with the old http protocol handler |
| * as far as how proxy is set |
| * |
| * Most of the implementation copied from the old http protocol handler |
| * |
| * Supports http/https/ftp.proxyHost, http/https/ftp.proxyPort, |
| * proxyHost, proxyPort, and http/https/ftp.nonProxyHost, and socks. |
| * NOTE: need to do gopher as well |
| */ |
| public class DefaultProxySelector extends ProxySelector { |
| |
| /** |
| * This is where we define all the valid System Properties we have to |
| * support for each given protocol. |
| * The format of this 2 dimensional array is : |
| * - 1 row per protocol (http, ftp, ...) |
| * - 1st element of each row is the protocol name |
| * - subsequent elements are prefixes for Host & Port properties |
| * listed in order of priority. |
| * Example: |
| * {"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"}, |
| * means for FTP we try in that oder: |
| * + ftp.proxyHost & ftp.proxyPort |
| * + ftpProxyHost & ftpProxyPort |
| * + proxyHost & proxyPort |
| * + socksProxyHost & socksProxyPort |
| * |
| * Note that the socksProxy should *always* be the last on the list |
| */ |
| final static String[][] props = { |
| /* |
| * protocol, Property prefix 1, Property prefix 2, ... |
| */ |
| {"http", "http.proxy", "proxy", "socksProxy"}, |
| {"https", "https.proxy", "proxy", "socksProxy"}, |
| {"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"}, |
| {"gopher", "gopherProxy", "socksProxy"}, |
| {"socket", "socksProxy"} |
| }; |
| |
| private static final String SOCKS_PROXY_VERSION = "socksProxyVersion"; |
| |
| private static boolean hasSystemProxies = false; |
| |
| /** |
| * How to deal with "non proxy hosts": |
| * since we do have to generate a RegexpPool we don't want to do that if |
| * it's not necessary. Therefore we do cache the result, on a per-protocol |
| * basis, and change it only when the "source", i.e. the system property, |
| * did change. |
| */ |
| |
| static class NonProxyInfo { |
| // Default value for nonProxyHosts, this provides backward compatibility |
| // by excluding localhost and its litteral notations. |
| static final String defStringVal = "localhost|127.*|[::1]|0.0.0.0|[::0]"; |
| |
| String hostsSource; |
| RegexpPool hostsPool; |
| final String property; |
| final String defaultVal; |
| static NonProxyInfo ftpNonProxyInfo = new NonProxyInfo("ftp.nonProxyHosts", null, null, defStringVal); |
| static NonProxyInfo httpNonProxyInfo = new NonProxyInfo("http.nonProxyHosts", null, null, defStringVal); |
| static NonProxyInfo httpsNonProxyInfo = new NonProxyInfo("https.nonProxyHosts", null, null, defStringVal); |
| |
| NonProxyInfo(String p, String s, RegexpPool pool, String d) { |
| property = p; |
| hostsSource = s; |
| hostsPool = pool; |
| defaultVal = d; |
| } |
| } |
| |
| |
| /** |
| * select() method. Where all the hard work is done. |
| * Build a list of proxies depending on URI. |
| * Since we're only providing compatibility with the system properties |
| * from previous releases (see list above), that list will always |
| * contain 1 single proxy, default being NO_PROXY. |
| */ |
| public java.util.List<Proxy> select(URI uri) { |
| if (uri == null) { |
| throw new IllegalArgumentException("URI can't be null."); |
| } |
| String protocol = uri.getScheme(); |
| String host = uri.getHost(); |
| |
| if (host == null) { |
| // This is a hack to ensure backward compatibility in two |
| // cases: 1. hostnames contain non-ascii characters, |
| // internationalized domain names. in which case, URI will |
| // return null, see BugID 4957669; 2. Some hostnames can |
| // contain '_' chars even though it's not supposed to be |
| // legal, in which case URI will return null for getHost, |
| // but not for getAuthority() See BugID 4913253 |
| String auth = uri.getAuthority(); |
| if (auth != null) { |
| int i; |
| i = auth.indexOf('@'); |
| if (i >= 0) { |
| auth = auth.substring(i+1); |
| } |
| i = auth.lastIndexOf(':'); |
| if (i >= 0) { |
| auth = auth.substring(0,i); |
| } |
| host = auth; |
| } |
| } |
| |
| if (protocol == null || host == null) { |
| throw new IllegalArgumentException("protocol = "+protocol+" host = "+host); |
| } |
| List<Proxy> proxyl = new ArrayList<Proxy>(1); |
| |
| NonProxyInfo pinfo = null; |
| |
| if ("http".equalsIgnoreCase(protocol)) { |
| pinfo = NonProxyInfo.httpNonProxyInfo; |
| } else if ("https".equalsIgnoreCase(protocol)) { |
| // HTTPS uses the same property as HTTP, for backward |
| // compatibility |
| // |
| // Android-changed: Allow a different set of flags for https hosts. |
| pinfo = NonProxyInfo.httpsNonProxyInfo; |
| } else if ("ftp".equalsIgnoreCase(protocol)) { |
| pinfo = NonProxyInfo.ftpNonProxyInfo; |
| } |
| |
| /** |
| * Let's check the System properties for that protocol |
| */ |
| final String proto = protocol; |
| final NonProxyInfo nprop = pinfo; |
| final String urlhost = host.toLowerCase(); |
| |
| /** |
| * This is one big doPrivileged call, but we're trying to optimize |
| * the code as much as possible. Since we're checking quite a few |
| * System properties it does help having only 1 call to doPrivileged. |
| * Be mindful what you do in here though! |
| */ |
| Proxy p = AccessController.doPrivileged( |
| new PrivilegedAction<Proxy>() { |
| public Proxy run() { |
| int i, j; |
| String phost = null; |
| int pport = 0; |
| String nphosts = null; |
| InetSocketAddress saddr = null; |
| |
| // Then let's walk the list of protocols in our array |
| for (i=0; i<props.length; i++) { |
| if (props[i][0].equalsIgnoreCase(proto)) { |
| for (j = 1; j < props[i].length; j++) { |
| /* System.getProp() will give us an empty |
| * String, "" for a defined but "empty" |
| * property. |
| */ |
| phost = NetProperties.get(props[i][j]+"Host"); |
| if (phost != null && phost.length() != 0) |
| break; |
| } |
| if (phost == null || phost.length() == 0) { |
| /** |
| * No system property defined for that |
| * protocol. Let's check System Proxy |
| * settings (Gnome & Windows) if we were |
| * instructed to. |
| */ |
| // Android-changed, hasSystemProxies is always false |
| return Proxy.NO_PROXY; |
| } |
| // If a Proxy Host is defined for that protocol |
| // Let's get the NonProxyHosts property |
| if (nprop != null) { |
| nphosts = NetProperties.get(nprop.property); |
| synchronized (nprop) { |
| if (nphosts == null) { |
| if (nprop.defaultVal != null) { |
| nphosts = nprop.defaultVal; |
| } else { |
| nprop.hostsSource = null; |
| nprop.hostsPool = null; |
| } |
| } else if (nphosts.length() != 0) { |
| // add the required default patterns |
| // but only if property no set. If it |
| // is empty, leave empty. |
| nphosts += "|" + NonProxyInfo |
| .defStringVal; |
| } |
| if (nphosts != null) { |
| if (!nphosts.equals(nprop.hostsSource)) { |
| RegexpPool pool = new RegexpPool(); |
| StringTokenizer st = new StringTokenizer(nphosts, "|", false); |
| try { |
| while (st.hasMoreTokens()) { |
| pool.add(st.nextToken().toLowerCase(), Boolean.TRUE); |
| } |
| } catch (sun.misc.REException ex) { |
| } |
| nprop.hostsPool = pool; |
| nprop.hostsSource = nphosts; |
| } |
| } |
| if (nprop.hostsPool != null && |
| nprop.hostsPool.match(urlhost) != null) { |
| return Proxy.NO_PROXY; |
| } |
| } |
| } |
| // We got a host, let's check for port |
| |
| pport = NetProperties.getInteger(props[i][j]+"Port", 0).intValue(); |
| if (pport == 0 && j < (props[i].length - 1)) { |
| // Can't find a port with same prefix as Host |
| // AND it's not a SOCKS proxy |
| // Let's try the other prefixes for that proto |
| for (int k = 1; k < (props[i].length - 1); k++) { |
| if ((k != j) && (pport == 0)) |
| pport = NetProperties.getInteger(props[i][k]+"Port", 0).intValue(); |
| } |
| } |
| |
| // Still couldn't find a port, let's use default |
| if (pport == 0) { |
| if (j == (props[i].length - 1)) // SOCKS |
| pport = defaultPort("socket"); |
| else |
| pport = defaultPort(proto); |
| } |
| // We did find a proxy definition. |
| // Let's create the address, but don't resolve it |
| // as this will be done at connection time |
| saddr = InetSocketAddress.createUnresolved(phost, pport); |
| // Socks is *always* the last on the list. |
| if (j == (props[i].length - 1)) { |
| int version = NetProperties.getInteger(SOCKS_PROXY_VERSION, 5).intValue(); |
| return SocksProxy.create(saddr, version); |
| } else { |
| return new Proxy(Proxy.Type.HTTP, saddr); |
| } |
| } |
| } |
| return Proxy.NO_PROXY; |
| }}); |
| |
| proxyl.add(p); |
| |
| /* |
| * If no specific property was set for that URI, we should be |
| * returning an iterator to an empty List. |
| */ |
| return proxyl; |
| } |
| |
| public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { |
| if (uri == null || sa == null || ioe == null) { |
| throw new IllegalArgumentException("Arguments can't be null."); |
| } |
| // ignored |
| } |
| |
| |
| private int defaultPort(String protocol) { |
| if ("http".equalsIgnoreCase(protocol)) { |
| return 80; |
| } else if ("https".equalsIgnoreCase(protocol)) { |
| return 443; |
| } else if ("ftp".equalsIgnoreCase(protocol)) { |
| return 80; |
| } else if ("socket".equalsIgnoreCase(protocol)) { |
| return 1080; |
| } else if ("gopher".equalsIgnoreCase(protocol)) { |
| return 80; |
| } else { |
| return -1; |
| } |
| } |
| } |