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  
31  import javax.net.ssl.SSLContext;
32  
33  import org.apache.hc.client5.http.auth.AuthScope;
34  import org.apache.hc.client5.http.auth.Credentials;
35  import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
36  import org.apache.hc.client5.http.classic.methods.HttpGet;
37  import org.apache.hc.client5.http.classic.methods.HttpOptions;
38  import org.apache.hc.client5.http.config.RequestConfig;
39  import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
40  import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
41  import org.apache.hc.client5.http.impl.classic.HttpClients;
42  import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
43  import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
44  import org.apache.hc.client5.http.protocol.HttpClientContext;
45  import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
46  import org.apache.hc.core5.http.ClassicHttpResponse;
47  import org.apache.hc.core5.http.HeaderElements;
48  import org.apache.hc.core5.http.HttpHeaders;
49  import org.apache.hc.core5.http.HttpHost;
50  import org.apache.hc.core5.http.HttpRequest;
51  import org.apache.hc.core5.http.HttpStatus;
52  import org.apache.hc.core5.http.io.entity.EntityUtils;
53  import org.apache.hc.core5.ssl.SSLContexts;
54  import org.apache.hc.core5.util.TextUtils;
55  import org.apache.hc.core5.util.TimeValue;
56  
57  public class HttpClientCompatibilityTest {
58  
59      public static void main(final String... args) throws Exception {
60          final HttpClientCompatibilityTest[] tests = new HttpClientCompatibilityTest[] {
61                  new HttpClientCompatibilityTest(
62                          new HttpHost("http", "localhost", 8080), null, null),
63                  new HttpClientCompatibilityTest(
64                          new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8888), null),
65                  new HttpClientCompatibilityTest(
66                          new HttpHost("http", "test-httpd", 8080), new HttpHost("localhost", 8889),
67                          new UsernamePasswordCredentials("squid", "nopassword".toCharArray())),
68                  new HttpClientCompatibilityTest(
69                          new HttpHost("https", "localhost", 8443), null, null),
70                  new HttpClientCompatibilityTest(
71                          new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8888), null),
72                  new HttpClientCompatibilityTest(
73                          new HttpHost("https", "test-httpd", 8443), new HttpHost("localhost", 8889),
74                          new UsernamePasswordCredentials("squid", "nopassword".toCharArray()))
75          };
76          for (final HttpClientCompatibilityTest test: tests) {
77              try {
78                  test.execute();
79              } finally {
80                  test.shutdown();
81              }
82          }
83      }
84  
85      private final HttpHost target;
86      private final HttpHost proxy;
87      private final BasicCredentialsProvider credentialsProvider;
88      private final PoolingHttpClientConnectionManager connManager;
89      private final CloseableHttpClient client;
90  
91      HttpClientCompatibilityTest(
92              final HttpHost target,
93              final HttpHost proxy,
94              final Credentials proxyCreds) throws Exception {
95          this.target = target;
96          this.proxy = proxy;
97          this.credentialsProvider = new BasicCredentialsProvider();
98          final RequestConfig requestConfig = RequestConfig.DEFAULT;
99          if (proxy != null && proxyCreds != null) {
100             this.credentialsProvider.setCredentials(new AuthScope(proxy), proxyCreds);
101         }
102         final SSLContext sslContext = SSLContexts.custom()
103                 .loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build();
104         this.connManager = PoolingHttpClientConnectionManagerBuilder.create()
105                 .setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext))
106                 .build();
107         this.client = HttpClients.custom()
108                 .setConnectionManager(this.connManager)
109                 .setProxy(this.proxy)
110                 .setDefaultRequestConfig(requestConfig)
111                 .build();
112     }
113 
114     void shutdown() throws Exception {
115         client.close();
116     }
117 
118     enum TestResult {OK, NOK}
119 
120     private void logResult(final TestResult result, final HttpRequest request, final String message) {
121         final StringBuilder buf = new StringBuilder();
122         buf.append(result);
123         if (buf.length() == 2) {
124             buf.append(" ");
125         }
126         buf.append(": ").append(target);
127         if (proxy != null) {
128             buf.append(" via ").append(proxy);
129         }
130         buf.append(": ");
131         buf.append(request.getMethod()).append(" ").append(request.getRequestUri());
132         if (message != null && !TextUtils.isBlank(message)) {
133             buf.append(" -> ").append(message);
134         }
135         System.out.println(buf);
136     }
137 
138     void execute() {
139 
140         // Initial ping
141         {
142             final HttpClientContext context = HttpClientContext.create();
143             context.setCredentialsProvider(credentialsProvider);
144             final HttpOptions options = new HttpOptions("*");
145             try (ClassicHttpResponse response = client.executeOpen(target, options, context)) {
146                 final int code = response.getCode();
147                 EntityUtils.consume(response.getEntity());
148                 if (code == HttpStatus.SC_OK) {
149                     logResult(TestResult.OK, options, Objects.toString(response.getFirstHeader("server")));
150                 } else {
151                     logResult(TestResult.NOK, options, "(status " + code + ")");
152                 }
153             } catch (final Exception ex) {
154                 logResult(TestResult.NOK, options, "(" + ex.getMessage() + ")");
155             }
156         }
157         // Basic GET requests
158         {
159             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
160             final HttpClientContext context = HttpClientContext.create();
161             context.setCredentialsProvider(credentialsProvider);
162             final String[] requestUris = new String[] {"/", "/news.html", "/status.html"};
163             for (final String requestUri: requestUris) {
164                 final HttpGet httpGet = new HttpGet(requestUri);
165                 try (ClassicHttpResponse response = client.executeOpen(target, httpGet, context)) {
166                     final int code = response.getCode();
167                     EntityUtils.consume(response.getEntity());
168                     if (code == HttpStatus.SC_OK) {
169                         logResult(TestResult.OK, httpGet, "200");
170                     } else {
171                         logResult(TestResult.NOK, httpGet, "(status " + code + ")");
172                     }
173                 } catch (final Exception ex) {
174                     logResult(TestResult.NOK, httpGet, "(" + ex.getMessage() + ")");
175                 }
176             }
177         }
178         // Wrong target auth scope
179         {
180             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
181             credentialsProvider.setCredentials(
182                     new AuthScope("http", "otherhost", -1, "Restricted Files", null),
183                     new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
184             final HttpClientContext context = HttpClientContext.create();
185             context.setCredentialsProvider(credentialsProvider);
186 
187             final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
188             try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) {
189                 final int code = response.getCode();
190                 EntityUtils.consume(response.getEntity());
191                 if (code == HttpStatus.SC_UNAUTHORIZED) {
192                     logResult(TestResult.OK, httpGetSecret, "401 (wrong target auth scope)");
193                 } else {
194                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
195                 }
196             } catch (final Exception ex) {
197                 logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
198             }
199         }
200         // Wrong target credentials
201         {
202             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
203             credentialsProvider.setCredentials(
204                     new AuthScope(target),
205                     new UsernamePasswordCredentials("testuser", "wrong password".toCharArray()));
206             final HttpClientContext context = HttpClientContext.create();
207             context.setCredentialsProvider(credentialsProvider);
208 
209             final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
210             try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) {
211                 final int code = response.getCode();
212                 EntityUtils.consume(response.getEntity());
213                 if (code == HttpStatus.SC_UNAUTHORIZED) {
214                     logResult(TestResult.OK, httpGetSecret, "401 (wrong target creds)");
215                 } else {
216                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
217                 }
218             } catch (final Exception ex) {
219                 logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
220             }
221         }
222         // Correct target credentials
223         {
224             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
225             credentialsProvider.setCredentials(
226                     new AuthScope(target),
227                     new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
228             final HttpClientContext context = HttpClientContext.create();
229             context.setCredentialsProvider(credentialsProvider);
230 
231             final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
232             try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) {
233                 final int code = response.getCode();
234                 EntityUtils.consume(response.getEntity());
235                 if (code == HttpStatus.SC_OK) {
236                     logResult(TestResult.OK, httpGetSecret, "200 (correct target creds)");
237                 } else {
238                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
239                 }
240             } catch (final Exception ex) {
241                 logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
242             }
243         }
244         // Correct target credentials (no keep-alive)
245         {
246             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
247             credentialsProvider.setCredentials(
248                     new AuthScope(target),
249                     new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
250             final HttpClientContext context = HttpClientContext.create();
251             context.setCredentialsProvider(credentialsProvider);
252 
253             final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
254             httpGetSecret.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
255             try (ClassicHttpResponse response = client.executeOpen(target, httpGetSecret, context)) {
256                 final int code = response.getCode();
257                 EntityUtils.consume(response.getEntity());
258                 if (code == HttpStatus.SC_OK) {
259                     logResult(TestResult.OK, httpGetSecret, "200 (correct target creds / no keep-alive)");
260                 } else {
261                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
262                 }
263             } catch (final Exception ex) {
264                 logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
265             }
266         }
267     }
268 
269 }