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