447515 Remove GzipFilter

Made GzipHandler the same as AsyncGzipFilter
Added a HttpOutput.Interceptor
This commit is contained in:
Greg Wilkins 2014-10-16 20:47:15 +11:00
parent 4120405b8e
commit aad1f9d058
19 changed files with 659 additions and 2479 deletions

View File

@ -61,7 +61,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
* HttpTransport.completed().
*
*/
public class HttpChannel implements Runnable
public class HttpChannel implements Runnable, HttpOutput.Interceptor
{
private static final Logger LOG = Log.getLogger(HttpChannel.class);
private static final ThreadLocal<HttpChannel> __currentChannel = new ThreadLocal<>();
@ -593,12 +593,13 @@ public class HttpChannel implements Runnable
/**
* <p>Non-Blocking write, committing the response if needed.</p>
*
* Called as last link in HttpOutput.Filter chain
* @param content the content buffer to write
* @param complete whether the content is complete for the response
* @param callback Callback when complete or failed
*/
protected void write(ByteBuffer content, boolean complete, Callback callback)
@Override
public void write(ByteBuffer content, boolean complete, Callback callback)
{
sendResponse(null,content,complete,callback);
}

View File

@ -54,10 +54,16 @@ import org.eclipse.jetty.util.log.Logger;
*/
public class HttpOutput extends ServletOutputStream implements Runnable
{
public interface Interceptor
{
void write(ByteBuffer content, boolean complete, Callback callback);
}
private static Logger LOG = Log.getLogger(HttpOutput.class);
private final HttpChannel _channel;
private final SharedBlockingCallback _writeBlock;
private Interceptor _filter;
private long _written;
private ByteBuffer _aggregate;
private int _bufferSize;
@ -80,6 +86,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
public HttpOutput(HttpChannel channel)
{
_channel = channel;
_filter = channel;
_writeBlock = new SharedBlockingCallback()
{
@Override
@ -96,6 +103,16 @@ public class HttpOutput extends ServletOutputStream implements Runnable
{
return _channel;
}
public Interceptor getFilter()
{
return _filter;
}
public void setFilter(Interceptor filter)
{
_filter=filter;
}
public boolean isWritten()
{
@ -107,12 +124,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
return _written;
}
public void reset()
{
_written = 0;
reopen();
}
public void reopen()
{
_state.set(OutputState.OPEN);
@ -146,7 +157,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
protected void write(ByteBuffer content, boolean complete, Callback callback)
{
_channel.write(content, complete, callback);
_filter.write(content, complete, callback);
}
private void abort(Throwable failure)
@ -776,10 +787,18 @@ public class HttpOutput extends ServletOutputStream implements Runnable
_commitSize = size;
}
public void recycle()
{
resetBuffer();
_filter=_channel;
}
public void resetBuffer()
{
_written = 0;
if (BufferUtil.hasContent(_aggregate))
BufferUtil.clear(_aggregate);
reopen();
}
@Override

View File

@ -111,7 +111,7 @@ public class Response implements HttpServletResponse
private final HttpChannel _channel;
private final HttpFields _fields = new HttpFields();
private final AtomicInteger _include = new AtomicInteger();
private HttpOutput _out;
private final HttpOutput _out;
private int _status = HttpStatus.OK_200;
private String _reason;
private Locale _locale;
@ -145,7 +145,7 @@ public class Response implements HttpServletResponse
_contentType = null;
_outputType = OutputType.NONE;
_contentLength = -1;
_out.reset();
_out.recycle();
_fields.clear();
_explicitEncoding=false;
}
@ -154,11 +154,6 @@ public class Response implements HttpServletResponse
{
return _out;
}
public void setHttpOutput(HttpOutput out)
{
_out=out;
}
public boolean isIncluding()
{
@ -1241,15 +1236,6 @@ public class Response implements HttpServletResponse
if (isCommitted())
throw new IllegalStateException("Committed");
switch (_outputType)
{
case STREAM:
case WRITER:
_out.reset();
break;
default:
}
_out.resetBuffer();
}

View File

@ -18,523 +18,7 @@
package org.eclipse.jetty.servlets;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
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.PreEncodedHttpField;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
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
@Deprecated
public class AsyncGzipFilter extends GzipFilter
{
private static final Logger LOG = Log.getLogger(AsyncGzipFilter.class);
public final static String GZIP = "gzip";
public static final String DEFLATE = "deflate";
public final static String ETAG_GZIP="--gzip";
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=32*1024;
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 PreEncodedHttpField(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(Locale.ENGLISH));
}
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.equals("image/svg+xml")) //always compressable (unless .svgz file)
continue;
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 PreEncodedHttpField(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
{
LOG.debug("{} doFilter {}",this,req);
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 && _excludeMimeTypes)
{
String mimeType = _context.getMimeType(request.getRequestURI());
if (mimeType!=null)
{
mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
if (_mimeTypes.contains(mimeType))
{
LOG.debug("{} excluded by path suffix {}",this,request);
// handle normally without setting vary header
super.doFilter(request,response,chain);
return;
}
}
}
//If the Content-Encoding is already set, then we won't compress
if (response.getHeader("Content-Encoding") != null)
{
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)
{
if (etag.contains(ETAG_GZIP))
request.setAttribute(ETAG,etag.replace(ETAG_GZIP,""));
}
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;
try
{
cout.mightCompress(this);
super.doFilter(request,response,chain);
}
catch(Throwable e)
{
LOG.debug("{} excepted {}",this,request,e);
if (!response.isCommitted())
{
cout.resetBuffer();
cout.noCompressionIfPossible();
}
throw e;
}
}
/**
* 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;
}
// If not HTTP/2, then we must check the accept encoding header
if (request.getHttpVersion()!=HttpVersion.HTTP_2)
{
HttpField accept = request.getHttpFields().getField(HttpHeader.ACCEPT_ENCODING);
if (accept==null)
{
LOG.debug("{} excluded !accept {}",this,request);
return null;
}
boolean gzip = accept.contains("gzip");
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)
{
deflater.reset();
if (_deflater.get()==null)
_deflater.set(deflater);
}
@Override
public boolean isExcludedMimeType(String mimetype)
{
return _mimeTypes.contains(mimetype) == _excludeMimeTypes;
}
@Override
public int getBufferSize()
{
return _bufferSize;
}
}

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.servlets;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
@ -28,8 +27,6 @@ import java.util.StringTokenizer;
import java.util.regex.Pattern;
import java.util.zip.Deflater;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
@ -39,19 +36,26 @@ import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.servlets.gzip.AbstractCompressedStream;
import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
import org.eclipse.jetty.servlets.gzip.DeflatedOutputStream;
import org.eclipse.jetty.servlets.gzip.GzipOutputStream;
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.GzipHttpOutputInterceptor;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/** GZIP Filter
* This filter will gzip or deflate the content of a response if: <ul>
/** 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
@ -62,9 +66,6 @@ import org.eclipse.jetty.util.log.Logger;
* </ul>
*
* <p>
* If both gzip and deflate are specified in the accept-encoding header, then gzip will be used.
* </p>
* <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
@ -87,9 +88,6 @@ import org.eclipse.jetty.util.log.Logger;
* <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.
@ -121,25 +119,22 @@ import org.eclipse.jetty.util.log.Logger;
* the normal processing is done so that the default servlet can send the pre existing gz content.
* </dd>
* </dl>
* @deprecated Use AsyncGzipFilter
*/
@Deprecated
public class GzipFilter extends UserAgentFilter
public class GzipFilter extends UserAgentFilter implements GzipFactory
{
private static final Logger LOG = Log.getLogger(GzipFilter.class);
public final static String GZIP="gzip";
public final static String ETAG_GZIP="--gzip\"";
public final static String DEFLATE="deflate";
public final static String ETAG_DEFLATE="--deflate\"";
public final static String ETAG="o.e.j.s.GzipFilter.ETag";
public final static String GZIP = "gzip";
public static final String DEFLATE = "deflate";
public final static String ETAG_GZIP="--gzip";
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=256;
protected int _bufferSize=32*1024;
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
@ -152,13 +147,7 @@ public class GzipFilter extends UserAgentFilter
protected Set<Pattern> _excludedAgentPatterns;
protected Set<String> _excludedPaths;
protected Set<Pattern> _excludedPathPatterns;
protected String _vary="Accept-Encoding, User-Agent";
private static final int STATE_SEPARATOR = 0;
private static final int STATE_Q = 1;
private static final int STATE_QVALUE = 2;
private static final int STATE_DEFAULT = 3;
protected HttpField _vary=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT);
/* ------------------------------------------------------------ */
/**
@ -174,22 +163,22 @@ public class GzipFilter extends UserAgentFilter
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);
tmp=filterConfig.getInitParameter("deflateNoWrap");
if (tmp!=null)
_deflateNoWrap=Boolean.parseBoolean(tmp);
LOG.debug("{} deflateCompressionLevel={}",this,_deflateCompressionLevel);
tmp=filterConfig.getInitParameter("checkGzExists");
if (tmp!=null)
_checkGzExists=Boolean.parseBoolean(tmp);
LOG.debug("{} checkGzExists={}",this,_checkGzExists);
tmp=filterConfig.getInitParameter("methods");
if (tmp!=null)
@ -200,6 +189,7 @@ public class GzipFilter extends UserAgentFilter
}
else
_methods.add(HttpMethod.GET.asString());
LOG.debug("{} methods={}",this,_methods);
tmp=filterConfig.getInitParameter("mimeTypes");
if (tmp==null)
@ -234,6 +224,8 @@ public class GzipFilter extends UserAgentFilter
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)
{
@ -242,6 +234,7 @@ public class GzipFilter extends UserAgentFilter
while (tok.hasMoreTokens())
_excludedAgents.add(tok.nextToken().trim());
}
LOG.debug("{} excludedAgents={}",this,_excludedAgents);
tmp=filterConfig.getInitParameter("excludeAgentPatterns");
if (tmp!=null)
@ -251,6 +244,7 @@ public class GzipFilter extends UserAgentFilter
while (tok.hasMoreTokens())
_excludedAgentPatterns.add(Pattern.compile(tok.nextToken().trim()));
}
LOG.debug("{} excludedAgentPatterns={}",this,_excludedAgentPatterns);
tmp=filterConfig.getInitParameter("excludePaths");
if (tmp!=null)
@ -260,6 +254,7 @@ public class GzipFilter extends UserAgentFilter
while (tok.hasMoreTokens())
_excludedPaths.add(tok.nextToken().trim());
}
LOG.debug("{} excludedPaths={}",this,_excludedPaths);
tmp=filterConfig.getInitParameter("excludePathPatterns");
if (tmp!=null)
@ -269,10 +264,12 @@ public class GzipFilter extends UserAgentFilter
while (tok.hasMoreTokens())
_excludedPathPatterns.add(Pattern.compile(tok.nextToken().trim()));
}
LOG.debug("{} excludedPathPatterns={}",this,_excludedPathPatterns);
tmp=filterConfig.getInitParameter("vary");
if (tmp!=null)
_vary=tmp;
_vary=new PreEncodedHttpField(HttpHeader.VARY,tmp);
LOG.debug("{} vary={}",this,_vary);
}
/* ------------------------------------------------------------ */
@ -292,13 +289,22 @@ public class GzipFilter extends UserAgentFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException
{
LOG.debug("{} doFilter {}",this,req);
HttpServletRequest request=(HttpServletRequest)req;
HttpServletResponse response=(HttpServletResponse)res;
// If not a supported method or it is an Excluded URI - no Vary because no matter what client, this URI is always excluded
// 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()) || isExcludedPath(requestURI))
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;
}
@ -313,20 +319,21 @@ public class GzipFilter extends UserAgentFilter
mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
if (_mimeTypes.contains(mimeType))
{
LOG.debug("{} excluded by path suffix {}",this,request);
// handle normally without setting vary header
super.doFilter(request,response,chain);
return;
}
}
}
//If the Content-Encoding is already set, then we won't compress
if (response.getHeader("Content-Encoding") != null)
{
super.doFilter(request,response,chain);
return;
}
if (_checkGzExists && request.getServletContext()!=null)
{
String path=request.getServletContext().getRealPath(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()));
@ -335,6 +342,7 @@ public class GzipFilter extends UserAgentFilter
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;
@ -342,249 +350,23 @@ public class GzipFilter extends UserAgentFilter
}
}
// Excluded User-Agents
String ua = getUserAgent(request);
boolean ua_excluded=ua!=null&&isExcludedAgent(ua);
// Acceptable compression type
String compressionType = ua_excluded?null:selectCompression(request.getHeader("accept-encoding"));
// 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("\"")?"\"":""));
if (etag.contains(ETAG_GZIP))
request.setAttribute(ETAG,etag.replace(ETAG_GZIP,""));
}
CompressedResponseWrapper wrappedResponse = createWrappedResponse(request,response,compressionType);
HttpChannel channel = HttpChannel.getCurrentHttpChannel();
HttpOutput out = channel.getResponse().getHttpOutput();
// TODO recycle the GzipHttpOutputFilter
out.setFilter(new GzipHttpOutputInterceptor(this,channel,out.getFilter()));
boolean exceptional=true;
try
{
super.doFilter(request,wrappedResponse,chain);
exceptional=false;
}
finally
{
if (request.isAsyncStarted())
{
request.getAsyncContext().addListener(new FinishOnCompleteListener(wrappedResponse));
}
else if (exceptional && !response.isCommitted())
{
wrappedResponse.resetBuffer();
wrappedResponse.noCompression();
}
else
wrappedResponse.finish();
}
}
/* ------------------------------------------------------------ */
private String selectCompression(String encodingHeader)
{
// TODO, this could be a little more robust.
// prefer gzip over deflate
String compression = null;
if (encodingHeader!=null)
{
String[] encodings = getEncodings(encodingHeader);
if (encodings != null)
{
for (int i=0; i< encodings.length; i++)
{
if (encodings[i].toLowerCase(Locale.ENGLISH).contains(GZIP))
{
if (isEncodingAcceptable(encodings[i]))
{
compression = GZIP;
break; //prefer Gzip over deflate
}
}
if (encodings[i].toLowerCase(Locale.ENGLISH).contains(DEFLATE))
{
if (isEncodingAcceptable(encodings[i]))
{
compression = DEFLATE; //Keep checking in case gzip is acceptable
}
}
}
}
}
return compression;
}
private String[] getEncodings (String encodingHeader)
{
if (encodingHeader == null)
return null;
return encodingHeader.split(",");
}
private boolean isEncodingAcceptable(String encoding)
{
int state = STATE_DEFAULT;
int qvalueIdx = -1;
for (int i=0;i<encoding.length();i++)
{
char c = encoding.charAt(i);
switch (state)
{
case STATE_DEFAULT:
{
if (';' == c)
state = STATE_SEPARATOR;
break;
}
case STATE_SEPARATOR:
{
if ('q' == c || 'Q' == c)
state = STATE_Q;
break;
}
case STATE_Q:
{
if ('=' == c)
state = STATE_QVALUE;
break;
}
case STATE_QVALUE:
{
if (qvalueIdx < 0 && '0' == c || '1' == c)
qvalueIdx = i;
break;
}
}
}
super.doFilter(request,response,chain);
if (qvalueIdx < 0)
return true;
if ("0".equals(encoding.substring(qvalueIdx).trim()))
return false;
return true;
}
protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType)
{
CompressedResponseWrapper wrappedResponse = null;
wrappedResponse = new CompressedResponseWrapper(request,response)
{
@Override
protected AbstractCompressedStream newCompressedStream(HttpServletRequest request, HttpServletResponse response) throws IOException
{
return new AbstractCompressedStream(compressionType,request,this,_vary)
{
private Deflater _allocatedDeflater;
private byte[] _allocatedBuffer;
@Override
protected OutputStream createStream() throws IOException
{
if (compressionType == null)
{
return null;
}
// acquire deflater instance
_allocatedDeflater = _deflater.get();
if (_allocatedDeflater==null)
_allocatedDeflater = new Deflater(_deflateCompressionLevel,_deflateNoWrap);
else
{
_deflater.remove();
_allocatedDeflater.reset();
}
// acquire buffer
_allocatedBuffer = _buffer.get();
if (_allocatedBuffer==null)
_allocatedBuffer = new byte[_bufferSize];
else
{
_buffer.remove();
}
switch (compressionType)
{
case GZIP:
return new GzipOutputStream(_response.getOutputStream(),_allocatedDeflater,_allocatedBuffer);
case DEFLATE:
return new DeflatedOutputStream(_response.getOutputStream(),_allocatedDeflater,_allocatedBuffer);
}
throw new IllegalStateException(compressionType + " not supported");
}
@Override
public void finish() throws IOException
{
super.finish();
if (_allocatedDeflater != null && _deflater.get() == null)
{
_deflater.set(_allocatedDeflater);
}
if (_allocatedBuffer != null && _buffer.get() == null)
{
_buffer.set(_allocatedBuffer);
}
}
};
}
};
configureWrappedResponse(wrappedResponse);
return wrappedResponse;
}
protected void configureWrappedResponse(CompressedResponseWrapper wrappedResponse)
{
wrappedResponse.setMimeTypes(_mimeTypes,_excludeMimeTypes);
wrappedResponse.setBufferSize(_bufferSize);
wrappedResponse.setMinCompressSize(_minGzipSize);
}
private class FinishOnCompleteListener implements AsyncListener
{
private CompressedResponseWrapper wrappedResponse;
public FinishOnCompleteListener(CompressedResponseWrapper wrappedResponse)
{
this.wrappedResponse = wrappedResponse;
}
@Override
public void onComplete(AsyncEvent event) throws IOException
{
try
{
wrappedResponse.finish();
}
catch (IOException e)
{
LOG.warn(e);
}
}
@Override
public void onTimeout(AsyncEvent event) throws IOException
{
}
@Override
public void onError(AsyncEvent event) throws IOException
{
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
}
}
/**
* Checks to see if the userAgent is excluded
@ -652,4 +434,77 @@ public class GzipFilter extends UserAgentFilter
}
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;
}
// If not HTTP/2, then we must check the accept encoding header
if (request.getHttpVersion()!=HttpVersion.HTTP_2)
{
HttpField accept = request.getHttpFields().getField(HttpHeader.ACCEPT_ENCODING);
if (accept==null)
{
LOG.debug("{} excluded !accept {}",this,request);
return null;
}
boolean gzip = accept.contains("gzip");
if (!gzip)
{
LOG.debug("{} excluded not gzip accept {}",this,request);
return null;
}
}
Deflater df = _deflater.get();
if (df==null)
df=new Deflater(_deflateCompressionLevel,true);
else
_deflater.set(null);
return df;
}
@Override
public void recycle(Deflater deflater)
{
deflater.reset();
if (_deflater.get()==null)
_deflater.set(deflater);
}
@Override
public boolean isExcludedMimeType(String mimetype)
{
return _mimeTypes.contains(mimetype) == _excludeMimeTypes;
}
@Override
public int getBufferSize()
{
return _bufferSize;
}
}

View File

@ -18,161 +18,7 @@
package org.eclipse.jetty.servlets;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPOutputStream;
import javax.servlet.DispatcherType;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.io.UncheckedPrintWriter;
import org.eclipse.jetty.servlets.gzip.AbstractCompressedStream;
import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
/* ------------------------------------------------------------ */
/** Includable GZip Filter.
* This extension to the {@link GzipFilter} that uses Jetty features to allow
* headers to be set during calls to
* {@link javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}.
* This allows the gzip filter to function correct during includes and to make a decision to gzip or not
* at the time the buffer fills and on the basis of all response headers.
*
* If the init parameter "uncheckedPrintWriter" is set to "true", then the PrintWriter used by
* the wrapped getWriter will be {@link UncheckedPrintWriter}.
*
*/
@Deprecated
public class IncludableGzipFilter extends GzipFilter
{
boolean _uncheckedPrintWriter=false;
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
super.init(filterConfig);
String tmp=filterConfig.getInitParameter("uncheckedPrintWriter");
if (tmp!=null)
_uncheckedPrintWriter=Boolean.valueOf(tmp).booleanValue();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.GzipFilter#createWrappedResponse(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String)
*/
@Override
protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType)
{
CompressedResponseWrapper wrappedResponse = null;
if (compressionType==null)
{
wrappedResponse = new IncludableResponseWrapper(request,response)
{
@Override
protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
{
return new AbstractCompressedStream(null,request,this,_vary)
{
@Override
protected DeflaterOutputStream createStream() throws IOException
{
return null;
}
};
}
};
}
else if (compressionType.equals(GZIP))
{
wrappedResponse = new IncludableResponseWrapper(request,response)
{
@Override
protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
{
return new AbstractCompressedStream(compressionType,request,this,_vary)
{
@Override
protected DeflaterOutputStream createStream() throws IOException
{
return new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
}
};
}
};
}
else if (compressionType.equals(DEFLATE))
{
wrappedResponse = new IncludableResponseWrapper(request,response)
{
@Override
protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
{
return new AbstractCompressedStream(compressionType,request,this,_vary)
{
@Override
protected DeflaterOutputStream createStream() throws IOException
{
return new DeflaterOutputStream(_response.getOutputStream(),new Deflater(_deflateCompressionLevel, _deflateNoWrap));
}
};
}
};
}
else
{
throw new IllegalStateException(compressionType + " not supported");
}
configureWrappedResponse(wrappedResponse);
return wrappedResponse;
}
// Extend CompressedResponseWrapper to be able to set headers during include and to create unchecked printwriters
private abstract class IncludableResponseWrapper extends CompressedResponseWrapper
{
public IncludableResponseWrapper(HttpServletRequest request, HttpServletResponse response)
{
super(request,response);
}
@Override
public void setHeader(String name,String value)
{
if (getRequest().getDispatcherType()==DispatcherType.INCLUDE)
{
if (!"etag".equalsIgnoreCase(name) && !name.startsWith("content-"))
{
HttpServletResponse response = (HttpServletResponse)getResponse();
response.setHeader("org.eclipse.jetty.server.include."+name,value);
}
}
else
super.setHeader(name,value);
}
@Override
public void addHeader(String name, String value)
{
super.addHeader(name, value);
HttpServletResponse response = (HttpServletResponse)getResponse();
if (!response.containsHeader(name))
setHeader(name,value);
}
@Override
protected PrintWriter newWriter(OutputStream out, String encoding) throws UnsupportedEncodingException
{
if (_uncheckedPrintWriter)
return encoding == null?new UncheckedPrintWriter(out):new UncheckedPrintWriter(new OutputStreamWriter(out,encoding));
return super.newWriter(out,encoding);
}
}
}

View File

@ -1,395 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.util.ByteArrayOutputStream2;
/* ------------------------------------------------------------ */
/**
* Skeletal implementation of a CompressedStream. This class adds compression features to a ServletOutputStream and takes care of setting response headers, etc.
* Major work and configuration is done here. Subclasses using different kinds of compression only have to implement the abstract methods doCompress() and
* setContentEncoding() using the desired compression and setting the appropriate Content-Encoding header string.
*/
public abstract class AbstractCompressedStream extends ServletOutputStream
{
private final String _encoding;
protected final String _vary;
protected final CompressedResponseWrapper _wrapper;
protected final HttpServletResponse _response;
protected OutputStream _out;
protected ByteArrayOutputStream2 _bOut;
protected OutputStream _compressedOutputStream;
protected boolean _closed;
protected boolean _doNotCompress;
/**
* Instantiates a new compressed stream.
*
*/
public AbstractCompressedStream(String encoding,HttpServletRequest request, CompressedResponseWrapper wrapper,String vary)
throws IOException
{
_encoding=encoding;
_wrapper = wrapper;
_response = (HttpServletResponse)wrapper.getResponse();
_vary=vary;
if (_wrapper.getMinCompressSize()==0)
doCompress();
}
/* ------------------------------------------------------------ */
/**
* Reset buffer.
*/
public void resetBuffer()
{
if (_response.isCommitted() || _compressedOutputStream!=null )
throw new IllegalStateException("Committed");
_closed = false;
_out = null;
_bOut = null;
_doNotCompress = false;
}
/* ------------------------------------------------------------ */
public void setBufferSize(int bufferSize)
{
if (_bOut!=null && _bOut.getBuf().length<bufferSize)
{
ByteArrayOutputStream2 b = new ByteArrayOutputStream2(bufferSize);
b.write(_bOut.getBuf(),0,_bOut.size());
_bOut=b;
}
}
/* ------------------------------------------------------------ */
public void setContentLength()
{
if (_doNotCompress)
{
long length=_wrapper.getContentLength();
if (length>=0)
{
if (length < Integer.MAX_VALUE)
_response.setContentLength((int)length);
else
_response.setHeader("Content-Length",Long.toString(length));
}
}
}
/* ------------------------------------------------------------ */
/**
* @see java.io.OutputStream#flush()
*/
@Override
public void flush() throws IOException
{
if (_out == null || _bOut != null)
{
long length=_wrapper.getContentLength();
if (length > 0 && length < _wrapper.getMinCompressSize())
doNotCompress(false);
else
doCompress();
}
_out.flush();
}
/* ------------------------------------------------------------ */
/**
* @see java.io.OutputStream#close()
*/
@Override
public void close() throws IOException
{
if (_closed)
return;
if (_wrapper.getRequest().getAttribute("javax.servlet.include.request_uri") != null)
flush();
else
{
if (_bOut != null)
{
long length=_wrapper.getContentLength();
if (length < 0)
{
length = _bOut.getCount();
_wrapper.setContentLength(length);
}
if (length < _wrapper.getMinCompressSize())
doNotCompress(false);
else
doCompress();
}
else if (_out == null)
{
// No output
doNotCompress(false);
}
if (_compressedOutputStream != null)
_compressedOutputStream.close();
else
_out.close();
_closed = true;
}
}
/**
* Finish.
*
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public void finish() throws IOException
{
if (!_closed)
{
if (_out == null || _bOut != null)
{
long length=_wrapper.getContentLength();
if (length<0 &&_bOut==null || length >= 0 && length < _wrapper.getMinCompressSize())
doNotCompress(false);
else
doCompress();
}
if (_compressedOutputStream != null && !_closed)
{
_closed = true;
_compressedOutputStream.close();
}
}
}
/* ------------------------------------------------------------ */
/**
* @see java.io.OutputStream#write(int)
*/
@Override
public void write(int b) throws IOException
{
checkOut(1);
_out.write(b);
}
/* ------------------------------------------------------------ */
/**
* @see java.io.OutputStream#write(byte[])
*/
@Override
public void write(byte b[]) throws IOException
{
checkOut(b.length);
_out.write(b);
}
/* ------------------------------------------------------------ */
/**
* @see java.io.OutputStream#write(byte[], int, int)
*/
@Override
public void write(byte b[], int off, int len) throws IOException
{
checkOut(len);
_out.write(b,off,len);
}
/**
* Do compress.
*
* @throws IOException Signals that an I/O exception has occurred.
*/
public void doCompress() throws IOException
{
if (_compressedOutputStream==null)
{
if (_response.isCommitted())
throw new IllegalStateException();
if (_encoding!=null)
{
setHeader("Content-Encoding", _encoding);
if (_response.containsHeader("Content-Encoding"))
{
addHeader("Vary",_vary);
_out=_compressedOutputStream=createStream();
if (_out!=null)
{
if (_bOut!=null)
{
_out.write(_bOut.getBuf(),0,_bOut.getCount());
_bOut=null;
}
String etag=_wrapper.getETag();
if (etag!=null)
setHeader("ETag",etag.substring(0,etag.length()-1)+"--"+_encoding+'"');
return;
}
}
}
doNotCompress(true); // Send vary as it could have been compressed if encoding was present
}
}
/**
* Do not compress.
*
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public void doNotCompress(boolean sendVary) throws IOException
{
if (_compressedOutputStream != null)
throw new IllegalStateException("Compressed output stream is already assigned.");
if (_out == null || _bOut != null)
{
if (sendVary)
addHeader("Vary",_vary);
if (_wrapper.getETag()!=null)
setHeader("ETag",_wrapper.getETag());
_doNotCompress = true;
_out = _response.getOutputStream();
setContentLength();
if (_bOut != null)
_out.write(_bOut.getBuf(),0,_bOut.getCount());
_bOut = null;
}
}
/**
* Check out.
*
* @param lengthToWrite
* the length
* @throws IOException
* Signals that an I/O exception has occurred.
*/
private void checkOut(int lengthToWrite) throws IOException
{
if (_closed)
throw new IOException("CLOSED");
if (_out == null)
{
// If this first write is larger than buffer size, then we are committing now
if (lengthToWrite>_wrapper.getBufferSize())
{
// if we know this is all the content and it is less than minimum, then do not compress, otherwise do compress
long length=_wrapper.getContentLength();
if (length>=0 && length<_wrapper.getMinCompressSize())
doNotCompress(false); // Not compressing by size, so no vary on request headers
else
doCompress();
}
else
{
// start aggregating writes into a buffered output stream
_out = _bOut = new ByteArrayOutputStream2(_wrapper.getBufferSize());
}
}
// else are we aggregating writes?
else if (_bOut !=null)
{
// We are aggregating into the buffered output stream.
// If this write fills the buffer, then we are committing
if (lengthToWrite>=(_bOut.getBuf().length - _bOut.getCount()))
{
// if we know this is all the content and it is less than minimum, then do not compress, otherwise do compress
long length=_wrapper.getContentLength();
if (length>=0 && length<_wrapper.getMinCompressSize())
doNotCompress(false); // Not compressing by size, so no vary on request headers
else
doCompress();
}
}
}
public OutputStream getOutputStream()
{
return _out;
}
public boolean isClosed()
{
return _closed;
}
/**
* Allows derived implementations to replace PrintWriter implementation.
*/
protected PrintWriter newWriter(OutputStream out, String encoding) throws UnsupportedEncodingException
{
return encoding == null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
}
protected void addHeader(String name,String value)
{
_response.addHeader(name, value);
}
protected void setHeader(String name,String value)
{
_response.setHeader(name, value);
}
@Override
public void setWriteListener(WriteListener writeListener)
{
throw new UnsupportedOperationException("Use AsyncGzipFilter");
}
@Override
public boolean isReady()
{
throw new UnsupportedOperationException("Use AsyncGzipFilter");
}
/**
* Create the stream fitting to the underlying compression type.
*
* @throws IOException
* Signals that an I/O exception has occurred.
*/
protected abstract OutputStream createStream() throws IOException;
}

View File

@ -1,485 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Set;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.eclipse.jetty.util.StringUtil;
/*------------------------------------------------------------ */
/**
*/
public abstract class CompressedResponseWrapper extends HttpServletResponseWrapper
{
public static final int DEFAULT_BUFFER_SIZE = 8192;
public static final int DEFAULT_MIN_COMPRESS_SIZE = 256;
private Set<String> _mimeTypes;
private boolean _excludeMimeTypes;
private int _bufferSize=DEFAULT_BUFFER_SIZE;
private int _minCompressSize=DEFAULT_MIN_COMPRESS_SIZE;
protected HttpServletRequest _request;
private PrintWriter _writer;
private AbstractCompressedStream _compressedStream;
private String _etag;
private long _contentLength=-1;
private boolean _noCompression;
/* ------------------------------------------------------------ */
public CompressedResponseWrapper(HttpServletRequest request, HttpServletResponse response)
{
super(response);
_request = request;
}
/* ------------------------------------------------------------ */
public long getContentLength()
{
return _contentLength;
}
/* ------------------------------------------------------------ */
@Override
public int getBufferSize()
{
return _bufferSize;
}
/* ------------------------------------------------------------ */
public int getMinCompressSize()
{
return _minCompressSize;
}
/* ------------------------------------------------------------ */
public String getETag()
{
return _etag;
}
/* ------------------------------------------------------------ */
public HttpServletRequest getRequest()
{
return _request;
}
/* ------------------------------------------------------------ */
/**
*/
public void setMimeTypes(Set<String> mimeTypes,boolean excludeMimeTypes)
{
_excludeMimeTypes=excludeMimeTypes;
_mimeTypes = mimeTypes;
}
/* ------------------------------------------------------------ */
/**
*/
@Override
public void setBufferSize(int bufferSize)
{
_bufferSize = bufferSize;
if (_compressedStream!=null)
_compressedStream.setBufferSize(bufferSize);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setMinCompressSize(int)
*/
public void setMinCompressSize(int minCompressSize)
{
_minCompressSize = minCompressSize;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setContentType(java.lang.String)
*/
@Override
public void setContentType(String ct)
{
super.setContentType(ct);
if (!_noCompression && (_compressedStream==null || _compressedStream.getOutputStream()==null))
{
if (ct!=null)
{
int colon=ct.indexOf(";");
if (colon>0)
ct=ct.substring(0,colon);
if (_mimeTypes.contains(StringUtil.asciiToLowerCase(ct))==_excludeMimeTypes)
noCompression();
}
}
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setStatus(int, java.lang.String)
*/
@SuppressWarnings("deprecation")
@Override
public void setStatus(int sc, String sm)
{
super.setStatus(sc,sm);
if (sc<200 || sc==204 || sc==205 || sc>=300)
noCompression();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setStatus(int)
*/
@Override
public void setStatus(int sc)
{
super.setStatus(sc);
if (sc<200 || sc==204 || sc==205 || sc>=300)
noCompression();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setContentLength(int)
*/
@Override
public void setContentLength(int length)
{
if (_noCompression)
super.setContentLength(length);
else
setContentLength((long)length);
}
/* ------------------------------------------------------------ */
protected void setContentLength(long length)
{
_contentLength=length;
if (_compressedStream!=null)
_compressedStream.setContentLength();
else if (_noCompression && _contentLength>=0)
{
HttpServletResponse response = (HttpServletResponse)getResponse();
if(_contentLength<Integer.MAX_VALUE)
{
response.setContentLength((int)_contentLength);
}
else
{
response.setHeader("Content-Length", Long.toString(_contentLength));
}
}
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#addHeader(java.lang.String, java.lang.String)
*/
@Override
public void addHeader(String name, String value)
{
if ("content-length".equalsIgnoreCase(name))
{
_contentLength=Long.parseLong(value);
if (_compressedStream!=null)
_compressedStream.setContentLength();
}
else if ("content-type".equalsIgnoreCase(name))
{
setContentType(value);
}
else if ("content-encoding".equalsIgnoreCase(name))
{
super.addHeader(name,value);
if (!isCommitted())
{
noCompression();
}
}
else if ("etag".equalsIgnoreCase(name))
_etag=value;
else
super.addHeader(name,value);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#flushBuffer()
*/
@Override
public void flushBuffer() throws IOException
{
if (_writer!=null)
_writer.flush();
if (_compressedStream!=null)
_compressedStream.flush();
else
getResponse().flushBuffer();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#reset()
*/
@Override
public void reset()
{
super.reset();
if (_compressedStream!=null)
_compressedStream.resetBuffer();
_writer=null;
_compressedStream=null;
_noCompression=false;
_contentLength=-1;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#resetBuffer()
*/
@Override
public void resetBuffer()
{
super.resetBuffer();
if (_compressedStream!=null)
_compressedStream.resetBuffer();
_writer=null;
_compressedStream=null;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#sendError(int, java.lang.String)
*/
@Override
public void sendError(int sc, String msg) throws IOException
{
resetBuffer();
super.sendError(sc,msg);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#sendError(int)
*/
@Override
public void sendError(int sc) throws IOException
{
resetBuffer();
super.sendError(sc);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#sendRedirect(java.lang.String)
*/
@Override
public void sendRedirect(String location) throws IOException
{
resetBuffer();
super.sendRedirect(location);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#noCompression()
*/
public void noCompression()
{
if (!_noCompression)
setDeferredHeaders();
_noCompression=true;
if (_compressedStream!=null)
{
try
{
_compressedStream.doNotCompress(false);
}
catch (IOException e)
{
throw new IllegalStateException(e);
}
}
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#finish()
*/
public void finish() throws IOException
{
if (_writer!=null && !_compressedStream.isClosed())
_writer.flush();
if (_compressedStream!=null)
_compressedStream.finish();
else
setDeferredHeaders();
}
/* ------------------------------------------------------------ */
private void setDeferredHeaders()
{
if (!isCommitted())
{
if (_contentLength>=0)
{
if (_contentLength < Integer.MAX_VALUE)
super.setContentLength((int)_contentLength);
else
super.setHeader("Content-Length",Long.toString(_contentLength));
}
if(_etag!=null)
super.setHeader("ETag",_etag);
}
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setHeader(java.lang.String, java.lang.String)
*/
@Override
public void setHeader(String name, String value)
{
if (_noCompression)
super.setHeader(name,value);
else if ("content-length".equalsIgnoreCase(name))
{
setContentLength(Long.parseLong(value));
}
else if ("content-type".equalsIgnoreCase(name))
{
setContentType(value);
}
else if ("content-encoding".equalsIgnoreCase(name))
{
super.setHeader(name,value);
if (!isCommitted())
{
noCompression();
}
}
else if ("etag".equalsIgnoreCase(name))
_etag=value;
else
super.setHeader(name,value);
}
/* ------------------------------------------------------------ */
@Override
public boolean containsHeader(String name)
{
if (!_noCompression && "etag".equalsIgnoreCase(name) && _etag!=null)
return true;
return super.containsHeader(name);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#getOutputStream()
*/
@Override
public ServletOutputStream getOutputStream() throws IOException
{
if (_compressedStream==null)
{
if (getResponse().isCommitted() || _noCompression)
return getResponse().getOutputStream();
_compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse());
}
else if (_writer!=null)
throw new IllegalStateException("getWriter() called");
return _compressedStream;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#getWriter()
*/
@Override
public PrintWriter getWriter() throws IOException
{
if (_writer==null)
{
if (_compressedStream!=null)
throw new IllegalStateException("getOutputStream() called");
if (getResponse().isCommitted() || _noCompression)
return getResponse().getWriter();
_compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse());
_writer=newWriter(_compressedStream,getCharacterEncoding());
}
return _writer;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setIntHeader(java.lang.String, int)
*/
@Override
public void setIntHeader(String name, int value)
{
if ("content-length".equalsIgnoreCase(name))
{
_contentLength=value;
if (_compressedStream!=null)
_compressedStream.setContentLength();
}
else
super.setIntHeader(name,value);
}
/* ------------------------------------------------------------ */
/**
* Allows derived implementations to replace PrintWriter implementation.
*
* @param out the out
* @param encoding the encoding
* @return the prints the writer
* @throws UnsupportedEncodingException the unsupported encoding exception
*/
protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException
{
return encoding==null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
}
/* ------------------------------------------------------------ */
/**
*@return the underlying CompressedStream implementation
*/
protected abstract AbstractCompressedStream newCompressedStream(HttpServletRequest _request, HttpServletResponse response) throws IOException;
}

View File

@ -1,101 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.Deflater;
/**
* Reimplementation of {@link java.util.zip.DeflaterOutputStream} that supports reusing the buffer.
*/
public class DeflatedOutputStream extends FilterOutputStream
{
protected final Deflater _def;
protected final byte[] _buf;
protected boolean closed = false;
public DeflatedOutputStream(OutputStream out, Deflater deflater, byte[] buffer)
{
super(out);
_def = deflater;
_buf = buffer;
}
@Override
public void write(int b) throws IOException
{
byte[] buf = new byte[1];
buf[0] = (byte)(b & 0xff);
write(buf,0,1);
}
@Override
public void write(byte[] b, int off, int len) throws IOException
{
if (_def.finished())
throw new IOException("Stream already finished");
if ((off | len | (off + len) | (b.length - (off + len))) < 0)
throw new IndexOutOfBoundsException();
if (len == 0)
return;
if (!_def.finished())
{
_def.setInput(b,off,len);
while (!_def.needsInput())
{
deflate();
}
}
}
private void deflate() throws IOException
{
int len = _def.deflate(_buf,0,_buf.length);
if (len > 0)
{
out.write(_buf,0,len);
}
}
public synchronized void finish() throws IOException
{
if (!_def.finished())
{
_def.finish();
while (!_def.finished())
{
deflate();
}
}
}
@Override
public synchronized void close() throws IOException
{
if (!closed)
{
finish();
out.close();
closed = true;
}
}
}

View File

@ -18,91 +18,467 @@
package org.eclipse.jetty.servlets.gzip;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPOutputStream;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
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.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.servlets.GzipFilter;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/**
* GZIP Handler This handler will gzip the content of a response if:
* <ul>
* <li>The filter is mapped to a matching path</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>The content-type is in the comma separated list of mimeTypes set in the <code>mimeTypes</code> initParameter or if no mimeTypes are defined the
* content-type is not "application/gzip"</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 handler is used for static content,
* then use of efficient direct NIO may be prevented, thus use of the gzip mechanism of the <code>org.eclipse.jetty.servlet.DefaultServlet</code> is advised instead.
* </p>
*/
public class GzipHandler extends HandlerWrapper
public class GzipHandler extends HandlerWrapper implements GzipFactory
{
private static final Logger LOG = Log.getLogger(GzipHandler.class);
final protected Set<String> _mimeTypes=new HashSet<>();
protected boolean _excludeMimeTypes=false;
protected Set<String> _excludedUA;
protected int _bufferSize = 8192;
protected int _minGzipSize = 256;
protected String _vary = "Accept-Encoding, User-Agent";
public final static String GZIP = "gzip";
public final static String DEFLATE = "deflate";
public final static String ETAG_GZIP="--gzip";
public final static String ETAG = "o.e.j.s.GzipFilter.ETag";
public final static int DEFAULT_MIN_GZIP_SIZE=16;
protected final Set<String> _mimeTypes=new HashSet<>();
protected boolean _excludeMimeTypes;
protected int _bufferSize=32*1024;
protected int _minGzipSize=DEFAULT_MIN_GZIP_SIZE;
protected int _deflateCompressionLevel=Deflater.DEFAULT_COMPRESSION;
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 MimeTypes _knownMimeTypes= new MimeTypes();
protected final Set<String> _methods=new HashSet<>();
protected final Set<String> _excludedAgents=new HashSet<>();
protected final Set<Pattern> _excludedAgentPatterns=new HashSet<>();
protected final Set<String> _excludedPaths=new HashSet<>();
protected final Set<Pattern> _excludedPathPatterns=new HashSet<>();
protected HttpField _vary=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT);
/* ------------------------------------------------------------ */
/**
* Instantiates a new gzip handler.
*/
public GzipHandler()
{
_methods.add(HttpMethod.GET.asString());
_excludeMimeTypes=true;
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");
}
}
@Override
public int getBufferSize()
{
return _bufferSize;
}
@Override
public Deflater getDeflater(Request request, long content_length)
{
String ua = request.getHttpFields().get(HttpHeader.USER_AGENT);
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;
}
// If not HTTP/2, then we must check the accept encoding header
if (request.getHttpVersion()!=HttpVersion.HTTP_2)
{
HttpField accept = request.getHttpFields().getField(HttpHeader.ACCEPT_ENCODING);
if (accept==null)
{
LOG.debug("{} excluded !accept {}",this,request);
return null;
}
boolean gzip = accept.contains("gzip");
if (!gzip)
{
LOG.debug("{} excluded not gzip accept {}",this,request);
return null;
}
}
Deflater df = _deflater.get();
if (df==null)
df=new Deflater(_deflateCompressionLevel,true);
else
_deflater.set(null);
return df;
}
/* ------------------------------------------------------------ */
/**
* Get the mime types.
* Get the minimum reponse size.
*
* @return mime types to set
* @return minimum reponse size
*/
public Set<String> getMimeTypes()
public int getMinGzipSize()
{
return _mimeTypes;
return _minGzipSize;
}
/* ------------------------------------------------------------ */
@Override
public HttpField getVaryField()
{
return _vary;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
LOG.debug("{} doFilter {}",this,request);
// 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);
_handler.handle(target,baseRequest, request, response);
return;
}
if (isExcludedPath(requestURI))
{
LOG.debug("{} excluded by path {}",this,request);
_handler.handle(target,baseRequest, request, response);
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 && _excludeMimeTypes)
{
ServletContext context = request.getServletContext();
String mimeType = context==null?_knownMimeTypes.getMimeByExtension(request.getRequestURI()):context.getMimeType(request.getRequestURI());
if (mimeType!=null)
{
mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType);
if (_mimeTypes.contains(mimeType))
{
LOG.debug("{} excluded by path suffix {}",this,request);
// handle normally without setting vary header
_handler.handle(target,baseRequest, request, response);
return;
}
}
}
//If the Content-Encoding is already set, then we won't compress
if (response.getHeader("Content-Encoding") != null)
{
_handler.handle(target,baseRequest, request, response);
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
_handler.handle(target,baseRequest, request, response);
return;
}
}
}
// Special handling for etags
String etag = request.getHeader("If-None-Match");
if (etag!=null)
{
if (etag.contains(ETAG_GZIP))
request.setAttribute(ETAG,etag.replace(ETAG_GZIP,""));
}
HttpChannel channel = HttpChannel.getCurrentHttpChannel();
HttpOutput out = channel.getResponse().getHttpOutput();
// TODO recycle the GzipHttpOutputFilter
out.setFilter(new GzipHttpOutputInterceptor(this,channel,out.getFilter()));
_handler.handle(target,baseRequest, request, response);
}
public int getDeflateCompressionLevel()
{
return _deflateCompressionLevel;
}
public void setDeflateCompressionLevel(int deflateCompressionLevel)
{
_deflateCompressionLevel = deflateCompressionLevel;
}
public boolean getCheckGzExists()
{
return _checkGzExists;
}
public void setCheckGzExists(boolean checkGzExists)
{
_checkGzExists = checkGzExists;
}
public String[] getMethods()
{
return _methods.toArray(new String[_methods.size()]);
}
public void setMethods(String[] methods)
{
_methods.clear();
for (String m : methods)
_methods.add(m);
}
/**
* 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;
}
@Override
public boolean isExcludedMimeType(String mimetype)
{
return _mimeTypes.contains(mimetype) == _excludeMimeTypes;
}
/**
* 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 void recycle(Deflater deflater)
{
deflater.reset();
if (_deflater.get()==null)
_deflater.set(deflater);
}
/* ------------------------------------------------------------ */
/**
* Set the buffer size.
*
* @param bufferSize
* buffer size to set
*/
public void setBufferSize(int bufferSize)
{
_bufferSize = bufferSize;
}
/* ------------------------------------------------------------ */
/**
* Set the mime types.
*
* @param mimeTypes
* the mime types to set
*/
public void setMimeTypes(Set<String> mimeTypes)
public void setExcludeMimeTypes(boolean exclude)
{
_excludeMimeTypes=false;
_mimeTypes.clear();
_mimeTypes.addAll(mimeTypes);
_excludeMimeTypes=exclude;
}
public String[] getMimeTypes()
{
return _mimeTypes.toArray(new String[_mimeTypes.size()]);
}
public void setMimeTypes(String[] types)
{
_mimeTypes.clear();
_mimeTypes.addAll(Arrays.asList(types));
}
public boolean isExcludeMimeTypes()
{
return _excludeMimeTypes;
}
public String[] getExcludedPaths()
{
return _excludedPaths.toArray(new String[_excludedPaths.size()]);
}
public void setExcludedPaths(String[] paths)
{
_excludedPaths.clear();
_excludedPaths.addAll(Arrays.asList(paths));
}
public String[] getExcludedPathPatterns()
{
Pattern[] ps = _excludedPathPatterns.toArray(new Pattern[_excludedPathPatterns.size()]);
String[] s = new String[ps.length];
int i=0;
for (Pattern p: ps)
s[i++]=p.toString();
return s;
}
public void setExcludedPathPatterns(String[] patterns)
{
_excludedPathPatterns.clear();
for (String s : patterns)
_excludedPathPatterns.add(Pattern.compile(s));
}
public String[] getExcludedAgents()
{
return _excludedAgents.toArray(new String[_excludedAgents.size()]);
}
public void setExcludedAgents(String[] paths)
{
_excludedAgents.clear();
_excludedAgents.addAll(Arrays.asList(paths));
}
public String[] getExcludedAgentPatterns()
{
Pattern[] ps = _excludedAgentPatterns.toArray(new Pattern[_excludedAgentPatterns.size()]);
String[] s = new String[ps.length];
int i=0;
for (Pattern p: ps)
s[i++]=p.toString();
return s;
}
public void setExcludedAgentPatterns(String[] patterns)
{
_excludedAgentPatterns.clear();
for (String s : patterns)
_excludedAgentPatterns.add(Pattern.compile(s));
}
/* ------------------------------------------------------------ */
/**
* Set the mime types.
@ -126,63 +502,16 @@ public class GzipHandler extends HandlerWrapper
/* ------------------------------------------------------------ */
/**
* Set the mime types.
*/
public void setExcludeMimeTypes(boolean exclude)
{
_excludeMimeTypes=exclude;
}
/* ------------------------------------------------------------ */
/**
* Get the excluded user agents.
* Set the minimum reponse size.
*
* @return excluded user agents
* @param minGzipSize
* minimum reponse size
*/
public Set<String> getExcluded()
public void setMinGzipSize(int minGzipSize)
{
return _excludedUA;
_minGzipSize = minGzipSize;
}
/* ------------------------------------------------------------ */
/**
* Set the excluded user agents.
*
* @param excluded
* excluded user agents to set
*/
public void setExcluded(Set<String> excluded)
{
_excludedUA = excluded;
}
/* ------------------------------------------------------------ */
/**
* Set the excluded user agents.
*
* @param excluded
* excluded user agents to set
*/
public void setExcluded(String excluded)
{
if (excluded != null)
{
_excludedUA = new HashSet<String>();
StringTokenizer tok = new StringTokenizer(excluded,",",false);
while (tok.hasMoreTokens())
_excludedUA.add(tok.nextToken());
}
}
/* ------------------------------------------------------------ */
/**
* @return The value of the Vary header set if a response can be compressed.
*/
public String getVary()
{
return _vary;
}
/* ------------------------------------------------------------ */
/**
* Set the value of the Vary header sent with responses that could be compressed.
@ -196,206 +525,9 @@ public class GzipHandler extends HandlerWrapper
*/
public void setVary(String vary)
{
_vary = vary;
_vary=new PreEncodedHttpField(HttpHeader.VARY,vary);
}
/* ------------------------------------------------------------ */
/**
* Get the buffer size.
*
* @return the buffer size
*/
public int getBufferSize()
{
return _bufferSize;
}
/* ------------------------------------------------------------ */
/**
* Set the buffer size.
*
* @param bufferSize
* buffer size to set
*/
public void setBufferSize(int bufferSize)
{
_bufferSize = bufferSize;
}
/* ------------------------------------------------------------ */
/**
* Get the minimum reponse size.
*
* @return minimum reponse size
*/
public int getMinGzipSize()
{
return _minGzipSize;
}
/* ------------------------------------------------------------ */
/**
* Set the minimum reponse size.
*
* @param minGzipSize
* minimum reponse size
*/
public void setMinGzipSize(int minGzipSize)
{
_minGzipSize = minGzipSize;
}
/* ------------------------------------------------------------ */
@Override
protected void doStart() throws Exception
{
if (_mimeTypes.size()==0)
{
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");
}
}
super.doStart();
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (_handler!=null && isStarted())
{
String ae = request.getHeader("accept-encoding");
if (ae != null && ae.indexOf("gzip")>=0 && !response.containsHeader("Content-Encoding")
&& !HttpMethod.HEAD.is(request.getMethod()))
{
if (_excludedUA!=null)
{
String ua = request.getHeader("User-Agent");
if (_excludedUA.contains(ua))
{
_handler.handle(target,baseRequest, request, response);
return;
}
}
final CompressedResponseWrapper wrappedResponse = newGzipResponseWrapper(request,response);
boolean exceptional=true;
try
{
_handler.handle(target, baseRequest, request, wrappedResponse);
exceptional=false;
}
finally
{
if (request.isAsyncStarted())
{
request.getAsyncContext().addListener(new AsyncListener()
{
@Override
public void onTimeout(AsyncEvent event) throws IOException
{
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
}
@Override
public void onError(AsyncEvent event) throws IOException
{
}
@Override
public void onComplete(AsyncEvent event) throws IOException
{
try
{
wrappedResponse.finish();
}
catch(IOException e)
{
LOG.warn(e);
}
}
});
}
else if (exceptional && !response.isCommitted())
{
wrappedResponse.resetBuffer();
wrappedResponse.noCompression();
}
else
wrappedResponse.finish();
}
}
else
{
_handler.handle(target,baseRequest, request, response);
}
}
}
/**
* Allows derived implementations to replace ResponseWrapper implementation.
*
* @param request the request
* @param response the response
* @return the gzip response wrapper
*/
protected CompressedResponseWrapper newGzipResponseWrapper(HttpServletRequest request, HttpServletResponse response)
{
return new CompressedResponseWrapper(request,response)
{
{
super.setMimeTypes(GzipHandler.this._mimeTypes,GzipHandler.this._excludeMimeTypes);
super.setBufferSize(GzipHandler.this._bufferSize);
super.setMinCompressSize(GzipHandler.this._minGzipSize);
}
@Override
protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
{
return new AbstractCompressedStream("gzip",request,this,_vary)
{
@Override
protected DeflaterOutputStream createStream() throws IOException
{
return new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
}
};
}
@Override
protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException
{
return GzipHandler.this.newWriter(out,encoding);
}
};
}
/**
* Allows derived implementations to replace PrintWriter implementation.
*
* @param out the out
* @param encoding the encoding
* @return the prints the writer
* @throws UnsupportedEncodingException
*/
protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException
{
return encoding==null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
}
}

View File

@ -30,8 +30,7 @@ import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.servlets.AsyncGzipFilter;
import org.eclipse.jetty.servlets.GzipFilter;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IteratingNestedCallback;
@ -39,45 +38,43 @@ import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class GzipHttpOutput extends HttpOutput
public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
{
public static Logger LOG = Log.getLogger(GzipHttpOutput.class);
public static Logger LOG = Log.getLogger(GzipHttpOutputInterceptor.class);
private final static PreEncodedHttpField CONTENT_ENCODING_GZIP=new PreEncodedHttpField(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 enum GZState { MIGHT_COMPRESS, NOT_COMPRESSING, COMMITTING, COMPRESSING, FINISHED};
private final AtomicReference<GZState> _state = new AtomicReference<>(GZState.MIGHT_COMPRESS);
private final CRC32 _crc = new CRC32();
private final GzipFactory _factory;
private final HttpOutput.Interceptor _filter;
private final HttpChannel _channel;
private Deflater _deflater;
private GzipFactory _factory;
private ByteBuffer _buffer;
public GzipHttpOutput(HttpChannel channel)
public GzipHttpOutputInterceptor(GzipFactory factory, HttpChannel channel, HttpOutput.Interceptor filter)
{
super(channel);
_factory=factory;
_channel=channel;
_filter=filter;
}
@Override
public void reset()
{
_state.set(GZState.NOT_COMPRESSING);
super.reset();
}
@Override
protected void write(ByteBuffer content, boolean complete, Callback callback)
public void write(ByteBuffer content, boolean complete, Callback callback)
{
switch (_state.get())
{
case NOT_COMPRESSING:
superWrite(content, complete, callback);
return;
case MIGHT_COMPRESS:
commit(content,complete,callback);
break;
case NOT_COMPRESSING:
_filter.write(content, complete, callback);
return;
case COMMITTING:
callback.failed(new WritePendingException());
break;
@ -92,11 +89,6 @@ public class GzipHttpOutput extends HttpOutput
}
}
private void superWrite(ByteBuffer content, boolean complete, Callback callback)
{
super.write(content,complete,callback);
}
private void addTrailer()
{
int i=_buffer.limit();
@ -132,18 +124,17 @@ public class GzipHttpOutput extends HttpOutput
protected void commit(ByteBuffer content, boolean complete, Callback callback)
{
// Are we excluding because of status?
Response response=getHttpChannel().getResponse();
int sc = response.getStatus();
int sc = _channel.getResponse().getStatus();
if (sc>0 && (sc<200 || sc==204 || sc==205 || sc>=300))
{
LOG.debug("{} exclude by status {}",this,sc);
noCompression();
superWrite(content, complete, callback);
_filter.write(content, complete, callback);
return;
}
// Are we excluding because of mime-type?
String ct = getHttpChannel().getResponse().getContentType();
String ct = _channel.getResponse().getContentType();
if (ct!=null)
{
ct=MimeTypes.getContentTypeWithoutCharset(ct);
@ -151,18 +142,18 @@ public class GzipHttpOutput extends HttpOutput
{
LOG.debug("{} exclude by mimeType {}",this,ct);
noCompression();
superWrite(content, complete, callback);
_filter.write(content, complete, callback);
return;
}
}
// Has the Content-Encoding header already been set?
String ce=getHttpChannel().getResponse().getHeader("Content-Encoding");
String ce=_channel.getResponse().getHeader("Content-Encoding");
if (ce != null)
{
LOG.debug("{} exclude by content-encoding {}",this,ce);
noCompression();
superWrite(content, complete, callback);
_filter.write(content, complete, callback);
return;
}
@ -170,33 +161,33 @@ public class GzipHttpOutput extends HttpOutput
if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING))
{
// We are varying the response due to accept encoding header.
HttpFields fields = response.getHttpFields();
HttpFields fields = _channel.getResponse().getHttpFields();
fields.add(_factory.getVaryField());
long content_length = response.getContentLength();
long content_length = _channel.getResponse().getContentLength();
if (content_length<0 && complete)
content_length=content.remaining();
_deflater = _factory.getDeflater(getHttpChannel().getRequest(),content_length);
_deflater = _factory.getDeflater(_channel.getRequest(),content_length);
if (_deflater==null)
{
LOG.debug("{} exclude no deflater",this);
_state.set(GZState.NOT_COMPRESSING);
superWrite(content, complete, callback);
_filter.write(content, complete, callback);
return;
}
fields.put(CONTENT_ENCODING_GZIP);
_crc.reset();
_buffer=getHttpChannel().getByteBufferPool().acquire(_factory.getBufferSize(),false);
_buffer=_channel.getByteBufferPool().acquire(_factory.getBufferSize(),false);
BufferUtil.fill(_buffer,GZIP_HEADER,0,GZIP_HEADER.length);
// Adjust headers
response.setContentLength(-1);
_channel.getResponse().setContentLength(-1);
String etag=fields.get(HttpHeader.ETAG);
if (etag!=null)
fields.put(HttpHeader.ETAG,etag.substring(0,etag.length()-1)+AsyncGzipFilter.ETAG_GZIP+ '"');
fields.put(HttpHeader.ETAG,etag.substring(0,etag.length()-1)+GzipFilter.ETAG_GZIP+ '"');
LOG.debug("{} compressing {}",this,_deflater);
_state.set(GZState.COMPRESSING);
@ -247,29 +238,6 @@ public class GzipHttpOutput extends HttpOutput
}
}
}
public void mightCompress(GzipFactory factory)
{
while (true)
{
switch (_state.get())
{
case NOT_COMPRESSING:
_factory=factory;
if (_state.compareAndSet(GZState.NOT_COMPRESSING,GZState.MIGHT_COMPRESS))
{
LOG.debug("{} might compress",this);
return;
}
_factory=null;
break;
default:
throw new IllegalStateException(_state.get().toString());
}
}
}
private class GzipArrayCB extends IteratingNestedCallback
{
@ -298,7 +266,7 @@ public class GzipHttpOutput extends HttpOutput
{
_factory.recycle(_deflater);
_deflater=null;
getHttpChannel().getByteBufferPool().release(_buffer);
_channel.getByteBufferPool().release(_buffer);
_buffer=null;
return Action.SUCCEEDED;
}
@ -318,7 +286,7 @@ public class GzipHttpOutput extends HttpOutput
if (complete)
addTrailer();
superWrite(_buffer,complete,this);
_filter.write(_buffer,complete,this);
return Action.SCHEDULED;
}
}
@ -331,7 +299,7 @@ public class GzipHttpOutput extends HttpOutput
public GzipBufferCB(ByteBuffer content, boolean complete, Callback callback)
{
super(callback);
_input=getHttpChannel().getByteBufferPool().acquire(Math.min(_factory.getBufferSize(),content.remaining()),false);
_input=_channel.getByteBufferPool().acquire(Math.min(_factory.getBufferSize(),content.remaining()),false);
_content=content;
_last=complete;
}
@ -347,7 +315,7 @@ public class GzipHttpOutput extends HttpOutput
{
_factory.recycle(_deflater);
_deflater=null;
getHttpChannel().getByteBufferPool().release(_buffer);
_channel.getByteBufferPool().release(_buffer);
_buffer=null;
return Action.SUCCEEDED;
}
@ -389,7 +357,7 @@ public class GzipHttpOutput extends HttpOutput
if (finished)
addTrailer();
superWrite(_buffer,finished,this);
_filter.write(_buffer,finished,this);
return Action.SCHEDULED;
}
}

View File

@ -1,72 +0,0 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.io.OutputStream;
import java.util.zip.CRC32;
import java.util.zip.Deflater;
/**
* Reimplementation of {@link java.util.zip.GZIPOutputStream} that supports reusing a {@link Deflater} instance.
*/
public class GzipOutputStream extends DeflatedOutputStream
{
private final static byte[] GZIP_HEADER = new byte[]
{ (byte)0x1f, (byte)0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0 };
private final CRC32 _crc = new CRC32();
public GzipOutputStream(OutputStream out, Deflater deflater, byte[] buffer) throws IOException
{
super(out,deflater,buffer);
out.write(GZIP_HEADER);
}
@Override
public synchronized void write(byte[] buf, int off, int len) throws IOException
{
super.write(buf,off,len);
_crc.update(buf,off,len);
}
@Override
public synchronized void finish() throws IOException
{
if (!_def.finished())
{
super.finish();
byte[] trailer = new byte[8];
writeInt((int)_crc.getValue(),trailer,0);
writeInt(_def.getTotalIn(),trailer,4);
out.write(trailer);
}
}
private void writeInt(int i, byte[] buf, int offset)
{
int o = offset;
buf[o++] = (byte)(i & 0xFF);
buf[o++] = (byte)((i >>> 8) & 0xFF);
buf[o++] = (byte)((i >>> 16) & 0xFF);
buf[o++] = (byte)((i >>> 24) & 0xFF);
}
}

View File

@ -90,14 +90,14 @@ public class GzipFilterContentLengthTest
{
return Arrays.asList(new Object[][]
{
{ 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, TestServletBufferTypeLengthWrite.class, GzipFilter.GZIP },
{ GzipFilter.class, TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP },
{ GzipFilter.class, TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP },
@ -107,14 +107,6 @@ public class GzipFilterContentLengthTest
{ 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 },
});
}
@ -122,7 +114,7 @@ public class GzipFilterContentLengthTest
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 static final int TINY = GzipFilter.DEFAULT_MIN_GZIP_SIZE / 2;
private String compressionType;

View File

@ -73,25 +73,25 @@ public class GzipFilterDefaultNoRecompressTest
// Some already compressed files
/* 16 */ { AsyncGzipFilter.class, "test_quotes.gz", "application/gzip", GzipFilter.GZIP },
/* 17 */ { AsyncGzipFilter.class, "test_quotes.bz2", "application/bzip2", GzipFilter.GZIP },
/* 18 */ { AsyncGzipFilter.class, "test_quotes.zip", "application/zip", GzipFilter.GZIP },
/* 19 */ { AsyncGzipFilter.class, "test_quotes.rar", "application/octet-stream", GzipFilter.GZIP },
/* 16 */ { GzipFilter.class, "test_quotes.gz", "application/gzip", GzipFilter.GZIP },
/* 17 */ { GzipFilter.class, "test_quotes.bz2", "application/bzip2", GzipFilter.GZIP },
/* 18 */ { GzipFilter.class, "test_quotes.zip", "application/zip", GzipFilter.GZIP },
/* 19 */ { GzipFilter.class, "test_quotes.rar", "application/octet-stream", GzipFilter.GZIP },
// Some images (common first)
/* 20 */ { AsyncGzipFilter.class, "jetty_logo.png", "image/png", GzipFilter.GZIP },
/* 21 */ { AsyncGzipFilter.class, "jetty_logo.gif", "image/gif", GzipFilter.GZIP },
/* 22 */ { AsyncGzipFilter.class, "jetty_logo.jpeg", "image/jpeg", GzipFilter.GZIP },
/* 23 */ { AsyncGzipFilter.class, "jetty_logo.jpg", "image/jpeg", GzipFilter.GZIP },
/* 20 */ { GzipFilter.class, "jetty_logo.png", "image/png", GzipFilter.GZIP },
/* 21 */ { GzipFilter.class, "jetty_logo.gif", "image/gif", GzipFilter.GZIP },
/* 22 */ { GzipFilter.class, "jetty_logo.jpeg", "image/jpeg", GzipFilter.GZIP },
/* 23 */ { GzipFilter.class, "jetty_logo.jpg", "image/jpeg", GzipFilter.GZIP },
// Lesser encountered images (usually found being requested from non-browser clients)
/* 24 */ { AsyncGzipFilter.class, "jetty_logo.bmp", "image/bmp", GzipFilter.GZIP },
/* 25 */ { AsyncGzipFilter.class, "jetty_logo.tga", "application/tga", GzipFilter.GZIP },
/* 26 */ { AsyncGzipFilter.class, "jetty_logo.tif", "image/tiff", GzipFilter.GZIP },
/* 27 */ { AsyncGzipFilter.class, "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
/* 28 */ { AsyncGzipFilter.class, "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
/* 29 */ { AsyncGzipFilter.class, "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP },
/* 24 */ { GzipFilter.class, "jetty_logo.bmp", "image/bmp", GzipFilter.GZIP },
/* 25 */ { GzipFilter.class, "jetty_logo.tga", "application/tga", GzipFilter.GZIP },
/* 26 */ { GzipFilter.class, "jetty_logo.tif", "image/tiff", GzipFilter.GZIP },
/* 27 */ { GzipFilter.class, "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
/* 28 */ { GzipFilter.class, "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
/* 29 */ { GzipFilter.class, "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP },
// qvalue disables compression
/* 30 */ { AsyncGzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+";q=0"},
/* 31 */ { AsyncGzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+"; q = 0 "}
/* 30 */ { GzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+";q=0"},
/* 31 */ { GzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+"; q = 0 "}
});
}

View File

@ -38,34 +38,19 @@ import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Test the GzipFilter support built into the {@link DefaultServlet}
*/
@RunWith(Parameterized.class)
public class GzipFilterDefaultTest
{
@Parameters
public static List<Object[]> data()
{
return Arrays.asList(new Object[][]
{
{ AsyncGzipFilter.class, AsyncGzipFilter.GZIP },
{ GzipFilter.class, GzipFilter.GZIP },
{ GzipFilter.class, GzipFilter.DEFLATE },
});
}
private Class<? extends Filter> testFilter;
private String compressionType;
public GzipFilterDefaultTest(Class<? extends Filter> testFilter, String compressionType)
public GzipFilterDefaultTest()
{
this.testFilter=testFilter;
this.compressionType = compressionType;
this.testFilter=GzipFilter.class;
this.compressionType = GzipFilter.GZIP;
}
public static class HttpStatusServlet extends HttpServlet

View File

@ -18,9 +18,6 @@
package org.eclipse.jetty.servlets;
import java.util.Arrays;
import java.util.Collection;
import javax.servlet.Servlet;
import org.eclipse.jetty.servlet.FilterHolder;
@ -29,9 +26,6 @@ import org.eclipse.jetty.servlets.gzip.TestMinGzipSizeServlet;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/**
* Perform specific tests on the IncludableGzipFilter's ability to manage
@ -39,24 +33,11 @@ import org.junit.runners.Parameterized.Parameters;
*
* @see <a href="Eclipse Bug 366106">http://bugs.eclipse.org/366106</a>
*/
@RunWith(Parameterized.class)
public class IncludableGzipFilterMinSizeTest
{
@Parameters
public static Collection<String[]> data()
public IncludableGzipFilterMinSizeTest()
{
String[][] data = new String[][]
{
{ GzipFilter.GZIP },
{ GzipFilter.DEFLATE }
};
return Arrays.asList(data);
}
public IncludableGzipFilterMinSizeTest(String compressionType)
{
this.compressionType = compressionType;
this.compressionType = GzipFilter.GZIP;
}
@Rule
@ -70,7 +51,7 @@ public class IncludableGzipFilterMinSizeTest
{
GzipTester tester = new GzipTester(testdir, compressionType);
// Use IncludableGzipFilter
tester.setGzipFilterClass(IncludableGzipFilter.class);
tester.setGzipFilterClass(GzipFilter.class);
FilterHolder holder = tester.setContentServlet(testServlet);
// A valid mime type that we will never use in this test.
@ -96,7 +77,7 @@ public class IncludableGzipFilterMinSizeTest
{
GzipTester tester = new GzipTester(testdir, compressionType);
// Use IncludableGzipFilter
tester.setGzipFilterClass(IncludableGzipFilter.class);
tester.setGzipFilterClass(GzipFilter.class);
FilterHolder holder = tester.setContentServlet(testServlet);
holder.setInitParameter("mimeTypes","application/soap+xml,text/javascript,application/javascript");

View File

@ -28,8 +28,6 @@ import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collection;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
@ -46,24 +44,9 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class IncludableGzipFilterTest
{
@Parameters
public static Collection<String[]> data()
{
String[][] data = new String[][]
{
{ GzipFilter.GZIP },
{ GzipFilter.DEFLATE }
};
return Arrays.asList(data);
}
@Rule
public TestingDir testdir = new TestingDir();
@ -85,9 +68,9 @@ public class IncludableGzipFilterTest
private ServletTester tester;
private String compressionType;
public IncludableGzipFilterTest(String compressionType)
public IncludableGzipFilterTest()
{
this.compressionType = compressionType;
this.compressionType = GzipFilter.GZIP;
}
@Before
@ -105,7 +88,7 @@ public class IncludableGzipFilterTest
tester=new ServletTester("/context");
tester.getContext().setResourceBase(testdir.getDir().getCanonicalPath());
tester.getContext().addServlet(org.eclipse.jetty.servlet.DefaultServlet.class, "/");
FilterHolder holder = tester.getContext().addFilter(IncludableGzipFilter.class,"/*",null);
FilterHolder holder = tester.getContext().addFilter(GzipFilter.class,"/*",null);
holder.setInitParameter("mimeTypes","text/plain");
tester.start();
}

View File

@ -221,7 +221,7 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
assertThat("Main request reply and/or data received", mainStreamLatch.await(5, TimeUnit.SECONDS), is(true));
assertThat("Not more than one push is received", pushDataLatch.await(1, TimeUnit.SECONDS), is(false));
assertThat("Push push headers valid", pushSynHeadersValid.await(5, TimeUnit.SECONDS), is(true));
assertThat("Push response headers are valid", pushResponseHeaders.await(5, TimeUnit.SECONDS), is(true));
assertThat("Push response headers are valid", pushResponseHeaders.await(500, TimeUnit.SECONDS), is(true));
}
@Test
@ -441,6 +441,8 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
output.print("body { background: #FFF; }");
else if (url.endsWith(".js"))
output.print("function(){}();");
else
output.print("DATA");
baseRequest.setHandled(true);
}
});
@ -475,9 +477,7 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
assertThat("Stream is unidirectional", stream.isUnidirectional(), is(true));
assertThat("URI header ends with css", pushInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version))
.getValue().endsWith
("" +
".css"),
.getValue().endsWith(".css"),
is(true));
if (resetPush)
stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM), new Callback.Adapter());
@ -1093,7 +1093,7 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
Fields.Field header = headers.get(name);
if (header != null && expectedValue.equals(header.getValue()))
return true;
System.out.println(name + " not valid! Expected: " + expectedValue + " headers received:" + headers);
System.out.println(name + " not valid! Expected: " + expectedValue + ", headers received:" + headers);
return false;
}

View File

@ -118,6 +118,7 @@ public class WebAppContext extends ServletContextHandler implements WebAppClassL
"org.eclipse.jetty.util.log.", // webapp should use server log
"org.eclipse.jetty.servlet.DefaultServlet", // webapp cannot change default servlets
"org.eclipse.jetty.servlets.AsyncGzipFilter", // special case for AsyncGzipFilter
"org.eclipse.jetty.servlets.GzipFilter", // special case for GzipFilter
"org.eclipse.jetty.servlets.PushCacheFilter" //must be loaded by container classpath
} ;