Fix bottleneck on simulator requests
[sdc.git] / utils / webseal-simulator / src / main / java / org / openecomp / sdc / webseal / simulator / SdcProxy.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2019 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.webseal.simulator;
22
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStream;
26 import java.io.UnsupportedEncodingException;
27 import java.net.MalformedURLException;
28 import java.net.URL;
29 import java.net.URLEncoder;
30 import java.security.KeyStoreException;
31 import java.security.NoSuchAlgorithmException;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Enumeration;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Map.Entry;
38 import java.util.Set;
39 import java.util.stream.Collectors;
40 import java.util.zip.GZIPInputStream;
41 import javax.net.ssl.SSLContext;
42 import javax.servlet.RequestDispatcher;
43 import javax.servlet.ServletConfig;
44 import javax.servlet.ServletException;
45 import javax.servlet.ServletInputStream;
46 import javax.servlet.http.Cookie;
47 import javax.servlet.http.HttpServlet;
48 import javax.servlet.http.HttpServletRequest;
49 import javax.servlet.http.HttpServletResponse;
50 import lombok.AllArgsConstructor;
51 import lombok.Getter;
52 import org.apache.http.Header;
53 import org.apache.http.client.methods.CloseableHttpResponse;
54 import org.apache.http.client.methods.HttpDelete;
55 import org.apache.http.client.methods.HttpGet;
56 import org.apache.http.client.methods.HttpPost;
57 import org.apache.http.client.methods.HttpPut;
58 import org.apache.http.client.methods.HttpRequestBase;
59 import org.apache.http.client.methods.HttpUriRequest;
60 import org.apache.http.config.Registry;
61 import org.apache.http.config.RegistryBuilder;
62 import org.apache.http.conn.socket.ConnectionSocketFactory;
63 import org.apache.http.conn.socket.PlainConnectionSocketFactory;
64 import org.apache.http.conn.ssl.NoopHostnameVerifier;
65 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
66 import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
67 import org.apache.http.entity.ContentType;
68 import org.apache.http.entity.InputStreamEntity;
69 import org.apache.http.impl.client.CloseableHttpClient;
70 import org.apache.http.impl.client.HttpClients;
71 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
72 import org.apache.http.ssl.SSLContextBuilder;
73 import org.openecomp.sdc.webseal.simulator.conf.Conf;
74
75 public class SdcProxy extends HttpServlet {
76
77     private static final long serialVersionUID = 1L;
78     private static final Set<String> RESERVED_HEADERS =
79         Arrays.stream(ReservedHeaders.values()).map(ReservedHeaders::getValue).collect(Collectors.toSet());
80     private static final String USER_ID = "USER_ID";
81     private static final String HTTP_IV_USER = "HTTP_IV_USER";
82     private static final String SDC1 = "/sdc1";
83     private static final String ONBOARDING = "/onboarding/";
84     private static final String SCRIPTS = "/scripts";
85     private static final String STYLES = "/styles";
86     private static final String LANGUAGES = "/languages";
87     private static final String CONFIGURATIONS = "/configurations";
88     private URL url;
89     private CloseableHttpClient httpClient;
90     private Conf conf;
91
92     @Override
93     public void init(ServletConfig config) throws ServletException {
94         super.init(config);
95         conf = Conf.getInstance();
96         try {
97             url = new URL(conf.getFeHost());
98         } catch (MalformedURLException me) {
99             throw new ServletException("Proxy URL is invalid", me);
100         }
101
102         try {
103             httpClient = buildRestClient();
104         } catch (Exception e) {
105             throw new ServletException("Build rest client failed", e);
106         }
107     }
108
109     @Override
110     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
111         proxy(request, response, MethodEnum.GET);
112     }
113
114     @Override
115     public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
116
117         String userId = request.getParameter("userId");
118         String password = request.getParameter("password");
119
120         // Already sign-in
121         if (userId == null) {
122             userId = request.getHeader(USER_ID);
123         }
124
125         System.out.println("SdcProxy -> doPost userId=" + userId);
126         request.setAttribute("message", "OK");
127         if (password != null && getUser(userId, password) == null) {
128             MutableHttpServletRequest mutableRequest = new MutableHttpServletRequest(request);
129             RequestDispatcher view = request.getRequestDispatcher("login");
130             request.setAttribute("message", "ERROR: userid or password incorect");
131             view.forward(mutableRequest, response);
132         } else {
133             System.out.println("SdcProxy -> doPost going to doGet");
134             request.setAttribute(HTTP_IV_USER, userId);
135             proxy(request, response, MethodEnum.POST);
136         }
137     }
138
139     @Override
140     public void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
141         proxy(request, response, MethodEnum.PUT);
142     }
143
144     @Override
145     public void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
146         proxy(request, response, MethodEnum.DELETE);
147     }
148
149     private void proxy(HttpServletRequest request, HttpServletResponse response, MethodEnum methodEnum) throws IOException {
150
151         Map<String, String[]> requestParameters = request.getParameterMap();
152         String userIdHeader = getUseridFromRequest(request);
153         // new request - forward to login page
154         if (userIdHeader == null) {
155             System.out.print("Going to login");
156             response.sendRedirect("/login");
157             return;
158         }
159
160         final User user = getUser(userIdHeader);
161
162         String uri = getUri(request, requestParameters);
163         HttpRequestBase httpMethod = createHttpMethod(request, methodEnum, uri);
164         addHeadersToMethod(httpMethod, user, request);
165
166         try (CloseableHttpResponse closeableHttpResponse = httpClient.execute(httpMethod)) {
167             response.setStatus(closeableHttpResponse.getStatusLine().getStatusCode());
168             if (request.getRequestURI().indexOf(".svg") > -1) {
169                 response.setContentType("image/svg+xml");
170             }
171
172             if (closeableHttpResponse.getEntity() != null) {
173                 InputStream responseBodyStream = closeableHttpResponse.getEntity().getContent();
174                 Header contentEncodingHeader = closeableHttpResponse.getLastHeader("Content-Encoding");
175                 if (contentEncodingHeader != null && contentEncodingHeader.getValue().equalsIgnoreCase("gzip")) {
176                     responseBodyStream = new GZIPInputStream(responseBodyStream);
177                 }
178                 write(responseBodyStream, response.getOutputStream());
179             }
180         }
181     }
182
183     private User getUser(String userId, String password) {
184         User user = getUser(userId);
185         if (user.getPassword().equals(password)) {
186             return user;
187         }
188         return null;
189     }
190
191     private User getUser(String userId) {
192         return conf.getUsers().get(userId);
193
194     }
195
196     private List<String> getContextPaths() {
197         List<String> contextPaths = new ArrayList<>();
198         contextPaths.add(SDC1);
199         contextPaths.add(ONBOARDING);
200         contextPaths.add(STYLES);
201         contextPaths.add(SCRIPTS);
202         contextPaths.add(LANGUAGES);
203         contextPaths.add(CONFIGURATIONS);
204         return contextPaths;
205     }
206
207     private String getUri(HttpServletRequest request, Map<String, String[]> requestParameters) throws UnsupportedEncodingException {
208         String suffix = request.getRequestURI();
209         if (getContextPaths().stream().anyMatch(request.getRequestURI()::contains)) {
210             suffix = alignUrlProxy(suffix);
211         }
212         StringBuilder query = alignUrlParameters(requestParameters);
213         return String.format("%s%s", url.toString() + suffix, query.toString());
214     }
215
216     private HttpRequestBase createHttpMethod(HttpServletRequest request, MethodEnum methodEnum, String uri) throws IOException {
217         HttpRequestBase proxyMethod = null;
218         ServletInputStream inputStream = null;
219         InputStreamEntity entity = null;
220
221         switch (methodEnum) {
222             case GET:
223                 proxyMethod = new HttpGet(uri);
224                 break;
225             case POST:
226                 proxyMethod = new HttpPost(uri);
227                 inputStream = request.getInputStream();
228                 entity = new InputStreamEntity(inputStream, getContentType(request));
229                 ((HttpPost) proxyMethod).setEntity(entity);
230                 break;
231             case PUT:
232                 proxyMethod = new HttpPut(uri);
233                 inputStream = request.getInputStream();
234                 entity = new InputStreamEntity(inputStream, getContentType(request));
235                 ((HttpPut) proxyMethod).setEntity(entity);
236                 break;
237             case DELETE:
238                 proxyMethod = new HttpDelete(uri);
239                 break;
240         }
241         return proxyMethod;
242     }
243
244     private ContentType getContentType(HttpServletRequest request) {
245         String contentTypeStr = request.getContentType();
246         if (contentTypeStr == null) {
247             contentTypeStr = request.getHeader("contentType");
248         }
249         ContentType contentType = ContentType.parse(contentTypeStr);
250         return ContentType.create(contentType.getMimeType());
251     }
252
253     private String getUseridFromRequest(HttpServletRequest request) {
254
255         String userIdHeader = request.getHeader(USER_ID);
256         if (userIdHeader != null) {
257             return userIdHeader;
258         }
259         Object o = request.getAttribute(HTTP_IV_USER);
260         if (o != null) {
261             return o.toString();
262         }
263         Cookie[] cookies = request.getCookies();
264
265         if (cookies != null) {
266             for (int i = 0; i < cookies.length; ++i) {
267                 if (cookies[i].getName().equals(USER_ID)) {
268                     userIdHeader = cookies[i].getValue();
269                 }
270             }
271         }
272         return userIdHeader;
273     }
274
275     private void addHeadersToMethod(HttpUriRequest proxyMethod, User user, HttpServletRequest request) {
276
277         proxyMethod.setHeader(ReservedHeaders.HTTP_IV_USER.name(), user.getUserId());
278         proxyMethod.setHeader(ReservedHeaders.USER_ID.name(), user.getUserId());
279         proxyMethod.setHeader(ReservedHeaders.HTTP_CSP_FIRSTNAME.name(), user.getFirstName());
280         proxyMethod.setHeader(ReservedHeaders.HTTP_CSP_EMAIL.name(), user.getEmail());
281         proxyMethod.setHeader(ReservedHeaders.HTTP_CSP_LASTNAME.name(), user.getLastName());
282         proxyMethod.setHeader(ReservedHeaders.HTTP_IV_REMOTE_ADDRESS.name(), "0.0.0.0");
283         proxyMethod.setHeader(ReservedHeaders.HTTP_CSP_WSTYPE.name(), "Intranet");
284         proxyMethod.setHeader(ReservedHeaders.HTTP_CSP_EMAIL.name(), "me@mail.com");
285
286         Enumeration<String> headerNames = request.getHeaderNames();
287         while (headerNames.hasMoreElements()) {
288             String headerName = headerNames.nextElement();
289             if (!RESERVED_HEADERS.contains(headerName)) {
290                 Enumeration<String> headers = request.getHeaders(headerName);
291                 while (headers.hasMoreElements()) {
292                     String headerValue = headers.nextElement();
293                     proxyMethod.setHeader(headerName, headerValue);
294                 }
295             }
296         }
297     }
298
299     private String alignUrlProxy(String requestURI) {
300
301         int i = requestURI.indexOf(ONBOARDING);
302         if (-1 != i) {
303             return requestURI.substring(i);
304         }
305
306         i = requestURI.indexOf(SDC1 + SDC1);
307         if (-1 != i) {
308             return requestURI.substring(SDC1.length());
309         }
310
311         i = requestURI.indexOf(SDC1);
312         if (-1 != i) {
313             return requestURI;
314         }
315
316         return SDC1 + requestURI;
317     }
318
319     private StringBuilder alignUrlParameters(Map<String, String[]> requestParameters) throws UnsupportedEncodingException {
320         final var query = new StringBuilder();
321         for (final Entry<String, String[]> entry : requestParameters.entrySet()) {
322             for (final String value : entry.getValue()) {
323                 if (query.length() == 0) {
324                     query.append("?");
325                 } else {
326                     query.append("&");
327                 }
328                 query.append(String.format("&%s=%s", URLEncoder.encode(entry.getKey(), "UTF-8"), URLEncoder.encode(value, "UTF-8")));
329             }
330         }
331         return query;
332     }
333
334     private void write(InputStream inputStream, OutputStream outputStream) throws IOException {
335         int b;
336         while (inputStream != null && (b = inputStream.read()) != -1) {
337             outputStream.write(b);
338         }
339         outputStream.flush();
340     }
341
342     @Override
343     public String getServletInfo() {
344         return "Http Proxy Servlet";
345     }
346
347     private CloseableHttpClient buildRestClient() throws NoSuchAlgorithmException, KeyStoreException {
348         final var builder = new SSLContextBuilder();
349         builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
350         SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault(),
351             NoopHostnameVerifier.INSTANCE);
352         Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
353             .register("http", new PlainConnectionSocketFactory())
354             .register("https", sslsf)
355             .build();
356         PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
357         return HttpClients.custom()
358             .setSSLSocketFactory(sslsf)
359             .setConnectionManager(cm)
360             .build();
361     }
362
363     @AllArgsConstructor
364     @Getter
365     enum ReservedHeaders {
366         HTTP_IV_USER(SdcProxy.HTTP_IV_USER), USER_ID(SdcProxy.USER_ID), HTTP_CSP_FIRSTNAME("HTTP_CSP_FIRSTNAME"), HTTP_CSP_EMAIL(
367             "HTTP_CSP_EMAIL"), HTTP_CSP_LASTNAME("HTTP_CSP_LASTNAME"), HTTP_IV_REMOTE_ADDRESS("HTTP_IV_REMOTE_ADDRESS"), HTTP_CSP_WSTYPE(
368             "HTTP_CSP_WSTYPE"), HOST("Host"), CONTENTLENGTH("Content-Length");
369
370         private final String value;
371
372     }
373
374 }