blob: 93a27f00c0edd4eeb478c0b13d50d6fbb6911334 [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 static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.Instant;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.ResponseCacheControl;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.util.TimeValue;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class TestCacheValidityPolicy {
private CacheValidityPolicy impl;
private Instant now;
private Instant oneSecondAgo;
private Instant sixSecondsAgo;
private Instant tenSecondsAgo;
private Instant elevenSecondsAgo;
@BeforeEach
public void setUp() {
impl = new CacheValidityPolicy();
now = Instant.now();
oneSecondAgo = now.minusSeconds(1);
sixSecondsAgo = now.minusSeconds(6);
tenSecondsAgo = now.minusSeconds(10);
elevenSecondsAgo = now.minusSeconds(11);
}
@Test
public void testApparentAgeIsMaxIntIfDateHeaderNotPresent() {
final Header[] headers = {
new BasicHeader("Server", "MockServer/1.0")
};
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
assertEquals(CacheSupport.MAX_AGE, impl.getApparentAge(entry));
}
@Test
public void testApparentAgeIsResponseReceivedTimeLessDateHeader() {
final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)) };
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, sixSecondsAgo, headers);
assertEquals(TimeValue.ofSeconds(4), impl.getApparentAge(entry));
}
@Test
public void testNegativeApparentAgeIsBroughtUpToZero() {
final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(sixSecondsAgo)) };
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, tenSecondsAgo, headers);
assertEquals(TimeValue.ofSeconds(0), impl.getApparentAge(entry));
}
@Test
public void testCorrectedReceivedAgeIsAgeHeaderIfLarger() {
final Header[] headers = new Header[] { new BasicHeader("Age", "10"), };
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
impl = new CacheValidityPolicy() {
@Override
protected TimeValue getApparentAge(final HttpCacheEntry ent) {
return TimeValue.ofSeconds(6);
}
};
assertEquals(TimeValue.ofSeconds(10), impl.getCorrectedAgeValue(entry));
}
@Test
public void testGetCorrectedAgeValue() {
final Header[] headers = new Header[] { new BasicHeader("Age", "6"), };
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
assertEquals(TimeValue.ofSeconds(6), impl.getCorrectedAgeValue(entry));
}
@Test
public void testResponseDelayIsDifferenceBetweenResponseAndRequestTimes() {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, sixSecondsAgo);
assertEquals(TimeValue.ofSeconds(4), impl.getResponseDelay(entry));
}
@Test
public void testCorrectedInitialAgeIsCorrectedReceivedAgePlusResponseDelay() {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
impl = new CacheValidityPolicy() {
@Override
protected TimeValue getCorrectedAgeValue(final HttpCacheEntry ent) {
return TimeValue.ofSeconds(7);
}
@Override
protected TimeValue getResponseDelay(final HttpCacheEntry ent) {
return TimeValue.ofSeconds(13);
}
};
assertEquals(TimeValue.ofSeconds(7), impl.getCorrectedInitialAge(entry));
}
@Test
public void testResidentTimeSecondsIsTimeSinceResponseTime() {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, sixSecondsAgo);
assertEquals(TimeValue.ofSeconds(6), impl.getResidentTime(entry, now));
}
@Test
public void testCurrentAgeIsCorrectedInitialAgePlusResidentTime() {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
impl = new CacheValidityPolicy() {
@Override
protected TimeValue getCorrectedInitialAge(final HttpCacheEntry ent) {
return TimeValue.ofSeconds(11);
}
@Override
protected TimeValue getResidentTime(final HttpCacheEntry ent, final Instant d) {
return TimeValue.ofSeconds(17);
}
};
assertEquals(TimeValue.ofSeconds(28), impl.getCurrentAge(entry, Instant.now()));
}
@Test
public void testFreshnessLifetimeIsSMaxAgeIfPresent() {
final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
.setSharedMaxAge(10)
.setMaxAge(5)
.build();
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(cacheControl, entry));
}
@Test
public void testSMaxAgeIsIgnoredWhenNotShared() {
final CacheConfig cacheConfig = CacheConfig.custom()
.setSharedCache(false)
.build();
impl = new CacheValidityPolicy(cacheConfig);
final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
.setSharedMaxAge(10)
.setMaxAge(5)
.build();
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
assertEquals(TimeValue.ofSeconds(5), impl.getFreshnessLifetime(cacheControl, entry));
}
@Test
public void testFreshnessLifetimeIsMaxAgeIfPresent() {
final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
.setMaxAge(10)
.build();
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(cacheControl, entry));
}
@Test
public void testFreshnessLifetimeUsesSharedMaxAgeInSharedCache() {
// assuming impl represents a shared cache
final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
.setMaxAge(10)
.setSharedMaxAge(20)
.build();
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
assertEquals(TimeValue.ofSeconds(20), impl.getFreshnessLifetime(cacheControl, entry));
}
@Test
public void testFreshnessLifetimeUsesMaxAgeWhenSharedMaxAgeNotPresent() {
// assuming impl represents a shared cache
final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
.setMaxAge(10)
.build();
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(cacheControl, entry));
}
@Test
public void testFreshnessLifetimeIsMaxAgeEvenIfExpiresIsPresent() {
final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
.setMaxAge(10)
.build();
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
new BasicHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)));
assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(cacheControl, entry));
}
@Test
public void testFreshnessLifetimeIsSMaxAgeEvenIfExpiresIsPresent() {
final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
.setSharedMaxAge(10)
.build();
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
new BasicHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)));
assertEquals(TimeValue.ofSeconds(10), impl.getFreshnessLifetime(cacheControl, entry));
}
@Test
public void testFreshnessLifetimeIsFromExpiresHeaderIfNoMaxAge() {
final ResponseCacheControl cacheControl = ResponseCacheControl.builder()
.build();
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
new BasicHeader("Date", DateUtils.formatStandardDate(tenSecondsAgo)),
new BasicHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)));
assertEquals(TimeValue.ofSeconds(4), impl.getFreshnessLifetime(cacheControl, entry));
}
@Test
public void testHeuristicFreshnessLifetime() {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
new BasicHeader("Date", DateUtils.formatStandardDate(oneSecondAgo)),
new BasicHeader("Last-Modified", DateUtils.formatStandardDate(elevenSecondsAgo)));
assertEquals(TimeValue.ofSeconds(1), impl.getHeuristicFreshnessLifetime(entry));
}
@Test
public void testHeuristicFreshnessLifetimeDefaultsProperly() {
final TimeValue defaultFreshness = TimeValue.ofSeconds(0);
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
assertEquals(defaultFreshness, impl.getHeuristicFreshnessLifetime(entry));
}
@Test
public void testHeuristicFreshnessLifetimeIsNonNegative() {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
new BasicHeader("Date", DateUtils.formatStandardDate(elevenSecondsAgo)),
new BasicHeader("Last-Modified", DateUtils.formatStandardDate(oneSecondAgo)));
assertTrue(TimeValue.isNonNegative(impl.getHeuristicFreshnessLifetime(entry)));
}
@Test
public void testHeuristicFreshnessLifetimeCustomProperly() {
final CacheConfig cacheConfig = CacheConfig.custom().setHeuristicDefaultLifetime(TimeValue.ofSeconds(10))
.setHeuristicCoefficient(0.5f).build();
impl = new CacheValidityPolicy(cacheConfig);
final TimeValue defaultFreshness = TimeValue.ofSeconds(10);
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
assertEquals(defaultFreshness, impl.getHeuristicFreshnessLifetime(entry));
}
@Test
public void testNegativeAgeHeaderValueReturnsZero() {
final Header[] headers = new Header[] { new BasicHeader("Age", "-100") };
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
// in seconds
assertEquals(0, impl.getAgeValue(entry));
}
@Test
public void testMalformedAgeHeaderValueReturnsMaxAge() {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
new BasicHeader("Age", "asdf"));
// in seconds
assertEquals(0, impl.getAgeValue(entry));
}
@Test
public void testMalformedAgeHeaderMultipleWellFormedAges() {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
new BasicHeader("Age", "123,456,789"));
// in seconds
assertEquals(123, impl.getAgeValue(entry));
}
@Test
public void testMalformedAgeHeaderMultiplesMalformedAges() {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
new BasicHeader("Age", "123 456 789"));
// in seconds
assertEquals(0, impl.getAgeValue(entry));
}
@Test
public void testMalformedAgeHeaderNegativeAge() {
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
new BasicHeader("Age", "-123"));
// in seconds
assertEquals(0, impl.getAgeValue(entry));
}
@Test
public void testMalformedAgeHeaderOverflow() {
final String reallyOldAge = "1" + Long.MAX_VALUE;
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(
new BasicHeader("Age", reallyOldAge));
// Expect the age value to be 0 in case of overflow
assertEquals(0, impl.getAgeValue(entry));
}
}