/*- * ============LICENSE_START======================================================= * VID * ================================================================================ * Copyright (C) 2018 - 2019 Nokia. All rights reserved. * Modifications Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= */ package org.onap.vid.client; import static org.onap.vid.client.UnirestPatchKt.patched; import static org.onap.vid.utils.KotlinUtilsKt.JOSHWORKS_JACKSON_OBJECT_MAPPER; import com.att.eelf.configuration.EELFLogger; import io.joshworks.restclient.http.HttpResponse; import io.joshworks.restclient.http.JsonNode; import io.joshworks.restclient.http.RestClient; import io.joshworks.restclient.http.exceptions.RestClientException; import io.joshworks.restclient.http.mapper.ObjectMapper; import io.joshworks.restclient.request.GetRequest; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.util.Map; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import org.apache.commons.lang3.ObjectUtils; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLContexts; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.eclipse.jetty.util.security.Password; import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; import org.onap.portalsdk.core.util.SystemProperties; import org.onap.vid.properties.VidProperties; import org.onap.vid.utils.Logging; import org.springframework.http.HttpMethod; public class SyncRestClient implements SyncRestClientInterface { private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SyncRestClient.class); private static final String[] SUPPORTED_SSL_VERSIONS = {"TLSv1", "TLSv1.2"}; private static final String HTTPS_SCHEMA = "https://"; private static final String HTTP_SCHEMA = "http://"; private final Logging loggingService; private final EELFLogger outgoingRequestsLogger; private RestClient restClient; public SyncRestClient(Logging loggingService) { this(null, null, loggingService); } public SyncRestClient(ObjectMapper objectMapper, Logging loggingService) { this(null, objectMapper, loggingService); } public SyncRestClient(CloseableHttpClient httpClient, Logging loggingService) { this(httpClient, null, loggingService); } public SyncRestClient(CloseableHttpClient httpClient, ObjectMapper objectMapper, Logging loggingService) { restClient = RestClient .newClient() .objectMapper(ObjectUtils.defaultIfNull(objectMapper, defaultObjectMapper())) .httpClient(ObjectUtils.defaultIfNull(httpClient , defaultHttpClient())) .build(); this.loggingService = loggingService; this.outgoingRequestsLogger = Logging.getRequestsLogger("syncRestClient"); } @Override public HttpResponse post(String url, Map headers, Object body) { loggingService.logRequest(outgoingRequestsLogger, HttpMethod.POST, url, body); HttpResponse response = callWithRetryOverHttp(url, url2 -> restClient.post(url2).headers(headers).body(body).asJson()); loggingService.logResponse(outgoingRequestsLogger, HttpMethod.POST, url, response); return response; } @Override public HttpResponse post(String url, Map headers, Object body, Class responseClass) { loggingService.logRequest(outgoingRequestsLogger, HttpMethod.POST, url, body); HttpResponse response = callWithRetryOverHttp(url, url2 -> restClient.post(url2).headers(headers).body(body).asObject(responseClass)); loggingService.logResponse(outgoingRequestsLogger, HttpMethod.POST, url, response); return response; } @Override public HttpResponse get(String url, Map headers, Map routeParams) { loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams); HttpResponse response = callWithRetryOverHttp(url, url2 -> { GetRequest getRequest = restClient.get(url2).headers(headers); routeParams.forEach(getRequest::routeParam); return getRequest.asJson(); }); loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, response); return response; } @Override public HttpResponse get(String url, Map headers, Map routeParams, Class responseClass) { loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams); HttpResponse response = callWithRetryOverHttp(url, url2 -> { GetRequest getRequest = restClient.get(url2).headers(headers); routeParams.forEach(getRequest::routeParam); return getRequest.asObject(responseClass); }); loggingService.logResponse(outgoingRequestsLogger, HttpMethod.GET, url, response); return response; } @Override public HttpResponse getStream(String url, Map headers, Map routeParams) { loggingService.logRequest(outgoingRequestsLogger, HttpMethod.GET, url, routeParams); HttpResponse response = callWithRetryOverHttp(url, url2 -> { GetRequest getRequest = restClient.get(url2).headers(headers); routeParams.forEach(getRequest::routeParam); return getRequest.asBinary(); }); //no logging of the response since the response is too long return response; } @Override public HttpResponse put(String url, Map headers, Object body) { loggingService.logRequest(outgoingRequestsLogger, HttpMethod.PUT, url, body); HttpResponse response = callWithRetryOverHttp(url, url2 -> restClient.put(url2).headers(headers).body(body).asJson()); loggingService.logResponse(outgoingRequestsLogger, HttpMethod.PUT, url, response); return response; } @Override public HttpResponse put(String url, Map headers, Object body, Class responseClass) { loggingService.logRequest(outgoingRequestsLogger, HttpMethod.PUT, url, body); HttpResponse response = callWithRetryOverHttp(url, url2 -> restClient.put(url2).headers(headers).body(body).asObject(responseClass)); loggingService.logResponse(outgoingRequestsLogger, HttpMethod.PUT, url, response); return response; } @Override public HttpResponse delete(String url, Map headers, Object body, Class responseClass) { loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url, body); HttpResponse response = callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).body(body).asObject(responseClass)); loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response); return response; } @Override public HttpResponse delete(String url, Map headers, Class responseClass) { loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url); HttpResponse response = callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asObject(responseClass)); loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response); return response; } @Override public HttpResponse delete(String url, Map headers) { loggingService.logRequest(outgoingRequestsLogger, HttpMethod.DELETE, url); HttpResponse response = callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asJson()); loggingService.logResponse(outgoingRequestsLogger, HttpMethod.DELETE, url, response); return response; } @Override public void destroy() { restClient.shutdown(); } private HttpResponse callWithRetryOverHttp(String url, HttpRequest httpRequest) { try { return callWithRetryOverHttpThrows(url, httpRequest); } catch (IOException e) { throw new SyncRestClientException("IOException while calling rest service", e); } } private HttpResponse callWithRetryOverHttpThrows(String url, HttpRequest httpRequest) throws IOException { try { return patched(httpRequest.apply(url)); } catch (RestClientException e) { if (causedBySslHandshakeError(e)) { logger.warn("SSL Handshake problem occured. Will try to retry over Http.", e); return patched(httpRequest.apply(url.replaceFirst(HTTPS_SCHEMA, HTTP_SCHEMA))); } throw e; } } private boolean causedBySslHandshakeError(RestClientException exception) { return exception.getCause() instanceof SSLException; } private ObjectMapper defaultObjectMapper() { return JOSHWORKS_JACKSON_OBJECT_MAPPER; } private CloseableHttpClient defaultHttpClient() { try { String trustStorePath = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_FILENAME); String trustStorePass = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_PASSWD_X); String decryptedTrustStorePass = Password.deobfuscate(trustStorePass); KeyStore trustStore = loadTruststore(trustStorePath, decryptedTrustStorePass); SSLContext sslContext = trustOwnCACerts(decryptedTrustStorePass, trustStore); SSLConnectionSocketFactory sslSf = allowTLSProtocols(sslContext); return HttpClients.custom().setSSLSocketFactory(sslSf).build(); } catch (IOException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) { logger.warn("Cannot initialize custom http client from current configuration. Using default one.", e); return HttpClients.createDefault(); } } private SSLConnectionSocketFactory allowTLSProtocols(SSLContext sslcontext) { return new SSLConnectionSocketFactory( sslcontext, SUPPORTED_SSL_VERSIONS, null, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); } private SSLContext trustOwnCACerts(String trustStorePass, KeyStore trustStore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { return SSLContexts.custom() .useTLS() .loadKeyMaterial(trustStore, trustStorePass.toCharArray()) .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()) .build(); } private KeyStore loadTruststore(String trustStorePath, String trustStorePass) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); try (FileInputStream instream = new FileInputStream(new File(trustStorePath))) { trustStore.load(instream, trustStorePass.toCharArray()); } return trustStore; } @FunctionalInterface private interface HttpRequest { HttpResponse apply(String url) throws IOException; } }