mirror of
https://github.com/apache/struts.git
synced 2026-08-11 01:27:14 +00:00
WW-4263 Adds proper and thread safe handling of If-Modified-Since header
git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@1555018 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
@@ -21,7 +21,15 @@
|
||||
|
||||
package org.apache.struts2;
|
||||
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import org.apache.commons.lang3.time.FastDateFormat;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
|
||||
/**
|
||||
@@ -29,6 +37,20 @@ import javax.servlet.http.HttpServletRequest;
|
||||
*/
|
||||
public class RequestUtils {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RequestUtils.class);
|
||||
|
||||
private static final TimeZone GMT = TimeZone.getTimeZone("GMT");
|
||||
|
||||
private static final String FORMAT_PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
|
||||
private static final String FORMAT_PATTERN_RFC1036 = "EEE, dd-MMM-yy HH:mm:ss zzz";
|
||||
private static final String FORMAT_PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy";
|
||||
|
||||
private static final FastDateFormat[] IF_MODIFIED_SINCE_FORMATS = {
|
||||
FastDateFormat.getInstance(FORMAT_PATTERN_RFC1123, GMT, Locale.US),
|
||||
FastDateFormat.getInstance(FORMAT_PATTERN_RFC1036, GMT, Locale.US),
|
||||
FastDateFormat.getInstance(FORMAT_PATTERN_ASCTIME, GMT, Locale.US)
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the current request servlet path.
|
||||
* Deals with differences between servlet specs (2.2 vs 2.3+)
|
||||
@@ -84,4 +106,30 @@ public class RequestUtils {
|
||||
uri = request.getRequestURI();
|
||||
return uri.substring(request.getContextPath().length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse input string as date in formats defined for If-Modified-Since header,
|
||||
* see:
|
||||
* https://issues.apache.org/jira/browse/WW-4263
|
||||
* https://web.archive.org/web/20081014021349/http://rfc.net/rfc2616.html#p20
|
||||
*
|
||||
* @param headerValue value of If-Modified-Since header
|
||||
* @return proper date or null
|
||||
*/
|
||||
public static Date parseIfModifiedSince(String headerValue) {
|
||||
for (FastDateFormat fastDateFormat : IF_MODIFIED_SINCE_FORMATS) {
|
||||
try {
|
||||
return fastDateFormat.parse(headerValue);
|
||||
} catch (ParseException ignore) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Error parsing value [#0] as [#1]!", headerValue, fastDateFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Error parsing value [#0] as date!", headerValue);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,12 +25,12 @@ package org.apache.struts2;
|
||||
* <code>RequestUtilsTest</code>
|
||||
*
|
||||
*/
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.easymock.MockControl;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Date;
|
||||
|
||||
public class RequestUtilsTest extends TestCase {
|
||||
|
||||
private MockControl control;
|
||||
@@ -86,6 +86,22 @@ public class RequestUtilsTest extends TestCase {
|
||||
control.verify();
|
||||
}
|
||||
|
||||
public void testParseRFC1123() {
|
||||
Date date = RequestUtils.parseIfModifiedSince("Thu, 23 Jul 2013 19:42:23 GMT");
|
||||
assertNotNull(date);
|
||||
}
|
||||
|
||||
public void testParseRFC1036() {
|
||||
Date date = RequestUtils.parseIfModifiedSince("Thursday, 23-Jul-13 19:42:23 GMT");
|
||||
assertNotNull(date);
|
||||
}
|
||||
|
||||
public void testParseASC() {
|
||||
Date date = RequestUtils.parseIfModifiedSince("Thu Jul 23 19:42:23 2013");
|
||||
assertNotNull(date);
|
||||
}
|
||||
|
||||
|
||||
protected void setUp() {
|
||||
control = MockControl.createControl(HttpServletRequest.class);
|
||||
requestMock = (HttpServletRequest) control.getMock();
|
||||
|
||||
@@ -21,29 +21,19 @@
|
||||
|
||||
package org.apache.struts2.rest;
|
||||
|
||||
import com.opensymphony.xwork2.util.logging.Logger;
|
||||
import com.opensymphony.xwork2.util.logging.LoggerFactory;
|
||||
import org.apache.struts2.RequestUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import static javax.servlet.http.HttpServletResponse.SC_CREATED;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_NOT_MODIFIED;
|
||||
import static javax.servlet.http.HttpServletResponse.SC_OK;
|
||||
import static javax.servlet.http.HttpServletResponse.*;
|
||||
|
||||
/**
|
||||
* Default implementation of rest info that uses fluent-style construction
|
||||
*/
|
||||
public class DefaultHttpHeaders implements HttpHeaders {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultHttpHeaders.class);
|
||||
|
||||
private static final String IF_MODIFIED_SINCE_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
|
||||
private static final SimpleDateFormat IF_MODIFIED_SINCE_FORMAT = new SimpleDateFormat(IF_MODIFIED_SINCE_DATE_FORMAT);
|
||||
|
||||
private String resultCode;
|
||||
private int status = SC_OK;
|
||||
private Object etag;
|
||||
@@ -144,13 +134,13 @@ public class DefaultHttpHeaders implements HttpHeaders {
|
||||
}
|
||||
}
|
||||
|
||||
String reqLastModified = request.getHeader("If-Modified-Since");
|
||||
if (lastModified != null && reqLastModified != null) {
|
||||
lastModifiedNotChanged = compareIfModifiedSince(reqLastModified);
|
||||
String headerIfModifiedSince = request.getHeader("If-Modified-Since");
|
||||
if (lastModified != null && headerIfModifiedSince != null) {
|
||||
lastModifiedNotChanged = compareIfModifiedSince(headerIfModifiedSince);
|
||||
}
|
||||
|
||||
if ((etagNotChanged && lastModifiedNotChanged) ||
|
||||
(etagNotChanged && reqLastModified == null) ||
|
||||
(etagNotChanged && headerIfModifiedSince == null) ||
|
||||
(lastModifiedNotChanged && reqETag == null)) {
|
||||
status = SC_NOT_MODIFIED;
|
||||
}
|
||||
@@ -160,18 +150,9 @@ public class DefaultHttpHeaders implements HttpHeaders {
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
private boolean compareIfModifiedSince(String reqLastModified) {
|
||||
try {
|
||||
if (lastModified.compareTo(IF_MODIFIED_SINCE_FORMAT.parse(reqLastModified)) >= 0) {
|
||||
return true;
|
||||
}
|
||||
} catch (ParseException e) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Got error parsing If-Modified-Since header value [#0] as [#1]!", e, reqLastModified, IF_MODIFIED_SINCE_DATE_FORMAT);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
private boolean compareIfModifiedSince(String headerIfModifiedSince) {
|
||||
Date requestLastModified = RequestUtils.parseIfModifiedSince(headerIfModifiedSince);
|
||||
return requestLastModified != null && lastModified.compareTo(requestLastModified) >= 0;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
|
||||
Reference in New Issue
Block a user