2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2018 - 2019 Nokia. All rights reserved.
6 * Modifications Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
7 * ================================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END=========================================================
22 package org.onap.vid.client;
24 import static org.onap.vid.client.UnirestPatchKt.patched;
25 import static org.onap.vid.utils.KotlinUtilsKt.JOSHWORKS_JACKSON_OBJECT_MAPPER;
27 import com.att.eelf.configuration.EELFLogger;
28 import io.joshworks.restclient.http.HttpResponse;
29 import io.joshworks.restclient.http.JsonNode;
30 import io.joshworks.restclient.http.RestClient;
31 import io.joshworks.restclient.http.exceptions.RestClientException;
32 import io.joshworks.restclient.http.mapper.ObjectMapper;
33 import io.joshworks.restclient.request.GetRequest;
35 import java.io.FileInputStream;
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.security.KeyManagementException;
39 import java.security.KeyStore;
40 import java.security.KeyStoreException;
41 import java.security.NoSuchAlgorithmException;
42 import java.security.UnrecoverableKeyException;
43 import java.security.cert.CertificateException;
45 import javax.net.ssl.SSLContext;
46 import javax.net.ssl.SSLException;
47 import org.apache.commons.lang3.ObjectUtils;
48 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
49 import org.apache.http.conn.ssl.SSLContexts;
50 import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
51 import org.apache.http.impl.client.CloseableHttpClient;
52 import org.apache.http.impl.client.HttpClientBuilder;
53 import org.apache.http.impl.client.HttpClients;
54 import org.eclipse.jetty.util.security.Password;
55 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
56 import org.onap.portalsdk.core.util.SystemProperties;
57 import org.onap.vid.logging.ApacheClientMetricRequestInterceptor;
58 import org.onap.vid.logging.ApacheClientMetricResponseInterceptor;
59 import org.onap.vid.properties.VidProperties;
60 import org.onap.vid.utils.Logging;
61 import org.springframework.http.HttpMethod;
63 public class SyncRestClient implements SyncRestClientInterface {
64 private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SyncRestClient.class);
65 private static final String[] SUPPORTED_SSL_VERSIONS = {"TLSv1", "TLSv1.2"};
66 private static final String HTTPS_SCHEMA = "https://";
67 private static final String HTTP_SCHEMA = "http://";
68 private final Logging loggingService;
69 private final EELFLogger outgoingRequestsLogger;
71 private RestClient restClient;
73 public SyncRestClient(Logging loggingService) {
74 this(null, null, loggingService, false);
77 public SyncRestClient(Logging loggingService, boolean useLoggingInterceptor) {
78 this(null, null, loggingService, useLoggingInterceptor);
81 public SyncRestClient(ObjectMapper objectMapper, Logging loggingService) {
82 this(null, objectMapper, loggingService, true);
85 public SyncRestClient(CloseableHttpClient httpClient, Logging loggingService) {
86 this(httpClient, null, loggingService, false);
89 public SyncRestClient(CloseableHttpClient httpClient,
90 ObjectMapper objectMapper,
91 Logging loggingService,
92 boolean useLoggingInterceptor) {
93 restClient = RestClient
95 .objectMapper(ObjectUtils.defaultIfNull(objectMapper, defaultObjectMapper()))
96 .httpClient(ObjectUtils.defaultIfNull(httpClient , defaultHttpClient(useLoggingInterceptor)))
98 this.loggingService = loggingService;
99 this.outgoingRequestsLogger = Logging.getRequestsLogger("syncRestClient");
103 public HttpResponse<JsonNode> post(String url, Map<String, String> headers, Object body) {
104 loggingService.logRequest(outgoingRequestsLogger, HttpMethod.POST, url, body);
106 HttpResponse<JsonNode> response = callWithRetryOverHttp(url,
107 url2 -> restClient.post(url2).headers(headers).body(body).asJson());
109 loggingService.logResponse(outgoingRequestsLogger, HttpMethod.POST, url, response);
115 public <T> HttpResponse<T> post(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
116 loggingService.logRequest(outgoingRequestsLogger, HttpMethod.POST, url, body);
118 HttpResponse<T> response = callWithRetryOverHttp(url,
119 url2 -> restClient.post(url2).headers(headers).body(body).asObject(responseClass));
121 loggingService.logResponse(outgoingRequestsLogger, HttpMethod.POST, url, response);
127 public HttpResponse<JsonNode> get(String url, Map<String, String> headers, Map<String, String> routeParams) {
128 loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams);
130 HttpResponse<JsonNode> response = callWithRetryOverHttp(url, url2 -> {
131 GetRequest getRequest = restClient.get(url2).headers(headers);
132 routeParams.forEach(getRequest::routeParam);
133 return getRequest.asJson();
136 loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, response);
142 public <T> HttpResponse<T> get(String url, Map<String, String> headers, Map<String, String> routeParams,
143 Class<T> responseClass) {
144 loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams);
146 HttpResponse<T> response = callWithRetryOverHttp(url, url2 -> {
147 GetRequest getRequest = restClient.get(url2).headers(headers);
148 routeParams.forEach(getRequest::routeParam);
149 return getRequest.asObject(responseClass);
152 loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, response);
158 public HttpResponse<InputStream> getStream(String url, Map<String, String> headers,
159 Map<String, String> routeParams) {
160 loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams);
162 HttpResponse<InputStream> response = callWithRetryOverHttp(url, url2 -> {
163 GetRequest getRequest = restClient.get(url2).headers(headers);
164 routeParams.forEach(getRequest::routeParam);
165 return getRequest.asBinary();
168 //no logging of the response since the response is too long
174 public HttpResponse<JsonNode> put(String url, Map<String, String> headers, Object body) {
175 loggingService.logRequest(outgoingRequestsLogger, HttpMethod.PUT, url, body);
177 HttpResponse<JsonNode> response = callWithRetryOverHttp(url,
178 url2 -> restClient.put(url2).headers(headers).body(body).asJson());
180 loggingService.logResponse(outgoingRequestsLogger, HttpMethod.PUT, url, response);
186 public <T> HttpResponse<T> put(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
187 loggingService.logRequest(outgoingRequestsLogger, HttpMethod.PUT, url, body);
189 HttpResponse<T> response = callWithRetryOverHttp(url,
190 url2 -> restClient.put(url2).headers(headers).body(body).asObject(responseClass));
192 loggingService.logResponse(outgoingRequestsLogger, HttpMethod.PUT, url, response);
198 public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
199 loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url, body);
201 HttpResponse<T> response = callWithRetryOverHttp(url,
202 url2 -> restClient.delete(url2).headers(headers).body(body).asObject(responseClass));
204 loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response);
210 public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Class<T> responseClass) {
211 loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url);
213 HttpResponse<T> response = callWithRetryOverHttp(url,
214 url2 -> restClient.delete(url2).headers(headers).asObject(responseClass));
216 loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response);
222 public HttpResponse<JsonNode> delete(String url, Map<String, String> headers) {
223 loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url);
225 HttpResponse<JsonNode> response = callWithRetryOverHttp(url,
226 url2 -> restClient.delete(url2).headers(headers).asJson());
228 loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response);
234 public void destroy() {
235 restClient.shutdown();
238 private <T> HttpResponse<T> callWithRetryOverHttp(String url, HttpRequest<T> httpRequest) {
240 return callWithRetryOverHttpThrows(url, httpRequest);
241 } catch (IOException e) {
242 throw new SyncRestClientException("IOException while calling rest service", e);
246 private <T> HttpResponse<T> callWithRetryOverHttpThrows(String url, HttpRequest<T> httpRequest) throws IOException {
248 return patched(httpRequest.apply(url));
249 } catch (RestClientException e) {
250 if (causedBySslHandshakeError(e)) {
251 logger.warn("SSL Handshake problem occured. Will try to retry over Http.", e);
252 return patched(httpRequest.apply(url.replaceFirst(HTTPS_SCHEMA, HTTP_SCHEMA)));
258 private boolean causedBySslHandshakeError(RestClientException exception) {
259 return exception.getCause() instanceof SSLException;
262 private ObjectMapper defaultObjectMapper() {
263 return JOSHWORKS_JACKSON_OBJECT_MAPPER;
266 private CloseableHttpClient defaultHttpClient(boolean useLoggingInterceptor) {
268 String trustStorePath = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_FILENAME);
269 String trustStorePass = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_PASSWD_X);
270 String decryptedTrustStorePass = Password.deobfuscate(trustStorePass);
272 KeyStore trustStore = loadTruststore(trustStorePath, decryptedTrustStorePass);
273 SSLContext sslContext = trustOwnCACerts(decryptedTrustStorePass, trustStore);
274 SSLConnectionSocketFactory sslSf = allowTLSProtocols(sslContext);
276 HttpClientBuilder httpClientBuilder = HttpClients.custom().setSSLSocketFactory(sslSf);
277 if (useLoggingInterceptor) {
279 .addInterceptorFirst(new ApacheClientMetricRequestInterceptor())
280 .addInterceptorLast(new ApacheClientMetricResponseInterceptor());
282 return httpClientBuilder.build();
283 } catch (IOException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
284 logger.warn("Cannot initialize custom http client from current configuration. Using default one.", e);
285 return HttpClients.createDefault();
289 private SSLConnectionSocketFactory allowTLSProtocols(SSLContext sslcontext) {
290 return new SSLConnectionSocketFactory(
292 SUPPORTED_SSL_VERSIONS,
294 SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
297 private SSLContext trustOwnCACerts(String trustStorePass, KeyStore trustStore)
298 throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
299 return SSLContexts.custom()
301 .loadKeyMaterial(trustStore, trustStorePass.toCharArray())
302 .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
306 private KeyStore loadTruststore(String trustStorePath, String trustStorePass)
307 throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException {
308 KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
309 try (FileInputStream instream = new FileInputStream(new File(trustStorePath))) {
310 trustStore.load(instream, trustStorePass.toCharArray());
316 private interface HttpRequest<T> {
317 HttpResponse<T> apply(String url) throws IOException;