/** * Copyright (c) Codice Foundation * <p> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p> * This program 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 * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. **/ package org.codice.proxy.http; import java.io.File; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.apache.camel.CamelContext; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.component.servlet.ServletComponent; import org.apache.commons.httpclient.contrib.ssl.AuthSSLProtocolSocketFactory; import org.apache.commons.httpclient.protocol.Protocol; import org.apache.commons.lang.StringUtils; import org.codice.ddf.configuration.AbsolutePathResolver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Http Proxy service which creates a Camel based http proxy * * @author ddf */ public class HttpProxyServiceImpl implements HttpProxyService { private static final Logger LOGGER = LoggerFactory.getLogger(HttpProxyServiceImpl.class); public static final String SERVLET_NAME = "CamelServlet"; private static final String SERVLET_COMPONENT = "servlet"; public static final String SERVLET_PATH = "/proxy"; public static final String GENERIC_ENDPOINT_NAME = "endpoint"; public static final String TRUSTSTORE_VALUE_DEFAULT = new AbsolutePathResolver( "etc/keystores/serverTruststore.jks").getPath(); public static final String TRUSTSTORE_PASSWORD_VALUE = "changeit"; public static final String HTTP_PROXY_KEY = "http."; public static final String HTTPS_PROXY_KEY = "https."; public static final String HTTP_PROXY_HOST_KEY = "proxyHost"; public static final String HTTP_PROXY_PORT_KEY = "proxyPort"; public static final String HTTP_PROXY_AUTH_METHOD_KEY = "proxyAuthMethod"; public static final String HTTP_PROXY_AUTH_USERNAME_KEY = "proxyAuthUsername"; public static final String HTTP_PROXY_AUTH_PASSWORD_KEY = "proxyAuthPassword"; public static final String HTTP_PROXY_AUTH_DOMAIN_KEY = "proxyAuthDomain"; public static final String HTTP_PROXY_AUTH_HOST_KEY = "proxyAuthHost"; private static final int DEFAULT_TIMEOUT_MS = 5000; private static final String SERVLET = "servlet"; int incrementer = 0; private String trustStore = null; private String trustStorePassword = null; private CamelContext camelContext = null; private String routeEndpointType = SERVLET; private final Set<String> endpointIds = Collections.synchronizedSet(new HashSet<>()); public HttpProxyServiceImpl(CamelContext camelContext, String endpointType) throws Exception { this(camelContext); this.routeEndpointType = endpointType; if (!this.routeEndpointType.equals(SERVLET)) { this.camelContext.removeComponent(SERVLET_COMPONENT); } } public HttpProxyServiceImpl(CamelContext camelContext) throws Exception { this.camelContext = camelContext; // Add servlet to the Camel Context ServletComponent servlet = new ServletComponent(); servlet.setCamelContext(camelContext); servlet.setServletName(SERVLET_NAME); this.camelContext.addComponent(SERVLET_COMPONENT, servlet); } public synchronized String start(String targetUri, Integer timeout) throws Exception { String endpointName = GENERIC_ENDPOINT_NAME + incrementer; if (timeout <= 0) { timeout = DEFAULT_TIMEOUT_MS; } start(endpointName, targetUri, timeout); incrementer++; return endpointName; } public String start(final String endpointName, final String targetUri, final Integer timeout) throws Exception { return start(endpointName, targetUri, timeout, false, null); } public String start(final String endpointName, final String targetUri, final Integer timeout, final boolean matchOnPrefix, final Object bean) throws Exception { // Enable proxy settings for the external target enableProxySettings(); // Fetch location of trust store and trust store password fetchTrustStoreLocation(); // Create SSL connection Camel protocol for https Protocol authhttps = null; File certStore = new File(trustStore); try { authhttps = new Protocol("https", new AuthSSLProtocolSocketFactory(certStore.toURI() .toURL(), trustStorePassword, certStore.toURI() .toURL(), trustStorePassword), 443); } catch (MalformedURLException e) { LOGGER.debug(e.getMessage()); } if (authhttps != null) { Protocol.registerProtocol("https", authhttps); } final String matchPrefix = (matchOnPrefix) ? "?matchOnUriPrefix=true" : ""; final String protocolDelimiter = (routeEndpointType.equals(SERVLET)) ? ":///" : "://"; // Create Camel route RouteBuilder routeBuilder; if (bean == null) { routeBuilder = new RouteBuilder() { @Override public void configure() throws Exception { from(routeEndpointType + protocolDelimiter + endpointName + matchPrefix).removeHeader("Authorization") .removeHeader("Cookie") .to(targetUri + "?bridgeEndpoint=true&throwExceptionOnFailure=false&httpClient.soTimeout=" + timeout + "&httpClient.connectionManagerTimeout=" + timeout) .routeId(endpointName); } }; } else { routeBuilder = new RouteBuilder() { @Override public void configure() throws Exception { from(routeEndpointType + protocolDelimiter + endpointName + matchPrefix).removeHeader("Authorization") .removeHeader("Cookie") .to(targetUri + "?bridgeEndpoint=true&throwExceptionOnFailure=false&httpClient.soTimeout=" + timeout + "&httpClient.connectionManagerTimeout=" + timeout) .routeId(endpointName) .bean(bean); } }; } camelContext.addRoutes(routeBuilder); camelContext.start(); LOGGER.debug("Started proxy route at servlet endpoint: {}, routing to: {}", endpointName, targetUri); endpointIds.add(endpointName); return endpointName; } /** * Enable external proxy settings */ private void enableProxySettings() { // Fetch all proxy settings and add settings to Camel context if not null, whitespace or // empty ArrayList<String> sysProxyConfigs = new ArrayList<String>(); sysProxyConfigs.add(HTTP_PROXY_KEY + HTTP_PROXY_HOST_KEY); sysProxyConfigs.add(HTTP_PROXY_KEY + HTTP_PROXY_PORT_KEY); sysProxyConfigs.add(HTTP_PROXY_KEY + HTTP_PROXY_AUTH_METHOD_KEY); sysProxyConfigs.add(HTTP_PROXY_KEY + HTTP_PROXY_AUTH_USERNAME_KEY); sysProxyConfigs.add(HTTP_PROXY_KEY + HTTP_PROXY_AUTH_PASSWORD_KEY); sysProxyConfigs.add(HTTP_PROXY_KEY + HTTP_PROXY_AUTH_DOMAIN_KEY); sysProxyConfigs.add(HTTP_PROXY_KEY + HTTP_PROXY_AUTH_HOST_KEY); sysProxyConfigs.add(HTTPS_PROXY_KEY + HTTP_PROXY_HOST_KEY); sysProxyConfigs.add(HTTPS_PROXY_KEY + HTTP_PROXY_PORT_KEY); sysProxyConfigs.add(HTTPS_PROXY_KEY + HTTP_PROXY_AUTH_METHOD_KEY); sysProxyConfigs.add(HTTPS_PROXY_KEY + HTTP_PROXY_AUTH_USERNAME_KEY); sysProxyConfigs.add(HTTPS_PROXY_KEY + HTTP_PROXY_AUTH_PASSWORD_KEY); sysProxyConfigs.add(HTTPS_PROXY_KEY + HTTP_PROXY_AUTH_DOMAIN_KEY); sysProxyConfigs.add(HTTPS_PROXY_KEY + HTTP_PROXY_AUTH_HOST_KEY); for (String sysProxyConfig : sysProxyConfigs) { String prop = System.getProperty(sysProxyConfig); if (StringUtils.isNotBlank(prop)) { LOGGER.debug("Enabling Proxy Property: {}", sysProxyConfig); camelContext.getProperties() .put(sysProxyConfig, prop); } } } private void fetchTrustStoreLocation() { trustStore = System.getProperty("javax.net.ssl.trustStore", TRUSTSTORE_VALUE_DEFAULT); trustStorePassword = System.getProperty("javax.net.ssl.trustStorePassword", TRUSTSTORE_PASSWORD_VALUE); LOGGER.debug("Trust Store: {}", trustStore); LOGGER.debug("Trust Store Password not empty: {}", StringUtils.isNotBlank(trustStorePassword)); } public void stop(String endpointName) throws Exception { LOGGER.debug("Stopping proxy route at endpoint: {}", endpointName); LOGGER.debug("Route list before = {}", Arrays.toString(camelContext.getRoutes() .toArray())); camelContext.stopRoute(endpointName); camelContext.removeRoute(endpointName); endpointIds.remove(endpointName); LOGGER.debug("Route list after = {}", Arrays.toString(camelContext.getRoutes() .toArray())); } public void destroy() { try { Object[] objects = endpointIds.toArray(); for (Object endpointId : objects) { stop((String) endpointId); } camelContext.stop(); } catch (Exception e) { LOGGER.debug(e.getMessage()); } camelContext.removeComponent(SERVLET_COMPONENT); } }