blob: 8a27c4937e1636edfaa85ae4ffb11da67813d435 [file] [log] [blame]
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.http.impl.cache;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BasicHeaderIterator;
import org.apache.hc.core5.http.message.BasicHttpRequest;
import org.apache.hc.core5.http.support.BasicRequestBuilder;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@SuppressWarnings({"boxing","static-access"}) // this is test code
public class TestCacheKeyGenerator {
private CacheKeyGenerator extractor;
@BeforeEach
public void setUp() throws Exception {
extractor = CacheKeyGenerator.INSTANCE;
}
@Test
public void testGetRequestUri() {
Assertions.assertEquals("http://foo.example.com/stuff?huh",
CacheKeyGenerator.getRequestUri(
new HttpHost("bar.example.com"),
new HttpGet("http://foo.example.com/stuff?huh")));
Assertions.assertEquals("http://bar.example.com/stuff?huh",
CacheKeyGenerator.getRequestUri(
new HttpHost("bar.example.com"),
new HttpGet("/stuff?huh")));
Assertions.assertEquals("http://foo.example.com:8888/stuff?huh",
CacheKeyGenerator.getRequestUri(
new HttpHost("bar.example.com", 8080),
new HttpGet("http://foo.example.com:8888/stuff?huh")));
Assertions.assertEquals("https://bar.example.com:8443/stuff?huh",
CacheKeyGenerator.getRequestUri(
new HttpHost("https", "bar.example.com", 8443),
new HttpGet("/stuff?huh")));
Assertions.assertEquals("http://foo.example.com/",
CacheKeyGenerator.getRequestUri(
new HttpHost("bar.example.com"),
new HttpGet("http://foo.example.com")));
Assertions.assertEquals("http://bar.example.com/stuff?huh",
CacheKeyGenerator.getRequestUri(
new HttpHost("bar.example.com"),
new HttpGet("stuff?huh")));
}
@Test
public void testNormalizeRequestUri() throws URISyntaxException {
Assertions.assertEquals(URI.create("http://bar.example.com:80/stuff?huh"),
CacheKeyGenerator.normalize(URI.create("//bar.example.com/stuff?huh")));
Assertions.assertEquals(URI.create("http://bar.example.com:80/stuff?huh"),
CacheKeyGenerator.normalize(URI.create("http://bar.example.com/stuff?huh")));
Assertions.assertEquals(URI.create("http://bar.example.com:80/stuff?huh"),
CacheKeyGenerator.normalize(URI.create("http://bar.example.com/stuff?huh#there")));
Assertions.assertEquals(URI.create("http://bar.example.com:80/stuff?huh"),
CacheKeyGenerator.normalize(URI.create("HTTP://BAR.example.com/p1/p2/../../stuff?huh")));
}
@Test
public void testExtractsUriFromAbsoluteUriInRequest() {
final HttpHost host = new HttpHost("bar.example.com");
final HttpRequest req = new HttpGet("http://foo.example.com/");
Assertions.assertEquals("http://foo.example.com:80/", extractor.generateKey(host, req));
}
@Test
public void testGetURIWithDefaultPortAndScheme() {
Assertions.assertEquals("http://www.comcast.net:80/", extractor.generateKey(
new HttpHost("www.comcast.net"),
new BasicHttpRequest("GET", "/")));
Assertions.assertEquals("http://www.fancast.com:80/full_episodes", extractor.generateKey(
new HttpHost("www.fancast.com"),
new BasicHttpRequest("GET", "/full_episodes")));
}
@Test
public void testGetURIWithDifferentScheme() {
Assertions.assertEquals("https://www.comcast.net:443/", extractor.generateKey(
new HttpHost("https", "www.comcast.net", -1),
new BasicHttpRequest("GET", "/")));
Assertions.assertEquals("myhttp://www.fancast.com/full_episodes", extractor.generateKey(
new HttpHost("myhttp", "www.fancast.com", -1),
new BasicHttpRequest("GET", "/full_episodes")));
}
@Test
public void testGetURIWithDifferentPort() {
Assertions.assertEquals("http://www.comcast.net:8080/", extractor.generateKey(
new HttpHost("www.comcast.net", 8080),
new BasicHttpRequest("GET", "/")));
Assertions.assertEquals("http://www.fancast.com:9999/full_episodes", extractor.generateKey(
new HttpHost("www.fancast.com", 9999),
new BasicHttpRequest("GET", "/full_episodes")));
}
@Test
public void testGetURIWithDifferentPortAndScheme() {
Assertions.assertEquals("https://www.comcast.net:8080/", extractor.generateKey(
new HttpHost("https", "www.comcast.net", 8080),
new BasicHttpRequest("GET", "/")));
Assertions.assertEquals("myhttp://www.fancast.com:9999/full_episodes", extractor.generateKey(
new HttpHost("myhttp", "www.fancast.com", 9999),
new BasicHttpRequest("GET", "/full_episodes")));
}
@Test
public void testEmptyPortEquivalentToDefaultPortForHttp() {
final HttpHost host1 = new HttpHost("foo.example.com:");
final HttpHost host2 = new HttpHost("foo.example.com:80");
final HttpRequest req = new BasicHttpRequest("GET", "/");
Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req));
}
@Test
public void testEmptyPortEquivalentToDefaultPortForHttps() {
final HttpHost host1 = new HttpHost("https", "foo.example.com", -1);
final HttpHost host2 = new HttpHost("https", "foo.example.com", 443);
final HttpRequest req = new BasicHttpRequest("GET", "/");
final String uri1 = extractor.generateKey(host1, req);
final String uri2 = extractor.generateKey(host2, req);
Assertions.assertEquals(uri1, uri2);
}
@Test
public void testEmptyPortEquivalentToDefaultPortForHttpsAbsoluteURI() {
final HttpHost host = new HttpHost("https", "foo.example.com", -1);
final HttpGet get1 = new HttpGet("https://bar.example.com:/");
final HttpGet get2 = new HttpGet("https://bar.example.com:443/");
final String uri1 = extractor.generateKey(host, get1);
final String uri2 = extractor.generateKey(host, get2);
Assertions.assertEquals(uri1, uri2);
}
@Test
public void testNotProvidedPortEquivalentToDefaultPortForHttpsAbsoluteURI() {
final HttpHost host = new HttpHost("https", "foo.example.com", -1);
final HttpGet get1 = new HttpGet("https://bar.example.com/");
final HttpGet get2 = new HttpGet("https://bar.example.com:443/");
final String uri1 = extractor.generateKey(host, get1);
final String uri2 = extractor.generateKey(host, get2);
Assertions.assertEquals(uri1, uri2);
}
@Test
public void testNotProvidedPortEquivalentToDefaultPortForHttp() {
final HttpHost host1 = new HttpHost("foo.example.com");
final HttpHost host2 = new HttpHost("foo.example.com:80");
final HttpRequest req = new BasicHttpRequest("GET", "/");
Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req));
}
@Test
public void testHostNameComparisonsAreCaseInsensitive() {
final HttpHost host1 = new HttpHost("foo.example.com");
final HttpHost host2 = new HttpHost("FOO.EXAMPLE.COM");
final HttpRequest req = new BasicHttpRequest("GET", "/");
Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req));
}
@Test
public void testSchemeNameComparisonsAreCaseInsensitive() {
final HttpHost host1 = new HttpHost("http", "foo.example.com", -1);
final HttpHost host2 = new HttpHost("HTTP", "foo.example.com", -1);
final HttpRequest req = new BasicHttpRequest("GET", "/");
Assertions.assertEquals(extractor.generateKey(host1, req), extractor.generateKey(host2, req));
}
@Test
public void testEmptyAbsPathIsEquivalentToSlash() {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req1 = new BasicHttpRequest("GET", "/");
final HttpRequest req2 = new HttpGet("http://foo.example.com");
Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
}
@Test
public void testExtraDotSegmentsAreIgnored() {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req1 = new BasicHttpRequest("GET", "/");
final HttpRequest req2 = new HttpGet("http://foo.example.com/./");
Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
}
@Test
public void testExtraDotDotSegmentsAreIgnored() {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req1 = new BasicHttpRequest("GET", "/");
final HttpRequest req2 = new HttpGet("http://foo.example.com/.././../");
Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
}
@Test
public void testIntermidateDotDotSegementsAreEquivalent() {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req1 = new BasicHttpRequest("GET", "/home.html");
final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/../home.html");
Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
}
@Test
public void testIntermidateEncodedDotDotSegementsAreEquivalent() {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req1 = new BasicHttpRequest("GET", "/home.html");
final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/../home.html");
Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
}
@Test
public void testIntermidateDotSegementsAreEquivalent() {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html");
final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/./home.html");
Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
}
@Test
public void testEquivalentPathEncodingsAreEquivalent() {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html");
final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/home.html");
Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
}
@Test
public void testEquivalentExtraPathEncodingsAreEquivalent() {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html");
final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/home.html");
Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
}
@Test
public void testEquivalentExtraPathEncodingsWithPercentAreEquivalent() {
final HttpHost host = new HttpHost("foo.example.com");
final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home%20folder.html");
final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/home%20folder.html");
Assertions.assertEquals(extractor.generateKey(host, req1), extractor.generateKey(host, req2));
}
@Test
public void testGetURIWithQueryParameters() {
Assertions.assertEquals("http://www.comcast.net:80/?foo=bar", extractor.generateKey(
new HttpHost("http", "www.comcast.net", -1), new BasicHttpRequest("GET", "/?foo=bar")));
Assertions.assertEquals("http://www.fancast.com:80/full_episodes?foo=bar", extractor.generateKey(
new HttpHost("http", "www.fancast.com", -1), new BasicHttpRequest("GET",
"/full_episodes?foo=bar")));
}
private static Iterator<Header> headers(final Header... headers) {
return new BasicHeaderIterator(headers, null);
}
@Test
public void testNormalizeHeaderElements() {
final List<String> tokens = new ArrayList<>();
CacheKeyGenerator.normalizeElements(headers(
new BasicHeader("Accept-Encoding", "gzip,zip,deflate")
), tokens::add);
Assertions.assertEquals(Arrays.asList("deflate", "gzip", "zip"), tokens);
tokens.clear();
CacheKeyGenerator.normalizeElements(headers(
new BasicHeader("Accept-Encoding", " gZip , Zip, , , deflate ")
), tokens::add);
Assertions.assertEquals(Arrays.asList("deflate", "gzip", "zip"), tokens);
tokens.clear();
CacheKeyGenerator.normalizeElements(headers(
new BasicHeader("Accept-Encoding", "gZip,Zip,,"),
new BasicHeader("Accept-Encoding", " gZip,Zip,,,"),
new BasicHeader("Accept-Encoding", "gZip, ,,,deflate")
), tokens::add);
Assertions.assertEquals(Arrays.asList("deflate", "gzip", "zip"), tokens);
tokens.clear();
CacheKeyGenerator.normalizeElements(headers(
new BasicHeader("Cookie", "name1 = value1 ; p1 = v1 ; P2 = \"v2\""),
new BasicHeader("Cookie", "name3;;;"),
new BasicHeader("Cookie", " name2 = \" value 2 \" ; ; ; ,,,")
), tokens::add);
Assertions.assertEquals(Arrays.asList("name1=value1;p1=v1;p2=v2", "name2=\" value 2 \"", "name3"), tokens);
}
@Test
public void testGetVariantKey() {
final HttpRequest request = BasicRequestBuilder.get("/blah")
.addHeader(HttpHeaders.USER_AGENT, "some-agent")
.addHeader(HttpHeaders.ACCEPT_ENCODING, "gzip,zip")
.addHeader(HttpHeaders.ACCEPT_ENCODING, "deflate")
.build();
Assertions.assertEquals("{user-agent=some-agent}",
extractor.generateVariantKey(request, Collections.singletonList(HttpHeaders.USER_AGENT)));
Assertions.assertEquals("{accept-encoding=deflate,gzip,zip}",
extractor.generateVariantKey(request, Collections.singletonList(HttpHeaders.ACCEPT_ENCODING)));
Assertions.assertEquals("{accept-encoding=deflate,gzip,zip&user-agent=some-agent}",
extractor.generateVariantKey(request, Arrays.asList(HttpHeaders.USER_AGENT, HttpHeaders.ACCEPT_ENCODING)));
}
@Test
public void testGetVariantKeyInputNormalization() {
final HttpRequest request = BasicRequestBuilder.get("/blah")
.addHeader(HttpHeaders.USER_AGENT, "Some-Agent")
.addHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, ZIP,,")
.addHeader(HttpHeaders.ACCEPT_ENCODING, "deflate")
.build();
Assertions.assertEquals("{user-agent=some-agent}",
extractor.generateVariantKey(request, Collections.singletonList(HttpHeaders.USER_AGENT)));
Assertions.assertEquals("{accept-encoding=deflate,gzip,zip}",
extractor.generateVariantKey(request, Collections.singletonList(HttpHeaders.ACCEPT_ENCODING)));
Assertions.assertEquals("{accept-encoding=deflate,gzip,zip&user-agent=some-agent}",
extractor.generateVariantKey(request, Arrays.asList(HttpHeaders.USER_AGENT, HttpHeaders.ACCEPT_ENCODING)));
Assertions.assertEquals("{accept-encoding=deflate,gzip,zip&user-agent=some-agent}",
extractor.generateVariantKey(request, Arrays.asList(HttpHeaders.USER_AGENT, HttpHeaders.ACCEPT_ENCODING, "USER-AGENT", HttpHeaders.ACCEPT_ENCODING)));
}
@Test
public void testGetVariantKeyInputNormalizationReservedChars() {
final HttpRequest request = BasicRequestBuilder.get("/blah")
.addHeader(HttpHeaders.USER_AGENT, "*===some-agent===*")
.build();
Assertions.assertEquals("{user-agent=%2A%3D%3D%3Dsome-agent%3D%3D%3D%2A}",
extractor.generateVariantKey(request, Collections.singletonList(HttpHeaders.USER_AGENT)));
}
@Test
public void testGetVariantKeyInputNoMatchingHeaders() {
final HttpRequest request = BasicRequestBuilder.get("/blah")
.build();
Assertions.assertEquals("{accept-encoding=&user-agent=}",
extractor.generateVariantKey(request, Arrays.asList(HttpHeaders.ACCEPT_ENCODING, HttpHeaders.USER_AGENT)));
}
@Test
public void testGetVariantKeyFromCachedResponse() {
final HttpRequest request = BasicRequestBuilder.get("/blah")
.addHeader("User-Agent", "agent1")
.addHeader("Accept-Encoding", "text/plain")
.build();
final HttpCacheEntry entry1 = HttpTestUtils.makeCacheEntry();
Assertions.assertNull(extractor.generateVariantKey(request, entry1));
final HttpCacheEntry entry2 = HttpTestUtils.makeCacheEntry(
new BasicHeader("Vary", "User-Agent, Accept-Encoding")
);
Assertions.assertEquals("{accept-encoding=text%2Fplain&user-agent=agent1}", extractor.generateVariantKey(request, entry2));
}
}