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;
28 import java.io.FileInputStream;
29 import java.io.FileNotFoundException;
30 import java.io.IOException;
31 import java.io.InputStream;
33 import java.security.KeyStore;
34 import java.security.KeyStoreException;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.Properties;
39 import javax.servlet.ServletConfig;
40 import javax.servlet.ServletException;
41 import javax.servlet.http.HttpServletRequest;
42 import javax.servlet.http.HttpServletResponse;
44 import org.apache.commons.io.IOUtils;
45 import org.apache.http.Header;
46 import org.apache.http.HttpEntity;
47 import org.apache.http.HttpResponse;
48 import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
49 import org.apache.http.client.methods.HttpGet;
50 import org.apache.http.client.methods.HttpRequestBase;
51 import org.apache.http.conn.scheme.Scheme;
52 import org.apache.http.conn.ssl.SSLSocketFactory;
53 import org.apache.http.entity.BasicHttpEntity;
54 import org.apache.http.impl.client.AbstractHttpClient;
55 import org.apache.http.impl.client.DefaultHttpClient;
56 import org.onap.dmaap.datarouter.provisioning.utils.DB;
57 import org.onap.dmaap.datarouter.provisioning.utils.URLUtilities;
60 * This class is the base class for those servlets that need to proxy their requests from the
61 * standby to active server. Its methods perform the proxy function to the active server. If the
62 * active server is not reachable, a 503 (SC_SERVICE_UNAVAILABLE) is returned. Only
63 * DELETE/GET/PUT/POST are supported.
66 * @version $Id: ProxyServlet.java,v 1.3 2014/03/24 18:47:10 eby Exp $
68 @SuppressWarnings("serial")
69 public class ProxyServlet extends BaseServlet {
70 private boolean inited = false;
74 * Initialize this servlet, by setting up SSL.
76 @SuppressWarnings("deprecation")
78 public void init(ServletConfig config) throws ServletException {
82 Properties props = (new DB()).getProperties();
83 String type = props.getProperty(Main.KEYSTORE_TYPE_PROPERTY, "jks");
84 String store = props.getProperty(Main.KEYSTORE_PATH_PROPERTY);
85 String pass = props.getProperty(Main.KEYSTORE_PASSWORD_PROPERTY);
86 KeyStore keyStore = readStore(store, pass, type);
88 store = props.getProperty(Main.TRUSTSTORE_PATH_PROPERTY);
89 pass = props.getProperty(Main.TRUSTSTORE_PASSWORD_PROPERTY);
90 if (store == null || store.length() == 0) {
91 store = Main.DEFAULT_TRUSTSTORE;
94 KeyStore trustStore = readStore(store, pass, KeyStore.getDefaultType());
96 // We are connecting with the node name, but the certificate will have the CNAME
97 // So we need to accept a non-matching certificate name
98 SSLSocketFactory socketFactory = new SSLSocketFactory(keyStore, "changeit", trustStore);
99 socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
100 sch = new Scheme("https", 443, socketFactory);
102 } catch (Exception e) {
105 intlogger.info("ProxyServlet: inited = "+inited);
107 private KeyStore readStore(String store, String pass, String type) throws KeyStoreException, FileNotFoundException {
108 KeyStore ks = KeyStore.getInstance(type);
109 FileInputStream instream = new FileInputStream(new File(store));
111 ks.load(instream, pass.toCharArray());
112 } catch (Exception x) {
113 System.err.println("READING TRUSTSTORE: "+x);
115 try { instream.close(); } catch (Exception ignore) {}
120 * Return <i>true</i> if the requester has NOT set the <i>noproxy</i> CGI variable.
121 * If they have, this indicates they want to forcibly turn the proxy off.
122 * @param req the HTTP request
123 * @return true or false
125 protected boolean isProxyOK(final HttpServletRequest req) {
126 String t = req.getQueryString();
128 t = t.replaceAll("&", "&");
129 for (String s : t.split("&")) {
130 if (s.equals("noproxy") || s.startsWith("noproxy="))
137 * Is this the standby server? If it is, the proxy functions can be used.
138 * If not, the proxy functions should not be called, and will send a response of 500
139 * (Internal Server Error).
140 * @return true if this server is the standby (and hence a proxy server).
142 public boolean isProxyServer() {
143 SynchronizerTask st = SynchronizerTask.getSynchronizer();
144 return st.getState() == SynchronizerTask.STANDBY;
147 * Issue a proxy DELETE to the active provisioning server.
150 public void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
151 doProxy(req, resp, "DELETE");
154 * Issue a proxy GET to the active provisioning server.
157 public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
158 doProxy(req, resp, "GET");
161 * Issue a proxy PUT to the active provisioning server.
164 public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
165 doProxy(req, resp, "PUT");
168 * Issue a proxy POST to the active provisioning server.
171 public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
172 doProxy(req, resp, "POST");
175 * Issue a proxy GET to the active provisioning server. Unlike doGet() above,
176 * this method will allow the caller to fall back to other code if the remote server is unreachable.
177 * @return true if the proxy succeeded
179 public boolean doGetWithFallback(HttpServletRequest req, HttpServletResponse resp) throws IOException {
182 String url = buildUrl(req);
183 intlogger.info("ProxyServlet: proxying with fallback GET "+url);
184 AbstractHttpClient httpclient = new DefaultHttpClient();
185 HttpRequestBase proxy = new HttpGet(url);
187 httpclient.getConnectionManager().getSchemeRegistry().register(sch);
189 // Copy request headers and request body
190 copyRequestHeaders(req, proxy);
192 // Execute the request
193 HttpResponse pxy_response = httpclient.execute(proxy);
195 // Get response headers and body
196 int code = pxy_response.getStatusLine().getStatusCode();
197 resp.setStatus(code);
198 copyResponseHeaders(pxy_response, resp);
200 HttpEntity entity = pxy_response.getEntity();
201 if (entity != null) {
202 InputStream in = entity.getContent();
203 IOUtils.copy(in, resp.getOutputStream());
207 } catch (IOException e) {
208 System.err.println("ProxyServlet: "+e);
211 proxy.releaseConnection();
212 httpclient.getConnectionManager().shutdown();
215 intlogger.warn("ProxyServlet: proxy disabled");
219 private void doProxy(HttpServletRequest req, HttpServletResponse resp, final String method) throws IOException {
220 if (inited && isProxyServer()) {
221 String url = buildUrl(req);
222 intlogger.info("ProxyServlet: proxying "+method + " "+url);
223 AbstractHttpClient httpclient = new DefaultHttpClient();
224 ProxyHttpRequest proxy = new ProxyHttpRequest(method, url);
226 httpclient.getConnectionManager().getSchemeRegistry().register(sch);
228 // Copy request headers and request body
229 copyRequestHeaders(req, proxy);
230 if (method.equals("POST") || method.equals("PUT")){
231 BasicHttpEntity body = new BasicHttpEntity();
232 body.setContent(req.getInputStream());
233 body.setContentLength(-1); // -1 = unknown
234 proxy.setEntity(body);
237 // Execute the request
238 HttpResponse pxy_response = httpclient.execute(proxy);
240 // Get response headers and body
241 int code = pxy_response.getStatusLine().getStatusCode();
242 resp.setStatus(code);
243 copyResponseHeaders(pxy_response, resp);
245 HttpEntity entity = pxy_response.getEntity();
246 if (entity != null) {
247 InputStream in = entity.getContent();
248 IOUtils.copy(in, resp.getOutputStream());
251 } catch (IOException e) {
252 intlogger.warn("ProxyServlet: "+e);
253 resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
256 proxy.releaseConnection();
257 httpclient.getConnectionManager().shutdown();
260 intlogger.warn("ProxyServlet: proxy disabled");
261 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
264 private String buildUrl(HttpServletRequest req) {
265 StringBuilder sb = new StringBuilder("https://");
266 sb.append(URLUtilities.getPeerPodName());
267 sb.append(req.getRequestURI());
268 String q = req.getQueryString();
270 sb.append("?").append(q);
271 return sb.toString();
273 private void copyRequestHeaders(HttpServletRequest from, HttpRequestBase to) {
274 @SuppressWarnings("unchecked")
275 List<String> list = Collections.list(from.getHeaderNames());
276 for (String name : list) {
277 // Proxy code will add this one
278 if (!name.equalsIgnoreCase("Content-Length"))
279 to.addHeader(name, from.getHeader(name));
282 private void copyResponseHeaders(HttpResponse from, HttpServletResponse to) {
283 for (Header hdr : from.getAllHeaders()) {
284 // Don't copy Date: our Jetty will add another Date header
285 if (!hdr.getName().equals("Date"))
286 to.addHeader(hdr.getName(), hdr.getValue());
290 public class ProxyHttpRequest extends HttpEntityEnclosingRequestBase {
291 private final String method;
293 public ProxyHttpRequest(final String method, final String uri) {
295 this.method = method;
296 setURI(URI.create(uri));
299 public String getMethod() {