Skip to content

Commit 794e859

Browse files
committed
checkNotModified leniently handles IE-10-style If-Modified-Since values and silently proceeds if header value cannot be parsed at all
Issue: SPR-11727
1 parent 5b47816 commit 794e859

File tree

3 files changed

+88
-54
lines changed

3 files changed

+88
-54
lines changed

spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java

+32-13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.web.context.request;
1818

1919
import java.security.Principal;
20+
import java.util.Date;
2021
import java.util.Iterator;
2122
import java.util.Locale;
2223
import java.util.Map;
@@ -103,6 +104,14 @@ public <T> T getNativeResponse(Class<T> requiredType) {
103104
}
104105

105106

107+
/**
108+
* Return the HTTP method of the request.
109+
* @since 4.0.2
110+
*/
111+
public HttpMethod getHttpMethod() {
112+
return HttpMethod.valueOf(getRequest().getMethod().trim().toUpperCase());
113+
}
114+
106115
@Override
107116
public String getHeader(String headerName) {
108117
return getRequest().getHeader(headerName);
@@ -170,10 +179,28 @@ public boolean isSecure() {
170179
}
171180

172181
@Override
182+
@SuppressWarnings("deprecation")
173183
public boolean checkNotModified(long lastModifiedTimestamp) {
174184
if (lastModifiedTimestamp >= 0 && !this.notModified &&
175185
(this.response == null || !this.response.containsHeader(HEADER_LAST_MODIFIED))) {
176-
long ifModifiedSince = getRequest().getDateHeader(HEADER_IF_MODIFIED_SINCE);
186+
long ifModifiedSince = -1;
187+
try {
188+
ifModifiedSince = getRequest().getDateHeader(HEADER_IF_MODIFIED_SINCE);
189+
}
190+
catch (IllegalArgumentException ex) {
191+
String headerValue = getRequest().getHeader(HEADER_IF_MODIFIED_SINCE);
192+
// Possibly an IE 10 style value: "Wed, 09 Apr 2014 09:57:42 GMT; length=13774"
193+
int separatorIndex = headerValue.indexOf(';');
194+
if (separatorIndex != -1) {
195+
String datePart = headerValue.substring(0, separatorIndex);
196+
try {
197+
ifModifiedSince = Date.parse(datePart);
198+
}
199+
catch (IllegalArgumentException ex2) {
200+
// Giving up
201+
}
202+
}
203+
}
177204
this.notModified = (ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000));
178205
if (this.response != null) {
179206
if (this.notModified && supportsNotModifiedStatus()) {
@@ -188,17 +215,17 @@ public boolean checkNotModified(long lastModifiedTimestamp) {
188215
}
189216

190217
@Override
191-
public boolean checkNotModified(String eTag) {
192-
if (StringUtils.hasLength(eTag) && !this.notModified &&
218+
public boolean checkNotModified(String etag) {
219+
if (StringUtils.hasLength(etag) && !this.notModified &&
193220
(this.response == null || !this.response.containsHeader(HEADER_ETAG))) {
194221
String ifNoneMatch = getRequest().getHeader(HEADER_IF_NONE_MATCH);
195-
this.notModified = eTag.equals(ifNoneMatch);
222+
this.notModified = etag.equals(ifNoneMatch);
196223
if (this.response != null) {
197224
if (this.notModified && supportsNotModifiedStatus()) {
198225
this.response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
199226
}
200227
else {
201-
this.response.setHeader(HEADER_ETAG, eTag);
228+
this.response.setHeader(HEADER_ETAG, etag);
202229
}
203230
}
204231
}
@@ -236,14 +263,6 @@ public String getDescription(boolean includeClientInfo) {
236263
return sb.toString();
237264
}
238265

239-
/**
240-
* Return the HTTP method of the request.
241-
* @since 4.0.2
242-
*/
243-
public HttpMethod getHttpMethod() {
244-
return HttpMethod.valueOf(getRequest().getMethod().trim().toUpperCase());
245-
}
246-
247266

248267
@Override
249268
public String toString() {

spring-web/src/main/java/org/springframework/web/context/request/WebRequest.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -142,8 +142,11 @@ public interface WebRequest extends RequestAttributes {
142142
* return "myViewName";
143143
* }</pre>
144144
* <p><strong>Note:</strong> that you typically want to use either
145-
* this {@link #checkNotModified(long)} method; or
145+
* this {@code #checkNotModified(long)} method; or
146146
* {@link #checkNotModified(String)}, but not both.
147+
* <p>If the "If-Modified-Since" header is set but cannot be parsed
148+
* to a date value, this method will ignore the header and proceed
149+
* with setting the last-modified timestamp on the response.
147150
* @param lastModifiedTimestamp the last-modified timestamp that
148151
* the application determined for the underlying resource
149152
* @return whether the request qualifies as not modified,
@@ -170,16 +173,16 @@ public interface WebRequest extends RequestAttributes {
170173
* return "myViewName";
171174
* }</pre>
172175
* <p><strong>Note:</strong> that you typically want to use either
173-
* this {@link #checkNotModified(String)} method; or
176+
* this {@code #checkNotModified(String)} method; or
174177
* {@link #checkNotModified(long)}, but not both.
175-
* @param eTag the entity tag that the application determined
178+
* @param etag the entity tag that the application determined
176179
* for the underlying resource. This parameter will be padded
177180
* with quotes (") if necessary.
178181
* @return whether the request qualifies as not modified,
179182
* allowing to abort request processing and relying on the response
180183
* telling the client that the content has not been modified
181184
*/
182-
boolean checkNotModified(String eTag);
185+
boolean checkNotModified(String etag);
183186

184187
/**
185188
* Get a short description of this request,

spring-web/src/test/java/org/springframework/web/context/request/ServletWebRequestTests.java

+48-36
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import java.util.Date;
2020
import java.util.Locale;
2121
import java.util.Map;
22-
2322
import javax.servlet.ServletRequest;
2423
import javax.servlet.ServletResponse;
2524
import javax.servlet.http.HttpServletRequest;
@@ -118,75 +117,90 @@ public void decoratedNativeRequest() {
118117
}
119118

120119
@Test
121-
public void checkNotModifiedTimeStampForGET() {
120+
public void checkNotModifiedTimestampForGET() {
122121
long currentTime = new Date().getTime();
123122
servletRequest.setMethod("GET");
124123
servletRequest.addHeader("If-Modified-Since", currentTime);
125124

126-
request.checkNotModified(currentTime);
127-
125+
assertTrue(request.checkNotModified(currentTime));
128126
assertEquals(304, servletResponse.getStatus());
129127
}
130128

131129
@Test
132-
public void checkModifiedTimeStampForGET() {
130+
public void checkModifiedTimestampForGET() {
133131
long currentTime = new Date().getTime();
134132
long oneMinuteAgo = currentTime - (1000 * 60);
135133
servletRequest.setMethod("GET");
136134
servletRequest.addHeader("If-Modified-Since", oneMinuteAgo);
137135

138-
request.checkNotModified(currentTime);
136+
assertFalse(request.checkNotModified(currentTime));
137+
assertEquals(200, servletResponse.getStatus());
138+
assertEquals("" + currentTime, servletResponse.getHeader("Last-Modified"));
139+
}
139140

141+
@Test
142+
public void checkNotModifiedTimestampForHEAD() {
143+
long currentTime = new Date().getTime();
144+
servletRequest.setMethod("HEAD");
145+
servletRequest.addHeader("If-Modified-Since", currentTime);
146+
147+
assertTrue(request.checkNotModified(currentTime));
148+
assertEquals(304, servletResponse.getStatus());
149+
}
150+
151+
@Test
152+
public void checkModifiedTimestampForHEAD() {
153+
long currentTime = new Date().getTime();
154+
long oneMinuteAgo = currentTime - (1000 * 60);
155+
servletRequest.setMethod("HEAD");
156+
servletRequest.addHeader("If-Modified-Since", oneMinuteAgo);
157+
158+
assertFalse(request.checkNotModified(currentTime));
140159
assertEquals(200, servletResponse.getStatus());
141160
assertEquals(""+currentTime, servletResponse.getHeader("Last-Modified"));
142161
}
143162

144163
@Test
145-
public void checkNotModifiedETagForGET() {
146-
String eTag = "\"Foo\"";
164+
public void checkNotModifiedTimestampWithLengthPart() {
165+
long currentTime = Date.parse("Wed, 09 Apr 2014 09:57:42 GMT");
147166
servletRequest.setMethod("GET");
148-
servletRequest.addHeader("If-None-Match", eTag );
149-
150-
request.checkNotModified(eTag);
167+
servletRequest.addHeader("If-Modified-Since", "Wed, 09 Apr 2014 09:57:42 GMT; length=13774");
151168

169+
assertTrue(request.checkNotModified(currentTime));
152170
assertEquals(304, servletResponse.getStatus());
153171
}
154172

155173
@Test
156-
public void checkModifiedETagForGET() {
157-
String currentETag = "\"Foo\"";
158-
String oldEtag = "Bar";
174+
public void checkModifiedTimestampWithLengthPart() {
175+
long currentTime = Date.parse("Wed, 09 Apr 2014 09:57:42 GMT");
159176
servletRequest.setMethod("GET");
160-
servletRequest.addHeader("If-None-Match", oldEtag);
161-
162-
request.checkNotModified(currentETag);
177+
servletRequest.addHeader("If-Modified-Since", "Wed, 08 Apr 2014 09:57:42 GMT; length=13774");
163178

179+
assertFalse(request.checkNotModified(currentTime));
164180
assertEquals(200, servletResponse.getStatus());
165-
assertEquals(currentETag, servletResponse.getHeader("ETag"));
181+
assertEquals("" + currentTime, servletResponse.getHeader("Last-Modified"));
166182
}
167183

168184
@Test
169-
public void checkNotModifiedTimeStampForHEAD() {
170-
long currentTime = new Date().getTime();
171-
servletRequest.setMethod("HEAD");
172-
servletRequest.addHeader("If-Modified-Since", currentTime);
173-
174-
request.checkNotModified(currentTime);
185+
public void checkNotModifiedETagForGET() {
186+
String eTag = "\"Foo\"";
187+
servletRequest.setMethod("GET");
188+
servletRequest.addHeader("If-None-Match", eTag );
175189

190+
assertTrue(request.checkNotModified(eTag));
176191
assertEquals(304, servletResponse.getStatus());
177192
}
178193

179194
@Test
180-
public void checkModifiedTimeStampForHEAD() {
181-
long currentTime = new Date().getTime();
182-
long oneMinuteAgo = currentTime - (1000 * 60);
183-
servletRequest.setMethod("HEAD");
184-
servletRequest.addHeader("If-Modified-Since", oneMinuteAgo);
185-
186-
request.checkNotModified(currentTime);
195+
public void checkModifiedETagForGET() {
196+
String currentETag = "\"Foo\"";
197+
String oldEtag = "Bar";
198+
servletRequest.setMethod("GET");
199+
servletRequest.addHeader("If-None-Match", oldEtag);
187200

201+
assertFalse(request.checkNotModified(currentETag));
188202
assertEquals(200, servletResponse.getStatus());
189-
assertEquals(""+currentTime, servletResponse.getHeader("Last-Modified"));
203+
assertEquals(currentETag, servletResponse.getHeader("ETag"));
190204
}
191205

192206
@Test
@@ -195,8 +209,7 @@ public void checkNotModifiedETagForHEAD() {
195209
servletRequest.setMethod("HEAD");
196210
servletRequest.addHeader("If-None-Match", eTag );
197211

198-
request.checkNotModified(eTag);
199-
212+
assertTrue(request.checkNotModified(eTag));
200213
assertEquals(304, servletResponse.getStatus());
201214
}
202215

@@ -207,8 +220,7 @@ public void checkModifiedETagForHEAD() {
207220
servletRequest.setMethod("HEAD");
208221
servletRequest.addHeader("If-None-Match", oldEtag);
209222

210-
request.checkNotModified(currentETag);
211-
223+
assertFalse(request.checkNotModified(currentETag));
212224
assertEquals(200, servletResponse.getStatus());
213225
assertEquals(currentETag, servletResponse.getHeader("ETag"));
214226
}

0 commit comments

Comments
 (0)