447515 Remove GzipFilter
Made GzipHandler the same as AsyncGzipFilter Added a HttpOutput.Interceptor
This commit is contained in:
parent
4120405b8e
commit
aad1f9d058
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 "}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
} ;
|
||||
|
||||
|
|
Loading…
Reference in New Issue