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.http.impl.cache;
28
29 import java.net.URI;
30 import java.net.URISyntaxException;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collections;
34 import java.util.Iterator;
35 import java.util.List;
36
37 import org.apache.hc.client5.http.cache.HttpCacheEntry;
38 import org.apache.hc.client5.http.classic.methods.HttpGet;
39 import org.apache.hc.core5.http.Header;
40 import org.apache.hc.core5.http.HttpHeaders;
41 import org.apache.hc.core5.http.HttpHost;
42 import org.apache.hc.core5.http.HttpRequest;
43 import org.apache.hc.core5.http.message.BasicHeader;
44 import org.apache.hc.core5.http.message.BasicHeaderIterator;
45 import org.apache.hc.core5.http.message.BasicHttpRequest;
46 import org.apache.hc.core5.http.support.BasicRequestBuilder;
47 import org.junit.jupiter.api.Assertions;
48 import org.junit.jupiter.api.BeforeEach;
49 import org.junit.jupiter.api.Test;
50
51 @SuppressWarnings({"boxing","static-access"})
52 public class TestCacheKeyGenerator {
53
54 private CacheKeyGenerator extractor;
55
56 @BeforeEach
57 public void setUp() throws Exception {
58 extractor = CacheKeyGenerator.INSTANCE;
59 }
60
61 @Test
62 public void testGetRequestUri() {
63 Assertions.assertEquals("http://foo.example.com/stuff?huh",
64 CacheKeyGenerator.getRequestUri(
65 new HttpHost("bar.example.com"),
66 new HttpGet("http://foo.example.com/stuff?huh")));
67
68 Assertions.assertEquals("http://bar.example.com/stuff?huh",
69 CacheKeyGenerator.getRequestUri(
70 new HttpHost("bar.example.com"),
71 new HttpGet("/stuff?huh")));
72
73 Assertions.assertEquals("http://foo.example.com:8888/stuff?huh",
74 CacheKeyGenerator.getRequestUri(
75 new HttpHost("bar.example.com", 8080),
76 new HttpGet("http://foo.example.com:8888/stuff?huh")));
77
78 Assertions.assertEquals("https://bar.example.com:8443/stuff?huh",
79 CacheKeyGenerator.getRequestUri(
80 new HttpHost("https", "bar.example.com", 8443),
81 new HttpGet("/stuff?huh")));
82
83 Assertions.assertEquals("http://foo.example.com/",
84 CacheKeyGenerator.getRequestUri(
85 new HttpHost("bar.example.com"),
86 new HttpGet("http://foo.example.com")));
87
88 Assertions.assertEquals("http://bar.example.com/stuff?huh",
89 CacheKeyGenerator.getRequestUri(
90 new HttpHost("bar.example.com"),
91 new HttpGet("stuff?huh")));
92 }
93
94 @Test
95 public void testNormalizeRequestUri() throws URISyntaxException {
96 Assertions.assertEquals(URI.create("http://bar.example.com:80/stuff?huh"),
97 CacheKeyGenerator.normalize(URI.create("//bar.example.com/stuff?huh")));
98
99 Assertions.assertEquals(URI.create("http://bar.example.com:80/stuff?huh"),
100 CacheKeyGenerator.normalize(URI.create("http://bar.example.com/stuff?huh")));
101
102 Assertions.assertEquals(URI.create("http://bar.example.com:80/stuff?huh"),
103 CacheKeyGenerator.normalize(URI.create("http://bar.example.com/stuff?huh#there")));
104
105 Assertions.assertEquals(URI.create("http://bar.example.com:80/stuff?huh"),
106 CacheKeyGenerator.normalize(URI.create("HTTP://BAR.example.com/p1/p2/../../stuff?huh")));
107 }
108
109 @Test
110 public void testExtractsUriFromAbsoluteUriInRequest() {
111 final HttpHost host = new HttpHost("bar.example.com");
112 final HttpRequest req = new HttpGet("http://foo.example.com/");
113 Assertions.assertEquals("http://foo.example.com:80/", extractor.generateKey(host, req));
114 }
115
116 @Test
117 public void testGetURIWithDefaultPortAndScheme() {
118 Assertions.assertEquals("http://www.comcast.net:80/", extractor.generateKey(
119 new HttpHost("www.comcast.net"),
120 new BasicHttpRequest("GET", "/")));
121
122 Assertions.assertEquals("http://www.fancast.com:80/full_episodes", extractor.generateKey(
123 new HttpHost("www.fancast.com"),
124 new BasicHttpRequest("GET", "/full_episodes")));
125 }
126
127 @Test
128 public void testGetURIWithDifferentScheme() {
129 Assertions.assertEquals("https://www.comcast.net:443/", extractor.generateKey(
130 new HttpHost("https", "www.comcast.net", -1),
131 new BasicHttpRequest("GET", "/")));
132
133 Assertions.assertEquals("myhttp://www.fancast.com/full_episodes", extractor.generateKey(
134 new HttpHost("myhttp", "www.fancast.com", -1),
135 new BasicHttpRequest("GET", "/full_episodes")));
136 }
137
138 @Test
139 public void testGetURIWithDifferentPort() {
140 Assertions.assertEquals("http://www.comcast.net:8080/", extractor.generateKey(
141 new HttpHost("www.comcast.net", 8080),
142 new BasicHttpRequest("GET", "/")));
143
144 Assertions.assertEquals("http://www.fancast.com:9999/full_episodes", extractor.generateKey(
145 new HttpHost("www.fancast.com", 9999),
146 new BasicHttpRequest("GET", "/full_episodes")));
147 }
148
149 @Test
150 public void testGetURIWithDifferentPortAndScheme() {
151 Assertions.assertEquals("https://www.comcast.net:8080/", extractor.generateKey(
152 new HttpHost("https", "www.comcast.net", 8080),
153 new BasicHttpRequest("GET", "/")));
154
155 Assertions.assertEquals("myhttp://www.fancast.com:9999/full_episodes", extractor.generateKey(
156 new HttpHost("myhttp", "www.fancast.com", 9999),
157 new BasicHttpRequest("GET", "/full_episodes")));
158 }
159
160 @Test
161 public void testEmptyPortEquivalentToDefaultPortForHttp() {
162 final HttpHost host1 = new HttpHost("foo.example.com:");
163 final HttpHost host2 = new HttpHost("foo.example.com:80");
164 final HttpRequest req = new BasicHttpRequest("GET", "/");
165 Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req));
166 }
167
168 @Test
169 public void testEmptyPortEquivalentToDefaultPortForHttps() {
170 final HttpHost host1 = new HttpHost("https", "foo.example.com", -1);
171 final HttpHost host2 = new HttpHost("https", "foo.example.com", 443);
172 final HttpRequest req = new BasicHttpRequest("GET", "/");
173 final String uri1 = extractor.generateKey(host1, req);
174 final String uri2 = extractor.generateKey(host2, req);
175 Assertions.assertEquals(uri1, uri2);
176 }
177
178 @Test
179 public void testEmptyPortEquivalentToDefaultPortForHttpsAbsoluteURI() {
180 final HttpHost host = new HttpHost("https", "foo.example.com", -1);
181 final HttpGet get1 = new HttpGet("https://bar.example.com:/");
182 final HttpGet get2 = new HttpGet("https://bar.example.com:443/");
183 final String uri1 = extractor.generateKey(host, get1);
184 final String uri2 = extractor.generateKey(host, get2);
185 Assertions.assertEquals(uri1, uri2);
186 }
187
188 @Test
189 public void testNotProvidedPortEquivalentToDefaultPortForHttpsAbsoluteURI() {
190 final HttpHost host = new HttpHost("https", "foo.example.com", -1);
191 final HttpGet get1 = new HttpGet("https://bar.example.com/");
192 final HttpGet get2 = new HttpGet("https://bar.example.com:443/");
193 final String uri1 = extractor.generateKey(host, get1);
194 final String uri2 = extractor.generateKey(host, get2);
195 Assertions.assertEquals(uri1, uri2);
196 }
197
198 @Test
199 public void testNotProvidedPortEquivalentToDefaultPortForHttp() {
200 final HttpHost host1 = new HttpHost("foo.example.com");
201 final HttpHost host2 = new HttpHost("foo.example.com:80");
202 final HttpRequest req = new BasicHttpRequest("GET", "/");
203 Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req));
204 }
205
206 @Test
207 public void testHostNameComparisonsAreCaseInsensitive() {
208 final HttpHost host1 = new HttpHost("foo.example.com");
209 final HttpHost host2 = new HttpHost("FOO.EXAMPLE.COM");
210 final HttpRequest req = new BasicHttpRequest("GET", "/");
211 Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req));
212 }
213
214 @Test
215 public void testSchemeNameComparisonsAreCaseInsensitive() {
216 final HttpHost host1 = new HttpHost("http", "foo.example.com", -1);
217 final HttpHost host2 = new HttpHost("HTTP", "foo.example.com", -1);
218 final HttpRequest req = new BasicHttpRequest("GET", "/");
219 Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req));
220 }
221
222 @Test
223 public void testEmptyAbsPathIsEquivalentToSlash() {
224 final HttpHost host = new HttpHost("foo.example.com");
225 final HttpRequest req1 = new BasicHttpRequest("GET", "/");
226 final HttpRequest req2 = new HttpGet("http://foo.example.com");
227 Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
228 }
229
230 @Test
231 public void testExtraDotSegmentsAreIgnored() {
232 final HttpHost host = new HttpHost("foo.example.com");
233 final HttpRequest req1 = new BasicHttpRequest("GET", "/");
234 final HttpRequest req2 = new HttpGet("http://foo.example.com/./");
235 Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
236 }
237
238 @Test
239 public void testExtraDotDotSegmentsAreIgnored() {
240 final HttpHost host = new HttpHost("foo.example.com");
241 final HttpRequest req1 = new BasicHttpRequest("GET", "/");
242 final HttpRequest req2 = new HttpGet("http://foo.example.com/.././../");
243 Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
244 }
245
246 @Test
247 public void testIntermidateDotDotSegementsAreEquivalent() {
248 final HttpHost host = new HttpHost("foo.example.com");
249 final HttpRequest req1 = new BasicHttpRequest("GET", "/home.html");
250 final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/../home.html");
251 Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
252 }
253
254 @Test
255 public void testIntermidateEncodedDotDotSegementsAreEquivalent() {
256 final HttpHost host = new HttpHost("foo.example.com");
257 final HttpRequest req1 = new BasicHttpRequest("GET", "/home.html");
258 final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/../home.html");
259 Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
260 }
261
262 @Test
263 public void testIntermidateDotSegementsAreEquivalent() {
264 final HttpHost host = new HttpHost("foo.example.com");
265 final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html");
266 final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/./home.html");
267 Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
268 }
269
270 @Test
271 public void testEquivalentPathEncodingsAreEquivalent() {
272 final HttpHost host = new HttpHost("foo.example.com");
273 final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html");
274 final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/home.html");
275 Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
276 }
277
278 @Test
279 public void testEquivalentExtraPathEncodingsAreEquivalent() {
280 final HttpHost host = new HttpHost("foo.example.com");
281 final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html");
282 final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/home.html");
283 Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
284 }
285
286 @Test
287 public void testEquivalentExtraPathEncodingsWithPercentAreEquivalent() {
288 final HttpHost host = new HttpHost("foo.example.com");
289 final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home%20folder.html");
290 final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/home%20folder.html");
291 Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
292 }
293
294 @Test
295 public void testGetURIWithQueryParameters() {
296 Assertions.assertEquals("http://www.comcast.net:80/?foo=bar", extractor.generateKey(
297 new HttpHost("http", "www.comcast.net", -1), new BasicHttpRequest("GET", "/?foo=bar")));
298 Assertions.assertEquals("http://www.fancast.com:80/full_episodes?foo=bar", extractor.generateKey(
299 new HttpHost("http", "www.fancast.com", -1), new BasicHttpRequest("GET",
300 "/full_episodes?foo=bar")));
301 }
302
303 private static Iterator<Header> headers(final Header... headers) {
304 return new BasicHeaderIterator(headers, null);
305 }
306
307 @Test
308 public void testNormalizeHeaderElements() {
309 final List<String> tokens = new ArrayList<>();
310 CacheKeyGenerator.normalizeElements(headers(
311 new BasicHeader("Accept-Encoding", "gzip,zip,deflate")
312 ), tokens::add);
313 Assertions.assertEquals(Arrays.asList("deflate", "gzip", "zip"), tokens);
314
315 tokens.clear();
316 CacheKeyGenerator.normalizeElements(headers(
317 new BasicHeader("Accept-Encoding", " gZip , Zip, , , deflate ")
318 ), tokens::add);
319 Assertions.assertEquals(Arrays.asList("deflate", "gzip", "zip"), tokens);
320
321 tokens.clear();
322 CacheKeyGenerator.normalizeElements(headers(
323 new BasicHeader("Accept-Encoding", "gZip,Zip,,"),
324 new BasicHeader("Accept-Encoding", " gZip,Zip,,,"),
325 new BasicHeader("Accept-Encoding", "gZip, ,,,deflate")
326 ), tokens::add);
327 Assertions.assertEquals(Arrays.asList("deflate", "gzip", "zip"), tokens);
328
329 tokens.clear();
330 CacheKeyGenerator.normalizeElements(headers(
331 new BasicHeader("Cookie", "name1 = value1 ; p1 = v1 ; P2 = \"v2\""),
332 new BasicHeader("Cookie", "name3;;;"),
333 new BasicHeader("Cookie", " name2 = \" value 2 \" ; ; ; ,,,")
334 ), tokens::add);
335 Assertions.assertEquals(Arrays.asList("name1=value1;p1=v1;p2=v2", "name2=\" value 2 \"", "name3"), tokens);
336 }
337
338 @Test
339 public void testGetVariantKey() {
340 final HttpRequest request = BasicRequestBuilder.get("/blah")
341 .addHeader(HttpHeaders.USER_AGENT, "some-agent")
342 .addHeader(HttpHeaders.ACCEPT_ENCODING, "gzip,zip")
343 .addHeader(HttpHeaders.ACCEPT_ENCODING, "deflate")
344 .build();
345
346 Assertions.assertEquals("{user-agent=some-agent}",
347 extractor.generateVariantKey(request, Collections.singletonList(HttpHeaders.USER_AGENT)));
348 Assertions.assertEquals("{accept-encoding=deflate,gzip,zip}",
349 extractor.generateVariantKey(request, Collections.singletonList(HttpHeaders.ACCEPT_ENCODING)));
350 Assertions.assertEquals("{accept-encoding=deflate,gzip,zip&user-agent=some-agent}",
351 extractor.generateVariantKey(request, Arrays.asList(HttpHeaders.USER_AGENT, HttpHeaders.ACCEPT_ENCODING)));
352 }
353
354 @Test
355 public void testGetVariantKeyInputNormalization() {
356 final HttpRequest request = BasicRequestBuilder.get("/blah")
357 .addHeader(HttpHeaders.USER_AGENT, "Some-Agent")
358 .addHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, ZIP,,")
359 .addHeader(HttpHeaders.ACCEPT_ENCODING, "deflate")
360 .build();
361
362 Assertions.assertEquals("{user-agent=some-agent}",
363 extractor.generateVariantKey(request, Collections.singletonList(HttpHeaders.USER_AGENT)));
364 Assertions.assertEquals("{accept-encoding=deflate,gzip,zip}",
365 extractor.generateVariantKey(request, Collections.singletonList(HttpHeaders.ACCEPT_ENCODING)));
366 Assertions.assertEquals("{accept-encoding=deflate,gzip,zip&user-agent=some-agent}",
367 extractor.generateVariantKey(request, Arrays.asList(HttpHeaders.USER_AGENT, HttpHeaders.ACCEPT_ENCODING)));
368 Assertions.assertEquals("{accept-encoding=deflate,gzip,zip&user-agent=some-agent}",
369 extractor.generateVariantKey(request, Arrays.asList(HttpHeaders.USER_AGENT, HttpHeaders.ACCEPT_ENCODING, "USER-AGENT", HttpHeaders.ACCEPT_ENCODING)));
370 }
371
372 @Test
373 public void testGetVariantKeyInputNormalizationReservedChars() {
374 final HttpRequest request = BasicRequestBuilder.get("/blah")
375 .addHeader(HttpHeaders.USER_AGENT, "*===some-agent===*")
376 .build();
377
378 Assertions.assertEquals("{user-agent=%2A%3D%3D%3Dsome-agent%3D%3D%3D%2A}",
379 extractor.generateVariantKey(request, Collections.singletonList(HttpHeaders.USER_AGENT)));
380 }
381
382 @Test
383 public void testGetVariantKeyInputNoMatchingHeaders() {
384 final HttpRequest request = BasicRequestBuilder.get("/blah")
385 .build();
386
387 Assertions.assertEquals("{accept-encoding=&user-agent=}",
388 extractor.generateVariantKey(request, Arrays.asList(HttpHeaders.ACCEPT_ENCODING, HttpHeaders.USER_AGENT)));
389 }
390
391 @Test
392 public void testGetVariantKeyFromCachedResponse() {
393 final HttpRequest request = BasicRequestBuilder.get("/blah")
394 .addHeader("User-Agent", "agent1")
395 .addHeader("Accept-Encoding", "text/plain")
396 .build();
397
398 final HttpCacheEntry entry1 = HttpTestUtils.makeCacheEntry();
399 Assertions.assertNull(extractor.generateVariantKey(request, entry1));
400
401 final HttpCacheEntry entry2 = HttpTestUtils.makeCacheEntry(
402 new BasicHeader("Vary", "User-Agent, Accept-Encoding")
403 );
404 Assertions.assertEquals("{accept-encoding=text%2Fplain&user-agent=agent1}", extractor.generateVariantKey(request, entry2));
405 }
406
407 }