Update project structure to org.onap
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / ProxyServlet.java
diff --git a/datarouter-prov/src/main/java/org/onap/dmaap/datarouter/provisioning/ProxyServlet.java b/datarouter-prov/src/main/java/org/onap/dmaap/datarouter/provisioning/ProxyServlet.java
new file mode 100644 (file)
index 0000000..2db1ce4
--- /dev/null
@@ -0,0 +1,303 @@
+/*******************************************************************************\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("&amp;", "&");\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