Improve Last-Modified & ETag support
Prior to this change, the `"Last-Modified"` and "`Etag`" support had been improved with SPR-11324: HTTP response headers are now automatically added for conditional requests and more. This commit fixes the format of the "`Last-Modified`" and "`ETag`" values, which were using an epoch timestamp rather than an HTTP-date format defined in RFC 7231 section 7.1.1.1. Also, Conditional responses are only applied when the given response applies, i.e. when it has an compatible HTTP status (2xx). Issue: SPR-13090
This commit is contained in:
@@ -460,7 +460,7 @@ public class ComplexWebApplicationContext extends StaticWebApplicationContext {
|
||||
|
||||
@Override
|
||||
public long lastModified() {
|
||||
return 99;
|
||||
return 1427846401000L;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 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.
|
||||
@@ -177,7 +177,7 @@ public class DispatcherServletTests extends TestCase {
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
simpleDispatcherServlet.service(request, response);
|
||||
assertTrue("Not forwarded", response.getForwardedUrl() == null);
|
||||
assertEquals("98", response.getHeader("Last-Modified"));
|
||||
assertEquals("Wed, 01 Apr 2015 00:00:00 GMT", response.getHeader("Last-Modified"));
|
||||
}
|
||||
|
||||
public void testUnknownRequest() throws Exception {
|
||||
@@ -205,7 +205,7 @@ public class DispatcherServletTests extends TestCase {
|
||||
assertTrue(request.getAttribute("test3") != null);
|
||||
assertTrue(request.getAttribute("test3x") != null);
|
||||
assertTrue(request.getAttribute("test3y") != null);
|
||||
assertEquals("99", response.getHeader("Last-Modified"));
|
||||
assertEquals("Wed, 01 Apr 2015 00:00:01 GMT", response.getHeader("Last-Modified"));
|
||||
}
|
||||
|
||||
public void testExistingMultipartRequest() throws Exception {
|
||||
|
||||
@@ -90,7 +90,7 @@ public class SimpleWebApplicationContext extends StaticWebApplicationContext {
|
||||
|
||||
@Override
|
||||
public long getLastModified(HttpServletRequest request) {
|
||||
return 98;
|
||||
return 1427846400000L;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,12 @@ import static org.springframework.web.servlet.HandlerMapping.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -61,6 +64,8 @@ import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
*/
|
||||
public class HttpEntityMethodProcessorMockTests {
|
||||
|
||||
private SimpleDateFormat dateFormat;
|
||||
|
||||
private HttpEntityMethodProcessor processor;
|
||||
|
||||
private HttpMessageConverter<String> messageConverter;
|
||||
@@ -87,6 +92,9 @@ public class HttpEntityMethodProcessorMockTests {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
|
||||
messageConverter = mock(HttpMessageConverter.class);
|
||||
given(messageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
|
||||
@@ -341,7 +349,7 @@ public class HttpEntityMethodProcessorMockTests {
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
assertEquals(HttpStatus.NOT_MODIFIED.value(), servletResponse.getStatus());
|
||||
assertEquals(oneMinuteAgo/1000 * 1000, Long.parseLong(servletResponse.getHeader(HttpHeaders.LAST_MODIFIED)));
|
||||
assertEquals(dateFormat.format(oneMinuteAgo), servletResponse.getHeader(HttpHeaders.LAST_MODIFIED));
|
||||
assertEquals(0, servletResponse.getContentAsByteArray().length);
|
||||
}
|
||||
|
||||
@@ -385,7 +393,7 @@ public class HttpEntityMethodProcessorMockTests {
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
assertEquals(HttpStatus.NOT_MODIFIED.value(), servletResponse.getStatus());
|
||||
assertEquals(oneMinuteAgo/1000 * 1000, Long.parseLong(servletResponse.getHeader(HttpHeaders.LAST_MODIFIED)));
|
||||
assertEquals(dateFormat.format(oneMinuteAgo), servletResponse.getHeader(HttpHeaders.LAST_MODIFIED));
|
||||
assertEquals(etagValue, servletResponse.getHeader(HttpHeaders.ETAG));
|
||||
assertEquals(0, servletResponse.getContentAsByteArray().length);
|
||||
}
|
||||
@@ -411,7 +419,7 @@ public class HttpEntityMethodProcessorMockTests {
|
||||
|
||||
assertTrue(mavContainer.isRequestHandled());
|
||||
assertEquals(HttpStatus.OK.value(), servletResponse.getStatus());
|
||||
assertEquals(oneMinuteAgo/1000 * 1000, Long.parseLong(servletResponse.getHeader(HttpHeaders.LAST_MODIFIED)));
|
||||
assertEquals(dateFormat.format(oneMinuteAgo), servletResponse.getHeader(HttpHeaders.LAST_MODIFIED));
|
||||
assertEquals(changedEtagValue, servletResponse.getHeader(HttpHeaders.ETAG));
|
||||
assertEquals(0, servletResponse.getContentAsByteArray().length);
|
||||
}
|
||||
|
||||
@@ -23,9 +23,13 @@ import static org.mockito.Mockito.*;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -52,6 +56,8 @@ import org.springframework.web.servlet.HandlerMapping;
|
||||
*/
|
||||
public class ResourceHttpRequestHandlerTests {
|
||||
|
||||
private SimpleDateFormat dateFormat;
|
||||
|
||||
private ResourceHttpRequestHandler handler;
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
@@ -61,6 +67,9 @@ public class ResourceHttpRequestHandlerTests {
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
dateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
|
||||
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
|
||||
|
||||
List<Resource> paths = new ArrayList<>(2);
|
||||
paths.add(new ClassPathResource("test/", getClass()));
|
||||
paths.add(new ClassPathResource("testalternatepath/", getClass()));
|
||||
@@ -127,7 +136,7 @@ public class ResourceHttpRequestHandlerTests {
|
||||
|
||||
assertEquals("no-cache", this.response.getHeader("Pragma"));
|
||||
assertThat(this.response.getHeaderValues("Cache-Control"), Matchers.contains("no-cache", "no-store"));
|
||||
assertTrue(headerAsLong("Expires") == 1);
|
||||
assertEquals(this.response.getHeaderValue("Expires"), dateFormat.format(System.currentTimeMillis()));
|
||||
assertTrue(this.response.containsHeader("Last-Modified"));
|
||||
assertEquals(headerAsLong("Last-Modified"), resourceLastModified("test/foo.css"));
|
||||
}
|
||||
@@ -467,8 +476,8 @@ public class ResourceHttpRequestHandlerTests {
|
||||
}
|
||||
|
||||
|
||||
private long headerAsLong(String responseHeaderName) {
|
||||
return Long.valueOf(this.response.getHeader(responseHeaderName));
|
||||
private long headerAsLong(String responseHeaderName) throws Exception {
|
||||
return dateFormat.parse(this.response.getHeader(responseHeaderName)).getTime();
|
||||
}
|
||||
|
||||
private long resourceLastModified(String resourceName) throws IOException {
|
||||
|
||||
Reference in New Issue
Block a user