Author: lukaszlenart Date: Fri Jan 3 06:42:19 2014 New Revision: 1555018 URL: http://svn.apache.org/r1555018 Log: WW-4263 Adds proper and thread safe handling of If-Modified-Since header
Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/RequestUtils.java struts/struts2/trunk/core/src/test/java/org/apache/struts2/RequestUtilsTest.java struts/struts2/trunk/plugins/rest/src/main/java/org/apache/struts2/rest/DefaultHttpHeaders.java Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/RequestUtils.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/RequestUtils.java?rev=1555018&r1=1555017&r2=1555018&view=diff ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/RequestUtils.java (original) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/RequestUtils.java Fri Jan 3 06:42:19 2014 @@ -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.HttpServletReq */ 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; + } + } Modified: struts/struts2/trunk/core/src/test/java/org/apache/struts2/RequestUtilsTest.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/RequestUtilsTest.java?rev=1555018&r1=1555017&r2=1555018&view=diff ============================================================================== --- struts/struts2/trunk/core/src/test/java/org/apache/struts2/RequestUtilsTest.java (original) +++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/RequestUtilsTest.java Fri Jan 3 06:42:19 2014 @@ -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 Te 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(); Modified: struts/struts2/trunk/plugins/rest/src/main/java/org/apache/struts2/rest/DefaultHttpHeaders.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/plugins/rest/src/main/java/org/apache/struts2/rest/DefaultHttpHeaders.java?rev=1555018&r1=1555017&r2=1555018&view=diff ============================================================================== --- struts/struts2/trunk/plugins/rest/src/main/java/org/apache/struts2/rest/DefaultHttpHeaders.java (original) +++ struts/struts2/trunk/plugins/rest/src/main/java/org/apache/struts2/rest/DefaultHttpHeaders.java Fri Jan 3 06:42:19 2014 @@ -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 implemen } } - 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 implemen 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() {