Fix http profile to override the headers
[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.ParseException;
39 import org.apache.http.client.CookieStore;
40 import org.apache.http.client.HttpClient;
41 import org.apache.http.client.methods.HttpDelete;
42 import org.apache.http.client.methods.HttpGet;
43 import org.apache.http.client.methods.HttpPost;
44 import org.apache.http.client.methods.HttpPut;
45 import org.apache.http.client.methods.HttpRequestBase;
46 import org.apache.http.client.protocol.ClientContext;
47 import org.apache.http.config.Registry;
48 import org.apache.http.config.RegistryBuilder;
49 import org.apache.http.conn.HttpClientConnectionManager;
50 import org.apache.http.conn.socket.ConnectionSocketFactory;
51 import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
52 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
53 import org.apache.http.conn.ssl.X509HostnameVerifier;
54 import org.apache.http.cookie.Cookie;
55 import org.apache.http.entity.StringEntity;
56 import org.apache.http.entity.mime.MultipartEntity;
57 import org.apache.http.entity.mime.content.FileBody;
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
69 /**
70  * Helps to make http connection.<br>
71  */
72 public class OnapHttpConnection {
73
74     private HttpClient httpClient = null;
75
76     Map<String, String> mapCommonHeaders = new HashMap<String, String> ();
77
78     protected boolean debug = false;
79
80     private String debugDetails = "";
81
82     public static class TrustAllX509TrustManager implements X509TrustManager {
83
84         @Override
85         public X509Certificate[] getAcceptedIssuers() {
86             return new X509Certificate[0];
87         }
88
89         @Override
90         public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
91             // No need to implement.
92         }
93
94         @Override
95         public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
96             // No need to implement.
97         }
98     }
99
100     /**
101      * OnapHttpConnection Constructor.
102      *
103      * @param debug
104      *            boolean
105      * @throws OnapCommandHttpFailure
106      *             exception
107      */
108     public OnapHttpConnection(boolean debug) throws OnapCommandHttpFailure {
109         this.debug = debug;
110     }
111
112     private void initHttpClient(boolean isSecured) throws OnapCommandHttpFailure {
113         if (this.httpClient == null) {
114             try {
115                 if (isSecured) {
116                     SSLContext sslContext = SSLContext.getInstance(OnapCommandHttpConstants.SSLCONTEST_TLS);
117                     sslContext.init(null, new TrustManager[] { new TrustAllX509TrustManager() },
118                             new java.security.SecureRandom());
119                     X509HostnameVerifier hostnameVerifier = new AllowAllHostnameVerifier();
120                     Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
121                             .<ConnectionSocketFactory>create()
122                             .register("https", new SSLConnectionSocketFactory(sslContext, hostnameVerifier)).build();
123                     HttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
124
125                     this.httpClient = HttpClients.custom().setConnectionManager(connManager)
126                             .setRedirectStrategy(new LaxRedirectStrategy()).build();
127                 } else {
128                     this.httpClient = HttpClients.createDefault();
129                 }
130             } catch (Exception e) {
131                 throw new OnapCommandHttpFailure(e);
132             }
133         }
134     }
135
136     public String getDebugInfo() {
137         return this.debugDetails;
138     }
139
140     private Map<String, String> getHttpHeaders(HttpResponse resp) {
141         Map<String, String> result = new HashMap<>();
142
143         Header[] hs = resp.getAllHeaders();
144         for (int i = 0; i < hs.length; i++) {
145             result.put(hs[i].getName(), hs[i].getValue());
146         }
147
148         return result;
149     }
150
151     private String getResponseBody(HttpResponse resp) throws OnapCommandHttpFailure {
152         if (resp.getEntity() == null) {
153             return null;
154         }
155         try {
156             String body = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);
157             EntityUtils.consume(resp.getEntity());
158             return body;
159         } catch (IOException e) {
160             throw new OnapCommandHttpFailure(e);
161         }
162     }
163
164     private StringEntity getStringEntity(HttpInput input) {
165         return new StringEntity(input.getBody(), StandardCharsets.UTF_8);
166     }
167
168     /**
169      * Post. <br>
170      *
171      * @param input
172      *            HttpInput Obj
173      * @return HttpResult
174      * @throws OnapCommandHttpFailure
175      *             http failure
176      */
177     public HttpResult post(final HttpInput input) throws OnapCommandHttpFailure {
178         input.setMethod("post");
179         return this.request(input);
180     }
181
182     /**
183      * Get. <br>
184      *
185      * @param input
186      *            input request
187      * @return HttpResult
188      * @throws OnapCommandHttpFailure
189      *             excpetion
190      */
191     public HttpResult get(final HttpInput input) throws OnapCommandHttpFailure {
192         input.setMethod("get");
193         return this.request(input);
194     }
195
196     /**
197      * Put. <br>
198      *
199      * @param input
200      *            input request
201      * @return HttpResult
202      * @throws OnapCommandHttpFailure
203      *             Exception
204      */
205     public HttpResult put(final HttpInput input) throws OnapCommandHttpFailure {
206         input.setMethod("put");
207         return this.request(input);
208     }
209
210     /**
211      * Delete. <br>
212      *
213      * @param input
214      *            input request
215      * @return HttpResult
216      * @throws OnapCommandHttpFailure
217      *             exception
218      */
219     public HttpResult delete(final HttpInput input) throws OnapCommandHttpFailure {
220         input.setMethod("delete");
221         return this.request(input);
222     }
223
224     public void setCommonHeaders(Map<String, String> headers) {
225         this.mapCommonHeaders = headers;
226     }
227
228     private void addCommonHeaders(HttpInput input) {
229         if (!input.isBinaryData()) {
230             if (!input.getReqHeaders().containsKey("Content-Type")) {
231                 input.getReqHeaders().put("Content-Type", OnapCommandHttpConstants.APPLICATION_JSON);
232             }
233         }
234         if (!input.getReqHeaders().containsKey("Accept")) {
235             input.getReqHeaders().put("Accept", OnapCommandHttpConstants.APPLICATION_JSON);
236         }
237
238         for (String headerName : this.mapCommonHeaders.keySet()) {
239             input.getReqHeaders().put(headerName, this.mapCommonHeaders.get(headerName));
240         }
241     }
242
243     private void addCommonCookies(CookieStore cookieStore) {
244         for (String headerName : this.mapCommonHeaders.keySet()) {
245             Cookie cookie = new BasicClientCookie(headerName, this.mapCommonHeaders.get(headerName));
246             cookieStore.addCookie(cookie);
247         }
248     }
249
250     private void updateResultFromCookies(HttpResult result, List<Cookie> cookies) {
251         for (Cookie cookie : cookies) {
252             result.getRespCookies().put(cookie.getName(), cookie.getValue());
253         }
254     }
255
256     private String getDomain(String url) {
257         try {
258             return new URL(url).getHost();
259         } catch (MalformedURLException e) {
260             // url is always proper !!
261             return url;
262         }
263     }
264
265     private void updateInputFromCookies(HttpInput input, CookieStore cookieStore) {
266         addCommonCookies(cookieStore);
267         for (String cookieName : input.getReqCookies().keySet()) {
268             BasicClientCookie cookie = new BasicClientCookie(cookieName, input.getReqCookies().get(cookieName));
269             cookie.setDomain(this.getDomain(input.getUri()));
270             cookieStore.addCookie(cookie);
271         }
272
273     }
274
275     /**
276      * Handles http method requests.
277      *
278      * @param input
279      *            HttpInput
280      * @return HttpResult
281      * @throws OnapCommandHttpFailure
282      *             exception
283      */
284     public HttpResult request(HttpInput input) throws OnapCommandHttpFailure {
285         this.addCommonHeaders(input);
286
287         HttpRequestBase requestBase = null;
288         if ("post".equals(input.getMethod())) {
289             HttpPost httpPost = new HttpPost();
290             if (input.isBinaryData()) {
291                 httpPost.setEntity(getMultipartEntity(input));
292             } else {
293             httpPost.setEntity(this.getStringEntity(input));
294             }
295             requestBase = httpPost;
296         } else if ("put".equals(input.getMethod())) {
297             HttpPut httpPut = new HttpPut();
298             httpPut.setEntity(this.getStringEntity(input));
299             requestBase = httpPut;
300         } else if ("get".equals(input.getMethod())) {
301             requestBase = new HttpGet();
302         } else if ("delete".equals(input.getMethod())) {
303             requestBase = new HttpDelete();
304         } else {
305             throw new IllegalArgumentException("Invalid HTTP method");
306         }
307
308         requestBase.setURI(URI.create(input.getUri()));
309
310         for (Entry<String, String> h : input.getReqHeaders().entrySet()) {
311             requestBase.addHeader(h.getKey(), h.getValue());
312         }
313
314         HttpResult result = new HttpResult();
315
316         try {
317             this.debugDetails = "";
318             CookieStore cookieStore = new BasicCookieStore();
319             updateInputFromCookies(input, cookieStore);
320             HttpContext localContext = new BasicHttpContext();
321             localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
322
323             this.initHttpClient(input.getUri().startsWith("https"));
324
325             HttpResponse resp = this.httpClient.execute(requestBase, localContext);
326             String respContent = this.getResponseBody(resp);
327             result.setBody(respContent);
328             result.setStatus(resp.getStatusLine().getStatusCode());
329             result.setRespHeaders(this.getHttpHeaders(resp));
330             this.updateResultFromCookies(result, cookieStore.getCookies());
331         } catch (ParseException | IOException e) {
332             throw new OnapCommandHttpFailure(e);
333         } finally {
334             if (this.debug) {
335                 this.debugDetails = input + " " + result;
336             }
337         }
338
339         return result;
340     }
341
342     public void close() {
343         this.mapCommonHeaders.clear();
344     }
345
346     private HttpEntity getMultipartEntity(HttpInput input) {
347         FileBody fileBody = new FileBody(new File(input.getBody().trim()));
348         MultipartEntity multipartEntity = new MultipartEntity();
349         String fileName = input.getMultipartEntityName() != "" ? input.getMultipartEntityName() : "upload";
350         multipartEntity.addPart(fileName, fileBody);
351         return multipartEntity;
352     }
353 }