You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2014/01/03 07:42:19 UTC

svn commit: r1555018 - in /struts/struts2/trunk: core/src/main/java/org/apache/struts2/RequestUtils.java core/src/test/java/org/apache/struts2/RequestUtilsTest.java plugins/rest/src/main/java/org/apache/struts2/rest/DefaultHttpHeaders.java

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() {