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 HttpClientCompatibilityTestlientCompatibilityTest.html#HttpClientCompatibilityTest">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.custom()
99                  .setProxy(proxy)
100                 .build();
101         if (proxy != null && proxyCreds != null) {
102             this.credentialsProvider.setCredentials(new AuthScope(proxy), proxyCreds);
103         }
104         final SSLContext sslContext = SSLContexts.custom()
105                 .loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build();
106         this.connManager = PoolingHttpClientConnectionManagerBuilder.create()
107                 .setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext))
108                 .build();
109         this.client = HttpClients.custom()
110                 .setConnectionManager(this.connManager)
111                 .setDefaultRequestConfig(requestConfig)
112                 .build();
113     }
114 
115     void shutdown() throws Exception {
116         client.close();
117     }
118 
119     enum TestResult {OK, NOK}
120 
121     private void logResult(final TestResult result, final HttpRequest request, final String message) {
122         final StringBuilder buf = new StringBuilder();
123         buf.append(result);
124         if (buf.length() == 2) {
125             buf.append(" ");
126         }
127         buf.append(": ").append(target);
128         if (proxy != null) {
129             buf.append(" via ").append(proxy);
130         }
131         buf.append(": ");
132         buf.append(request.getMethod()).append(" ").append(request.getRequestUri());
133         if (message != null && !TextUtils.isBlank(message)) {
134             buf.append(" -> ").append(message);
135         }
136         System.out.println(buf);
137     }
138 
139     void execute() {
140 
141         // Initial ping
142         {
143             final HttpClientContext context = HttpClientContext.create();
144             context.setCredentialsProvider(credentialsProvider);
145             final HttpOptions options = new HttpOptions("*");
146             try (ClassicHttpResponse response = client.execute(target, options, context)) {
147                 final int code = response.getCode();
148                 EntityUtils.consume(response.getEntity());
149                 if (code == HttpStatus.SC_OK) {
150                     logResult(TestResult.OK, options, Objects.toString(response.getFirstHeader("server")));
151                 } else {
152                     logResult(TestResult.NOK, options, "(status " + code + ")");
153                 }
154             } catch (final Exception ex) {
155                 logResult(TestResult.NOK, options, "(" + ex.getMessage() + ")");
156             }
157         }
158         // Basic GET requests
159         {
160             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
161             final HttpClientContext context = HttpClientContext.create();
162             context.setCredentialsProvider(credentialsProvider);
163             final String[] requestUris = new String[] {"/", "/news.html", "/status.html"};
164             for (final String requestUri: requestUris) {
165                 final HttpGet httpGet = new HttpGet(requestUri);
166                 try (ClassicHttpResponse response = client.execute(target, httpGet, context)) {
167                     final int code = response.getCode();
168                     EntityUtils.consume(response.getEntity());
169                     if (code == HttpStatus.SC_OK) {
170                         logResult(TestResult.OK, httpGet, "200");
171                     } else {
172                         logResult(TestResult.NOK, httpGet, "(status " + code + ")");
173                     }
174                 } catch (final Exception ex) {
175                     logResult(TestResult.NOK, httpGet, "(" + ex.getMessage() + ")");
176                 }
177             }
178         }
179         // Wrong target auth scope
180         {
181             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
182             credentialsProvider.setCredentials(
183                     new AuthScope("http", "otherhost", -1, "Restricted Files", null),
184                     new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
185             final HttpClientContext context = HttpClientContext.create();
186             context.setCredentialsProvider(credentialsProvider);
187 
188             final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
189             try (ClassicHttpResponse response = client.execute(target, httpGetSecret, context)) {
190                 final int code = response.getCode();
191                 EntityUtils.consume(response.getEntity());
192                 if (code == HttpStatus.SC_UNAUTHORIZED) {
193                     logResult(TestResult.OK, httpGetSecret, "401 (wrong target auth scope)");
194                 } else {
195                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
196                 }
197             } catch (final Exception ex) {
198                 logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
199             }
200         }
201         // Wrong target credentials
202         {
203             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
204             credentialsProvider.setCredentials(
205                     new AuthScope(target),
206                     new UsernamePasswordCredentials("testuser", "wrong password".toCharArray()));
207             final HttpClientContext context = HttpClientContext.create();
208             context.setCredentialsProvider(credentialsProvider);
209 
210             final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
211             try (ClassicHttpResponse response = client.execute(target, httpGetSecret, context)) {
212                 final int code = response.getCode();
213                 EntityUtils.consume(response.getEntity());
214                 if (code == HttpStatus.SC_UNAUTHORIZED) {
215                     logResult(TestResult.OK, httpGetSecret, "401 (wrong target creds)");
216                 } else {
217                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
218                 }
219             } catch (final Exception ex) {
220                 logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
221             }
222         }
223         // Correct target credentials
224         {
225             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
226             credentialsProvider.setCredentials(
227                     new AuthScope(target),
228                     new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
229             final HttpClientContext context = HttpClientContext.create();
230             context.setCredentialsProvider(credentialsProvider);
231 
232             final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
233             try (ClassicHttpResponse response = client.execute(target, httpGetSecret, context)) {
234                 final int code = response.getCode();
235                 EntityUtils.consume(response.getEntity());
236                 if (code == HttpStatus.SC_OK) {
237                     logResult(TestResult.OK, httpGetSecret, "200 (correct target creds)");
238                 } else {
239                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
240                 }
241             } catch (final Exception ex) {
242                 logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
243             }
244         }
245         // Correct target credentials (no keep-alive)
246         {
247             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
248             credentialsProvider.setCredentials(
249                     new AuthScope(target),
250                     new UsernamePasswordCredentials("testuser", "nopassword".toCharArray()));
251             final HttpClientContext context = HttpClientContext.create();
252             context.setCredentialsProvider(credentialsProvider);
253 
254             final HttpGet httpGetSecret = new HttpGet("/private/big-secret.txt");
255             httpGetSecret.setHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE);
256             try (ClassicHttpResponse response = client.execute(target, httpGetSecret, context)) {
257                 final int code = response.getCode();
258                 EntityUtils.consume(response.getEntity());
259                 if (code == HttpStatus.SC_OK) {
260                     logResult(TestResult.OK, httpGetSecret, "200 (correct target creds / no keep-alive)");
261                 } else {
262                     logResult(TestResult.NOK, httpGetSecret, "(status " + code + ")");
263                 }
264             } catch (final Exception ex) {
265                 logResult(TestResult.NOK, httpGetSecret, "(" + ex.getMessage() + ")");
266             }
267         }
268     }
269 
270 }