1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.wcm.caravan.commons.httpasyncclient.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.impl.client.BasicCredentialsProvider;
36 import org.apache.http.impl.client.ProxyAuthenticationStrategy;
37 import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
38 import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
39 import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
40 import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
41 import org.apache.http.impl.nio.reactor.IOReactorConfig;
42 import org.apache.http.nio.conn.NoopIOSessionStrategy;
43 import org.apache.http.nio.conn.SchemeIOSessionStrategy;
44 import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
45 import org.apache.http.nio.reactor.ConnectingIOReactor;
46 import org.apache.http.nio.reactor.IOReactorException;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import io.wcm.caravan.commons.httpclient.HttpClientConfig;
53 import io.wcm.caravan.commons.httpclient.impl.helpers.CertificateLoader;
54
55
56
57
58 class HttpAsyncClientItem {
59
60 private final HttpClientConfig config;
61 private final CloseableHttpAsyncClient httpAsyncClient;
62 private final RequestConfig defaultRequestConfig;
63
64 private static final Logger log = LoggerFactory.getLogger(HttpAsyncClientItem.class);
65
66
67
68
69 HttpAsyncClientItem(@NotNull HttpClientConfig config) {
70 this.config = config;
71
72
73 SSLContext sslContext;
74 if (CertificateLoader.isSslKeyManagerEnabled(config) || CertificateLoader.isSslTrustStoreEnbaled(config)) {
75 try {
76 sslContext = CertificateLoader.buildSSLContext(config);
77 }
78 catch (IOException | GeneralSecurityException ex) {
79 throw new IllegalArgumentException("Invalid SSL client certificate configuration.", ex);
80 }
81 }
82 else {
83 sslContext = CertificateLoader.createDefaultSSlContext();
84 }
85
86 CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
87
88 if (StringUtils.isNotEmpty(config.getProxyUser())) {
89 credentialsProvider.setCredentials(new AuthScope(config.getProxyHost(), config.getProxyPort()),
90 new UsernamePasswordCredentials(config.getProxyUser(), config.getProxyPassword()));
91 }
92
93 if (StringUtils.isNotEmpty(config.getHttpUser())) {
94 credentialsProvider.setCredentials(AuthScope.ANY,
95 new UsernamePasswordCredentials(config.getHttpUser(), config.getHttpPassword()));
96 }
97
98
99 defaultRequestConfig = buildDefaultRequestConfig(config);
100
101
102 PoolingNHttpClientConnectionManager asyncConnectionManager = buildAsyncConnectionManager(config, sslContext);
103 httpAsyncClient = buildHttpAsyncClient(config, asyncConnectionManager, credentialsProvider, defaultRequestConfig);
104
105
106 httpAsyncClient.start();
107 }
108
109 private static @NotNull RequestConfig buildDefaultRequestConfig(@NotNull HttpClientConfig config) {
110 return RequestConfig.custom()
111 .setConnectionRequestTimeout(config.getConnectionRequestTimeout())
112 .setConnectTimeout(config.getConnectTimeout())
113 .setSocketTimeout(config.getSocketTimeout())
114 .setCookieSpec(config.getCookieSpec())
115 .build();
116 }
117
118 private static @NotNull PoolingNHttpClientConnectionManager buildAsyncConnectionManager(@NotNull HttpClientConfig config,
119 @NotNull SSLContext sslContext) {
120
121 SchemeIOSessionStrategy sslSocketFactory = new SSLIOSessionStrategy(sslContext);
122 Registry<SchemeIOSessionStrategy> asyncSchemeRegistry = RegistryBuilder.<SchemeIOSessionStrategy>create()
123 .register("http", NoopIOSessionStrategy.INSTANCE)
124 .register("https", sslSocketFactory)
125 .build();
126
127
128 ConnectingIOReactor ioreactor;
129 try {
130 ioreactor = new DefaultConnectingIOReactor(IOReactorConfig.DEFAULT);
131 }
132 catch (IOReactorException ex) {
133 throw new RuntimeException("Unable to initialize IO reactor.", ex);
134 }
135 PoolingNHttpClientConnectionManager conmgr = new PoolingNHttpClientConnectionManager(ioreactor, asyncSchemeRegistry);
136 conmgr.setMaxTotal(config.getMaxTotalConnections());
137 conmgr.setDefaultMaxPerRoute(config.getMaxConnectionsPerHost());
138 return conmgr;
139 }
140
141 private static @NotNull CloseableHttpAsyncClient buildHttpAsyncClient(@NotNull HttpClientConfig config,
142 @NotNull PoolingNHttpClientConnectionManager connectionManager, @NotNull CredentialsProvider credentialsProvider,
143 @NotNull RequestConfig defaultRequestConfig) {
144
145
146 HttpAsyncClientBuilder httpClientAsyncBuilder = HttpAsyncClientBuilder.create()
147 .setConnectionManager(connectionManager);
148
149
150 httpClientAsyncBuilder.setDefaultRequestConfig(defaultRequestConfig);
151
152 httpClientAsyncBuilder.setDefaultCredentialsProvider(credentialsProvider);
153
154
155 if (StringUtils.isNotEmpty(config.getProxyHost())) {
156 httpClientAsyncBuilder.setProxy(new HttpHost(config.getProxyHost(), config.getProxyPort()));
157
158
159 if (StringUtils.isNotEmpty(config.getProxyUser())) {
160 httpClientAsyncBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
161 }
162 }
163
164 return httpClientAsyncBuilder.build();
165 }
166
167
168
169
170 public @NotNull CloseableHttpAsyncClient getHttpAsyncClient() {
171 return httpAsyncClient;
172 }
173
174
175
176
177 public @NotNull RequestConfig getDefaultRequestConfig() {
178 return defaultRequestConfig;
179 }
180
181
182
183
184
185
186
187
188 public boolean matches(@Nullable String hostName, @Nullable String wsAddressingToURI, @Nullable String path, boolean isWsCall) {
189 if (isWsCall) {
190 return config.isEnabled()
191 && config.matchesHost(hostName)
192 && config.matchesPath(path)
193 && config.matchesWsAddressingToUri(wsAddressingToURI);
194 }
195 else {
196 return config.isEnabled()
197 && config.matchesHost(hostName)
198 && config.matchesPath(path);
199 }
200 }
201
202
203
204
205 public void close() {
206 try {
207 httpAsyncClient.close();
208 }
209 catch (IOException ex) {
210 log.warn("Error closing async HTTP client.", ex);
211 }
212 }
213
214 @Override
215 public @NotNull String toString() {
216 return "HttpClientItem[" + config.toString() + "]";
217 }
218
219 }