View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2014 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.caravan.commons.httpclient.impl;
21  
22  import java.io.IOException;
23  import java.security.GeneralSecurityException;
24  
25  import javax.net.ssl.SSLContext;
26  
27  import org.apache.commons.lang3.StringUtils;
28  import org.apache.http.HttpHost;
29  import org.apache.http.auth.AuthScope;
30  import org.apache.http.auth.UsernamePasswordCredentials;
31  import org.apache.http.client.CredentialsProvider;
32  import org.apache.http.client.config.RequestConfig;
33  import org.apache.http.config.Registry;
34  import org.apache.http.config.RegistryBuilder;
35  import org.apache.http.conn.socket.ConnectionSocketFactory;
36  import org.apache.http.conn.socket.PlainConnectionSocketFactory;
37  import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
38  import org.apache.http.impl.client.BasicCredentialsProvider;
39  import org.apache.http.impl.client.CloseableHttpClient;
40  import org.apache.http.impl.client.HttpClientBuilder;
41  import org.apache.http.impl.client.ProxyAuthenticationStrategy;
42  import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
43  import org.jetbrains.annotations.NotNull;
44  import org.jetbrains.annotations.Nullable;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  
48  import io.wcm.caravan.commons.httpclient.HttpClientConfig;
49  import io.wcm.caravan.commons.httpclient.impl.helpers.CertificateLoader;
50  
51  /**
52   * Item for {@link HttpClientFactoryImpl} for each {@link HttpClientConfig} configured.
53   */
54  class HttpClientItem {
55  
56    private final HttpClientConfig config;
57    private final CloseableHttpClient httpClient;
58    private final RequestConfig defaultRequestConfig;
59  
60    private static final Logger log = LoggerFactory.getLogger(HttpClientItem.class);
61  
62    /**
63     * @param config Http client configuration
64     */
65    HttpClientItem(@NotNull HttpClientConfig config) {
66      this.config = config;
67  
68      // optional SSL client certificate support
69      SSLContext sslContext;
70      if (CertificateLoader.isSslKeyManagerEnabled(config) || CertificateLoader.isSslTrustStoreEnbaled(config)) {
71        try {
72          sslContext = CertificateLoader.buildSSLContext(config);
73        }
74        catch (IOException | GeneralSecurityException ex) {
75          throw new IllegalArgumentException("Invalid SSL client certificate configuration.", ex);
76        }
77      }
78      else {
79        sslContext = CertificateLoader.createDefaultSSlContext();
80      }
81  
82      CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
83      // optional proxy authentication
84      if (StringUtils.isNotEmpty(config.getProxyUser())) {
85        credentialsProvider.setCredentials(new AuthScope(config.getProxyHost(), config.getProxyPort()),
86            new UsernamePasswordCredentials(config.getProxyUser(), config.getProxyPassword()));
87      }
88      // optional http basic authentication support
89      if (StringUtils.isNotEmpty(config.getHttpUser())) {
90        credentialsProvider.setCredentials(AuthScope.ANY,
91            new UsernamePasswordCredentials(config.getHttpUser(), config.getHttpPassword()));
92      }
93  
94      // build request config
95      defaultRequestConfig = buildDefaultRequestConfig(config);
96  
97      // build http clients
98      PoolingHttpClientConnectionManager connectionManager = buildConnectionManager(config, sslContext);
99      httpClient = buildHttpClient(config, connectionManager, credentialsProvider, defaultRequestConfig);
100   }
101 
102   private static @NotNull RequestConfig buildDefaultRequestConfig(@NotNull HttpClientConfig config) {
103     return RequestConfig.custom()
104         // timeout settings
105         .setConnectionRequestTimeout(config.getConnectionRequestTimeout())
106         .setConnectTimeout(config.getConnectTimeout())
107         .setSocketTimeout(config.getSocketTimeout())
108         .setCookieSpec(config.getCookieSpec())
109         .build();
110   }
111 
112   private static @NotNull PoolingHttpClientConnectionManager buildConnectionManager(@NotNull HttpClientConfig config,
113       @NotNull SSLContext sslContext) {
114     // scheme configuration
115     ConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext);
116     Registry<ConnectionSocketFactory> schemeRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
117         .register("http", PlainConnectionSocketFactory.getSocketFactory())
118         .register("https", sslSocketFactory)
119         .build();
120 
121     // pooling settings
122     PoolingHttpClientConnectionManager conmgr = new PoolingHttpClientConnectionManager(schemeRegistry);
123     conmgr.setMaxTotal(config.getMaxTotalConnections());
124     conmgr.setDefaultMaxPerRoute(config.getMaxConnectionsPerHost());
125     return conmgr;
126   }
127 
128   private static @NotNull CloseableHttpClient buildHttpClient(@NotNull HttpClientConfig config,
129       @NotNull PoolingHttpClientConnectionManager connectionManager, @NotNull CredentialsProvider credentialsProvider,
130       @NotNull RequestConfig defaultRequestConfig) {
131 
132     // prepare HTTPClient builder
133     HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
134         .setConnectionManager(connectionManager);
135 
136     httpClientBuilder.setDefaultRequestConfig(defaultRequestConfig);
137 
138     httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
139 
140     // optional proxy support
141     if (StringUtils.isNotEmpty(config.getProxyHost())) {
142       httpClientBuilder.setProxy(new HttpHost(config.getProxyHost(), config.getProxyPort()));
143 
144       // optional proxy authentication
145       if (StringUtils.isNotEmpty(config.getProxyUser())) {
146         httpClientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
147       }
148     }
149 
150     return httpClientBuilder.build();
151   }
152 
153   /**
154    * @return Http client instance (synchronous)
155    */
156   public @NotNull CloseableHttpClient getHttpClient() {
157     return httpClient;
158   }
159 
160   /**
161    * @return Default request config
162    */
163   public @NotNull RequestConfig getDefaultRequestConfig() {
164     return defaultRequestConfig;
165   }
166 
167   /**
168    * @param hostName Host name
169    * @param wsAddressingToURI WS addressing "to" URI
170    * @param path Path part of URI
171    * @param isWsCall indicates if the call is a soap webservice call
172    * @return true if host name is associated with this http client config
173    */
174   public boolean matches(@Nullable String hostName, @Nullable String wsAddressingToURI, @Nullable String path, boolean isWsCall) {
175     if (isWsCall) {
176       return config.isEnabled()
177           && config.matchesHost(hostName)
178           && config.matchesPath(path)
179           && config.matchesWsAddressingToUri(wsAddressingToURI);
180     }
181     else {
182       return config.isEnabled()
183           && config.matchesHost(hostName)
184           && config.matchesPath(path);
185     }
186   }
187 
188   /**
189    * Close underlying http clients.
190    */
191   public void close() {
192     try {
193       httpClient.close();
194     }
195     catch (IOException ex) {
196       log.warn("Error closing HTTP client.", ex);
197     }
198   }
199 
200   @Override
201   public @NotNull String toString() {
202     return "HttpClientItem[" + config.toString() + "]";
203   }
204 
205 }