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 org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
35  import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
36  import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
37  import org.apache.hc.client5.http.cache.CacheResponseStatus;
38  import org.apache.hc.client5.http.cache.HttpCacheContext;
39  import org.apache.hc.client5.http.config.TlsConfig;
40  import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
41  import org.apache.hc.client5.http.impl.cache.CacheConfig;
42  import org.apache.hc.client5.http.impl.cache.CachingHttpAsyncClients;
43  import org.apache.hc.client5.http.impl.cache.HeapResourceFactory;
44  import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
45  import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
46  import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
47  import org.apache.hc.core5.http.HttpHeaders;
48  import org.apache.hc.core5.http.HttpHost;
49  import org.apache.hc.core5.http.HttpRequest;
50  import org.apache.hc.core5.http.HttpResponse;
51  import org.apache.hc.core5.http.HttpStatus;
52  import org.apache.hc.core5.http.HttpVersion;
53  import org.apache.hc.core5.http2.HttpVersionPolicy;
54  import org.apache.hc.core5.ssl.SSLContexts;
55  import org.apache.hc.core5.util.TextUtils;
56  import org.apache.hc.core5.util.TimeValue;
57  import org.apache.hc.core5.util.Timeout;
58  
59  public class CachingHttpAsyncClientCompatibilityTest {
60  
61      public static void main(final String... args) throws Exception {
62          final CachingHttpAsyncClientCompatibilityTest[] tests = new CachingHttpAsyncClientCompatibilityTest[] {
63                  new CachingHttpAsyncClientCompatibilityTest(
64                          HttpVersion.HTTP_1_1, new HttpHost("http", "localhost", 8080)),
65                  new CachingHttpAsyncClientCompatibilityTest(
66                          HttpVersion.HTTP_2_0, new HttpHost("http", "localhost", 8080))
67          };
68          for (final CachingHttpAsyncClientCompatibilityTest test: tests) {
69              try {
70                  test.execute();
71              } finally {
72                  test.shutdown();
73              }
74          }
75      }
76  
77      private static final Timeout TIMEOUT = Timeout.ofSeconds(5);
78  
79      private final HttpVersion protocolVersion;
80      private final HttpHost target;
81      private final PoolingAsyncClientConnectionManager connManager;
82      private final CloseableHttpAsyncClient client;
83  
84      CachingHttpAsyncClientCompatibilityTest(final HttpVersion protocolVersion, final HttpHost target) throws Exception {
85          this.protocolVersion = protocolVersion;
86          this.target = target;
87          this.connManager = PoolingAsyncClientConnectionManagerBuilder.create()
88                  .setTlsStrategy(new DefaultClientTlsStrategy(SSLContexts.custom()
89                          .loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray())
90                          .build()))
91                  .setDefaultTlsConfig(TlsConfig.custom()
92                          .setVersionPolicy(this.protocolVersion == HttpVersion.HTTP_2 ?
93                                  HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1)
94                          .build())
95                  .build();
96          this.client = CachingHttpAsyncClients.custom()
97                  .setCacheConfig(CacheConfig.custom()
98                          .setMaxObjectSize(20480)
99                          .setHeuristicCachingEnabled(true)
100                         .build())
101                 .setResourceFactory(HeapResourceFactory.INSTANCE)
102                 .setConnectionManager(this.connManager)
103                 .build();
104     }
105 
106     void shutdown() throws Exception {
107         client.close();
108     }
109 
110     enum TestResult {OK, NOK}
111 
112     private void logResult(final TestResult result,
113                            final HttpRequest request,
114                            final HttpResponse response,
115                            final String message) {
116         final StringBuilder buf = new StringBuilder();
117         buf.append(result);
118         if (buf.length() == 2) {
119             buf.append(" ");
120         }
121         buf.append(": ");
122         if (response != null && response.getVersion() != null) {
123             buf.append(response.getVersion()).append(" ");
124         } else {
125             buf.append(protocolVersion).append(" ");
126         }
127         buf.append(target);
128         buf.append(": ");
129         buf.append(request.getMethod()).append(" ").append(request.getRequestUri());
130         if (message != null && !TextUtils.isBlank(message)) {
131             buf.append(" -> ").append(message);
132         }
133         System.out.println(buf);
134     }
135 
136     void execute() throws InterruptedException {
137 
138         client.start();
139         // Initial ping
140         {
141             final HttpCacheContext context = HttpCacheContext.create();
142             final SimpleHttpRequest options = SimpleRequestBuilder.options()
143                     .setHttpHost(target)
144                     .setPath("*")
145                     .build();
146             final Future<SimpleHttpResponse> future = client.execute(options, context, null);
147             try {
148                 final SimpleHttpResponse response = future.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
149                 final int code = response.getCode();
150                 if (code == HttpStatus.SC_OK) {
151                     logResult(TestResult.OK, options, response, Objects.toString(response.getFirstHeader("server")));
152                 } else {
153                     logResult(TestResult.NOK, options, response, "(status " + code + ")");
154                 }
155             } catch (final ExecutionException ex) {
156                 final Throwable cause = ex.getCause();
157                 logResult(TestResult.NOK, options, null, "(" + cause.getMessage() + ")");
158             } catch (final TimeoutException ex) {
159                 logResult(TestResult.NOK, options, null, "(time out)");
160             }
161         }
162 
163         // GET from cache
164         {
165             connManager.closeIdle(TimeValue.NEG_ONE_MILLISECOND);
166             final HttpCacheContext context = HttpCacheContext.create();
167 
168             final String[] links = {"/", "/css/hc-maven.css", "/images/logos/httpcomponents.png"};
169 
170             for (final String link: links) {
171                 final SimpleHttpRequest httpGet1 = SimpleRequestBuilder.get()
172                         .setHttpHost(target)
173                         .setPath(link)
174                         .build();
175                 final Future<SimpleHttpResponse> linkFuture1 = client.execute(httpGet1, context, null);
176                 try {
177                     final SimpleHttpResponse response = linkFuture1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
178                     final int code = response.getCode();
179                     final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
180                     if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_MISS) {
181                         logResult(TestResult.OK, httpGet1, response, "200, " + cacheResponseStatus);
182                     } else {
183                         logResult(TestResult.NOK, httpGet1, response, "(status " + code + ", " + cacheResponseStatus + ")");
184                     }
185                 } catch (final ExecutionException ex) {
186                     final Throwable cause = ex.getCause();
187                     logResult(TestResult.NOK, httpGet1, null, "(" + cause.getMessage() + ")");
188                 } catch (final TimeoutException ex) {
189                     logResult(TestResult.NOK, httpGet1,  null,"(time out)");
190                 }
191 
192                 final SimpleHttpRequest httpGet2 = SimpleRequestBuilder.get()
193                         .setHttpHost(target)
194                         .setPath(link)
195                         .build();
196                 final Future<SimpleHttpResponse> linkFuture2 = client.execute(httpGet2, context, null);
197                 try {
198                     final SimpleHttpResponse response = linkFuture2.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
199                     final int code = response.getCode();
200                     final CacheResponseStatus cacheResponseStatus = context.getCacheResponseStatus();
201                     if (code == HttpStatus.SC_OK && cacheResponseStatus == CacheResponseStatus.CACHE_HIT) {
202                         logResult(TestResult.OK, httpGet2, response, "200, " + cacheResponseStatus);
203                     } else {
204                         logResult(TestResult.NOK, httpGet2, response, "(status " + code + ", " + cacheResponseStatus + ")");
205                     }
206                 } catch (final ExecutionException ex) {
207                     final Throwable cause = ex.getCause();
208                     logResult(TestResult.NOK, httpGet2, null, "(" + cause.getMessage() + ")");
209                 } catch (final TimeoutException ex) {
210                     logResult(TestResult.NOK, httpGet2,  null,"(time out)");
211                 }
212 
213                 Thread.sleep(2000);
214 
215                 final SimpleHttpRequest httpGet3 = SimpleRequestBuilder.get()
216                         .setHttpHost(target)
217                         .setPath(link)
218                         .setHeader(HttpHeaders.CACHE_CONTROL, "max-age=0")
219                         .build();
220                 final Future<SimpleHttpResponse> linkFuture3 = client.execute(httpGet3, context, null);
221                 try {
222                     final SimpleHttpResponse response = linkFuture3.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, httpGet3, response, "200, " + cacheResponseStatus);
227                     } else {
228                         logResult(TestResult.NOK, httpGet3, response, "(status " + code + ", " + cacheResponseStatus + ")");
229                     }
230                 } catch (final ExecutionException ex) {
231                     final Throwable cause = ex.getCause();
232                     logResult(TestResult.NOK, httpGet3, null, "(" + cause.getMessage() + ")");
233                 } catch (final TimeoutException ex) {
234                     logResult(TestResult.NOK, httpGet3,  null,"(time out)");
235                 }
236             }
237         }
238     }
239 
240 }