--- /dev/null
+/*******************************************************************************\r
+ * ============LICENSE_START==================================================\r
+ * * org.onap.dmaap\r
+ * * ===========================================================================\r
+ * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
+ * * ===========================================================================\r
+ * * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * * you may not use this file except in compliance with the License.\r
+ * * You may obtain a copy of the License at\r
+ * * \r
+ * * http://www.apache.org/licenses/LICENSE-2.0\r
+ * * \r
+ * * Unless required by applicable law or agreed to in writing, software\r
+ * * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * * See the License for the specific language governing permissions and\r
+ * * limitations under the License.\r
+ * * ============LICENSE_END====================================================\r
+ * *\r
+ * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
+ * *\r
+ ******************************************************************************/\r
+\r
+\r
+package org.onap.dmaap.datarouter.provisioning;\r
+\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileNotFoundException;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.net.URI;\r
+import java.security.KeyStore;\r
+import java.security.KeyStoreException;\r
+import java.util.Collections;\r
+import java.util.List;\r
+import java.util.Properties;\r
+\r
+import javax.servlet.ServletConfig;\r
+import javax.servlet.ServletException;\r
+import javax.servlet.http.HttpServletRequest;\r
+import javax.servlet.http.HttpServletResponse;\r
+\r
+import org.apache.commons.io.IOUtils;\r
+import org.apache.http.Header;\r
+import org.apache.http.HttpEntity;\r
+import org.apache.http.HttpResponse;\r
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;\r
+import org.apache.http.client.methods.HttpGet;\r
+import org.apache.http.client.methods.HttpRequestBase;\r
+import org.apache.http.conn.scheme.Scheme;\r
+import org.apache.http.conn.ssl.SSLSocketFactory;\r
+import org.apache.http.entity.BasicHttpEntity;\r
+import org.apache.http.impl.client.AbstractHttpClient;\r
+import org.apache.http.impl.client.DefaultHttpClient;\r
+import org.onap.dmaap.datarouter.provisioning.utils.DB;\r
+import org.onap.dmaap.datarouter.provisioning.utils.URLUtilities;\r
+\r
+/**\r
+ * This class is the base class for those servlets that need to proxy their requests from the\r
+ * standby to active server. Its methods perform the proxy function to the active server. If the\r
+ * active server is not reachable, a 503 (SC_SERVICE_UNAVAILABLE) is returned. Only\r
+ * DELETE/GET/PUT/POST are supported.\r
+ *\r
+ * @author Robert Eby\r
+ * @version $Id: ProxyServlet.java,v 1.3 2014/03/24 18:47:10 eby Exp $\r
+ */\r
+@SuppressWarnings("serial")\r
+public class ProxyServlet extends BaseServlet {\r
+ private boolean inited = false;\r
+ private Scheme sch;\r
+\r
+ /**\r
+ * Initialize this servlet, by setting up SSL.\r
+ */\r
+ @SuppressWarnings("deprecation")\r
+ @Override\r
+ public void init(ServletConfig config) throws ServletException {\r
+ super.init(config);\r
+ try {\r
+ // Set up keystore\r
+ Properties props = (new DB()).getProperties();\r
+ String type = props.getProperty(Main.KEYSTORE_TYPE_PROPERTY, "jks");\r
+ String store = props.getProperty(Main.KEYSTORE_PATH_PROPERTY);\r
+ String pass = props.getProperty(Main.KEYSTORE_PASSWORD_PROPERTY);\r
+ KeyStore keyStore = readStore(store, pass, type);\r
+\r
+ store = props.getProperty(Main.TRUSTSTORE_PATH_PROPERTY);\r
+ pass = props.getProperty(Main.TRUSTSTORE_PASSWORD_PROPERTY);\r
+ if (store == null || store.length() == 0) {\r
+ store = Main.DEFAULT_TRUSTSTORE;\r
+ pass = "changeit";\r
+ }\r
+ KeyStore trustStore = readStore(store, pass, KeyStore.getDefaultType());\r
+\r
+ // We are connecting with the node name, but the certificate will have the CNAME\r
+ // So we need to accept a non-matching certificate name\r
+ SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore, "changeit", trustStore);\r
+ socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);\r
+ sch = new Scheme("https", 443, socketFactory);\r
+ inited = true;\r
+ } catch (Exception e) {\r
+ e.printStackTrace();\r
+ }\r
+ intlogger.info("ProxyServlet: inited = "+inited);\r
+ }\r
+ private KeyStore readStore(String store, String pass, String type) throws KeyStoreException, FileNotFoundException {\r
+ KeyStore ks = KeyStore.getInstance(type);\r
+ FileInputStream instream = new FileInputStream(new File(store));\r
+ try {\r
+ ks.load(instream, pass.toCharArray());\r
+ } catch (Exception x) {\r
+ System.err.println("READING TRUSTSTORE: "+x);\r
+ } finally {\r
+ try { instream.close(); } catch (Exception ignore) {}\r
+ }\r
+ return ks;\r
+ }\r
+ /**\r
+ * Return <i>true</i> if the requester has NOT set the <i>noproxy</i> CGI variable.\r
+ * If they have, this indicates they want to forcibly turn the proxy off.\r
+ * @param req the HTTP request\r
+ * @return true or false\r
+ */\r
+ protected boolean isProxyOK(final HttpServletRequest req) {\r
+ String t = req.getQueryString();\r
+ if (t != null) {\r
+ t = t.replaceAll("&", "&");\r
+ for (String s : t.split("&")) {\r
+ if (s.equals("noproxy") || s.startsWith("noproxy="))\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ /**\r
+ * Is this the standby server? If it is, the proxy functions can be used.\r
+ * If not, the proxy functions should not be called, and will send a response of 500\r
+ * (Internal Server Error).\r
+ * @return true if this server is the standby (and hence a proxy server).\r
+ */\r
+ public boolean isProxyServer() {\r
+ SynchronizerTask st = SynchronizerTask.getSynchronizer();\r
+ return st.getState() == SynchronizerTask.STANDBY;\r
+ }\r
+ /**\r
+ * Issue a proxy DELETE to the active provisioning server.\r
+ */\r
+ @Override\r
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+ doProxy(req, resp, "DELETE");\r
+ }\r
+ /**\r
+ * Issue a proxy GET to the active provisioning server.\r
+ */\r
+ @Override\r
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+ doProxy(req, resp, "GET");\r
+ }\r
+ /**\r
+ * Issue a proxy PUT to the active provisioning server.\r
+ */\r
+ @Override\r
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+ doProxy(req, resp, "PUT");\r
+ }\r
+ /**\r
+ * Issue a proxy POST to the active provisioning server.\r
+ */\r
+ @Override\r
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+ doProxy(req, resp, "POST");\r
+ }\r
+ /**\r
+ * Issue a proxy GET to the active provisioning server. Unlike doGet() above,\r
+ * this method will allow the caller to fall back to other code if the remote server is unreachable.\r
+ * @return true if the proxy succeeded\r
+ */\r
+ public boolean doGetWithFallback(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
+ boolean rv = false;\r
+ if (inited) {\r
+ String url = buildUrl(req);\r
+ intlogger.info("ProxyServlet: proxying with fallback GET "+url);\r
+ AbstractHttpClient httpclient = new DefaultHttpClient();\r
+ HttpRequestBase proxy = new HttpGet(url);\r
+ try {\r
+ httpclient.getConnectionManager().getSchemeRegistry().register(sch);\r
+\r
+ // Copy request headers and request body\r
+ copyRequestHeaders(req, proxy);\r
+\r
+ // Execute the request\r
+ HttpResponse pxy_response = httpclient.execute(proxy);\r
+\r
+ // Get response headers and body\r
+ int code = pxy_response.getStatusLine().getStatusCode();\r
+ resp.setStatus(code);\r
+ copyResponseHeaders(pxy_response, resp);\r
+\r
+ HttpEntity entity = pxy_response.getEntity();\r
+ if (entity != null) {\r
+ InputStream in = entity.getContent();\r
+ IOUtils.copy(in, resp.getOutputStream());\r
+ in.close();\r
+ }\r
+ rv = true;\r
+ } catch (IOException e) {\r
+ System.err.println("ProxyServlet: "+e);\r
+ e.printStackTrace();\r
+ } finally {\r
+ proxy.releaseConnection();\r
+ httpclient.getConnectionManager().shutdown();\r
+ }\r
+ } else {\r
+ intlogger.warn("ProxyServlet: proxy disabled");\r
+ }\r
+ return rv;\r
+ }\r
+ private void doProxy(HttpServletRequest req, HttpServletResponse resp, final String method) throws IOException {\r
+ if (inited && isProxyServer()) {\r
+ String url = buildUrl(req);\r
+ intlogger.info("ProxyServlet: proxying "+method + " "+url);\r
+ AbstractHttpClient httpclient = new DefaultHttpClient();\r
+ ProxyHttpRequest proxy = new ProxyHttpRequest(method, url);\r
+ try {\r
+ httpclient.getConnectionManager().getSchemeRegistry().register(sch);\r
+\r
+ // Copy request headers and request body\r
+ copyRequestHeaders(req, proxy);\r
+ if (method.equals("POST") || method.equals("PUT")){\r
+ BasicHttpEntity body = new BasicHttpEntity();\r
+ body.setContent(req.getInputStream());\r
+ body.setContentLength(-1); // -1 = unknown\r
+ proxy.setEntity(body);\r
+ }\r
+\r
+ // Execute the request\r
+ HttpResponse pxy_response = httpclient.execute(proxy);\r
+\r
+ // Get response headers and body\r
+ int code = pxy_response.getStatusLine().getStatusCode();\r
+ resp.setStatus(code);\r
+ copyResponseHeaders(pxy_response, resp);\r
+\r
+ HttpEntity entity = pxy_response.getEntity();\r
+ if (entity != null) {\r
+ InputStream in = entity.getContent();\r
+ IOUtils.copy(in, resp.getOutputStream());\r
+ in.close();\r
+ }\r
+ } catch (IOException e) {\r
+ intlogger.warn("ProxyServlet: "+e);\r
+ resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);\r
+ e.printStackTrace();\r
+ } finally {\r
+ proxy.releaseConnection();\r
+ httpclient.getConnectionManager().shutdown();\r
+ }\r
+ } else {\r
+ intlogger.warn("ProxyServlet: proxy disabled");\r
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
+ }\r
+ }\r
+ private String buildUrl(HttpServletRequest req) {\r
+ StringBuilder sb = new StringBuilder("https://");\r
+ sb.append(URLUtilities.getPeerPodName());\r
+ sb.append(req.getRequestURI());\r
+ String q = req.getQueryString();\r
+ if (q != null)\r
+ sb.append("?").append(q);\r
+ return sb.toString();\r
+ }\r
+ private void copyRequestHeaders(HttpServletRequest from, HttpRequestBase to) {\r
+ @SuppressWarnings("unchecked")\r
+ List<String> list = Collections.list(from.getHeaderNames());\r
+ for (String name : list) {\r
+ // Proxy code will add this one\r
+ if (!name.equalsIgnoreCase("Content-Length"))\r
+ to.addHeader(name, from.getHeader(name));\r
+ }\r
+ }\r
+ private void copyResponseHeaders(HttpResponse from, HttpServletResponse to) {\r
+ for (Header hdr : from.getAllHeaders()) {\r
+ // Don't copy Date: our Jetty will add another Date header\r
+ if (!hdr.getName().equals("Date"))\r
+ to.addHeader(hdr.getName(), hdr.getValue());\r
+ }\r
+ }\r
+\r
+ public class ProxyHttpRequest extends HttpEntityEnclosingRequestBase {\r
+ private final String method;\r
+\r
+ public ProxyHttpRequest(final String method, final String uri) {\r
+ super();\r
+ this.method = method;\r
+ setURI(URI.create(uri));\r
+ }\r
+ @Override\r
+ public String getMethod() {\r
+ return method;\r
+ }\r
+ }\r
+}\r