Author: markt Date: Tue Jun 12 19:07:39 2012 New Revision: 1349477 URL: http://svn.apache.org/viewvc?rev=1349477&view=rev Log: Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=50182 Various improvements to the compression filter example. Patch by David Becker
Modified: tomcat/tc7.0.x/trunk/ (props changed) tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/web.xml Propchange: tomcat/tc7.0.x/trunk/ ------------------------------------------------------------------------------ Merged /tomcat/trunk:r1349473 Modified: tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml?rev=1349477&r1=1349476&r2=1349477&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Tue Jun 12 19:07:39 2012 @@ -344,6 +344,10 @@ Remove obsolete bug warning from Windows service documentation page. (rjung) </fix> + <add> + <bug>50182</bug>: Various improvements to the Compression Filter. Patch + provided by David Becker. (markt) + </add> <fix> <bug>52853</bug>: Clarify how Jar Scanner handles directories. (markt) </fix> Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java?rev=1349477&r1=1349476&r2=1349477&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java (original) +++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilter.java Tue Jun 12 19:07:39 2012 @@ -14,11 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package compressionFilters; import java.io.IOException; import java.util.Enumeration; +import java.util.StringTokenizer; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -29,17 +29,14 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - /** * Implementation of <code>javax.servlet.Filter</code> used to compress * the ServletResponse if it is bigger than a threshold. * * @author Amy Roh * @author Dmitri Valdin - * @version $Id$ */ - -public class CompressionFilter implements Filter{ +public class CompressionFilter implements Filter { /** * The filter configuration object we are associated with. If this value @@ -48,18 +45,32 @@ public class CompressionFilter implement private FilterConfig config = null; /** - * Minimal reasonable threshold + * Minimal reasonable threshold. */ private int minThreshold = 128; + /** + * The threshold number to compress. + */ + protected int compressionThreshold = 0; + + /** + * Minimal reasonable buffer. + */ + private int minBuffer = 8192; // 8KB is what tomcat would use by default anyway + + /** + * The compression buffer size to avoid chunking. + */ + protected int compressionBuffer = 0; /** - * The threshold number to compress + * The mime types to compress. */ - protected int compressionThreshold; + protected String[] compressionMimeTypes = {"text/html", "text/xml", "text/plain"}; /** - * Debug level for this filter + * Debug level for this filter. */ private int debug = 0; @@ -68,7 +79,6 @@ public class CompressionFilter implement * * @param filterConfig The filter configuration object */ - @Override public void init(FilterConfig filterConfig) { @@ -77,9 +87,8 @@ public class CompressionFilter implement String value = filterConfig.getInitParameter("debug"); if (value!=null) { debug = Integer.parseInt(value); - } else { - debug = 0; } + String str = filterConfig.getInitParameter("compressionThreshold"); if (str!=null) { compressionThreshold = Integer.parseInt(str); @@ -90,12 +99,33 @@ public class CompressionFilter implement } compressionThreshold = minThreshold; } - } else { - compressionThreshold = 0; } - } else { - compressionThreshold = 0; + str = filterConfig.getInitParameter("compressionBuffer"); + if (str!=null) { + compressionBuffer = Integer.parseInt(str); + if (compressionBuffer < minBuffer) { + if (debug > 0) { + System.out.println("compressionBuffer should be >= " + minBuffer); + System.out.println("compressionBuffer set to " + minBuffer); + } + compressionBuffer = minBuffer; + } + } + + str = filterConfig.getInitParameter("compressionMimeTypes"); + if (str!=null) { + compressionMimeTypes = null; + StringTokenizer st = new StringTokenizer(str, ","); + + while (st.hasMoreTokens()) { + compressionMimeTypes = addStringArray(compressionMimeTypes, st.nextToken().trim()); + } + + if (debug > 0) { + System.out.println("compressionMimeTypes set to " + compressionMimeTypes); + } + } } } @@ -126,7 +156,6 @@ public class CompressionFilter implement * It then invokes the next entity in the chain using the FilterChain object * (<code>chain.doFilter()</code>), <br> **/ - @Override public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException { @@ -137,7 +166,7 @@ public class CompressionFilter implement if (compressionThreshold == 0) { if (debug > 0) { - System.out.println("doFilter gets called, but compressionTreshold is set to 0 - no compression"); + System.out.println("doFilter got called, but compressionTreshold is set to 0 - no compression"); } chain.doFilter(request, response); return; @@ -170,33 +199,35 @@ public class CompressionFilter implement supportCompression = true; } else { if (debug > 0) { - System.out.println("no support for compresion"); + System.out.println("no support for compression"); } } } } - if (!supportCompression) { - if (debug > 0) { - System.out.println("doFilter gets called wo compression"); + if (supportCompression) { + if (response instanceof HttpServletResponse) { + CompressionServletResponseWrapper wrappedResponse = + new CompressionServletResponseWrapper((HttpServletResponse)response); + wrappedResponse.setDebugLevel(debug); + wrappedResponse.setCompressionThreshold(compressionThreshold); + wrappedResponse.setCompressionBuffer(compressionBuffer); + wrappedResponse.setCompressionMimeTypes(compressionMimeTypes); + if (debug > 0) { + System.out.println("doFilter gets called with compression"); + } + try { + chain.doFilter(request, wrappedResponse); + } finally { + wrappedResponse.finishResponse(); + } + return; } - chain.doFilter(request, response); - return; - } - - if (response instanceof HttpServletResponse) { - CompressionServletResponseWrapper wrappedResponse = - new CompressionServletResponseWrapper((HttpServletResponse)response); - wrappedResponse.setDebugLevel(debug); - wrappedResponse.setCompressionThreshold(compressionThreshold); + } else { if (debug > 0) { - System.out.println("doFilter gets called with compression"); - } - try { - chain.doFilter(request, wrappedResponse); - } finally { - wrappedResponse.finishResponse(); + System.out.println("doFilter gets called w/o compression"); } + chain.doFilter(request, response); return; } } @@ -219,5 +250,26 @@ public class CompressionFilter implement return config; } + /** + * General use method + * + * @param sArray the StringArray + * @param value string + */ + private String[] addStringArray(String sArray[], String value) { + String[] result = null; + if (sArray == null) { + result = new String[1]; + result[0] = value; + } + else { + result = new String[sArray.length + 1]; + for (int i = 0; i < sArray.length; i++) + result[i] = sArray[i]; + result[sArray.length] = value; + } + return result; + } + } Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java?rev=1349477&r1=1349476&r2=1349477&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java (original) +++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionFilterTestServlet.java Tue Jun 12 19:07:39 2012 @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package compressionFilters; import java.io.IOException; @@ -29,9 +28,7 @@ import javax.servlet.http.HttpServletRes /** * Very Simple test servlet to test compression filter * @author Amy Roh - * @version $Id$ */ - public class CompressionFilterTestServlet extends HttpServlet { private static final long serialVersionUID = 1L; @@ -57,6 +54,11 @@ public class CompressionFilterTestServle out.println("Compression Filter Test Servlet"); + out.println("Minimum content length for compression is 128 bytes"); + out.println("********** 32 bytes **********"); + out.println("********** 32 bytes **********"); + out.println("********** 32 bytes **********"); + out.println("********** 32 bytes **********"); out.close(); } Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java?rev=1349477&r1=1349476&r2=1349477&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java (original) +++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionResponseStream.java Tue Jun 12 19:07:39 2012 @@ -21,8 +21,6 @@ import java.io.OutputStream; import java.util.zip.GZIPOutputStream; import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletResponse; - /** * Implementation of <b>ServletOutputStream</b> that works with @@ -30,28 +28,26 @@ import javax.servlet.http.HttpServletRes * * @author Amy Roh * @author Dmitri Valdin - * @version $Id$ */ - -public class CompressionResponseStream - extends ServletOutputStream { - +public class CompressionResponseStream extends ServletOutputStream { // ----------------------------------------------------------- Constructors - /** * Construct a servlet output stream associated with the specified Response. * * @param response The associated response + * @param response + * @param originalOutput */ - public CompressionResponseStream(HttpServletResponse response) throws IOException{ + public CompressionResponseStream( + CompressionServletResponseWrapper responseWrapper, + ServletOutputStream originalOutput) { super(); closed = false; - this.response = response; - this.output = response.getOutputStream(); - + this.response = responseWrapper; + this.output = originalOutput; } @@ -65,6 +61,16 @@ public class CompressionResponseStream protected int compressionThreshold = 0; /** + * The compression buffer size to avoid chunking + */ + protected int compressionBuffer = 0; + + /** + * The mime types to compress + */ + protected String[] compressionMimeTypes = {"text/html", "text/xml", "text/plain"}; + + /** * Debug level */ private int debug = 0; @@ -98,10 +104,10 @@ public class CompressionResponseStream /** * The response with which this servlet output stream is associated. */ - protected HttpServletResponse response = null; + protected CompressionServletResponseWrapper response = null; /** - * The underlying servket output stream to which we should write data. + * The underlying servlet output stream to which we should write data. */ protected ServletOutputStream output = null; @@ -119,11 +125,31 @@ public class CompressionResponseStream /** * Set the compressionThreshold number and create buffer for this size */ - protected void setBuffer(int threshold) { - compressionThreshold = threshold; - buffer = new byte[compressionThreshold]; + protected void setCompressionThreshold(int compressionThreshold) { + this.compressionThreshold = compressionThreshold; + buffer = new byte[this.compressionThreshold]; + if (debug > 1) { + System.out.println("compressionThreshold is set to "+ this.compressionThreshold); + } + } + + /** + * The compression buffer size to avoid chunking + */ + protected void setCompressionBuffer(int compressionBuffer) { + this.compressionBuffer = compressionBuffer; + if (debug > 1) { + System.out.println("compressionBuffer is set to "+ this.compressionBuffer); + } + } + + /** + * Set supported mime types + */ + public void setCompressionMimeTypes(String[] compressionMimeTypes) { + this.compressionMimeTypes = compressionMimeTypes; if (debug > 1) { - System.out.println("buffer is set to "+compressionThreshold); + System.out.println("compressionMimeTypes is set to " + this.compressionMimeTypes); } } @@ -301,22 +327,53 @@ public class CompressionResponseStream if (debug > 1) { System.out.println("new GZIPOutputStream"); } + + boolean alreadyCompressed = false; + String contentEncoding = response.getHeader("Content-Encoding"); + if (contentEncoding != null) { + if (contentEncoding.contains("gzip")) { + alreadyCompressed = true; + if (debug > 0) { + System.out.println("content is already compressed"); + } + } else { + if (debug > 0) { + System.out.println("content is not compressed yet"); + } + } + } + + boolean compressibleMimeType = false; + // Check for compatible MIME-TYPE + if (compressionMimeTypes != null) { + if (startsWithStringArray(compressionMimeTypes, response.getContentType())) { + compressibleMimeType = true; + if (debug > 0) { + System.out.println("mime type " + response.getContentType() + " is compressible"); + } + } else { + if (debug > 0) { + System.out.println("mime type " + response.getContentType() + " is not compressible"); + } + } + } + if (response.isCommitted()) { if (debug > 1) System.out.print("Response already committed. Using original output stream"); gzipstream = output; + } else if (alreadyCompressed) { + if (debug > 1) + System.out.print("Response already compressed. Using original output stream"); + gzipstream = output; + } else if (!compressibleMimeType) { + if (debug > 1) + System.out.print("Response mime type is not compressible. Using original output stream"); + gzipstream = output; } else { response.addHeader("Content-Encoding", "gzip"); - String vary = response.getHeader("Vary"); - if (vary == null) { - // Add a new Vary header - response.setHeader("Vary", "Accept-Encoding"); - } else if (vary.equals("*")) { - // No action required - } else { - // Merge into current header - response.setHeader("Vary", vary + ",Accept-Encoding"); - } + response.setContentLength(-1); // don't use any preset content-length as it will be wrong after gzipping + response.setBufferSize(compressionBuffer); gzipstream = new GZIPOutputStream(output); } } @@ -337,4 +394,20 @@ public class CompressionResponseStream } + /** + * Checks if any entry in the string array starts with the specified value + * + * @param sArray the StringArray + * @param value string + */ + private boolean startsWithStringArray(String sArray[], String value) { + if (value == null) + return false; + for (int i = 0; i < sArray.length; i++) { + if (value.startsWith(sArray[i])) { + return true; + } + } + return false; + } } Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java?rev=1349477&r1=1349476&r2=1349477&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java (original) +++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/compressionFilters/CompressionServletResponseWrapper.java Tue Jun 12 19:07:39 2012 @@ -19,6 +19,8 @@ package compressionFilters; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.PrintWriter; +import java.util.HashMap; +import java.util.Map; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; @@ -30,10 +32,9 @@ import javax.servlet.http.HttpServletRes * * @author Amy Roh * @author Dmitri Valdin - * @version $Id$ */ - -public class CompressionServletResponseWrapper extends HttpServletResponseWrapper { +public class CompressionServletResponseWrapper + extends HttpServletResponseWrapper { // ----------------------------------------------------- Constructor @@ -41,7 +42,6 @@ public class CompressionServletResponseW * Calls the parent constructor which creates a ServletResponse adaptor * wrapping the given response object. */ - public CompressionServletResponseWrapper(HttpServletResponse response) { super(response); origResponse = response; @@ -83,44 +83,61 @@ public class CompressionServletResponseW /** * The threshold number to compress */ - protected int threshold = 0; + protected int compressionThreshold = 0; + + /** + * The compression buffer size + */ + protected int compressionBuffer = 8192; // 8KB default + + /** + * The mime types to compress + */ + protected String[] compressionMimeTypes = {"text/html", "text/xml", "text/plain"}; /** * Debug level */ - private int debug = 0; + protected int debug = 0; /** - * Content type + * keeps a copy of all headers set */ - protected String contentType = null; + private Map<String,String> headerCopies = new HashMap<String,String>(); + // --------------------------------------------------------- Public Methods /** - * Set content type + * Set threshold number */ - @Override - public void setContentType(String contentType) { + public void setCompressionThreshold(int threshold) { if (debug > 1) { - System.out.println("setContentType to "+contentType); + System.out.println("setCompressionThreshold to " + threshold); } - this.contentType = contentType; - origResponse.setContentType(contentType); + this.compressionThreshold = threshold; } - /** - * Set threshold number + * Set compression buffer */ - public void setCompressionThreshold(int threshold) { + public void setCompressionBuffer(int buffer) { if (debug > 1) { - System.out.println("setCompressionThreshold to " + threshold); + System.out.println("setCompressionBuffer to " + buffer); } - this.threshold = threshold; + this.compressionBuffer = buffer; } + /** + * Set compressible mime types + */ + public void setCompressionMimeTypes(String[] mimeTypes) { + if (debug > 1) { + System.out.println("setCompressionMimeTypes to " + mimeTypes); + } + this.compressionMimeTypes = mimeTypes; + } /** * Set debug level @@ -141,13 +158,14 @@ public class CompressionServletResponseW System.out.println("createOutputStream gets called"); } - CompressionResponseStream compressedStream = - new CompressionResponseStream(origResponse); - compressedStream.setDebugLevel(debug); - compressedStream.setBuffer(threshold); - - return compressedStream; + CompressionResponseStream stream = new CompressionResponseStream( + this, origResponse.getOutputStream()); + stream.setDebugLevel(debug); + stream.setCompressionThreshold(compressionThreshold); + stream.setCompressionBuffer(compressionBuffer); + stream.setCompressionMimeTypes(compressionMimeTypes); + return stream; } @@ -179,7 +197,7 @@ public class CompressionServletResponseW @Override public void flushBuffer() throws IOException { if (debug > 1) { - System.out.println("flush buffer @ CompressionServletResponseWrapper"); + System.out.println("flush buffer @ GZipServletResponseWrapper"); } ((CompressionResponseStream)stream).flush(); @@ -228,7 +246,6 @@ public class CompressionServletResponseW if (debug > 1) { System.out.println("stream is set to "+stream+" in getWriter"); } - //String charset = getCharsetFromContentType(contentType); String charEnc = origResponse.getCharacterEncoding(); if (debug > 1) { System.out.println("character encoding is " + charEnc); @@ -242,12 +259,27 @@ public class CompressionServletResponseW } return (writer); + } + @Override + public String getHeader(String name) { + return headerCopies.get(name); } @Override - public void setContentLength(int length) { - // Don't, as compression will change it + public void addHeader(String name, String value) { + if (headerCopies.containsKey(name)) { + String existingValue = headerCopies.get(name); + if ((existingValue != null) && (existingValue.length() > 0)) headerCopies.put(name, existingValue + "," + value); + else headerCopies.put(name, value); + } else headerCopies.put(name, value); + super.addHeader(name, value); } + + @Override + public void setHeader(String name, String value) { + headerCopies.put(name, value); + super.setHeader(name, value); + } } Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/web.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/web.xml?rev=1349477&r1=1349476&r2=1349477&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/web.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/web.xml Tue Jun 12 19:07:39 2012 @@ -60,10 +60,17 @@ <filter> <filter-name>Compression Filter</filter-name> <filter-class>compressionFilters.CompressionFilter</filter-class> - <init-param> - <param-name>compressionThreshold</param-name> - <param-value>10</param-value> + <param-name>compressionThreshold</param-name> + <param-value>128</param-value> + </init-param> + <init-param> + <param-name>compressionBuffer</param-name> + <param-value>8192</param-value> + </init-param> + <init-param> + <param-name>compressionMimeTypes</param-name> + <param-value>text/html,text/plain,text/xml</param-value> </init-param> <init-param> <param-name>debug</param-name> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org