/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.seed.core.internal.init;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import org.seedstack.seed.ProxyConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.ProxySelector;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import static java.util.stream.Collectors.toList;
public class ProxyManager {
private static final Logger LOGGER = LoggerFactory.getLogger(ProxyManager.class);
private SeedProxySelector seedProxySelector;
private SeedProxyAuthenticator seedProxyAuthenticator;
private static class Holder {
private static final ProxyManager INSTANCE = new ProxyManager();
}
public static ProxyManager get() {
return Holder.INSTANCE;
}
private ProxyManager() {
// noop
}
public void install(ProxyConfig proxyConfig) {
if (!proxyConfig.getMode().equals(ProxyConfig.ProxyMode.DISABLED)) {
boolean autoProxy = proxyConfig.getMode().equals(ProxyConfig.ProxyMode.AUTO);
String httpProxyValue = getValue(proxyConfig.getHttpProxy(), "http_proxy", autoProxy);
String httpsProxyValue = getValue(proxyConfig.getHttpsProxy(), "https_proxy", autoProxy);
String noProxyValue = getValue(proxyConfig.getNoProxy(), "no_proxy", autoProxy);
Proxy httpProxy = buildProxy(httpProxyValue, 80);
Proxy httpsProxy = buildProxy(httpsProxyValue, 443);
List<Pattern> exclusions = buildExclusions(noProxyValue);
ProxySelector.setDefault(seedProxySelector = new SeedProxySelector(
httpProxy,
httpsProxy,
ProxySelector.getDefault(),
exclusions
));
Authenticator.setDefault(seedProxyAuthenticator = new SeedProxyAuthenticator(
buildPasswordAuthentication(httpProxyValue),
buildPasswordAuthentication(httpsProxyValue)
));
if (httpProxy != Proxy.NO_PROXY) {
if (Strings.isNullOrEmpty(noProxyValue)) {
LOGGER.info("Proxy configured to {} with no exclusion", httpProxy.address().toString());
} else {
LOGGER.info("Proxy configured to {} with exclusion(s) on {}", httpProxy.address().toString(), noProxyValue);
}
}
}
}
public void uninstall() {
if (seedProxySelector != null) {
ProxySelector.setDefault(null);
}
if (seedProxyAuthenticator != null) {
Authenticator.setDefault(null);
}
}
private String getValue(String configuredValue, String variable, boolean auto) {
if (Strings.isNullOrEmpty(configuredValue) && auto) {
String value = System.getenv(variable);
if (Strings.isNullOrEmpty(value)) {
value = System.getenv(variable.toUpperCase());
}
return value;
} else {
return configuredValue;
}
}
private List<Pattern> buildExclusions(String noProxy) {
if (noProxy == null) {
return Lists.newArrayList();
}
return Arrays.stream(noProxy.split(",")).map(this::makePattern).collect(toList());
}
private Proxy buildProxy(String value, int defaultPort) {
String[] httpProxyInfo = parseProxy(value, String.valueOf(defaultPort));
if (httpProxyInfo != null) {
return new Proxy(Proxy.Type.HTTP, new InetSocketAddress(httpProxyInfo[0], Integer.parseInt(httpProxyInfo[1])));
} else {
return Proxy.NO_PROXY;
}
}
private PasswordAuthentication buildPasswordAuthentication(String value) {
String[] httpCredentials = parseCredentials(value);
if (httpCredentials != null) {
return new PasswordAuthentication(httpCredentials[0], httpCredentials[1].toCharArray());
} else {
return null;
}
}
/**
* Given a proxy URL returns a two element arrays containing the user name and the password. The second component
* of the array is null if no password is specified.
*
* @param url The proxy host URL.
* @return An array containing the user name and the password or null when none are present or the url is empty.
*/
private String[] parseCredentials(String url) {
String[] result = new String[2];
if (url == null || url.isEmpty())
return null;
int p = url.indexOf("://");
if (p != -1)
url = url.substring(p + 3);
if ((p = url.indexOf('@')) != -1) {
String credentials = url.substring(0, p);
if ((p = credentials.indexOf(':')) != -1) {
result[0] = credentials.substring(0, p);
result[1] = credentials.substring(p + 1);
} else {
result[0] = credentials;
}
} else {
return null;
}
return result;
}
/**
* Given a proxy URL returns a two element arrays containing the host name and the port
*
* @param url The proxy host URL.
* @param defPort The default proxy port
* @return An array containing the host name and the proxy port or null when url is empty
*/
private String[] parseProxy(String url, String defPort) {
String[] result = new String[2];
if (url == null || url.isEmpty())
return null;
int p = url.indexOf("://");
if (p != -1)
url = url.substring(p + 3);
if ((p = url.indexOf('@')) != -1)
url = url.substring(p + 1);
if ((p = url.indexOf(':')) != -1) {
result[0] = url.substring(0, p);
result[1] = url.substring(p + 1);
} else {
result[0] = url;
result[1] = defPort;
}
// remove trailing slash from the host name
p = result[0].indexOf("/");
if (p != -1) {
result[0] = result[0].substring(0, p);
}
// remove trailing slash from the port number
p = result[1].indexOf("/");
if (p != -1) {
result[1] = result[1].substring(0, p);
}
return result;
}
/**
* Creates a regexp pattern equivalent to the classic noProxy wildcard expression.
*
* @param noProxy the noProxy expression.
* @return the regexp pattern.
*/
private Pattern makePattern(String noProxy) {
return Pattern.compile(noProxy.replaceAll("\\.", "\\\\.").replaceAll("\\*", ".*"));
}
}