1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.forgerock.http.apache.async;
18
19 import static java.util.concurrent.TimeUnit.MILLISECONDS;
20 import static org.forgerock.http.handler.HttpClientHandler.OPTION_CONNECT_TIMEOUT;
21 import static org.forgerock.http.handler.HttpClientHandler.OPTION_HOSTNAME_VERIFIER;
22 import static org.forgerock.http.handler.HttpClientHandler.OPTION_KEY_MANAGERS;
23 import static org.forgerock.http.handler.HttpClientHandler.OPTION_MAX_CONNECTIONS;
24 import static org.forgerock.http.handler.HttpClientHandler.OPTION_REUSE_CONNECTIONS;
25 import static org.forgerock.http.handler.HttpClientHandler.OPTION_SO_TIMEOUT;
26 import static org.forgerock.http.handler.HttpClientHandler.OPTION_SSLCONTEXT_ALGORITHM;
27 import static org.forgerock.http.handler.HttpClientHandler.OPTION_SSL_CIPHER_SUITES;
28 import static org.forgerock.http.handler.HttpClientHandler.OPTION_SSL_ENABLED_PROTOCOLS;
29 import static org.forgerock.http.handler.HttpClientHandler.OPTION_TEMPORARY_STORAGE;
30 import static org.forgerock.http.handler.HttpClientHandler.OPTION_TRUST_MANAGERS;
31 import static org.forgerock.http.util.Lists.asArrayOrNull;
32
33 import java.security.GeneralSecurityException;
34 import java.util.List;
35
36 import javax.net.ssl.HostnameVerifier;
37 import javax.net.ssl.SSLContext;
38
39 import org.apache.http.HttpRequest;
40 import org.apache.http.HttpResponse;
41 import org.apache.http.ProtocolException;
42 import org.apache.http.client.RedirectStrategy;
43 import org.apache.http.client.methods.HttpUriRequest;
44 import org.apache.http.config.Registry;
45 import org.apache.http.config.RegistryBuilder;
46 import org.apache.http.conn.ssl.DefaultHostnameVerifier;
47 import org.apache.http.conn.ssl.NoopHostnameVerifier;
48 import org.apache.http.impl.NoConnectionReuseStrategy;
49 import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
50 import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
51 import org.apache.http.impl.nio.client.HttpAsyncClients;
52 import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
53 import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
54 import org.apache.http.impl.nio.reactor.IOReactorConfig;
55 import org.apache.http.nio.conn.NoopIOSessionStrategy;
56 import org.apache.http.nio.conn.SchemeIOSessionStrategy;
57 import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
58 import org.apache.http.nio.reactor.ConnectingIOReactor;
59 import org.apache.http.nio.reactor.IOReactorException;
60 import org.apache.http.protocol.HttpContext;
61 import org.forgerock.http.HttpApplicationException;
62 import org.forgerock.http.apache.NoAuthenticationStrategy;
63 import org.forgerock.http.io.Buffer;
64 import org.forgerock.http.spi.HttpClient;
65 import org.forgerock.http.spi.HttpClientProvider;
66 import org.forgerock.util.Factory;
67 import org.forgerock.util.Option;
68 import org.forgerock.util.Options;
69 import org.forgerock.util.time.Duration;
70
71
72
73
74
75
76 public class AsyncHttpClientProvider implements HttpClientProvider {
77
78
79
80
81
82 public static final Option<Integer> OPTION_WORKER_THREADS = Option.of(Integer.class, null);
83
84
85
86
87 private static final RedirectStrategy DISABLE_REDIRECT = new RedirectStrategy() {
88 @Override
89 public boolean isRedirected(final HttpRequest request, final HttpResponse response,
90 final HttpContext context) throws ProtocolException {
91 return false;
92 }
93
94 @Override
95 public HttpUriRequest getRedirect(final HttpRequest request, final HttpResponse response,
96 final HttpContext context) throws ProtocolException {
97 return null;
98 }
99 };
100
101 @Override
102 public HttpClient newHttpClient(final Options options) throws HttpApplicationException {
103
104 final Factory<Buffer> storage = options.get(OPTION_TEMPORARY_STORAGE);
105
106
107 final SSLContext sslContext;
108 try {
109 sslContext = SSLContext.getInstance(options.get(OPTION_SSLCONTEXT_ALGORITHM));
110 sslContext.init(options.get(OPTION_KEY_MANAGERS),
111 options.get(OPTION_TRUST_MANAGERS), null);
112 } catch (final GeneralSecurityException e) {
113 throw new HttpApplicationException("Can't create SSL Context", e);
114 }
115
116 HostnameVerifier verifier = new DefaultHostnameVerifier();
117 switch (options.get(OPTION_HOSTNAME_VERIFIER)) {
118 case ALLOW_ALL:
119 verifier = NoopHostnameVerifier.INSTANCE;
120 break;
121 }
122
123 List<String> protocols = options.get(OPTION_SSL_ENABLED_PROTOCOLS);
124 List<String> ciphers = options.get(OPTION_SSL_CIPHER_SUITES);
125
126
127 Registry<SchemeIOSessionStrategy> registry =
128 RegistryBuilder.<SchemeIOSessionStrategy>create()
129 .register("http", NoopIOSessionStrategy.INSTANCE)
130 .register("https", new SSLIOSessionStrategy(sslContext, asArrayOrNull(protocols),
131 asArrayOrNull(ciphers), verifier))
132 .build();
133
134
135 final Duration soTimeout = options.get(OPTION_SO_TIMEOUT);
136 final Duration connectTimeout = options.get(OPTION_CONNECT_TIMEOUT);
137
138
139
140 IOReactorConfig.Builder reactorBuilder = IOReactorConfig.custom();
141
142 if (!connectTimeout.isUnlimited()) {
143 reactorBuilder.setConnectTimeout((int) connectTimeout.to(MILLISECONDS));
144 }
145 if (!soTimeout.isUnlimited()) {
146 reactorBuilder.setSoTimeout((int) soTimeout.to(MILLISECONDS));
147 }
148 Integer threadCount = options.get(OPTION_WORKER_THREADS);
149 if (threadCount != null) {
150 reactorBuilder.setIoThreadCount(threadCount);
151 }
152 IOReactorConfig ioReactorConfig = reactorBuilder.build();
153
154
155 ConnectingIOReactor reactor;
156 try {
157 reactor = new DefaultConnectingIOReactor(ioReactorConfig);
158 } catch (IOReactorException e) {
159 throw new HttpApplicationException("Cannot create I/O Reactor", e);
160 }
161
162
163 PoolingNHttpClientConnectionManager manager = new PoolingNHttpClientConnectionManager(reactor, registry);
164
165
166 final int maxConnections = options.get(OPTION_MAX_CONNECTIONS);
167 manager.setMaxTotal(maxConnections);
168 manager.setDefaultMaxPerRoute(maxConnections);
169
170
171
172
173
174
175
176 HttpAsyncClientBuilder builder = HttpAsyncClients.custom();
177
178 if (!options.get(OPTION_REUSE_CONNECTIONS)) {
179 builder.setConnectionReuseStrategy(NoConnectionReuseStrategy.INSTANCE);
180 }
181
182
183
184
185 CloseableHttpAsyncClient client = builder.setConnectionManager(manager)
186 .disableCookieManagement()
187 .setRedirectStrategy(DISABLE_REDIRECT)
188 .setTargetAuthenticationStrategy(NoAuthenticationStrategy.INSTANCE)
189 .setProxyAuthenticationStrategy(NoAuthenticationStrategy.INSTANCE)
190 .build();
191 client.start();
192 return new AsyncHttpClient(client, storage, ioReactorConfig.getIoThreadCount());
193 }
194 }