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.ArrayList;
30  import java.util.List;
31  import java.util.Objects;
32  import java.util.concurrent.ExecutionException;
33  import java.util.concurrent.Future;
34  import java.util.concurrent.TimeoutException;
35  import java.util.regex.Matcher;
36  import java.util.regex.Pattern;
37  
38  import javax.net.ssl.SSLContext;
39  
40  import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
41  import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
42  import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
43  import org.apache.hc.client5.http.cache.CacheResponseStatus;
44  import org.apache.hc.client5.http.cache.HttpCacheContext;
45  import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
46  import org.apache.hc.client5.http.impl.cache.CacheConfig;
47  import org.apache.hc.client5.http.impl.cache.CachingHttpAsyncClients;
48  import org.apache.hc.client5.http.impl.cache.HeapResourceFactory;
49  import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
50  import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
51  import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
52  import org.apache.hc.core5.http.Header;
53  import org.apache.hc.core5.http.HttpHost;
54  import org.apache.hc.core5.http.HttpRequest;
55  import org.apache.hc.core5.http.HttpStatus;
56  import org.apache.hc.core5.http.HttpVersion;
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 CachingHttpAsyncClientCompatibilityTest {
64  
65      public static void main(final String... args) throws Exception {
66          final CachingHttpAsyncClientCompatibilityTestlientCompatibilityTest.html#CachingHttpAsyncClientCompatibilityTest">CachingHttpAsyncClientCompatibilityTest[] tests = new CachingHttpAsyncClientCompatibilityTest[] {
67                  new CachingHttpAsyncClientCompatibilityTest(
68                          HttpVersion.HTTP_1_1, new HttpHost("http", "localhost", 8080)),
69                  new CachingHttpAsyncClientCompatibilityTest(
70                          HttpVersion.HTTP_2_0, new HttpHost("http", "localhost", 8080))
71          };
72          for (final CachingHttpAsyncClientCompatibilityTest test: tests) {
73              try {
74                  test.execute();
75              } finally {
76                  test.shutdown();
77              }
78          }
79      }
80  
81      private static final Timeout TIMEOUT = Timeout.ofSeconds(5);
82  
83      private final HttpVersion protocolVersion;
84      private final HttpHost target;
85      private final PoolingAsyncClientConnectionManager connManager;
86      private final CloseableHttpAsyncClient client;
87  
88      CachingHttpAsyncClientCompatibilityTest(final HttpVersion protocolVersion, final HttpHost target) throws Exception {
89          this.protocolVersion = protocolVersion;
90          this.target = target;
91          final SSLContext sslContext = SSLContexts.custom()
92                  .loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build();
93          this.connManager = PoolingAsyncClientConnectionManagerBuilder.create()
94                  .setTlsStrategy(new DefaultClientTlsStrategy(sslContext))
95                  .build();
96          this.client = CachingHttpAsyncClients.custom()
97                  .setCacheConfig(CacheConfig.custom()
98                          .setMaxObjectSize(20480)
99                          .build())
100                 .setResourceFactory(HeapResourceFactory.INSTANCE)
101                 .setVersionPolicy(this.protocolVersion == HttpVersion.HTTP_2 ? HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1)
102                 .setConnectionManager(this.connManager)
103                 .build();
104     }
105 
106 
107     void shutdown() throws Exception {
108         client.close();
109     }
110 
111     enum TestResult {OK, NOK}
112 
113     private void logResult(final TestResult result, final HttpRequest request, final String message) {
114         final StringBuilder buf = new StringBuilder();
115         buf.append(result);
116         if (buf.length() == 2) {
117             buf.append(" ");
118         }
119         buf.append(": ").append(target);
120         buf.append(": ");
121         buf.append(request.getMethod()).append(" ").append(request.getRequestUri());
122         if (message != null && !TextUtils.isBlank(message)) {
123             buf.append(" -> ").append(message);
124         }
125         System.out.println(buf);
126     }
127 
128     void execute() throws Exception {
129 
130         client.start();
131         // Initial ping
132         {
133             final HttpCacheContext context = HttpCacheContext.create();
134             final SimpleHttpRequest options = SimpleRequestBuilder.options()
135                     .setHttpHost(target)
136                     .setPath("*")
137                     .build();
138             final Future<SimpleHttpResponse> future = client.execute(options, context, null);
139             try {
140                 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
141                 final int code = response.getCode();
142                 if (code == HttpStatus.SC_OK) {
143                     logResult(TestResult.OK, options, Objects.toString(response.getFirstHeader("server")));
144                 } else {
145                     logResult(TestResult.NOK, options, "(status " + code + ")");
146                 }
147             } catch (final ExecutionException ex) {
148                 final Throwable cause = ex.getCause();
149                 logResult(TestResult.NOK, options, "(" + cause.getMessage() + ")");
150             } catch (final TimeoutException ex) {
151                 logResult(TestResult.NOK, options, "(time out)");
152             }
153         }
154         // GET with links
155         {
156             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
157             final HttpCacheContext context = HttpCacheContext.create();
158 
159             final Pattern linkPattern = Pattern.compile("^<(.*)>;rel=preload$");
160             final List<String> links = new ArrayList<>();
161             final SimpleHttpRequest getRoot1 = SimpleRequestBuilder.get()
162                     .setHttpHost(target)
163                     .setPath("/")
164                     .build();
165             final Future<SimpleHttpResponse> future1 = client.execute(getRoot1, context, null);
166             try {
167                 final SimpleHttpResponse response = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
168                 final int code = response.getCode();
169                 final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
170                 if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_MISS) {
171                     logResult(TestResult.OK, getRoot1, "200, " + cacheResponseStatus);
172                 } else {
173                     logResult(TestResult.NOK, getRoot1, "(status " + code + ", " + cacheResponseStatus + ")");
174                 }
175                 for (final Header header: response.getHeaders("Link")) {
176                     final Matcher matcher = linkPattern.matcher(header.getValue());
177                     if (matcher.matches()) {
178                         links.add(matcher.group(1));
179                     }
180                 }
181             } catch (final ExecutionException ex) {
182                 final Throwable cause = ex.getCause();
183                 logResult(TestResult.NOK, getRoot1, "(" + cause.getMessage() + ")");
184             } catch (final TimeoutException ex) {
185                 logResult(TestResult.NOK, getRoot1, "(time out)");
186             }
187             for (final String link: links) {
188                 final SimpleHttpRequest getLink = SimpleRequestBuilder.get()
189                         .setHttpHost(target)
190                         .setPath(link)
191                         .build();
192                 final Future<SimpleHttpResponse> linkFuture = client.execute(getLink, context, null);
193                 try {
194                     final SimpleHttpResponse response = linkFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
195                     final int code = response.getCode();
196                     final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
197                     if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_MISS) {
198                         logResult(TestResult.OK, getLink, "200, " + cacheResponseStatus);
199                     } else {
200                         logResult(TestResult.NOK, getLink, "(status " + code + ", " + cacheResponseStatus + ")");
201                     }
202                 } catch (final ExecutionException ex) {
203                     final Throwable cause = ex.getCause();
204                     logResult(TestResult.NOK, getLink, "(" + cause.getMessage() + ")");
205                 } catch (final TimeoutException ex) {
206                     logResult(TestResult.NOK, getLink, "(time out)");
207                 }
208             }
209 
210             final SimpleHttpRequest getRoot2 = SimpleRequestBuilder.get()
211                     .setHttpHost(target)
212                     .setPath("/")
213                     .build();
214             final Future<SimpleHttpResponse> future2 = client.execute(getRoot2, context, null);
215             try {
216                 final SimpleHttpResponse response = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
217                 final int code = response.getCode();
218                 final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
219                 if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.VALIDATED) {
220                     logResult(TestResult.OK, getRoot2, "200, " + cacheResponseStatus);
221                 } else {
222                     logResult(TestResult.NOK, getRoot2, "(status " + code + ", " + cacheResponseStatus + ")");
223                 }
224             } catch (final ExecutionException ex) {
225                 final Throwable cause = ex.getCause();
226                 logResult(TestResult.NOK, getRoot2, "(" + cause.getMessage() + ")");
227             } catch (final TimeoutException ex) {
228                 logResult(TestResult.NOK, getRoot2, "(time out)");
229             }
230             for (final String link: links) {
231                 final SimpleHttpRequest getLink = SimpleRequestBuilder.get()
232                         .setHttpHost(target)
233                         .setPath(link)
234                         .build();
235                 final Future<SimpleHttpResponse> linkFuture = client.execute(getLink, context, null);
236                 try {
237                     final SimpleHttpResponse response = linkFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
238                     final int code = response.getCode();
239                     final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
240                     if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.VALIDATED) {
241                         logResult(TestResult.OK, getLink, "200, " + cacheResponseStatus);
242                     } else {
243                         logResult(TestResult.NOK, getLink, "(status " + code + ", " + cacheResponseStatus + ")");
244                     }
245                 } catch (final ExecutionException ex) {
246                     final Throwable cause = ex.getCause();
247                     logResult(TestResult.NOK, getLink, "(" + cause.getMessage() + ")");
248                 } catch (final TimeoutException ex) {
249                     logResult(TestResult.NOK, getLink, "(time out)");
250                 }
251             }
252         }
253     }
254 
255 }