2 * ================================================================================
3 * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 * ============LICENSE_END=========================================================
20 package org.onap.dcae.analytics.web.http;
23 import java.io.IOException;
25 import java.security.KeyManagementException;
26 import java.security.KeyStoreException;
27 import java.security.NoSuchAlgorithmException;
28 import java.security.cert.CertificateException;
29 import java.security.cert.X509Certificate;
30 import java.util.List;
32 import java.util.Optional;
34 import org.apache.http.HttpHost;
35 import org.apache.http.auth.AuthScope;
36 import org.apache.http.auth.Credentials;
37 import org.apache.http.auth.UsernamePasswordCredentials;
38 import org.apache.http.client.CredentialsProvider;
39 import org.apache.http.conn.ssl.NoopHostnameVerifier;
40 import org.apache.http.conn.ssl.TrustStrategy;
41 import org.apache.http.impl.client.BasicCredentialsProvider;
42 import org.apache.http.impl.client.HttpClientBuilder;
43 import org.apache.http.impl.client.HttpClients;
44 import org.apache.http.impl.conn.DefaultProxyRoutePlanner;
45 import org.apache.http.ssl.SSLContextBuilder;
46 import org.onap.dcae.analytics.model.AnalyticsHttpConstants;
47 import org.onap.dcae.analytics.model.util.function.StringToURLFunction;
48 import org.onap.dcae.analytics.web.util.AnalyticsWebUtils;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51 import org.springframework.boot.web.client.RestTemplateCustomizer;
52 import org.springframework.http.HttpHeaders;
53 import org.springframework.http.HttpRequest;
54 import org.springframework.http.client.BufferingClientHttpRequestFactory;
55 import org.springframework.http.client.ClientHttpRequestExecution;
56 import org.springframework.http.client.ClientHttpRequestInterceptor;
57 import org.springframework.http.client.ClientHttpResponse;
58 import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
59 import org.springframework.http.client.support.BasicAuthorizationInterceptor;
60 import org.springframework.util.ReflectionUtils;
61 import org.springframework.util.StringUtils;
62 import org.springframework.web.client.RestTemplate;
63 import org.springframework.web.util.DefaultUriBuilderFactory;
66 * Creates a {@link RestTemplateCustomizer} which can be used to configure the spring rest templates
67 * based on given {@link HttpClientPreferences}
69 * @param <T> Http Client Configurations
71 * @author Rajiv Singla
73 public class HttpClientPreferencesCustomizer<T extends HttpClientPreferences> implements RestTemplateCustomizer {
75 private static final Logger logger = LoggerFactory.getLogger(HttpClientPreferencesCustomizer.class);
77 private final T httpClientConfig;
79 public HttpClientPreferencesCustomizer(final T httpClientConfig) {
80 this.httpClientConfig = httpClientConfig;
84 public void customize(final RestTemplate restTemplate) {
86 final String httpClientId = httpClientConfig.getHttpClientId() != null ? httpClientConfig.getHttpClientId()
87 : AnalyticsHttpConstants.DEFAULT_HTTP_CLIENT_ID_PREFIX + AnalyticsWebUtils.RANDOM_ID_SUPPLIER.get();
88 logger.debug("Customizing Rest Template for Http Client Id: {}", httpClientId);
91 final URL requestURL = new StringToURLFunction().apply(httpClientConfig.getRequestURL())
92 .orElseThrow(() -> new IllegalArgumentException("Http Client URL is required"));
93 restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(requestURL.toString()));
95 // add basic authentication headers
96 final String username = httpClientConfig.getUsername();
97 if (username != null) {
98 restTemplate.getInterceptors().add(
99 new BasicAuthorizationInterceptor(username, httpClientConfig.getPassword()));
102 // set default request headers
103 final HttpHeaders defaultRequestHeaders = httpClientConfig.getRequestHeaders();
104 if (defaultRequestHeaders != null) {
105 restTemplate.getInterceptors().add(new DefaultHeadersRequestInterceptor(defaultRequestHeaders));
108 // create new http client builder
109 final HttpClientBuilder httpClientBuilder = HttpClients.custom().useSystemProperties()
110 .disableContentCompression();
111 final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
113 // set basic authentication credentials
114 configureAuthenticationCredentials(httpClientId, requestURL, credentialsProvider);
116 configureProxySettings(httpClientId, httpClientBuilder, credentialsProvider);
117 // setup credentials provider
118 httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
119 // set up ssl Context
120 configureSSLContext(httpClientId, httpClientBuilder);
122 // set rest client builder
123 final HttpComponentsClientHttpRequestFactory httpRequestFactory =
124 new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
126 // set ecomp logging interceptor
127 if (httpClientConfig.getEnableEcompAuditLogging()) {
128 restTemplate.getInterceptors().add(new EelfAuditLogInterceptor(httpClientConfig));
131 restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(httpRequestFactory));
135 * Configures authentication credentials
137 * @param httpClientId http client id
138 * @param requestURL request url
139 * @param credentialsProvider credentials provider
141 private void configureAuthenticationCredentials(final String httpClientId, final URL requestURL,
142 final CredentialsProvider credentialsProvider) {
143 final String username = httpClientConfig.getUsername();
144 if (username != null) {
145 logger.info("Setting basic Authentication credentials for Http Client Id: {} with username: {}",
146 httpClientId, username);
147 final String requestURLProtocol = requestURL.getProtocol();
148 final String requestUrlHost = requestURL.getHost();
149 final Integer requestUrlPortNumber = requestURL.getPort();
150 final HttpHost requestURLHost = new HttpHost(requestUrlHost, requestUrlPortNumber, requestURLProtocol);
151 final String password = httpClientConfig.getPassword();
152 final AuthScope httpClientAuthScope = new AuthScope(requestURLHost);
153 final Credentials credentials = new UsernamePasswordCredentials(username, password);
154 credentialsProvider.setCredentials(httpClientAuthScope, credentials);
156 logger.warn("No credentials set for Http Client Id: {}. No username present", httpClientId);
161 * Configures proxy host, port and authentication
163 * @param httpClientId http client id
164 * @param httpClientBuilder http client builder
165 * @param credentialsProvider http credentials provider
167 private void configureProxySettings(final String httpClientId, final HttpClientBuilder httpClientBuilder,
168 final CredentialsProvider credentialsProvider) {
170 final URL proxyURL = httpClientConfig.getProxyURL();
172 if (proxyURL == null) {
173 logger.debug("Proxy not Enabled - bypassing setting Proxy settings for Http Client Id: {}", httpClientId);
177 final String proxyProtocol = proxyURL.getProtocol();
178 final String proxyHost = proxyURL.getHost();
179 final Integer proxyPort = proxyURL.getPort();
180 final HttpHost proxy = new HttpHost(proxyHost, proxyPort, proxyProtocol);
182 logger.info("Setting up proxy for Http Client Id: {} as: {}", httpClientId, proxy);
184 final DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);
185 httpClientBuilder.setRoutePlanner(routePlanner);
187 // get proxy credentials information
188 final String userInfo = proxyURL.getUserInfo();
190 if (!StringUtils.hasText(userInfo)) {
191 logger.debug("Proxy username not present. " +
192 "No proxy authentication credentials will be set for Http Client Id: {}", httpClientId);
196 final String[] userInfoArray = userInfo.split(":");
197 final String proxyUsername = userInfoArray[0];
198 String proxyPassword = null;
199 if (userInfoArray.length > 1) {
200 proxyPassword = userInfoArray[1];
202 logger.info("Setting proxy credentials with username: {} for Http Client Id: {}", proxyUsername, httpClientId);
203 final AuthScope proxyAuthScope = new AuthScope(proxyHost, proxyPort);
204 final Credentials proxyCredentials = new UsernamePasswordCredentials(proxyUsername, proxyPassword);
205 credentialsProvider.setCredentials(proxyAuthScope, proxyCredentials);
209 * Configures SSL Context
211 * @param httpClientId http client id
212 * @param httpClientBuilder http client builder
214 private void configureSSLContext(final String httpClientId, final HttpClientBuilder httpClientBuilder) {
216 // Setup SSL Context to ignore SSL certificate issues if ignoreSSLCertificateErrors is true
217 final boolean ignoreSSLValidation =
218 Optional.ofNullable(httpClientConfig.getIgnoreSSLValidation()).orElse(false);
219 logger.info("Ignore SSL Certificate Errors attributed is set to: {} for Http Client Id: {}",
220 ignoreSSLValidation, httpClientId);
222 if (!ignoreSSLValidation) {
223 logger.info("SSL Validation will be enforced for Http Client Id: {}", httpClientId);
227 logger.warn("SSL Certificate Errors will be ignored for Http Client Id: {}", httpClientId);
229 SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
230 sslContextBuilder.loadTrustMaterial(null, new AlwaysTrustingTrustStrategy());
231 httpClientBuilder.setSSLContext(sslContextBuilder.build());
232 } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
233 ReflectionUtils.rethrowRuntimeException(e);
235 httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
241 * Header Request Interceptor adds defaults headers if not set explicitly
243 private static class DefaultHeadersRequestInterceptor implements ClientHttpRequestInterceptor {
244 private final HttpHeaders httpHeaders;
246 DefaultHeadersRequestInterceptor(final HttpHeaders httpHeaders) {
247 this.httpHeaders = httpHeaders;
251 public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
252 final ClientHttpRequestExecution execution) throws IOException {
253 final HttpHeaders currentRequestHeaders = request.getHeaders();
254 for (Map.Entry<String, List<String>> defaultHttpHeader : httpHeaders.entrySet()) {
255 if (!currentRequestHeaders.containsKey(defaultHttpHeader.getKey())) {
256 currentRequestHeaders.addAll(defaultHttpHeader.getKey(), defaultHttpHeader.getValue());
259 currentRequestHeaders.setAccept(httpHeaders.getAccept());
260 currentRequestHeaders.setAcceptCharset(httpHeaders.getAcceptCharset());
261 currentRequestHeaders.remove(HttpHeaders.ACCEPT_ENCODING);
262 return execution.execute(request, body);
267 * An implementation of SSL Trust Strategy which does no SSL certificate validation effectively
268 * bypassing any SSL certificate related issues
270 private static class AlwaysTrustingTrustStrategy implements TrustStrategy {
272 public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {