package org.openslx.dozmod.util;
import java.io.IOException;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.Socket;
import java.text.MessageFormat;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultRoutePlanner;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContexts;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
import org.openslx.bwlp.thrift.iface.MasterServer;
import org.openslx.dozmod.App;
import org.openslx.dozmod.authentication.ShibbolethEcp;
import org.openslx.thrifthelper.ThriftManager;
import org.openslx.util.Util;
import com.btr.proxy.search.ProxySearch;
import com.btr.proxy.search.wpad.WpadProxySearchStrategy;
import com.btr.proxy.util.Logger.LogBackEnd;
import com.btr.proxy.util.Logger.LogLevel;
/**
* Configures the proxy
*
* @author Jonathan Bauer
*/
public class ProxyConfigurator {
/**
* Logger for this class
*/
private final static Logger LOGGER = Logger.getLogger(ProxyConfigurator.class);
private static AtomicReference<CloseableHttpClient> apacheClient = new AtomicReference<>();
/**
* Initialization method.
*/
public static void init() {
MasterServer.Client masterClient = ThriftManager.getNewMasterClient(null, App.getMasterServerAddress(),
App.THRIFT_PORT, 4000);
if (masterClient != null) {
try {
masterClient.ping();
try {
masterClient.getInputProtocol().getTransport().close();
masterClient.getOutputProtocol().getTransport().close();
} catch (Throwable e) {
}
} catch (Exception e) {
masterClient = null;
}
}
// To be discussed: Let's bail out if the master server is reachable
if (masterClient != null && testHttpsMaster()) {
LOGGER.info("Not setting up proxy because master server seems reachable.");
return;
}
// Build special HTTP Client Builder for apache and try again
apacheClient.set(createSlxBuilder().build());
if (masterClient != null && testHttpsMaster()) {
LOGGER.info("Not setting up proxy since master server is reachable with custom client builder");
return;
}
// first setup the logger of proxy_vole
com.btr.proxy.util.Logger.setBackend(new LogBackEnd() {
public void log(Class<?> clazz, LogLevel loglevel, String msg, Object... params) {
Priority priority;
switch (loglevel) {
case ERROR:
priority = Level.ERROR;
break;
case WARNING:
priority = Level.WARN;
break;
case INFO:
priority = Level.INFO;
break;
default:
priority = Level.DEBUG;
}
Logger.getLogger(clazz).log(priority, MessageFormat.format(msg, params));
}
public boolean isLogginEnabled(LogLevel logLevel) {
return true;
}
});
LOGGER.info("Master server not directly reachable; trying to determine proxy");
// try to find local proxy settings
ProxySearch proxySearch = ProxySearch.getDefaultProxySearch();
ProxySelector myProxySelector = proxySearch.getProxySelector();
if (myProxySelector == null) {
// didn't work, try WPAD detection
LOGGER.error("No suitable proxy settings found, trying WPAD...");
WpadProxySearchStrategy ss = new WpadProxySearchStrategy();
myProxySelector = ss.getProxySelector();
}
// final check to see if WPAD actually worked
if (myProxySelector != null) {
ProxySelector.setDefault(myProxySelector);
LOGGER.debug("Proxy initialised.");
Util.sleep(10);
} else {
LOGGER.error("Could not find a suitable proxy!");
}
}
/**
* Get the HttpClient to be used with apache
* httpclient.
*
* @return client
*/
public static CloseableHttpClient getClient() {
CloseableHttpClient inst = apacheClient.get();
if (inst != null)
return inst;
inst = createShortTimeoutBuilder().build();
apacheClient.compareAndSet(null, inst);
return inst;
}
private static SSLConnectionSocketFactory createSslFactory() {
// TODO: Geht nich
for (String proto : new String[] { "TLSv1.2", "TLSv1.1", "TLS" }) {
try {
return new SSLConnectionSocketFactory(SSLContexts.custom().useProtocol(proto).build());
} catch (Exception e) {
LOGGER.warn(proto + " not available", e);
}
}
return SSLConnectionSocketFactory.getSystemSocketFactory();
}
private static HttpClientBuilder createShortTimeoutBuilder() {
HttpClientBuilder builder = HttpClientBuilder.create().setSSLSocketFactory(createSslFactory());
builder.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(8000).build());
PoolingHttpClientConnectionManager pm = new PoolingHttpClientConnectionManager();
pm.setDefaultMaxPerRoute(4);
builder.setConnectionManager(pm);
return builder;
}
private static HttpClientBuilder createSlxBuilder() {
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setRoutePlanner(new SlxRoutePlanner(null));
builder.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(8000).build());
Registry<ConnectionSocketFactory> csf = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", new SlxSocketFactory())
.register("https", createSslFactory())
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(csf);
cm.setDefaultMaxPerRoute(4);
builder.setConnectionManager(cm);
return builder;
}
private static boolean testHttpsMaster() {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectionRequestTimeout(3000)
.setConnectTimeout(3000)
.setSocketTimeout(3000)
.build();
HttpGet httpGet = new HttpGet(ShibbolethEcp.BWLP_SP.toString());
httpGet.setConfig(requestConfig);
httpGet.setHeader("Accept", "text/html, application/vnd.paos+xml");
httpGet.setHeader("PAOS",
"ver=\"urn:liberty:paos:2003-08\";\"urn:oasis:names:tc:SAML:2.0:profiles:SSO:ecp\"");
try {
HttpResponse response = getClient().execute(httpGet);
LOGGER.debug("Master server replies with " + response.getStatusLine().getStatusCode());
return response.getStatusLine().getStatusCode() == 200;
} catch (Exception e) {
LOGGER.debug("Cannot reach master server via HTTPS", e);
return false;
}
}
private static class SlxSocketFactory extends PlainConnectionSocketFactory {
@Override
public Socket createSocket(HttpContext context) throws IOException {
Object obj = context.getAttribute("openslx.l7proxy");
if (obj instanceof HttpRoute) {
HttpRoute route = (HttpRoute) obj;
if (route != null && route.getProxyHost() != null) {
return new Socket(Proxy.NO_PROXY);
}
}
return new Socket();
}
}
private static class SlxRoutePlanner extends SystemDefaultRoutePlanner {
public SlxRoutePlanner(ProxySelector proxySelector) {
super(proxySelector);
}
@Override
public HttpRoute determineRoute(HttpHost host, HttpRequest request, HttpContext context)
throws HttpException {
HttpRoute route = super.determineRoute(host, request, context);
context.setAttribute("openslx.l7proxy", route);
return route;
}
}
}