Add Option to proxy to plugins
[sdc.git] / catalog-fe / src / main / java / org / openecomp / sdc / fe / servlets / FeProxyServlet.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 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
21 package org.openecomp.sdc.fe.servlets;
22
23 import com.google.common.cache.Cache;
24 import com.google.common.cache.CacheBuilder;
25 import org.eclipse.jetty.client.api.Response;
26 import org.openecomp.sdc.common.api.Constants;
27 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
28 import org.openecomp.sdc.common.log.wrappers.Logger;
29 import org.openecomp.sdc.fe.config.Configuration;
30 import org.openecomp.sdc.fe.config.ConfigurationManager;
31 import org.openecomp.sdc.fe.config.FeEcompErrorManager;
32 import org.openecomp.sdc.fe.config.PluginsConfiguration;
33 import org.openecomp.sdc.fe.config.PluginsConfiguration.Plugin;
34 import org.openecomp.sdc.fe.impl.MdcData;
35 import org.openecomp.sdc.fe.utils.BeProtocol;
36 import org.slf4j.MDC;
37
38 import javax.servlet.http.HttpServletRequest;
39 import javax.servlet.http.HttpServletResponse;
40 import java.net.MalformedURLException;
41 import java.net.URL;
42 import java.util.concurrent.TimeUnit;
43
44 public class FeProxyServlet extends SSLProxyServlet {
45     private static final long serialVersionUID = 1L;
46     private static final String URL = "%s://%s%s%s";
47     private static final String ONBOARDING_CONTEXT = "/onboarding-api";
48     private static final String DCAED_CONTEXT = "/dcae-api";
49     private static final String SDC1_FE_PROXY = "/sdc1/feProxy";
50     private static final String SDC1_PLUGIN_REDIRECT = SDC1_FE_PROXY + "/plugin";
51
52     private static final Logger LOGGER = Logger.getLogger(FeProxyServlet.class);
53     private static final int EXPIRE_DURATION = 10;
54     private static Cache<String, MdcData> mdcDataCache = CacheBuilder.newBuilder().expireAfterWrite(EXPIRE_DURATION, TimeUnit.SECONDS).build();
55
56
57     @Override
58     protected String rewriteTarget(HttpServletRequest request) {
59         String originalUrl = "";
60         String redirectedUrl = "";
61
62         try {
63             logFeRequest(request);
64
65             originalUrl = request.getRequestURL().toString();
66             redirectedUrl = getModifiedUrl(request);
67
68         } catch (MalformedURLException mue) {
69             FeEcompErrorManager.getInstance().logFeHttpLoggingError("FE Request");
70             LOGGER.error(EcompLoggerErrorCode.DATA_ERROR, "FeProxyServlet rewriteTarget", "sdc-FE", "Malformed URL Exception: ", mue);
71         } catch (Exception e) {
72             FeEcompErrorManager.getInstance().logFeHttpLoggingError("FE Request");
73             LOGGER.error(EcompLoggerErrorCode.UNKNOWN_ERROR, "FeProxyServlet rewriteTarget", "sdc-FE", "Unexpected FE request processing error: ", e);
74         }
75
76         LOGGER.debug("FeProxyServlet Redirecting request from: {} , to: {}", originalUrl, redirectedUrl);
77
78         return redirectedUrl;
79     }
80
81     @Override
82     protected void onProxyResponseSuccess(HttpServletRequest request, HttpServletResponse proxyResponse, Response response) {
83         try {
84             logFeResponse(request, response);
85         } catch (Exception e) {
86             FeEcompErrorManager.getInstance().logFeHttpLoggingError("FE Response");
87             LOGGER.error(EcompLoggerErrorCode.UNKNOWN_ERROR, "FeProxyServlet onProxyResponseSuccess", "sdc-FE", "Unexpected FE response logging error: ", e);
88         }
89         super.onProxyResponseSuccess(request, proxyResponse, response);
90     }
91
92     private void logFeRequest(HttpServletRequest httpRequest) {
93
94         MDC.clear();
95
96         Long transactionStartTime = System.currentTimeMillis();
97         // UUID - In FE, we are supposed to get the below header from UI.
98         // We do not generate it if it's missing - BE does.
99         String uuid = httpRequest.getHeader(Constants.X_ECOMP_REQUEST_ID_HEADER);
100         String serviceInstanceID = httpRequest.getHeader(Constants.X_ECOMP_SERVICE_ID_HEADER);
101
102         if (uuid != null && uuid.length() > 0) {
103             // UserId for logging
104             String userId = httpRequest.getHeader(Constants.USER_ID_HEADER);
105
106             String remoteAddr = httpRequest.getRemoteAddr();
107             String localAddr = httpRequest.getLocalAddr();
108
109             mdcDataCache.put(uuid, new MdcData(serviceInstanceID, userId, remoteAddr, localAddr, transactionStartTime));
110
111             updateMdc(uuid, serviceInstanceID, userId, remoteAddr, localAddr, null);
112         }
113         inHttpRequest(httpRequest);
114     }
115
116     private void logFeResponse(HttpServletRequest request, Response proxyResponse) {
117         String uuid = request.getHeader(Constants.X_ECOMP_REQUEST_ID_HEADER);
118         String transactionRoundTime = null;
119
120         if (uuid != null) {
121             MdcData mdcData = mdcDataCache.getIfPresent(uuid);
122             if (mdcData != null) {
123                 Long transactionStartTime = mdcData.getTransactionStartTime();
124                 if (transactionStartTime != null) { // should'n ever be null, but
125                     // just to be defensive
126                     transactionRoundTime = Long.toString(System.currentTimeMillis() - transactionStartTime);
127                 }
128                 updateMdc(uuid, mdcData.getServiceInstanceID(), mdcData.getUserId(), mdcData.getRemoteAddr(), mdcData.getLocalAddr(), transactionRoundTime);
129             }
130         }
131         outHttpResponse(proxyResponse);
132
133         MDC.clear();
134     }
135
136     // Extracted for purpose of clear method name, for logback %M parameter
137     private void inHttpRequest(HttpServletRequest httpRequest) {
138         LOGGER.info("{} {} {}", httpRequest.getMethod(), httpRequest.getRequestURI(), httpRequest.getProtocol());
139     }
140
141     // Extracted for purpose of clear method name, for logback %M parameter
142     private void outHttpResponse(Response proxyResponse) {
143         LOGGER.info("SC=\"{}\"", proxyResponse.getStatus());
144     }
145
146     private void updateMdc(String uuid, String serviceInstanceID, String userId, String remoteAddr, String localAddr, String transactionStartTime) {
147         MDC.put("uuid", uuid);
148         MDC.put("serviceInstanceID", serviceInstanceID);
149         MDC.put("userId", userId);
150         MDC.put("remoteAddr", remoteAddr);
151         MDC.put("localAddr", localAddr);
152         MDC.put("timer", transactionStartTime);
153     }
154
155
156     /****
157      * scan all the plugins from the configuration against the URL and the redicert path
158      * @param request
159      * @return
160      */
161     private Plugin getPluginProxyForRequest(HttpServletRequest request) {
162         return getPluginConfiguration(request).getPluginsList()
163                 .stream()
164                 .filter(plugin -> {
165                     if (plugin.getPluginProxyRedirectPath() != null && !plugin.getPluginProxyRedirectPath().isEmpty()) {
166                         return request.getRequestURI().contains(SDC1_PLUGIN_REDIRECT + plugin.getPluginProxyRedirectPath());
167                     } else {
168                         return false;
169                     }
170                 })
171                 .findFirst().orElse(null);
172     }
173
174     private String getModifiedUrl(HttpServletRequest request) throws MalformedURLException {
175         Configuration config = getConfiguration(request);
176         if (config == null) {
177             LOGGER.error(EcompLoggerErrorCode.UNKNOWN_ERROR, "FeProxyServlet getModifiedUrl", "sdc-FE", "failed to retrieve configuration.");
178             throw new RuntimeException("failed to read FE configuration");
179         }
180         String uri = request.getRequestURI();
181
182         // the modify logic is as follows:
183         // - proxy ONBOARDING to the onboarding context. this is not a plugin and hardcoded
184         // - proxy DCAE to the correct context. also - not a plugin but hardcoded
185         // - proxy to the plugin according to configuration if the path is found in the plugin patterns
186         // - proxy to the catalog backend if no other proxy was found
187
188         if (uri.contains(ONBOARDING_CONTEXT)) {
189             uri = uri.replace(SDC1_FE_PROXY + ONBOARDING_CONTEXT, ONBOARDING_CONTEXT);
190             return getModifiedUrlString(
191                     request,
192                     uri,
193                     config.getOnboarding().getHostBe(),
194                     config.getOnboarding().getPortBe().toString(),
195                     config.getOnboarding().getProtocolBe());
196         }
197         if (uri.contains(DCAED_CONTEXT)) {
198             uri = uri.replace(SDC1_FE_PROXY + DCAED_CONTEXT, DCAED_CONTEXT);
199             return getModifiedUrlString(
200                     request,
201                     uri,
202                     config.getBeHost(),
203                     getCatalogBePort(config),
204                     config.getBeProtocol());
205         }
206
207         if (uri.contains(SDC1_PLUGIN_REDIRECT)) {
208             Plugin proxyPlugin = getPluginProxyForRequest(request);
209             if (proxyPlugin != null) {
210                 String proxyUrlStr = (proxyPlugin.getPluginFeProxyUrl() != null) ? proxyPlugin.getPluginFeProxyUrl() : proxyPlugin.getPluginSourceUrl();
211                 URL proxyUrl = new URL(proxyUrlStr);
212                 uri = uri.replace(SDC1_PLUGIN_REDIRECT + proxyPlugin.getPluginProxyRedirectPath(), proxyUrl.getPath());
213                 return getModifiedUrlString(request, uri, proxyUrl);
214             }
215         }
216
217         Plugin proxyPlugin = getPluginProxyForRequest(request);
218         if (proxyPlugin != null) {
219             String proxyUrlStr = (proxyPlugin.getPluginFeProxyUrl() != null) ? proxyPlugin.getPluginFeProxyUrl() : proxyPlugin.getPluginSourceUrl();
220             URL proxyUrl = new URL(proxyUrlStr);
221             uri = uri.replace(SDC1_FE_PROXY + proxyPlugin.getPluginProxyRedirectPath(), proxyUrl.getPath());
222             return getModifiedUrlString(request, uri, proxyUrl);
223         }
224
225         uri = uri.replace(SDC1_FE_PROXY, "/sdc2");
226         return getModifiedUrlString(
227                 request,
228                 uri,
229                 config.getBeHost(),
230                 getCatalogBePort(config),
231                 config.getBeProtocol());
232     }
233
234
235     private String getCatalogBePort(Configuration config) {
236         if (config.getBeProtocol().equals(BeProtocol.HTTP.getProtocolName())) {
237             return config.getBeHttpPort().toString();
238         } else {
239             return config.getBeSslPort().toString();
240         }
241     }
242
243     private String getModifiedUrlString(HttpServletRequest request, String uri, URL url) {
244         String queryString = getQueryString(request);
245         return String.format(URL, url.getProtocol(), url.getAuthority(), uri, queryString);
246     }
247
248     private String getModifiedUrlString(HttpServletRequest request, String uri, String host, String port, String protocol) {
249         String authority = getAuthority(host, port);
250         String queryString = getQueryString(request);
251         return String.format(URL, protocol, authority, uri, queryString);
252     }
253
254     private PluginsConfiguration getPluginConfiguration(HttpServletRequest request) {
255         return ((ConfigurationManager) request.getSession().getServletContext().getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).getPluginsConfiguration();
256     }
257
258     private Configuration getConfiguration(HttpServletRequest request) {
259         return ((ConfigurationManager) request.getSession().getServletContext().getAttribute(Constants.CONFIGURATION_MANAGER_ATTR)).getConfiguration();
260     }
261
262     private String getAuthority(String host, String port) {
263         String authority;
264         if (port == null) {
265             authority = host;
266         } else {
267             authority = host + ":" + port;
268         }
269         return authority;
270     }
271
272     private String getQueryString(HttpServletRequest request) {
273         String queryString = request.getQueryString();
274         if (queryString != null) {
275             queryString = "?" + queryString;
276         } else {
277             queryString = "";
278         }
279         return queryString;
280     }
281 }