Added Date util methods for common operations

This commit is contained in:
Oleg Kalnichevski 2017-12-26 17:18:20 +01:00
parent 1e4e204162
commit 6459d6882c
11 changed files with 190 additions and 145 deletions

View File

@ -132,11 +132,7 @@ public class HttpCacheEntry implements MessageHeaders, Serializable {
* @return the Date value of the header or null if the header is not present
*/
private Date parseDate() {
final Header dateHdr = getFirstHeader(HttpHeaders.DATE);
if (dateHdr == null) {
return null;
}
return DateUtils.parseDate(dateHdr.getValue());
return DateUtils.parseDate(this, HttpHeaders.DATE);
}
/**

View File

@ -44,7 +44,6 @@ import org.apache.hc.core5.http.HttpHeaders;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.MessageHeaders;
import org.apache.hc.core5.http.message.HeaderGroup;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.ByteArrayBuffer;
@ -137,16 +136,8 @@ class CacheUpdateHandler {
variantMap);
}
private static Date getDate(final MessageHeaders messageHeaders) {
final Header dateHeader = messageHeaders.getFirstHeader(HttpHeaders.DATE);
return dateHeader != null ? DateUtils.parseDate(dateHeader.getValue()): null;
}
private Header[] mergeHeaders(final HttpCacheEntry entry, final HttpResponse response) {
final Date cacheDate = getDate(entry);
final Date responseDate = getDate(response);
if (cacheDate != null && responseDate != null && cacheDate.after(responseDate)) {
// Don't merge headers, keep the entry's headers as they are newer.
if (DateUtils.isAfter(entry, response, HttpHeaders.DATE)) {
return entry.getAllHeaders();
}
final HeaderGroup headerGroup = new HeaderGroup();

View File

@ -69,7 +69,7 @@ class CacheValidityPolicy {
return 0L;
}
final Date expiry = getExpirationDate(entry);
final Date expiry = DateUtils.parseDate(entry, HeaderConstants.EXPIRES);
if (expiry == null) {
return 0;
}
@ -102,7 +102,7 @@ class CacheValidityPolicy {
public long getHeuristicFreshnessLifetimeSecs(final HttpCacheEntry entry,
final float coefficient, final long defaultLifetime) {
final Date dateValue = entry.getDate();
final Date lastModifiedValue = getLastModifiedValue(entry);
final Date lastModifiedValue = DateUtils.parseDate(entry, HeaderConstants.LAST_MODIFIED);
if (dateValue != null && lastModifiedValue != null) {
final long diff = dateValue.getTime() - lastModifiedValue.getTime();
@ -173,14 +173,6 @@ class CacheValidityPolicy {
return result;
}
protected Date getLastModifiedValue(final HttpCacheEntry entry) {
final Header dateHdr = entry.getFirstHeader(HeaderConstants.LAST_MODIFIED);
if (dateHdr == null) {
return null;
}
return DateUtils.parseDate(dateHdr.getValue());
}
/**
* This matters for deciding whether the cache entry is valid to serve as a
* response. If these values do not match, we might have a partial response
@ -275,14 +267,6 @@ class CacheValidityPolicy {
return maxage;
}
protected Date getExpirationDate(final HttpCacheEntry entry) {
final Header expiresHeader = entry.getFirstHeader(HeaderConstants.EXPIRES);
if (expiresHeader == null) {
return null;
}
return DateUtils.parseDate(expiresHeader.getValue());
}
public boolean hasCacheControlDirective(final HttpCacheEntry entry, final String directive) {
final Iterator<HeaderElement> it = MessageSupport.iterate(entry, HeaderConstants.CACHE_CONTROL);
while (it.hasNext()) {

View File

@ -336,11 +336,7 @@ class CachedResponseSuitabilityChecker {
* @return boolean Does the last modified header match
*/
private boolean lastModifiedValidatorMatches(final HttpRequest request, final HttpCacheEntry entry, final Date now) {
final Header lastModifiedHeader = entry.getFirstHeader(HeaderConstants.LAST_MODIFIED);
Date lastModified = null;
if (lastModifiedHeader != null) {
lastModified = DateUtils.parseDate(lastModifiedHeader.getValue());
}
final Date lastModified = DateUtils.parseDate(entry, HeaderConstants.LAST_MODIFIED);
if (lastModified == null) {
return false;
}

View File

@ -362,25 +362,12 @@ public class CachingExecBase {
return true;
}
boolean revalidationResponseIsTooOld(final HttpResponse backendResponse,
final HttpCacheEntry cacheEntry) {
final Header entryDateHeader = cacheEntry.getFirstHeader(HttpHeaders.DATE);
final Header responseDateHeader = backendResponse.getFirstHeader(HttpHeaders.DATE);
if (entryDateHeader != null && responseDateHeader != null) {
final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
final Date respDate = DateUtils.parseDate(responseDateHeader.getValue());
if (entryDate == null || respDate == null) {
// either backend response or cached entry did not have a valid
// 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 recommended in 13.2.6 of RFC 2616.
return false;
}
if (respDate.before(entryDate)) {
return true;
}
}
return false;
boolean revalidationResponseIsTooOld(final HttpResponse backendResponse, final HttpCacheEntry cacheEntry) {
// either backend response or cached entry did not have a valid
// 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 recommended in 13.2.6 of RFC 2616.
return DateUtils.isBefore(backendResponse, cacheEntry, HttpHeaders.DATE);
}
void tryToUpdateVariantMap(
@ -426,6 +413,14 @@ public class CachingExecBase {
boolean alreadyHaveNewerCacheEntry(
final HttpHost target, final HttpRequest request, final HttpResponse backendResponse) {
final Header responseDateHeader = backendResponse.getFirstHeader(HttpHeaders.DATE);
if (responseDateHeader == null) {
return false;
}
final Date responseDate = DateUtils.parseDate(responseDateHeader.getValue());
if (responseDate == null) {
return false;
}
HttpCacheEntry existing = null;
try {
existing = responseCache.getCacheEntry(target, request);
@ -435,17 +430,8 @@ public class CachingExecBase {
if (existing == null) {
return false;
}
final Header entryDateHeader = existing.getFirstHeader(HttpHeaders.DATE);
if (entryDateHeader == null) {
return false;
}
final Header responseDateHeader = backendResponse.getFirstHeader(HttpHeaders.DATE);
if (responseDateHeader == null) {
return false;
}
final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
final Date responseDate = DateUtils.parseDate(responseDateHeader.getValue());
if (entryDate == null || responseDate == null) {
final Date entryDate = DateUtils.parseDate(existing, HttpHeaders.DATE);
if (entryDate == null) {
return false;
}
return responseDate.before(entryDate);

View File

@ -29,7 +29,6 @@ package org.apache.hc.client5.http.impl.cache;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Date;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
@ -273,19 +272,7 @@ class DefaultCacheInvalidator implements HttpCacheInvalidator {
return (!entryEtag.getValue().equals(responseEtag.getValue()));
}
private boolean responseDateOlderThanEntryDate(final HttpResponse response,
final HttpCacheEntry entry) {
final Header entryDateHeader = entry.getFirstHeader(HttpHeaders.DATE);
final Header responseDateHeader = response.getFirstHeader(HttpHeaders.DATE);
if (entryDateHeader == null || responseDateHeader == null) {
/* be conservative; should probably flush */
return false;
}
final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue());
final Date responseDate = DateUtils.parseDate(responseDateHeader.getValue());
if (entryDate == null || responseDate == null) {
return false;
}
return responseDate.before(entryDate);
private boolean responseDateOlderThanEntryDate(final HttpResponse response, final HttpCacheEntry entry) {
return DateUtils.isBefore(response, entry, HttpHeaders.DATE);
}
}

View File

@ -161,8 +161,7 @@ class ResponseCachingPolicy {
return false;
}
final Header h = response.getFirstHeader(HttpHeaders.DATE);
final Date date = h != null ? DateUtils.parseDate(h.getValue()) : null;
final Date date = DateUtils.parseDate(response, HttpHeaders.DATE);
if (date == null) {
log.debug("Invalid / missing Date header");
return false;

View File

@ -91,7 +91,7 @@ class ResponseProtocolCompliance {
private void warningsWithNonMatchingWarnDatesAreRemoved(
final HttpResponse response) {
final Date responseDate = DateUtils.parseDate(response.getFirstHeader(HttpHeaders.DATE).getValue());
final Date responseDate = DateUtils.parseDate(response, HttpHeaders.DATE);
if (responseDate == null) {
return;
}

View File

@ -28,7 +28,6 @@ package org.apache.hc.client5.http.impl.cache;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
@ -382,13 +381,6 @@ public class TestCacheValidityPolicy {
assertEquals(0, impl.getMaxAge(entry));
}
@Test
public void testMalformedExpirationDateReturnsNull() {
final Header[] headers = new Header[] { new BasicHeader("Expires", "asdf") };
final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(headers);
assertNull(impl.getExpirationDate(entry));
}
@Test
public void testMustRevalidateIsFalseIfDirectiveNotPresent() {
final Header[] headers = new Header[] { new BasicHeader("Cache-Control","public") };

View File

@ -39,6 +39,8 @@ import java.util.TimeZone;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.MessageHeaders;
import org.apache.hc.core5.util.Args;
/**
@ -97,6 +99,99 @@ public final class DateUtils {
return parseDate(dateValue, null, null);
}
/**
* Parses a date value from a header with the given name.
*
* @param headers message headers
* @param headerName header name
*
* @return the parsed date or null if input could not be parsed
*
* @since 5.0
*/
public static Date parseDate(final MessageHeaders headers, final String headerName) {
if (headers == null) {
return null;
}
final Header header = headers.getFirstHeader(headerName);
if (header == null) {
return null;
}
return parseDate(header.getValue(), null, null);
}
/**
* Tests if the first message is after (newer) than seconds message
* 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 Date date1 = parseDate(dateHeader1.getValue());
if (date1 != null) {
final Date date2 = parseDate(dateHeader2.getValue());
if (date2 != null) {
return date1.after(date2);
}
}
}
}
}
return false;
}
/**
* Tests if the first message is before (older) than seconds message
* 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 Date date1 = parseDate(dateHeader1.getValue());
if (date1 != null) {
final Date date2 = parseDate(dateHeader2.getValue());
if (date2 != null) {
return date1.before(date2);
}
}
}
}
}
return false;
}
/**
* Parses the date value using the given date formats.
*

View File

@ -30,6 +30,10 @@ package org.apache.hc.client5.http.utils;
import java.util.Calendar;
import java.util.Date;
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.Assert;
import org.junit.Test;
@ -38,23 +42,33 @@ import org.junit.Test;
*/
public class TestDateUtils {
@Test
public void testBasicDateParse() throws Exception {
private static Date createDate(final int year, final int month, final int day) {
final Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(DateUtils.GMT);
calendar.set(2005, Calendar.OCTOBER, 14, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
final Date date1 = calendar.getTime();
calendar.setTimeInMillis(0);
calendar.set(year, month, day);
return calendar.getTime();
}
final String[] formats = new String[] {
DateUtils.PATTERN_RFC1123
};
Date date2 = DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 GMT", formats, null);
Assert.assertEquals(date1, date2);
date2 = DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 GMT", formats);
Assert.assertEquals(date1, date2);
date2 = DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 GMT");
Assert.assertEquals(date1, date2);
@Test
public void testBasicDateParse() throws Exception {
final Date date = createDate(2005, Calendar.OCTOBER, 14);
final String[] formats = new String[] { DateUtils.PATTERN_RFC1123 };
Assert.assertEquals(date, DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 GMT", formats, null));
Assert.assertEquals(date, DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 GMT", formats));
Assert.assertEquals(date, DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 GMT"));
}
@Test
public void testDateParseMessage() throws Exception {
final HeaderGroup message1 = new HeaderGroup();
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "Fri, 14 Oct 2005 00:00:00 GMT"));
Assert.assertEquals(createDate(2005, Calendar.OCTOBER, 14), DateUtils.parseDate(message1, HttpHeaders.DATE));
final HeaderGroup message2 = new HeaderGroup();
message2.addHeader(new BasicHeader(HttpHeaders.DATE, "Fri, 14 Oct 2005 00:00:00 GMT"));
message2.addHeader(new BasicHeader(HttpHeaders.DATE, "Fri, 21 Oct 2005 00:00:00 GMT"));
Assert.assertEquals(createDate(2005, Calendar.OCTOBER, 14), DateUtils.parseDate(message2, HttpHeaders.DATE));
}
@Test
@ -86,55 +100,60 @@ public class TestDateUtils {
@Test
public void testTwoDigitYearDateParse() throws Exception {
final Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(DateUtils.GMT);
calendar.set(2005, Calendar.OCTOBER, 14, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
Date date1 = calendar.getTime();
final String[] formats = new String[] {
DateUtils.PATTERN_RFC1036
};
Date date2 = DateUtils.parseDate("Friday, 14-Oct-05 00:00:00 GMT", formats, null);
Assert.assertEquals(date1, date2);
calendar.set(1900, Calendar.JANUARY, 0, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
final Date startDate = calendar.getTime();
calendar.set(1905, Calendar.OCTOBER, 14, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
date1 = calendar.getTime();
date2 = DateUtils.parseDate("Friday, 14-Oct-05 00:00:00 GMT", formats, startDate);
Assert.assertEquals(date1, date2);
final String[] formats = new String[] { DateUtils.PATTERN_RFC1036 };
Assert.assertEquals(createDate(2005, Calendar.OCTOBER, 14), DateUtils.parseDate("Friday, 14-Oct-05 00:00:00 GMT", formats, null));
Assert.assertEquals(createDate(1905, Calendar.OCTOBER, 14), DateUtils.parseDate("Friday, 14-Oct-05 00:00:00 GMT", formats,
createDate(1900, Calendar.JANUARY, 0)));
}
@Test
public void testParseQuotedDate() throws Exception {
final Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(DateUtils.GMT);
calendar.set(2005, Calendar.OCTOBER, 14, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
final Date date1 = calendar.getTime();
final String[] formats = new String[] {
DateUtils.PATTERN_RFC1123
};
final Date date1 = createDate(2005, Calendar.OCTOBER, 14);
final String[] formats = new String[] { DateUtils.PATTERN_RFC1123 };
final Date date2 = DateUtils.parseDate("'Fri, 14 Oct 2005 00:00:00 GMT'", formats);
Assert.assertEquals(date1, date2);
}
@Test
public void testBasicDateFormat() throws Exception {
final Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(DateUtils.GMT);
calendar.set(2005, Calendar.OCTOBER, 14, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
final Date date = calendar.getTime();
final Date date = createDate(2005, Calendar.OCTOBER, 14);
Assert.assertEquals("Fri, 14 Oct 2005 00:00:00 GMT", DateUtils.formatDate(date));
Assert.assertEquals("Fri, 14 Oct 2005 00:00:00 GMT", DateUtils.formatDate(date, DateUtils.PATTERN_RFC1123));
}
@Test
public void testIsBefore() throws Exception {
final HeaderGroup message1 = new HeaderGroup();
final HeaderGroup message2 = new HeaderGroup();
Assert.assertThat(DateUtils.isBefore(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false));
Assert.assertThat(DateUtils.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
Assert.assertThat(DateUtils.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
message2.setHeader(new BasicHeader(HttpHeaders.DATE, "Tuesday, 26-Dec-2017 00:00:00 GMT"));
Assert.assertThat(DateUtils.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "Wednesday, 25-Dec-2017 00:00:00 GMT"));
Assert.assertThat(DateUtils.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(true));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "Thursday, 27-Dec-2017 00:00:00 GMT"));
Assert.assertThat(DateUtils.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
}
@Test
public void testIsAfter() throws Exception {
final HeaderGroup message1 = new HeaderGroup();
final HeaderGroup message2 = new HeaderGroup();
Assert.assertThat(DateUtils.isAfter(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false));
Assert.assertThat(DateUtils.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
Assert.assertThat(DateUtils.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
message2.setHeader(new BasicHeader(HttpHeaders.DATE, "Tuesday, 26-Dec-2017 00:00:00 GMT"));
Assert.assertThat(DateUtils.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "Thursday, 27-Dec-2017 00:00:00 GMT"));
Assert.assertThat(DateUtils.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(true));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "Wednesday, 25-Dec-2017 00:00:00 GMT"));
Assert.assertThat(DateUtils.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
}
}