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