diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index 53d0a5fba82..090822ee271 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -367,9 +367,9 @@ public class HttpHeaders implements MultiValueMap, Serializable * @see Section 7.1.1.1 of RFC 7231 */ private static final String[] DATE_FORMATS = new String[] { - "EEE, dd MMM yyyy HH:mm:ss zzz", - "EEE, dd-MMM-yy HH:mm:ss zzz", - "EEE MMM dd HH:mm:ss yyyy" + "EEE, dd MMM yyyy HH:mm:ss zzz", + "EEE, dd-MMM-yy HH:mm:ss zzz", + "EEE MMM dd HH:mm:ss yyyy" }; private static TimeZone GMT = TimeZone.getTimeZone("GMT"); @@ -779,12 +779,7 @@ public class HttpHeaders implements MultiValueMap, Serializable * January 1, 1970 GMT. Returns -1 when the date is unknown. */ public long getExpires() { - try { - return getFirstDate(EXPIRES); - } - catch (IllegalArgumentException ex) { - return -1; - } + return getFirstDate(EXPIRES, false); } /** @@ -802,7 +797,7 @@ public class HttpHeaders implements MultiValueMap, Serializable * January 1, 1970 GMT. Returns -1 when the date is unknown. */ public long getIfModifiedSince() { - return getFirstDate(IF_MODIFIED_SINCE); + return getFirstDate(IF_MODIFIED_SINCE, false); } /** @@ -867,7 +862,7 @@ public class HttpHeaders implements MultiValueMap, Serializable * January 1, 1970 GMT. Returns -1 when the date is unknown. */ public long getLastModified() { - return getFirstDate(LAST_MODIFIED); + return getFirstDate(LAST_MODIFIED, false); } /** @@ -969,24 +964,49 @@ public class HttpHeaders implements MultiValueMap, Serializable * Parse the first header value for the given header name as a date, * return -1 if there is no value, or raise {@link IllegalArgumentException} * if the value cannot be parsed as a date. + * @param headerName the header name + * @return the parsed date header, or -1 if none */ public long getFirstDate(String headerName) { + return getFirstDate(headerName, true); + } + + /** + * Parse the first header value for the given header name as a date, + * return -1 if there is no value or also in case of an invalid value + * (if {@code rejectInvalid=false}), or raise {@link IllegalArgumentException} + * if the value cannot be parsed as a date. + * @param headerName the header name + * @param rejectInvalid whether to reject invalid values with an + * {@link IllegalArgumentException} ({@code true}) or rather return -1 + * in that case ({@code false}) + * @return the parsed date header, or -1 if none (or invalid) + */ + private long getFirstDate(String headerName, boolean rejectInvalid) { String headerValue = getFirst(headerName); if (headerValue == null) { + // No header value sent at all return -1; } - for (String dateFormat : DATE_FORMATS) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US); - simpleDateFormat.setTimeZone(GMT); - try { - return simpleDateFormat.parse(headerValue).getTime(); - } - catch (ParseException ex) { - // ignore + if (headerValue.length() >= 3) { + // Short "0" or "-1" like values are never valid HTTP date headers... + // Let's only bother with SimpleDateFormat parsing for long enough values. + for (String dateFormat : DATE_FORMATS) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US); + simpleDateFormat.setTimeZone(GMT); + try { + return simpleDateFormat.parse(headerValue).getTime(); + } + catch (ParseException ex) { + // ignore + } } } - throw new IllegalArgumentException("Cannot parse date value \"" + headerValue + - "\" for \"" + headerName + "\" header"); + if (rejectInvalid) { + throw new IllegalArgumentException("Cannot parse date value \"" + headerValue + + "\" for \"" + headerName + "\" header"); + } + return -1; } /** diff --git a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java index d88038b904e..9fa07041230 100644 --- a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java +++ b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,14 +30,9 @@ import java.util.Locale; import java.util.TimeZone; import org.hamcrest.Matchers; - import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; +import static org.junit.Assert.*; /** * Unit tests for {@link org.springframework.http.HttpHeaders}. @@ -215,9 +210,7 @@ public class HttpHeadersTests { assertEquals("Invalid Expires header", "Thu, 18 Dec 2008 10:20:00 GMT", headers.getFirst("expires")); } - // SPR-10648 (example is from INT-3063) - - @Test + @Test // SPR-10648 (example is from INT-3063) public void expiresInvalidDate() { headers.set("Expires", "-1"); assertEquals(-1, headers.getExpires()); @@ -234,6 +227,18 @@ public class HttpHeadersTests { headers.getFirst("if-modified-since")); } + @Test // SPR-14144 + public void invalidIfModifiedSinceHeader() { + headers.set(HttpHeaders.IF_MODIFIED_SINCE, "0"); + assertEquals(-1, headers.getIfModifiedSince()); + + headers.set(HttpHeaders.IF_MODIFIED_SINCE, "-1"); + assertEquals(-1, headers.getIfModifiedSince()); + + headers.set(HttpHeaders.IF_MODIFIED_SINCE, "XXX"); + assertEquals(-1, headers.getIfModifiedSince()); + } + @Test public void pragma() { String pragma = "no-cache";