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:
Simone Bordet 2016-03-08 22:05:10 +01:00
parent 87f011d98b
commit 710949dbb7
2 changed files with 85 additions and 79 deletions

View File

@ -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
*/ */

View File

@ -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;
} }