HttpCacheEntry to cache parsed DATE, EXPIRES and LAST_MODIFIED values; avoid parsing DATE header of cache entries and HTTP messages multiple times
This commit is contained in:
parent
e7ee13701e
commit
3ff5496ffb
|
@ -36,6 +36,7 @@ import java.util.HashSet;
|
|||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.hc.client5.http.utils.DateUtils;
|
||||
|
@ -74,6 +75,9 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
|
|||
private final HeaderGroup responseHeaders;
|
||||
private final Resource resource;
|
||||
private final Set<String> variants;
|
||||
private final AtomicReference<Instant> dateRef;
|
||||
private final AtomicReference<Instant> expiresRef;
|
||||
private final AtomicReference<Instant> lastModifiedRef;
|
||||
|
||||
/**
|
||||
* Internal constructor that makes no validation of the input parameters and makes
|
||||
|
@ -100,6 +104,9 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
|
|||
this.responseHeaders = responseHeaders;
|
||||
this.resource = resource;
|
||||
this.variants = variants != null ? Collections.unmodifiableSet(new HashSet<>(variants)) : null;
|
||||
this.dateRef = new AtomicReference<>();
|
||||
this.expiresRef = new AtomicReference<>();
|
||||
this.lastModifiedRef = new AtomicReference<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,6 +176,9 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
|
|||
this.responseHeaders.setHeaders(responseHeaders);
|
||||
this.resource = resource;
|
||||
this.variants = variantMap != null ? Collections.unmodifiableSet(new HashSet<>(variantMap.keySet())) : null;
|
||||
this.dateRef = new AtomicReference<>();
|
||||
this.expiresRef = new AtomicReference<>();
|
||||
this.lastModifiedRef = new AtomicReference<>();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -342,8 +352,41 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
|
|||
return DateUtils.toDate(getInstant());
|
||||
}
|
||||
|
||||
private static final Instant NON_VALUE = Instant.ofEpochSecond(Instant.MIN.getEpochSecond(), 0);
|
||||
|
||||
private Instant getInstant(final AtomicReference<Instant> ref, final String headerName) {
|
||||
Instant instant = ref.get();
|
||||
if (instant == null) {
|
||||
instant = DateUtils.parseStandardDate(this, headerName);
|
||||
if (instant == null) {
|
||||
instant = NON_VALUE;
|
||||
}
|
||||
if (!ref.compareAndSet(null, instant)) {
|
||||
instant = ref.get();
|
||||
}
|
||||
}
|
||||
return instant != null && instant != NON_VALUE ? instant : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.2
|
||||
*/
|
||||
public Instant getInstant() {
|
||||
return DateUtils.parseStandardDate(this, HttpHeaders.DATE);
|
||||
return getInstant(dateRef, HttpHeaders.DATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.4
|
||||
*/
|
||||
public Instant getExpires() {
|
||||
return getInstant(expiresRef, HttpHeaders.EXPIRES);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.4
|
||||
*/
|
||||
public Instant getLastModified() {
|
||||
return getInstant(lastModifiedRef, HttpHeaders.LAST_MODIFIED);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -404,6 +447,28 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
|
|||
return requestHeaders.headerIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given {@link HttpCacheEntry} is newer than the given {@link MessageHeaders}
|
||||
* by comparing values of their {@literal DATE} header. In case the given entry, or the message,
|
||||
* or their {@literal DATE} headers are null, this method returns {@code false}.
|
||||
*
|
||||
* @since 5.3
|
||||
*/
|
||||
public static boolean isNewer(final HttpCacheEntry entry, final MessageHeaders message) {
|
||||
if (entry == null || message == null) {
|
||||
return false;
|
||||
}
|
||||
final Instant cacheDate = entry.getInstant();
|
||||
if (cacheDate == null) {
|
||||
return false;
|
||||
}
|
||||
final Instant messageDate = DateUtils.parseStandardDate(message, HttpHeaders.DATE);
|
||||
if (messageDate == null) {
|
||||
return false;
|
||||
}
|
||||
return cacheDate.compareTo(messageDate) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a string representation of this instance suitable for
|
||||
* human consumption.
|
||||
|
|
|
@ -37,7 +37,6 @@ import java.util.Set;
|
|||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.hc.client5.http.impl.cache.CacheSupport;
|
||||
import org.apache.hc.client5.http.impl.cache.DateSupport;
|
||||
import org.apache.hc.client5.http.utils.DateUtils;
|
||||
import org.apache.hc.core5.annotation.Contract;
|
||||
import org.apache.hc.core5.annotation.Internal;
|
||||
|
@ -252,7 +251,7 @@ public class HttpCacheEntryFactory {
|
|||
Args.check(response.getCode() == HttpStatus.SC_NOT_MODIFIED,
|
||||
"Response must have 304 status code");
|
||||
Args.notNull(entry, "Cache entry");
|
||||
if (DateSupport.isAfter(entry, response, HttpHeaders.DATE)) {
|
||||
if (HttpCacheEntry.isNewer(entry, response)) {
|
||||
return entry;
|
||||
}
|
||||
final HeaderGroup mergedHeaders = mergeHeaders(entry, response);
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.apache.hc.client5.http.async.methods.SimpleBody;
|
|||
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
|
||||
import org.apache.hc.client5.http.cache.CacheResponseStatus;
|
||||
import org.apache.hc.client5.http.cache.HttpAsyncCacheStorage;
|
||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||
import org.apache.hc.client5.http.cache.ResourceFactory;
|
||||
import org.apache.hc.client5.http.cache.ResourceIOException;
|
||||
import org.apache.hc.client5.http.impl.ExecSupport;
|
||||
|
@ -531,7 +532,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
|
|||
@Override
|
||||
public void completed(final CacheMatch result) {
|
||||
final CacheHit hit = result != null ? result.hit : null;
|
||||
if (DateSupport.isAfter(hit != null ? hit.entry : null, backendResponse, HttpHeaders.DATE)) {
|
||||
if (HttpCacheEntry.isNewer(hit != null ? hit.entry : null, backendResponse)) {
|
||||
LOG.debug("Backend already contains fresher cache entry");
|
||||
try {
|
||||
final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry);
|
||||
|
|
|
@ -520,7 +520,7 @@ class BasicHttpAsyncCache implements HttpAsyncCache {
|
|||
final Header newETag = response.getFirstHeader(HttpHeaders.ETAG);
|
||||
if (existingETag != null && newETag != null &&
|
||||
!Objects.equals(existingETag.getValue(), newETag.getValue()) &&
|
||||
!DateSupport.isBefore(response, root, HttpHeaders.DATE)) {
|
||||
!HttpCacheEntry.isNewer(root, response)) {
|
||||
evictAll(root, rootKey);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -329,7 +329,7 @@ class BasicHttpCache implements HttpCache {
|
|||
final Header newETag = response.getFirstHeader(HttpHeaders.ETAG);
|
||||
if (existingETag != null && newETag != null &&
|
||||
!Objects.equals(existingETag.getValue(), newETag.getValue()) &&
|
||||
!DateSupport.isBefore(response, root, HttpHeaders.DATE)) {
|
||||
!HttpCacheEntry.isNewer(root, response)) {
|
||||
evictAll(root, rootKey);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import java.time.Instant;
|
|||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||
import org.apache.hc.client5.http.utils.DateUtils;
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.http.HttpHeaders;
|
||||
import org.apache.hc.core5.util.TimeValue;
|
||||
|
@ -105,7 +104,7 @@ class CacheValidityPolicy {
|
|||
// If the Expires response header field is present, use its value minus the value of the Date response header field
|
||||
final Instant dateValue = entry.getInstant();
|
||||
if (dateValue != null) {
|
||||
final Instant expiry = DateUtils.parseStandardDate(entry, HttpHeaders.EXPIRES);
|
||||
final Instant expiry = entry.getExpires();
|
||||
if (expiry != null) {
|
||||
final Duration diff = Duration.between(dateValue, expiry);
|
||||
if (diff.isNegative()) {
|
||||
|
@ -148,7 +147,7 @@ class CacheValidityPolicy {
|
|||
|
||||
public TimeValue getHeuristicFreshnessLifetime(final HttpCacheEntry entry) {
|
||||
final Instant dateValue = entry.getInstant();
|
||||
final Instant lastModifiedValue = DateUtils.parseStandardDate(entry, HttpHeaders.LAST_MODIFIED);
|
||||
final Instant lastModifiedValue = entry.getLastModified();
|
||||
|
||||
if (dateValue != null && lastModifiedValue != null) {
|
||||
final Duration diff = Duration.between(lastModifiedValue, dateValue);
|
||||
|
@ -163,8 +162,7 @@ class CacheValidityPolicy {
|
|||
}
|
||||
|
||||
public boolean isRevalidatable(final HttpCacheEntry entry) {
|
||||
return entry.getFirstHeader(HttpHeaders.ETAG) != null
|
||||
|| entry.getFirstHeader(HttpHeaders.LAST_MODIFIED) != null;
|
||||
return entry.containsHeader(HttpHeaders.ETAG) || entry.containsHeader(HttpHeaders.LAST_MODIFIED);
|
||||
}
|
||||
|
||||
public boolean mayReturnStaleWhileRevalidating(final ResponseCacheControl responseCacheControl,
|
||||
|
|
|
@ -286,7 +286,7 @@ class CachedResponseSuitabilityChecker {
|
|||
* @return boolean Does the last modified header match
|
||||
*/
|
||||
private boolean lastModifiedValidatorMatches(final HttpRequest request, final HttpCacheEntry entry, final Instant now) {
|
||||
final Instant lastModified = DateUtils.parseStandardDate(entry, HttpHeaders.LAST_MODIFIED);
|
||||
final Instant lastModified = entry.getLastModified();
|
||||
if (lastModified == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.apache.hc.client5.http.HttpRoute;
|
|||
import org.apache.hc.client5.http.async.methods.SimpleBody;
|
||||
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
|
||||
import org.apache.hc.client5.http.cache.CacheResponseStatus;
|
||||
import org.apache.hc.client5.http.cache.HttpCacheEntry;
|
||||
import org.apache.hc.client5.http.cache.HttpCacheStorage;
|
||||
import org.apache.hc.client5.http.cache.ResourceIOException;
|
||||
import org.apache.hc.client5.http.classic.ExecChain;
|
||||
|
@ -428,7 +429,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
|
|||
if (cacheConfig.isFreshnessCheckEnabled()) {
|
||||
final CacheMatch result = responseCache.match(target ,request);
|
||||
hit = result != null ? result.hit : null;
|
||||
if (DateSupport.isAfter(hit != null ? hit.entry : null, backendResponse, HttpHeaders.DATE)) {
|
||||
if (HttpCacheEntry.isNewer(hit != null ? hit.entry : null, backendResponse)) {
|
||||
LOG.debug("Backend already contains fresher cache entry");
|
||||
} else {
|
||||
hit = responseCache.store(target, request, backendResponse, buf, requestSent, responseReceived);
|
||||
|
|
|
@ -250,7 +250,7 @@ public class CachingExecBase {
|
|||
// Date header, so we can't tell if they are out of order
|
||||
// according to the origin clock; thus we can skip the
|
||||
// unconditional retry.
|
||||
return DateSupport.isBefore(backendResponse, cacheEntry, HttpHeaders.DATE);
|
||||
return HttpCacheEntry.isNewer(cacheEntry, backendResponse);
|
||||
}
|
||||
|
||||
boolean shouldSendNotModifiedResponse(final HttpRequest request, final HttpCacheEntry responseEntry) {
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* 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.time.Instant;
|
||||
|
||||
import org.apache.hc.client5.http.utils.DateUtils;
|
||||
import org.apache.hc.core5.annotation.Internal;
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.http.MessageHeaders;
|
||||
|
||||
/**
|
||||
* HTTP cache date support utilities.
|
||||
*
|
||||
* @since 5.2
|
||||
*/
|
||||
@Internal
|
||||
public final class DateSupport {
|
||||
|
||||
/**
|
||||
* Tests if the first message is after (newer) than second one
|
||||
* using the given message header for comparison.
|
||||
*
|
||||
* @param message1 the first message
|
||||
* @param message2 the second message
|
||||
* @param headerName header name
|
||||
*
|
||||
* @return {@code true} if both messages contain a header with the given name
|
||||
* and the value of the header from the first message is newer that of
|
||||
* the second message.
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public static boolean isAfter(
|
||||
final MessageHeaders message1,
|
||||
final MessageHeaders message2,
|
||||
final String headerName) {
|
||||
if (message1 != null && message2 != null) {
|
||||
final Header dateHeader1 = message1.getFirstHeader(headerName);
|
||||
if (dateHeader1 != null) {
|
||||
final Header dateHeader2 = message2.getFirstHeader(headerName);
|
||||
if (dateHeader2 != null) {
|
||||
final Instant date1 = DateUtils.parseStandardDate(dateHeader1.getValue());
|
||||
if (date1 != null) {
|
||||
final Instant date2 = DateUtils.parseStandardDate(dateHeader2.getValue());
|
||||
if (date2 != null) {
|
||||
return date1.isAfter(date2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the first message is before (older) than the second one
|
||||
* using the given message header for comparison.
|
||||
*
|
||||
* @param message1 the first message
|
||||
* @param message2 the second message
|
||||
* @param headerName header name
|
||||
*
|
||||
* @return {@code true} if both messages contain a header with the given name
|
||||
* and the value of the header from the first message is older that of
|
||||
* the second message.
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
public static boolean isBefore(
|
||||
final MessageHeaders message1,
|
||||
final MessageHeaders message2,
|
||||
final String headerName) {
|
||||
if (message1 != null && message2 != null) {
|
||||
final Header dateHeader1 = message1.getFirstHeader(headerName);
|
||||
if (dateHeader1 != null) {
|
||||
final Header dateHeader2 = message2.getFirstHeader(headerName);
|
||||
if (dateHeader2 != null) {
|
||||
final Instant date1 = DateUtils.parseStandardDate(dateHeader1.getValue());
|
||||
if (date1 != null) {
|
||||
final Instant date2 = DateUtils.parseStandardDate(dateHeader2.getValue());
|
||||
if (date2 != null) {
|
||||
return date1.isBefore(date2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -36,18 +36,22 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.ZoneId;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.hc.client5.http.impl.cache.HttpTestUtils;
|
||||
import org.apache.hc.client5.http.utils.DateUtils;
|
||||
import org.apache.hc.core5.http.Header;
|
||||
import org.apache.hc.core5.http.HttpHeaders;
|
||||
import org.apache.hc.core5.http.HttpStatus;
|
||||
import org.apache.hc.core5.http.Method;
|
||||
import org.apache.hc.core5.http.message.BasicHeader;
|
||||
import org.apache.hc.core5.http.message.HeaderGroup;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -84,79 +88,82 @@ public class TestHttpCacheEntry {
|
|||
"GET", "/", HttpTestUtils.headers(),
|
||||
status, HttpTestUtils.headers(headers), resource, variants);
|
||||
}
|
||||
|
||||
private HttpCacheEntry makeEntry(final Instant requestDate,
|
||||
final Instant responseDate,
|
||||
final int status,
|
||||
final Header... headers) {
|
||||
return new HttpCacheEntry(requestDate, responseDate,
|
||||
"GET", "/", HttpTestUtils.headers(),
|
||||
status, HttpTestUtils.headers(headers), mockResource, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHeadersReturnsCorrectHeaders() {
|
||||
final Header[] headers = { new BasicHeader("foo", "fooValue"),
|
||||
entry = makeEntry(
|
||||
new BasicHeader("bar", "barValue1"),
|
||||
new BasicHeader("bar", "barValue2")
|
||||
};
|
||||
entry = makeEntry(headers);
|
||||
new BasicHeader("bar", "barValue2"));
|
||||
assertEquals(2, entry.getHeaders("bar").length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFirstHeaderReturnsCorrectHeader() {
|
||||
final Header[] headers = { new BasicHeader("foo", "fooValue"),
|
||||
entry = makeEntry(
|
||||
new BasicHeader("bar", "barValue1"),
|
||||
new BasicHeader("bar", "barValue2")
|
||||
};
|
||||
entry = makeEntry(headers);
|
||||
new BasicHeader("bar", "barValue2"));
|
||||
assertEquals("barValue1", entry.getFirstHeader("bar").getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetHeadersReturnsEmptyArrayIfNoneMatch() {
|
||||
final Header[] headers = { new BasicHeader("foo", "fooValue"),
|
||||
entry = makeEntry(
|
||||
new BasicHeader("foo", "fooValue"),
|
||||
new BasicHeader("bar", "barValue1"),
|
||||
new BasicHeader("bar", "barValue2")
|
||||
};
|
||||
entry = makeEntry(headers);
|
||||
new BasicHeader("bar", "barValue2"));
|
||||
assertEquals(0, entry.getHeaders("baz").length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFirstHeaderReturnsNullIfNoneMatch() {
|
||||
final Header[] headers = { new BasicHeader("foo", "fooValue"),
|
||||
entry = makeEntry(
|
||||
new BasicHeader("foo", "fooValue"),
|
||||
new BasicHeader("bar", "barValue1"),
|
||||
new BasicHeader("bar", "barValue2")
|
||||
};
|
||||
entry = makeEntry(headers);
|
||||
new BasicHeader("bar", "barValue2"));
|
||||
assertNull(entry.getFirstHeader("quux"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMethodReturnsCorrectRequestMethod() {
|
||||
final Header[] headers = { new BasicHeader("foo", "fooValue"),
|
||||
entry = makeEntry(
|
||||
new BasicHeader("foo", "fooValue"),
|
||||
new BasicHeader("bar", "barValue1"),
|
||||
new BasicHeader("bar", "barValue2")
|
||||
};
|
||||
entry = makeEntry(headers);
|
||||
new BasicHeader("bar", "barValue2"));
|
||||
assertEquals(Method.GET.name(), entry.getRequestMethod());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void statusCodeComesFromOriginalStatusLine() {
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource, null);
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK);
|
||||
assertEquals(HttpStatus.SC_OK, entry.getStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canGetOriginalRequestDate() {
|
||||
final Instant requestDate = Instant.now();
|
||||
entry = makeEntry(requestDate, Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource, null);
|
||||
entry = makeEntry(requestDate, Instant.now(), HttpStatus.SC_OK);
|
||||
assertEquals(requestDate, entry.getRequestInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canGetOriginalResponseDate() {
|
||||
final Instant responseDate = Instant.now();
|
||||
entry = makeEntry(Instant.now(), responseDate, HttpStatus.SC_OK, new Header[]{}, mockResource, null);
|
||||
entry = makeEntry(Instant.now(), responseDate, HttpStatus.SC_OK);
|
||||
assertEquals(responseDate, entry.getResponseInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canGetOriginalResource() {
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource, null);
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK);
|
||||
assertSame(mockResource, entry.getResource());
|
||||
}
|
||||
|
||||
|
@ -174,18 +181,6 @@ public class TestHttpCacheEntry {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canConstructWithoutVariants() {
|
||||
makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canProvideVariantMap() {
|
||||
makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
||||
new Header[]{}, mockResource,
|
||||
null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void canRetrieveOriginalVariantMap() {
|
||||
final Set<String> variants = new HashSet<>();
|
||||
|
@ -219,33 +214,91 @@ public class TestHttpCacheEntry {
|
|||
|
||||
@Test
|
||||
public void canConvertToString() {
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, new Header[]{}, mockResource, null);
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK);
|
||||
assertNotNull(entry.toString());
|
||||
assertNotEquals("", entry.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissingDateHeaderIsIgnored() {
|
||||
final Header[] headers = new Header[] {};
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, headers, mockResource, null);
|
||||
assertNull(entry.getDate());
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK);
|
||||
assertNull(entry.getInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMalformedDateHeaderIsIgnored() {
|
||||
final Header[] headers = new Header[] { new BasicHeader("Date", "asdf") };
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, headers, mockResource, null);
|
||||
assertNull(entry.getDate());
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
||||
new BasicHeader("Date", "asdf"));
|
||||
assertNull(entry.getInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidDateHeaderIsParsed() {
|
||||
final Instant date = Instant.now().with(ChronoField.MILLI_OF_SECOND, 0);
|
||||
final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatStandardDate(date)) };
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK, headers, mockResource, null);
|
||||
final Date dateHeaderValue = entry.getDate();
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
||||
new BasicHeader("Date", DateUtils.formatStandardDate(date)));
|
||||
final Instant dateHeaderValue = entry.getInstant();
|
||||
assertNotNull(dateHeaderValue);
|
||||
assertEquals(DateUtils.toDate(date), dateHeaderValue);
|
||||
assertEquals(date, dateHeaderValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEpochDateHeaderIsParsed() {
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
||||
new BasicHeader("Date", DateUtils.formatStandardDate(Instant.EPOCH)));
|
||||
final Instant dateHeaderValue = entry.getInstant();
|
||||
assertNotNull(dateHeaderValue);
|
||||
assertEquals(Instant.EPOCH, dateHeaderValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateParsedOnce() {
|
||||
final Instant date = Instant.now().with(ChronoField.MILLI_OF_SECOND, 0);
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
||||
new BasicHeader("Date", DateUtils.formatStandardDate(date)));
|
||||
final Instant dateHeaderValue = entry.getInstant();
|
||||
assertNotNull(dateHeaderValue);
|
||||
assertSame(dateHeaderValue, entry.getInstant());
|
||||
assertSame(dateHeaderValue, entry.getInstant());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpiresParsedOnce() {
|
||||
final Instant date = Instant.now().with(ChronoField.MILLI_OF_SECOND, 0);
|
||||
entry = makeEntry(Instant.now(), Instant.now(), HttpStatus.SC_OK,
|
||||
new BasicHeader("Last-Modified", DateUtils.formatStandardDate(date)));
|
||||
final Instant lastModifiedHeaderValue = entry.getLastModified();
|
||||
assertNotNull(lastModifiedHeaderValue);
|
||||
assertSame(lastModifiedHeaderValue, entry.getLastModified());
|
||||
assertSame(lastModifiedHeaderValue, entry.getLastModified());
|
||||
}
|
||||
|
||||
private static Instant createInstant(final int year, final Month month, final int day) {
|
||||
return LocalDate.of(year, month, day).atStartOfDay(ZoneId.of("GMT")).toInstant();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsCacheEntryNewer() throws Exception {
|
||||
assertFalse(HttpCacheEntry.isNewer(null, null));
|
||||
entry = makeEntry();
|
||||
final HeaderGroup message = new HeaderGroup();
|
||||
assertFalse(HttpCacheEntry.isNewer(entry, message));
|
||||
|
||||
entry = makeEntry(new BasicHeader(HttpHeaders.DATE, "huh?"));
|
||||
message.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
|
||||
assertFalse(HttpCacheEntry.isNewer(entry, message));
|
||||
|
||||
entry = makeEntry(new BasicHeader(HttpHeaders.DATE, "huh?"));
|
||||
message.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
|
||||
assertFalse(HttpCacheEntry.isNewer(entry, message));
|
||||
|
||||
entry = makeEntry(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 27))));
|
||||
message.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
|
||||
assertTrue(HttpCacheEntry.isNewer(entry, message));
|
||||
|
||||
entry = makeEntry(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 25))));
|
||||
message.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
|
||||
assertFalse(HttpCacheEntry.isNewer(entry, message));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* 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.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Month;
|
||||
import java.time.ZoneId;
|
||||
|
||||
import org.apache.hc.client5.http.utils.DateUtils;
|
||||
import org.apache.hc.core5.http.HttpHeaders;
|
||||
import org.apache.hc.core5.http.message.BasicHeader;
|
||||
import org.apache.hc.core5.http.message.HeaderGroup;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link DateSupport}.
|
||||
*/
|
||||
public class TestDateSupport {
|
||||
|
||||
private static Instant createInstant(final int year, final Month month, final int day) {
|
||||
return LocalDate.of(year, month, day).atStartOfDay(ZoneId.of("GMT")).toInstant();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsBefore() throws Exception {
|
||||
final HeaderGroup message1 = new HeaderGroup();
|
||||
final HeaderGroup message2 = new HeaderGroup();
|
||||
assertThat(DateSupport.isBefore(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false));
|
||||
assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
|
||||
|
||||
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
|
||||
message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
|
||||
assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
|
||||
|
||||
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
|
||||
message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
|
||||
assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
|
||||
|
||||
message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 25))));
|
||||
message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
|
||||
assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(true));
|
||||
|
||||
message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 27))));
|
||||
message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
|
||||
assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAfter() throws Exception {
|
||||
final HeaderGroup message1 = new HeaderGroup();
|
||||
final HeaderGroup message2 = new HeaderGroup();
|
||||
assertThat(DateSupport.isAfter(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false));
|
||||
assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
|
||||
|
||||
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
|
||||
message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
|
||||
assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
|
||||
|
||||
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
|
||||
message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
|
||||
assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
|
||||
|
||||
message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 27))));
|
||||
message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
|
||||
assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(true));
|
||||
|
||||
message1.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 25))));
|
||||
message2.setHeader(new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(createInstant(2017, Month.DECEMBER, 26))));
|
||||
assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue