Browse Source

HttpHeaders consistently ignores invalid date header values

Issue: SPR-14144
pull/1031/head
Juergen Hoeller 10 years ago
parent
commit
448621ac58
  1. 62
      spring-web/src/main/java/org/springframework/http/HttpHeaders.java
  2. 25
      spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java

62
spring-web/src/main/java/org/springframework/http/HttpHeaders.java

@ -367,9 +367,9 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
* @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.1.1">Section 7.1.1.1 of RFC 7231</a> * @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.1.1">Section 7.1.1.1 of RFC 7231</a>
*/ */
private static final String[] DATE_FORMATS = new String[] { private static final String[] DATE_FORMATS = new String[] {
"EEE, dd MMM yyyy HH:mm:ss zzz", "EEE, dd MMM yyyy HH:mm:ss zzz",
"EEE, dd-MMM-yy HH:mm:ss zzz", "EEE, dd-MMM-yy HH:mm:ss zzz",
"EEE MMM dd HH:mm:ss yyyy" "EEE MMM dd HH:mm:ss yyyy"
}; };
private static TimeZone GMT = TimeZone.getTimeZone("GMT"); private static TimeZone GMT = TimeZone.getTimeZone("GMT");
@ -779,12 +779,7 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
* January 1, 1970 GMT. Returns -1 when the date is unknown. * January 1, 1970 GMT. Returns -1 when the date is unknown.
*/ */
public long getExpires() { public long getExpires() {
try { return getFirstDate(EXPIRES, false);
return getFirstDate(EXPIRES);
}
catch (IllegalArgumentException ex) {
return -1;
}
} }
/** /**
@ -802,7 +797,7 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
* January 1, 1970 GMT. Returns -1 when the date is unknown. * January 1, 1970 GMT. Returns -1 when the date is unknown.
*/ */
public long getIfModifiedSince() { public long getIfModifiedSince() {
return getFirstDate(IF_MODIFIED_SINCE); return getFirstDate(IF_MODIFIED_SINCE, false);
} }
/** /**
@ -867,7 +862,7 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
* January 1, 1970 GMT. Returns -1 when the date is unknown. * January 1, 1970 GMT. Returns -1 when the date is unknown.
*/ */
public long getLastModified() { public long getLastModified() {
return getFirstDate(LAST_MODIFIED); return getFirstDate(LAST_MODIFIED, false);
} }
/** /**
@ -969,24 +964,49 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
* Parse the first header value for the given header name as a date, * Parse the first header value for the given header name as a date,
* return -1 if there is no value, or raise {@link IllegalArgumentException} * return -1 if there is no value, or raise {@link IllegalArgumentException}
* if the value cannot be parsed as a date. * 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) { 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); String headerValue = getFirst(headerName);
if (headerValue == null) { if (headerValue == null) {
// No header value sent at all
return -1; return -1;
} }
for (String dateFormat : DATE_FORMATS) { if (headerValue.length() >= 3) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US); // Short "0" or "-1" like values are never valid HTTP date headers...
simpleDateFormat.setTimeZone(GMT); // Let's only bother with SimpleDateFormat parsing for long enough values.
try { for (String dateFormat : DATE_FORMATS) {
return simpleDateFormat.parse(headerValue).getTime(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat, Locale.US);
} simpleDateFormat.setTimeZone(GMT);
catch (ParseException ex) { try {
// ignore return simpleDateFormat.parse(headerValue).getTime();
}
catch (ParseException ex) {
// ignore
}
} }
} }
throw new IllegalArgumentException("Cannot parse date value \"" + headerValue + if (rejectInvalid) {
"\" for \"" + headerName + "\" header"); throw new IllegalArgumentException("Cannot parse date value \"" + headerValue +
"\" for \"" + headerName + "\" header");
}
return -1;
} }
/** /**

25
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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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 java.util.TimeZone;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
/** /**
* Unit tests for {@link org.springframework.http.HttpHeaders}. * 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")); assertEquals("Invalid Expires header", "Thu, 18 Dec 2008 10:20:00 GMT", headers.getFirst("expires"));
} }
// SPR-10648 (example is from INT-3063) @Test // SPR-10648 (example is from INT-3063)
@Test
public void expiresInvalidDate() { public void expiresInvalidDate() {
headers.set("Expires", "-1"); headers.set("Expires", "-1");
assertEquals(-1, headers.getExpires()); assertEquals(-1, headers.getExpires());
@ -234,6 +227,18 @@ public class HttpHeadersTests {
headers.getFirst("if-modified-since")); 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 @Test
public void pragma() { public void pragma() {
String pragma = "no-cache"; String pragma = "no-cache";

Loading…
Cancel
Save