1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.client5.testing.external;
28
29 import java.util.Objects;
30 import java.util.concurrent.ExecutionException;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.TimeoutException;
33
34 import javax.net.ssl.SSLContext;
35
36 import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
37 import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
38 import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
39 import org.apache.hc.client5.http.auth.AuthScope;
40 import org.apache.hc.client5.http.auth.Credentials;
41 import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
42 import org.apache.hc.client5.http.config.RequestConfig;
43 import org.apache.hc.client5.http.config.TlsConfig;
44 import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
45 import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
46 import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
47 import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
48 import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
49 import org.apache.hc.client5.http.protocol.HttpClientContext;
50 import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
51 import org.apache.hc.core5.http.HeaderElements;
52 import org.apache.hc.core5.http.HttpHeaders;
53 import org.apache.hc.core5.http.HttpHost;
54 import org.apache.hc.core5.http.HttpRequest;
55 import org.apache.hc.core5.http.HttpResponse;
56 import org.apache.hc.core5.http.HttpStatus;
57 import org.apache.hc.core5.http2.HttpVersionPolicy;
58 import org.apache.hc.core5.ssl.SSLContexts;
59 import org.apache.hc.core5.util.TextUtils;
60 import org.apache.hc.core5.util.TimeValue;
61 import org.apache.hc.core5.util.Timeout;
62
63 public class HttpAsyncClientCompatibilityTest {
64
65 public static void main(final String... args) throws Exception {
66 final HttpAsyncClientCompatibilityTest[] tests = new HttpAsyncClientCompatibilityTest[] {
67 new HttpAsyncClientCompatibilityTest(
68 HttpVersionPolicy.FORCE_HTTP_1,
69 new HttpHost("http", "localhost", 8080), null, null),
70 new HttpAsyncClientCompatibilityTest(
71 HttpVersionPolicy.FORCE_HTTP_1,
72 new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8888), null),
73 new HttpAsyncClientCompatibilityTest(
74 HttpVersionPolicy.FORCE_HTTP_1,
75 new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8889),
76 new UsernamePasswordCredentials("squid", "nopassword".toCharArray())),
77 new HttpAsyncClientCompatibilityTest(
78 HttpVersionPolicy.FORCE_HTTP_1,
79 new HttpHost("https", "localhost", 8443), null, null),
80 new HttpAsyncClientCompatibilityTest(
81 HttpVersionPolicy.FORCE_HTTP_1,
82 new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null),
83 new HttpAsyncClientCompatibilityTest(
84 HttpVersionPolicy.FORCE_HTTP_1,
85 new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889),
86 new UsernamePasswordCredentials("squid", "nopassword".toCharArray())),
87 new HttpAsyncClientCompatibilityTest(
88 HttpVersionPolicy.FORCE_HTTP_2,
89 new HttpHost("http", "localhost", 8080), null, null),
90 new HttpAsyncClientCompatibilityTest(
91 HttpVersionPolicy.FORCE_HTTP_2,
92 new HttpHost("https", "localhost", 8443), null, null),
93 new HttpAsyncClientCompatibilityTest(
94 HttpVersionPolicy.NEGOTIATE,
95 new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null),
96 new HttpAsyncClientCompatibilityTest(
97 HttpVersionPolicy.NEGOTIATE,
98 new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889),
99 new UsernamePasswordCredentials("squid", "nopassword".toCharArray())),
100 new HttpAsyncClientCompatibilityTest(
101 HttpVersionPolicy.FORCE_HTTP_2,
102 new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null),
103 new HttpAsyncClientCompatibilityTest(
104 HttpVersionPolicy.FORCE_HTTP_2,
105 new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889),
106 new UsernamePasswordCredentials("squid", "nopassword".toCharArray()))
107 };
108 for (final HttpAsyncClientCompatibilityTest test: tests) {
109 try {
110 test.execute();
111 } finally {
112 test.shutdown();
113 }
114 }
115 }
116
117 private static final Timeout TIMEOUT = Timeout.ofSeconds(5);
118
119 private final HttpVersionPolicy versionPolicy;
120 private final HttpHost target;
121 private final HttpHost proxy;
122 private final BasicCredentialsProvider credentialsProvider;
123 private final PoolingAsyncClientConnectionManager connManager;
124 private final CloseableHttpAsyncClient client;
125
126 HttpAsyncClientCompatibilityTest(
127 final HttpVersionPolicy versionPolicy,
128 final HttpHost target,
129 final HttpHost proxy,
130 final Credentials proxyCreds) throws Exception {
131 this.versionPolicy = versionPolicy;
132 this.target = target;
133 this.proxy = proxy;
134 this.credentialsProvider = new BasicCredentialsProvider();
135 final RequestConfig requestConfig = RequestConfig.DEFAULT;
136 if (proxy != null && proxyCreds != null) {
137 this.credentialsProvider.setCredentials(new AuthScope(proxy), proxyCreds);
138 }
139 final SSLContext sslContext = SSLContexts.custom()
140 .loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build();
141 this.connManager = PoolingAsyncClientConnectionManagerBuilder.create()
142 .setTlsStrategy(new DefaultClientTlsStrategy(sslContext))
143 .setDefaultTlsConfig(TlsConfig.custom()
144 .setVersionPolicy(versionPolicy)
145 .build())
146 .build();
147 this.client = HttpAsyncClients.custom()
148 .setConnectionManager(this.connManager)
149 .setProxy(this.proxy)
150 .setDefaultRequestConfig(requestConfig)
151 .build();
152 }
153
154 void shutdown() throws Exception {
155 client.close();
156 }
157
158 enum TestResult {OK, NOK}
159
160 private void logResult(final TestResult result,
161 final HttpRequest request,
162 final HttpResponse response,
163 final String message) {
164 final StringBuilder buf = new StringBuilder();
165 buf.append(result);
166 if (buf.length() == 2) {
167 buf.append(" ");
168 }
169 buf.append(": ");
170 if (response != null) {
171 buf.append(response.getVersion()).append(" ");
172 } else {
173 buf.append(versionPolicy).append(" ");
174 }
175 buf.append(target);
176 if (proxy != null) {
177 buf.append(" via ").append(proxy);
178 }
179 buf.append(": ");
180 buf.append(request.getMethod()).append(" ").append(request.getRequestUri());
181 if (message != null && !TextUtils.isBlank(message)) {
182 buf.append(" -> ").append(message);
183 }
184 System.out.println(buf);
185 }
186
187 void execute() throws Exception {
188
189 client.start();
190
191 {
192 final HttpClientContext context = HttpClientContext.create();
193 context.setCredentialsProvider(credentialsProvider);
194
195 final SimpleHttpRequest options = SimpleRequestBuilder.options()
196 .setHttpHost(target)
197 .setPath("*")
198 .build();
199 final Future<SimpleHttpResponse> future = client.execute(options, context, null);
200 try {
201 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
202 final int code = response.getCode();
203 if (code == HttpStatus.SC_OK) {
204 logResult(TestResult.OK, options, response, Objects.toString(response.getFirstHeader("server")));
205 } else {
206 logResult(TestResult.NOK, options, response, "(status " + code + ")");
207 }
208 } catch (final ExecutionException ex) {
209 final Throwable cause = ex.getCause();
210 logResult(TestResult.NOK, options, null, "(" + cause.getMessage() + ")");
211 } catch (final TimeoutException ex) {
212 logResult(TestResult.NOK, options, null, "(time out)");
213 }
214 }
215
216 {
217 connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
218 final HttpClientContext context = HttpClientContext.create();
219 context.setCredentialsProvider(credentialsProvider);
220
221 final String[] requestUris = new String[] {"/", "/news.html", "/status.html"};
222 for (final String requestUri: requestUris) {
223 final SimpleHttpRequest httpGet = SimpleRequestBuilder.get()
224 .setHttpHost(target)
225 .setPath(requestUri)
226 .build();
227 final Future<SimpleHttpResponse> future = client.execute(httpGet, context, null);
228 try {
229 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
230 final int code = response.getCode();
231 if (code == HttpStatus.SC_OK) {
232 logResult(TestResult.OK, httpGet, response, "200");
233 } else {
234 logResult(TestResult.NOK, httpGet, response, "(status " + code + ")");
235 }
236 } catch (final ExecutionException ex) {
237 final Throwable cause = ex.getCause();
238 logResult(TestResult.NOK, httpGet, null, "(" + cause.getMessage() + ")");
239 } catch (final TimeoutException ex) {
240 logResult(TestResult.NOK, httpGet, null, "(time out)");
241 }
242 }
243 }
244
245 {
246 connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
247 credentialsProvider.setCredentials(
248 new AuthScope("http", "otherhost", -1, "Restricted Files", null),
249 new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
250 final HttpClientContext context = HttpClientContext.create();
251 context.setCredentialsProvider(credentialsProvider);
252
253 final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
254 .setHttpHost(target)
255 .setPath("/private/big-secret.txt")
256 .build();
257 final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
258 try {
259 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
260 final int code = response.getCode();
261 if (code == HttpStatus.SC_UNAUTHORIZED) {
262 logResult(TestResult.OK, httpGetSecret, response, "401 (wrong target auth scope)");
263 } else {
264 logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")");
265 }
266 } catch (final ExecutionException ex) {
267 final Throwable cause = ex.getCause();
268 logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")");
269 } catch (final TimeoutException ex) {
270 logResult(TestResult.NOK, httpGetSecret, null, "(time out)");
271 }
272 }
273
274 {
275 connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
276 credentialsProvider.setCredentials(
277 new AuthScope(target),
278 new UsernamePasswordCredentials("testuser", "wrong password".toCharArray()));
279 final HttpClientContext context = HttpClientContext.create();
280 context.setCredentialsProvider(credentialsProvider);
281
282 final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
283 .setHttpHost(target)
284 .setPath("/private/big-secret.txt")
285 .build();
286 final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
287 try {
288 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
289 final int code = response.getCode();
290 if (code == HttpStatus.SC_UNAUTHORIZED) {
291 logResult(TestResult.OK, httpGetSecret, response, "401 (wrong target creds)");
292 } else {
293 logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")");
294 }
295 } catch (final ExecutionException ex) {
296 final Throwable cause = ex.getCause();
297 logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")");
298 } catch (final TimeoutException ex) {
299 logResult(TestResult.NOK, httpGetSecret, null, "(time out)");
300 }
301 }
302
303 {
304 connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
305 credentialsProvider.setCredentials(
306 new AuthScope(target),
307 new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
308 final HttpClientContext context = HttpClientContext.create();
309 context.setCredentialsProvider(credentialsProvider);
310
311 final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
312 .setHttpHost(target)
313 .setPath("/private/big-secret.txt")
314 .build();
315 final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
316 try {
317 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
318 final int code = response.getCode();
319 if (code == HttpStatus.SC_OK) {
320 logResult(TestResult.OK, httpGetSecret, response, "200 (correct target creds)");
321 } else {
322 logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")");
323 }
324 } catch (final ExecutionException ex) {
325 final Throwable cause = ex.getCause();
326 logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")");
327 } catch (final TimeoutException ex) {
328 logResult(TestResult.NOK, httpGetSecret, null, "(time out)");
329 }
330 }
331
332 if (versionPolicy == HttpVersionPolicy.FORCE_HTTP_1)
333 {
334 connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
335 credentialsProvider.setCredentials(
336 new AuthScope(target),
337 new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
338 final HttpClientContext context = HttpClientContext.create();
339 context.setCredentialsProvider(credentialsProvider);
340
341 final SimpleHttpRequest httpGetSecret = SimpleRequestBuilder.get()
342 .setHttpHost(target)
343 .setPath("/private/big-secret.txt")
344 .build();
345 httpGetSecret.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
346 final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
347 try {
348 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
349 final int code = response.getCode();
350 if (code == HttpStatus.SC_OK) {
351 logResult(TestResult.OK, httpGetSecret, response, "200 (correct target creds / no keep-alive)");
352 } else {
353 logResult(TestResult.NOK, httpGetSecret, response, "(status " + code + ")");
354 }
355 } catch (final ExecutionException ex) {
356 final Throwable cause = ex.getCause();
357 logResult(TestResult.NOK, httpGetSecret, null, "(" + cause.getMessage() + ")");
358 } catch (final TimeoutException ex) {
359 logResult(TestResult.NOK, httpGetSecret, null, "(time out)");
360 }
361 }
362 }
363
364 }