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.SimpleHttpRequests;
42  import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
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 = SimpleHttpRequests.options(target, "*");
135             final Future<SimpleHttpResponse> future = client.execute(options, context, null);
136             try {
137                 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
138                 final int code = response.getCode();
139                 if (code == HttpStatus.SC_OK) {
140                     logResult(TestResult.OK, options, Objects.toString(response.getFirstHeader("server")));
141                 } else {
142                     logResult(TestResult.NOK, options, "(status " + code + ")");
143                 }
144             } catch (final ExecutionException ex) {
145                 final Throwable cause = ex.getCause();
146                 logResult(TestResult.NOK, options, "(" + cause.getMessage() + ")");
147             } catch (final TimeoutException ex) {
148                 logResult(TestResult.NOK, options, "(time out)");
149             }
150         }
151         // GET with links
152         {
153             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
154             final HttpCacheContext context = HttpCacheContext.create();
155 
156             final Pattern linkPattern = Pattern.compile("^<(.*)>;rel=preload$");
157             final List<String> links = new ArrayList<>();
158             final SimpleHttpRequest getRoot1 = SimpleHttpRequests.get(target, "/");
159             final Future<SimpleHttpResponse> future1 = client.execute(getRoot1, context, null);
160             try {
161                 final SimpleHttpResponse response = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
162                 final int code = response.getCode();
163                 final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
164                 if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_MISS) {
165                     logResult(TestResult.OK, getRoot1, "200, " + cacheResponseStatus);
166                 } else {
167                     logResult(TestResult.NOK, getRoot1, "(status " + code + ", " + cacheResponseStatus + ")");
168                 }
169                 for (final Header header: response.getHeaders("Link")) {
170                     final Matcher matcher = linkPattern.matcher(header.getValue());
171                     if (matcher.matches()) {
172                         links.add(matcher.group(1));
173                     }
174                 }
175             } catch (final ExecutionException ex) {
176                 final Throwable cause = ex.getCause();
177                 logResult(TestResult.NOK, getRoot1, "(" + cause.getMessage() + ")");
178             } catch (final TimeoutException ex) {
179                 logResult(TestResult.NOK, getRoot1, "(time out)");
180             }
181             for (final String link: links) {
182                 final SimpleHttpRequest getLink = SimpleHttpRequests.get(target, link);
183                 final Future<SimpleHttpResponse> linkFuture = client.execute(getLink, context, null);
184                 try {
185                     final SimpleHttpResponse response = linkFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
186                     final int code = response.getCode();
187                     final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
188                     if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_MISS) {
189                         logResult(TestResult.OK, getLink, "200, " + cacheResponseStatus);
190                     } else {
191                         logResult(TestResult.NOK, getLink, "(status " + code + ", " + cacheResponseStatus + ")");
192                     }
193                 } catch (final ExecutionException ex) {
194                     final Throwable cause = ex.getCause();
195                     logResult(TestResult.NOK, getLink, "(" + cause.getMessage() + ")");
196                 } catch (final TimeoutException ex) {
197                     logResult(TestResult.NOK, getLink, "(time out)");
198                 }
199             }
200 
201             final SimpleHttpRequest getRoot2 = SimpleHttpRequests.get(target, "/");
202             final Future<SimpleHttpResponse> future2 = client.execute(getRoot2, context, null);
203             try {
204                 final SimpleHttpResponse response = future2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
205                 final int code = response.getCode();
206                 final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
207                 if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.VALIDATED) {
208                     logResult(TestResult.OK, getRoot2, "200, " + cacheResponseStatus);
209                 } else {
210                     logResult(TestResult.NOK, getRoot2, "(status " + code + ", " + cacheResponseStatus + ")");
211                 }
212             } catch (final ExecutionException ex) {
213                 final Throwable cause = ex.getCause();
214                 logResult(TestResult.NOK, getRoot2, "(" + cause.getMessage() + ")");
215             } catch (final TimeoutException ex) {
216                 logResult(TestResult.NOK, getRoot2, "(time out)");
217             }
218             for (final String link: links) {
219                 final SimpleHttpRequest getLink = SimpleHttpRequests.get(target, link);
220                 final Future<SimpleHttpResponse> linkFuture = client.execute(getLink, context, null);
221                 try {
222                     final SimpleHttpResponse response = linkFuture.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
223                     final int code = response.getCode();
224                     final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
225                     if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.VALIDATED) {
226                         logResult(TestResult.OK, getLink, "200, " + cacheResponseStatus);
227                     } else {
228                         logResult(TestResult.NOK, getLink, "(status " + code + ", " + cacheResponseStatus + ")");
229                     }
230                 } catch (final ExecutionException ex) {
231                     final Throwable cause = ex.getCause();
232                     logResult(TestResult.NOK, getLink, "(" + cause.getMessage() + ")");
233                 } catch (final TimeoutException ex) {
234                     logResult(TestResult.NOK, getLink, "(time out)");
235                 }
236             }
237         }
238     }
239 
240 }