Issue #406 (GzipHandler: allow to override the Vary response header)
Added an overridable getVaryField() method that, if returns null, disables the send of the Vary header.
This commit is contained in:
parent
87f011d98b
commit
710949dbb7
|
@ -18,8 +18,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server.handler.gzip;
|
package org.eclipse.jetty.server.handler.gzip;
|
||||||
|
|
||||||
import static org.eclipse.jetty.http.GzipHttpContent.ETAG_GZIP_QUOTE;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -47,9 +45,8 @@ import org.eclipse.jetty.util.URIUtil;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Handler that can dynamically GZIP compress responses. Unlike
|
* A Handler that can dynamically GZIP compress responses. Unlike
|
||||||
* previous and 3rd party GzipFilters, this mechanism works with asynchronously
|
* previous and 3rd party GzipFilters, this mechanism works with asynchronously
|
||||||
* generated responses and does not need to wrap the response or it's output
|
* generated responses and does not need to wrap the response or it's output
|
||||||
* stream. Instead it uses the efficient {@link org.eclipse.jetty.server.HttpOutput.Interceptor} mechanism.
|
* stream. Instead it uses the efficient {@link org.eclipse.jetty.server.HttpOutput.Interceptor} mechanism.
|
||||||
|
@ -69,22 +66,22 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
private int _compressionLevel=Deflater.DEFAULT_COMPRESSION;
|
private int _compressionLevel=Deflater.DEFAULT_COMPRESSION;
|
||||||
private boolean _checkGzExists = true;
|
private boolean _checkGzExists = true;
|
||||||
private boolean _syncFlush = false;
|
private boolean _syncFlush = false;
|
||||||
|
|
||||||
// non-static, as other GzipHandler instances may have different configurations
|
// non-static, as other GzipHandler instances may have different configurations
|
||||||
private final ThreadLocal<Deflater> _deflater = new ThreadLocal<Deflater>();
|
private final ThreadLocal<Deflater> _deflater = new ThreadLocal<>();
|
||||||
|
|
||||||
private final IncludeExclude<String> _agentPatterns=new IncludeExclude<>(RegexSet.class);
|
private final IncludeExclude<String> _agentPatterns=new IncludeExclude<>(RegexSet.class);
|
||||||
private final IncludeExclude<String> _methods = new IncludeExclude<>();
|
private final IncludeExclude<String> _methods = new IncludeExclude<>();
|
||||||
private final IncludeExclude<String> _paths = new IncludeExclude<>(PathSpecSet.class);
|
private final IncludeExclude<String> _paths = new IncludeExclude<>(PathSpecSet.class);
|
||||||
private final IncludeExclude<String> _mimeTypes = new IncludeExclude<>();
|
private final IncludeExclude<String> _mimeTypes = new IncludeExclude<>();
|
||||||
|
|
||||||
private HttpField _vary;
|
private HttpField _vary;
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Instantiates a new gzip handler.
|
* Instantiates a new gzip handler.
|
||||||
* The excluded Mime Types are initialized to common known
|
* The excluded Mime Types are initialized to common known
|
||||||
* images, audio, video and other already compressed types.
|
* images, audio, video and other already compressed types.
|
||||||
* The included methods is initialized to GET.
|
* The included methods is initialized to GET.
|
||||||
* The excluded agent patterns are set to exclude MSIE 6.0
|
* The excluded agent patterns are set to exclude MSIE 6.0
|
||||||
|
@ -107,7 +104,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
_mimeTypes.exclude("application/bzip2");
|
_mimeTypes.exclude("application/bzip2");
|
||||||
_mimeTypes.exclude("application/x-rar-compressed");
|
_mimeTypes.exclude("application/x-rar-compressed");
|
||||||
LOG.debug("{} mime types {}",this,_mimeTypes);
|
LOG.debug("{} mime types {}",this,_mimeTypes);
|
||||||
|
|
||||||
_agentPatterns.exclude(".*MSIE 6.0.*");
|
_agentPatterns.exclude(".*MSIE 6.0.*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +142,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @param pathspecs Path specs (as per servlet spec) to exclude. If a
|
* @param pathspecs Path specs (as per servlet spec) to exclude. If a
|
||||||
* ServletContext is available, the paths are relative to the context path,
|
* ServletContext is available, the paths are relative to the context path,
|
||||||
* otherwise they are absolute.
|
* otherwise they are absolute.
|
||||||
* For backward compatibility the pathspecs may be comma separated strings, but this
|
* For backward compatibility the pathspecs may be comma separated strings, but this
|
||||||
|
@ -165,7 +162,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
{
|
{
|
||||||
_agentPatterns.include(patterns);
|
_agentPatterns.include(patterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @param methods The methods to include in compression
|
* @param methods The methods to include in compression
|
||||||
|
@ -214,7 +211,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Add path specs to include. Inclusion takes precedence over exclusion.
|
* Add path specs to include. Inclusion takes precedence over exclusion.
|
||||||
* @param pathspecs Path specs (as per servlet spec) to include. If a
|
* @param pathspecs Path specs (as per servlet spec) to include. If a
|
||||||
* ServletContext is available, the paths are relative to the context path,
|
* ServletContext is available, the paths are relative to the context path,
|
||||||
* otherwise they are absolute
|
* otherwise they are absolute
|
||||||
* For backward compatibility the pathspecs may be comma separated strings, but this
|
* For backward compatibility the pathspecs may be comma separated strings, but this
|
||||||
|
@ -225,7 +222,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
for (String p : pathspecs)
|
for (String p : pathspecs)
|
||||||
_paths.include(StringUtil.csvSplit(p));
|
_paths.include(StringUtil.csvSplit(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@Override
|
@Override
|
||||||
protected void doStart() throws Exception
|
protected void doStart() throws Exception
|
||||||
|
@ -245,7 +242,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
{
|
{
|
||||||
return _compressionLevel;
|
return _compressionLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@Override
|
@Override
|
||||||
public Deflater getDeflater(Request request, long content_length)
|
public Deflater getDeflater(Request request, long content_length)
|
||||||
|
@ -256,7 +253,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
LOG.debug("{} excluded user agent {}",this,request);
|
LOG.debug("{} excluded user agent {}",this,request);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content_length>=0 && content_length<_minGzipSize)
|
if (content_length>=0 && content_length<_minGzipSize)
|
||||||
{
|
{
|
||||||
LOG.debug("{} excluded minGzipSize {}",this,request);
|
LOG.debug("{} excluded minGzipSize {}",this,request);
|
||||||
|
@ -281,16 +278,16 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Deflater df = _deflater.get();
|
Deflater df = _deflater.get();
|
||||||
if (df==null)
|
if (df==null)
|
||||||
df=new Deflater(_compressionLevel,true);
|
df=new Deflater(_compressionLevel,true);
|
||||||
else
|
else
|
||||||
_deflater.set(null);
|
_deflater.set(null);
|
||||||
|
|
||||||
return df;
|
return df;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public String[] getExcludedAgentPatterns()
|
public String[] getExcludedAgentPatterns()
|
||||||
{
|
{
|
||||||
|
@ -325,7 +322,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
Set<String> includes=_agentPatterns.getIncluded();
|
Set<String> includes=_agentPatterns.getIncluded();
|
||||||
return includes.toArray(new String[includes.size()]);
|
return includes.toArray(new String[includes.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public String[] getIncludedMethods()
|
public String[] getIncludedMethods()
|
||||||
{
|
{
|
||||||
|
@ -365,6 +362,11 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
return _minGzipSize;
|
return _minGzipSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected 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)
|
* @see org.eclipse.jetty.server.handler.HandlerWrapper#handle(java.lang.String, org.eclipse.jetty.server.Request, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
|
||||||
|
@ -375,8 +377,8 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
ServletContext context = baseRequest.getServletContext();
|
ServletContext context = baseRequest.getServletContext();
|
||||||
String path = context==null?baseRequest.getRequestURI():URIUtil.addPaths(baseRequest.getServletPath(),baseRequest.getPathInfo());
|
String path = context==null?baseRequest.getRequestURI():URIUtil.addPaths(baseRequest.getServletPath(),baseRequest.getPathInfo());
|
||||||
LOG.debug("{} handle {} in {}",this,baseRequest,context);
|
LOG.debug("{} handle {} in {}",this,baseRequest,context);
|
||||||
|
|
||||||
HttpOutput out = baseRequest.getResponse().getHttpOutput();
|
HttpOutput out = baseRequest.getResponse().getHttpOutput();
|
||||||
// Are we already being gzipped?
|
// Are we already being gzipped?
|
||||||
HttpOutput.Interceptor interceptor = out.getInterceptor();
|
HttpOutput.Interceptor interceptor = out.getInterceptor();
|
||||||
while (interceptor!=null)
|
while (interceptor!=null)
|
||||||
|
@ -389,7 +391,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
}
|
}
|
||||||
interceptor=interceptor.getNextInterceptor();
|
interceptor=interceptor.getNextInterceptor();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not a supported method - no Vary because no matter what client, this URI is always excluded
|
// If not a supported method - no Vary because no matter what client, this URI is always excluded
|
||||||
if (!_methods.matches(baseRequest.getMethod()))
|
if (!_methods.matches(baseRequest.getMethod()))
|
||||||
{
|
{
|
||||||
|
@ -397,7 +399,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
_handler.handle(target,baseRequest, request, response);
|
_handler.handle(target,baseRequest, request, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not a supported URI- no Vary because no matter what client, this URI is always excluded
|
// If not a supported URI- no Vary because no matter what client, this URI is always excluded
|
||||||
// Use pathInfo because this is be
|
// Use pathInfo because this is be
|
||||||
if (!isPathGzipable(path))
|
if (!isPathGzipable(path))
|
||||||
|
@ -420,7 +422,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_checkGzExists && context!=null)
|
if (_checkGzExists && context!=null)
|
||||||
{
|
{
|
||||||
String realpath=request.getServletContext().getRealPath(path);
|
String realpath=request.getServletContext().getRealPath(path);
|
||||||
|
@ -436,25 +438,26 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special handling for etags
|
// Special handling for etags
|
||||||
String etag = baseRequest.getHttpFields().get(HttpHeader.IF_NONE_MATCH);
|
String etag = baseRequest.getHttpFields().get(HttpHeader.IF_NONE_MATCH);
|
||||||
if (etag!=null)
|
if (etag!=null)
|
||||||
{
|
{
|
||||||
int i=etag.indexOf(ETAG_GZIP_QUOTE);
|
int i=etag.indexOf(GzipHttpContent.ETAG_GZIP_QUOTE);
|
||||||
if (i>0)
|
if (i>0)
|
||||||
{
|
{
|
||||||
while (i>=0)
|
while (i>=0)
|
||||||
{
|
{
|
||||||
etag=etag.substring(0,i)+etag.substring(i+GzipHttpContent.ETAG_GZIP.length());
|
etag=etag.substring(0,i)+etag.substring(i+GzipHttpContent.ETAG_GZIP.length());
|
||||||
i=etag.indexOf(ETAG_GZIP_QUOTE,i);
|
i=etag.indexOf(GzipHttpContent.ETAG_GZIP_QUOTE,i);
|
||||||
}
|
}
|
||||||
baseRequest.getHttpFields().put(new HttpField(HttpHeader.IF_NONE_MATCH,etag));
|
baseRequest.getHttpFields().put(new HttpField(HttpHeader.IF_NONE_MATCH,etag));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// install interceptor and handle
|
// install interceptor and handle
|
||||||
out.setInterceptor(new GzipHttpOutputInterceptor(this,_vary,baseRequest.getHttpChannel(),out.getInterceptor(),_syncFlush));
|
out.setInterceptor(new GzipHttpOutputInterceptor(this,getVaryField(),baseRequest.getHttpChannel(),out.getInterceptor(),isSyncFlush()));
|
||||||
|
|
||||||
if (_handler!=null)
|
if (_handler!=null)
|
||||||
_handler.handle(target,baseRequest, request, response);
|
_handler.handle(target,baseRequest, request, response);
|
||||||
}
|
}
|
||||||
|
@ -470,7 +473,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
{
|
{
|
||||||
if (ua == null)
|
if (ua == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return _agentPatterns.matches(ua);
|
return _agentPatterns.matches(ua);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,7 +486,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Checks to see if the path is included or not excluded
|
* Checks to see if the path is included or not excluded
|
||||||
*
|
*
|
||||||
* @param requestURI
|
* @param requestURI
|
||||||
* the request uri
|
* the request uri
|
||||||
|
@ -493,7 +496,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
{
|
{
|
||||||
if (requestURI == null)
|
if (requestURI == null)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return _paths.matches(requestURI);
|
return _paths.matches(requestURI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,7 +518,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
{
|
{
|
||||||
_checkGzExists = checkGzExists;
|
_checkGzExists = checkGzExists;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @param compressionLevel The compression level to use to initialize {@link Deflater#setLevel(int)}
|
* @param compressionLevel The compression level to use to initialize {@link Deflater#setLevel(int)}
|
||||||
|
@ -558,7 +561,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @param pathspecs Path specs (as per servlet spec) to exclude. If a
|
* @param pathspecs Path specs (as per servlet spec) to exclude. If a
|
||||||
* ServletContext is available, the paths are relative to the context path,
|
* ServletContext is available, the paths are relative to the context path,
|
||||||
* otherwise they are absolute.
|
* otherwise they are absolute.
|
||||||
*/
|
*/
|
||||||
|
@ -577,7 +580,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
_agentPatterns.getIncluded().clear();
|
_agentPatterns.getIncluded().clear();
|
||||||
addIncludedAgentPatterns(patterns);
|
addIncludedAgentPatterns(patterns);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @param methods The methods to include in compression
|
* @param methods The methods to include in compression
|
||||||
|
@ -587,7 +590,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
_methods.getIncluded().clear();
|
_methods.getIncluded().clear();
|
||||||
_methods.include(methods);
|
_methods.include(methods);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Set included mime types. Inclusion takes precedence over
|
* Set included mime types. Inclusion takes precedence over
|
||||||
|
@ -603,7 +606,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* Set the path specs to include. Inclusion takes precedence over exclusion.
|
* Set the path specs to include. Inclusion takes precedence over exclusion.
|
||||||
* @param pathspecs Path specs (as per servlet spec) to include. If a
|
* @param pathspecs Path specs (as per servlet spec) to include. If a
|
||||||
* ServletContext is available, the paths are relative to the context path,
|
* ServletContext is available, the paths are relative to the context path,
|
||||||
* otherwise they are absolute
|
* otherwise they are absolute
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.eclipse.jetty.http.MimeTypes;
|
||||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||||
import org.eclipse.jetty.server.HttpChannel;
|
import org.eclipse.jetty.server.HttpChannel;
|
||||||
import org.eclipse.jetty.server.HttpOutput;
|
import org.eclipse.jetty.server.HttpOutput;
|
||||||
|
import org.eclipse.jetty.server.Response;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.IteratingNestedCallback;
|
import org.eclipse.jetty.util.IteratingNestedCallback;
|
||||||
|
@ -46,7 +47,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
|
|
||||||
public final static HttpField VARY_ACCEPT_ENCODING_USER_AGENT=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT);
|
public final static HttpField VARY_ACCEPT_ENCODING_USER_AGENT=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT);
|
||||||
public final static HttpField VARY_ACCEPT_ENCODING=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING.asString());
|
public final static HttpField VARY_ACCEPT_ENCODING=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING.asString());
|
||||||
|
|
||||||
private enum GZState { MIGHT_COMPRESS, NOT_COMPRESSING, COMMITTING, COMPRESSING, FINISHED};
|
private enum GZState { MIGHT_COMPRESS, NOT_COMPRESSING, COMMITTING, COMPRESSING, FINISHED};
|
||||||
private final AtomicReference<GZState> _state = new AtomicReference<>(GZState.MIGHT_COMPRESS);
|
private final AtomicReference<GZState> _state = new AtomicReference<>(GZState.MIGHT_COMPRESS);
|
||||||
private final CRC32 _crc = new CRC32();
|
private final CRC32 _crc = new CRC32();
|
||||||
|
@ -57,7 +58,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
private final HttpField _vary;
|
private final HttpField _vary;
|
||||||
private final int _bufferSize;
|
private final int _bufferSize;
|
||||||
private final boolean _syncFlush;
|
private final boolean _syncFlush;
|
||||||
|
|
||||||
private Deflater _deflater;
|
private Deflater _deflater;
|
||||||
private ByteBuffer _buffer;
|
private ByteBuffer _buffer;
|
||||||
|
|
||||||
|
@ -65,12 +66,12 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
{
|
{
|
||||||
this(factory,VARY_ACCEPT_ENCODING_USER_AGENT,channel.getHttpConfiguration().getOutputBufferSize(),channel,next,syncFlush);
|
this(factory,VARY_ACCEPT_ENCODING_USER_AGENT,channel.getHttpConfiguration().getOutputBufferSize(),channel,next,syncFlush);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GzipHttpOutputInterceptor(GzipFactory factory, HttpField vary, HttpChannel channel, HttpOutput.Interceptor next,boolean syncFlush)
|
public GzipHttpOutputInterceptor(GzipFactory factory, HttpField vary, HttpChannel channel, HttpOutput.Interceptor next,boolean syncFlush)
|
||||||
{
|
{
|
||||||
this(factory,vary,channel.getHttpConfiguration().getOutputBufferSize(),channel,next,syncFlush);
|
this(factory,vary,channel.getHttpConfiguration().getOutputBufferSize(),channel,next,syncFlush);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GzipHttpOutputInterceptor(GzipFactory factory, HttpField vary, int bufferSize, HttpChannel channel, HttpOutput.Interceptor next,boolean syncFlush)
|
public GzipHttpOutputInterceptor(GzipFactory factory, HttpField vary, int bufferSize, HttpChannel channel, HttpOutput.Interceptor next,boolean syncFlush)
|
||||||
{
|
{
|
||||||
_factory=factory;
|
_factory=factory;
|
||||||
|
@ -85,14 +86,14 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
{
|
{
|
||||||
return _interceptor;
|
return _interceptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOptimizedForDirectBuffers()
|
public boolean isOptimizedForDirectBuffers()
|
||||||
{
|
{
|
||||||
return false; // No point as deflator is in user space.
|
return false; // No point as deflator is in user space.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ByteBuffer content, boolean complete, Callback callback)
|
public void write(ByteBuffer content, boolean complete, Callback callback)
|
||||||
{
|
{
|
||||||
|
@ -101,11 +102,11 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
case MIGHT_COMPRESS:
|
case MIGHT_COMPRESS:
|
||||||
commit(content,complete,callback);
|
commit(content,complete,callback);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case NOT_COMPRESSING:
|
case NOT_COMPRESSING:
|
||||||
_interceptor.write(content, complete, callback);
|
_interceptor.write(content, complete, callback);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case COMMITTING:
|
case COMMITTING:
|
||||||
callback.failed(new WritePendingException());
|
callback.failed(new WritePendingException());
|
||||||
break;
|
break;
|
||||||
|
@ -124,21 +125,21 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
{
|
{
|
||||||
int i=_buffer.limit();
|
int i=_buffer.limit();
|
||||||
_buffer.limit(i+8);
|
_buffer.limit(i+8);
|
||||||
|
|
||||||
int v=(int)_crc.getValue();
|
int v=(int)_crc.getValue();
|
||||||
_buffer.put(i++,(byte)(v & 0xFF));
|
_buffer.put(i++,(byte)(v & 0xFF));
|
||||||
_buffer.put(i++,(byte)((v>>>8) & 0xFF));
|
_buffer.put(i++,(byte)((v>>>8) & 0xFF));
|
||||||
_buffer.put(i++,(byte)((v>>>16) & 0xFF));
|
_buffer.put(i++,(byte)((v>>>16) & 0xFF));
|
||||||
_buffer.put(i++,(byte)((v>>>24) & 0xFF));
|
_buffer.put(i++,(byte)((v>>>24) & 0xFF));
|
||||||
|
|
||||||
v=_deflater.getTotalIn();
|
v=_deflater.getTotalIn();
|
||||||
_buffer.put(i++,(byte)(v & 0xFF));
|
_buffer.put(i++,(byte)(v & 0xFF));
|
||||||
_buffer.put(i++,(byte)((v>>>8) & 0xFF));
|
_buffer.put(i++,(byte)((v>>>8) & 0xFF));
|
||||||
_buffer.put(i++,(byte)((v>>>16) & 0xFF));
|
_buffer.put(i++,(byte)((v>>>16) & 0xFF));
|
||||||
_buffer.put(i++,(byte)((v>>>24) & 0xFF));
|
_buffer.put(i++,(byte)((v>>>24) & 0xFF));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void gzip(ByteBuffer content, boolean complete, final Callback callback)
|
private void gzip(ByteBuffer content, boolean complete, final Callback callback)
|
||||||
{
|
{
|
||||||
if (content.hasRemaining() || complete)
|
if (content.hasRemaining() || complete)
|
||||||
|
@ -150,7 +151,8 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
protected void commit(ByteBuffer content, boolean complete, Callback callback)
|
protected void commit(ByteBuffer content, boolean complete, Callback callback)
|
||||||
{
|
{
|
||||||
// Are we excluding because of status?
|
// Are we excluding because of status?
|
||||||
int sc = _channel.getResponse().getStatus();
|
Response response = _channel.getResponse();
|
||||||
|
int sc = response.getStatus();
|
||||||
if (sc>0 && (sc<200 || sc==204 || sc==205 || sc>=300))
|
if (sc>0 && (sc<200 || sc==204 || sc==205 || sc>=300))
|
||||||
{
|
{
|
||||||
LOG.debug("{} exclude by status {}",this,sc);
|
LOG.debug("{} exclude by status {}",this,sc);
|
||||||
|
@ -158,9 +160,9 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
_interceptor.write(content, complete, callback);
|
_interceptor.write(content, complete, callback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we excluding because of mime-type?
|
// Are we excluding because of mime-type?
|
||||||
String ct = _channel.getResponse().getContentType();
|
String ct = response.getContentType();
|
||||||
if (ct!=null)
|
if (ct!=null)
|
||||||
{
|
{
|
||||||
ct=MimeTypes.getContentTypeWithoutCharset(ct);
|
ct=MimeTypes.getContentTypeWithoutCharset(ct);
|
||||||
|
@ -172,9 +174,9 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has the Content-Encoding header already been set?
|
// Has the Content-Encoding header already been set?
|
||||||
String ce=_channel.getResponse().getHeader("Content-Encoding");
|
String ce=response.getHeader("Content-Encoding");
|
||||||
if (ce != null)
|
if (ce != null)
|
||||||
{
|
{
|
||||||
LOG.debug("{} exclude by content-encoding {}",this,ce);
|
LOG.debug("{} exclude by content-encoding {}",this,ce);
|
||||||
|
@ -182,20 +184,21 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
_interceptor.write(content, complete, callback);
|
_interceptor.write(content, complete, callback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are we the thread that commits?
|
// Are we the thread that commits?
|
||||||
if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING))
|
if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING))
|
||||||
{
|
{
|
||||||
// We are varying the response due to accept encoding header.
|
// We are varying the response due to accept encoding header.
|
||||||
HttpFields fields = _channel.getResponse().getHttpFields();
|
HttpFields fields = response.getHttpFields();
|
||||||
fields.add(_vary);
|
if (_vary != null)
|
||||||
|
fields.add(_vary);
|
||||||
|
|
||||||
long content_length = _channel.getResponse().getContentLength();
|
long content_length = response.getContentLength();
|
||||||
if (content_length<0 && complete)
|
if (content_length<0 && complete)
|
||||||
content_length=content.remaining();
|
content_length=content.remaining();
|
||||||
|
|
||||||
_deflater = _factory.getDeflater(_channel.getRequest(),content_length);
|
_deflater = _factory.getDeflater(_channel.getRequest(),content_length);
|
||||||
|
|
||||||
if (_deflater==null)
|
if (_deflater==null)
|
||||||
{
|
{
|
||||||
LOG.debug("{} exclude no deflater",this);
|
LOG.debug("{} exclude no deflater",this);
|
||||||
|
@ -210,7 +213,7 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
BufferUtil.fill(_buffer,GZIP_HEADER,0,GZIP_HEADER.length);
|
BufferUtil.fill(_buffer,GZIP_HEADER,0,GZIP_HEADER.length);
|
||||||
|
|
||||||
// Adjust headers
|
// Adjust headers
|
||||||
_channel.getResponse().setContentLength(-1);
|
response.setContentLength(-1);
|
||||||
String etag=fields.get(HttpHeader.ETAG);
|
String etag=fields.get(HttpHeader.ETAG);
|
||||||
if (etag!=null)
|
if (etag!=null)
|
||||||
{
|
{
|
||||||
|
@ -218,10 +221,10 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
etag=(etag.charAt(end)=='"')?etag.substring(0,end)+GzipHttpContent.ETAG_GZIP+'"':etag+GzipHttpContent.ETAG_GZIP;
|
etag=(etag.charAt(end)=='"')?etag.substring(0,end)+GzipHttpContent.ETAG_GZIP+'"':etag+GzipHttpContent.ETAG_GZIP;
|
||||||
fields.put(HttpHeader.ETAG,etag);
|
fields.put(HttpHeader.ETAG,etag);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.debug("{} compressing {}",this,_deflater);
|
LOG.debug("{} compressing {}",this,_deflater);
|
||||||
_state.set(GZState.COMPRESSING);
|
_state.set(GZState.COMPRESSING);
|
||||||
|
|
||||||
gzip(content,complete,callback);
|
gzip(content,complete,callback);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -268,14 +271,14 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean mightCompress()
|
public boolean mightCompress()
|
||||||
{
|
{
|
||||||
return _state.get()==GZState.MIGHT_COMPRESS;
|
return _state.get()==GZState.MIGHT_COMPRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class GzipBufferCB extends IteratingNestedCallback
|
private class GzipBufferCB extends IteratingNestedCallback
|
||||||
{
|
{
|
||||||
private ByteBuffer _copy;
|
private ByteBuffer _copy;
|
||||||
private final ByteBuffer _content;
|
private final ByteBuffer _content;
|
||||||
private final boolean _last;
|
private final boolean _last;
|
||||||
|
@ -291,11 +294,11 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
{
|
{
|
||||||
if (_deflater==null)
|
if (_deflater==null)
|
||||||
return Action.SUCCEEDED;
|
return Action.SUCCEEDED;
|
||||||
|
|
||||||
if (_deflater.needsInput())
|
if (_deflater.needsInput())
|
||||||
{
|
{
|
||||||
if (BufferUtil.isEmpty(_content))
|
if (BufferUtil.isEmpty(_content))
|
||||||
{
|
{
|
||||||
if (_deflater.finished())
|
if (_deflater.finished())
|
||||||
{
|
{
|
||||||
_factory.recycle(_deflater);
|
_factory.recycle(_deflater);
|
||||||
|
@ -309,12 +312,12 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
}
|
}
|
||||||
return Action.SUCCEEDED;
|
return Action.SUCCEEDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_last)
|
if (!_last)
|
||||||
{
|
{
|
||||||
return Action.SUCCEEDED;
|
return Action.SUCCEEDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
_deflater.finish();
|
_deflater.finish();
|
||||||
}
|
}
|
||||||
else if (_content.hasArray())
|
else if (_content.hasArray())
|
||||||
|
@ -323,9 +326,9 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
int off=_content.arrayOffset()+_content.position();
|
int off=_content.arrayOffset()+_content.position();
|
||||||
int len=_content.remaining();
|
int len=_content.remaining();
|
||||||
BufferUtil.clear(_content);
|
BufferUtil.clear(_content);
|
||||||
|
|
||||||
_crc.update(array,off,len);
|
_crc.update(array,off,len);
|
||||||
_deflater.setInput(array,off,len);
|
_deflater.setInput(array,off,len);
|
||||||
if (_last)
|
if (_last)
|
||||||
_deflater.finish();
|
_deflater.finish();
|
||||||
}
|
}
|
||||||
|
@ -338,13 +341,13 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
BufferUtil.flipToFlush(_copy,0);
|
BufferUtil.flipToFlush(_copy,0);
|
||||||
if (took==0)
|
if (took==0)
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
|
||||||
byte[] array=_copy.array();
|
byte[] array=_copy.array();
|
||||||
int off=_copy.arrayOffset()+_copy.position();
|
int off=_copy.arrayOffset()+_copy.position();
|
||||||
int len=_copy.remaining();
|
int len=_copy.remaining();
|
||||||
|
|
||||||
_crc.update(array,off,len);
|
_crc.update(array,off,len);
|
||||||
_deflater.setInput(array,off,len);
|
_deflater.setInput(array,off,len);
|
||||||
if (_last && BufferUtil.isEmpty(_content))
|
if (_last && BufferUtil.isEmpty(_content))
|
||||||
_deflater.finish();
|
_deflater.finish();
|
||||||
}
|
}
|
||||||
|
@ -359,10 +362,10 @@ public class GzipHttpOutputInterceptor implements HttpOutput.Interceptor
|
||||||
_buffer.limit(_buffer.limit()+produced);
|
_buffer.limit(_buffer.limit()+produced);
|
||||||
}
|
}
|
||||||
boolean finished=_deflater.finished();
|
boolean finished=_deflater.finished();
|
||||||
|
|
||||||
if (finished)
|
if (finished)
|
||||||
addTrailer();
|
addTrailer();
|
||||||
|
|
||||||
_interceptor.write(_buffer,finished,this);
|
_interceptor.write(_buffer,finished,this);
|
||||||
return Action.SCHEDULED;
|
return Action.SCHEDULED;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue