Update project structure to org.onap
[dmaap/datarouter.git] / datarouter-prov / src / main / java / org / onap / dmaap / datarouter / provisioning / ProxyServlet.java
1 /*******************************************************************************\r
2  * ============LICENSE_START==================================================\r
3  * * org.onap.dmaap\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * ===========================================================================\r
7  * * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * * you may not use this file except in compliance with the License.\r
9  * * You may obtain a copy of the License at\r
10  * * \r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * * \r
13  *  * Unless required by applicable law or agreed to in writing, software\r
14  * * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * * See the License for the specific language governing permissions and\r
17  * * limitations under the License.\r
18  * * ============LICENSE_END====================================================\r
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 \r
24 \r
25 package org.onap.dmaap.datarouter.provisioning;\r
26 \r
27 import java.io.File;\r
28 import java.io.FileInputStream;\r
29 import java.io.FileNotFoundException;\r
30 import java.io.IOException;\r
31 import java.io.InputStream;\r
32 import java.net.URI;\r
33 import java.security.KeyStore;\r
34 import java.security.KeyStoreException;\r
35 import java.util.Collections;\r
36 import java.util.List;\r
37 import java.util.Properties;\r
38 \r
39 import javax.servlet.ServletConfig;\r
40 import javax.servlet.ServletException;\r
41 import javax.servlet.http.HttpServletRequest;\r
42 import javax.servlet.http.HttpServletResponse;\r
43 \r
44 import org.apache.commons.io.IOUtils;\r
45 import org.apache.http.Header;\r
46 import org.apache.http.HttpEntity;\r
47 import org.apache.http.HttpResponse;\r
48 import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;\r
49 import org.apache.http.client.methods.HttpGet;\r
50 import org.apache.http.client.methods.HttpRequestBase;\r
51 import org.apache.http.conn.scheme.Scheme;\r
52 import org.apache.http.conn.ssl.SSLSocketFactory;\r
53 import org.apache.http.entity.BasicHttpEntity;\r
54 import org.apache.http.impl.client.AbstractHttpClient;\r
55 import org.apache.http.impl.client.DefaultHttpClient;\r
56 import org.onap.dmaap.datarouter.provisioning.utils.DB;\r
57 import org.onap.dmaap.datarouter.provisioning.utils.URLUtilities;\r
58 \r
59 /**\r
60  * This class is the base class for those servlets that need to proxy their requests from the\r
61  * standby to active server.  Its methods perform the proxy function to the active server. If the\r
62  * active server is not reachable, a 503 (SC_SERVICE_UNAVAILABLE) is returned.  Only\r
63  * DELETE/GET/PUT/POST are supported.\r
64  *\r
65  * @author Robert Eby\r
66  * @version $Id: ProxyServlet.java,v 1.3 2014/03/24 18:47:10 eby Exp $\r
67  */\r
68 @SuppressWarnings("serial")\r
69 public class ProxyServlet extends BaseServlet {\r
70         private boolean inited = false;\r
71         private Scheme sch;\r
72 \r
73         /**\r
74          * Initialize this servlet, by setting up SSL.\r
75          */\r
76         @SuppressWarnings("deprecation")\r
77         @Override\r
78         public void init(ServletConfig config) throws ServletException {\r
79                 super.init(config);\r
80                 try {\r
81                         // Set up keystore\r
82                         Properties props = (new DB()).getProperties();\r
83                         String type  = props.getProperty(Main.KEYSTORE_TYPE_PROPERTY, "jks");\r
84                         String store = props.getProperty(Main.KEYSTORE_PATH_PROPERTY);\r
85                         String pass  = props.getProperty(Main.KEYSTORE_PASSWORD_PROPERTY);\r
86                         KeyStore keyStore = readStore(store, pass, type);\r
87 \r
88                         store = props.getProperty(Main.TRUSTSTORE_PATH_PROPERTY);\r
89                         pass  = props.getProperty(Main.TRUSTSTORE_PASSWORD_PROPERTY);\r
90                         if (store == null || store.length() == 0) {\r
91                                 store = Main.DEFAULT_TRUSTSTORE;\r
92                                 pass = "changeit";\r
93                         }\r
94                         KeyStore trustStore = readStore(store, pass, KeyStore.getDefaultType());\r
95 \r
96                         // We are connecting with the node name, but the certificate will have the CNAME\r
97                         // So we need to accept a non-matching certificate name\r
98                         SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore, "changeit", trustStore);\r
99                         socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);\r
100                         sch = new Scheme("https", 443, socketFactory);\r
101                         inited = true;\r
102                 } catch (Exception e) {\r
103                         e.printStackTrace();\r
104                 }\r
105                 intlogger.info("ProxyServlet: inited = "+inited);\r
106         }\r
107         private KeyStore readStore(String store, String pass, String type) throws KeyStoreException, FileNotFoundException {\r
108                 KeyStore ks = KeyStore.getInstance(type);\r
109                 FileInputStream instream = new FileInputStream(new File(store));\r
110                 try {\r
111                     ks.load(instream, pass.toCharArray());\r
112                 } catch (Exception x) {\r
113                         System.err.println("READING TRUSTSTORE: "+x);\r
114                 } finally {\r
115                     try { instream.close(); } catch (Exception ignore) {}\r
116                 }\r
117                 return ks;\r
118         }\r
119         /**\r
120          * Return <i>true</i> if the requester has NOT set the <i>noproxy</i> CGI variable.\r
121          * If they have, this indicates they want to forcibly turn the proxy off.\r
122          * @param req the HTTP request\r
123          * @return true or false\r
124          */\r
125         protected boolean isProxyOK(final HttpServletRequest req) {\r
126                 String t = req.getQueryString();\r
127                 if (t != null) {\r
128                         t = t.replaceAll("&amp;", "&");\r
129                         for (String s : t.split("&")) {\r
130                                 if (s.equals("noproxy") || s.startsWith("noproxy="))\r
131                                         return false;\r
132                         }\r
133                 }\r
134                 return true;\r
135         }\r
136         /**\r
137          * Is this the standby server?  If it is, the proxy functions can be used.\r
138          * If not, the proxy functions should not be called, and will send a response of 500\r
139          * (Internal Server Error).\r
140          * @return true if this server is the standby (and hence a proxy server).\r
141          */\r
142         public boolean isProxyServer() {\r
143                 SynchronizerTask st = SynchronizerTask.getSynchronizer();\r
144                 return st.getState() == SynchronizerTask.STANDBY;\r
145         }\r
146         /**\r
147          * Issue a proxy DELETE to the active provisioning server.\r
148          */\r
149         @Override\r
150         public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
151                 doProxy(req, resp, "DELETE");\r
152         }\r
153         /**\r
154          * Issue a proxy GET to the active provisioning server.\r
155          */\r
156         @Override\r
157         public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
158                 doProxy(req, resp, "GET");\r
159         }\r
160         /**\r
161          * Issue a proxy PUT to the active provisioning server.\r
162          */\r
163         @Override\r
164         public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
165                 doProxy(req, resp, "PUT");\r
166         }\r
167         /**\r
168          * Issue a proxy POST to the active provisioning server.\r
169          */\r
170         @Override\r
171         public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
172                 doProxy(req, resp, "POST");\r
173         }\r
174         /**\r
175          * Issue a proxy GET to the active provisioning server.  Unlike doGet() above,\r
176          * this method will allow the caller to fall back to other code if the remote server is unreachable.\r
177          * @return true if the proxy succeeded\r
178          */\r
179         public boolean doGetWithFallback(HttpServletRequest req, HttpServletResponse resp) throws IOException {\r
180                 boolean rv = false;\r
181                 if (inited) {\r
182                         String url = buildUrl(req);\r
183                         intlogger.info("ProxyServlet: proxying with fallback GET "+url);\r
184                         AbstractHttpClient httpclient = new DefaultHttpClient();\r
185                         HttpRequestBase proxy = new HttpGet(url);\r
186                         try {\r
187                                 httpclient.getConnectionManager().getSchemeRegistry().register(sch);\r
188 \r
189                                 // Copy request headers and request body\r
190                                 copyRequestHeaders(req, proxy);\r
191 \r
192                                 // Execute the request\r
193                                 HttpResponse pxy_response = httpclient.execute(proxy);\r
194 \r
195                                 // Get response headers and body\r
196                                 int code = pxy_response.getStatusLine().getStatusCode();\r
197                                 resp.setStatus(code);\r
198                                 copyResponseHeaders(pxy_response, resp);\r
199 \r
200                                 HttpEntity entity = pxy_response.getEntity();\r
201                                 if (entity != null) {\r
202                                         InputStream in = entity.getContent();\r
203                                         IOUtils.copy(in, resp.getOutputStream());\r
204                                         in.close();\r
205                                 }\r
206                                 rv = true;\r
207                         } catch (IOException e) {\r
208                                 System.err.println("ProxyServlet: "+e);\r
209                                 e.printStackTrace();\r
210                         } finally {\r
211                                 proxy.releaseConnection();\r
212                                 httpclient.getConnectionManager().shutdown();\r
213                         }\r
214                 } else {\r
215                         intlogger.warn("ProxyServlet: proxy disabled");\r
216                 }\r
217                 return rv;\r
218         }\r
219         private void doProxy(HttpServletRequest req, HttpServletResponse resp, final String method) throws IOException {\r
220                 if (inited && isProxyServer()) {\r
221                         String url = buildUrl(req);\r
222                         intlogger.info("ProxyServlet: proxying "+method + " "+url);\r
223                         AbstractHttpClient httpclient = new DefaultHttpClient();\r
224                         ProxyHttpRequest proxy = new ProxyHttpRequest(method, url);\r
225                         try {\r
226                                 httpclient.getConnectionManager().getSchemeRegistry().register(sch);\r
227 \r
228                                 // Copy request headers and request body\r
229                                 copyRequestHeaders(req, proxy);\r
230                                 if (method.equals("POST") || method.equals("PUT")){\r
231                                         BasicHttpEntity body = new BasicHttpEntity();\r
232                                         body.setContent(req.getInputStream());\r
233                                         body.setContentLength(-1);      // -1 = unknown\r
234                                         proxy.setEntity(body);\r
235                                 }\r
236 \r
237                                 // Execute the request\r
238                                 HttpResponse pxy_response = httpclient.execute(proxy);\r
239 \r
240                                 // Get response headers and body\r
241                                 int code = pxy_response.getStatusLine().getStatusCode();\r
242                                 resp.setStatus(code);\r
243                                 copyResponseHeaders(pxy_response, resp);\r
244 \r
245                                 HttpEntity entity = pxy_response.getEntity();\r
246                                 if (entity != null) {\r
247                                         InputStream in = entity.getContent();\r
248                                         IOUtils.copy(in, resp.getOutputStream());\r
249                                         in.close();\r
250                                 }\r
251                         } catch (IOException e) {\r
252                                 intlogger.warn("ProxyServlet: "+e);\r
253                                 resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);\r
254                                 e.printStackTrace();\r
255                         } finally {\r
256                                 proxy.releaseConnection();\r
257                                 httpclient.getConnectionManager().shutdown();\r
258                         }\r
259                 } else {\r
260                         intlogger.warn("ProxyServlet: proxy disabled");\r
261                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);\r
262                 }\r
263         }\r
264         private String buildUrl(HttpServletRequest req) {\r
265                 StringBuilder sb = new StringBuilder("https://");\r
266                 sb.append(URLUtilities.getPeerPodName());\r
267                 sb.append(req.getRequestURI());\r
268                 String q = req.getQueryString();\r
269                 if (q != null)\r
270                         sb.append("?").append(q);\r
271                 return sb.toString();\r
272         }\r
273         private void copyRequestHeaders(HttpServletRequest from, HttpRequestBase to) {\r
274                 @SuppressWarnings("unchecked")\r
275                 List<String> list = Collections.list(from.getHeaderNames());\r
276                 for (String name : list) {\r
277                         // Proxy code will add this one\r
278                         if (!name.equalsIgnoreCase("Content-Length"))\r
279                                 to.addHeader(name, from.getHeader(name));\r
280                 }\r
281         }\r
282         private void copyResponseHeaders(HttpResponse from, HttpServletResponse to) {\r
283                 for (Header hdr : from.getAllHeaders()) {\r
284                         // Don't copy Date: our Jetty will add another Date header\r
285                         if (!hdr.getName().equals("Date"))\r
286                                 to.addHeader(hdr.getName(), hdr.getValue());\r
287                 }\r
288         }\r
289 \r
290         public class ProxyHttpRequest extends HttpEntityEnclosingRequestBase {\r
291                 private final String method;\r
292 \r
293                 public ProxyHttpRequest(final String method, final String uri) {\r
294                         super();\r
295                         this.method = method;\r
296                 setURI(URI.create(uri));\r
297                 }\r
298                 @Override\r
299                 public String getMethod() {\r
300                         return method;\r
301                 }\r
302         }\r
303 }\r