1 /*******************************************************************************
2 * ============LICENSE_START==================================================
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
11 * * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 ******************************************************************************/
25 package org.onap.dmaap.datarouter.provisioning;
27 import static org.onap.dmaap.datarouter.provisioning.utils.HttpServletUtils.sendResponseError;
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;
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;
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.
60 * @version $Id: ProxyServlet.java,v 1.3 2014/03/24 18:47:10 eby Exp $
62 @SuppressWarnings("serial")
64 public class ProxyServlet extends BaseServlet {
66 private boolean inited = false;
70 * Initialize this servlet, by setting up SSL.
72 @SuppressWarnings("deprecation")
74 public void init(ServletConfig config) throws ServletException {
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);
82 PlainSocketFactory socketFactory = new PlainSocketFactory();
83 sch = new Scheme("http", 80, socketFactory);
86 } catch (Exception e) {
87 intlogger.error("ProxyServlet.init: " + e.getMessage(), e);
89 intlogger.info("ProxyServlet: inited = " + inited);
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.
96 * @param req the HTTP request
97 * @return true or false
99 boolean isProxyOK(final HttpServletRequest req) {
100 String str = req.getQueryString();
102 str = str.replaceAll("&", "&");
103 for (String s : str.split("&")) {
104 if ("noproxy".equals(s) || s.startsWith("noproxy=")) {
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).
116 * @return true if this server is the standby (and hence a proxy server).
118 boolean isProxyServer() {
119 SynchronizerTask st = SynchronizerTask.getSynchronizer();
120 return st.getPodState() == SynchronizerTask.STANDBY_POD;
124 * Issue a proxy DELETE to the active provisioning server.
127 public void doDelete(HttpServletRequest req, HttpServletResponse resp) {
128 doProxy(req, resp, "DELETE");
132 * Issue a proxy GET to the active provisioning server.
135 public void doGet(HttpServletRequest req, HttpServletResponse resp) {
136 doProxy(req, resp, "GET");
140 * Issue a proxy PUT to the active provisioning server.
143 public void doPut(HttpServletRequest req, HttpServletResponse resp) {
144 doProxy(req, resp, "PUT");
148 * Issue a proxy POST to the active provisioning server.
151 public void doPost(HttpServletRequest req, HttpServletResponse resp) {
152 doProxy(req, resp, "POST");
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.
159 * @return true if the proxy succeeded
161 boolean doGetWithFallback(HttpServletRequest req, HttpServletResponse resp) {
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);
169 httpclient.getConnectionManager().getSchemeRegistry().register(sch);
171 // Copy request headers and request body
172 copyRequestHeaders(req, proxy);
174 // Execute the request
175 HttpResponse pxyResponse = httpclient.execute(proxy);
177 // Get response headers and body
178 int code = pxyResponse.getStatusLine().getStatusCode();
179 resp.setStatus(code);
180 copyResponseHeaders(pxyResponse, resp);
181 copyEntityContent(pxyResponse, resp);
184 } catch (IOException e) {
185 intlogger.error("ProxyServlet.doGetWithFallback: " + e.getMessage(), e);
187 proxy.releaseConnection();
188 httpclient.getConnectionManager().shutdown();
192 intlogger.warn("ProxyServlet: proxy disabled");
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);
204 httpclient.getConnectionManager().getSchemeRegistry().register(sch);
206 // Copy request headers and request body
207 copyRequestHeaders(req, proxy);
209 handlePutOrPost(req, method, proxy);
211 // Execute the request
212 HttpResponse pxyResponse = httpclient.execute(proxy);
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);
223 proxy.releaseConnection();
224 httpclient.getConnectionManager().shutdown();
228 intlogger.warn("ProxyServlet: proxy disabled");
229 sendResponseError(resp, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, DB_PROBLEM_MSG, intlogger);
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);
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();
248 sb.append("?").append(query);
250 return sb.toString();
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));
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());
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);
283 public static class ProxyHttpRequest extends HttpEntityEnclosingRequestBase {
285 private final String method;
287 ProxyHttpRequest(final String method, final String uri) {
289 this.method = method;
290 setURI(URI.create(uri));
294 public String getMethod() {