Replaced SimpleDateFormat and Calendar with Java 8 Time APIs; removed thread-local from DateUtils

This commit is contained in:
Oleg Kalnichevski 2021-10-23 19:48:00 +02:00
parent ffc8cd7585
commit dfc2086d24
27 changed files with 716 additions and 416 deletions

View File

@ -132,7 +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() {
return DateUtils.parseDate(this, HttpHeaders.DATE);
return DateUtils.toDate(DateUtils.parseStandardDate(this, HttpHeaders.DATE));
}
/**

View File

@ -51,7 +51,6 @@ import org.apache.hc.client5.http.cache.ResourceIOException;
import org.apache.hc.client5.http.impl.ExecSupport;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.concurrent.CancellableDependency;
@ -574,7 +573,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
@Override
public void completed(final HttpCacheEntry existingEntry) {
if (DateUtils.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
if (DateSupport.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
LOG.debug("Backend already contains fresher cache entry");
try {
final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, existingEntry);

View File

@ -30,7 +30,6 @@ import java.net.URI;
import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.client5.http.utils.URIUtils;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHeaders;
@ -99,7 +98,7 @@ class CacheInvalidatorBase {
}
static boolean responseDateOlderThanEntryDate(final HttpResponse response, final HttpCacheEntry entry) {
return DateUtils.isBefore(response, entry, HttpHeaders.DATE);
return DateSupport.isBefore(response, entry, HttpHeaders.DATE);
}
}

View File

@ -36,7 +36,6 @@ import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.Resource;
import org.apache.hc.client5.http.cache.ResourceFactory;
import org.apache.hc.client5.http.cache.ResourceIOException;
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.HttpRequest;
@ -132,7 +131,7 @@ class CacheUpdateHandler {
}
private Header[] mergeHeaders(final HttpCacheEntry entry, final HttpResponse response) {
if (DateUtils.isAfter(entry, response, HttpHeaders.DATE)) {
if (DateSupport.isAfter(entry, response, HttpHeaders.DATE)) {
return entry.getHeaders();
}
final HeaderGroup headerGroup = new HeaderGroup();

View File

@ -26,6 +26,7 @@
*/
package org.apache.hc.client5.http.impl.cache;
import java.time.Instant;
import java.util.Date;
import java.util.Iterator;
@ -64,11 +65,11 @@ class CacheValidityPolicy {
return TimeValue.ZERO_MILLISECONDS;
}
final Date expiry = DateUtils.parseDate(entry, HeaderConstants.EXPIRES);
final Instant expiry = DateUtils.parseStandardDate(entry, HeaderConstants.EXPIRES);
if (expiry == null) {
return TimeValue.ZERO_MILLISECONDS;
}
final long diff = expiry.getTime() - dateValue.getTime();
final long diff = expiry.toEpochMilli() - dateValue.getTime();
return TimeValue.ofSeconds(diff / 1000);
}
@ -98,10 +99,10 @@ class CacheValidityPolicy {
public TimeValue getHeuristicFreshnessLifetime(final HttpCacheEntry entry,
final float coefficient, final TimeValue defaultLifetime) {
final Date dateValue = entry.getDate();
final Date lastModifiedValue = DateUtils.parseDate(entry, HeaderConstants.LAST_MODIFIED);
final Instant lastModifiedValue = DateUtils.parseStandardDate(entry, HeaderConstants.LAST_MODIFIED);
if (dateValue != null && lastModifiedValue != null) {
final long diff = dateValue.getTime() - lastModifiedValue.getTime();
final long diff = dateValue.getTime() - lastModifiedValue.toEpochMilli();
if (diff < 0) {
return TimeValue.ZERO_MILLISECONDS;
}

View File

@ -26,6 +26,7 @@
*/
package org.apache.hc.client5.http.impl.cache;
import java.time.Instant;
import java.util.Date;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
@ -105,7 +106,7 @@ class CachedHttpResponseGenerator {
// - Date, unless its omission is required by section 14.8.1
Header dateHeader = entry.getFirstHeader(HttpHeaders.DATE);
if (dateHeader == null) {
dateHeader = new BasicHeader(HttpHeaders.DATE, DateUtils.formatDate(new Date()));
dateHeader = new BasicHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now()));
}
response.addHeader(dateHeader);

View File

@ -26,6 +26,7 @@
*/
package org.apache.hc.client5.http.impl.cache;
import java.time.Instant;
import java.util.Date;
import java.util.Iterator;
@ -278,7 +279,8 @@ class CachedResponseSuitabilityChecker {
final boolean hasLastModifiedValidator = hasSupportedLastModifiedValidator(request);
final boolean etagValidatorMatches = (hasEtagValidator) && etagValidatorMatches(request, entry);
final boolean lastModifiedValidatorMatches = (hasLastModifiedValidator) && lastModifiedValidatorMatches(request, entry, now);
final boolean lastModifiedValidatorMatches = (hasLastModifiedValidator) && lastModifiedValidatorMatches(request, entry,
DateUtils.toInstant(now));
if ((hasEtagValidator && hasLastModifiedValidator)
&& !(etagValidatorMatches && lastModifiedValidatorMatches)) {
@ -332,16 +334,16 @@ class CachedResponseSuitabilityChecker {
* @param now right NOW in time
* @return boolean Does the last modified header match
*/
private boolean lastModifiedValidatorMatches(final HttpRequest request, final HttpCacheEntry entry, final Date now) {
final Date lastModified = DateUtils.parseDate(entry, HeaderConstants.LAST_MODIFIED);
private boolean lastModifiedValidatorMatches(final HttpRequest request, final HttpCacheEntry entry, final Instant now) {
final Instant lastModified = DateUtils.parseStandardDate(entry, HeaderConstants.LAST_MODIFIED);
if (lastModified == null) {
return false;
}
for (final Header h : request.getHeaders(HeaderConstants.IF_MODIFIED_SINCE)) {
final Date ifModifiedSince = DateUtils.parseDate(h.getValue());
final Instant ifModifiedSince = DateUtils.parseStandardDate(h.getValue());
if (ifModifiedSince != null) {
if (ifModifiedSince.after(now) || lastModified.after(ifModifiedSince)) {
if (ifModifiedSince.isAfter(now) || lastModified.isAfter(ifModifiedSince)) {
return false;
}
}
@ -351,8 +353,8 @@ class CachedResponseSuitabilityChecker {
private boolean hasValidDateField(final HttpRequest request, final String headerName) {
for(final Header h : request.getHeaders(headerName)) {
final Date date = DateUtils.parseDate(h.getValue());
return date != null;
final Instant instant = DateUtils.parseStandardDate(h.getValue());
return instant != null;
}
return false;
}

View File

@ -47,7 +47,6 @@ import org.apache.hc.client5.http.classic.ExecChainHandler;
import org.apache.hc.client5.http.impl.ExecSupport;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.schedule.SchedulingStrategy;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
@ -413,7 +412,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
final HttpCacheEntry cacheEntry;
if (cacheConfig.isFreshnessCheckEnabled()) {
final HttpCacheEntry existingEntry = responseCache.getCacheEntry(target, request);
if (DateUtils.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
if (DateSupport.isAfter(existingEntry, backendResponse, HttpHeaders.DATE)) {
LOG.debug("Backend already contains fresher cache entry");
cacheEntry = existingEntry;
} else {

View File

@ -40,7 +40,6 @@ import org.apache.hc.client5.http.cache.HeaderConstants;
import org.apache.hc.client5.http.cache.HttpCacheContext;
import org.apache.hc.client5.http.cache.HttpCacheEntry;
import org.apache.hc.client5.http.cache.ResourceIOException;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HeaderElement;
import org.apache.hc.core5.http.HttpHeaders;
@ -333,7 +332,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 recommended in 13.2.6 of RFC 2616.
return DateUtils.isBefore(backendResponse, cacheEntry, HttpHeaders.DATE);
return DateSupport.isBefore(backendResponse, cacheEntry, HttpHeaders.DATE);
}
boolean shouldSendNotModifiedResponse(final HttpRequest request, final HttpCacheEntry responseEntry) {

View File

@ -0,0 +1,116 @@
/*
* ====================================================================
* 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;
}
}

View File

@ -26,9 +26,9 @@
*/
package org.apache.hc.client5.http.impl.cache;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
@ -154,7 +154,7 @@ class ResponseCachingPolicy {
return false;
}
final Date date = DateUtils.parseDate(response, HttpHeaders.DATE);
final Instant date = DateUtils.parseStandardDate(response, HttpHeaders.DATE);
if (date == null) {
LOG.debug("Invalid / missing Date header");
return false;
@ -292,12 +292,12 @@ class ResponseCachingPolicy {
if (expiresHdr == null || dateHdr == null) {
return false;
}
final Date expires = DateUtils.parseDate(expiresHdr.getValue());
final Date date = DateUtils.parseDate(dateHdr.getValue());
final Instant expires = DateUtils.parseStandardDate(expiresHdr.getValue());
final Instant date = DateUtils.parseStandardDate(dateHdr.getValue());
if (expires == null || date == null) {
return false;
}
return expires.equals(date) || expires.before(date);
return expires.equals(date) || expires.isBefore(date);
}
private boolean from1_0Origin(final HttpResponse response) {

View File

@ -27,6 +27,7 @@
package org.apache.hc.client5.http.impl.cache;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@ -85,7 +86,7 @@ class ResponseProtocolCompliance {
private void warningsWithNonMatchingWarnDatesAreRemoved(
final HttpResponse response) {
final Date responseDate = DateUtils.parseDate(response, HttpHeaders.DATE);
final Instant responseDate = DateUtils.parseStandardDate(response, HttpHeaders.DATE);
if (responseDate == null) {
return;
}
@ -153,7 +154,7 @@ class ResponseProtocolCompliance {
private void ensure206ContainsDateHeader(final HttpResponse response) {
if (response.getFirstHeader(HttpHeaders.DATE) == null) {
response.addHeader(HttpHeaders.DATE, DateUtils.formatDate(new Date()));
response.addHeader(HttpHeaders.DATE, DateUtils.formatStandardDate(Instant.now()));
}
}

View File

@ -265,7 +265,7 @@ class WarningValue {
parseError();
}
offs += m.end();
warnDate = DateUtils.parseDate(src.substring(curr+1,offs-1));
warnDate = DateUtils.toDate(DateUtils.parseStandardDate(src.substring(curr+1,offs-1)));
}
/*
@ -359,7 +359,7 @@ class WarningValue {
public String toString() {
if (warnDate != null) {
return String.format("%d %s %s \"%s\"", warnCode,
warnAgent, warnText, DateUtils.formatDate(warnDate));
warnAgent, warnText, DateUtils.formatStandardDate(DateUtils.toInstant(warnDate)));
} else {
return String.format("%d %s %s", warnCode, warnAgent, warnText);
}

View File

@ -0,0 +1,100 @@
/*
* ====================================================================
* 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 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.hamcrest.MatcherAssert;
import org.junit.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();
MatcherAssert.assertThat(DateSupport.isBefore(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false));
MatcherAssert.assertThat(DateSupport.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
MatcherAssert.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))));
MatcherAssert.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))));
MatcherAssert.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))));
MatcherAssert.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();
MatcherAssert.assertThat(DateSupport.isAfter(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false));
MatcherAssert.assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
MatcherAssert.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))));
MatcherAssert.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))));
MatcherAssert.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))));
MatcherAssert.assertThat(DateSupport.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
}
}

View File

@ -56,6 +56,10 @@ public class TestResponseCachingPolicy {
private Date tenSecondsFromNow;
private Date sixSecondsAgo;
static String formatDate(final Date date) {
return DateUtils.formatStandardDate(DateUtils.toInstant(date));
}
@Before
public void setUp() throws Exception {
now = new Date();
@ -65,7 +69,7 @@ public class TestResponseCachingPolicy {
policy = new ResponseCachingPolicy(0, true, false, false);
request = new BasicHttpRequest("GET","/");
response = new BasicHttpResponse(HttpStatus.SC_OK, "");
response.setHeader("Date", DateUtils.formatDate(new Date()));
response.setHeader("Date", formatDate(new Date()));
response.setHeader("Content-Length", "0");
}
@ -205,7 +209,7 @@ public class TestResponseCachingPolicy {
public void testNon206WithExplicitExpiresIsCacheable() {
final int status = getRandomStatus();
response.setCode(status);
response.setHeader("Expires", DateUtils.formatDate(new Date()));
response.setHeader("Expires", formatDate(new Date()));
Assert.assertTrue(policy.isResponseCacheable("GET", response));
}
@ -360,7 +364,7 @@ public class TestResponseCachingPolicy {
Assert.assertTrue(policy.isResponseCacheable("GET", response));
response = new BasicHttpResponse(HttpStatus.SC_OK, "");
response.setHeader("Date", DateUtils.formatDate(new Date()));
response.setHeader("Date", formatDate(new Date()));
response.addHeader("Cache-Control", "no-transform");
response.setHeader("Content-Length", "0");
@ -375,7 +379,7 @@ public class TestResponseCachingPolicy {
Assert.assertTrue(policy.isResponseCacheable("HEAD", response));
response = new BasicHttpResponse(HttpStatus.SC_OK, "");
response.setHeader("Date", DateUtils.formatDate(new Date()));
response.setHeader("Date", formatDate(new Date()));
response.addHeader("Cache-Control", "no-transform");
response.setHeader("Content-Length", "0");
@ -480,8 +484,8 @@ public class TestResponseCachingPolicy {
@Test
public void testResponsesWithMultipleDateHeadersAreNotCacheable() {
response.addHeader("Date", DateUtils.formatDate(now));
response.addHeader("Date", DateUtils.formatDate(sixSecondsAgo));
response.addHeader("Date", formatDate(now));
response.addHeader("Date", formatDate(sixSecondsAgo));
Assert.assertFalse(policy.isResponseCacheable("GET", response));
}
@ -491,8 +495,8 @@ public class TestResponseCachingPolicy {
request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
response.setHeader("Cache-Control", "public");
response.addHeader("Date", DateUtils.formatDate(now));
response.addHeader("Date", DateUtils.formatDate(sixSecondsAgo));
response.addHeader("Date", formatDate(now));
response.addHeader("Date", formatDate(sixSecondsAgo));
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@ -514,8 +518,8 @@ public class TestResponseCachingPolicy {
@Test
public void testResponsesWithMultipleExpiresHeadersAreNotCacheable() {
response.addHeader("Expires", DateUtils.formatDate(now));
response.addHeader("Expires", DateUtils.formatDate(sixSecondsAgo));
response.addHeader("Expires", formatDate(now));
response.addHeader("Expires", formatDate(sixSecondsAgo));
Assert.assertFalse(policy.isResponseCacheable("GET", response));
}
@ -525,8 +529,8 @@ public class TestResponseCachingPolicy {
request.setHeader("Authorization", StandardAuthScheme.BASIC + " QWxhZGRpbjpvcGVuIHNlc2FtZQ==");
response.setHeader("Cache-Control", "public");
response.addHeader("Expires", DateUtils.formatDate(now));
response.addHeader("Expires", DateUtils.formatDate(sixSecondsAgo));
response.addHeader("Expires", formatDate(now));
response.addHeader("Expires", formatDate(sixSecondsAgo));
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@ -587,8 +591,8 @@ public class TestResponseCachingPolicy {
@Test
public void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheable() {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -596,8 +600,8 @@ public class TestResponseCachingPolicy {
public void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheable() {
policy = new ResponseCachingPolicy(0, true, false, false);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -605,8 +609,8 @@ public class TestResponseCachingPolicy {
public void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0QueryCachingDisabled() {
policy = new ResponseCachingPolicy(0, true, true, false);
request = new BasicHttpRequest("GET", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -614,8 +618,8 @@ public class TestResponseCachingPolicy {
public void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0QueryCachingDisabled() {
policy = new ResponseCachingPolicy(0, true, true, false);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -658,8 +662,8 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -669,8 +673,8 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -680,8 +684,8 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@ -691,8 +695,8 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@ -713,8 +717,8 @@ public class TestResponseCachingPolicy {
@Test
public void getsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -723,8 +727,8 @@ public class TestResponseCachingPolicy {
public void headsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() {
policy = new ResponseCachingPolicy(0, true, false, false);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -733,8 +737,8 @@ public class TestResponseCachingPolicy {
public void getsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithExpires() {
policy = new ResponseCachingPolicy(0, true, true, true);
request = new BasicHttpRequest("GET", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@ -743,8 +747,8 @@ public class TestResponseCachingPolicy {
public void headsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithExpires() {
policy = new ResponseCachingPolicy(0, true, true, true);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.0 someproxy");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@ -752,8 +756,8 @@ public class TestResponseCachingPolicy {
@Test
public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExpires() {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -762,8 +766,8 @@ public class TestResponseCachingPolicy {
public void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExpires() {
policy = new ResponseCachingPolicy(0, true, false, false);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -772,8 +776,8 @@ public class TestResponseCachingPolicy {
public void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEvenWithExpires() {
policy = new ResponseCachingPolicy(0, true, true, true);
request = new BasicHttpRequest("GET", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@ -782,8 +786,8 @@ public class TestResponseCachingPolicy {
public void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEvenWithExpires() {
policy = new ResponseCachingPolicy(0, true, true, true);
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "HTTP/1.0 someproxy");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@ -793,8 +797,8 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("GET", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.1 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -805,24 +809,24 @@ public class TestResponseCachingPolicy {
request = new BasicHttpRequest("HEAD", "/foo?s=bar");
response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
response.setVersion(HttpVersion.HTTP_1_0);
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(tenSecondsFromNow));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(tenSecondsFromNow));
response.setHeader("Via", "1.1 someproxy");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@Test
public void notCacheableIfExpiresEqualsDateAndNoCacheControl() {
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(now));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(now));
response.removeHeaders("Cache-Control");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@Test
public void notCacheableIfExpiresPrecedesDateAndNoCacheControl() {
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Expires", DateUtils.formatDate(sixSecondsAgo));
response.setHeader("Date", formatDate(now));
response.setHeader("Expires", formatDate(sixSecondsAgo));
response.removeHeaders("Cache-Control");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@ -830,7 +834,7 @@ public class TestResponseCachingPolicy {
@Test
public void test302WithExplicitCachingHeaders() {
response.setCode(HttpStatus.SC_MOVED_TEMPORARILY);
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Date", formatDate(now));
response.setHeader("Cache-Control","max-age=300");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -839,7 +843,7 @@ public class TestResponseCachingPolicy {
public void test303WithExplicitCachingHeadersUnderDefaultBehavior() {
// RFC 2616 says: 303 should not be cached
response.setCode(HttpStatus.SC_SEE_OTHER);
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Date", formatDate(now));
response.setHeader("Cache-Control","max-age=300");
Assert.assertFalse(policy.isResponseCacheable(request, response));
}
@ -850,7 +854,7 @@ public class TestResponseCachingPolicy {
// response headers
policy = new ResponseCachingPolicy(0, true, false, true);
response.setCode(HttpStatus.SC_SEE_OTHER);
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Date", formatDate(now));
response.setHeader("Cache-Control","max-age=300");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -858,7 +862,7 @@ public class TestResponseCachingPolicy {
@Test
public void test307WithExplicitCachingHeaders() {
response.setCode(HttpStatus.SC_TEMPORARY_REDIRECT);
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Date", formatDate(now));
response.setHeader("Cache-Control","max-age=300");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}
@ -866,7 +870,7 @@ public class TestResponseCachingPolicy {
@Test
public void otherStatusCodesAreCacheableWithExplicitCachingHeaders() {
response.setCode(HttpStatus.SC_NOT_FOUND);
response.setHeader("Date", DateUtils.formatDate(now));
response.setHeader("Date", formatDate(now));
response.setHeader("Cache-Control","max-age=300");
Assert.assertTrue(policy.isResponseCacheable(request, response));
}

View File

@ -26,7 +26,7 @@
*/
package org.apache.hc.client5.http.impl.cache;
import java.util.Date;
import java.time.Instant;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.Header;
@ -203,8 +203,8 @@ public class TestWarningValue {
Assert.assertEquals(110, impl.getWarnCode());
Assert.assertEquals("fred", impl.getWarnAgent());
Assert.assertEquals("\"stale\"", impl.getWarnText());
final Date target = DateUtils.parseDate("Sun Nov 6 08:49:37 1994");
Assert.assertEquals(target, impl.getWarnDate());
final Instant target = DateUtils.parseStandardDate("Sun Nov 6 08:49:37 1994");
Assert.assertEquals(target, DateUtils.toInstant(impl.getWarnDate()));
}
@Test
@ -213,8 +213,8 @@ public class TestWarningValue {
Assert.assertEquals(110, impl.getWarnCode());
Assert.assertEquals("fred", impl.getWarnAgent());
Assert.assertEquals("\"stale\"", impl.getWarnText());
final Date target = DateUtils.parseDate("Sunday, 06-Nov-94 08:49:37 GMT");
Assert.assertEquals(target, impl.getWarnDate());
final Instant target = DateUtils.parseStandardDate("Sunday, 06-Nov-94 08:49:37 GMT");
Assert.assertEquals(target, DateUtils.toInstant(impl.getWarnDate()));
}
@Test
@ -223,8 +223,8 @@ public class TestWarningValue {
Assert.assertEquals(110, impl.getWarnCode());
Assert.assertEquals("fred", impl.getWarnAgent());
Assert.assertEquals("\"stale\"", impl.getWarnText());
final Date target = DateUtils.parseDate("Sun, 06 Nov 1994 08:49:37 GMT");
Assert.assertEquals(target, impl.getWarnDate());
final Instant target = DateUtils.parseStandardDate("Sun, 06 Nov 1994 08:49:37 GMT");
Assert.assertEquals(target, DateUtils.toInstant(impl.getWarnDate()));
}
}

View File

@ -33,7 +33,6 @@ import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
@ -46,6 +45,7 @@ import org.apache.hc.client5.http.config.Configurable;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
@ -70,8 +70,20 @@ import org.apache.hc.core5.util.Timeout;
*/
public class Request {
/**
* @deprecated This attribute is no longer supported as a part of the public API.
*/
@Deprecated
public static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
/**
* @deprecated This attribute is no longer supported as a part of the public API.
*/
@Deprecated
public static final Locale DATE_LOCALE = Locale.US;
/**
* @deprecated This attribute is no longer supported as a part of the public API.
*/
@Deprecated
public static final TimeZone TIME_ZONE = TimeZone.getTimeZone("GMT");
private final ClassicHttpRequest request;
@ -80,8 +92,6 @@ public class Request {
private Timeout responseTimeout;
private HttpHost proxy;
private SimpleDateFormat dateFormatter;
public static Request create(final Method method, final URI uri) {
return new Request(new HttpUriRequestBase(method.name(), uri));
}
@ -246,30 +256,22 @@ public class Request {
return this;
}
private SimpleDateFormat getDateFormat() {
if (this.dateFormatter == null) {
this.dateFormatter = new SimpleDateFormat(DATE_FORMAT, DATE_LOCALE);
this.dateFormatter.setTimeZone(TIME_ZONE);
}
return this.dateFormatter;
}
ClassicHttpRequest getRequest() {
return request;
}
public Request setDate(final Date date) {
this.request.setHeader(HttpHeader.DATE, getDateFormat().format(date));
this.request.setHeader(HttpHeader.DATE, DateUtils.formatStandardDate(DateUtils.toInstant(date)));
return this;
}
public Request setIfModifiedSince(final Date date) {
this.request.setHeader(HttpHeader.IF_MODIFIED_SINCE, getDateFormat().format(date));
this.request.setHeader(HttpHeader.IF_MODIFIED_SINCE, DateUtils.formatStandardDate(DateUtils.toInstant(date)));
return this;
}
public Request setIfUnmodifiedSince(final Date date) {
this.request.setHeader(HttpHeader.IF_UNMODIFIED_SINCE, getDateFormat().format(date));
this.request.setHeader(HttpHeader.IF_UNMODIFIED_SINCE, DateUtils.formatStandardDate(DateUtils.toInstant(date)));
return this;
}

View File

@ -32,9 +32,9 @@ import java.io.InterruptedIOException;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.net.UnknownHostException;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
@ -214,10 +214,10 @@ public class DefaultHttpRequestRetryStrategy implements HttpRequestRetryStrategy
try {
retryAfter = TimeValue.ofSeconds(Long.parseLong(value));
} catch (final NumberFormatException ignore) {
final Date retryAfterDate = DateUtils.parseDate(value);
final Instant retryAfterDate = DateUtils.parseStandardDate(value);
if (retryAfterDate != null) {
retryAfter =
TimeValue.ofMilliseconds(retryAfterDate.getTime() - System.currentTimeMillis());
TimeValue.ofMilliseconds(retryAfterDate.toEpochMilli() - System.currentTimeMillis());
}
}

View File

@ -26,7 +26,9 @@
*/
package org.apache.hc.client5.http.impl.cookie;
import java.util.Date;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler;
import org.apache.hc.client5.http.cookie.Cookie;
@ -46,11 +48,30 @@ import org.apache.hc.core5.util.Args;
public class BasicExpiresHandler extends AbstractCookieAttributeHandler implements CommonCookieAttributeHandler {
/** Valid date patterns */
private final String[] datePatterns;
private final DateTimeFormatter[] datePatterns;
/**
* @since 5.2
*/
public BasicExpiresHandler(final DateTimeFormatter... datePatterns) {
this.datePatterns = datePatterns;
}
/**
* @deprecated Use {@link #BasicExpiresHandler(DateTimeFormatter...)}
*/
@Deprecated
public BasicExpiresHandler(final String[] datePatterns) {
Args.notNull(datePatterns, "Array of date patterns");
this.datePatterns = datePatterns.clone();
this.datePatterns = new DateTimeFormatter[datePatterns.length];
for (int i = 0; i < datePatterns.length; i++) {
this.datePatterns[i] = new DateTimeFormatterBuilder()
.parseLenient()
.parseCaseInsensitive()
.appendPattern(datePatterns[i])
.toFormatter();
}
}
@Override
@ -60,12 +81,12 @@ public class BasicExpiresHandler extends AbstractCookieAttributeHandler implemen
if (value == null) {
throw new MalformedCookieException("Missing value for 'expires' attribute");
}
final Date expiry = DateUtils.parseDate(value, this.datePatterns);
final Instant expiry = DateUtils.parseDate(value, this.datePatterns);
if (expiry == null) {
throw new MalformedCookieException("Invalid 'expires' attribute: "
+ value);
}
cookie.setExpiryDate(expiry);
cookie.setExpiryDate(DateUtils.toDate(expiry));
}
@Override

View File

@ -26,11 +26,13 @@
*/
package org.apache.hc.client5.http.impl.cookie;
import java.time.Instant;
import java.time.Month;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.BitSet;
import java.util.Calendar;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -39,6 +41,7 @@ import org.apache.hc.client5.http.cookie.CommonCookieAttributeHandler;
import org.apache.hc.client5.http.cookie.Cookie;
import org.apache.hc.client5.http.cookie.MalformedCookieException;
import org.apache.hc.client5.http.cookie.SetCookie;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.util.Args;
@ -54,8 +57,6 @@ import org.apache.hc.core5.util.Tokenizer;
@Contract(threading = ThreadingBehavior.STATELESS)
public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements CommonCookieAttributeHandler {
static final TimeZone UTC = TimeZone.getTimeZone("UTC");
private static final BitSet DELIMS;
static {
final BitSet bitSet = new BitSet();
@ -74,21 +75,21 @@ public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements
}
DELIMS = bitSet;
}
private static final Map<String, Integer> MONTHS;
private static final Map<String, Month> MONTHS;
static {
final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>(12);
map.put("jan", Calendar.JANUARY);
map.put("feb", Calendar.FEBRUARY);
map.put("mar", Calendar.MARCH);
map.put("apr", Calendar.APRIL);
map.put("may", Calendar.MAY);
map.put("jun", Calendar.JUNE);
map.put("jul", Calendar.JULY);
map.put("aug", Calendar.AUGUST);
map.put("sep", Calendar.SEPTEMBER);
map.put("oct", Calendar.OCTOBER);
map.put("nov", Calendar.NOVEMBER);
map.put("dec", Calendar.DECEMBER);
final ConcurrentHashMap<String, Month> map = new ConcurrentHashMap<>(12);
map.put("jan", Month.JANUARY);
map.put("feb", Month.FEBRUARY);
map.put("mar", Month.MARCH);
map.put("apr", Month.APRIL);
map.put("may", Month.MAY);
map.put("jun", Month.JUNE);
map.put("jul", Month.JULY);
map.put("aug", Month.AUGUST);
map.put("sep", Month.SEPTEMBER);
map.put("oct", Month.OCTOBER);
map.put("nov", Month.NOVEMBER);
map.put("dec", Month.DECEMBER);
MONTHS = map;
}
@ -114,7 +115,8 @@ public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements
final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, value.length());
final StringBuilder content = new StringBuilder();
int second = 0, minute = 0, hour = 0, day = 0, month = 0, year = 0;
int second = 0, minute = 0, hour = 0, day = 0, year = 0;
Month month = Month.JANUARY;
boolean foundTime = false, foundDayOfMonth = false, foundMonth = false, foundYear = false;
try {
while (!cursor.atEnd()) {
@ -147,7 +149,7 @@ public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements
final Matcher matcher = MONTH_PATTERN.matcher(content);
if (matcher.matches()) {
foundMonth = true;
month = MONTHS.get(matcher.group(1).toLowerCase(Locale.ROOT)).intValue();
month = MONTHS.get(matcher.group(1).toLowerCase(Locale.ROOT));
continue;
}
}
@ -176,16 +178,9 @@ public class LaxExpiresHandler extends AbstractCookieAttributeHandler implements
throw new MalformedCookieException("Invalid 'expires' attribute: " + value);
}
final Calendar c = Calendar.getInstance();
c.setTimeZone(UTC);
c.setTimeInMillis(0L);
c.set(Calendar.SECOND, second);
c.set(Calendar.MINUTE, minute);
c.set(Calendar.HOUR_OF_DAY, hour);
c.set(Calendar.DAY_OF_MONTH, day);
c.set(Calendar.MONTH, month);
c.set(Calendar.YEAR, year);
cookie.setExpiryDate(c.getTime());
final Instant expiryDate = ZonedDateTime.of(year, month.getValue(), day, hour, minute, second, 0,
ZoneId.of("UTC")).toInstant();
cookie.setExpiryDate(DateUtils.toDate(expiryDate));
}
private void skipDelims(final CharSequence buf, final Tokenizer.Cursor cursor) {

View File

@ -33,6 +33,7 @@ import org.apache.hc.client5.http.cookie.CookieSpec;
import org.apache.hc.client5.http.cookie.CookieSpecFactory;
import org.apache.hc.client5.http.cookie.MalformedCookieException;
import org.apache.hc.client5.http.psl.PublicSuffixMatcher;
import org.apache.hc.client5.http.utils.DateUtils;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.protocol.HttpContext;
@ -88,7 +89,7 @@ public class RFC6265CookieSpecFactory implements CookieSpecFactory {
new BasicMaxAgeHandler(),
new BasicSecureHandler(),
new BasicHttpOnlyHandler(),
new BasicExpiresHandler(RFC6265StrictSpec.DATE_PATTERNS));
new BasicExpiresHandler(DateUtils.STANDARD_PATTERNS));
break;
case IE_MEDIUM_SECURITY:
this.cookieSpec = new RFC6265LaxSpec(
@ -105,7 +106,7 @@ public class RFC6265CookieSpecFactory implements CookieSpecFactory {
new BasicMaxAgeHandler(),
new BasicSecureHandler(),
new BasicHttpOnlyHandler(),
new BasicExpiresHandler(RFC6265StrictSpec.DATE_PATTERNS));
new BasicExpiresHandler(DateUtils.STANDARD_PATTERNS));
break;
default:
this.cookieSpec = new RFC6265LaxSpec(

View File

@ -42,19 +42,13 @@ import org.apache.hc.core5.annotation.ThreadingBehavior;
@Contract(threading = ThreadingBehavior.SAFE)
public class RFC6265StrictSpec extends RFC6265CookieSpecBase {
final static String[] DATE_PATTERNS = {
DateUtils.PATTERN_RFC1123,
DateUtils.PATTERN_RFC1036,
DateUtils.PATTERN_ASCTIME
};
public RFC6265StrictSpec() {
super(new BasicPathHandler(),
new BasicDomainHandler(),
new BasicMaxAgeHandler(),
new BasicSecureHandler(),
new BasicHttpOnlyHandler(),
new BasicExpiresHandler(DATE_PATTERNS));
new BasicExpiresHandler(DateUtils.STANDARD_PATTERNS));
}
RFC6265StrictSpec(final CommonCookieAttributeHandler... handlers) {

View File

@ -27,14 +27,14 @@
package org.apache.hc.client5.http.utils;
import java.lang.ref.SoftReference;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DateTimeParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import org.apache.hc.core5.http.Header;
@ -55,35 +55,192 @@ public final class DateUtils {
*/
public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
/**
* Date formatter used to parse HTTP date headers in RFC 1123 format.
*
* @since 5.2
*/
public static final DateTimeFormatter FORMATTER_RFC1123 = new DateTimeFormatterBuilder()
.parseLenient()
.parseCaseInsensitive()
.appendPattern(PATTERN_RFC1123)
.toFormatter();
/**
* Date format pattern used to parse HTTP date headers in RFC 1036 format.
*/
public static final String PATTERN_RFC1036 = "EEE, dd-MMM-yy HH:mm:ss zzz";
/**
* Date formatter used to parse HTTP date headers in RFC 1036 format.
*
* @since 5.2
*/
public static final DateTimeFormatter FORMATTER_RFC1036 = new DateTimeFormatterBuilder()
.parseLenient()
.parseCaseInsensitive()
.appendPattern(PATTERN_RFC1036)
.toFormatter();
/**
* Date format pattern used to parse HTTP date headers in ANSI C
* {@code asctime()} format.
*/
public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy";
private static final String[] DEFAULT_PATTERNS = new String[] {
PATTERN_RFC1123,
PATTERN_RFC1036,
PATTERN_ASCTIME
/**
* Date formatter used to parse HTTP date headers in in ANSI C {@code asctime()} format.
*
* @since 5.2
*/
public static final DateTimeFormatter FORMATTER_ASCTIME = new DateTimeFormatterBuilder()
.parseLenient()
.parseCaseInsensitive()
.appendPattern(PATTERN_ASCTIME)
.toFormatter();
/**
* Standard date formatters: {@link #FORMATTER_RFC1123}, {@link #FORMATTER_RFC1036}, {@link #FORMATTER_ASCTIME}.
*
* @since 5.2
*/
public static final DateTimeFormatter[] STANDARD_PATTERNS = new DateTimeFormatter[] {
FORMATTER_RFC1123,
FORMATTER_RFC1036,
FORMATTER_ASCTIME
};
private static final Date DEFAULT_TWO_DIGIT_YEAR_START;
static final ZoneId GMT_ID = ZoneId.of("GMT");
public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
static {
final Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(GMT);
calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
DEFAULT_TWO_DIGIT_YEAR_START = calendar.getTime();
/**
* @since 5.2
*/
public static Date toDate(final Instant instant) {
return instant != null ? new Date(instant.toEpochMilli()) : null;
}
/**
* @since 5.2
*/
public static Instant toInstant(final Date date) {
return date != null ? Instant.ofEpochMilli(date.getTime()) : null;
}
/**
* @since 5.2
*/
public static LocalDateTime toUTC(final Instant instant) {
return instant != null ? instant.atZone(ZoneOffset.UTC).toLocalDateTime() : null;
}
/**
* @since 5.2
*/
public static LocalDateTime toUTC(final Date date) {
return toUTC(toInstant(date));
}
/**
* Parses the date value using the given date/time formats.
*
* @param dateValue the instant value to parse
* @param dateFormatters the date/time formats to use
*
* @return the parsed instant or null if input could not be parsed
*
* @since 5.2
*/
public static Instant parseDate(final String dateValue, final DateTimeFormatter... dateFormatters) {
Args.notNull(dateValue, "Date value");
String v = dateValue;
// trim single quotes around date if present
// see issue #5279
if (v.length() > 1 && v.startsWith("'") && v.endsWith("'")) {
v = v.substring (1, v.length() - 1);
}
for (final DateTimeFormatter dateFormatter : dateFormatters) {
try {
return Instant.from(dateFormatter.parse(v));
} catch (final DateTimeParseException ignore) {
}
}
return null;
}
/**
* Parses the instant value using the standard date/time formats ({@link #PATTERN_RFC1123},
* {@link #PATTERN_RFC1036}, {@link #PATTERN_ASCTIME}).
*
* @param dateValue the instant value to parse
* @param dateFormatters the date/time formats to use
*
* @return the parsed instant or null if input could not be parsed
*
* @since 5.2
*/
public static Instant parseStandardDate(final String dateValue) {
return parseDate(dateValue, STANDARD_PATTERNS);
}
/**
* Parses an instant value from a header with the given name.
*
* @param headers message headers
* @param headerName header name
*
* @return the parsed instant or null if input could not be parsed
*
* @since 5.2
*/
public static Instant parseStandardDate(final MessageHeaders headers, final String headerName) {
if (headers == null) {
return null;
}
final Header header = headers.getFirstHeader(headerName);
if (header == null) {
return null;
}
return parseStandardDate(header.getValue());
}
/**
* Formats the given instant according to the RFC 1123 pattern.
*
* @param instant Instant to format.
* @return An RFC 1123 formatted instant string.
*
* @see #PATTERN_RFC1123
*
* @since 5.2
*/
public static String formatStandardDate(final Instant instant) {
return formatDate(instant, FORMATTER_RFC1123);
}
/**
* Formats the given date according to the specified pattern.
*
* @param instant Instant to format.
* @param dateTimeFormatter The pattern to use for formatting the instant.
* @return A formatted instant string.
*
* @throws IllegalArgumentException If the given date pattern is invalid.
*
* @since 5.2
*/
public static String formatDate(final Instant instant, final DateTimeFormatter dateTimeFormatter) {
Args.notNull(instant, "Instant");
Args.notNull(dateTimeFormatter, "DateTimeFormatter");
return dateTimeFormatter.format(instant.atZone(GMT_ID));
}
/**
* @deprecated This attribute is no longer supported as a part of the public API.
*/
@Deprecated
public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
/**
* Parses a date value. The formats used for parsing the date value are retrieved from
* the default http params.
@ -91,7 +248,10 @@ public final class DateUtils {
* @param dateValue the date value to parse
*
* @return the parsed date or null if input could not be parsed
*
* @deprecated Use {@link #parseStandardDate(String)}
*/
@Deprecated
public static Date parseDate(final String dateValue) {
return parseDate(dateValue, null, null);
}
@ -105,16 +265,12 @@ public final class DateUtils {
* @return the parsed date or null if input could not be parsed
*
* @since 5.0
*
* @deprecated Use {@link #parseStandardDate(MessageHeaders, String)}
*/
@Deprecated
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);
return toDate(parseStandardDate(headers, headerName));
}
/**
@ -130,7 +286,10 @@ public final class DateUtils {
* the second message.
*
* @since 5.0
*
* @deprecated This method is no longer supported as a part of the public API.
*/
@Deprecated
public static boolean isAfter(
final MessageHeaders message1,
final MessageHeaders message2,
@ -166,7 +325,10 @@ public final class DateUtils {
* the second message.
*
* @since 5.0
*
* @deprecated This method is no longer supported as a part of the public API.
*/
@Deprecated
public static boolean isBefore(
final MessageHeaders message1,
final MessageHeaders message2,
@ -190,53 +352,53 @@ public final class DateUtils {
}
/**
* Parses the date value using the given date formats.
* Parses the date value using the given date/time formats.
*
* @param dateValue the date value to parse
* @param dateFormats the date formats to use
* @param dateFormats the date/time formats to use
*
* @return the parsed date or null if input could not be parsed
*
* @deprecated Use {@link #parseDate(String, DateTimeFormatter...)}
*/
@Deprecated
public static Date parseDate(final String dateValue, final String[] dateFormats) {
return parseDate(dateValue, dateFormats, null);
}
/**
* Parses the date value using the given date formats.
* Parses the date value using the given date/time formats.
*
* @param dateValue the date value to parse
* @param dateFormats the date formats to use
* @param dateFormats the date/time formats to use
* @param startDate During parsing, two digit years will be placed in the range
* {@code startDate} to {@code startDate + 100 years}. This value may
* be {@code null}. When {@code null} is given as a parameter, year
* {@code 2000} will be used.
*
* @return the parsed date or null if input could not be parsed
*
* @deprecated Use {@link #parseDate(String, DateTimeFormatter...)}
*/
@Deprecated
public static Date parseDate(
final String dateValue,
final String[] dateFormats,
final Date startDate) {
Args.notNull(dateValue, "Date value");
final String[] localDateFormats = dateFormats != null ? dateFormats : DEFAULT_PATTERNS;
final Date localStartDate = startDate != null ? startDate : DEFAULT_TWO_DIGIT_YEAR_START;
String v = dateValue;
// trim single quotes around date if present
// see issue #5279
if (v.length() > 1 && v.startsWith("'") && v.endsWith("'")) {
v = v.substring (1, v.length() - 1);
}
for (final String dateFormat : localDateFormats) {
final SimpleDateFormat dateParser = DateFormatHolder.formatFor(dateFormat);
dateParser.set2DigitYearStart(localStartDate);
final ParsePosition pos = new ParsePosition(0);
final Date result = dateParser.parse(v, pos);
if (pos.getIndex() != 0) {
return result;
final DateTimeFormatter[] dateTimeFormatters;
if (dateFormats != null) {
dateTimeFormatters = new DateTimeFormatter[dateFormats.length];
for (int i = 0; i < dateFormats.length; i++) {
dateTimeFormatters[i] = new DateTimeFormatterBuilder()
.parseLenient()
.parseCaseInsensitive()
.appendPattern(dateFormats[i])
.toFormatter();
}
} else {
dateTimeFormatters = STANDARD_PATTERNS;
}
return null;
return toDate(parseDate(dateValue, dateTimeFormatters));
}
/**
@ -246,15 +408,16 @@ public final class DateUtils {
* @return An RFC 1123 formatted date string.
*
* @see #PATTERN_RFC1123
*
* @deprecated Use {@link #formatStandardDate(Instant)}
*/
@Deprecated
public static String formatDate(final Date date) {
return formatDate(date, PATTERN_RFC1123);
return formatStandardDate(toInstant(date));
}
/**
* Formats the given date according to the specified pattern. The pattern
* must conform to that used by the {@link SimpleDateFormat simple date
* format} class.
* Formats the given date according to the specified pattern.
*
* @param date The date to format.
* @param pattern The pattern to use for formatting the date.
@ -262,72 +425,28 @@ public final class DateUtils {
*
* @throws IllegalArgumentException If the given date pattern is invalid.
*
* @see SimpleDateFormat
* @deprecated Use {@link #formatDate(Instant, DateTimeFormatter)}
*/
@Deprecated
public static String formatDate(final Date date, final String pattern) {
Args.notNull(date, "Date");
Args.notNull(pattern, "Pattern");
final SimpleDateFormat formatter = DateFormatHolder.formatFor(pattern);
return formatter.format(date);
return DateTimeFormatter.ofPattern(pattern).format(toInstant(date).atZone(GMT_ID));
}
/**
* Clears thread-local variable containing {@link java.text.DateFormat} cache.
*
* @since 4.3
*
* @deprecated Noop method. Do not use.
*/
@Deprecated
public static void clearThreadLocal() {
DateFormatHolder.clearThreadLocal();
}
/** This class should not be instantiated. */
private DateUtils() {
}
/**
* A factory for {@link SimpleDateFormat}s. The instances are stored in a
* threadlocal way because SimpleDateFormat is not threadsafe as noted in
* {@link SimpleDateFormat its javadoc}.
*
*/
final static class DateFormatHolder {
private static final ThreadLocal<SoftReference<Map<String, SimpleDateFormat>>> THREADLOCAL_FORMATS = new ThreadLocal<>();
/**
* creates a {@link SimpleDateFormat} for the requested format string.
*
* @param pattern
* a non-{@code null} format String according to
* {@link SimpleDateFormat}. The format is not checked against
* {@code null} since all paths go through
* {@link DateUtils}.
* @return the requested format. This simple dateformat should not be used
* to {@link SimpleDateFormat#applyPattern(String) apply} to a
* different pattern.
*/
public static SimpleDateFormat formatFor(final String pattern) {
final SoftReference<Map<String, SimpleDateFormat>> ref = THREADLOCAL_FORMATS.get();
Map<String, SimpleDateFormat> formats = ref == null ? null : ref.get();
if (formats == null) {
formats = new HashMap<>();
THREADLOCAL_FORMATS.set(new SoftReference<>(formats));
}
SimpleDateFormat format = formats.get(pattern);
if (format == null) {
format = new SimpleDateFormat(pattern, Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
formats.put(pattern, format);
}
return format;
}
public static void clearThreadLocal() {
THREADLOCAL_FORMATS.remove();
}
}
}

View File

@ -31,7 +31,8 @@ import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.Date;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import javax.net.ssl.SSLException;
@ -85,7 +86,7 @@ public class TestDefaultHttpRequestRetryStrategy {
public void testRetryAfterHeaderAsDate() throws Exception {
this.retryStrategy = new DefaultHttpRequestRetryStrategy(3, TimeValue.ZERO_MILLISECONDS);
final HttpResponse response = new BasicHttpResponse(503, "Oopsie");
response.setHeader(HttpHeaders.RETRY_AFTER, DateUtils.formatDate(new Date(System.currentTimeMillis() + 100000L)));
response.setHeader(HttpHeaders.RETRY_AFTER, DateUtils.formatStandardDate(Instant.now().plus(100, ChronoUnit.SECONDS)));
Assert.assertTrue(this.retryStrategy.getRetryInterval(response, 3, null).compareTo(TimeValue.ZERO_MILLISECONDS) > 0);
}
@ -93,7 +94,7 @@ public class TestDefaultHttpRequestRetryStrategy {
@Test
public void testRetryAfterHeaderAsPastDate() throws Exception {
final HttpResponse response = new BasicHttpResponse(503, "Oopsie");
response.setHeader(HttpHeaders.RETRY_AFTER, DateUtils.formatDate(new Date(System.currentTimeMillis() - 100000L)));
response.setHeader(HttpHeaders.RETRY_AFTER, DateUtils.formatStandardDate(Instant.now().minus(100, ChronoUnit.SECONDS)));
Assert.assertEquals(TimeValue.ofMilliseconds(1234L), this.retryStrategy.getRetryInterval(response, 3, null));
}

View File

@ -27,11 +27,8 @@
package org.apache.hc.client5.http.impl.cookie;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Arrays;
import java.util.Date;
import java.util.Locale;
import org.apache.hc.client5.http.cookie.Cookie;
import org.apache.hc.client5.http.cookie.CookieAttributeHandler;
@ -327,21 +324,16 @@ public class TestBasicCookieAttribHandlers {
@Test
public void testBasicExpiresParse() throws Exception {
final BasicClientCookie cookie = new BasicClientCookie("name", "value");
final CookieAttributeHandler h = new BasicExpiresHandler(new String[] {DateUtils.PATTERN_RFC1123});
final CookieAttributeHandler h = new BasicExpiresHandler(DateUtils.FORMATTER_RFC1123);
final DateFormat dateformat = new SimpleDateFormat(DateUtils.PATTERN_RFC1123, Locale.US);
dateformat.setTimeZone(DateUtils.GMT);
final Date now = new Date();
h.parse(cookie, dateformat.format(now));
h.parse(cookie, DateUtils.formatStandardDate(Instant.now()));
Assert.assertNotNull(cookie.getExpiryDate());
}
@Test
public void testBasicExpiresParseInvalid() throws Exception {
final BasicClientCookie cookie = new BasicClientCookie("name", "value");
final CookieAttributeHandler h = new BasicExpiresHandler(new String[] {DateUtils.PATTERN_RFC1123});
final CookieAttributeHandler h = new BasicExpiresHandler(DateUtils.FORMATTER_RFC1123);
Assert.assertThrows(MalformedCookieException.class, () ->
h.parse(cookie, "garbage"));
Assert.assertThrows(MalformedCookieException.class, () ->
@ -351,8 +343,7 @@ public class TestBasicCookieAttribHandlers {
@SuppressWarnings("unused")
@Test
public void testBasicExpiresInvalidInput() throws Exception {
Assert.assertThrows(NullPointerException.class, () -> new BasicExpiresHandler(null));
final CookieAttributeHandler h = new BasicExpiresHandler(new String[] {DateUtils.PATTERN_RFC1123});
final CookieAttributeHandler h = new BasicExpiresHandler(DateUtils.FORMATTER_RFC1123);
Assert.assertThrows(NullPointerException.class, () -> h.parse(null, null));
}

View File

@ -27,11 +27,12 @@
package org.apache.hc.client5.http.impl.cookie;
import java.util.Calendar;
import java.util.Date;
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;
import org.apache.hc.client5.http.cookie.CookieAttributeHandler;
import org.apache.hc.client5.http.cookie.MalformedCookieException;
import org.apache.hc.client5.http.utils.DateUtils;
import org.junit.Assert;
import org.junit.Test;
@ -97,18 +98,16 @@ public class TestLaxCookieAttribHandlers {
final CookieAttributeHandler h = new LaxExpiresHandler();
h.parse(cookie, "1:0:12 8-jan-2012");
final Date expiryDate = cookie.getExpiryDate();
final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryDate());
Assert.assertNotNull(expiryDate);
final Calendar c = Calendar.getInstance();
c.setTimeZone(LaxExpiresHandler.UTC);
c.setTime(expiryDate);
Assert.assertEquals(2012, c.get(Calendar.YEAR));
Assert.assertEquals(Calendar.JANUARY, c.get(Calendar.MONTH));
Assert.assertEquals(8, c.get(Calendar.DAY_OF_MONTH));
Assert.assertEquals(1, c.get(Calendar.HOUR_OF_DAY));
Assert.assertEquals(0, c.get(Calendar.MINUTE));
Assert.assertEquals(12, c.get(Calendar.SECOND));
Assert.assertEquals(0, c.get(Calendar.MILLISECOND));
Assert.assertEquals(2012, expiryDate.get(ChronoField.YEAR));
Assert.assertEquals(1, expiryDate.get(ChronoField.MONTH_OF_YEAR));
Assert.assertEquals(8, expiryDate.get(ChronoField.DAY_OF_MONTH));
Assert.assertEquals(1, expiryDate.get(ChronoField.HOUR_OF_DAY));
Assert.assertEquals(0, expiryDate.get(ChronoField.MINUTE_OF_HOUR));
Assert.assertEquals(12, expiryDate.get(ChronoField.SECOND_OF_MINUTE));
Assert.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND));
}
@Test
@ -157,18 +156,16 @@ public class TestLaxCookieAttribHandlers {
final CookieAttributeHandler h = new LaxExpiresHandler();
h.parse(cookie, "1:59:00blah; 8-feb-2000");
final Date expiryDate = cookie.getExpiryDate();
final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryDate());
Assert.assertNotNull(expiryDate);
final Calendar c = Calendar.getInstance();
c.setTimeZone(LaxExpiresHandler.UTC);
c.setTime(expiryDate);
Assert.assertEquals(2000, c.get(Calendar.YEAR));
Assert.assertEquals(Calendar.FEBRUARY, c.get(Calendar.MONTH));
Assert.assertEquals(8, c.get(Calendar.DAY_OF_MONTH));
Assert.assertEquals(1, c.get(Calendar.HOUR_OF_DAY));
Assert.assertEquals(59, c.get(Calendar.MINUTE));
Assert.assertEquals(0, c.get(Calendar.SECOND));
Assert.assertEquals(0, c.get(Calendar.MILLISECOND));
Assert.assertEquals(2000, expiryDate.get(ChronoField.YEAR_OF_ERA));
Assert.assertEquals(2, expiryDate.get(ChronoField.MONTH_OF_YEAR));
Assert.assertEquals(8, expiryDate.get(ChronoField.DAY_OF_MONTH));
Assert.assertEquals(1, expiryDate.get(ChronoField.HOUR_OF_DAY));
Assert.assertEquals(59, expiryDate.get(ChronoField.MINUTE_OF_HOUR));
Assert.assertEquals(0, expiryDate.get(ChronoField.SECOND_OF_MINUTE));
Assert.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND));
}
@Test
@ -201,18 +198,16 @@ public class TestLaxCookieAttribHandlers {
final CookieAttributeHandler h = new LaxExpiresHandler();
h.parse(cookie, "12:00:00 8blah;mar;1880");
final Date expiryDate = cookie.getExpiryDate();
final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryDate());
Assert.assertNotNull(expiryDate);
final Calendar c = Calendar.getInstance();
c.setTimeZone(LaxExpiresHandler.UTC);
c.setTime(expiryDate);
Assert.assertEquals(1880, c.get(Calendar.YEAR));
Assert.assertEquals(Calendar.MARCH, c.get(Calendar.MONTH));
Assert.assertEquals(8, c.get(Calendar.DAY_OF_MONTH));
Assert.assertEquals(12, c.get(Calendar.HOUR_OF_DAY));
Assert.assertEquals(0, c.get(Calendar.MINUTE));
Assert.assertEquals(0, c.get(Calendar.SECOND));
Assert.assertEquals(0, c.get(Calendar.MILLISECOND));
Assert.assertEquals(1880, expiryDate.get(ChronoField.YEAR));
Assert.assertEquals(3, expiryDate.get(ChronoField.MONTH_OF_YEAR));
Assert.assertEquals(8, expiryDate.get(ChronoField.DAY_OF_MONTH));
Assert.assertEquals(12, expiryDate.get(ChronoField.HOUR_OF_DAY));
Assert.assertEquals(0, expiryDate.get(ChronoField.MINUTE_OF_HOUR));
Assert.assertEquals(0, expiryDate.get(ChronoField.SECOND_OF_MINUTE));
Assert.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND));
}
@Test
@ -229,18 +224,16 @@ public class TestLaxCookieAttribHandlers {
final CookieAttributeHandler h = new LaxExpiresHandler();
h.parse(cookie, "23:59:59; 1-ApriLLLLL-2008");
final Date expiryDate = cookie.getExpiryDate();
final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryDate());
Assert.assertNotNull(expiryDate);
final Calendar c = Calendar.getInstance();
c.setTimeZone(LaxExpiresHandler.UTC);
c.setTime(expiryDate);
Assert.assertEquals(2008, c.get(Calendar.YEAR));
Assert.assertEquals(Calendar.APRIL, c.get(Calendar.MONTH));
Assert.assertEquals(1, c.get(Calendar.DAY_OF_MONTH));
Assert.assertEquals(23, c.get(Calendar.HOUR_OF_DAY));
Assert.assertEquals(59, c.get(Calendar.MINUTE));
Assert.assertEquals(59, c.get(Calendar.SECOND));
Assert.assertEquals(0, c.get(Calendar.MILLISECOND));
Assert.assertEquals(2008, expiryDate.get(ChronoField.YEAR));
Assert.assertEquals(4, expiryDate.get(ChronoField.MONTH_OF_YEAR));
Assert.assertEquals(1, expiryDate.get(ChronoField.DAY_OF_MONTH));
Assert.assertEquals(23, expiryDate.get(ChronoField.HOUR_OF_DAY));
Assert.assertEquals(59, expiryDate.get(ChronoField.MINUTE_OF_HOUR));
Assert.assertEquals(59, expiryDate.get(ChronoField.SECOND_OF_MINUTE));
Assert.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND));
}
@Test
@ -273,18 +266,16 @@ public class TestLaxCookieAttribHandlers {
final CookieAttributeHandler h = new LaxExpiresHandler();
h.parse(cookie, "23:59:59; 1-Apr-2008blah");
final Date expiryDate = cookie.getExpiryDate();
final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryDate());
Assert.assertNotNull(expiryDate);
final Calendar c = Calendar.getInstance();
c.setTimeZone(LaxExpiresHandler.UTC);
c.setTime(expiryDate);
Assert.assertEquals(2008, c.get(Calendar.YEAR));
Assert.assertEquals(Calendar.APRIL, c.get(Calendar.MONTH));
Assert.assertEquals(1, c.get(Calendar.DAY_OF_MONTH));
Assert.assertEquals(23, c.get(Calendar.HOUR_OF_DAY));
Assert.assertEquals(59, c.get(Calendar.MINUTE));
Assert.assertEquals(59, c.get(Calendar.SECOND));
Assert.assertEquals(0, c.get(Calendar.MILLISECOND));
Assert.assertEquals(2008, expiryDate.get(ChronoField.YEAR));
Assert.assertEquals(4, expiryDate.get(ChronoField.MONTH_OF_YEAR));
Assert.assertEquals(1, expiryDate.get(ChronoField.DAY_OF_MONTH));
Assert.assertEquals(23, expiryDate.get(ChronoField.HOUR_OF_DAY));
Assert.assertEquals(59, expiryDate.get(ChronoField.MINUTE_OF_HOUR));
Assert.assertEquals(59, expiryDate.get(ChronoField.SECOND_OF_MINUTE));
Assert.assertEquals(0, expiryDate.get(ChronoField.MILLI_OF_SECOND));
}
@Test
@ -293,12 +284,10 @@ public class TestLaxCookieAttribHandlers {
final CookieAttributeHandler h = new LaxExpiresHandler();
h.parse(cookie, "23:59:59; 1-Apr-70");
final Date expiryDate = cookie.getExpiryDate();
final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryDate());
Assert.assertNotNull(expiryDate);
final Calendar c = Calendar.getInstance();
c.setTimeZone(LaxExpiresHandler.UTC);
c.setTime(expiryDate);
Assert.assertEquals(1970, c.get(Calendar.YEAR));
Assert.assertEquals(1970, expiryDate.get(ChronoField.YEAR));
}
@Test
@ -307,12 +296,10 @@ public class TestLaxCookieAttribHandlers {
final CookieAttributeHandler h = new LaxExpiresHandler();
h.parse(cookie, "23:59:59; 1-Apr-99");
final Date expiryDate = cookie.getExpiryDate();
final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryDate());
Assert.assertNotNull(expiryDate);
final Calendar c = Calendar.getInstance();
c.setTimeZone(LaxExpiresHandler.UTC);
c.setTime(expiryDate);
Assert.assertEquals(1999, c.get(Calendar.YEAR));
Assert.assertEquals(1999, expiryDate.get(ChronoField.YEAR));
}
@Test
@ -321,12 +308,10 @@ public class TestLaxCookieAttribHandlers {
final CookieAttributeHandler h = new LaxExpiresHandler();
h.parse(cookie, "23:59:59; 1-Apr-00");
final Date expiryDate = cookie.getExpiryDate();
final LocalDateTime expiryDate = DateUtils.toUTC(cookie.getExpiryDate());
Assert.assertNotNull(expiryDate);
final Calendar c = Calendar.getInstance();
c.setTimeZone(LaxExpiresHandler.UTC);
c.setTime(expiryDate);
Assert.assertEquals(2000, c.get(Calendar.YEAR));
Assert.assertEquals(2000, expiryDate.get(ChronoField.YEAR));
}
}

View File

@ -27,14 +27,17 @@
package org.apache.hc.client5.http.utils;
import java.util.Calendar;
import java.time.Instant;
import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
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.hamcrest.MatcherAssert;
import org.junit.Assert;
import org.junit.Test;
@ -43,103 +46,71 @@ import org.junit.Test;
*/
public class TestDateUtils {
private static Date createDate(final int year, final int month, final int day) {
final Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(DateUtils.GMT);
calendar.setTimeInMillis(0);
calendar.set(year, month, day);
return calendar.getTime();
private static Instant createInstant(final int year, final Month month, final int day) {
return LocalDate.of(year, month, day).atStartOfDay(ZoneId.of("GMT")).toInstant();
}
private static Date createDate(final int year, final Month month, final int day) {
final Instant instant = createInstant(year, month, day);
return new Date(instant.toEpochMilli());
}
@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"));
final Instant instant = createInstant(2005, Month.OCTOBER, 14);
Assert.assertEquals(instant, DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 GMT", DateUtils.FORMATTER_RFC1123));
Assert.assertEquals(instant, DateUtils.parseDate("Friday, 14 Oct 2005 00:00:00 GMT", DateUtils.FORMATTER_RFC1123));
Assert.assertEquals(instant, DateUtils.parseDate("Fri, 14-Oct-2005 00:00:00 GMT", DateUtils.FORMATTER_RFC1036));
Assert.assertEquals(instant, DateUtils.parseDate("Friday, 14-Oct-2005 00:00:00 GMT", DateUtils.FORMATTER_RFC1036));
Assert.assertEquals(instant.minus(2, ChronoUnit.HOURS),
DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 CET", DateUtils.FORMATTER_RFC1123));
Assert.assertEquals(instant.minus(2, ChronoUnit.HOURS),
DateUtils.parseDate("Fri, 14-Oct-05 00:00:00 CET", DateUtils.FORMATTER_RFC1036));
Assert.assertEquals(instant, DateUtils.parseStandardDate("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));
Assert.assertEquals(createInstant(2005, Month.OCTOBER, 14), DateUtils.parseStandardDate(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));
Assert.assertEquals(createInstant(2005, Month.OCTOBER, 14), DateUtils.parseStandardDate(message2, HttpHeaders.DATE));
}
@Test
public void testMalformedDate() {
Assert.assertNull(DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 GMT", new String[] {}, null));
Assert.assertNull(DateUtils.parseDate("Fri, 14 Oct 2005 00:00:00 GMT", new DateTimeFormatter[] {}));
}
@Test
public void testInvalidInput() throws Exception {
Assert.assertThrows(NullPointerException.class, () -> DateUtils.parseDate(null, null, null));
Assert.assertThrows(NullPointerException.class, () -> DateUtils.formatDate(null));
Assert.assertThrows(NullPointerException.class, () -> DateUtils.formatDate(new Date(), null));
Assert.assertThrows(NullPointerException.class, () -> DateUtils.parseStandardDate(null));
Assert.assertThrows(NullPointerException.class, () -> DateUtils.formatStandardDate(null));
}
@Test
public void testTwoDigitYearDateParse() throws Exception {
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)));
Assert.assertEquals(createInstant(2005, Month.OCTOBER, 14),
DateUtils.parseDate("Friday, 14-Oct-05 00:00:00 GMT", DateUtils.FORMATTER_RFC1036));
}
@Test
public void testParseQuotedDate() throws Exception {
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);
Assert.assertEquals(createInstant(2005, Month.OCTOBER, 14),
DateUtils.parseDate("'Fri, 14 Oct 2005 00:00:00 GMT'", DateUtils.FORMATTER_RFC1123));
}
@Test
public void testBasicDateFormat() throws Exception {
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));
final Instant instant = createInstant(2005, Month.OCTOBER, 14);
Assert.assertEquals("Fri, 14 Oct 2005 00:00:00 GMT", DateUtils.formatStandardDate(instant));
Assert.assertEquals("Fri, 14 Oct 2005 00:00:00 GMT", DateUtils.formatDate(instant, DateUtils.FORMATTER_RFC1123));
Assert.assertEquals("Fri, 14-Oct-05 00:00:00 GMT", DateUtils.formatDate(instant, DateUtils.FORMATTER_RFC1036));
Assert.assertEquals("Fri Oct 14 00:00:00 2005", DateUtils.formatDate(instant, DateUtils.FORMATTER_ASCTIME));
}
@Test
public void testIsBefore() throws Exception {
final HeaderGroup message1 = new HeaderGroup();
final HeaderGroup message2 = new HeaderGroup();
MatcherAssert.assertThat(DateUtils.isBefore(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false));
MatcherAssert.assertThat(DateUtils.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
MatcherAssert.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"));
MatcherAssert.assertThat(DateUtils.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "Wednesday, 25-Dec-2017 00:00:00 GMT"));
MatcherAssert.assertThat(DateUtils.isBefore(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(true));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "Thursday, 27-Dec-2017 00:00:00 GMT"));
MatcherAssert.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();
MatcherAssert.assertThat(DateUtils.isAfter(null, null, HttpHeaders.DATE), CoreMatchers.equalTo(false));
MatcherAssert.assertThat(DateUtils.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "huh?"));
message2.setHeader(new BasicHeader(HttpHeaders.DATE, "eh?"));
MatcherAssert.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"));
MatcherAssert.assertThat(DateUtils.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "Thursday, 27-Dec-2017 00:00:00 GMT"));
MatcherAssert.assertThat(DateUtils.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(true));
message1.setHeader(new BasicHeader(HttpHeaders.DATE, "Wednesday, 25-Dec-2017 00:00:00 GMT"));
MatcherAssert.assertThat(DateUtils.isAfter(message1, message2, HttpHeaders.DATE), CoreMatchers.equalTo(false));
}
}