blob: 62d673cb57f6778bc8fb08664691d437550f6ebf [file] [log] [blame]
/*-
* ============LICENSE_START=======================================================
* OSAM
* ================================================================================
* Copyright (C) 2018 AT&T
* ================================================================================
* 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.osam.client;
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 org.apache.http.impl.client.CloseableHttpClient;
import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.eclipse.jetty.util.security.Password;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.conn.ssl.SSLContexts;
import io.vavr.CheckedFunction1;
import lombok.SneakyThrows;
import lombok.val;
import java.security.UnrecoverableKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.KeyManagementException;
import java.security.cert.CertificateException;
import javax.net.ssl.SSLException;
import java.security.KeyStoreException;
import java.text.SimpleDateFormat;
import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.text.DateFormat;
import java.io.InputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.io.File;
import org.onap.portalsdk.core.util.SystemProperties;
import org.onap.osam.properties.VidProperties;
public class SyncRestClient implements SyncRestClientInterface {
private static final String CANNOT_INITIALIZE_CUSTOM_HTTP_CLIENT = "Cannot initialize custom http client from current configuration. Using default one.";
private static final String TRY_TO_CALL_OVER_HTTP = "SSL Handshake problem occured. Will try to retry over Http.";
private static final EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(SyncRestClient.class);
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("HH:mm:ss:SSSS");
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 RestClient restClient;
public SyncRestClient() {
restClient = RestClient.newClient().objectMapper(defaultObjectMapper()).httpClient(defaultHttpClient()).build();
}
public SyncRestClient(ObjectMapper objectMapper) {
restClient = RestClient.newClient().objectMapper(objectMapper).httpClient(defaultHttpClient()).build();
}
public SyncRestClient(CloseableHttpClient httpClient) {
restClient = RestClient.newClient().objectMapper(defaultObjectMapper()).httpClient(httpClient).build();
}
public SyncRestClient(CloseableHttpClient httpClient, ObjectMapper objectMapper) {
restClient = RestClient.newClient().objectMapper(objectMapper).httpClient(httpClient).build();
}
@Override
public HttpResponse<JsonNode> post(String url, Map<String, String> headers, Object body) {
return callWithRetryOverHttp(url, url2 -> restClient.post(url2).headers(headers).body(body).asJson());
}
@Override
public <T> HttpResponse<T> post(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
return callWithRetryOverHttp(url,
url2 -> restClient.post(url2).headers(headers).body(body).asObject(responseClass));
}
@Override
public HttpResponse<JsonNode> get(String url, Map<String, String> headers, Map<String, String> routeParams) {
return callWithRetryOverHttp(url, url2 -> {
val getRequest = restClient.get(url2).headers(headers);
routeParams.forEach(getRequest::routeParam);
return getRequest.asJson();
});
}
@Override
public <T> HttpResponse<T> get(String url, Map<String, String> headers, Map<String, String> routeParams,
Class<T> responseClass) {
return callWithRetryOverHttp(url, url2 -> {
val getRequest = restClient.get(url2).headers(headers);
routeParams.forEach(getRequest::routeParam);
return getRequest.asObject(responseClass);
});
}
@Override
public HttpResponse<InputStream> getStream(String url, Map<String, String> headers,
Map<String, String> routeParams) {
return callWithRetryOverHttp(url, url2 -> {
val getRequest = restClient.get(url2).headers(headers);
routeParams.forEach(getRequest::routeParam);
return getRequest.asBinary();
});
}
@Override
public HttpResponse<JsonNode> put(String url, Map<String, String> headers, Object body) {
return callWithRetryOverHttp(url, url2 -> restClient.put(url2).headers(headers).body(body).asJson());
}
@Override
public <T> HttpResponse<T> put(String url, Map<String, String> headers, Object body, Class<T> responseClass) {
return callWithRetryOverHttp(url,
url2 -> restClient.put(url2).headers(headers).body(body).asObject(responseClass));
}
@Override
public <T> HttpResponse<T> delete(String url, Map<String, String> headers, Class<T> responseClass) {
return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asObject(responseClass));
}
@Override
public HttpResponse<JsonNode> delete(String url, Map<String, String> headers) {
return callWithRetryOverHttp(url, url2 -> restClient.delete(url2).headers(headers).asJson());
}
@Override
public void destroy() {
restClient.shutdown();
}
@SneakyThrows
private <T> HttpResponse<T> callWithRetryOverHttp(String url,
CheckedFunction1<String, HttpResponse<T>> httpRequest) {
try {
return httpRequest.apply(url);
} catch (RestClientException e) {
if (e.getCause() instanceof SSLException) {
logger.warn(EELFLoggerDelegate.debugLogger,
DATE_FORMAT.format(new Date()) + TRY_TO_CALL_OVER_HTTP, e);
return httpRequest.apply(url.replaceFirst(HTTPS_SCHEMA, HTTP_SCHEMA));
}
throw e;
}
}
private ObjectMapper defaultObjectMapper() {
val objectMapper = new com.fasterxml.jackson.databind.ObjectMapper();
return new ObjectMapper() {
@Override
@SneakyThrows
public <T> T readValue(String value, Class<T> aClass) {
return objectMapper.readValue(value, aClass);
}
@Override
@SneakyThrows
public String writeValue(Object value) {
return objectMapper.writeValueAsString(value);
}
};
}
private CloseableHttpClient defaultHttpClient() {
try {
val trustStorePath = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_FILENAME);
val trustStorePass = SystemProperties.getProperty(VidProperties.VID_TRUSTSTORE_PASSWD_X);
val decryptedTrustStorePass = Password.deobfuscate(trustStorePass);
val trustStore = loadTruststore(trustStorePath, decryptedTrustStorePass);
val sslContext = trustOwnCACerts(decryptedTrustStorePass, trustStore);
val sslSf = allowTLSProtocols(sslContext);
return HttpClients.custom().setSSLSocketFactory(sslSf).build();
} catch (IOException | CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
logger.warn(EELFLoggerDelegate.debugLogger,
DATE_FORMAT.format(new Date()) + CANNOT_INITIALIZE_CUSTOM_HTTP_CLIENT, 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 {
val trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream instream = new FileInputStream(new File(trustStorePath))) {
trustStore.load(instream, trustStorePass.toCharArray());
}
return trustStore;
}
}