tapestry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jkuhn...@apache.org
Subject svn commit: r545785 - in /tapestry/tapestry4/trunk/tapestry-framework/src: java/org/apache/tapestry/asset/AssetService.java java/org/apache/tapestry/util/io/GzipUtil.java test/org/apache/tapestry/asset/TestAssetService.java
Date Sat, 09 Jun 2007 19:36:16 GMT
Author: jkuhnert
Date: Sat Jun  9 12:36:15 2007
New Revision: 545785

URL: http://svn.apache.org/viewvc?view=rev&rev=545785
Log:
Fixes TAPESTRY-1553 / TAPESTRY-1550.  Implemented correct ETag format that is slightly more
deterministic and removed user-agent check for IE since it actually can cache gzip js. 

Also removed reporting exceptions for EOF IO stream closes by the browser.

Modified:
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/asset/AssetService.java
    tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/util/io/GzipUtil.java
    tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/asset/TestAssetService.java

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/asset/AssetService.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/asset/AssetService.java?view=diff&rev=545785&r1=545784&r2=545785
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/asset/AssetService.java
(original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/asset/AssetService.java
Sat Jun  9 12:36:15 2007
@@ -35,10 +35,7 @@
 import org.apache.tapestry.web.WebResponse;
 
 import javax.servlet.http.HttpServletResponse;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.*;
 import java.net.URL;
 import java.net.URLConnection;
 import java.text.DateFormat;
@@ -106,7 +103,7 @@
     }
     
     /** Represents a month of time in seconds. */
-    private static final long MONTH_SECONDS = 60 * 60 * 24 * 30;
+    static final long MONTH_SECONDS = 60 * 60 * 24 * 30;
     
     private Log _log;
     
@@ -145,13 +142,16 @@
      * them to not expire ... but a year will do.
      */
 
-    private final long _expireTime = _startupTime + 365 * 24 * 60 * 60 * 1000L;
+    final long _expireTime = _startupTime + 365 * 24 * 60 * 60 * 1000L;
 
     /** @since 4.0 */
 
     private RequestExceptionReporter _exceptionReporter;
-    
-    private Map _cache = new HashMap();
+
+    /**
+     * Cache of static content resources.
+     */
+    private final Map _cache = new HashMap();
     
     /**
      * Builds a {@link ILink}for a {@link PrivateAsset}.
@@ -224,11 +224,9 @@
         
         try
         {
-            if (checkDigest
-                    && !_digestSource.getDigestForResource(path).equals(md5Digest))
+            if (checkDigest && !_digestSource.getDigestForResource(path).equals(md5Digest))
             {
-                _response.sendError(HttpServletResponse.SC_FORBIDDEN, AssetMessages
-                        .md5Mismatch(path));
+                _response.sendError(HttpServletResponse.SC_FORBIDDEN, AssetMessages.md5Mismatch(path));
                 return;
             }
             
@@ -243,7 +241,8 @@
             
             URL resourceURL = _classResolver.getResource(translatePath(path));
             
-            if (resourceURL == null) {
+            if (resourceURL == null)
+            {
                 _response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                 _log.warn(AssetMessages.noSuchResource(path));
                 return;
@@ -258,6 +257,10 @@
             
             writeAssetContent(cycle, path, resourceConnection);
         }
+        catch (EOFException eof)
+        {
+            // ignored / expected exceptions happen when browser prematurely abandons connections
- IE does this a lot
+        }
         catch (Throwable ex)
         {
             _exceptionReporter.reportRequestException(AssetMessages.exceptionReportTitle(path),
ex);
@@ -310,17 +313,17 @@
         DateFormat format = null;
         
         try {
-            
-            if (header != null) {
-                
+
+            if (header != null)
+            {
                 format = (DateFormat) CACHED_FORMAT_POOL.borrowObject();
-                
+
                 modify = format.parse(header).getTime();
             }
-            
+
         } catch (Exception e) { e.printStackTrace(); }
-        finally {
-            
+        finally
+        {    
             if (format != null) 
                 try { CACHED_FORMAT_POOL.returnObject(format); } catch (Throwable t) { t.printStackTrace();
}
         }
@@ -355,13 +358,6 @@
         _response.setDateHeader("Expires", _expireTime);
         _response.setHeader("Cache-Control", "public, max-age=" + (MONTH_SECONDS * 3));
         
-        // ie won't cache javascript with etag attached
-        if (_request.getHeader("User-Agent") != null 
-                && _request.getHeader("User-Agent").indexOf("MSIE") < 0 
-                || contentType.indexOf("javascript") < 0)
-            _response.setHeader("ETag", String.valueOf(resourcePath.hashCode()));
-            
-        
         // Set the content type. If the servlet container doesn't
         // provide it, try and guess it by the extension.
         
@@ -369,7 +365,11 @@
             contentType = getMimeType(resourcePath);
         
         byte[] data = getAssetData(cycle, resourcePath, resourceConnection, contentType);
+
+        // See ETag definition  - http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19
         
+        _response.setHeader("ETag", "W/\"" + data.length + "-" + lastModified + "\"");
+
         // force image(or other) caching when detected, esp helps with ie related things
         // see http://mir.aculo.us/2005/08/28/internet-explorer-and-ajax-image-caching-woes
         

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/util/io/GzipUtil.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/util/io/GzipUtil.java?view=diff&rev=545785&r1=545784&r2=545785
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/util/io/GzipUtil.java
(original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/java/org/apache/tapestry/util/io/GzipUtil.java
Sat Jun  9 12:36:15 2007
@@ -19,22 +19,22 @@
 /**
  * Encapsulates logic related to various gzip compression schemes and their rules
  * as they apply to different browsers.
- * 
+ *
  * @author jkuhnert
  */
 public final class GzipUtil
 {
     private static final float MIN_IE_VERSION = 7.0f;
-    
+
     private static final String MSIE_6_COMPATIBLE_STRING = "SV1";
-    
+
     /* defeat instantiation */
     private GzipUtil() { }
-    
+
     /**
      * Determines if gzip compression is appropriate/possible based on the User Agent and

      * other limiting factors. IE versions &lt; 6.1 are known to not work with gzip compression
reliably. 
-     * 
+     *
      * @return True, if this request can be served in gzip format. False otherwise.
      */
     public static boolean isGzipCapable(WebRequest request)
@@ -42,51 +42,51 @@
         String encoding = request.getHeader("Accept-Encoding");
         if (encoding == null || encoding.indexOf("gzip") < 0)
             return false;
-        
+
         // Handle IE specific hacks
-        
+
         String userAgent = request.getHeader("User-Agent");
         int ieIndex = (userAgent != null) ? userAgent.indexOf("MSIE") : -1;
         if (ieIndex > -1) {
-            
+
             float version = -1;
-            
+
             try {
-             version = Float.parseFloat(userAgent.substring(ieIndex + 4, ieIndex + 8));
+                version = Float.parseFloat(userAgent.substring(ieIndex + 4, ieIndex + 8));
             } catch (NumberFormatException nf) {nf.printStackTrace();}
-            
+
             if (version >= MIN_IE_VERSION)
                 return true;
-            
+
             if (userAgent.indexOf(MSIE_6_COMPATIBLE_STRING) > -1)
                 return true;
-            
+
             // else false
-            
+
             return false;
         }
-        
+
         return true;
     }
-    
+
     /**
      * Based on the given type of content, determines if compression is appropriate. The
biggest
      * thing it does is make sure that image content isn't compressed as that kind of content
      * is already compressed fairly well.
-     * 
+     *
      * @param contentType
      *          The content type to check. (ie "text/javascript","text/html", etc..)
-     *          
+     *
      * @return True if compression is appropriate for the content specified, false otherwise.
      */
     public static boolean shouldCompressContentType(String contentType)
     {
         if (contentType == null)
             return false;
-        
+
         return contentType.indexOf("javascript") > -1
-        || contentType.indexOf("css") > -1 
-        || contentType.indexOf("html") > -1
-        || contentType.indexOf("text") > -1;
+               || contentType.indexOf("css") > -1
+               || contentType.indexOf("html") > -1
+               || contentType.indexOf("text") > -1;
     }
 }

Modified: tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/asset/TestAssetService.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/asset/TestAssetService.java?view=diff&rev=545785&r1=545784&r2=545785
==============================================================================
--- tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/asset/TestAssetService.java
(original)
+++ tapestry/tapestry4/trunk/tapestry-framework/src/test/org/apache/tapestry/asset/TestAssetService.java
Sat Jun  9 12:36:15 2007
@@ -14,7 +14,12 @@
 package org.apache.tapestry.asset;
 
 import org.apache.commons.logging.LogFactory;
+import org.apache.hivemind.ClassResolver;
+import org.apache.hivemind.impl.DefaultClassResolver;
+import org.apache.tapestry.IRequestCycle;
 import org.apache.tapestry.TestBase;
+import org.apache.tapestry.util.ContentType;
+import org.apache.tapestry.web.WebContext;
 import org.apache.tapestry.web.WebRequest;
 import org.apache.tapestry.web.WebResponse;
 import static org.easymock.EasyMock.checkOrder;
@@ -22,6 +27,7 @@
 import org.testng.annotations.Test;
 
 import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayOutputStream;
 import java.net.URLConnection;
 import java.text.DateFormat;
 
@@ -141,5 +147,52 @@
         
         verify();
         org.easymock.classextension.EasyMock.verify(url);
+    }
+
+    public void test_ETag_Header_Response()
+            throws Exception
+    {
+        WebRequest request = newMock(WebRequest.class);
+        checkOrder(request, false);
+        WebResponse response = newMock(WebResponse.class);
+        WebContext context = newMock(WebContext.class);
+        IRequestCycle cycle = newMock(IRequestCycle.class);
+        ResourceMatcher matcher = newMock(ResourceMatcher.class);
+        
+        ClassResolver resolver = new DefaultClassResolver();
+        URLConnection url = resolver.getResource("/org/apache/tapestry/asset/tapestry-in-action.png").openConnection();
+
+        AssetService service = new AssetService();
+        service.setRequest(request);
+        service.setResponse(response);
+        service.setLog(LogFactory.getLog("test"));
+        service.setUnprotectedMatcher(matcher);
+        service.setClassResolver(resolver);
+        service.setContext(context);
+
+        expect(cycle.getParameter("path")).andReturn("/org/apache/tapestry/asset/tapestry-in-action.png");
+        expect(cycle.getParameter("digest")).andReturn(null);
+
+        expect(matcher.containsResource("/org/apache/tapestry/asset/tapestry-in-action.png")).andReturn(true);
+
+        expect(request.getHeader("If-Modified-Since")).andReturn(null);
+        expect(context.getMimeType("/org/apache/tapestry/asset/tapestry-in-action.png")).andReturn("image/png");
+
+        response.setDateHeader("Last-Modified", url.getLastModified());
+        response.setDateHeader("Expires", service._expireTime);
+        response.setHeader("Cache-Control", "public, max-age=" + (AssetService.MONTH_SECONDS
* 3));
+
+        expect(request.getHeader("User-Agent")).andReturn("Mozilla").anyTimes();
+
+        response.setHeader("ETag", "W/\"" + url.getContentLength() + "-" + url.getLastModified()
+ "\"");
+        response.setContentLength(url.getContentLength());
+
+        expect(response.getOutputStream(new ContentType("image/png"))).andReturn(new ByteArrayOutputStream());
+
+        replay();
+
+        service.service(cycle);
+
+        verify();        
     }
 }



Mime
View raw message