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.SimpleHttpRequests;
38  import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
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.HttpStatus;
55  import org.apache.hc.core5.http.HttpVersion;
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                          HttpVersion.HTTP_1_1,
68                          new HttpHost("http", "localhost", 8080), null, null),
69                  new HttpAsyncClientCompatibilityTest(
70                          HttpVersion.HTTP_1_1,
71                          new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8888), null),
72                  new HttpAsyncClientCompatibilityTest(
73                          HttpVersion.HTTP_1_1,
74                          new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8889),
75                          new UsernamePasswordCredentials("squid", "nopassword".toCharArray())),
76                  new HttpAsyncClientCompatibilityTest(
77                          HttpVersion.HTTP_1_1,
78                          new HttpHost("https", "localhost", 8443), null, null),
79                  new HttpAsyncClientCompatibilityTest(
80                          HttpVersion.HTTP_1_1,
81                          new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null),
82                  new HttpAsyncClientCompatibilityTest(
83                          HttpVersion.HTTP_1_1,
84                          new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889),
85                          new UsernamePasswordCredentials("squid", "nopassword".toCharArray())),
86                  new HttpAsyncClientCompatibilityTest(
87                          HttpVersion.HTTP_2_0,
88                          new HttpHost("http", "localhost", 8080), null, null),
89                  new HttpAsyncClientCompatibilityTest(
90                          HttpVersion.HTTP_2_0,
91                          new HttpHost("https", "localhost", 8443), null, null)
92          };
93          for (final HttpAsyncClientCompatibilityTest test: tests) {
94              try {
95                  test.execute();
96              } finally {
97                  test.shutdown();
98              }
99          }
100     }
101 
102     private static final Timeout TIMEOUT = Timeout.ofSeconds(5);
103 
104     private final HttpVersion protocolVersion;
105     private final HttpHost target;
106     private final HttpHost proxy;
107     private final BasicCredentialsProvider credentialsProvider;
108     private final PoolingAsyncClientConnectionManager connManager;
109     private final CloseableHttpAsyncClient client;
110 
111     HttpAsyncClientCompatibilityTest(
112             final HttpVersion protocolVersion,
113             final HttpHost target,
114             final HttpHost proxy,
115             final Credentials proxyCreds) throws Exception {
116         this.protocolVersion = protocolVersion;
117         this.target = target;
118         this.proxy = proxy;
119         this.credentialsProvider = new BasicCredentialsProvider();
120         final RequestConfig requestConfig = RequestConfig.custom()
121                 .setProxy(proxy)
122                 .build();
123         if (proxy != null && proxyCreds != null) {
124             this.credentialsProvider.setCredentials(new AuthScope(proxy), proxyCreds);
125         }
126         final SSLContext sslContext = SSLContexts.custom()
127                 .loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build();
128         this.connManager = PoolingAsyncClientConnectionManagerBuilder.create()
129                 .setTlsStrategy(new DefaultClientTlsStrategy(sslContext))
130                 .build();
131         this.client = HttpAsyncClients.custom()
132                 .setVersionPolicy(this.protocolVersion == HttpVersion.HTTP_2 ? HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1)
133                 .setConnectionManager(this.connManager)
134                 .setProxy(this.proxy)
135                 .setDefaultRequestConfig(requestConfig)
136                 .build();
137     }
138 
139     void shutdown() throws Exception {
140         client.close();
141     }
142 
143     enum TestResult {OK, NOK}
144 
145     private void logResult(final TestResult result, final HttpRequest request, final String message) {
146         final StringBuilder buf = new StringBuilder();
147         buf.append(result);
148         if (buf.length() == 2) {
149             buf.append(" ");
150         }
151         buf.append(": ").append(protocolVersion).append(" ").append(target);
152         if (proxy != null) {
153             buf.append(" via ").append(proxy);
154         }
155         buf.append(": ");
156         buf.append(request.getMethod()).append(" ").append(request.getRequestUri());
157         if (message != null && !TextUtils.isBlank(message)) {
158             buf.append(" -> ").append(message);
159         }
160         System.out.println(buf);
161     }
162 
163     void execute() throws Exception {
164 
165         client.start();
166         // Initial ping
167         {
168             final HttpClientContext context = HttpClientContext.create();
169             context.setCredentialsProvider(credentialsProvider);
170 
171             final SimpleHttpRequest options = SimpleHttpRequests.options(target, "*");
172             final Future<SimpleHttpResponse> future = client.execute(options, context, null);
173             try {
174                 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
175                 final int code = response.getCode();
176                 if (code == HttpStatus.SC_OK) {
177                     logResult(TestResult.OK, options, Objects.toString(response.getFirstHeader("server")));
178                 } else {
179                     logResult(TestResult.NOK, options, "(status " + code + ")");
180                 }
181             } catch (final ExecutionException ex) {
182                 final Throwable cause = ex.getCause();
183                 logResult(TestResult.NOK, options, "(" + cause.getMessage() + ")");
184             } catch (final TimeoutException ex) {
185                 logResult(TestResult.NOK, options, "(time out)");
186             }
187         }
188         // Basic GET requests
189         {
190             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
191             final HttpClientContext context = HttpClientContext.create();
192             context.setCredentialsProvider(credentialsProvider);
193 
194             final String[] requestUris = new String[] {"/", "/news.html", "/status.html"};
195             for (final String requestUri: requestUris) {
196                 final SimpleHttpRequest httpGet = SimpleHttpRequests.get(target, requestUri);
197                 final Future<SimpleHttpResponse> future = client.execute(httpGet, context, null);
198                 try {
199                     final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
200                     final int code = response.getCode();
201                     if (code == HttpStatus.SC_OK) {
202                         logResult(TestResult.OK, httpGet, "200");
203                     } else {
204                         logResult(TestResult.NOK, httpGet, "(status " + code + ")");
205                     }
206                 } catch (final ExecutionException ex) {
207                     final Throwable cause = ex.getCause();
208                     logResult(TestResult.NOK, httpGet, "(" + cause.getMessage() + ")");
209                 } catch (final TimeoutException ex) {
210                     logResult(TestResult.NOK, httpGet, "(time out)");
211                 }
212             }
213         }
214         // Wrong target auth scope
215         {
216             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
217             credentialsProvider.setCredentials(
218                     new AuthScope("http", "otherhost", -1, "Restricted Files", null),
219                     new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
220             final HttpClientContext context = HttpClientContext.create();
221             context.setCredentialsProvider(credentialsProvider);
222 
223             final SimpleHttpRequest httpGetSecret = SimpleHttpRequests.get(target, "/private/big-secret.txt");
224             final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
225             try {
226                 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
227                 final int code = response.getCode();
228                 if (code == HttpStatus.SC_UNAUTHORIZED) {
229                     logResult(TestResult.OK, httpGetSecret, "401 (wrong target auth scope)");
230                 } else {
231                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
232                 }
233             } catch (final ExecutionException ex) {
234                 final Throwable cause = ex.getCause();
235                 logResult(TestResult.NOK, httpGetSecret, "(" + cause.getMessage() + ")");
236             } catch (final TimeoutException ex) {
237                 logResult(TestResult.NOK, httpGetSecret, "(time out)");
238             }
239         }
240         // Wrong target credentials
241         {
242             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
243             credentialsProvider.setCredentials(
244                     new AuthScope(target),
245                     new UsernamePasswordCredentials("testuser", "wrong password".toCharArray()));
246             final HttpClientContext context = HttpClientContext.create();
247             context.setCredentialsProvider(credentialsProvider);
248 
249             final SimpleHttpRequest httpGetSecret = SimpleHttpRequests.get(target, "/private/big-secret.txt");
250             final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
251             try {
252                 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
253                 final int code = response.getCode();
254                 if (code == HttpStatus.SC_UNAUTHORIZED) {
255                     logResult(TestResult.OK, httpGetSecret, "401 (wrong target creds)");
256                 } else {
257                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
258                 }
259             } catch (final ExecutionException ex) {
260                 final Throwable cause = ex.getCause();
261                 logResult(TestResult.NOK, httpGetSecret, "(" + cause.getMessage() + ")");
262             } catch (final TimeoutException ex) {
263                 logResult(TestResult.NOK, httpGetSecret, "(time out)");
264             }
265         }
266         // Correct target credentials
267         {
268             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
269             credentialsProvider.setCredentials(
270                     new AuthScope(target),
271                     new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
272             final HttpClientContext context = HttpClientContext.create();
273             context.setCredentialsProvider(credentialsProvider);
274 
275             final SimpleHttpRequest httpGetSecret = SimpleHttpRequests.get(target, "/private/big-secret.txt");
276             final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
277             try {
278                 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
279                 final int code = response.getCode();
280                 if (code == HttpStatus.SC_OK) {
281                     logResult(TestResult.OK, httpGetSecret, "200 (correct target creds)");
282                 } else {
283                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
284                 }
285             } catch (final ExecutionException ex) {
286                 final Throwable cause = ex.getCause();
287                 logResult(TestResult.NOK, httpGetSecret, "(" + cause.getMessage() + ")");
288             } catch (final TimeoutException ex) {
289                 logResult(TestResult.NOK, httpGetSecret, "(time out)");
290             }
291         }
292         // Correct target credentials (no keep-alive)
293         if (protocolVersion.lessEquals(HttpVersion.HTTP_1_1))
294         {
295             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
296             credentialsProvider.setCredentials(
297                     new AuthScope(target),
298                     new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
299             final HttpClientContext context = HttpClientContext.create();
300             context.setCredentialsProvider(credentialsProvider);
301 
302             final SimpleHttpRequest httpGetSecret = SimpleHttpRequests.get(target, "/private/big-secret.txt");
303             httpGetSecret.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
304             final Future<SimpleHttpResponse> future = client.execute(httpGetSecret, context, null);
305             try {
306                 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
307                 final int code = response.getCode();
308                 if (code == HttpStatus.SC_OK) {
309                     logResult(TestResult.OK, httpGetSecret, "200 (correct target creds / no keep-alive)");
310                 } else {
311                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
312                 }
313             } catch (final ExecutionException ex) {
314                 final Throwable cause = ex.getCause();
315                 logResult(TestResult.NOK, httpGetSecret, "(" + cause.getMessage() + ")");
316             } catch (final TimeoutException ex) {
317                 logResult(TestResult.NOK, httpGetSecret, "(time out)");
318             }
319         }
320     }
321 
322 }