Align MVC checkNotModified with reactive support
Since SPR-14522, the web reactive framework supports checkNotModified features. This commit aligns the existing MVC infrastructure with web reactive's behavior. Because of the new Servlet 3.0 baseline, some constraints aren't relevant anymore and duplicate code has been removed in `HttpEntityMethodProcessor`. Issue: SPR-14659
This commit is contained in:
@@ -28,8 +28,6 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
@@ -41,6 +39,7 @@ import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||
import org.springframework.web.context.request.NativeWebRequest;
|
||||
import org.springframework.web.context.request.ServletWebRequest;
|
||||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
@@ -182,15 +181,15 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
||||
}
|
||||
|
||||
if (responseEntity instanceof ResponseEntity) {
|
||||
outputMessage.getServletResponse().setStatus(((ResponseEntity<?>) responseEntity).getStatusCodeValue());
|
||||
HttpMethod method = inputMessage.getMethod();
|
||||
boolean isGetOrHead = (HttpMethod.GET == method || HttpMethod.HEAD == method);
|
||||
if (isGetOrHead && isResourceNotModified(inputMessage, outputMessage)) {
|
||||
outputMessage.setStatusCode(HttpStatus.NOT_MODIFIED);
|
||||
// Ensure headers are flushed, no body should be written.
|
||||
outputMessage.flush();
|
||||
// Skip call to converters, as they may update the body.
|
||||
return;
|
||||
int returnStatus = ((ResponseEntity<?>) responseEntity).getStatusCodeValue();
|
||||
outputMessage.getServletResponse().setStatus(returnStatus);
|
||||
if(returnStatus == 200) {
|
||||
if (isResourceNotModified(inputMessage, outputMessage)) {
|
||||
// Ensure headers are flushed, no body should be written.
|
||||
outputMessage.flush();
|
||||
// Skip call to converters, as they may update the body.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,55 +222,15 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
||||
}
|
||||
|
||||
private boolean isResourceNotModified(ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) {
|
||||
boolean notModified = false;
|
||||
try {
|
||||
long ifModifiedSince = inputMessage.getHeaders().getIfModifiedSince();
|
||||
String eTag = addEtagPadding(outputMessage.getHeaders().getETag());
|
||||
long lastModified = outputMessage.getHeaders().getLastModified();
|
||||
List<String> ifNoneMatch = inputMessage.getHeaders().getIfNoneMatch();
|
||||
if (!ifNoneMatch.isEmpty() && (inputMessage.getHeaders().containsKey(HttpHeaders.IF_UNMODIFIED_SINCE)
|
||||
|| inputMessage.getHeaders().containsKey(HttpHeaders.IF_MATCH))) {
|
||||
// invalid conditional request, do not process
|
||||
}
|
||||
else if (lastModified != -1 && StringUtils.hasLength(eTag)) {
|
||||
notModified = isETagNotModified(ifNoneMatch, eTag) && isTimeStampNotModified(ifModifiedSince, lastModified);
|
||||
}
|
||||
else if (lastModified != -1) {
|
||||
notModified = isTimeStampNotModified(ifModifiedSince, lastModified);
|
||||
}
|
||||
else if (StringUtils.hasLength(eTag)) {
|
||||
notModified = isETagNotModified(ifNoneMatch, eTag);
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException exc) {
|
||||
// invalid conditional request, do not process
|
||||
}
|
||||
return notModified;
|
||||
}
|
||||
ServletWebRequest servletWebRequest =
|
||||
new ServletWebRequest(inputMessage.getServletRequest(), outputMessage.getServletResponse());
|
||||
HttpHeaders responseHeaders = outputMessage.getHeaders();
|
||||
String etag = responseHeaders.getETag();
|
||||
long lastModifiedTimestamp = responseHeaders.getLastModified();
|
||||
responseHeaders.remove(HttpHeaders.ETAG);
|
||||
responseHeaders.remove(HttpHeaders.LAST_MODIFIED);
|
||||
|
||||
private boolean isETagNotModified(List<String> ifNoneMatch, String etag) {
|
||||
if (StringUtils.hasLength(etag)) {
|
||||
for (String clientETag : ifNoneMatch) {
|
||||
// Compare weak/strong ETags as per https://tools.ietf.org/html/rfc7232#section-2.3
|
||||
if (StringUtils.hasLength(clientETag) &&
|
||||
(clientETag.replaceFirst("^W/", "").equals(etag.replaceFirst("^W/", "")))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isTimeStampNotModified(long ifModifiedSince, long lastModifiedTimestamp) {
|
||||
return (ifModifiedSince >= (lastModifiedTimestamp / 1000 * 1000));
|
||||
}
|
||||
|
||||
private String addEtagPadding(String etag) {
|
||||
if (StringUtils.hasLength(etag) &&
|
||||
(!(etag.startsWith("\"") || etag.startsWith("W/\"")) || !etag.endsWith("\"")) ) {
|
||||
etag = "\"" + etag + "\"";
|
||||
}
|
||||
return etag;
|
||||
return servletWebRequest.checkNotModified(etag, lastModifiedTimestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user