Code improvement for pending sonar issues
[cli.git] / profiles / http / src / main / java / org / onap / cli / fw / http / connect / OnapHttpConnection.java
1 /*
2  * Copyright 2017 Huawei Technologies Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package org.onap.cli.fw.http.connect;
18
19 import java.io.File;
20 import java.io.IOException;
21 import java.net.MalformedURLException;
22 import java.net.URI;
23 import java.net.URL;
24 import java.nio.charset.StandardCharsets;
25 import java.security.cert.X509Certificate;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30
31 import javax.net.ssl.SSLContext;
32 import javax.net.ssl.TrustManager;
33 import javax.net.ssl.X509TrustManager;
34
35 import org.apache.http.Header;
36 import org.apache.http.HttpEntity;
37 import org.apache.http.HttpResponse;
38 import org.apache.http.annotation.Contract;
39 import org.apache.http.annotation.ThreadingBehavior;
40 import org.apache.http.client.CookieStore;
41 import org.apache.http.client.config.RequestConfig;
42 import org.apache.http.client.methods.HttpDelete;
43 import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
44 import org.apache.http.client.methods.HttpGet;
45 import org.apache.http.client.methods.HttpPatch;
46 import org.apache.http.client.methods.HttpPost;
47 import org.apache.http.client.methods.HttpPut;
48 import org.apache.http.client.methods.HttpRequestBase;
49 import org.apache.http.config.Registry;
50 import org.apache.http.config.RegistryBuilder;
51 import org.apache.http.conn.HttpClientConnectionManager;
52 import org.apache.http.conn.socket.ConnectionSocketFactory;
53 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
54 import org.apache.http.cookie.Cookie;
55 import org.apache.http.entity.ContentType;
56 import org.apache.http.entity.StringEntity;
57 import org.apache.http.entity.mime.MultipartEntityBuilder;
58 import org.apache.http.impl.client.BasicCookieStore;
59 import org.apache.http.impl.client.HttpClients;
60 import org.apache.http.impl.client.LaxRedirectStrategy;
61 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
62 import org.apache.http.impl.cookie.BasicClientCookie;
63 import org.apache.http.protocol.BasicHttpContext;
64 import org.apache.http.protocol.HttpContext;
65 import org.apache.http.util.EntityUtils;
66 import org.onap.cli.fw.http.conf.OnapCommandHttpConstants;
67 import org.onap.cli.fw.http.error.OnapCommandHttpFailure;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70 import org.apache.http.client.protocol.HttpClientContext;
71 import org.apache.http.conn.ssl.NoopHostnameVerifier;
72 import org.apache.http.impl.client.CloseableHttpClient;
73 import javax.net.ssl.HostnameVerifier;
74 /**
75  * Helps to make http connection.<br>
76  */
77 public class OnapHttpConnection {
78
79     private static Logger log = LoggerFactory.getLogger(OnapHttpConnection.class);
80
81     private CloseableHttpClient httpClient = null;
82
83     Map<String, String> mapCommonHeaders = new HashMap<> ();
84
85     public static class TrustAllX509TrustManager implements X509TrustManager {
86
87         @Override
88         public X509Certificate[] getAcceptedIssuers() {
89             return new X509Certificate[0];
90         }
91
92         @Override
93         public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) { //NOSONAR
94             // No need to implement.
95         }
96
97         @Override
98         public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) { //NOSONAR
99             // No need to implement.
100         }
101     }
102
103     private void initHttpClient(boolean isSecured) throws OnapCommandHttpFailure {
104         if (this.httpClient == null) {
105             try {
106                 if (isSecured) {
107                     SSLContext sslContext = SSLContext.getInstance(OnapCommandHttpConstants.SSLCONTEST_TLS);
108                     sslContext.init(null, new TrustManager[] { new TrustAllX509TrustManager() },
109                             new java.security.SecureRandom());
110                     HostnameVerifier hostnameVerifier = new NoopHostnameVerifier();
111                     Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
112                             .<ConnectionSocketFactory>create()
113                             .register("https", new SSLConnectionSocketFactory(sslContext, hostnameVerifier)).build();
114                     HttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);  // NOSONAR
115
116                     this.httpClient = HttpClients.custom().setConnectionManager(connManager)
117                             .setRedirectStrategy(new LaxRedirectStrategy()).build();
118                 } else {
119                     this.httpClient = HttpClients.createDefault();  // NOSONAR
120                 }
121             } catch (Exception e) {
122                 throw new OnapCommandHttpFailure(e);
123             }
124         }
125     }
126
127     private Map<String, String> getHttpHeaders(HttpResponse resp) {
128         Map<String, String> result = new HashMap<>();
129
130         Header[] hs = resp.getAllHeaders();
131         for (int i = 0; i < hs.length; i++) {
132             result.put(hs[i].getName(), hs[i].getValue());
133         }
134
135         return result;
136     }
137
138     private String getResponseBody(HttpResponse resp) throws OnapCommandHttpFailure {
139         if (resp.getEntity() == null) {
140             return null;
141         }
142         try {
143             String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
144             EntityUtils.consume(resp.getEntity());
145             return body;
146         } catch (IOException e) {
147             throw new OnapCommandHttpFailure(e);
148         }
149     }
150
151     private StringEntity getStringEntity(HttpInput input) {
152         return new StringEntity(input.getBody(), StandardCharsets.UTF_8);
153     }
154
155     /**
156      * Post. <br>
157      *
158      * @param input
159      *            HttpInput Obj
160      * @return HttpResult
161      * @throws OnapCommandHttpFailure
162      *             http failure
163      */
164     public HttpResult post(final HttpInput input) throws OnapCommandHttpFailure {
165         input.setMethod("post");
166         return this.request(input);
167     }
168
169     /**
170      * Get. <br>
171      *
172      * @param input
173      *            input request
174      * @return HttpResult
175      * @throws OnapCommandHttpFailure
176      *             excpetion
177      */
178     public HttpResult get(final HttpInput input) throws OnapCommandHttpFailure {
179         input.setMethod("get");
180         return this.request(input);
181     }
182
183     /**
184      * Put. <br>
185      *
186      * @param input
187      *            input request
188      * @return HttpResult
189      * @throws OnapCommandHttpFailure
190      *             Exception
191      */
192     public HttpResult put(final HttpInput input) throws OnapCommandHttpFailure {
193         input.setMethod("put");
194         return this.request(input);
195     }
196
197     /**
198      * Delete. <br>
199      *
200      * @param input
201      *            input request
202      * @return HttpResult
203      * @throws OnapCommandHttpFailure
204      *             exception
205      */
206     public HttpResult delete(final HttpInput input) throws OnapCommandHttpFailure {
207         input.setMethod("delete");
208         return this.request(input);
209     }
210
211     public void setCommonHeaders(Map<String, String> headers) {
212         this.mapCommonHeaders = headers;
213     }
214
215     private void addCommonHeaders(HttpInput input) {
216         if (!input.isBinaryData() && !input.getReqHeaders().containsKey("Content-Type")) {
217             input.getReqHeaders().put("Content-Type", OnapCommandHttpConstants.APPLICATION_JSON);
218         }
219
220         if (!input.getReqHeaders().containsKey("Accept")) {
221             input.getReqHeaders().put("Accept", OnapCommandHttpConstants.APPLICATION_JSON);
222         }
223
224         for (Entry<String, String> header : this.mapCommonHeaders.entrySet()) {
225             if (!input.getReqHeaders().containsKey(header.getKey()))
226                 input.getReqHeaders().put(header.getKey(), header.getValue());
227         }
228     }
229
230     private void addCommonCookies(HttpInput input, CookieStore cookieStore) {
231          for (Entry<String, String> header : this.mapCommonHeaders.entrySet()) {
232              //take care of overriden headers in OCS YAML
233                 String value = input.getReqHeaders().getOrDefault(header.getKey(),
234                          header.getValue());
235              Cookie cookie = new BasicClientCookie(header.getKey(), value);
236              cookieStore.addCookie(cookie);
237          }
238     }
239
240     private void updateResultFromCookies(HttpResult result, List<Cookie> cookies) {
241         for (Cookie cookie : cookies) {
242             result.getRespCookies().put(cookie.getName(), cookie.getValue());
243         }
244     }
245
246     private String getDomain(String url) {
247         try {
248             return new URL(url).getHost();
249         } catch (MalformedURLException e) {
250             // url is always proper !!
251             return url;
252         }
253     }
254
255     private void updateInputFromCookies(HttpInput input, CookieStore cookieStore) {
256         addCommonCookies(input, cookieStore);
257         for (String cookieName : input.getReqCookies().keySet()) {
258             BasicClientCookie cookie = new BasicClientCookie(cookieName, input.getReqCookies().get(cookieName));
259             cookie.setDomain(this.getDomain(input.getUri()));
260             cookieStore.addCookie(cookie);
261         }
262
263     }
264
265     /**
266      * Handles http method requests.
267      *
268      * @param input
269      *            HttpInput
270      * @return HttpResult
271      * @throws OnapCommandHttpFailure
272      *             exception
273      */
274     public HttpResult request(HttpInput input) throws OnapCommandHttpFailure {
275         this.addCommonHeaders(input);
276
277         HttpRequestBase requestBase = null;
278         if ("post".equals(input.getMethod())) {
279             HttpPost httpPost = new HttpPost();
280             if (input.isBinaryData() || !(input.getMultiparts().isEmpty())) {
281                 httpPost.setEntity(getMultipartEntity(input));
282             } else {
283                 httpPost.setEntity(this.getStringEntity(input));
284             }
285             requestBase = httpPost;
286         } else if ("put".equals(input.getMethod())) {
287             HttpPut httpPut = new HttpPut();
288             httpPut.setEntity(this.getStringEntity(input));
289             requestBase = httpPut;
290         } else if ("patch".equals(input.getMethod())) {
291             HttpPatch httpPatch = new HttpPatch();
292             httpPatch.setEntity(this.getStringEntity(input));
293             requestBase = httpPatch;
294         } else if ("get".equals(input.getMethod())) {
295             requestBase = new HttpGet();
296         } else if ("delete".equals(input.getMethod())) {
297             if (!input.getBody().isEmpty()) {
298                 HttpDeleteWithBody httpDelete = new HttpDeleteWithBody();
299                 httpDelete.setEntity(new StringEntity(input.getBody(), ContentType.APPLICATION_JSON));
300                 requestBase = httpDelete;
301             } else {
302                 requestBase = new HttpDelete();
303             }
304         } else {
305             throw new IllegalArgumentException("Invalid HTTP method");
306         }
307
308         requestBase.setURI(URI.create(input.getUri()));
309         requestBase.setConfig(RequestConfig.custom()
310                 .setSocketTimeout(600000).setConnectTimeout(600000).build());
311
312         for (Entry<String, String> h : input.getReqHeaders().entrySet()) {
313             requestBase.addHeader(h.getKey(), h.getValue());
314         }
315
316         HttpResult result = new HttpResult();
317
318         try {
319             CookieStore cookieStore = new BasicCookieStore();
320             updateInputFromCookies(input, cookieStore);
321             HttpContext localContext = new BasicHttpContext();
322             localContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
323
324             this.initHttpClient(input.getUri().startsWith("https"));
325
326             HttpResponse resp = this.httpClient.execute(requestBase, localContext);
327             String respContent = this.getResponseBody(resp);
328             result.setBody(respContent);
329             result.setStatus(resp.getStatusLine().getStatusCode());
330             result.setRespHeaders(this.getHttpHeaders(resp));
331             this.updateResultFromCookies(result, cookieStore.getCookies());
332         } catch (Exception e) {  // NOSONAR
333             throw new OnapCommandHttpFailure(e);
334         }
335
336         return result;
337     }
338
339     public void close() throws IOException {
340         this.mapCommonHeaders.clear();
341         if (this.httpClient != null) {
342             this.httpClient.close();
343         }
344     }
345
346     private HttpEntity getMultipartEntity(HttpInput input) {
347         if (!input.getMultiparts().isEmpty()) {
348             MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
349             for (HttpInput.Part part: input.getMultiparts()) {
350                 if (part.isBinary()) {
351                      File file = new File(part.getContent());
352                      entityBuilder.addBinaryBody(
353                             part.getName(),
354                             file,
355                             ContentType.APPLICATION_OCTET_STREAM,
356                             file.getName());
357                 } else {
358                     entityBuilder.addTextBody(part.getName(), part.getContent(), ContentType.APPLICATION_JSON);
359                 }
360             }
361
362             return entityBuilder.build();
363         } else {
364             String fileTag = (!input.getMultipartEntityName().isEmpty()) ? input.getMultipartEntityName() : "file";
365             File file = new File(input.getBody().trim());
366             return MultipartEntityBuilder
367                     .create()
368                     .addBinaryBody(fileTag, file, ContentType.create("application/octet-stream"), file.getName())
369                     .build();
370         }
371     }
372
373      @Contract(threading = ThreadingBehavior.UNSAFE)
374     static class HttpDeleteWithBody extends HttpEntityEnclosingRequestBase {
375
376         public HttpDeleteWithBody() {
377             super();
378         }
379
380         public String getMethod() {
381             return OnapCommandHttpConstants.DELETE.toUpperCase();
382         }
383
384     }
385 }