Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/net/spi/DefaultProxySelector.java
38918 views
/*1* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.net.spi;2627import java.net.InetSocketAddress;28import java.net.Proxy;29import java.net.ProxySelector;30import java.net.SocketAddress;31import java.net.URI;32import java.util.ArrayList;33import java.util.List;34import java.io.IOException;35import java.security.AccessController;36import java.security.PrivilegedAction;37import java.util.StringJoiner;38import java.util.regex.Pattern;39import sun.net.NetProperties;40import sun.net.SocksProxy;41import static java.util.regex.Pattern.quote;4243/**44* Supports proxy settings using system properties This proxy selector45* provides backward compatibility with the old http protocol handler46* as far as how proxy is set47*48* Most of the implementation copied from the old http protocol handler49*50* Supports http/https/ftp.proxyHost, http/https/ftp.proxyPort,51* proxyHost, proxyPort, and http/https/ftp.nonProxyHost, and socks.52* NOTE: need to do gopher as well53*/54public class DefaultProxySelector extends ProxySelector {5556/**57* This is where we define all the valid System Properties we have to58* support for each given protocol.59* The format of this 2 dimensional array is :60* - 1 row per protocol (http, ftp, ...)61* - 1st element of each row is the protocol name62* - subsequent elements are prefixes for Host & Port properties63* listed in order of priority.64* Example:65* {"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"},66* means for FTP we try in that oder:67* + ftp.proxyHost & ftp.proxyPort68* + ftpProxyHost & ftpProxyPort69* + proxyHost & proxyPort70* + socksProxyHost & socksProxyPort71*72* Note that the socksProxy should *always* be the last on the list73*/74final static String[][] props = {75/*76* protocol, Property prefix 1, Property prefix 2, ...77*/78{"http", "http.proxy", "proxy", "socksProxy"},79{"https", "https.proxy", "proxy", "socksProxy"},80{"ftp", "ftp.proxy", "ftpProxy", "proxy", "socksProxy"},81{"gopher", "gopherProxy", "socksProxy"},82{"socket", "socksProxy"}83};8485private static final String SOCKS_PROXY_VERSION = "socksProxyVersion";8687private static boolean hasSystemProxies = false;8889static {90final String key = "java.net.useSystemProxies";91Boolean b = AccessController.doPrivileged(92new PrivilegedAction<Boolean>() {93public Boolean run() {94return NetProperties.getBoolean(key);95}});96if (b != null && b.booleanValue()) {97java.security.AccessController.doPrivileged(98new java.security.PrivilegedAction<Void>() {99public Void run() {100System.loadLibrary("net");101return null;102}103});104hasSystemProxies = init();105}106}107108/**109* How to deal with "non proxy hosts":110* since we do have to generate a pattern we don't want to do that if111* it's not necessary. Therefore we do cache the result, on a per-protocol112* basis, and change it only when the "source", i.e. the system property,113* did change.114*/115116static class NonProxyInfo {117// Default value for nonProxyHosts, this provides backward compatibility118// by excluding localhost and its litteral notations.119static final String defStringVal = "localhost|127.*|[::1]|0.0.0.0|[::0]";120121String hostsSource;122Pattern pattern;123final String property;124final String defaultVal;125static NonProxyInfo ftpNonProxyInfo = new NonProxyInfo("ftp.nonProxyHosts", null, null, defStringVal);126static NonProxyInfo httpNonProxyInfo = new NonProxyInfo("http.nonProxyHosts", null, null, defStringVal);127static NonProxyInfo socksNonProxyInfo = new NonProxyInfo("socksNonProxyHosts", null, null, defStringVal);128129NonProxyInfo(String p, String s, Pattern pattern, String d) {130property = p;131hostsSource = s;132this.pattern = pattern;133defaultVal = d;134}135}136137138/**139* select() method. Where all the hard work is done.140* Build a list of proxies depending on URI.141* Since we're only providing compatibility with the system properties142* from previous releases (see list above), that list will always143* contain 1 single proxy, default being NO_PROXY.144*/145public java.util.List<Proxy> select(URI uri) {146if (uri == null) {147throw new IllegalArgumentException("URI can't be null.");148}149String protocol = uri.getScheme();150String host = uri.getHost();151152if (host == null) {153// This is a hack to ensure backward compatibility in two154// cases: 1. hostnames contain non-ascii characters,155// internationalized domain names. in which case, URI will156// return null, see BugID 4957669; 2. Some hostnames can157// contain '_' chars even though it's not supposed to be158// legal, in which case URI will return null for getHost,159// but not for getAuthority() See BugID 4913253160String auth = uri.getAuthority();161if (auth != null) {162int i;163i = auth.indexOf('@');164if (i >= 0) {165auth = auth.substring(i+1);166}167i = auth.lastIndexOf(':');168if (i >= 0) {169auth = auth.substring(0,i);170}171host = auth;172}173}174175if (protocol == null || host == null) {176throw new IllegalArgumentException("protocol = "+protocol+" host = "+host);177}178List<Proxy> proxyl = new ArrayList<Proxy>(1);179180NonProxyInfo pinfo = null;181182if ("http".equalsIgnoreCase(protocol)) {183pinfo = NonProxyInfo.httpNonProxyInfo;184} else if ("https".equalsIgnoreCase(protocol)) {185// HTTPS uses the same property as HTTP, for backward186// compatibility187pinfo = NonProxyInfo.httpNonProxyInfo;188} else if ("ftp".equalsIgnoreCase(protocol)) {189pinfo = NonProxyInfo.ftpNonProxyInfo;190} else if ("socket".equalsIgnoreCase(protocol)) {191pinfo = NonProxyInfo.socksNonProxyInfo;192}193194/**195* Let's check the System properties for that protocol196*/197final String proto = protocol;198final NonProxyInfo nprop = pinfo;199final String urlhost = host.toLowerCase();200201/**202* This is one big doPrivileged call, but we're trying to optimize203* the code as much as possible. Since we're checking quite a few204* System properties it does help having only 1 call to doPrivileged.205* Be mindful what you do in here though!206*/207Proxy p = AccessController.doPrivileged(208new PrivilegedAction<Proxy>() {209public Proxy run() {210int i, j;211String phost = null;212int pport = 0;213String nphosts = null;214InetSocketAddress saddr = null;215216// Then let's walk the list of protocols in our array217for (i=0; i<props.length; i++) {218if (props[i][0].equalsIgnoreCase(proto)) {219for (j = 1; j < props[i].length; j++) {220/* System.getProp() will give us an empty221* String, "" for a defined but "empty"222* property.223*/224phost = NetProperties.get(props[i][j]+"Host");225if (phost != null && phost.length() != 0)226break;227}228if (phost == null || phost.length() == 0) {229/**230* No system property defined for that231* protocol. Let's check System Proxy232* settings (Gnome & Windows) if we were233* instructed to.234*/235if (hasSystemProxies) {236String sproto;237if (proto.equalsIgnoreCase("socket"))238sproto = "socks";239else240sproto = proto;241Proxy sproxy = getSystemProxy(sproto, urlhost);242if (sproxy != null) {243return sproxy;244}245}246return Proxy.NO_PROXY;247}248// If a Proxy Host is defined for that protocol249// Let's get the NonProxyHosts property250if (nprop != null) {251nphosts = NetProperties.get(nprop.property);252synchronized (nprop) {253if (nphosts == null) {254if (nprop.defaultVal != null) {255nphosts = nprop.defaultVal;256} else {257nprop.hostsSource = null;258nprop.pattern = null;259}260} else if (nphosts.length() != 0) {261// add the required default patterns262// but only if property no set. If it263// is empty, leave empty.264nphosts += "|" + NonProxyInfo265.defStringVal;266}267if (nphosts != null) {268if (!nphosts.equals(nprop.hostsSource)) {269nprop.pattern = toPattern(nphosts);270nprop.hostsSource = nphosts;271}272}273if (shouldNotUseProxyFor(nprop.pattern, urlhost)) {274return Proxy.NO_PROXY;275}276}277}278// We got a host, let's check for port279280pport = NetProperties.getInteger(props[i][j]+"Port", 0).intValue();281if (pport == 0 && j < (props[i].length - 1)) {282// Can't find a port with same prefix as Host283// AND it's not a SOCKS proxy284// Let's try the other prefixes for that proto285for (int k = 1; k < (props[i].length - 1); k++) {286if ((k != j) && (pport == 0))287pport = NetProperties.getInteger(props[i][k]+"Port", 0).intValue();288}289}290291// Still couldn't find a port, let's use default292if (pport == 0) {293if (j == (props[i].length - 1)) // SOCKS294pport = defaultPort("socket");295else296pport = defaultPort(proto);297}298// We did find a proxy definition.299// Let's create the address, but don't resolve it300// as this will be done at connection time301saddr = InetSocketAddress.createUnresolved(phost, pport);302// Socks is *always* the last on the list.303if (j == (props[i].length - 1)) {304int version = NetProperties.getInteger(SOCKS_PROXY_VERSION, 5).intValue();305return SocksProxy.create(saddr, version);306} else {307return new Proxy(Proxy.Type.HTTP, saddr);308}309}310}311return Proxy.NO_PROXY;312}});313314proxyl.add(p);315316/*317* If no specific property was set for that URI, we should be318* returning an iterator to an empty List.319*/320return proxyl;321}322323public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {324if (uri == null || sa == null || ioe == null) {325throw new IllegalArgumentException("Arguments can't be null.");326}327// ignored328}329330331private int defaultPort(String protocol) {332if ("http".equalsIgnoreCase(protocol)) {333return 80;334} else if ("https".equalsIgnoreCase(protocol)) {335return 443;336} else if ("ftp".equalsIgnoreCase(protocol)) {337return 80;338} else if ("socket".equalsIgnoreCase(protocol)) {339return 1080;340} else if ("gopher".equalsIgnoreCase(protocol)) {341return 80;342} else {343return -1;344}345}346347private native static boolean init();348private synchronized native Proxy getSystemProxy(String protocol, String host);349350/**351* @return {@code true} if given this pattern for non-proxy hosts and this352* urlhost the proxy should NOT be used to access this urlhost353*/354static boolean shouldNotUseProxyFor(Pattern pattern, String urlhost) {355if (pattern == null || urlhost.isEmpty())356return false;357boolean matches = pattern.matcher(urlhost).matches();358return matches;359}360361/**362* @param mask non-null mask363* @return {@link java.util.regex.Pattern} corresponding to this mask364* or {@code null} in case mask should not match anything365*/366static Pattern toPattern(String mask) {367boolean disjunctionEmpty = true;368StringJoiner joiner = new StringJoiner("|");369for (String disjunct : mask.split("\\|")) {370if (disjunct.isEmpty())371continue;372disjunctionEmpty = false;373String regex = disjunctToRegex(disjunct.toLowerCase());374joiner.add(regex);375}376return disjunctionEmpty ? null : Pattern.compile(joiner.toString());377}378379/**380* @param disjunct non-null mask disjunct381* @return java regex string corresponding to this mask382*/383static String disjunctToRegex(String disjunct) {384String regex;385if (disjunct.startsWith("*")) {386regex = ".*" + quote(disjunct.substring(1));387} else if (disjunct.endsWith("*")) {388regex = quote(disjunct.substring(0, disjunct.length() - 1)) + ".*";389} else {390regex = quote(disjunct);391}392return regex;393}394}395396397