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