Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Wilkins2013-12-06 11:51:50 +0000
committerGreg Wilkins2013-12-06 11:53:41 +0000
commitcd05751ff7cb4bbad00480bffe12186a3c262215 (patch)
tree6b027225b28fce2154d463605237baa6e55caac9 /jetty-servlets
parent5f204b881295d410130743cb6a4441f72d4df0b8 (diff)
downloadorg.eclipse.jetty.project-cd05751ff7cb4bbad00480bffe12186a3c262215.tar.gz
org.eclipse.jetty.project-cd05751ff7cb4bbad00480bffe12186a3c262215.tar.xz
org.eclipse.jetty.project-cd05751ff7cb4bbad00480bffe12186a3c262215.zip
423392 - GzipFilter without wrapping or blocking
I Added AsyncGzipFilter, which uses a modified HttpOutput instance to provide gzip compression without wrapping or blocking. Does not currently handle deflate.
Diffstat (limited to 'jetty-servlets')
-rw-r--r--jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java535
-rw-r--r--jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipFactory.java38
-rw-r--r--jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java353
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java58
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java76
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java141
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java29
-rw-r--r--jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletBufferTypeLengthWrite.java68
-rw-r--r--jetty-servlets/src/test/resources/jetty-logging.properties1
9 files changed, 1161 insertions, 138 deletions
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java
new file mode 100644
index 0000000000..eb7df12794
--- /dev/null
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java
@@ -0,0 +1,535 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.servlets;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+import java.util.zip.Deflater;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpOutput;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.servlets.gzip.GzipFactory;
+import org.eclipse.jetty.servlets.gzip.GzipHttpOutput;
+import org.eclipse.jetty.util.URIUtil;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/* ------------------------------------------------------------ */
+/** Async GZIP Filter
+ * This filter is a gzip filter using jetty internal mechanism to apply gzip compression
+ * to output that is compatible with async IO and does not need to wrap the response nor output stream.
+ * The filter will gzip the content of a response if: <ul>
+ * <li>The filter is mapped to a matching path</li>
+ * <li>accept-encoding header is set to either gzip, deflate or a combination of those</li>
+ * <li>The response status code is >=200 and <300
+ * <li>The content length is unknown or more than the <code>minGzipSize</code> initParameter or the minGzipSize is 0(default)</li>
+ * <li>If a list of mimeTypes is set by the <code>mimeTypes</code> init parameter, then the Content-Type is in the list.</li>
+ * <li>If no mimeType list is set, then the content-type is not in the list defined by <code>excludedMimeTypes</code></li>
+ * <li>No content-encoding is specified by the resource</li>
+ * </ul>
+ *
+ * <p>
+ * Compressing the content can greatly improve the network bandwidth usage, but at a cost of memory and
+ * CPU cycles. If this filter is mapped for static content, then use of efficient direct NIO may be
+ * prevented, thus use of the gzip mechanism of the {@link org.eclipse.jetty.servlet.DefaultServlet} is
+ * advised instead.
+ * </p>
+ * <p>
+ * This filter extends {@link UserAgentFilter} and if the the initParameter <code>excludedAgents</code>
+ * is set to a comma separated list of user agents, then these agents will be excluded from gzip content.
+ * </p>
+ * <p>Init Parameters:</p>
+ * <dl>
+ * <dt>bufferSize</dt> <dd>The output buffer size. Defaults to 8192. Be careful as values <= 0 will lead to an
+ * {@link IllegalArgumentException}.
+ * See: {@link java.util.zip.GZIPOutputStream#GZIPOutputStream(java.io.OutputStream, int)}
+ * and: {@link java.util.zip.DeflaterOutputStream#DeflaterOutputStream(java.io.OutputStream, Deflater, int)}
+ * </dd>
+ * <dt>minGzipSize</dt> <dd>Content will only be compressed if content length is either unknown or greater
+ * than <code>minGzipSize</code>.
+ * </dd>
+ * <dt>deflateCompressionLevel</dt> <dd>The compression level used for deflate compression. (0-9).
+ * See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
+ * </dd>
+ * <dt>deflateNoWrap</dt> <dd>The noWrap setting for deflate compression. Defaults to true. (true/false)
+ * See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
+ * </dd>
+ * <dt>methods</dt> <dd>Comma separated list of HTTP methods to compress. If not set, only GET requests are compressed.
+ * </dd>
+ * <dt>mimeTypes</dt> <dd>Comma separated list of mime types to compress. If it is not set, then the excludedMimeTypes list is used.
+ * </dd>
+ * <dt>excludedMimeTypes</dt> <dd>Comma separated list of mime types to never compress. If not set, then the default is the commonly known
+ * image, video, audio and compressed types.
+ * </dd>
+
+ * <dt>excludedAgents</dt> <dd>Comma separated list of user agents to exclude from compression. Does a
+ * {@link String#contains(CharSequence)} to check if the excluded agent occurs
+ * in the user-agent header. If it does -> no compression
+ * </dd>
+ * <dt>excludeAgentPatterns</dt> <dd>Same as excludedAgents, but accepts regex patterns for more complex matching.
+ * </dd>
+ * <dt>excludePaths</dt> <dd>Comma separated list of paths to exclude from compression.
+ * Does a {@link String#startsWith(String)} comparison to check if the path matches.
+ * If it does match -> no compression. To match subpaths use <code>excludePathPatterns</code>
+ * instead.
+ * </dd>
+ * <dt>excludePathPatterns</dt> <dd>Same as excludePath, but accepts regex patterns for more complex matching.
+ * </dd>
+ * <dt>vary</dt> <dd>Set to the value of the Vary header sent with responses that could be compressed. By default it is
+ * set to 'Vary: Accept-Encoding, User-Agent' since IE6 is excluded by default from the excludedAgents.
+ * If user-agents are not to be excluded, then this can be set to 'Vary: Accept-Encoding'. Note also
+ * that shared caches may cache copies of a resource that is varied by User-Agent - one per variation of
+ * the User-Agent, unless the cache does some normalization of the UA string.
+ * </dd>
+ * <dt>checkGzExists</dt> <dd>If set to true, the filter check if a static resource with ".gz" appended exists. If so then
+ * the normal processing is done so that the default servlet can send the pre existing gz content.
+ * </dd>
+ * </dl>
+ */
+public class AsyncGzipFilter extends UserAgentFilter implements GzipFactory
+{
+ private static final Logger LOG = Log.getLogger(GzipFilter.class);
+ public final static String GZIP = "gzip";
+ public static final String DEFLATE = "deflate";
+ public final static String ETAG = "o.e.j.s.GzipFilter.ETag";
+ public final static int DEFAULT_MIN_GZIP_SIZE=256;
+
+ protected ServletContext _context;
+ protected final Set<String> _mimeTypes=new HashSet<>();
+ protected boolean _excludeMimeTypes;
+ protected int _bufferSize=8192;
+ protected int _minGzipSize=DEFAULT_MIN_GZIP_SIZE;
+ protected int _deflateCompressionLevel=Deflater.DEFAULT_COMPRESSION;
+ protected boolean _deflateNoWrap = true;
+ protected boolean _checkGzExists = true;
+
+ // non-static, as other GzipFilter instances may have different configurations
+ protected final ThreadLocal<Deflater> _deflater = new ThreadLocal<Deflater>();
+
+ protected final static ThreadLocal<byte[]> _buffer= new ThreadLocal<byte[]>();
+
+ protected final Set<String> _methods=new HashSet<String>();
+ protected Set<String> _excludedAgents;
+ protected Set<Pattern> _excludedAgentPatterns;
+ protected Set<String> _excludedPaths;
+ protected Set<Pattern> _excludedPathPatterns;
+ protected HttpField _vary=new HttpGenerator.CachedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT);
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.servlets.UserAgentFilter#init(javax.servlet.FilterConfig)
+ */
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException
+ {
+ super.init(filterConfig);
+
+ _context=filterConfig.getServletContext();
+
+ String tmp=filterConfig.getInitParameter("bufferSize");
+ if (tmp!=null)
+ _bufferSize=Integer.parseInt(tmp);
+ LOG.debug("{} bufferSize={}",this,_bufferSize);
+
+ tmp=filterConfig.getInitParameter("minGzipSize");
+ if (tmp!=null)
+ _minGzipSize=Integer.parseInt(tmp);
+ LOG.debug("{} minGzipSize={}",this,_minGzipSize);
+
+ tmp=filterConfig.getInitParameter("deflateCompressionLevel");
+ if (tmp!=null)
+ _deflateCompressionLevel=Integer.parseInt(tmp);
+ LOG.debug("{} deflateCompressionLevel={}",this,_deflateCompressionLevel);
+
+ tmp=filterConfig.getInitParameter("deflateNoWrap");
+ if (tmp!=null)
+ _deflateNoWrap=Boolean.parseBoolean(tmp);
+ LOG.debug("{} deflateNoWrap={}",this,_deflateNoWrap);
+
+ tmp=filterConfig.getInitParameter("checkGzExists");
+ if (tmp!=null)
+ _checkGzExists=Boolean.parseBoolean(tmp);
+ LOG.debug("{} checkGzExists={}",this,_checkGzExists);
+
+ tmp=filterConfig.getInitParameter("methods");
+ if (tmp!=null)
+ {
+ StringTokenizer tok = new StringTokenizer(tmp,",",false);
+ while (tok.hasMoreTokens())
+ _methods.add(tok.nextToken().trim().toUpperCase());
+ }
+ else
+ _methods.add(HttpMethod.GET.asString());
+ LOG.debug("{} methods={}",this,_methods);
+
+ tmp=filterConfig.getInitParameter("mimeTypes");
+ if (tmp==null)
+ {
+ _excludeMimeTypes=true;
+ tmp=filterConfig.getInitParameter("excludedMimeTypes");
+ if (tmp==null)
+ {
+ for (String type:MimeTypes.getKnownMimeTypes())
+ {
+ if (type.startsWith("image/")||
+ type.startsWith("audio/")||
+ type.startsWith("video/"))
+ _mimeTypes.add(type);
+ _mimeTypes.add("application/compress");
+ _mimeTypes.add("application/zip");
+ _mimeTypes.add("application/gzip");
+ }
+ }
+ else
+ {
+ StringTokenizer tok = new StringTokenizer(tmp,",",false);
+ while (tok.hasMoreTokens())
+ _mimeTypes.add(tok.nextToken().trim());
+ }
+ }
+ else
+ {
+ StringTokenizer tok = new StringTokenizer(tmp,",",false);
+ while (tok.hasMoreTokens())
+ _mimeTypes.add(tok.nextToken().trim());
+ }
+ LOG.debug("{} mimeTypes={}",this,_mimeTypes);
+ LOG.debug("{} excludeMimeTypes={}",this,_excludeMimeTypes);
+ tmp=filterConfig.getInitParameter("excludedAgents");
+ if (tmp!=null)
+ {
+ _excludedAgents=new HashSet<String>();
+ StringTokenizer tok = new StringTokenizer(tmp,",",false);
+ while (tok.hasMoreTokens())
+ _excludedAgents.add(tok.nextToken().trim());
+ }
+ LOG.debug("{} excludedAgents={}",this,_excludedAgents);
+
+ tmp=filterConfig.getInitParameter("excludeAgentPatterns");
+ if (tmp!=null)
+ {
+ _excludedAgentPatterns=new HashSet<Pattern>();
+ StringTokenizer tok = new StringTokenizer(tmp,",",false);
+ while (tok.hasMoreTokens())
+ _excludedAgentPatterns.add(Pattern.compile(tok.nextToken().trim()));
+ }
+ LOG.debug("{} excludedAgentPatterns={}",this,_excludedAgentPatterns);
+
+ tmp=filterConfig.getInitParameter("excludePaths");
+ if (tmp!=null)
+ {
+ _excludedPaths=new HashSet<String>();
+ StringTokenizer tok = new StringTokenizer(tmp,",",false);
+ while (tok.hasMoreTokens())
+ _excludedPaths.add(tok.nextToken().trim());
+ }
+ LOG.debug("{} excludedPaths={}",this,_excludedPaths);
+
+ tmp=filterConfig.getInitParameter("excludePathPatterns");
+ if (tmp!=null)
+ {
+ _excludedPathPatterns=new HashSet<Pattern>();
+ StringTokenizer tok = new StringTokenizer(tmp,",",false);
+ while (tok.hasMoreTokens())
+ _excludedPathPatterns.add(Pattern.compile(tok.nextToken().trim()));
+ }
+ LOG.debug("{} excludedPathPatterns={}",this,_excludedPathPatterns);
+
+ tmp=filterConfig.getInitParameter("vary");
+ if (tmp!=null)
+ _vary=new HttpGenerator.CachedHttpField(HttpHeader.VARY,tmp);
+ LOG.debug("{} vary={}",this,_vary);
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.servlets.UserAgentFilter#destroy()
+ */
+ @Override
+ public void destroy()
+ {
+ }
+
+ /* ------------------------------------------------------------ */
+ /**
+ * @see org.eclipse.jetty.servlets.UserAgentFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
+ */
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
+ throws IOException, ServletException
+ {
+ HttpServletRequest request=(HttpServletRequest)req;
+ HttpServletResponse response=(HttpServletResponse)res;
+
+ // If not a supported method or it is an Excluded URI or an excluded UA - no Vary because no matter what client, this URI is always excluded
+ String requestURI = request.getRequestURI();
+ if (!_methods.contains(request.getMethod()))
+ {
+ LOG.debug("{} excluded by method {}",this,request);
+ super.doFilter(request,response,chain);
+ return;
+ }
+
+ if (isExcludedPath(requestURI))
+ {
+ LOG.debug("{} excluded by path {}",this,request);
+ super.doFilter(request,response,chain);
+ return;
+ }
+
+ // Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded
+ if (_mimeTypes.size()>0)
+ {
+ String mimeType = _context.getMimeType(request.getRequestURI());
+
+ if (mimeType!=null && _mimeTypes.contains(mimeType)==_excludeMimeTypes)
+ {
+ LOG.debug("{} excluded by path suffix {}",this,request);
+ // handle normally without setting vary header
+ super.doFilter(request,response,chain);
+ return;
+ }
+ }
+
+ if (_checkGzExists && request.getServletContext()!=null)
+ {
+ String path=request.getServletContext().getRealPath(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()));
+ if (path!=null)
+ {
+ File gz=new File(path+".gz");
+ if (gz.exists())
+ {
+ LOG.debug("{} gzip exists {}",this,request);
+ // allow default servlet to handle
+ super.doFilter(request,response,chain);
+ return;
+ }
+ }
+ }
+
+ // Special handling for etags
+ String etag = request.getHeader("If-None-Match");
+ if (etag!=null)
+ {
+ int dd=etag.indexOf("--");
+ if (dd>0)
+ request.setAttribute(ETAG,etag.substring(0,dd)+(etag.endsWith("\"")?"\"":""));
+ }
+
+ HttpChannel<?> channel = HttpChannel.getCurrentHttpChannel();
+ HttpOutput out = channel.getResponse().getHttpOutput();
+ if (!(out instanceof GzipHttpOutput))
+ {
+ if (out.getClass()!=HttpOutput.class)
+ throw new IllegalStateException();
+ channel.getResponse().setHttpOutput(out = new GzipHttpOutput(channel));
+ }
+
+ GzipHttpOutput cout=(GzipHttpOutput)out;
+
+ boolean exceptional=true;
+ try
+ {
+ cout.mightCompress(this);
+ super.doFilter(request,response,chain);
+ exceptional=false;
+ }
+ finally
+ {
+ LOG.debug("{} excepted {}",this,request);
+ if (exceptional && !response.isCommitted())
+ {
+ cout.resetBuffer();
+ cout.noCompression();
+ }
+ }
+ }
+
+
+ /**
+ * Checks to see if the userAgent is excluded
+ *
+ * @param ua
+ * the user agent
+ * @return boolean true if excluded
+ */
+ private boolean isExcludedAgent(String ua)
+ {
+ if (ua == null)
+ return false;
+
+ if (_excludedAgents != null)
+ {
+ if (_excludedAgents.contains(ua))
+ {
+ return true;
+ }
+ }
+ if (_excludedAgentPatterns != null)
+ {
+ for (Pattern pattern : _excludedAgentPatterns)
+ {
+ if (pattern.matcher(ua).matches())
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks to see if the path is excluded
+ *
+ * @param requestURI
+ * the request uri
+ * @return boolean true if excluded
+ */
+ private boolean isExcludedPath(String requestURI)
+ {
+ if (requestURI == null)
+ return false;
+ if (_excludedPaths != null)
+ {
+ for (String excludedPath : _excludedPaths)
+ {
+ if (requestURI.startsWith(excludedPath))
+ {
+ return true;
+ }
+ }
+ }
+ if (_excludedPathPatterns != null)
+ {
+ for (Pattern pattern : _excludedPathPatterns)
+ {
+ if (pattern.matcher(requestURI).matches())
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public HttpField getVaryField()
+ {
+ return _vary;
+ }
+
+ @Override
+ public Deflater getDeflater(Request request, long content_length)
+ {
+
+ String ua = getUserAgent(request);
+ if (ua!=null && isExcludedAgent(ua))
+ {
+ LOG.debug("{} excluded user agent {}",this,request);
+ return null;
+ }
+
+ if (content_length>=0 && content_length<_minGzipSize)
+ {
+ LOG.debug("{} excluded minGzipSize {}",this,request);
+ return null;
+ }
+
+ String accept = request.getHttpFields().get(HttpHeader.ACCEPT_ENCODING);
+ if (accept==null)
+ {
+ LOG.debug("{} excluded !accept {}",this,request);
+ return null;
+ }
+
+ boolean gzip=false;
+ if (GZIP.equals(accept) || accept.startsWith("gzip,"))
+ gzip=true;
+ else
+ {
+ List<String> list=HttpFields.qualityList(request.getHttpFields().getValues(HttpHeader.ACCEPT_ENCODING.asString(),","));
+ for (String a:list)
+ {
+ if (GZIP.equalsIgnoreCase(HttpFields.valueParameters(a,null)))
+ {
+ gzip=true;
+ break;
+ }
+ }
+ }
+
+ if (!gzip)
+ {
+ LOG.debug("{} excluded not gzip accept {}",this,request);
+ return null;
+ }
+
+ Deflater df = _deflater.get();
+ if (df==null)
+ df=new Deflater(_deflateCompressionLevel,_deflateNoWrap);
+ else
+ _deflater.set(null);
+
+ return df;
+ }
+
+ @Override
+ public void recycle(Deflater deflater)
+ {
+ if (_deflater.get()==null)
+ _deflater.set(deflater);
+
+ }
+
+ @Override
+ public boolean isExcludedMimeType(String mimetype)
+ {
+ return _mimeTypes.contains(mimetype) == _excludeMimeTypes;
+ }
+
+ @Override
+ public int getBufferSize()
+ {
+ return _bufferSize;
+ }
+
+
+}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipFactory.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipFactory.java
new file mode 100644
index 0000000000..474706f8c4
--- /dev/null
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipFactory.java
@@ -0,0 +1,38 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.servlets.gzip;
+
+import java.util.zip.Deflater;
+
+import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.server.Request;
+
+public interface GzipFactory
+{
+ int getBufferSize();
+
+ HttpField getVaryField();
+
+ Deflater getDeflater(Request request, long content_length);
+
+ boolean isExcludedMimeType(String asciiToLowerCase);
+
+ void recycle(Deflater deflater);
+
+}
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java
new file mode 100644
index 0000000000..29f969e630
--- /dev/null
+++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java
@@ -0,0 +1,353 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.servlets.gzip;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.WritePendingException;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+
+import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.http.HttpGenerator;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpOutput;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.IteratingNestedCallback;
+import org.eclipse.jetty.util.StringUtil;
+
+public class GzipHttpOutput extends HttpOutput
+{
+ private final static HttpGenerator.CachedHttpField CONTENT_ENCODING_GZIP=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_ENCODING,"gzip");
+ private final static byte[] GZIP_HEADER = new byte[] { (byte)0x1f, (byte)0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0 };
+
+ private enum GZState { NOT_COMPRESSING, MIGHT_COMPRESS, COMMITTING, COMPRESSING, FINISHED};
+ private final AtomicReference<GZState> _state = new AtomicReference<>(GZState.NOT_COMPRESSING);
+ private final CRC32 _crc = new CRC32();
+
+ private Deflater _deflater;
+ private GzipFactory _factory;
+ private ByteBuffer _buffer;
+
+
+ public GzipHttpOutput(HttpChannel<?> channel)
+ {
+ super(channel);
+ }
+
+ @Override
+ public void reset()
+ {
+ super.reset();
+ }
+
+ @Override
+ protected void write(ByteBuffer content, boolean complete, Callback callback)
+ {
+ switch (_state.get())
+ {
+ case NOT_COMPRESSING:
+ super.write(content,complete,callback);
+ return;
+
+ case MIGHT_COMPRESS:
+ commit(content,complete,callback);
+ break;
+
+ case COMMITTING:
+ throw new WritePendingException();
+
+ case COMPRESSING:
+ gzip(content,complete,callback);
+ break;
+
+ case FINISHED:
+ throw new IllegalStateException();
+ }
+ }
+
+ private void superWrite(ByteBuffer content, boolean complete, Callback callback)
+ {
+ super.write(content,complete,callback);
+ }
+
+ private void addTrailer()
+ {
+ int i=_buffer.limit();
+ _buffer.limit(i+8);
+
+ int v=(int)_crc.getValue();
+ _buffer.put(i++,(byte)(v & 0xFF));
+ _buffer.put(i++,(byte)((v>>>8) & 0xFF));
+ _buffer.put(i++,(byte)((v>>>16) & 0xFF));
+ _buffer.put(i++,(byte)((v>>>24) & 0xFF));
+
+ v=_deflater.getTotalIn();
+ _buffer.put(i++,(byte)(v & 0xFF));
+ _buffer.put(i++,(byte)((v>>>8) & 0xFF));
+ _buffer.put(i++,(byte)((v>>>16) & 0xFF));
+ _buffer.put(i++,(byte)((v>>>24) & 0xFF));
+ }
+
+
+ private void gzip(ByteBuffer content, boolean complete, final Callback callback)
+ {
+ if (content.hasRemaining())
+ {
+ if (content.hasArray())
+ new GzipArrayCB(content,complete,callback).iterate();
+ else
+ new GzipBufferCB(content,complete,callback).iterate();
+ }
+ else if (complete)
+ {
+ _deflater.finish();
+
+ int produced=_deflater.deflate(_buffer.array(),_buffer.arrayOffset()+_buffer.limit(),_buffer.capacity()-_buffer.limit(),Deflater.NO_FLUSH);
+ _buffer.limit(_buffer.limit()+produced);
+ addTrailer();
+ superWrite(_buffer,complete,new Callback()
+ {
+ @Override
+ public void succeeded()
+ {
+ getHttpChannel().getByteBufferPool().release(_buffer);
+ _buffer=null;
+ callback.succeeded();
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ callback.failed(x);
+ }
+ });
+ }
+ }
+
+ protected void commit(ByteBuffer content, boolean complete, Callback callback)
+ {
+ // Are we excluding because of status?
+ Response response=getHttpChannel().getResponse();
+ int sc = response.getStatus();
+ if (sc>0 && (sc<200 || sc==204 || sc==205 || sc>=300))
+ {
+ noCompression();
+ super.write(content,complete,callback);
+ return;
+ }
+
+ // Are we excluding because of mime-type?
+ String ct = getHttpChannel().getResponse().getContentType();
+ if (ct!=null)
+ {
+ ct=MimeTypes.getContentTypeWithoutCharset(ct);
+ if (_factory.isExcludedMimeType(StringUtil.asciiToLowerCase(ct)))
+ {
+ noCompression();
+ super.write(content,complete,callback);
+ return;
+ }
+ }
+
+ // Are we the thread that commits?
+ if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING))
+ {
+ // We are varying the response due to accept encoding header.
+ HttpFields fields = response.getHttpFields();
+ fields.add(_factory.getVaryField());
+
+ long content_length = response.getContentLength();
+ if (content_length<0 && complete)
+ content_length=content.remaining();
+
+ _deflater = content.isDirect()?null:_factory.getDeflater(getHttpChannel().getRequest(),content_length);
+
+ if (_deflater==null)
+ {
+ _state.set(GZState.NOT_COMPRESSING);
+ super.write(content,complete,callback);
+ return;
+ }
+
+ fields.put(CONTENT_ENCODING_GZIP);
+ _crc.reset();
+ _buffer=getHttpChannel().getByteBufferPool().acquire(_factory.getBufferSize(),false);
+ BufferUtil.fill(_buffer,GZIP_HEADER,0,GZIP_HEADER.length);
+
+ // Adjust headers
+ response.setContentLength(-1);
+ String etag=fields.get(HttpHeader.ETAG);
+ if (etag!=null)
+ fields.put(HttpHeader.ETAG,etag.substring(0,etag.length()-1)+"--gzip\"");
+
+ _state.set(GZState.COMPRESSING);
+
+ gzip(content,complete,callback);
+ }
+ }
+
+ public void noCompression()
+ {
+ while (true)
+ {
+ switch (_state.get())
+ {
+ case NOT_COMPRESSING:
+ return;
+
+ case MIGHT_COMPRESS:
+ if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.NOT_COMPRESSING))
+ return;
+ break;
+
+ default:
+ throw new IllegalStateException(_state.get().toString());
+ }
+ }
+ }
+
+ public void mightCompress(GzipFactory factory)
+ {
+ while (true)
+ {
+ switch (_state.get())
+ {
+ case NOT_COMPRESSING:
+ _factory=factory;
+ if (_state.compareAndSet(GZState.NOT_COMPRESSING,GZState.MIGHT_COMPRESS))
+ return;
+ _factory=null;
+ break;
+
+ default:
+ throw new IllegalStateException();
+ }
+ }
+ }
+
+ private class GzipArrayCB extends IteratingNestedCallback
+ {
+ private final boolean _complete;
+ public GzipArrayCB(ByteBuffer content, boolean complete, Callback callback)
+ {
+ super(callback);
+ _complete=complete;
+
+ byte[] array=content.array();
+ int off=content.arrayOffset()+content.position();
+ int len=content.remaining();
+ _crc.update(array,off,len);
+ _deflater.setInput(array,off,len);
+ if (complete)
+ _deflater.finish();
+ content.position(content.limit());
+ }
+
+ @Override
+ protected State process() throws Exception
+ {
+ if (_deflater.needsInput())
+ {
+ if (_deflater.finished())
+ {
+ _factory.recycle(_deflater);
+ _deflater=null;
+ getHttpChannel().getByteBufferPool().release(_buffer);
+ _buffer=null;
+ }
+ return State.SUCCEEDED;
+ }
+
+ int off=_buffer.arrayOffset()+_buffer.limit();
+ int len=_buffer.capacity()-_buffer.limit()- (_complete?8:0);
+ int produced=_deflater.deflate(_buffer.array(),off,len,Deflater.NO_FLUSH);
+ _buffer.limit(_buffer.limit()+produced);
+ boolean complete=_deflater.finished();
+ if (complete)
+ {
+ addTrailer();
+ _deflater.end(); // TODO recycle
+ }
+ superWrite(_buffer,complete,this);
+ return State.SCHEDULED;
+ }
+
+ }
+
+ private class GzipBufferCB extends IteratingNestedCallback
+ {
+ private final ByteBuffer _input;
+ private final ByteBuffer _content;
+ private final boolean _complete;
+ public GzipBufferCB(ByteBuffer content, boolean complete, Callback callback)
+ {
+ super(callback);
+ _input=getHttpChannel().getByteBufferPool().acquire(Math.min(_factory.getBufferSize(),content.remaining()),false);
+ _content=content;
+ _complete=complete;
+ }
+
+ @Override
+ protected State process() throws Exception
+ {
+ if (_deflater.needsInput())
+ {
+ if (BufferUtil.isEmpty(_content))
+ {
+ if (_deflater.finished())
+ {
+ _factory.recycle(_deflater);
+ _deflater=null;
+ getHttpChannel().getByteBufferPool().release(_buffer);
+ _buffer=null;
+ }
+ return State.SUCCEEDED;
+ }
+
+ BufferUtil.clearToFill(_input);
+ BufferUtil.put(_content,_input);
+ BufferUtil.flipToFlush(_input,0);
+
+ byte[] array=_input.array();
+ int off=_input.arrayOffset()+_input.position();
+ int len=_input.remaining();
+ _crc.update(array,off,len);
+ _deflater.setInput(array,off,len);
+ if (_complete && BufferUtil.isEmpty(_content))
+ _deflater.finish();
+ }
+
+ int off=_buffer.arrayOffset()+_buffer.limit();
+ int len=_buffer.capacity()-_buffer.limit() - (_complete?8:0);
+ int produced=_deflater.deflate(_buffer.array(),off,len,Deflater.NO_FLUSH);
+ _buffer.limit(_buffer.limit()+produced);
+ boolean complete=_deflater.finished();
+ if (complete)
+ addTrailer();
+ superWrite(_buffer,complete,this);
+ return State.SCHEDULED;
+ }
+
+ }
+}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java
index 101eaafdb7..9e1d4aab14 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterContentLengthTest.java
@@ -22,13 +22,15 @@ import java.io.File;
import java.util.Arrays;
import java.util.List;
+import javax.servlet.Filter;
import javax.servlet.Servlet;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
import org.eclipse.jetty.servlets.gzip.GzipTester;
+import org.eclipse.jetty.servlets.gzip.TestServletBufferTypeLengthWrite;
import org.eclipse.jetty.servlets.gzip.TestServletLengthStreamTypeWrite;
import org.eclipse.jetty.servlets.gzip.TestServletLengthTypeStreamWrite;
import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWrite;
@@ -71,32 +73,45 @@ public class GzipFilterContentLengthTest
{
return Arrays.asList(new Object[][]
{
- { TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP },
- { TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP },
- { TestServletStreamLengthTypeWrite.class, GzipFilter.GZIP },
- { TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.GZIP },
- { TestServletStreamTypeLengthWrite.class, GzipFilter.GZIP },
- { TestServletTypeLengthStreamWrite.class, GzipFilter.GZIP },
- { TestServletTypeStreamLengthWrite.class, GzipFilter.GZIP },
- { TestServletLengthStreamTypeWrite.class, GzipFilter.DEFLATE },
- { TestServletLengthTypeStreamWrite.class, GzipFilter.DEFLATE },
- { TestServletStreamLengthTypeWrite.class, GzipFilter.DEFLATE },
- { TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.DEFLATE },
- { TestServletStreamTypeLengthWrite.class, GzipFilter.DEFLATE },
- { TestServletTypeLengthStreamWrite.class, GzipFilter.DEFLATE },
- { TestServletTypeStreamLengthWrite.class, GzipFilter.DEFLATE }
+ { AsyncGzipFilter.class, TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP },
+ { AsyncGzipFilter.class, TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP },
+ { AsyncGzipFilter.class, TestServletStreamLengthTypeWrite.class, GzipFilter.GZIP },
+ { AsyncGzipFilter.class, TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.GZIP },
+ { AsyncGzipFilter.class, TestServletStreamTypeLengthWrite.class, GzipFilter.GZIP },
+ { AsyncGzipFilter.class, TestServletTypeLengthStreamWrite.class, GzipFilter.GZIP },
+ { AsyncGzipFilter.class, TestServletTypeStreamLengthWrite.class, GzipFilter.GZIP },
+ { AsyncGzipFilter.class, TestServletBufferTypeLengthWrite.class, GzipFilter.GZIP },
+
+ { GzipFilter.class, TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP },
+ { GzipFilter.class, TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP },
+ { GzipFilter.class, TestServletStreamLengthTypeWrite.class, GzipFilter.GZIP },
+ { GzipFilter.class, TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.GZIP },
+ { GzipFilter.class, TestServletStreamTypeLengthWrite.class, GzipFilter.GZIP },
+ { GzipFilter.class, TestServletTypeLengthStreamWrite.class, GzipFilter.GZIP },
+ { GzipFilter.class, TestServletTypeStreamLengthWrite.class, GzipFilter.GZIP },
+
+ { GzipFilter.class, TestServletLengthStreamTypeWrite.class, GzipFilter.DEFLATE },
+ { GzipFilter.class, TestServletLengthTypeStreamWrite.class, GzipFilter.DEFLATE },
+ { GzipFilter.class, TestServletStreamLengthTypeWrite.class, GzipFilter.DEFLATE },
+ { GzipFilter.class, TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.DEFLATE },
+ { GzipFilter.class, TestServletStreamTypeLengthWrite.class, GzipFilter.DEFLATE },
+ { GzipFilter.class, TestServletTypeLengthStreamWrite.class, GzipFilter.DEFLATE },
+ { GzipFilter.class, TestServletTypeStreamLengthWrite.class, GzipFilter.DEFLATE },
+
});
}
- private static final int LARGE = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 8;
- private static final int MEDIUM = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE;
- private static final int SMALL = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
- private static final int TINY = CompressedResponseWrapper.DEFAULT_MIN_COMPRESS_SIZE/ 2;
+ private static final HttpConfiguration defaultHttp = new HttpConfiguration();
+ private static final int LARGE = defaultHttp.getOutputBufferSize() * 8;
+ private static final int MEDIUM = defaultHttp.getOutputBufferSize();
+ private static final int SMALL = defaultHttp.getOutputBufferSize() / 4;
+ private static final int TINY = AsyncGzipFilter.DEFAULT_MIN_GZIP_SIZE / 2;
private String compressionType;
- public GzipFilterContentLengthTest(Class<? extends Servlet> testServlet, String compressionType)
+ public GzipFilterContentLengthTest(Class<? extends Filter> testFilter,Class<? extends Servlet> testServlet, String compressionType)
{
+ this.testFilter = testFilter;
this.testServlet = testServlet;
this.compressionType = compressionType;
}
@@ -104,11 +119,13 @@ public class GzipFilterContentLengthTest
@Rule
public TestingDir testingdir = new TestingDir();
+ private Class<? extends Filter> testFilter;
private Class<? extends Servlet> testServlet;
private void assertIsGzipCompressed(String filename, int filesize) throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
File testfile = tester.prepareServerFile(testServlet.getSimpleName() + "-" + filename,filesize);
@@ -129,6 +146,7 @@ public class GzipFilterContentLengthTest
private void assertIsNotGzipCompressed(String filename, int filesize) throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
File testfile = tester.prepareServerFile(testServlet.getSimpleName() + "-" + filename,filesize);
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java
index 0eee233bad..92acd9e5ed 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultNoRecompressTest.java
@@ -23,6 +23,8 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.List;
+import javax.servlet.Filter;
+
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlets.gzip.GzipTester;
@@ -49,56 +51,61 @@ public class GzipFilterDefaultNoRecompressTest
return Arrays.asList(new Object[][]
{
// Some already compressed files
- { "test_quotes.gz", "application/gzip", GzipFilter.GZIP },
- { "test_quotes.bz2", "application/bzip2", GzipFilter.GZIP },
- { "test_quotes.zip", "application/zip", GzipFilter.GZIP },
- { "test_quotes.rar", "application/octet-stream", GzipFilter.GZIP },
+ { GzipFilter.class, "test_quotes.gz", "application/gzip", GzipFilter.GZIP },
+ { GzipFilter.class, "test_quotes.bz2", "application/bzip2", GzipFilter.GZIP },
+ { GzipFilter.class, "test_quotes.zip", "application/zip", GzipFilter.GZIP },
+ { GzipFilter.class, "test_quotes.rar", "application/octet-stream", GzipFilter.GZIP },
// Some images (common first)
- { "jetty_logo.png", "image/png", GzipFilter.GZIP },
- { "jetty_logo.gif", "image/gif", GzipFilter.GZIP },
- { "jetty_logo.jpeg", "image/jpeg", GzipFilter.GZIP },
- { "jetty_logo.jpg", "image/jpeg", GzipFilter.GZIP },
+ { GzipFilter.class, "jetty_logo.png", "image/png", GzipFilter.GZIP },
+ { GzipFilter.class, "jetty_logo.gif", "image/gif", GzipFilter.GZIP },
+ { GzipFilter.class, "jetty_logo.jpeg", "image/jpeg", GzipFilter.GZIP },
+ { GzipFilter.class, "jetty_logo.jpg", "image/jpeg", GzipFilter.GZIP },
// Lesser encountered images (usually found being requested from non-browser clients)
- { "jetty_logo.bmp", "image/bmp", GzipFilter.GZIP },
- { "jetty_logo.tga", "application/tga", GzipFilter.GZIP },
- { "jetty_logo.tif", "image/tiff", GzipFilter.GZIP },
- { "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
- { "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
- { "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP },
+ { GzipFilter.class, "jetty_logo.bmp", "image/bmp", GzipFilter.GZIP },
+ { GzipFilter.class, "jetty_logo.tga", "application/tga", GzipFilter.GZIP },
+ { GzipFilter.class, "jetty_logo.tif", "image/tiff", GzipFilter.GZIP },
+ { GzipFilter.class, "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
+ { GzipFilter.class, "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
+ { GzipFilter.class, "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP },
//qvalue disables compression
- { "test_quotes.txt", "text/plain", GzipFilter.GZIP+";q=0"},
- { "test_quotes.txt", "text/plain", GzipFilter.GZIP+"; q = 0 "},
-
-
- // Same tests again for deflate
+ { GzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+";q=0"},
+ { GzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+"; q = 0 "},
+
+
// Some already compressed files
- { "test_quotes.gz", "application/gzip", GzipFilter.DEFLATE },
- { "test_quotes.bz2", "application/bzip2", GzipFilter.DEFLATE },
- { "test_quotes.zip", "application/zip", GzipFilter.DEFLATE },
- { "test_quotes.rar", "application/octet-stream", GzipFilter.DEFLATE },
+ { AsyncGzipFilter.class, "test_quotes.gz", "application/gzip", GzipFilter.GZIP },
+ { AsyncGzipFilter.class, "test_quotes.bz2", "application/bzip2", GzipFilter.GZIP },
+ { AsyncGzipFilter.class, "test_quotes.zip", "application/zip", GzipFilter.GZIP },
+ { AsyncGzipFilter.class, "test_quotes.rar", "application/octet-stream", GzipFilter.GZIP },
// Some images (common first)
- { "jetty_logo.png", "image/png", GzipFilter.DEFLATE },
- { "jetty_logo.gif", "image/gif", GzipFilter.DEFLATE },
- { "jetty_logo.jpeg", "image/jpeg", GzipFilter.DEFLATE },
- { "jetty_logo.jpg", "image/jpeg", GzipFilter.DEFLATE },
+ { AsyncGzipFilter.class, "jetty_logo.png", "image/png", GzipFilter.GZIP },
+ { AsyncGzipFilter.class, "jetty_logo.gif", "image/gif", GzipFilter.GZIP },
+ { AsyncGzipFilter.class, "jetty_logo.jpeg", "image/jpeg", GzipFilter.GZIP },
+ { AsyncGzipFilter.class, "jetty_logo.jpg", "image/jpeg", GzipFilter.GZIP },
// Lesser encountered images (usually found being requested from non-browser clients)
- { "jetty_logo.bmp", "image/bmp", GzipFilter.DEFLATE },
- { "jetty_logo.tga", "application/tga", GzipFilter.DEFLATE },
- { "jetty_logo.tif", "image/tiff", GzipFilter.DEFLATE },
- { "jetty_logo.tiff", "image/tiff", GzipFilter.DEFLATE },
- { "jetty_logo.xcf", "image/xcf", GzipFilter.DEFLATE },
- { "jetty_logo.jp2", "image/jpeg2000", GzipFilter.DEFLATE } });
+ { AsyncGzipFilter.class, "jetty_logo.bmp", "image/bmp", GzipFilter.GZIP },
+ { AsyncGzipFilter.class, "jetty_logo.tga", "application/tga", GzipFilter.GZIP },
+ { AsyncGzipFilter.class, "jetty_logo.tif", "image/tiff", GzipFilter.GZIP },
+ { AsyncGzipFilter.class, "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
+ { AsyncGzipFilter.class, "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
+ { AsyncGzipFilter.class, "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP },
+ //qvalue disables compression
+ { AsyncGzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+";q=0"},
+ { AsyncGzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+"; q = 0 "}
+ });
}
@Rule
public TestingDir testingdir = new TestingDir();
+ private Class<? extends Filter> testFilter;
private String alreadyCompressedFilename;
private String expectedContentType;
private String compressionType;
- public GzipFilterDefaultNoRecompressTest(String testFilename, String expectedContentType, String compressionType)
+ public GzipFilterDefaultNoRecompressTest(Class<? extends Filter> testFilter,String testFilename, String expectedContentType, String compressionType)
{
+ this.testFilter = testFilter;
this.alreadyCompressedFilename = testFilename;
this.expectedContentType = expectedContentType;
this.compressionType = compressionType;
@@ -108,6 +115,7 @@ public class GzipFilterDefaultNoRecompressTest
public void testNotGzipFiltered_Default_AlreadyCompressed() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
copyTestFileToServer(alreadyCompressedFilename);
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java
index cdb0564c5d..6620046918 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/GzipFilterDefaultTest.java
@@ -21,22 +21,22 @@ package org.eclipse.jetty.servlets;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
+import javax.servlet.Filter;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import junit.framework.Assert;
-
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
-import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
import org.eclipse.jetty.servlets.gzip.GzipTester;
import org.eclipse.jetty.toolchain.test.TestingDir;
+import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -50,20 +50,22 @@ import org.junit.runners.Parameterized.Parameters;
public class GzipFilterDefaultTest
{
@Parameters
- public static Collection<String[]> data()
+ public static List<Object[]> data()
{
- String[][] data = new String[][]
- {
- { GzipFilter.GZIP },
- { GzipFilter.DEFLATE } };
-
- return Arrays.asList(data);
+ return Arrays.asList(new Object[][]
+ {
+ { AsyncGzipFilter.class, GzipFilter.GZIP },
+ { GzipFilter.class, GzipFilter.GZIP },
+ { GzipFilter.class, GzipFilter.DEFLATE },
+ });
}
+ private Class<? extends Filter> testFilter;
private String compressionType;
- public GzipFilterDefaultTest(String compressionType)
+ public GzipFilterDefaultTest(Class<? extends Filter> testFilter, String compressionType)
{
+ this.testFilter=testFilter;
this.compressionType = compressionType;
}
@@ -105,7 +107,10 @@ public class GzipFilterDefaultTest
public static class HttpContentTypeWithEncoding extends HttpServlet
{
- public static final String COMPRESSED_CONTENT = "<html><head></head><body><h1>COMPRESSED</h1></body></html>";
+ public static final String COMPRESSED_CONTENT = "<html><head></head><body><h1>COMPRESSABLE CONTENT</h1>"+
+ "This content must be longer than the default min gzip length, which is 256 bytes. "+
+ "The moon is blue to a fish in love. How now brown cow. The quick brown fox jumped over the lazy dog. A woman needs a man like a fish needs a bicycle!"+
+ "</body></html>";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
@@ -128,9 +133,9 @@ public class GzipFilterDefaultTest
public void testIsGzipByMethod() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
- // Test content that is smaller than the buffer.
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 2;
+ int filesize = tester.getOutputBufferSize() * 2;
tester.prepareServerFile("file.txt",filesize);
FilterHolder holder = tester.setContentServlet(GetServlet.class);
@@ -177,8 +182,8 @@ public class GzipFilterDefaultTest
public void testIsGzipCompressedEmpty() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
- // Test content that is smaller than the buffer.
tester.prepareServerFile("empty.txt",0);
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
@@ -199,9 +204,9 @@ public class GzipFilterDefaultTest
public void testIsGzipCompressedTiny() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
- // Test content that is smaller than the buffer.
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
+ int filesize = tester.getOutputBufferSize() / 4;
tester.prepareServerFile("file.txt",filesize);
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
@@ -218,40 +223,16 @@ public class GzipFilterDefaultTest
tester.stop();
}
}
-
- @Test
- public void testIsGzipCompressedTinyWithQ() throws Exception
- {
- GzipTester tester = new GzipTester(testingdir, compressionType+";q=0.5");
-
- // Test content that is smaller than the buffer.
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
- tester.prepareServerFile("file.txt",filesize);
-
- FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
- holder.setInitParameter("mimeTypes","text/plain");
-
- try
- {
- tester.start();
- HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
- Assert.assertEquals("Accept-Encoding",http.get("Vary"));
- }
- finally
- {
- tester.stop();
- }
- }
@Test
- public void testIsGzipCompressedTinyWithBadQ() throws Exception
+ public void testIsGzipCompressedLarge() throws Exception
{
- GzipTester tester = new GzipTester(testingdir, compressionType+";q=");
+ GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
- // Test content that is smaller than the buffer.
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
+ int filesize = tester.getOutputBufferSize() * 4;
tester.prepareServerFile("file.txt",filesize);
-
+
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
holder.setInitParameter("mimeTypes","text/plain");
@@ -267,22 +248,23 @@ public class GzipFilterDefaultTest
}
}
+
@Test
- public void testIsGzipCompressedLarge() throws Exception
+ public void testGzipedIfModified() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
- // Test content that is smaller than the buffer.
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
+ int filesize = tester.getOutputBufferSize() * 4;
tester.prepareServerFile("file.txt",filesize);
-
+
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
holder.setInitParameter("mimeTypes","text/plain");
try
{
tester.start();
- HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
+ HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt",System.currentTimeMillis()-4000);
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
}
finally
@@ -293,22 +275,22 @@ public class GzipFilterDefaultTest
@Test
- public void testGzipedIfModified() throws Exception
+ public void testNotGzipedIfNotModified() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
- // Test content that is smaller than the buffer.
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
+ int filesize = tester.getOutputBufferSize() * 4;
tester.prepareServerFile("file.txt",filesize);
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
holder.setInitParameter("mimeTypes","text/plain");
+ holder.setInitParameter("etags","true");
try
{
tester.start();
- HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt",System.currentTimeMillis()-4000);
- Assert.assertEquals("Accept-Encoding",http.get("Vary"));
+ tester.assertIsResponseNotModified("GET","file.txt",System.currentTimeMillis()+4000);
}
finally
{
@@ -318,36 +300,36 @@ public class GzipFilterDefaultTest
@Test
- public void testNotGzipedIfNotModified() throws Exception
+ public void testIsNotGzipCompressedWithZeroQ() throws Exception
{
- GzipTester tester = new GzipTester(testingdir, compressionType);
-
- // Test content that is smaller than the buffer.
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
+ GzipTester tester = new GzipTester(testingdir, compressionType+"; q=0");
+ tester.setGzipFilterClass(testFilter);
+
+ int filesize = tester.getOutputBufferSize() / 4;
tester.prepareServerFile("file.txt",filesize);
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
holder.setInitParameter("mimeTypes","text/plain");
- holder.setInitParameter("etags","true");
try
{
tester.start();
- tester.assertIsResponseNotModified("GET","file.txt",System.currentTimeMillis()+4000);
+ HttpTester.Response http = tester.assertIsResponseNotGzipCompressed("GET","file.txt", filesize, HttpStatus.OK_200);
+ Assert.assertEquals("Accept-Encoding",http.get("Vary"));
}
finally
{
tester.stop();
}
}
-
@Test
- public void testIsNotGzipCompressedWithQ() throws Exception
+ public void testIsGzipCompressedWithQ() throws Exception
{
- GzipTester tester = new GzipTester(testingdir, compressionType+"; q = 0");
+ GzipTester tester = new GzipTester(testingdir, compressionType,"something;q=0.1,"+compressionType+";q=0.5");
+ tester.setGzipFilterClass(testFilter);
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
+ int filesize = tester.getOutputBufferSize() / 4;
tester.prepareServerFile("file.txt",filesize);
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
@@ -356,7 +338,7 @@ public class GzipFilterDefaultTest
try
{
tester.start();
- HttpTester.Response http = tester.assertIsResponseNotGzipCompressed("GET","file.txt", filesize, HttpStatus.OK_200);
+ HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
}
finally
@@ -369,8 +351,9 @@ public class GzipFilterDefaultTest
public void testIsNotGzipCompressedByContentType() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
+ int filesize = tester.getOutputBufferSize() * 4;
tester.prepareServerFile("file.mp3",filesize);
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
@@ -392,6 +375,7 @@ public class GzipFilterDefaultTest
public void testGzipCompressedByContentTypeWithEncoding() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
FilterHolder holder = tester.setContentServlet(HttpContentTypeWithEncoding.class);
holder.setInitParameter("mimeTypes","text/plain");
try
@@ -399,8 +383,6 @@ public class GzipFilterDefaultTest
tester.start();
HttpTester.Response http = tester.assertNonStaticContentIsResponseGzipCompressed("GET","xxx", HttpContentTypeWithEncoding.COMPRESSED_CONTENT);
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
- System.err.println(http.get("Content-Type"));
- System.err.println(http.get("Content-Encoding"));
}
finally
{
@@ -413,8 +395,9 @@ public class GzipFilterDefaultTest
public void testIsNotGzipCompressedByDeferredContentType() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
+ int filesize = tester.getOutputBufferSize() * 4;
tester.prepareServerFile("file.mp3.deferred",filesize);
FilterHolder holder = tester.setContentServlet(GetServlet.class);
@@ -436,6 +419,7 @@ public class GzipFilterDefaultTest
public void testIsNotGzipCompressedHttpStatus() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
// Test error code 204
FilterHolder holder = tester.setContentServlet(HttpStatusServlet.class);
@@ -457,6 +441,7 @@ public class GzipFilterDefaultTest
public void testIsNotGzipCompressedHttpBadRequestStatus() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
+ tester.setGzipFilterClass(testFilter);
// Test error code 400
FilterHolder holder = tester.setContentServlet(HttpErrorServlet.class);
@@ -478,12 +463,13 @@ public class GzipFilterDefaultTest
public void testUserAgentExclusion() throws Exception
{
GzipTester tester = new GzipTester(testingdir,compressionType);
+ tester.setGzipFilterClass(testFilter);
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
holder.setInitParameter("excludedAgents","bar, foo");
tester.setUserAgent("foo");
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
+ int filesize = tester.getOutputBufferSize() * 4;
tester.prepareServerFile("file.txt",filesize);
try
@@ -501,13 +487,14 @@ public class GzipFilterDefaultTest
public void testUserAgentExclusionByExcludedAgentPatterns() throws Exception
{
GzipTester tester = new GzipTester(testingdir,compressionType);
+ tester.setGzipFilterClass(testFilter);
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
holder.setInitParameter("excludedAgents","bar");
holder.setInitParameter("excludeAgentPatterns","fo.*");
tester.setUserAgent("foo");
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
+ int filesize = tester.getOutputBufferSize() * 4;
tester.prepareServerFile("file.txt",filesize);
try
@@ -525,11 +512,12 @@ public class GzipFilterDefaultTest
public void testExcludePaths() throws Exception
{
GzipTester tester = new GzipTester(testingdir,compressionType);
+ tester.setGzipFilterClass(testFilter);
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
holder.setInitParameter("excludePaths","/bar/, /context/");
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
+ int filesize = tester.getOutputBufferSize() * 4;
tester.prepareServerFile("file.txt",filesize);
try
@@ -547,11 +535,12 @@ public class GzipFilterDefaultTest
public void testExcludePathPatterns() throws Exception
{
GzipTester tester = new GzipTester(testingdir,compressionType);
+ tester.setGzipFilterClass(testFilter);
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
holder.setInitParameter("excludePathPatterns","/cont.*");
- int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
+ int filesize = tester.getOutputBufferSize() * 4;
tester.prepareServerFile("file.txt",filesize);
try
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
index b4ecdc1af8..8181e81058 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/GzipTester.java
@@ -44,12 +44,14 @@ import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import javax.servlet.DispatcherType;
+import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.DateGenerator;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpTester;
+import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletTester;
@@ -62,21 +64,33 @@ import org.junit.Assert;
public class GzipTester
{
- private Class<? extends GzipFilter> gzipFilterClass = GzipFilter.class;
+ private Class<? extends Filter> gzipFilterClass = GzipFilter.class;
private String encoding = "ISO8859_1";
private String userAgent = null;
- private ServletTester tester;
+ private final ServletTester tester = new ServletTester();;
private TestingDir testdir;
+ private String accept;
private String compressionType;
+ public GzipTester(TestingDir testingdir, String compressionType, String accept)
+ {
+ this.testdir = testingdir;
+ this.compressionType = compressionType;
+ this.accept=accept;
+ }
+
public GzipTester(TestingDir testingdir, String compressionType)
{
this.testdir = testingdir;
this.compressionType = compressionType;
- // Make sure we start with a clean testing directory.
- // DOES NOT WORK IN WINDOWS - this.testdir.ensureEmpty();
+ this.accept=compressionType;
}
+ public int getOutputBufferSize()
+ {
+ return tester.getConnector().getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().getOutputBufferSize();
+ }
+
public HttpTester.Response assertIsResponseGzipCompressed(String method, String filename) throws Exception
{
return assertIsResponseGzipCompressed(method,filename,filename,-1);
@@ -101,7 +115,7 @@ public class GzipTester
request.setMethod(method);
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
- request.setHeader("Accept-Encoding",compressionType);
+ request.setHeader("Accept-Encoding",accept);
if (this.userAgent != null)
request.setHeader("User-Agent", this.userAgent);
@@ -569,7 +583,6 @@ public class GzipTester
*/
public FilterHolder setContentServlet(Class<? extends Servlet> servletClass) throws IOException
{
- tester = new ServletTester();
tester.setContextPath("/context");
tester.setResourceBase(testdir.getDir().getCanonicalPath());
ServletHolder servletHolder = tester.addServlet(servletClass,"/");
@@ -580,12 +593,12 @@ public class GzipTester
return holder;
}
- public Class<? extends GzipFilter> getGzipFilterClass()
+ public Class<? extends Filter> getGzipFilterClass()
{
return gzipFilterClass;
}
- public void setGzipFilterClass(Class<? extends GzipFilter> gzipFilterClass)
+ public void setGzipFilterClass(Class<? extends Filter> gzipFilterClass)
{
this.gzipFilterClass = gzipFilterClass;
}
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletBufferTypeLengthWrite.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletBufferTypeLengthWrite.java
new file mode 100644
index 0000000000..0dff53c1b2
--- /dev/null
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/gzip/TestServletBufferTypeLengthWrite.java
@@ -0,0 +1,68 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.servlets.gzip;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.HttpOutput;
+import org.eclipse.jetty.servlets.GzipFilter;
+
+/**
+ * A sample servlet to serve static content, using a order of construction that has caused problems for
+ * {@link GzipFilter} in the past.
+ *
+ * Using a real-world pattern of:
+ *
+ * <pre>
+ * 1) get stream
+ * 2) set content type
+ * 2) set content length
+ * 4) write
+ * </pre>
+ *
+ * @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
+ */
+@SuppressWarnings("serial")
+public class TestServletBufferTypeLengthWrite extends TestDirContentServlet
+{
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ String fileName = request.getServletPath();
+ byte[] dataBytes = loadContentFileBytes(fileName);
+
+ ServletOutputStream out = response.getOutputStream();
+
+ if (fileName.endsWith("txt"))
+ response.setContentType("text/plain");
+ else if (fileName.endsWith("mp3"))
+ response.setContentType("audio/mpeg");
+ response.setHeader("ETag","W/etag-"+fileName);
+
+ response.setContentLength(dataBytes.length);
+
+ ((HttpOutput)out).write(ByteBuffer.wrap(dataBytes).asReadOnlyBuffer());
+ }
+}
diff --git a/jetty-servlets/src/test/resources/jetty-logging.properties b/jetty-servlets/src/test/resources/jetty-logging.properties
index 9ef3d34faf..f97be99cc8 100644
--- a/jetty-servlets/src/test/resources/jetty-logging.properties
+++ b/jetty-servlets/src/test/resources/jetty-logging.properties
@@ -1,3 +1,4 @@
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.servlets.LEVEL=DEBUG
+#org.eclipse.jetty.servlets.GzipFilter.LEVEL=DEBUG

Back to the top