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